From 0ffc8c517f15680392328c9af1be085190177b99 Mon Sep 17 00:00:00 2001 From: Pivok Date: Sat, 17 May 2025 12:32:17 +0200 Subject: [PATCH 001/242] Pbr example fix --- examples/shaders/shaders_basic_pbr.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/examples/shaders/shaders_basic_pbr.c b/examples/shaders/shaders_basic_pbr.c index e595ef9ca..b375c4fa1 100644 --- a/examples/shaders/shaders_basic_pbr.c +++ b/examples/shaders/shaders_basic_pbr.c @@ -125,6 +125,8 @@ int main() SetShaderValue(shader, GetShaderLocation(shader, "ambient"), &ambientIntensity, SHADER_UNIFORM_FLOAT); // Get location for shader parameters that can be modified in real time + int metallicValueLoc = GetShaderLocation(shader, "metallicValue"); + int roughnessValueLoc = GetShaderLocation(shader, "roughnessValue"); int emissiveIntensityLoc = GetShaderLocation(shader, "emissivePower"); int emissiveColorLoc = GetShaderLocation(shader, "emissiveColor"); int textureTilingLoc = GetShaderLocation(shader, "tiling"); @@ -141,7 +143,7 @@ int main() // Setup materials[0].maps default parameters car.materials[0].maps[MATERIAL_MAP_ALBEDO].color = WHITE; - car.materials[0].maps[MATERIAL_MAP_METALNESS].value = 0.0f; + car.materials[0].maps[MATERIAL_MAP_METALNESS].value = 1.0f; car.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value = 0.0f; car.materials[0].maps[MATERIAL_MAP_OCCLUSION].value = 1.0f; car.materials[0].maps[MATERIAL_MAP_EMISSION].color = (Color){ 255, 162, 0, 255 }; @@ -163,8 +165,8 @@ int main() floor.materials[0].shader = shader; floor.materials[0].maps[MATERIAL_MAP_ALBEDO].color = WHITE; - floor.materials[0].maps[MATERIAL_MAP_METALNESS].value = 0.0f; - floor.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value = 0.0f; + floor.materials[0].maps[MATERIAL_MAP_METALNESS].value = 0.8f; + floor.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value = 0.1f; floor.materials[0].maps[MATERIAL_MAP_OCCLUSION].value = 1.0f; floor.materials[0].maps[MATERIAL_MAP_EMISSION].color = BLACK; @@ -228,6 +230,10 @@ int main() SetShaderValue(shader, textureTilingLoc, &floorTextureTiling, SHADER_UNIFORM_VEC2); Vector4 floorEmissiveColor = ColorNormalize(floor.materials[0].maps[MATERIAL_MAP_EMISSION].color); SetShaderValue(shader, emissiveColorLoc, &floorEmissiveColor, SHADER_UNIFORM_VEC4); + + // Set floor metallic and roughness values + SetShaderValue(shader, metallicValueLoc, &floor.materials[0].maps[MATERIAL_MAP_METALNESS].value, SHADER_UNIFORM_FLOAT); + SetShaderValue(shader, roughnessValueLoc, &floor.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value, SHADER_UNIFORM_FLOAT); DrawModel(floor, (Vector3){ 0.0f, 0.0f, 0.0f }, 5.0f, WHITE); // Draw floor model @@ -237,6 +243,10 @@ int main() SetShaderValue(shader, emissiveColorLoc, &carEmissiveColor, SHADER_UNIFORM_VEC4); float emissiveIntensity = 0.01f; SetShaderValue(shader, emissiveIntensityLoc, &emissiveIntensity, SHADER_UNIFORM_FLOAT); + + // Set old car metallic and roughness values + SetShaderValue(shader, metallicValueLoc, &car.materials[0].maps[MATERIAL_MAP_METALNESS].value, SHADER_UNIFORM_FLOAT); + SetShaderValue(shader, roughnessValueLoc, &car.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value, SHADER_UNIFORM_FLOAT); DrawModel(car, (Vector3){ 0.0f, 0.0f, 0.0f }, 0.25f, WHITE); // Draw car model From 5da2d1011848d277d7848567dd17650ef341b6f2 Mon Sep 17 00:00:00 2001 From: Padmadev D <128023777+padmadevd@users.noreply.github.com> Date: Sun, 18 May 2025 18:53:28 +0530 Subject: [PATCH 002/242] Update rcore_android.c Bug Fix Update Code to Ignore Hovering Inputs Completely --- src/platforms/rcore_android.c | 95 +++++++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 20 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index ddf7802ba..7f88b7363 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -246,6 +246,17 @@ static const KeyboardKey mapKeycode[KEYCODE_MAP_SIZE] = { KEY_KP_EQUAL // AKEYCODE_NUMPAD_EQUALS }; +// Store data for both Hover and Touch events +// Used to ignore Hover events which are interpreted as Touch events +static struct { + + int32_t pointCount; // Number of touch points active + int32_t pointId[MAX_TOUCH_POINTS]; // Point identifiers + Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen + + int32_t hoverPoints[MAX_TOUCH_POINTS]; // Hover Points +}touchRaw; + //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- @@ -801,6 +812,11 @@ int InitPlatform(void) } } + touchRaw.pointCount = 0; + for(int i = 0; i < MAX_TOUCH_POINTS; i++){ + touchRaw.hoverPoints[i] = -1; + } + return 0; } @@ -1269,25 +1285,78 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) } // Register touch points count - CORE.Input.Touch.pointCount = AMotionEvent_getPointerCount(event); + touchRaw.pointCount = AMotionEvent_getPointerCount(event); - for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++) + for (int i = 0; (i < touchRaw.pointCount) && (i < MAX_TOUCH_POINTS); i++) { // Register touch points id - CORE.Input.Touch.pointId[i] = AMotionEvent_getPointerId(event, i); + touchRaw.pointId[i] = AMotionEvent_getPointerId(event, i); // Register touch points position - CORE.Input.Touch.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) }; + touchRaw.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) }; // Normalize CORE.Input.Touch.position[i] for CORE.Window.screen.width and CORE.Window.screen.height float widthRatio = (float)(CORE.Window.screen.width + CORE.Window.renderOffset.x)/(float)CORE.Window.display.width; float heightRatio = (float)(CORE.Window.screen.height + CORE.Window.renderOffset.y)/(float)CORE.Window.display.height; - CORE.Input.Touch.position[i].x = CORE.Input.Touch.position[i].x*widthRatio - (float)CORE.Window.renderOffset.x/2; - CORE.Input.Touch.position[i].y = CORE.Input.Touch.position[i].y*heightRatio - (float)CORE.Window.renderOffset.y/2; + touchRaw.position[i].x = touchRaw.position[i].x*widthRatio - (float)CORE.Window.renderOffset.x/2; + touchRaw.position[i].y = touchRaw.position[i].y*heightRatio - (float)CORE.Window.renderOffset.y/2; } int32_t action = AMotionEvent_getAction(event); unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; + int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + + if(flags == AMOTION_EVENT_ACTION_HOVER_ENTER){ + // The new pointer is hover + // So add it to hoverPoints + for(int i = 0; i < MAX_TOUCH_POINTS; i++){ + if(touchRaw.hoverPoints[i] == -1){ + touchRaw.hoverPoints[i] = touchRaw.pointId[pointerIndex]; + break; + } + } + } + + if (flags == AMOTION_EVENT_ACTION_POINTER_UP || flags == AMOTION_EVENT_ACTION_UP || flags == AMOTION_EVENT_ACTION_HOVER_EXIT) + { + // One of the touchpoints is released, remove it from touch point arrays + if(flags == AMOTION_EVENT_ACTION_HOVER_EXIT){ + // If the touchPoint is hover, remove it from hoverPoints + for(int i = 0; i < MAX_TOUCH_POINTS; i++){ + if(touchRaw.hoverPoints[i] == touchRaw.pointId[pointerIndex]){ + touchRaw.hoverPoints[i] = -1; + break; + } + } + } + for (int i = pointerIndex; (i < touchRaw.pointCount - 1) && (i < MAX_TOUCH_POINTS-1); i++) + { + touchRaw.pointId[i] = touchRaw.pointId[i+1]; + touchRaw.position[i] = touchRaw.position[i+1]; + } + touchRaw.pointCount--; + } + + int pointCount = 0; + for (int i = 0; (i < touchRaw.pointCount) && (i < MAX_TOUCH_POINTS); i++) + { + // If the touchPoint is hover, Ignore it + bool hover = false; + for(int j = 0; j < MAX_TOUCH_POINTS; j++){ + // Check if the touchPoint is in hoverPointers + if(touchRaw.hoverPoints[j] == touchRaw.pointId[i]){ + hover = true; + break; + } + } + if(hover) + continue; + + CORE.Input.Touch.pointId[pointCount] = touchRaw.pointId[i]; + CORE.Input.Touch.position[pointCount] = touchRaw.position[i]; + pointCount++; + } + CORE.Input.Touch.pointCount = pointCount; #if defined(SUPPORT_GESTURES_SYSTEM) GestureEvent gestureEvent = { 0 }; @@ -1312,20 +1381,6 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) ProcessGestureEvent(gestureEvent); #endif - int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - - if (flags == AMOTION_EVENT_ACTION_POINTER_UP || flags == AMOTION_EVENT_ACTION_UP) - { - // One of the touchpoints is released, remove it from touch point arrays - for (int i = pointerIndex; (i < CORE.Input.Touch.pointCount - 1) && (i < MAX_TOUCH_POINTS); i++) - { - CORE.Input.Touch.pointId[i] = CORE.Input.Touch.pointId[i+1]; - CORE.Input.Touch.position[i] = CORE.Input.Touch.position[i+1]; - } - - CORE.Input.Touch.pointCount--; - } - // When all touchpoints are tapped and released really quickly, this event is generated if (flags == AMOTION_EVENT_ACTION_CANCEL) CORE.Input.Touch.pointCount = 0; From 21e711b13f6de679bbc9df54ee0c40f6a28dd793 Mon Sep 17 00:00:00 2001 From: Colby Newman Date: Sun, 18 May 2025 19:35:21 -0400 Subject: [PATCH 003/242] Fix typo in mesh animNormals --- src/rmodels.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index d927f4099..e5af19bf5 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1433,7 +1433,7 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) else rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.vertices); rlEnableStatePointer(GL_TEXTURE_COORD_ARRAY, mesh.texcoords); - if (mesh.normals) rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.animNormalss); + if (mesh.normals) rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.animNormals); else rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.normals); rlEnableStatePointer(GL_COLOR_ARRAY, mesh.colors); From b6daa48a9cad5d1be62401131cf8390e4331bad5 Mon Sep 17 00:00:00 2001 From: Padmadev D <128023777+padmadevd@users.noreply.github.com> Date: Mon, 19 May 2025 15:09:58 +0530 Subject: [PATCH 004/242] Update rcore_android.c corrected coding conventions. --- src/platforms/rcore_android.c | 59 +++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 7f88b7363..03cb9741c 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -70,6 +70,16 @@ typedef struct { EGLConfig config; // Graphic config } PlatformData; +typedef struct { + // Store data for both Hover and Touch events + // Used to ignore Hover events which are interpreted as Touch events + int32_t pointCount; // Number of touch points active + int32_t pointId[MAX_TOUCH_POINTS]; // Point identifiers + Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen + + int32_t hoverPoints[MAX_TOUCH_POINTS]; // Hover Points +} TouchRaw; + //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- @@ -246,16 +256,7 @@ static const KeyboardKey mapKeycode[KEYCODE_MAP_SIZE] = { KEY_KP_EQUAL // AKEYCODE_NUMPAD_EQUALS }; -// Store data for both Hover and Touch events -// Used to ignore Hover events which are interpreted as Touch events -static struct { - - int32_t pointCount; // Number of touch points active - int32_t pointId[MAX_TOUCH_POINTS]; // Point identifiers - Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen - - int32_t hoverPoints[MAX_TOUCH_POINTS]; // Hover Points -}touchRaw; +static TouchRaw touchRaw = { 0 }; //---------------------------------------------------------------------------------- // Module Internal Functions Declaration @@ -812,10 +813,7 @@ int InitPlatform(void) } } - touchRaw.pointCount = 0; - for(int i = 0; i < MAX_TOUCH_POINTS; i++){ - touchRaw.hoverPoints[i] = -1; - } + for (int i = 0; i < MAX_TOUCH_POINTS; i++) touchRaw.hoverPoints[i] = -1; return 0; } @@ -1306,30 +1304,36 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - if(flags == AMOTION_EVENT_ACTION_HOVER_ENTER){ + if (flags == AMOTION_EVENT_ACTION_HOVER_ENTER) + { // The new pointer is hover // So add it to hoverPoints - for(int i = 0; i < MAX_TOUCH_POINTS; i++){ - if(touchRaw.hoverPoints[i] == -1){ + for (int i = 0; i < MAX_TOUCH_POINTS; i++) + { + if (touchRaw.hoverPoints[i] == -1) + { touchRaw.hoverPoints[i] = touchRaw.pointId[pointerIndex]; break; } } } - if (flags == AMOTION_EVENT_ACTION_POINTER_UP || flags == AMOTION_EVENT_ACTION_UP || flags == AMOTION_EVENT_ACTION_HOVER_EXIT) + if ((flags == AMOTION_EVENT_ACTION_POINTER_UP) || (flags == AMOTION_EVENT_ACTION_UP) || (flags == AMOTION_EVENT_ACTION_HOVER_EXIT)) { // One of the touchpoints is released, remove it from touch point arrays - if(flags == AMOTION_EVENT_ACTION_HOVER_EXIT){ + if (flags == AMOTION_EVENT_ACTION_HOVER_EXIT) + { // If the touchPoint is hover, remove it from hoverPoints - for(int i = 0; i < MAX_TOUCH_POINTS; i++){ - if(touchRaw.hoverPoints[i] == touchRaw.pointId[pointerIndex]){ + for (int i = 0; i < MAX_TOUCH_POINTS; i++) + { + if (touchRaw.hoverPoints[i] == touchRaw.pointId[pointerIndex]) + { touchRaw.hoverPoints[i] = -1; break; } } } - for (int i = pointerIndex; (i < touchRaw.pointCount - 1) && (i < MAX_TOUCH_POINTS-1); i++) + for (int i = pointerIndex; (i < touchRaw.pointCount - 1) && (i < MAX_TOUCH_POINTS - 1); i++) { touchRaw.pointId[i] = touchRaw.pointId[i+1]; touchRaw.position[i] = touchRaw.position[i+1]; @@ -1339,18 +1343,19 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) int pointCount = 0; for (int i = 0; (i < touchRaw.pointCount) && (i < MAX_TOUCH_POINTS); i++) - { + { // If the touchPoint is hover, Ignore it bool hover = false; - for(int j = 0; j < MAX_TOUCH_POINTS; j++){ + for (int j = 0; j < MAX_TOUCH_POINTS; j++) + { // Check if the touchPoint is in hoverPointers - if(touchRaw.hoverPoints[j] == touchRaw.pointId[i]){ + if (touchRaw.hoverPoints[j] == touchRaw.pointId[i]) + { hover = true; break; } } - if(hover) - continue; + if (hover) continue; CORE.Input.Touch.pointId[pointCount] = touchRaw.pointId[i]; CORE.Input.Touch.position[pointCount] = touchRaw.position[i]; From 21f0fe2a73eca20c3a9724149bf3f1cee29c2bca Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 21 May 2025 19:06:04 +0200 Subject: [PATCH 005/242] Removed some spaces --- src/rlgl.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index bdbdc9824..d595f4606 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1748,7 +1748,6 @@ void rlTextureParameters(unsigned int id, int param, int value) #endif } else glTexParameteri(GL_TEXTURE_2D, param, value); - } break; case RL_TEXTURE_MAG_FILTER: case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_2D, param, value); break; @@ -1793,7 +1792,6 @@ void rlCubemapParameters(unsigned int id, int param, int value) else TRACELOG(RL_LOG_WARNING, "GL: Clamp mirror wrap mode not supported (GL_MIRROR_CLAMP_EXT)"); } else glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value); - } break; case RL_TEXTURE_MAG_FILTER: case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value); break; @@ -2112,14 +2110,12 @@ void rlSetBlendMode(int mode) { // NOTE: Using GL blend src/dst factors and GL equation configured with rlSetBlendFactors() glBlendFunc(RLGL.State.glBlendSrcFactor, RLGL.State.glBlendDstFactor); glBlendEquation(RLGL.State.glBlendEquation); - } break; case RL_BLEND_CUSTOM_SEPARATE: { // NOTE: Using GL blend src/dst factors and GL equation configured with rlSetBlendFactorsSeparate() glBlendFuncSeparate(RLGL.State.glBlendSrcFactorRGB, RLGL.State.glBlendDestFactorRGB, RLGL.State.glBlendSrcFactorAlpha, RLGL.State.glBlendDestFactorAlpha); glBlendEquationSeparate(RLGL.State.glBlendEquationRGB, RLGL.State.glBlendEquationAlpha); - } break; default: break; } @@ -3747,19 +3743,16 @@ void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_TEXTURE_2D, texId, mipLevel); else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_RENDERBUFFER, texId); else if (texType >= RL_ATTACHMENT_CUBEMAP_POSITIVE_X) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_TEXTURE_CUBE_MAP_POSITIVE_X + texType, texId, mipLevel); - } break; case RL_ATTACHMENT_DEPTH: { if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texId, mipLevel); else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, texId); - } break; case RL_ATTACHMENT_STENCIL: { if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texId, mipLevel); else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, texId); - } break; default: break; } From afb52b19a40dde040e4b01624017f2ead54d3c6d Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 22 May 2025 17:06:55 +0200 Subject: [PATCH 006/242] WARNING: REDESIGNED: `EncodeDataBase64()`, NULL terminated string returned Note that returned output size considers the NULL terminator as an additional byte. --- src/raylib.h | 2 +- src/rcore.c | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 0cffa4272..73cf931be 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1153,7 +1153,7 @@ RLAPI long GetFileModTime(const char *fileName); // Get file mo // Compression/Encoding functionality RLAPI unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize); // Compress data (DEFLATE algorithm), memory must be MemFree() RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // Decompress data (DEFLATE algorithm), memory must be MemFree() -RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string, memory must be MemFree() +RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string (includes NULL terminator), memory must be MemFree() RLAPI unsigned char *DecodeDataBase64(const char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree() RLAPI unsigned int ComputeCRC32(unsigned char *data, int dataSize); // Compute CRC32 hash code RLAPI unsigned int *ComputeMD5(unsigned char *data, int dataSize); // Compute MD5 hash code, returns static int[4] (16 bytes) diff --git a/src/rcore.c b/src/rcore.c index 207e112a4..8d2142a05 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2539,6 +2539,7 @@ unsigned char *DecompressData(const unsigned char *compData, int compDataSize, i } // Encode data to Base64 string +// NOTE: Returned string includes NULL terminator, considered on outputSize char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize) { static const unsigned char base64encodeTable[] = { @@ -2549,7 +2550,7 @@ char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize) static const int modTable[] = { 0, 2, 1 }; - *outputSize = 4*((dataSize + 2)/3); + *outputSize = 4*((dataSize + 2)/3) + 1; // Adding +1 for NULL terminator char *encodedData = (char *)RL_MALLOC(*outputSize); @@ -2571,6 +2572,8 @@ char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize) for (int i = 0; i < modTable[dataSize%3]; i++) encodedData[*outputSize - 1 - i] = '='; // Padding character + encodedData[*outputSize - 1] = '\0'; + return encodedData; } From 8d9c1cecb7f53aef720e2ee0d1558ffc39fa7eef Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 22 May 2025 15:07:11 +0000 Subject: [PATCH 007/242] Update raylib_api.* by CI --- parser/output/raylib_api.json | 2 +- parser/output/raylib_api.lua | 2 +- parser/output/raylib_api.txt | 2 +- parser/output/raylib_api.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index e73e18fba..c9b314207 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -4684,7 +4684,7 @@ }, { "name": "EncodeDataBase64", - "description": "Encode data to Base64 string, memory must be MemFree()", + "description": "Encode data to Base64 string (includes NULL terminator), memory must be MemFree()", "returnType": "char *", "params": [ { diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index fa92cdea7..18ef44303 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -4198,7 +4198,7 @@ return { }, { name = "EncodeDataBase64", - description = "Encode data to Base64 string, memory must be MemFree()", + description = "Encode data to Base64 string (includes NULL terminator), memory must be MemFree()", returnType = "char *", params = { {type = "const unsigned char *", name = "data"}, diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index 4aa682d78..dd7b21c86 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -1787,7 +1787,7 @@ Function 147: DecompressData() (3 input parameters) Function 148: EncodeDataBase64() (3 input parameters) Name: EncodeDataBase64 Return type: char * - Description: Encode data to Base64 string, memory must be MemFree() + Description: Encode data to Base64 string (includes NULL terminator), memory must be MemFree() Param[1]: data (type: const unsigned char *) Param[2]: dataSize (type: int) Param[3]: outputSize (type: int *) diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index c88a20bd1..18ef07eb2 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -1123,7 +1123,7 @@ - + From 5ddd13b775fa3622f63917bc856ed1d3d35a9ef7 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 28 May 2025 17:18:02 +0200 Subject: [PATCH 008/242] REVIEWED: Hexadecimal formatting to be consistent --- src/rcore.c | 64 ++++++++++++++++++++++++------------------------- src/rmodels.c | 6 ++--- src/rtext.c | 2 +- src/rtextures.c | 26 ++++++++++---------- 4 files changed, 48 insertions(+), 50 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 8d2142a05..bd0e8399b 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2639,38 +2639,38 @@ unsigned char *DecodeDataBase64(const char *data, int *outputSize) unsigned int ComputeCRC32(unsigned char *data, int dataSize) { static unsigned int crcTable[256] = { - 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, - 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, - 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, - 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, - 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, - 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, - 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, - 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, - 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, - 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, - 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, - 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, - 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, - 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, - 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, - 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, - 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, - 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, - 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, - 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, - 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, - 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, - 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, - 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, - 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, - 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, - 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, - 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, - 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, - 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, - 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, - 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; unsigned int crc = ~0u; diff --git a/src/rmodels.c b/src/rmodels.c index e5af19bf5..5f3fd8993 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -6623,11 +6623,11 @@ static Model LoadM3D(const char *fileName) // Without vertex color (full transparency), we use the default color if (model.meshes[k].colors != NULL) { - if (m3d->vertex[m3d->face[i].vertex[0]].color & 0xFF000000) + if (m3d->vertex[m3d->face[i].vertex[0]].color & 0xff000000) memcpy(&model.meshes[k].colors[l*12 + 0], &m3d->vertex[m3d->face[i].vertex[0]].color, 4); - if (m3d->vertex[m3d->face[i].vertex[1]].color & 0xFF000000) + if (m3d->vertex[m3d->face[i].vertex[1]].color & 0xff000000) memcpy(&model.meshes[k].colors[l*12 + 4], &m3d->vertex[m3d->face[i].vertex[1]].color, 4); - if (m3d->vertex[m3d->face[i].vertex[2]].color & 0xFF000000) + if (m3d->vertex[m3d->face[i].vertex[2]].color & 0xff000000) memcpy(&model.meshes[k].colors[l*12 + 8], &m3d->vertex[m3d->face[i].vertex[2]].color, 4); } diff --git a/src/rtext.c b/src/rtext.c index 70dcfc38c..ee498b119 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -253,7 +253,7 @@ extern void LoadFontDefault(void) } else { - ((unsigned char *)imFont.data)[(i + j)*sizeof(short)] = 0xFF; + ((unsigned char *)imFont.data)[(i + j)*sizeof(short)] = 0xff; ((unsigned char *)imFont.data)[(i + j)*sizeof(short) + 1] = 0x00; } } diff --git a/src/rtextures.c b/src/rtextures.c index 2f5aaa871..7a404a6de 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -5148,10 +5148,10 @@ Color GetColor(unsigned int hexValue) { Color color; - color.r = (unsigned char)(hexValue >> 24) & 0xFF; - color.g = (unsigned char)(hexValue >> 16) & 0xFF; - color.b = (unsigned char)(hexValue >> 8) & 0xFF; - color.a = (unsigned char)hexValue & 0xFF; + color.r = (unsigned char)(hexValue >> 24) & 0xff; + color.g = (unsigned char)(hexValue >> 16) & 0xff; + color.b = (unsigned char)(hexValue >> 8) & 0xff; + color.a = (unsigned char)hexValue & 0xff; return color; } @@ -5391,17 +5391,16 @@ static float HalfToFloat(unsigned short x) { float result = 0.0f; - union - { + union { float fm; unsigned int ui; } uni; - const unsigned int e = (x & 0x7C00) >> 10; // Exponent - const unsigned int m = (x & 0x03FF) << 13; // Mantissa + const unsigned int e = (x & 0x7c00) >> 10; // Exponent + const unsigned int m = (x & 0x03cc) << 13; // Mantissa uni.fm = (float)m; const unsigned int v = uni.ui >> 23; // Evil log2 bit hack to count leading zeros in denormalized format - uni.ui = (x & 0x8000) << 16 | (e != 0)*((e + 112) << 23 | m) | ((e == 0)&(m != 0))*((v - 37) << 23 | ((m << (150 - v)) & 0x007FE000)); // sign : normalized : denormalized + uni.ui = (x & 0x8000) << 16 | (e != 0)*((e + 112) << 23 | m) | ((e == 0)&(m != 0))*((v - 37) << 23 | ((m << (150 - v)) & 0x007fe000)); // sign : normalized : denormalized result = uni.fm; @@ -5413,18 +5412,17 @@ static unsigned short FloatToHalf(float x) { unsigned short result = 0; - union - { + union { float fm; unsigned int ui; } uni; uni.fm = x; const unsigned int b = uni.ui + 0x00001000; // Round-to-nearest-even: add last bit after truncated mantissa - const unsigned int e = (b & 0x7F800000) >> 23; // Exponent - const unsigned int m = b & 0x007FFFFF; // Mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding + const unsigned int e = (b & 0x7f800000) >> 23; // Exponent + const unsigned int m = b & 0x007fffff; // Mantissa; in line below: 0x007ff000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding - result = (b & 0x80000000) >> 16 | (e > 112)*((((e - 112) << 10) & 0x7C00) | m >> 13) | ((e < 113) & (e > 101))*((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) | (e > 143)*0x7FFF; // sign : normalized : denormalized : saturate + result = (b & 0x80000000) >> 16 | (e > 112)*((((e - 112) << 10) & 0x7c00) | m >> 13) | ((e < 113) & (e > 101))*((((0x007ff000 + m) >> (125 - e)) + 1) >> 1) | (e > 143)*0x7fff; // sign : normalized : denormalized : saturate return result; } From d7148f5f9d7a6f091adcc0d6444ef3f2c1040007 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 28 May 2025 17:19:19 +0200 Subject: [PATCH 009/242] REDESIGNED: Base64 encoding/decoding functions Found some issues with output size when padding required, just re-implemented both functions from scratch. --- src/raylib.h | 2 +- src/rcore.c | 151 ++++++++++++++++++++++++++++----------------------- 2 files changed, 83 insertions(+), 70 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 73cf931be..563156525 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1154,7 +1154,7 @@ RLAPI long GetFileModTime(const char *fileName); // Get file mo RLAPI unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize); // Compress data (DEFLATE algorithm), memory must be MemFree() RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // Decompress data (DEFLATE algorithm), memory must be MemFree() RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string (includes NULL terminator), memory must be MemFree() -RLAPI unsigned char *DecodeDataBase64(const char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree() +RLAPI unsigned char *DecodeDataBase64(const char *text, int *outputSize); // Decode Base64 string (expected NULL terminated), memory must be MemFree() RLAPI unsigned int ComputeCRC32(unsigned char *data, int dataSize); // Compute CRC32 hash code RLAPI unsigned int *ComputeMD5(unsigned char *data, int dataSize); // Compute MD5 hash code, returns static int[4] (16 bytes) RLAPI unsigned int *ComputeSHA1(unsigned char *data, int dataSize); // Compute SHA1 hash code, returns static int[5] (20 bytes) diff --git a/src/rcore.c b/src/rcore.c index bd0e8399b..5f81b7099 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2542,96 +2542,109 @@ unsigned char *DecompressData(const unsigned char *compData, int compDataSize, i // NOTE: Returned string includes NULL terminator, considered on outputSize char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize) { - static const unsigned char base64encodeTable[] = { - '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', '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', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' - }; + // Base64 conversion table from RFC 4648 [0..63] + // NOTE: They represent 64 values (6 bits), to encode 3 bytes of data into 4 "sixtets" (6bit characters) + static const char base64EncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - static const int modTable[] = { 0, 2, 1 }; + // Compute expected size and padding + int paddedSize = dataSize; + while (paddedSize%3 != 0) paddedSize++; // Padding bytes to round 4*(dataSize/3) to 4 bytes + int estimatedOutputSize = 4*(paddedSize/3); + int padding = paddedSize - dataSize; - *outputSize = 4*((dataSize + 2)/3) + 1; // Adding +1 for NULL terminator + // Adding null terminator to string + estimatedOutputSize += 1; - char *encodedData = (char *)RL_MALLOC(*outputSize); + // Load some memory to store encoded string + char *encodedData = (char *)RL_CALLOC(estimatedOutputSize, 1); + if (encodedData == NULL) return NULL; - if (encodedData == NULL) return NULL; // Security check - - for (int i = 0, j = 0; i < dataSize;) + int outputCount = 0; + for (int i = 0; i < dataSize;) { - unsigned int octetA = (i < dataSize)? (unsigned char)data[i++] : 0; - unsigned int octetB = (i < dataSize)? (unsigned char)data[i++] : 0; - unsigned int octetC = (i < dataSize)? (unsigned char)data[i++] : 0; + unsigned int octetA = 0; + unsigned int octetB = 0; + unsigned int octetC = 0; + unsigned int octetPack = 0; - unsigned int triple = (octetA << 0x10) + (octetB << 0x08) + octetC; + octetA = data[i]; // Generates 2 sextets + octetB = ((i + 1) < dataSize)? data[i + 1] : 0; // Generates 3 sextets + octetC = ((i + 2) < dataSize)? data[i + 2] : 0; // Generates 4 sextets - encodedData[j++] = base64encodeTable[(triple >> 3*6) & 0x3F]; - encodedData[j++] = base64encodeTable[(triple >> 2*6) & 0x3F]; - encodedData[j++] = base64encodeTable[(triple >> 1*6) & 0x3F]; - encodedData[j++] = base64encodeTable[(triple >> 0*6) & 0x3F]; + octetPack = (octetA << 16) | (octetB << 8) | octetC; + + encodedData[outputCount + 0] = (unsigned char)(base64EncodeTable[(octetPack >> 18) & 0x3f]); + encodedData[outputCount + 1] = (unsigned char)(base64EncodeTable[(octetPack >> 12) & 0x3f]); + encodedData[outputCount + 2] = (unsigned char)(base64EncodeTable[(octetPack >> 6) & 0x3f]); + encodedData[outputCount + 3] = (unsigned char)(base64EncodeTable[octetPack & 0x3f]); + outputCount += 4; + i += 3; } + + // Add required padding bytes + for (int p = 0; p < padding; p++) encodedData[outputCount - p - 1] = '='; - for (int i = 0; i < modTable[dataSize%3]; i++) encodedData[*outputSize - 1 - i] = '='; // Padding character + // Add null terminator to string + encodedData[outputCount] = '\0'; + outputCount++; - encodedData[*outputSize - 1] = '\0'; + if (outputCount != estimatedOutputSize) TRACELOG(LOG_WARNING, "BASE64: Output size differs from estimation"); + *outputSize = estimatedOutputSize; return encodedData; } -// Decode Base64 string data -unsigned char *DecodeDataBase64(const char *data, int *outputSize) +// Decode Base64 string (expected NULL terminated) +unsigned char *DecodeDataBase64(const char *text, int *outputSize) { - static const unsigned char base64decodeTable[] = { - 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 + // Base64 decode table + // NOTE: Following ASCII order [0..255] assigning the expected sixtet value to + // every character in the corresponding ASCII position + static const unsigned char base64DecodeTable[256] = { + ['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, ['E'] = 4, ['F'] = 5, ['G'] = 6, ['H'] = 7, + ['I'] = 8, ['J'] = 9, ['K'] = 10, ['L'] = 11, ['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15, + ['Q'] = 16, ['R'] = 17, ['S'] = 18, ['T'] = 19, ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23, ['Y'] = 24, ['Z'] = 25, + ['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29, ['e'] = 30, ['f'] = 31, ['g'] = 32, ['h'] = 33, + ['i'] = 34, ['j'] = 35, ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39, ['o'] = 40, ['p'] = 41, + ['q'] = 42, ['r'] = 43, ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47, ['w'] = 48, ['x'] = 49, ['y'] = 50, ['z'] = 51, + ['0'] = 52, ['1'] = 53, ['2'] = 54, ['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59, + ['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63 }; - // Get output size of Base64 input data - int outSize = 0; - for (int i = 0; data[4*i] != 0; i++) + // Compute expected size and padding + int dataSize = (int)strlen(text); // WARNING: Expecting NULL terminated strings! + int ending = dataSize - 1; + int padding = 0; + while (text[ending] == '=') { padding++; ending--; } + int estimatedOutputSize = 3*(dataSize/4) - padding; + int maxOutputSize = 3*(dataSize/4); + + // Load some memory to store decoded data + // NOTE: Allocated enough size to include padding + unsigned char *decodedData = (unsigned char *)RL_CALLOC(maxOutputSize, 1); + if (decodedData == NULL) return NULL; + + int outputCount = 0; + for (int i = 0, j = 0; i < dataSize;) { - if (data[4*i + 3] == '=') - { - if (data[4*i + 2] == '=') outSize += 1; - else outSize += 2; - } - else outSize += 3; + // Every 4 sixtets must generate 3 octets + unsigned int sixtetA = base64DecodeTable[(unsigned char)text[i]]; + unsigned int sixtetB = base64DecodeTable[(unsigned char)text[i + 1]]; + unsigned int sixtetC = ((unsigned char)text[i + 2] != '=')? base64DecodeTable[(unsigned char)text[i + 2]] : 0; + unsigned int sixtetD = ((unsigned char)text[i + 3] != '=')? base64DecodeTable[(unsigned char)text[i + 3]] : 0; + + unsigned int octetPack = (sixtetA << 18) | (sixtetB << 12) | (sixtetC << 6) | sixtetD; + + decodedData[outputCount + 0] = (octetPack >> 16) & 0xff; + decodedData[outputCount + 1] = (octetPack >> 8) & 0xff; + decodedData[outputCount + 2] = octetPack & 0xff; + outputCount += 3; + i += 4; } - // Allocate memory to store decoded Base64 data - unsigned char *decodedData = (unsigned char *)RL_MALLOC(outSize); + if (estimatedOutputSize != (outputCount - padding)) TRACELOG(LOG_WARNING, "BASE64: Decoded size differs from estimation"); - for (int i = 0; i < outSize/3; i++) - { - unsigned char a = base64decodeTable[(int)data[4*i]]; - unsigned char b = base64decodeTable[(int)data[4*i + 1]]; - unsigned char c = base64decodeTable[(int)data[4*i + 2]]; - unsigned char d = base64decodeTable[(int)data[4*i + 3]]; - - decodedData[3*i] = (a << 2) | (b >> 4); - decodedData[3*i + 1] = (b << 4) | (c >> 2); - decodedData[3*i + 2] = (c << 6) | d; - } - - if (outSize%3 == 1) - { - int n = outSize/3; - unsigned char a = base64decodeTable[(int)data[4*n]]; - unsigned char b = base64decodeTable[(int)data[4*n + 1]]; - decodedData[outSize - 1] = (a << 2) | (b >> 4); - } - else if (outSize%3 == 2) - { - int n = outSize/3; - unsigned char a = base64decodeTable[(int)data[4*n]]; - unsigned char b = base64decodeTable[(int)data[4*n + 1]]; - unsigned char c = base64decodeTable[(int)data[4*n + 2]]; - decodedData[outSize - 2] = (a << 2) | (b >> 4); - decodedData[outSize - 1] = (b << 4) | (c >> 2); - } - - *outputSize = outSize; + *outputSize = estimatedOutputSize; return decodedData; } From 2d952d8e94f7b91cbc4be875e67a192055f9d05f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 28 May 2025 15:19:32 +0000 Subject: [PATCH 010/242] Update raylib_api.* by CI --- parser/output/raylib_api.json | 4 ++-- parser/output/raylib_api.lua | 4 ++-- parser/output/raylib_api.txt | 4 ++-- parser/output/raylib_api.xml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index c9b314207..72a3477a4 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -4703,12 +4703,12 @@ }, { "name": "DecodeDataBase64", - "description": "Decode Base64 string data, memory must be MemFree()", + "description": "Decode Base64 string (expected NULL terminated), memory must be MemFree()", "returnType": "unsigned char *", "params": [ { "type": "const char *", - "name": "data" + "name": "text" }, { "type": "int *", diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index 18ef44303..662d45a78 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -4208,10 +4208,10 @@ return { }, { name = "DecodeDataBase64", - description = "Decode Base64 string data, memory must be MemFree()", + description = "Decode Base64 string (expected NULL terminated), memory must be MemFree()", returnType = "unsigned char *", params = { - {type = "const char *", name = "data"}, + {type = "const char *", name = "text"}, {type = "int *", name = "outputSize"} } }, diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index dd7b21c86..c399c5291 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -1794,8 +1794,8 @@ Function 148: EncodeDataBase64() (3 input parameters) Function 149: DecodeDataBase64() (2 input parameters) Name: DecodeDataBase64 Return type: unsigned char * - Description: Decode Base64 string data, memory must be MemFree() - Param[1]: data (type: const char *) + Description: Decode Base64 string (expected NULL terminated), memory must be MemFree() + Param[1]: text (type: const char *) Param[2]: outputSize (type: int *) Function 150: ComputeCRC32() (2 input parameters) Name: ComputeCRC32 diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index 18ef07eb2..511d5d121 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -1128,8 +1128,8 @@ - - + + From f7fe8b88cbed5bba641588f5750f83ffca0d271d Mon Sep 17 00:00:00 2001 From: Moros Smith Date: Wed, 28 May 2025 17:46:32 -0400 Subject: [PATCH 011/242] add EmscriptenKeyboardCallback to consume key events --- src/platforms/rcore_web.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index bf5a6ba5a..1c61052a9 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -137,6 +137,7 @@ static EM_BOOL EmscriptenFocusCallback(int eventType, const EmscriptenFocusEvent static EM_BOOL EmscriptenVisibilityChangeCallback(int eventType, const EmscriptenVisibilityChangeEvent *visibilityChangeEvent, void *userData); // Emscripten input callback events +static EM_BOOL EmscriptenKeyboardCallback(int eventType, const EmscriptenKeyboardEvent *keyboardEvent, void *userData); static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); static EM_BOOL EmscriptenPointerlockCallback(int eventType, const EmscriptenPointerlockChangeEvent *pointerlockChangeEvent, void *userData); @@ -1362,9 +1363,11 @@ int InitPlatform(void) // Trigger this once to get initial window sizing EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); - // Support keyboard events -> Not used, GLFW.JS takes care of that - // emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); - // emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); + // Support keyboard events + // NOTE: used only to consume keyboard events. GLFW.JS takes care of + // the actual input. + emscripten_set_keypress_callback(GetCanvasId(), NULL, 1, EmscriptenKeyboardCallback); + emscripten_set_keydown_callback(GetCanvasId(), NULL, 1, EmscriptenKeyboardCallback); // Support mouse events emscripten_set_click_callback(GetCanvasId(), NULL, 1, EmscriptenMouseCallback); @@ -1620,6 +1623,14 @@ static void MouseCursorPosCallback(GLFWwindow *window, double x, double y) #endif } +static EM_BOOL EmscriptenKeyboardCallback(int eventType, const EmscriptenKeyboardEvent *keyboardEvent, void *userData) +{ + // NOTE: handled by GLFW, this is only to consume the keyboard events so we + // make use of F-keys and other shortcuts without triggering browser + // functions. + return 1; // The event was consumed by the callback handler +} + static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) { // To emulate the GLFW_RAW_MOUSE_MOTION property. From 299f5350a4c07518a9bd52d6f98e5479d7a24bc7 Mon Sep 17 00:00:00 2001 From: M374LX Date: Wed, 28 May 2025 19:49:57 -0300 Subject: [PATCH 012/242] Remove unused variable --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 5f81b7099..cccd2310e 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2625,7 +2625,7 @@ unsigned char *DecodeDataBase64(const char *text, int *outputSize) if (decodedData == NULL) return NULL; int outputCount = 0; - for (int i = 0, j = 0; i < dataSize;) + for (int i = 0; i < dataSize;) { // Every 4 sixtets must generate 3 octets unsigned int sixtetA = base64DecodeTable[(unsigned char)text[i]]; From c0cf57f8f02faa9403f5a9ad333ef19fabc6b44f Mon Sep 17 00:00:00 2001 From: M374LX Date: Wed, 28 May 2025 22:38:16 -0300 Subject: [PATCH 013/242] RGFW backend: add missing Right Control key --- src/platforms/rcore_desktop_rgfw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 92e3def05..1c4f606a7 100644 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -175,6 +175,7 @@ static const unsigned short keyMappingRGFW[] = { [RGFW_superL] = KEY_LEFT_SUPER, #ifndef RGFW_MACOS [RGFW_shiftR] = KEY_RIGHT_SHIFT, + [RGFW_controlR] = KEY_RIGHT_CONTROL, [RGFW_altR] = KEY_RIGHT_ALT, #endif [RGFW_space] = KEY_SPACE, From 341bfb22cc80fae266c8d5f29c5fed272abc3d32 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 29 May 2025 12:25:00 +0200 Subject: [PATCH 014/242] REVIEWED: `MAX_GAMEPAD_AXEX` for consistency #4960 --- src/config.h | 2 +- src/rcore.c | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/config.h b/src/config.h index ca54fa8cd..58f092ed1 100644 --- a/src/config.h +++ b/src/config.h @@ -104,7 +104,7 @@ #define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported #define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported #define MAX_GAMEPADS 4 // Maximum number of gamepads supported -#define MAX_GAMEPAD_AXIS 8 // Maximum number of axis supported (per gamepad) +#define MAX_GAMEPAD_AXES 8 // Maximum number of axis supported (per gamepad) #define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad) #define MAX_GAMEPAD_VIBRATION_TIME 2.0f // Maximum vibration time in seconds #define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported diff --git a/src/rcore.c b/src/rcore.c index cccd2310e..bfa0cc036 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -237,8 +237,8 @@ __declspec(dllimport) unsigned int __stdcall timeEndPeriod(unsigned int uPeriod) #ifndef MAX_GAMEPAD_NAME_LENGTH #define MAX_GAMEPAD_NAME_LENGTH 128 // Maximum number of characters of gamepad name (byte size) #endif -#ifndef MAX_GAMEPAD_AXIS - #define MAX_GAMEPAD_AXIS 8 // Maximum number of axis supported (per gamepad) +#ifndef MAX_GAMEPAD_AXES + #define MAX_GAMEPAD_AXES 8 // Maximum number of axis supported (per gamepad) #endif #ifndef MAX_GAMEPAD_BUTTONS #define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad) @@ -359,7 +359,7 @@ typedef struct CoreData { char name[MAX_GAMEPADS][MAX_GAMEPAD_NAME_LENGTH]; // Gamepad name holder char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state - float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state + float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXES]; // Gamepad axes state } Gamepad; } Input; @@ -3368,11 +3368,11 @@ int GetGamepadAxisCount(int gamepad) // Get axis movement vector for a gamepad float GetGamepadAxisMovement(int gamepad, int axis) { - float value = (axis == GAMEPAD_AXIS_LEFT_TRIGGER || axis == GAMEPAD_AXIS_RIGHT_TRIGGER)? -1.0f : 0.0f; + 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)) + if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXES)) { - float movement = value < 0.0f ? CORE.Input.Gamepad.axisState[gamepad][axis] : fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]); + float movement = (value < 0.0f)? CORE.Input.Gamepad.axisState[gamepad][axis] : fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]); if (movement > value) value = CORE.Input.Gamepad.axisState[gamepad][axis]; } @@ -4050,10 +4050,10 @@ static void RecordAutomationEvent(void) if (currentEventList->count == currentEventList->capacity) return; // Security check } - for (int axis = 0; axis < MAX_GAMEPAD_AXIS; axis++) + for (int axis = 0; axis < MAX_GAMEPAD_AXES; axis++) { // Event type: INPUT_GAMEPAD_AXIS_MOTION - float defaultMovement = (axis == GAMEPAD_AXIS_LEFT_TRIGGER || axis == GAMEPAD_AXIS_RIGHT_TRIGGER)? -1.0f : 0.0f; + float defaultMovement = ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER))? -1.0f : 0.0f; if (GetGamepadAxisMovement(gamepad, axis) != defaultMovement) { currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; From 913c236487712ea6281b96990c14dea08979e727 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 29 May 2025 12:51:08 +0200 Subject: [PATCH 015/242] REVIEWED: `MAX_GAMEPAD_AXES` --- src/platforms/rcore_drm.c | 4 ++-- src/platforms/rcore_web.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 74af3d453..ac56e0f9e 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -124,7 +124,7 @@ typedef struct { // Gamepad data int gamepadStreamFd[MAX_GAMEPADS]; // Gamepad device file descriptor - int gamepadAbsAxisRange[MAX_GAMEPADS][MAX_GAMEPAD_AXIS][2]; // [0] = min, [1] = range value of the axis + int gamepadAbsAxisRange[MAX_GAMEPADS][MAX_GAMEPAD_AXES][2]; // [0] = min, [1] = range value of the axis int gamepadAbsAxisMap[MAX_GAMEPADS][ABS_CNT]; // Maps the axes gamepads from the evdev api to a sequential one int gamepadCount; // The number of gamepads registered } PlatformData; @@ -1681,7 +1681,7 @@ static void PollGamepadEvents(void) TRACELOG(LOG_DEBUG, "INPUT: Gamepad %2i: Axis: %2i Value: %i", i, axisRaylib, event.value); - if (axisRaylib < MAX_GAMEPAD_AXIS) + if (axisRaylib < MAX_GAMEPAD_AXES) { int min = platform.gamepadAbsAxisRange[i][event.code][0]; int range = platform.gamepadAbsAxisRange[i][event.code][1]; diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index bf5a6ba5a..f83e3159e 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -1081,7 +1081,7 @@ void PollInputEvents(void) } // Register axis data for every connected gamepad - for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXIS); j++) + for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXES); j++) { CORE.Input.Gamepad.axisState[i][j] = gamepadState.axis[j]; } From 6d5aedbd3816ae9fa2309a7f3835f7e9a2a469e1 Mon Sep 17 00:00:00 2001 From: Meowster <142757105+meowstr@users.noreply.github.com> Date: Thu, 29 May 2025 07:05:49 -0400 Subject: [PATCH 016/242] Add DrawEllipseV and DrawEllipseLinesV --- examples/shapes/shapes_basic_shapes.c | 2 ++ src/raylib.h | 2 ++ src/rshapes.c | 22 +++++++++++++++++----- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/examples/shapes/shapes_basic_shapes.c b/examples/shapes/shapes_basic_shapes.c index bd1688453..0302a2ef5 100644 --- a/examples/shapes/shapes_basic_shapes.c +++ b/examples/shapes/shapes_basic_shapes.c @@ -52,6 +52,8 @@ int main(void) DrawCircle(screenWidth/5, 120, 35, DARKBLUE); DrawCircleGradient(screenWidth/5, 220, 60, GREEN, SKYBLUE); DrawCircleLines(screenWidth/5, 340, 80, DARKBLUE); + DrawEllipse(screenWidth/5, 120, 25, 20, YELLOW); + DrawEllipseLines(screenWidth/5, 120, 30, 25, YELLOW); // Rectangle shapes and lines DrawRectangle(screenWidth/4*2 - 60, 100, 120, 60, RED); diff --git a/src/raylib.h b/src/raylib.h index 563156525..c2f2c4797 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1264,7 +1264,9 @@ RLAPI void DrawCircleV(Vector2 center, float radius, Color color); 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) RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse +RLAPI void DrawEllipseV(Vector2 center, float radiusH, float radiusV, Color color); // Draw ellipse (Vector version) RLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse outline +RLAPI void DrawEllipseLinesV(Vector2 center, float radiusH, float radiusV, Color color); // Draw ellipse outline (Vector version) RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring RLAPI void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring outline RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle diff --git a/src/rshapes.c b/src/rshapes.c index c739f4162..7fa3223cf 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -470,27 +470,39 @@ void DrawCircleLinesV(Vector2 center, float radius, Color color) // Draw ellipse void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color) +{ + DrawEllipseV((Vector2){ (float)centerX, (float)centerY }, radiusH, radiusV, color); +} + +// Draw ellipse (Vector version) +void DrawEllipseV(Vector2 center, float radiusH, float radiusV, Color color) { rlBegin(RL_TRIANGLES); for (int i = 0; i < 360; i += 10) { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f((float)centerX, (float)centerY); - rlVertex2f((float)centerX + cosf(DEG2RAD*(i + 10))*radiusH, (float)centerY + sinf(DEG2RAD*(i + 10))*radiusV); - rlVertex2f((float)centerX + cosf(DEG2RAD*i)*radiusH, (float)centerY + sinf(DEG2RAD*i)*radiusV); + rlVertex2f(center.x, center.y); + rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radiusH, center.y + sinf(DEG2RAD*(i + 10))*radiusV); + rlVertex2f(center.x + cosf(DEG2RAD*i)*radiusH, center.y + sinf(DEG2RAD*i)*radiusV); } rlEnd(); } // Draw ellipse outline void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color) +{ + DrawEllipseLinesV((Vector2){ (float)centerX, (float)centerY }, radiusH, radiusV, color); +} + +// Draw ellipse outline +void DrawEllipseLinesV(Vector2 center, float radiusH, float radiusV, Color color) { rlBegin(RL_LINES); for (int i = 0; i < 360; i += 10) { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(centerX + cosf(DEG2RAD*(i + 10))*radiusH, centerY + sinf(DEG2RAD*(i + 10))*radiusV); - rlVertex2f(centerX + cosf(DEG2RAD*i)*radiusH, centerY + sinf(DEG2RAD*i)*radiusV); + rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radiusH, center.y + sinf(DEG2RAD*(i + 10))*radiusV); + rlVertex2f(center.x + cosf(DEG2RAD*i)*radiusH, center.y + sinf(DEG2RAD*i)*radiusV); } rlEnd(); } From 16f398b464acdf5b44e873a141d89bb8262fb4e5 Mon Sep 17 00:00:00 2001 From: M374LX Date: Thu, 29 May 2025 15:01:01 -0300 Subject: [PATCH 017/242] Update comment (gamepad axes) --- src/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.h b/src/config.h index 58f092ed1..dc6cc5893 100644 --- a/src/config.h +++ b/src/config.h @@ -104,7 +104,7 @@ #define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported #define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported #define MAX_GAMEPADS 4 // Maximum number of gamepads supported -#define MAX_GAMEPAD_AXES 8 // Maximum number of axis supported (per gamepad) +#define MAX_GAMEPAD_AXES 8 // Maximum number of axes supported (per gamepad) #define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad) #define MAX_GAMEPAD_VIBRATION_TIME 2.0f // Maximum vibration time in seconds #define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported From a9525bfbc26f9b94ad69b0202a0e198f828cf384 Mon Sep 17 00:00:00 2001 From: M374LX Date: Thu, 29 May 2025 16:03:29 -0300 Subject: [PATCH 018/242] Update RGFW to 1.7 --- src/external/RGFW.h | 20938 ++++++++++++++------------- src/platforms/rcore_desktop_rgfw.c | 20 +- 2 files changed, 10627 insertions(+), 10331 deletions(-) diff --git a/src/external/RGFW.h b/src/external/RGFW.h index 40349bd98..f121ef630 100644 --- a/src/external/RGFW.h +++ b/src/external/RGFW.h @@ -1,10321 +1,10617 @@ -/* -* -* RGFW 1.6.5-dev -* -* Copyright (C) 2022-25 ColleagueRiley -* -* libpng license -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -* -* -*/ - -/* - (MAKE SURE RGFW_IMPLEMENTATION is in exactly one header or you use -D RGFW_IMPLEMENTATION) - #define RGFW_IMPLEMENTATION - makes it so source code is included with header -*/ - -/* - #define RGFW_IMPLEMENTATION - (required) makes it so the source code is included - #define RGFW_DEBUG - (optional) makes it so RGFW prints debug messages and errors when they're found - #define RGFW_OSMESA - (optional) use OSmesa as backend (instead of system's opengl api + regular opengl) - #define RGFW_BUFFER - (optional) just draw directly to (RGFW) window pixel buffer that is drawn to screen (the buffer is in the RGBA format) - #define RGFW_EGL - (optional) use EGL for loading an OpenGL context (instead of the system's opengl api) - #define RGFW_OPENGL_ES1 - (optional) use EGL to load and use Opengl ES (version 1) for backend rendering (instead of the system's opengl api) - This version doesn't work for desktops (I'm pretty sure) - #define RGFW_OPENGL_ES2 - (optional) use OpenGL ES (version 2) - #define RGFW_OPENGL_ES3 - (optional) use OpenGL ES (version 3) - #define RGFW_DIRECTX - (optional) use directX for the rendering backend (rather than opengl) (windows only, defaults to opengl for unix) - #define RGFW_WEBGPU - (optional) use webGPU for rendering (Web ONLY) - #define RGFW_NO_API - (optional) don't use any rendering API (no opengl, no vulkan, no directX) - - #define RGFW_LINK_EGL (optional) (windows only) if EGL is being used, if EGL functions should be defined dymanically (using GetProcAddress) - #define RGFW_X11 (optional) (unix only) if X11 should be used. This option is turned on by default by unix systems except for MacOS - #define RGFW_WAYLAND (optional) (unix only) use Wayland. (This can be used with X11) - #define RGFW_NO_X11 (optional) (unix only) don't fallback to X11 when using Wayland - #define RGFW_NO_LOAD_WGL (optional) (windows only) if WGL should be loaded dynamically during runtime - #define RGFW_NO_X11_CURSOR (optional) (unix only) don't use XCursor - #define RGFW_NO_X11_CURSOR_PRELOAD (optional) (unix only) use XCursor, but don't link it in code, (you'll have to link it with -lXcursor) - #define RGFW_NO_LOAD_WINMM (optional) (windows only) use winmm (timeBeginPeriod), but don't link it in code, (you'll have to link it with -lwinmm) - #define RGFW_NO_WINMM (optional) (windows only) don't use winmm - #define RGFW_NO_IOKIT (optional) (macOS) don't use IOKit - #define RGFW_NO_UNIX_CLOCK (optional) (unix) don't link unix clock functions - #define RGFW_NO_DWM (windows only) - do not use or link dwmapi - #define RGFW_USE_XDL (optional) (X11) if XDL (XLib Dynamic Loader) should be used to load X11 dynamically during runtime (must include XDL.h along with RGFW) - #define RGFW_COCOA_GRAPHICS_SWITCHING - (optional) (cocoa) use automatic graphics switching (allow the system to choose to use GPU or iGPU) - #define RGFW_COCOA_FRAME_NAME (optional) (cocoa) set frame name - #define RGFW_NO_DPI - do not calculate DPI (no XRM nor libShcore included) - #define RGFW_BUFFER_BGR - use the BGR format for bufffers instead of RGB, saves processing time - - #define RGFW_ALLOC x - choose the default allocation function (defaults to standard malloc) - #define RGFW_FREE x - choose the default deallocation function (defaults to standard free) - #define RGFW_USERPTR x - choose the default userptr sent to the malloc call, (NULL by default) - - #define RGFW_EXPORT - use when building RGFW - #define RGFW_IMPORT - use when linking with RGFW (not as a single-header) - - #define RGFW_USE_INT - force the use c-types rather than stdint.h (for systems that might not have stdint.h (msvc)) - #define RGFW_bool x - choose what type to use for bool, by default u32 is used -*/ - -/* -Example to get you started : - -linux : gcc main.c -lX11 -lXrandr -lGL -windows : gcc main.c -lopengl32 -lgdi32 -macos : gcc main.c -framework Cocoa -framework CoreVideo -framework OpenGL -framework IOKit - -#define RGFW_IMPLEMENTATION -#include "RGFW.h" - -u8 icon[4 * 3 * 3] = {0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF}; - -int main() { - RGFW_window* win = RGFW_createWindow("name", RGFW_RECT(100, 100, 500, 500), (u64)0); - - RGFW_window_setIcon(win, icon, RGFW_AREA(3, 3), 4); - - for (;;) { - RGFW_window_checkEvent(win); // NOTE: checking events outside of a while loop may cause input lag - if (win->event.type == RGFW_quit || RGFW_isPressed(win, RGFW_escape)) - break; - - RGFW_window_swapBuffers(win); - - glClearColor(1, 1, 1, 1); - glClear(GL_COLOR_BUFFER_BIT); - } - - RGFW_window_close(win); -} - - compiling : - - if you wish to compile the library all you have to do is create a new file with this in it - - rgfw.c - #define RGFW_IMPLEMENTATION - #include "RGFW.h" - - You may also want to add - `#define RGFW_EXPORT` when compiling and - `#define RGFW_IMPORT`when linking RGFW on it's own: - this reduces inline functions and prevents bloat in the object file - - then you can use gcc (or whatever compile you wish to use) to compile the library into object file - - ex. gcc -c RGFW.c -fPIC - - after you compile the library into an object file, you can also turn the object file into an static or shared library - - (commands ar and gcc can be replaced with whatever equivalent your system uses) - - static : ar rcs RGFW.a RGFW.o - shared : - windows: - gcc -shared RGFW.o -lopengl32 -lgdi32 -o RGFW.dll - linux: - gcc -shared RGFW.o -lX11 -lGL -lXrandr -o RGFW.so - macos: - gcc -shared RGFW.o -framework CoreVideo -framework Cocoa -framework OpenGL -framework IOKit -*/ - - - -/* - Credits : - EimaMei/Sacode : Much of the code for creating windows using winapi, Wrote the Silicon library, helped with MacOS Support, siliapp.h -> referencing - - stb - This project is heavily inspired by the stb single header files - - GLFW: - certain parts of winapi and X11 are very poorly documented, - GLFW's source code was referenced and used throughout the project. - - contributors : (feel free to put yourself here if you contribute) - krisvers -> code review - EimaMei (SaCode) -> code review - 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 - Joshua Rowe (omnisci3nce) - bug fix, review (macOS) - @lesleyrs -> bug fix, review (OpenGL) - Nick Porcino (meshula) - testing, organization, review (MacOS, examples) - @DarekParodia -> code review (X11) (C++) -*/ - -#if _MSC_VER - #pragma comment(lib, "gdi32") - #pragma comment(lib, "shell32") - #pragma comment(lib, "opengl32") - #pragma comment(lib, "winmm") - #pragma comment(lib, "user32") -#endif - -#ifndef RGFW_USERPTR - #define RGFW_USERPTR NULL -#endif - -#ifndef RGFW_UNUSED - #define RGFW_UNUSED(x) (void)(x) -#endif - -#ifndef RGFW_ROUND - #define RGFW_ROUND(x) (int)((x) >= 0 ? (x) + 0.5f : (x) - 0.5f) -#endif - -#ifndef RGFW_ALLOC - #include - - #ifndef __USE_POSIX199309 - #define __USE_POSIX199309 - #endif - - #define RGFW_ALLOC malloc - #define RGFW_FREE free -#endif - -#ifndef RGFW_ASSERT - #include - #define RGFW_ASSERT assert -#endif - -#ifndef RGFW_MEMCPY - #include - - #define RGFW_MEMCPY(dist, src, len) memcpy(dist, src, len) - #define RGFW_STRNCMP(s1, s2, max) strncmp(s1, s2, max) - #define RGFW_STRNCPY(dist, src, len) strncpy(dist, src, len) - #define RGFW_STRSTR(str, substr) strstr(str, substr) - //required for X11 XDnD - #define RGFW_STRTOL(str, endptr, base) strtol(str, endptr, base) -#else -#undef _INC_STRING -#endif - -#if !_MSC_VER - #ifndef inline - #ifndef __APPLE__ - #define inline __inline - #endif - #endif -#endif - -#ifdef RGFW_WIN95 /* for windows 95 testing (not that it really works) */ - #define RGFW_NO_MONITOR - #define RGFW_NO_PASSTHROUGH -#endif - -#if defined(RGFW_EXPORT) || defined(RGFW_IMPORT) - #if defined(_WIN32) - #if defined(__TINYC__) && (defined(RGFW_EXPORT) || defined(RGFW_IMPORT)) - #define __declspec(x) __attribute__((x)) - #endif - - #if defined(RGFW_EXPORT) - #define RGFWDEF __declspec(dllexport) - #else - #define RGFWDEF __declspec(dllimport) - #endif - #else - #if defined(RGFW_EXPORT) - #define RGFWDEF __attribute__((visibility("default"))) - #endif - #endif -#endif - -#ifndef RGFWDEF - #define RGFWDEF inline -#endif - -#ifndef RGFW_ENUM - #define RGFW_ENUM(type, name) type name; enum -#endif - - -#if defined(__cplusplus) && !defined(__EMSCRIPTEN__) - #ifdef __clang__ - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wnullability-completeness" - #endif - extern "C" { -#endif - - /* makes sure the header file part is only defined once by default */ -#ifndef RGFW_HEADER - -#define RGFW_HEADER - -#include -#if !defined(u8) - #ifdef RGFW_USE_INT /* optional for any system that might not have stdint.h */ - typedef unsigned char u8; - typedef signed char i8; - typedef unsigned short u16; - typedef signed short i16; - typedef unsigned long int u32; - typedef signed long int i32; - typedef unsigned long long u64; - typedef signed long long i64; - #else /* use stdint standard types instead of c ""standard"" types */ - #include - - typedef uint8_t u8; - typedef int8_t i8; - typedef uint16_t u16; - typedef int16_t i16; - typedef uint32_t u32; - typedef int32_t i32; - typedef uint64_t u64; - typedef int64_t i64; - #endif - #define u8 u8 -#endif - -#if !defined(RGFW_bool) /* RGFW bool type */ - typedef u8 RGFW_bool; - #define RGFW_bool u8 -#endif - -#define RGFW_BOOL(x) ((x) ? RGFW_TRUE : RGFW_FALSE) /* force an value to be 0 or 1 */ -#define RGFW_TRUE 1 -#define RGFW_FALSE 0 - -/* these OS macros look better & are standardized */ -/* plus it helps with cross-compiling */ - -#ifdef __EMSCRIPTEN__ - #define RGFW_WASM - - #if !defined(RGFW_NO_API) && !defined(RGFW_WEBGPU) - #define RGFW_OPENGL - #endif - - #ifdef RGFW_EGL - #undef RGFW_EGL - #endif - - #include - #include - - #ifdef RGFW_WEBGPU - #include - #endif -#endif - -#if defined(RGFW_X11) && defined(__APPLE__) && !defined(RGFW_CUSTOM_BACKEND) - #define RGFW_MACOS_X11 - #define RGFW_UNIX - #undef __APPLE__ -#endif - -#if defined(_WIN32) && !defined(RGFW_UNIX) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND) /* (if you're using X11 on windows some how) */ - #define RGFW_WINDOWS - /* make sure the correct architecture is defined */ - #if defined(_WIN64) - #define _AMD64_ - #undef _X86_ - #else - #undef _AMD64_ - #ifndef _X86_ - #define _X86_ - #endif - #endif - - #ifndef RGFW_NO_XINPUT - #ifdef __MINGW32__ /* try to find the right header */ - #include - #else - #include - #endif - #endif -#elif defined(RGFW_WAYLAND) - #define RGFW_DEBUG // wayland will be in debug mode by default for now - #if !defined(RGFW_NO_API) && (!defined(RGFW_BUFFER) || defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) - #define RGFW_EGL - #define RGFW_OPENGL - #define RGFW_UNIX - #include - #endif - - #include -#endif -#if !defined(RGFW_NO_X11) && !defined(RGFW_NO_X11) && (defined(__unix__) || defined(RGFW_MACOS_X11) || defined(RGFW_X11)) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND) - #define RGFW_MACOS_X11 - #define RGFW_X11 - #define RGFW_UNIX - #include -#elif defined(__APPLE__) && !defined(RGFW_MACOS_X11) && !defined(RGFW_X11) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND) - #define RGFW_MACOS -#endif - -#if (defined(RGFW_OPENGL_ES1) || defined(RGFW_OPENGL_ES2) || defined(RGFW_OPENGL_ES3)) && !defined(RGFW_EGL) - #define RGFW_EGL -#endif - -#if !defined(RGFW_OSMESA) && !defined(RGFW_EGL) && !defined(RGFW_OPENGL) && !defined(RGFW_DIRECTX) && !defined(RGFW_BUFFER) && !defined(RGFW_NO_API) - #define RGFW_OPENGL -#endif - -#ifdef RGFW_EGL - #include -#elif defined(RGFW_OSMESA) - #ifdef RGFW_WINDOWS - #define OEMRESOURCE - #include - #define GLAPIENTRY APIENTRY - #define GLAPI WINGDIAPI - #endif - - #ifndef __APPLE__ - #include - #else - #include - #endif -#endif - -#if defined(RGFW_OPENGL) && defined(RGFW_X11) - #ifndef GLX_MESA_swap_control - #define GLX_MESA_swap_control - #endif - #include /* GLX defs, xlib.h, gl.h */ -#endif - -#define RGFW_COCOA_FRAME_NAME NULL - -/*! (unix) Toggle use of wayland. This will be on by default if you use `RGFW_WAYLAND` (if you don't use RGFW_WAYLAND, you don't expose WAYLAND functions) - this is mostly used to allow you to force the use of XWayland -*/ -RGFWDEF void RGFW_useWayland(RGFW_bool wayland); -/* - regular RGFW stuff -*/ - -#define RGFW_key u8 - -typedef RGFW_ENUM(u8, RGFW_eventType) { - /*! event codes */ - RGFW_eventNone = 0, /*!< no event has been sent */ - RGFW_keyPressed, /* 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.key - !!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.keyMod holds the current keyMod - 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 - - RGFW_event.button holds which mouse button was pressed - */ - RGFW_gamepadConnected, /*!< a gamepad was connected */ - RGFW_gamepadDisconnected, /*!< a gamepad was disconnected */ - RGFW_gamepadButtonPressed, /*!< a gamepad button was pressed */ - RGFW_gamepadButtonReleased, /*!< a gamepad button was released */ - RGFW_gamepadAxisMove, /*!< an axis of a gamepad was moved */ - /*! gamepad event note - RGFW_event.gamepad holds which gamepad was altered, if any - RGFW_event.button holds which gamepad button was pressed - - RGFW_event.axis holds the data of all the axises - RGFW_event.axisesCount says how many axises there are - */ - RGFW_windowMoved, /*!< the window was moved (by the user) */ - RGFW_windowResized, /*!< the window was resized (by the user), [on WASM 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_DNDInit, /*!< 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 - - This is also the size of the array which stores all the dropped file string, - RGFW_event.droppedFiles - */ - RGFW_windowMaximized, /*!< the window was maximized */ - RGFW_windowMinimized, /*!< the window was minimized */ - RGFW_windowRestored, /*!< the window was restored */ -}; - -/*! mouse button codes (RGFW_event.button) */ -typedef RGFW_ENUM(u8, RGFW_mouseButton) { - RGFW_mouseLeft = 0, /*!< left mouse button is pressed */ - RGFW_mouseMiddle, /*!< mouse-wheel-button is pressed */ - RGFW_mouseRight, /*!< right mouse button is pressed */ - RGFW_mouseScrollUp, /*!< mouse wheel is scrolling up */ - RGFW_mouseScrollDown, /*!< mouse wheel is scrolling down */ - RGFW_mouseMisc1, RGFW_mouseMisc2, RGFW_mouseMisc3, RGFW_mouseMisc4, RGFW_mouseMisc5, - RGFW_mouseFinal -}; - -#ifndef RGFW_MAX_PATH -#define RGFW_MAX_PATH 260 /* max length of a path (for dnd) */ -#endif -#ifndef RGFW_MAX_DROPS -#define RGFW_MAX_DROPS 260 /* max items you can drop at once */ -#endif - -#define RGFW_BIT(x) (1L << x) - -/* for RGFW_event.lockstate */ -typedef RGFW_ENUM(u8, RGFW_keymod) { - RGFW_modCapsLock = RGFW_BIT(0), - RGFW_modNumLock = RGFW_BIT(1), - RGFW_modControl = RGFW_BIT(2), - RGFW_modAlt = RGFW_BIT(3), - RGFW_modShift = RGFW_BIT(4), - RGFW_modSuper = RGFW_BIT(5), - RGFW_modScrollLock = RGFW_BIT(6) -}; - -/*! gamepad button codes (based on xbox/playstation), you may need to change these values per controller */ -typedef RGFW_ENUM(u8, RGFW_gamepadCodes) { - RGFW_gamepadNone = 0, /*!< or PS X button */ - RGFW_gamepadA, /*!< or PS X button */ - RGFW_gamepadB, /*!< or PS circle button */ - RGFW_gamepadY, /*!< or PS triangle button */ - RGFW_gamepadX, /*!< or PS square button */ - RGFW_gamepadStart, /*!< start button */ - RGFW_gamepadSelect, /*!< select button */ - RGFW_gamepadHome, /*!< home button */ - RGFW_gamepadUp, /*!< dpad up */ - RGFW_gamepadDown, /*!< dpad down */ - RGFW_gamepadLeft, /*!< dpad left */ - RGFW_gamepadRight, /*!< dpad right */ - RGFW_gamepadL1, /*!< left bump */ - RGFW_gamepadL2, /*!< left trigger */ - RGFW_gamepadR1, /*!< right bumper */ - RGFW_gamepadR2, /*!< right trigger */ - RGFW_gamepadL3, /* left thumb stick */ - RGFW_gamepadR3, /*!< right thumb stick */ - RGFW_gamepadFinal -}; - -/*! 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 */ -#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 */ -#ifndef RGFW_area - typedef struct { u32 w, h; } RGFW_area; -#endif - -#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)} - -#ifndef RGFW_NO_MONITOR - /* monitor mode data | can be changed by the user (with functions)*/ - typedef struct RGFW_monitorMode { - RGFW_area area; /*!< monitor workarea size */ - u32 refreshRate; /*!< monitor refresh rate */ - u8 red, blue, green; - } RGFW_monitorMode; - - /*! structure for monitor data */ - typedef struct RGFW_monitor { - i32 x, y; /*!< x - y of the monitor workarea */ - char name[128]; /*!< monitor name */ - float scaleX, scaleY; /*!< monitor content scale */ - float pixelRatio; /*!< pixel ratio for monitor (1.0 for regular, 2.0 for hiDPI) */ - float physW, physH; /*!< monitor physical size in inches */ - - RGFW_monitorMode mode; - } RGFW_monitor; - - /*! get an array of all the monitors (max 6) */ - RGFWDEF RGFW_monitor* RGFW_getMonitors(void); - /*! get the primary monitor */ - RGFWDEF RGFW_monitor RGFW_getPrimaryMonitor(void); - - typedef RGFW_ENUM(u8, RGFW_modeRequest) { - RGFW_monitorScale = RGFW_BIT(0), /*!< scale the monitor size */ - RGFW_monitorRefresh = RGFW_BIT(1), /*!< change the refresh rate */ - RGFW_monitorRGB = RGFW_BIT(2), /*!< change the monitor RGB bits size */ - RGFW_monitorAll = RGFW_monitorScale | RGFW_monitorRefresh | RGFW_monitorRGB - }; - - /*! request a specific mode */ - RGFWDEF RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request); - /*! check if 2 monitor modes are the same */ - RGFWDEF RGFW_bool RGFW_monitorModeCompare(RGFW_monitorMode mon, RGFW_monitorMode mon2, RGFW_modeRequest request); -#endif - -/* RGFW mouse loading */ -typedef void RGFW_mouse; - -/*!< loads mouse icon from bitmap (similar to RGFW_window_setIcon). Icon NOT resized by default */ -RGFWDEF RGFW_mouse* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels); -/*!< frees RGFW_mouse data */ -RGFWDEF void RGFW_freeMouse(RGFW_mouse* mouse); - -/* 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 { - RGFW_eventType type; /*!< which event has been sent?*/ - RGFW_point point; /*!< mouse x, y of event (or drop point) */ - RGFW_point vector; /*!< raw mouse movement */ - - RGFW_key key; /*!< the physical key of the event, refers to where key is physically !!Keycodes defined at the bottom of the RGFW_HEADER part of this file!! */ - u8 keyChar; /*!< mapped key char of the event */ - - RGFW_bool repeat; /*!< key press event repeated (the key is being held) */ - RGFW_keymod keyMod; - - u8 button; /* !< which mouse (or gamepad) button was pressed */ - double scroll; /*!< the raw mouse scroll value */ - - u16 gamepad; /*! which gamepad this event applies to (if applicable to any) */ - u8 axisesCount; /*!< number of axises */ - - u8 whichAxis; /* which axis was effected */ - RGFW_point axis[4]; /*!< x, y of axises (-100 to 100) */ - - /*! drag and drop data */ - /* 260 max paths with a max length of 260 */ - char** droppedFiles; /*!< dropped files */ - size_t droppedFilesCount; /*!< house many files were dropped */ - - void* _win; /*!< the window this event applies too (for event queue events) */ -} RGFW_event; - -/*! source data for the window (used by the APIs) */ -#ifdef RGFW_WINDOWS -typedef struct RGFW_window_src { - HWND window; /*!< source window */ - HDC hdc; /*!< source HDC */ - u32 hOffset; /*!< height offset for window */ - HICON hIconSmall, hIconBig; /*!< source window icons */ - #if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL) - HGLRC ctx; /*!< source graphics context */ - #elif defined(RGFW_OSMESA) - OSMesaContext ctx; - #elif defined(RGFW_EGL) - EGLSurface EGL_surface; - EGLDisplay EGL_display; - EGLContext EGL_context; - #endif - - #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - HDC hdcMem; - HBITMAP bitmap; - u8* bitmapBits; - #endif - RGFW_area maxSize, minSize, aspectRatio; /*!< for setting max/min resize (RGFW_WINDOWS) */ -} RGFW_window_src; -#elif defined(RGFW_UNIX) -typedef struct RGFW_window_src { -#if defined(RGFW_X11) - Display* display; /*!< source display */ - Window window; /*!< source window */ - #if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL) - GLXContext ctx; /*!< source graphics context */ - #elif defined(RGFW_OSMESA) - OSMesaContext ctx; - #elif defined(RGFW_EGL) - EGLSurface EGL_surface; - EGLDisplay EGL_display; - EGLContext EGL_context; - #endif - - #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - XImage* bitmap; - #endif - GC gc; - char* clipboard; /* for writing to the clipboard selection */ - size_t clipboard_len; -#endif /* RGFW_X11 */ -#if defined(RGFW_WAYLAND) - struct wl_display* wl_display; - struct wl_surface* surface; - struct wl_buffer* wl_buffer; - struct wl_keyboard* keyboard; - - struct wl_compositor* compositor; - struct xdg_surface* xdg_surface; - struct xdg_toplevel* xdg_toplevel; - struct zxdg_toplevel_decoration_v1* decoration; - struct xdg_wm_base* xdg_wm_base; - struct wl_shm* shm; - struct wl_seat *seat; - u8* buffer; - #if defined(RGFW_EGL) - struct wl_egl_window* eglWindow; - #endif - #if defined(RGFW_EGL) && !defined(RGFW_X11) - EGLSurface EGL_surface; - EGLDisplay EGL_display; - EGLContext EGL_context; - #elif defined(RGFW_OSMESA) && !defined(RGFW_X11) - OSMesaContext ctx; - #endif -#endif /* RGFW_WAYLAND */ -} RGFW_window_src; -#endif /* RGFW_UNIX */ -#if defined(RGFW_MACOS) -typedef struct RGFW_window_src { - void* window; -#if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL) - void* ctx; /*!< source graphics context */ -#elif defined(RGFW_OSMESA) - OSMesaContext ctx; -#elif defined(RGFW_EGL) - EGLSurface EGL_surface; - EGLDisplay EGL_display; - EGLContext EGL_context; -#endif - - void* view; /* apple viewpoint thingy */ - -#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - void* bitmap; /*!< API's bitmap for storing or managing */ - void* image; -#endif -} RGFW_window_src; -#elif defined(RGFW_WASM) -typedef struct RGFW_window_src { - #ifdef RGFW_WEBGPU - WGPUInstance ctx; - WGPUDevice device; - WGPUQueue queue; - #else - EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx; - #endif -} RGFW_window_src; -#endif - -/*! Optional arguments for making a windows */ -typedef RGFW_ENUM(u32, RGFW_windowFlags) { - RGFW_windowNoInitAPI = RGFW_BIT(0), /* do NOT init an API (mostly for bindings. you should use `#define RGFW_NO_API` in C) */ - RGFW_windowNoBorder = RGFW_BIT(1), /*!< the window doesn't have a border */ - RGFW_windowNoResize = RGFW_BIT(2), /*!< the window cannot be resized by the user */ - RGFW_windowAllowDND = RGFW_BIT(3), /*!< the window supports drag and drop */ - RGFW_windowHideMouse = RGFW_BIT(4), /*! the window should hide the mouse (can be toggled later on using `RGFW_window_mouseShow`) */ - RGFW_windowFullscreen = RGFW_BIT(5), /*!< the window is fullscreen by default */ - RGFW_windowTransparent = RGFW_BIT(6), /*!< the window is transparent (only properly works on X11 and MacOS, although it's meant for for windows) */ - RGFW_windowCenter = RGFW_BIT(7), /*! center the window on the screen */ - RGFW_windowOpenglSoftware = RGFW_BIT(8), /*! use OpenGL software rendering */ - RGFW_windowCocoaCHDirToRes = RGFW_BIT(9), /*! (cocoa only), change directory to resource folder */ - RGFW_windowScaleToMonitor = RGFW_BIT(10), /*! scale the window to the screen */ - RGFW_windowHide = RGFW_BIT(11), /*! the window is hidden */ - RGFW_windowMaximize = RGFW_BIT(12), - RGFW_windowCenterCursor = RGFW_BIT(13), - RGFW_windowFloating = RGFW_BIT(14), /*!< create a floating window */ - RGFW_windowFreeOnClose = RGFW_BIT(15), /*!< free (RGFW_window_close) the RGFW_window struct when the window is closed (by the end user) */ - RGFW_windowFocusOnShow = RGFW_BIT(16), /*!< focus the window when it's shown */ - RGFW_windowMinimize = RGFW_BIT(17), /*!< focus the window when it's shown */ - RGFW_windowFocus = RGFW_BIT(18), /*!< if the window is in focus */ - RGFW_windowedFullscreen = RGFW_windowNoBorder | RGFW_windowMaximize, -}; - -typedef struct RGFW_window { - 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) */ - /* when rendering using RGFW_BUFFER, the buffer is in the RGBA format */ - RGFW_area bufferSize; -#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_point _lastMousePoint; /*!< last cusor point (for raw mouse data) */ - - u32 _flags; /*!< windows flags (for RGFW to check) */ - RGFW_rect _oldRect; /*!< rect before fullscreen */ -} RGFW_window; /*!< window structure for managing the window */ - -#if defined(RGFW_X11) || defined(RGFW_MACOS) - typedef u64 RGFW_thread; /*!< thread type unix */ -#else - typedef void* RGFW_thread; /*!< thread type for windows */ -#endif - -/*! scale monitor to window size */ -RGFWDEF RGFW_bool RGFW_monitor_scaleToWindow(RGFW_monitor mon, RGFW_window* win); - -/** * @defgroup Window_management -* @{ */ - - -/*! - * the class name for X11 and WinAPI. apps with the same class will be grouped by the WM - * by default the class name will == the root window's name -*/ -RGFWDEF void RGFW_setClassName(const char* name); -RGFWDEF void RGFW_setXInstName(const char* name); /*!< X11 instance name (window name will by used by default) */ - -/*! (cocoa only) change directory to resource folder */ -RGFWDEF void RGFW_moveToMacOSResourceDir(void); - -/* NOTE: (windows) if the executable has an icon resource named RGFW_ICON, it will be set as the initial icon for the window */ - -RGFWDEF RGFW_window* RGFW_createWindow( - const char* name, /* name of the window */ - RGFW_rect rect, /* rect of window */ - RGFW_windowFlags flags /* extra arguments ((u32)0 means no flags used)*/ -); /*!< function to create a window and struct */ - -RGFWDEF RGFW_window* RGFW_createWindowPtr( - const char* name, /* name of the window */ - RGFW_rect rect, /* rect of window */ - RGFW_windowFlags flags, /* extra arguments (NULL / (u32)0 means no flags used) */ - RGFW_window* win /* ptr to the window struct you want to use */ -); /*!< function to create a window (without allocating a window struct) */ - -RGFWDEF void RGFW_window_initBuffer(RGFW_window* win); -RGFWDEF void RGFW_window_initBufferSize(RGFW_window* win, RGFW_area area); -RGFWDEF void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area); - -/*! set the window flags (will undo flags if they don't match the old ones) */ -RGFWDEF void RGFW_window_setFlags(RGFW_window* win, RGFW_windowFlags); - -/*! 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 - - ex. - - while (RGFW_window_checkEvent(win) != NULL) [this keeps checking events until it reaches the last one] - - this function is optional if you choose to use event callbacks, - although you still need some way to tell RGFW to process events eg. `RGFW_window_checkEvents` -*/ - -RGFWDEF 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 the function to keep checking for events even after `RGFW_window_checkEvent == NULL` - if waitMS == 0, the loop will not wait for events - if waitMS > 0, the loop will wait that many miliseconds after there are no more events until it returns - if waitMS == -1 or waitMS == the max size of an unsigned 32-bit int, the loop will not return until it gets another event -*/ -typedef RGFW_ENUM(u32, RGFW_eventWait) { - RGFW_eventNoWait = 0, - RGFW_eventWaitNext = 0xFFFFFFFF -}; - -/*! sleep until RGFW gets an event or the timer ends (defined by OS) */ -RGFWDEF void RGFW_window_eventWait(RGFW_window* win, u32 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, u32 waitMS); - -/*! - tell RGFW_window_eventWait to stop waiting (to be ran from another thread) -*/ -RGFWDEF void RGFW_stopCheckEvents(void); - -/*! window managment functions */ -RGFWDEF void RGFW_window_close(RGFW_window* win); /*!< close the window and free leftover data */ - -/*! move a window to a given point */ -RGFWDEF void RGFW_window_move(RGFW_window* win, - RGFW_point v /*!< new pos */ -); - -#ifndef RGFW_NO_MONITOR - /*! move window 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, /*!< source window */ - RGFW_area a /*!< new size */ -); - -/*! set window aspect ratio */ -RGFWDEF void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a); -/*! set the minimum dimensions of a window */ -RGFWDEF void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a); -/*! set the maximum dimensions of a window */ -RGFWDEF void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a); - -RGFWDEF void RGFW_window_focus(RGFW_window* win); /*!< sets the focus to this window */ -RGFWDEF RGFW_bool RGFW_window_isInFocus(RGFW_window* win); /*!< checks the focus to this window */ -RGFWDEF void RGFW_window_raise(RGFW_window* win); /*!< raise the window (to the top) */ -RGFWDEF void RGFW_window_maximize(RGFW_window* win); /*!< maximize the window */ -RGFWDEF void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen); /*!< turn fullscreen on / off for a window */ -RGFWDEF void RGFW_window_center(RGFW_window* win); /*!< center the window */ -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_setFloating(RGFW_window* win, RGFW_bool floating); /*!< make the window a floating window */ -RGFWDEF void RGFW_window_setOpacity(RGFW_window* win, u8 opacity); /*!< sets the opacity of a window */ - -/*! if the window should have a border or not (borderless) based on bool value of `border` */ -RGFWDEF void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border); -RGFWDEF RGFW_bool RGFW_window_borderless(RGFW_window* win); - -/*! turn on / off dnd (RGFW_windowAllowDND stil must be passed to the window)*/ -RGFWDEF void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow); -/*! check if DND is allowed */ -RGFWDEF RGFW_bool RGFW_window_allowsDND(RGFW_window* win); - - -#ifndef RGFW_NO_PASSTHROUGH - /*! turn on / off mouse passthrough */ - RGFWDEF void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough); -#endif - -/*! rename window to a given string */ -RGFWDEF void RGFW_window_setName(RGFW_window* win, - const char* name -); - -RGFWDEF RGFW_bool RGFW_window_setIcon(RGFW_window* win, /*!< source window */ - u8* icon /*!< icon bitmap */, - RGFW_area a /*!< width and height of the bitmap */, - i32 channels /*!< how many channels the bitmap has (rgb : 3, rgba : 4) */ -); /*!< image MAY be resized by default, set both the taskbar and window icon */ - -typedef RGFW_ENUM(u8, RGFW_icon) { - RGFW_iconTaskbar = RGFW_BIT(0), - RGFW_iconWindow = RGFW_BIT(1), - RGFW_iconBoth = RGFW_iconTaskbar | RGFW_iconWindow -}; -RGFWDEF RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* icon, RGFW_area a, i32 channels, u8 type); - -/*!< sets mouse to RGFW_mouse icon (loaded from a bitmap struct) */ -RGFWDEF void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse); - -/*!< 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 RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse); - -RGFWDEF RGFW_bool 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 becomes raw mouse movement data - - 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 */ -RGFWDEF void RGFW_window_mouseUnhold(RGFW_window* win); - -/*! hide the window */ -RGFWDEF void RGFW_window_hide(RGFW_window* win); -/*! show the window */ -RGFWDEF void RGFW_window_show(RGFW_window* win); - -/* - makes it so `RGFW_window_shouldClose` returns true - by setting the window event.type to RGFW_quit -*/ -RGFWDEF void RGFW_window_setShouldClose(RGFW_window* win); - -/*! where the mouse is on the screen */ -RGFWDEF RGFW_point RGFW_getGlobalMousePoint(void); - -/*! where the mouse is on the window */ -RGFWDEF RGFW_point RGFW_window_getMousePoint(RGFW_window* win); - -/*! show the mouse or hide the mouse */ -RGFWDEF void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show); -/*! if the mouse is hidden */ -RGFWDEF RGFW_bool RGFW_window_mouseHidden(RGFW_window* win); -/*! move the mouse to a given point */ -RGFWDEF void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v); - -/*! if the window should close (RGFW_close was sent or escape was pressed) */ -RGFWDEF RGFW_bool RGFW_window_shouldClose(RGFW_window* win); -/*! if the window is fullscreen */ -RGFWDEF RGFW_bool RGFW_window_isFullscreen(RGFW_window* win); -/*! if the window is hidden */ -RGFWDEF RGFW_bool RGFW_window_isHidden(RGFW_window* win); -/*! if the window is minimized */ -RGFWDEF RGFW_bool RGFW_window_isMinimized(RGFW_window* win); -/*! if the window is maximized */ -RGFWDEF RGFW_bool RGFW_window_isMaximized(RGFW_window* win); -/*! if the window is floating */ -RGFWDEF RGFW_bool RGFW_window_isFloating(RGFW_window* win); - -/** @} */ - -/** * @defgroup Monitor -* @{ */ - -#ifndef RGFW_NO_MONITOR -/* - scale the window to the monitor. - This is run by default if the user uses the arg `RGFW_scaleToMonitor` during window creation -*/ -RGFWDEF void RGFW_window_scaleToMonitor(RGFW_window* win); -/*! get the struct of the window's monitor */ -RGFWDEF RGFW_monitor RGFW_window_getMonitor(RGFW_window* win); -#endif - -/** @} */ - -/** * @defgroup Input -* @{ */ - -/*! 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 RGFW_bool RGFW_isPressed(RGFW_window* win, RGFW_key key); /*!< if key is pressed (key code)*/ - -RGFWDEF RGFW_bool RGFW_wasPressed(RGFW_window* win, RGFW_key key); /*!< if key was pressed (checks previous state only) (key code) */ - -RGFWDEF RGFW_bool RGFW_isHeld(RGFW_window* win, RGFW_key key); /*!< if key is held (key code) */ -RGFWDEF RGFW_bool RGFW_isReleased(RGFW_window* win, RGFW_key key); /*!< if key is released (key code) */ - -/* if a key is pressed and then released, pretty much the same as RGFW_isReleased */ -RGFWDEF RGFW_bool RGFW_isClicked(RGFW_window* win, RGFW_key key /*!< key code */); - -/*! if a mouse button is pressed */ -RGFWDEF RGFW_bool RGFW_isMousePressed(RGFW_window* win, RGFW_mouseButton button /*!< mouse button code */ ); -/*! if a mouse button is held */ -RGFWDEF RGFW_bool RGFW_isMouseHeld(RGFW_window* win, RGFW_mouseButton button /*!< mouse button code */ ); -/*! if a mouse button was released */ -RGFWDEF RGFW_bool RGFW_isMouseReleased(RGFW_window* win, RGFW_mouseButton button /*!< mouse button code */ ); -/*! if a mouse button was pressed (checks previous state only) */ -RGFWDEF RGFW_bool RGFW_wasMousePressed(RGFW_window* win, RGFW_mouseButton button /*!< mouse button code */ ); -/** @} */ - -/** * @defgroup Clipboard -* @{ */ -typedef ptrdiff_t RGFW_ssize_t; - -RGFWDEF const char* RGFW_readClipboard(size_t* size); /*!< read clipboard data */ -/*! read clipboard data or send a NULL str to just get the length of the clipboard data */ -RGFWDEF RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity); -RGFWDEF void RGFW_writeClipboard(const char* text, u32 textLen); /*!< write text to the clipboard */ -/** @} */ - - - -/** * @defgroup error handling -* @{ */ -typedef RGFW_ENUM(u8, RGFW_debugType) { - RGFW_typeError = 0, RGFW_typeWarning, RGFW_typeInfo -}; - -typedef RGFW_ENUM(u8, RGFW_errorCode) { - RGFW_noError = 0, /*!< no error */ - RGFW_errOpenglContext, RGFW_errEGLContext, /*!< error with the OpenGL context */ - RGFW_errWayland, - RGFW_errDirectXContext, - RGFW_errIOKit, - RGFW_errClipboard, - RGFW_errFailedFuncLoad, - RGFW_errBuffer, - RGFW_infoMonitor, RGFW_infoWindow, RGFW_infoBuffer, - RGFW_warningWayland, RGFW_warningOpenGL -}; - -typedef struct RGFW_debugContext { RGFW_window* win; RGFW_monitor monitor; u32 srcError; } RGFW_debugContext; -#define RGFW_DEBUG_CTX(win, err) (RGFW_debugContext){win, (RGFW_monitor){}, err} -#define RGFW_DEBUG_CTX_MON(monitor) (RGFW_debugContext){RGFW_root, monitor, 0} - -typedef void (* RGFW_debugfunc)(RGFW_debugType type, RGFW_errorCode err, RGFW_debugContext ctx, const char* msg); -RGFWDEF RGFW_debugfunc RGFW_setDebugCallback(RGFW_debugfunc func); -RGFWDEF void RGFW_sendDebugInfo(RGFW_debugType type, RGFW_errorCode err, RGFW_debugContext ctx, const char* msg); -/** @} */ - -/** - - - event callbacks. - These are completely optional, so you can use the normal - RGFW_checkEvent() method if you prefer that - -* @defgroup Callbacks -* @{ -*/ - -/*! 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 */ -typedef void (* RGFW_windowresizefunc)(RGFW_window* win, RGFW_rect r); -/*! 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 in focus */ -typedef void (* RGFW_focusfunc)(RGFW_window* win, RGFW_bool inFocus); -/*! 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, RGFW_bool status); -/*! 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_point vector); -/*! RGFW_DNDInit, 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 */ -typedef void (* RGFW_windowrefreshfunc)(RGFW_window* win); -/*! RGFW_keyPressed / RGFW_keyReleased, the window that got the event, the mapped key, the physical key, the string version, the state of the mod keys, if it was a press (else it's a release) */ -typedef void (* RGFW_keyfunc)(RGFW_window* win, u8 key, char keyChar, RGFW_keymod keyMod, RGFW_bool 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) */ -typedef void (* RGFW_mousebuttonfunc)(RGFW_window* win, RGFW_mouseButton button, double scroll, RGFW_bool pressed); -/*! RGFW_gamepadButtonPressed, 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_gamepadButtonfunc)(RGFW_window* win, u16 gamepad, u8 button, RGFW_bool pressed); -/*! RGFW_gamepadAxisMove, the window that got the event, the gamepad in question, the axis values and the axis count */ -typedef void (* RGFW_gamepadAxisfunc)(RGFW_window* win, u16 gamepad, RGFW_point axis[2], u8 axisesCount, u8 whichAxis); -/*! RGFW_gamepadConnected / RGFW_gamepadDisconnected, the window that got the event, the gamepad in question, if the controller was connected (else it was disconnected) */ -typedef void (* RGFW_gamepadfunc)(RGFW_window* win, u16 gamepad, RGFW_bool connected); -/*! RGFW_dnd, the window that had the drop, the drop data and the number of files dropped */ -typedef void (* RGFW_dndfunc)(RGFW_window* win, char** droppedFiles, u32 droppedFilesCount); - -/*! 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_gamepadButtonfunc RGFW_setgamepadButtonCallback(RGFW_gamepadButtonfunc func); -/*! set callback for a gamepad axis move event. Returns previous callback function (if it was set) */ -RGFWDEF RGFW_gamepadAxisfunc RGFW_setgamepadAxisCallback(RGFW_gamepadAxisfunc func); -/*! set callback for when a controller is connected or disconnected. Returns the previous callback function (if it was set) */ -RGFWDEF RGFW_gamepadfunc RGFW_setGamepadCallback(RGFW_gamepadfunc func); -/*! set call back for when window is maximized. Returns the previous callback function (if it was set) */ -RGFWDEF RGFW_windowresizefunc RGFW_setWindowMaximizedCallback(RGFW_windowresizefunc func); -/*! set call back for when window is minimized. Returns the previous callback function (if it was set) */ -RGFWDEF RGFW_windowresizefunc RGFW_setWindowMinimizedCallback(RGFW_windowresizefunc func); -/*! set call back for when window is restored. Returns the previous callback function (if it was set) */ -RGFWDEF RGFW_windowresizefunc RGFW_setWindowRestoredCallback(RGFW_windowresizefunc func); - -/** @} */ - -/** * @defgroup Threads -* @{ */ - -#ifndef RGFW_NO_THREADS -/*! threading functions */ - -/*! NOTE! (for X11/linux) : if you define a window in a thread, it must be run after the original thread's window is created or else there will be a memory error */ -/* - I'd suggest you use sili's threading functions instead - if you're going to use sili - which is a good idea generally -*/ - -#if defined(__unix__) || defined(__APPLE__) || defined(RGFW_WASM) || defined(RGFW_CUSTOM_BACKEND) - typedef void* (* RGFW_threadFunc_ptr)(void*); -#else - typedef DWORD (__stdcall *RGFW_threadFunc_ptr) (LPVOID lpThreadParameter); -#endif - -RGFWDEF RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args); /*!< create a thread */ -RGFWDEF void RGFW_cancelThread(RGFW_thread thread); /*!< cancels a thread */ -RGFWDEF void RGFW_joinThread(RGFW_thread thread); /*!< join thread to current thread */ -RGFWDEF void RGFW_setThreadPriority(RGFW_thread thread, u8 priority); /*!< sets the priority priority */ -#endif - -/** @} */ - -/** * @defgroup gamepad -* @{ */ - -typedef RGFW_ENUM(u8, RGFW_gamepadType) { - RGFW_gamepadMicrosoft = 0, RGFW_gamepadSony, RGFW_gamepadNintendo, RGFW_gamepadLogitech, RGFW_gamepadUnknown -}; - -/*! gamepad count starts at 0*/ -RGFWDEF u32 RGFW_isPressedGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button); -RGFWDEF u32 RGFW_isReleasedGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button); -RGFWDEF u32 RGFW_isHeldGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button); -RGFWDEF u32 RGFW_wasPressedGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button); -RGFWDEF RGFW_point RGFW_getGamepadAxis(RGFW_window* win, u16 controller, u16 whichAxis); -RGFWDEF const char* RGFW_getGamepadName(RGFW_window* win, u16 controller); -RGFWDEF size_t RGFW_getGamepadCount(RGFW_window* win); -RGFWDEF RGFW_gamepadType RGFW_getGamepadType(RGFW_window* win, u16 controller); - -/** @} */ - -/** * @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); - -/* supports openGL, directX, OSMesa, EGL and software rendering */ -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, RGFW_bool set); -RGFWDEF void RGFW_window_setCPURender(RGFW_window* win, RGFW_bool set); - -/*! native API functions */ -#if defined(RGFW_OPENGL) || defined(RGFW_EGL) -/*! OpenGL init hints */ -typedef RGFW_ENUM(u8, RGFW_glHints) { - RGFW_glStencil = 0, /*!< set stencil buffer bit size (8 by default) */ - RGFW_glSamples, /*!< set number of sampiling buffers (4 by default) */ - RGFW_glStereo, /*!< use GL_STEREO (GL_FALSE by default) */ - RGFW_glAuxBuffers, /*!< number of aux buffers (0 by default) */ - RGFW_glDoubleBuffer, /*!< request double buffering */ - RGFW_glRed, RGFW_glGreen, RGFW_glBlue, RGFW_glAlpha, /*!< set RGBA bit sizes */ - RGFW_glDepth, - RGFW_glAccumRed, RGFW_glAccumGreen, RGFW_glAccumBlue,RGFW_glAccumAlpha, /*!< set accumulated RGBA bit sizes */ - RGFW_glSRGB, /*!< request sRGA */ - RGFW_glRobustness, /*!< request a robust context */ - RGFW_glDebug, /*!< request opengl debugging */ - RGFW_glNoError, /*!< request no opengl errors */ - RGFW_glReleaseBehavior, - RGFW_glProfile, - RGFW_glMajor, RGFW_glMinor, - RGFW_glFinalHint, /*!< the final hint (not for setting) */ - RGFW_releaseFlush = 0, RGFW_glReleaseNone, /* RGFW_glReleaseBehavior options */ - RGFW_glCore = 0, RGFW_glCompatibility /*!< RGFW_glProfile options */ -}; -RGFWDEF void RGFW_setGLHint(RGFW_glHints hint, i32 value); -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 */ -void* RGFW_getCurrent_OpenGL(void); /*!< get the current context (OpenGL backend (GLX) (WGL) (EGL) (cocoa) (webgl))*/ -#elif defined(RGFW_VULKAN) - #if defined(RGFW_X11) - #define VK_USE_PLATFORM_XLIB_KHR - #define RGFW_VK_SURFACE "VK_KHR_xlib_surface" - #elif defined(RGFW_WINDOWS) - #define VK_USE_PLATFORM_WIN32_KHR - #define OEMRESOURCE - #define RGFW_VK_SURFACE "VK_KHR_win32_surface" - #elif defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11) - #define VK_USE_PLATFORM_MACOS_MVK - #define RGFW_VK_SURFACE "VK_MVK_macos_surface" - #elif defined(RGFW_WAYLAND) - #define VK_USE_PLATFORM_WAYLAND_KHR - #define RGFW_VK_SURFACE "VK_KHR_wayland_surface" - #else - #define RGFW_VK_SURFACE NULL - #endif - -#include - -RGFWDEF VkResult RGFW_window_createVKSurface(RGFW_window* win, VkInstance instance, VkSurfaceKHR* surface); -#endif - -/** @} */ - -/** * @defgroup Supporting -* @{ */ -RGFWDEF double RGFW_getTime(void); /*!< get time in seconds since RGFW_setTime, which ran when the first window is open */ -RGFWDEF u64 RGFW_getTimeNS(void); /*!< get time in nanoseconds RGFW_setTime, which ran when the first window is open */ -RGFWDEF void RGFW_sleep(u64 milisecond); /*!< sleep for a set time */ -RGFWDEF void RGFW_setTime(double time); /*!< set timer in seconds */ -RGFWDEF u64 RGFW_getTimerValue(void); /*!< get API timer value */ -RGFWDEF u64 RGFW_getTimerFreq(void); /*!< get API time freq */ - -/*< 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_checkFPS(double startTime, u32 frameCount, u32 fpsCap); - -/*!< change which window is the root window */ -RGFWDEF void RGFW_setRootWindow(RGFW_window* win); -RGFWDEF RGFW_window* RGFW_getRootWindow(void); - -/*! standard event queue, used for injecting events and returning source API callback events like any other queue check */ -/* these are all used internally by RGFW */ -void RGFW_eventQueuePush(RGFW_event event); -RGFW_event* RGFW_eventQueuePop(RGFW_window* win); - -/*! - key codes and mouse icon enums -*/ -#undef RGFW_key -typedef RGFW_ENUM(u8, RGFW_key) { - RGFW_keyNULL = 0, - RGFW_escape = '\033', - RGFW_backtick = '`', - RGFW_0 = '0', - RGFW_1 = '1', - RGFW_2 = '2', - RGFW_3 = '3', - RGFW_4 = '4', - RGFW_5 = '5', - RGFW_6 = '6', - RGFW_7 = '7', - RGFW_8 = '8', - RGFW_9 = '9', - - RGFW_minus = '-', - RGFW_equals = '=', - RGFW_backSpace = '\b', - RGFW_tab = '\t', - RGFW_space = ' ', - - RGFW_a = 'a', - RGFW_b = 'b', - RGFW_c = 'c', - RGFW_d = 'd', - RGFW_e = 'e', - RGFW_f = 'f', - RGFW_g = 'g', - RGFW_h = 'h', - RGFW_i = 'i', - RGFW_j = 'j', - RGFW_k = 'k', - RGFW_l = 'l', - RGFW_m = 'm', - RGFW_n = 'n', - RGFW_o = 'o', - RGFW_p = 'p', - RGFW_q = 'q', - RGFW_r = 'r', - RGFW_s = 's', - RGFW_t = 't', - RGFW_u = 'u', - RGFW_v = 'v', - RGFW_w = 'w', - RGFW_x = 'x', - RGFW_y = 'y', - RGFW_z = 'z', - - RGFW_period = '.', - RGFW_comma = ',', - RGFW_slash = '/', - RGFW_bracket = '{', - RGFW_closeBracket = '}', - RGFW_semicolon = ';', - RGFW_apostrophe = '\'', - RGFW_backSlash = '\\', - RGFW_return = '\n', - - RGFW_delete = '\177', /* 127 */ - - RGFW_F1, - RGFW_F2, - RGFW_F3, - RGFW_F4, - RGFW_F5, - RGFW_F6, - RGFW_F7, - RGFW_F8, - RGFW_F9, - RGFW_F10, - RGFW_F11, - RGFW_F12, - - RGFW_capsLock, - RGFW_shiftL, - RGFW_controlL, - RGFW_altL, - RGFW_superL, - RGFW_shiftR, - RGFW_controlR, - RGFW_altR, - RGFW_superR, - RGFW_up, - RGFW_down, - RGFW_left, - RGFW_right, - - RGFW_insert, - RGFW_end, - RGFW_home, - RGFW_pageUp, - RGFW_pageDown, - - RGFW_numLock, - RGFW_KP_Slash, - RGFW_multiply, - RGFW_KP_Minus, - RGFW_KP_1, - RGFW_KP_2, - RGFW_KP_3, - RGFW_KP_4, - RGFW_KP_5, - RGFW_KP_6, - RGFW_KP_7, - RGFW_KP_8, - RGFW_KP_9, - RGFW_KP_0, - RGFW_KP_Period, - RGFW_KP_Return, - RGFW_scrollLock, - RGFW_keyLast -}; - -RGFWDEF u32 RGFW_apiKeyToRGFW(u32 keycode); - -typedef RGFW_ENUM(u8, RGFW_mouseIcons) { - RGFW_mouseNormal = 0, - RGFW_mouseArrow, - RGFW_mouseIbeam, - RGFW_mouseCrosshair, - RGFW_mousePointingHand, - RGFW_mouseResizeEW, - RGFW_mouseResizeNS, - RGFW_mouseResizeNWSE, - RGFW_mouseResizeNESW, - RGFW_mouseResizeAll, - RGFW_mouseNotAllowed, -}; - -/** @} */ - -#endif /* RGFW_HEADER */ -#if defined(RGFW_X11) || defined(RGFW_WAYLAND) - #define RGFW_OS_BASED_VALUE(l, w, m, h) l -#elif defined(RGFW_WINDOWS) - #define RGFW_OS_BASED_VALUE(l, w, m, h) w -#elif defined(RGFW_MACOS) - #define RGFW_OS_BASED_VALUE(l, w, m, h) m -#elif defined(RGFW_WASM) - #define RGFW_OS_BASED_VALUE(l, w, m, h) h -#endif - - -#ifdef RGFW_IMPLEMENTATION -RGFW_bool RGFW_useWaylandBool = 1; - -#ifdef RGFW_DEBUG -#include -#endif - -char* RGFW_clipboard_data; -void RGFW_clipboard_switch(char* newstr) { - if (RGFW_clipboard_data != NULL) - RGFW_FREE(RGFW_clipboard_data); - RGFW_clipboard_data = newstr; -} - -#define RGFW_CHECK_CLIPBOARD() \ - if (size <= 0 && RGFW_clipboard_data != NULL) \ - return (const char*)RGFW_clipboard_data; \ - else if (size <= 0) \ - return "\0"; - -const char* RGFW_readClipboard(size_t* len) { - RGFW_ssize_t size = RGFW_readClipboardPtr(NULL, 0); - RGFW_CHECK_CLIPBOARD(); - char* str = (char*)RGFW_ALLOC(size); - size = RGFW_readClipboardPtr(str, size); - RGFW_CHECK_CLIPBOARD(); - - if (len != NULL) *len = size; - - RGFW_clipboard_switch(str); - return (const char*)str; -} - -RGFW_debugfunc RGFW_debugCallback = NULL; -RGFW_debugfunc RGFW_setDebugCallback(RGFW_debugfunc func) { - RGFW_debugfunc RGFW_debugCallbackPrev = RGFW_debugCallback; - RGFW_debugCallback = func; - return RGFW_debugCallbackPrev; -} - -void RGFW_sendDebugInfo(RGFW_debugType type, RGFW_errorCode err, RGFW_debugContext ctx, const char* msg) { - if (RGFW_debugCallback) RGFW_debugCallback(type, err, ctx, msg); - #ifdef RGFW_DEBUG - switch (type) { - case RGFW_typeInfo: printf("RGFW INFO (%i %i): %s", type, err, msg); break; - case RGFW_typeError: printf("RGFW DEBUG (%i %i): %s", type, err, msg); break; - case RGFW_typeWarning: printf("RGFW WARNING (%i %i): %s", type, err, msg); break; - default: break; - } - - switch (err) { - #ifdef RGFW_BUFFER - case RGFW_errBuffer: case RGFW_infoBuffer: printf(" buffer size: %i %i\n", ctx.win->bufferSize.w, ctx.win->bufferSize.h); - #endif - case RGFW_infoMonitor: printf(": scale (%s):\n rect: {%i, %i, %i, %i}\n physical size:%f %f\n scale: %f %f\n pixelRatio: %f\n refreshRate: %i\n depth: %i\n", ctx.monitor.name, ctx.monitor.x, ctx.monitor.y, ctx.monitor.mode.area.w, ctx.monitor.mode.area.h, ctx.monitor.physW, ctx.monitor.physH, ctx.monitor.scaleX, ctx.monitor.scaleY, ctx.monitor.pixelRatio, ctx.monitor.mode.refreshRate, ctx.monitor.mode.red + ctx.monitor.mode.green + ctx.monitor.mode.blue); break; - case RGFW_infoWindow: printf(" with rect of {%i, %i, %i, %i} \n", ctx.win->r.x, ctx.win->r.y,ctx. win->r.w, ctx.win->r.h); break; - case RGFW_errDirectXContext: printf(" srcError %i\n", ctx.srcError); break; - default: printf("\n"); - } - #endif -} - -u32 RGFW_timerOffset = 0; -void RGFW_setTime(double time) { - RGFW_timerOffset = RGFW_getTimerValue() - (u64)(time * RGFW_getTimerFreq()); -} - -double RGFW_getTime(void) { - return (double) ((RGFW_getTimerValue() - RGFW_timerOffset) / (double) RGFW_getTimerFreq()); -} - -u64 RGFW_getTimeNS(void) { - return (u64)(((RGFW_getTimerValue() - RGFW_timerOffset) * 1e9) / RGFW_getTimerFreq()); -} - -/* -RGFW_IMPLEMENTATION starts with generic RGFW defines - -This is the start of keycode data - - Why not use macros instead of the numbers itself? - Windows -> Not all scancodes keys are macros - Linux -> Only symcodes are values, (XK_0 - XK_1, XK_a - XK_z) are larger than 0xFF00, I can't find any way to work with them without making the array an unreasonable size - MacOS -> windows and linux already don't have keycodes as macros, so there's no point -*/ - - - -/* - 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 RGFW_CUSTOM_BACKEND - -#ifndef __cplusplus -#define RGFW_NEXT , -#define RGFW_MAP -#else -#define RGFW_NEXT ; -#define RGFW_MAP RGFW_keycodes -#endif - -u8 RGFW_keycodes [RGFW_OS_BASED_VALUE(136, 0x15C + 1, 128, DOM_VK_WIN_OEM_CLEAR + 1)] = { -#ifdef __cplusplus - 0 -}; -void RGFW_init_keys(void) { -#endif - RGFW_MAP [RGFW_OS_BASED_VALUE(49, 0x029, 50, DOM_VK_BACK_QUOTE)] = RGFW_backtick RGFW_NEXT - - RGFW_MAP [RGFW_OS_BASED_VALUE(19, 0x00B, 29, DOM_VK_0)] = RGFW_0 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(10, 0x002, 18, DOM_VK_1)] = RGFW_1 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(11, 0x003, 19, DOM_VK_2)] = RGFW_2 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(12, 0x004, 20, DOM_VK_3)] = RGFW_3 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(13, 0x005, 21, DOM_VK_4)] = RGFW_4 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(14, 0x006, 23, DOM_VK_5)] = RGFW_5 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(15, 0x007, 22, DOM_VK_6)] = RGFW_6 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(16, 0x008, 26, DOM_VK_7)] = RGFW_7 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(17, 0x009, 28, DOM_VK_8)] = RGFW_8 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(18, 0x00A, 25, DOM_VK_9)] = RGFW_9, - RGFW_MAP [RGFW_OS_BASED_VALUE(65, 0x039, 49, DOM_VK_SPACE)] = RGFW_space, - RGFW_MAP [RGFW_OS_BASED_VALUE(38, 0x01E, 0, DOM_VK_A)] = RGFW_a RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(56, 0x030, 11, DOM_VK_B)] = RGFW_b RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(54, 0x02E, 8, DOM_VK_C)] = RGFW_c RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(40, 0x020, 2, DOM_VK_D)] = RGFW_d RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(26, 0x012, 14, DOM_VK_E)] = RGFW_e RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(41, 0x021, 3, DOM_VK_F)] = RGFW_f RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(42, 0x022, 5, DOM_VK_G)] = RGFW_g RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(43, 0x023, 4, DOM_VK_H)] = RGFW_h RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(31, 0x017, 34, DOM_VK_I)] = RGFW_i RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(44, 0x024, 38, DOM_VK_J)] = RGFW_j RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(45, 0x025, 40, DOM_VK_K)] = RGFW_k RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(46, 0x026, 37, DOM_VK_L)] = RGFW_l RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(58, 0x032, 46, DOM_VK_M)] = RGFW_m RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(57, 0x031, 45, DOM_VK_N)] = RGFW_n RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(32, 0x018, 31, DOM_VK_O)] = RGFW_o RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(33, 0x019, 35, DOM_VK_P)] = RGFW_p RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(24, 0x010, 12, DOM_VK_Q)] = RGFW_q RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(27, 0x013, 15, DOM_VK_R)] = RGFW_r RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(39, 0x01F, 1, DOM_VK_S)] = RGFW_s RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(28, 0x014, 17, DOM_VK_T)] = RGFW_t RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(30, 0x016, 32, DOM_VK_U)] = RGFW_u RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(55, 0x02F, 9, DOM_VK_V)] = RGFW_v RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(25, 0x011, 13, DOM_VK_W)] = RGFW_w RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(53, 0x02D, 7, DOM_VK_X)] = RGFW_x RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(29, 0x015, 16, DOM_VK_Y)] = RGFW_y RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(52, 0x02C, 6, DOM_VK_Z)] = RGFW_z, - RGFW_MAP [RGFW_OS_BASED_VALUE(60, 0x034, 47, DOM_VK_PERIOD)] = RGFW_period RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(59, 0x033, 43, DOM_VK_COMMA)] = RGFW_comma RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(61, 0x035, 44, DOM_VK_SLASH)] = RGFW_slash RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(34, 0x01A, 33, DOM_VK_OPEN_BRACKET)] = RGFW_bracket RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(35, 0x01B, 30, DOM_VK_CLOSE_BRACKET)] = RGFW_closeBracket RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(47, 0x027, 41, DOM_VK_SEMICOLON)] = RGFW_semicolon RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(48, 0x028, 39, DOM_VK_QUOTE)] = RGFW_apostrophe RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(51, 0x02B, 42, DOM_VK_BACK_SLASH)] = RGFW_backSlash, - RGFW_MAP [RGFW_OS_BASED_VALUE(36, 0x01C, 36, DOM_VK_RETURN)] = RGFW_return RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(119, 0x153, 118, DOM_VK_DELETE)] = RGFW_delete RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(77, 0x145, 72, DOM_VK_NUM_LOCK)] = RGFW_numLock RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(106, 0x135, 82, DOM_VK_DIVIDE)] = RGFW_KP_Slash RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(63, 0x037, 76, DOM_VK_MULTIPLY)] = RGFW_multiply RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(82, 0x04A, 67, DOM_VK_SUBTRACT)] = RGFW_KP_Minus RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(87, 0x04F, 84, DOM_VK_NUMPAD1)] = RGFW_KP_1 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(88, 0x050, 85, DOM_VK_NUMPAD2)] = RGFW_KP_2 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(89, 0x051, 86, DOM_VK_NUMPAD3)] = RGFW_KP_3 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(83, 0x04B, 87, DOM_VK_NUMPAD4)] = RGFW_KP_4 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(84, 0x04C, 88, DOM_VK_NUMPAD5)] = RGFW_KP_5 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(85, 0x04D, 89, DOM_VK_NUMPAD6)] = RGFW_KP_6 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(79, 0x047, 90, DOM_VK_NUMPAD7)] = RGFW_KP_7 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(80, 0x048, 92, DOM_VK_NUMPAD8)] = RGFW_KP_8 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(81, 0x049, 93, DOM_VK_NUMPAD9)] = RGFW_KP_9 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(90, 0x052, 83, DOM_VK_NUMPAD0)] = RGFW_KP_0 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(91, 0x053, 65, DOM_VK_DECIMAL)] = RGFW_KP_Period RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(104, 0x11C, 77, 0)] = RGFW_KP_Return, - RGFW_MAP [RGFW_OS_BASED_VALUE(20, 0x00C, 27, DOM_VK_HYPHEN_MINUS)] = RGFW_minus RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(21, 0x00D, 24, DOM_VK_EQUALS)] = RGFW_equals RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(22, 0x00E, 51, DOM_VK_BACK_SPACE)] = RGFW_backSpace RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(23, 0x00F, 48, DOM_VK_TAB)] = RGFW_tab RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(66, 0x03A, 57, DOM_VK_CAPS_LOCK)] = RGFW_capsLock RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(50, 0x02A, 56, DOM_VK_SHIFT)] = RGFW_shiftL RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(37, 0x01D, 59, DOM_VK_CONTROL)] = RGFW_controlL RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(64, 0x038, 58, DOM_VK_ALT)] = RGFW_altL RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(133, 0x15B, 55, DOM_VK_WIN)] = RGFW_superL, - #if !defined(RGFW_MACOS) && !defined(RGFW_WASM) - RGFW_MAP [RGFW_OS_BASED_VALUE(105, 0x11D, 59, 0)] = RGFW_controlR RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(135, 0x15C, 55, 0)] = RGFW_superR, - RGFW_MAP [RGFW_OS_BASED_VALUE(62, 0x036, 56, 0)] = RGFW_shiftR RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(108, 0x138, 58, 0)] = RGFW_altR, - #endif - RGFW_MAP [RGFW_OS_BASED_VALUE(67, 0x03B, 127, DOM_VK_F1)] = RGFW_F1 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(68, 0x03C, 121, DOM_VK_F2)] = RGFW_F2 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(69, 0x03D, 100, DOM_VK_F3)] = RGFW_F3 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(70, 0x03E, 119, DOM_VK_F4)] = RGFW_F4 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(71, 0x03F, 97, DOM_VK_F5)] = RGFW_F5 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(72, 0x040, 98, DOM_VK_F6)] = RGFW_F6 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(73, 0x041, 99, DOM_VK_F7)] = RGFW_F7 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(74, 0x042, 101, DOM_VK_F8)] = RGFW_F8 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(75, 0x043, 102, DOM_VK_F9)] = RGFW_F9 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(76, 0x044, 110, DOM_VK_F10)] = RGFW_F10 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(95, 0x057, 104, DOM_VK_F11)] = RGFW_F11 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(96, 0x058, 112, DOM_VK_F12)] = RGFW_F12 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(111, 0x148, 126, DOM_VK_UP)] = RGFW_up RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(116, 0x150, 125, DOM_VK_DOWN)] = RGFW_down RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(113, 0x14B, 123, DOM_VK_LEFT)] = RGFW_left RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(114, 0x14D, 124, DOM_VK_RIGHT)] = RGFW_right RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(118, 0x152, 115, DOM_VK_INSERT)] = RGFW_insert RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(115, 0x14F, 120, DOM_VK_END)] = RGFW_end RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(112, 0x149, 117, DOM_VK_PAGE_UP)] = RGFW_pageUp RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(117, 0x151, 122, DOM_VK_PAGE_DOWN)] = RGFW_pageDown RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(9, 0x001, 53, DOM_VK_ESCAPE)] = RGFW_escape RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(110, 0x147, 116, DOM_VK_HOME)] = RGFW_home RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(78, 0x046, 107, DOM_VK_SCROLL_LOCK)] = RGFW_scrollLock RGFW_NEXT -#ifndef __cplusplus -}; -#else -} -#endif - -#undef RGFW_NEXT -#undef RGFW_MAP - -u32 RGFW_apiKeyToRGFW(u32 keycode) { - #ifdef __cplusplus - if (RGFW_keycodes[RGFW_OS_BASED_VALUE(49, 0x029, 50, DOM_VK_BACK_QUOTE)] != RGFW_backtick) { - RGFW_init_keys(); - } - #endif - - /* make sure the key isn't out of bounds */ - if (keycode > sizeof(RGFW_keycodes) / sizeof(u8)) - return 0; - - return RGFW_keycodes[keycode]; -} -#endif - -typedef struct { - RGFW_bool current : 1; - RGFW_bool prev : 1; -} RGFW_keyState; - -RGFW_keyState RGFW_keyboard[RGFW_keyLast] = { {0, 0} }; - -RGFWDEF void RGFW_resetKey(void); -void RGFW_resetKey(void) { - size_t len = RGFW_keyLast; /*!< last_key == length */ - - size_t i; /*!< reset each previous state */ - for (i = 0; i < len; i++) - RGFW_keyboard[i].prev = 0; -} - -/* - this is the end of keycode data -*/ - -/* gamepad data */ -RGFW_keyState RGFW_gamepadPressed[4][18]; /*!< if a key is currently pressed or not (per gamepad) */ -RGFW_point RGFW_gamepadAxes[4][4]; /*!< if a key is currently pressed or not (per gamepad) */ - -RGFW_gamepadType RGFW_gamepads_type[4]; /*!< if a key is currently pressed or not (per gamepad) */ -i32 RGFW_gamepads[4] = {0, 0, 0, 0}; /*!< limit of 4 gamepads at a time */ -char RGFW_gamepads_name[4][128]; /*!< gamepad names */ -u16 RGFW_gamepadCount = 0; /*!< the actual amount of gamepads */ - -#define RGFW_MAX_EVENTS 20 -RGFW_event RGFW_events[RGFW_MAX_EVENTS]; -size_t RGFW_eventLen = 0; -i32 RGFW_eventIndex = 0; -void RGFW_eventQueuePush(RGFW_event event) { - if (RGFW_eventLen >= RGFW_MAX_EVENTS) return; - RGFW_events[RGFW_eventLen] = event; - RGFW_eventLen++; -} - -RGFW_event* RGFW_eventQueuePop(RGFW_window* win) { - if (RGFW_eventLen == 0) return NULL; - - RGFW_event* ev = &RGFW_events[RGFW_eventIndex]; - - RGFW_eventLen--; - if (RGFW_eventLen) - RGFW_eventIndex++; - else - RGFW_eventIndex = 0; - - if (ev->_win != win && ev->_win != NULL) { - RGFW_eventQueuePush(*ev); - return NULL; - } - - ev->droppedFiles = win->event.droppedFiles; - return ev; -} - -RGFW_event* RGFW_window_checkEventCore(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - if (win->event.type == 0 && RGFW_eventLen == 0) - RGFW_resetKey(); - - if (win->event.type == RGFW_quit) { - if (win->_flags & RGFW_windowFreeOnClose) { - RGFW_window_close(win); - return (RGFW_event*)-1; - } - - return &win->event; - } - - if (win->event.type != RGFW_DNDInit) win->event.type = 0; - - /* check queued events */ - RGFW_event* ev = RGFW_eventQueuePop(win); - if (ev != NULL) win->event = *ev; - else return NULL; - - return &win->event; -} - -/* - event callback defines start here -*/ - - -/* - These exist to avoid the - if (func == NULL) check - for (allegedly) better performance -*/ -void RGFW_windowmovefuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); } -void RGFW_windowresizefuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); } -void RGFW_windowquitfuncEMPTY(RGFW_window* win) { RGFW_UNUSED(win); } -void RGFW_focusfuncEMPTY(RGFW_window* win, RGFW_bool inFocus) {RGFW_UNUSED(win); RGFW_UNUSED(inFocus);} -void RGFW_mouseNotifyfuncEMPTY(RGFW_window* win, RGFW_point point, RGFW_bool status) {RGFW_UNUSED(win); RGFW_UNUSED(point); RGFW_UNUSED(status);} -void RGFW_mouseposfuncEMPTY(RGFW_window* win, RGFW_point point, RGFW_point vector) {RGFW_UNUSED(win); RGFW_UNUSED(point); RGFW_UNUSED(vector);} -void RGFW_dndInitfuncEMPTY(RGFW_window* win, RGFW_point point) {RGFW_UNUSED(win); RGFW_UNUSED(point);} -void RGFW_windowrefreshfuncEMPTY(RGFW_window* win) {RGFW_UNUSED(win); } -void RGFW_keyfuncEMPTY(RGFW_window* win, RGFW_key key, char keyChar, RGFW_keymod keyMod, RGFW_bool pressed) {RGFW_UNUSED(win); RGFW_UNUSED(key); RGFW_UNUSED(keyChar); RGFW_UNUSED(keyMod); RGFW_UNUSED(pressed);} -void RGFW_mousebuttonfuncEMPTY(RGFW_window* win, RGFW_mouseButton button, double scroll, RGFW_bool pressed) {RGFW_UNUSED(win); RGFW_UNUSED(button); RGFW_UNUSED(scroll); RGFW_UNUSED(pressed);} -void RGFW_gamepadButtonfuncEMPTY(RGFW_window* win, u16 gamepad, u8 button, RGFW_bool pressed){RGFW_UNUSED(win); RGFW_UNUSED(gamepad); RGFW_UNUSED(button); RGFW_UNUSED(pressed); } -void RGFW_gamepadAxisfuncEMPTY(RGFW_window* win, u16 gamepad, RGFW_point axis[2], u8 axisesCount, u8 whichAxis){RGFW_UNUSED(win); RGFW_UNUSED(gamepad); RGFW_UNUSED(axis); RGFW_UNUSED(axisesCount); RGFW_UNUSED(whichAxis); } -void RGFW_gamepadfuncEMPTY(RGFW_window* win, u16 gamepad, RGFW_bool connected) {RGFW_UNUSED(win); RGFW_UNUSED(gamepad); RGFW_UNUSED(connected);} -void RGFW_dndfuncEMPTY(RGFW_window* win, char** droppedFiles, u32 droppedFilesCount) {RGFW_UNUSED(win); RGFW_UNUSED(droppedFiles); RGFW_UNUSED(droppedFilesCount);} - -RGFW_windowmovefunc RGFW_windowMoveCallback = RGFW_windowmovefuncEMPTY; -RGFW_windowresizefunc RGFW_windowResizeCallback = RGFW_windowresizefuncEMPTY; -RGFW_windowresizefunc RGFW_windowMaximizedCallback = RGFW_windowresizefuncEMPTY; -RGFW_windowresizefunc RGFW_windowMinimizedCallback = RGFW_windowresizefuncEMPTY; -RGFW_windowresizefunc RGFW_windowRestoredCallback = RGFW_windowresizefuncEMPTY; -RGFW_windowquitfunc RGFW_windowQuitCallback = RGFW_windowquitfuncEMPTY; -RGFW_mouseposfunc RGFW_mousePosCallback = RGFW_mouseposfuncEMPTY; -RGFW_windowrefreshfunc RGFW_windowRefreshCallback = RGFW_windowrefreshfuncEMPTY; -RGFW_focusfunc RGFW_focusCallback = RGFW_focusfuncEMPTY; -RGFW_mouseNotifyfunc RGFW_mouseNotifyCallBack = RGFW_mouseNotifyfuncEMPTY; -RGFW_dndfunc RGFW_dndCallback = RGFW_dndfuncEMPTY; -RGFW_dndInitfunc RGFW_dndInitCallback = RGFW_dndInitfuncEMPTY; -RGFW_keyfunc RGFW_keyCallback = RGFW_keyfuncEMPTY; -RGFW_mousebuttonfunc RGFW_mouseButtonCallback = RGFW_mousebuttonfuncEMPTY; -RGFW_gamepadButtonfunc RGFW_gamepadButtonCallback = RGFW_gamepadButtonfuncEMPTY; -RGFW_gamepadAxisfunc RGFW_gamepadAxisCallback = RGFW_gamepadAxisfuncEMPTY; -RGFW_gamepadfunc RGFW_gamepadCallback = RGFW_gamepadfuncEMPTY; - -void RGFW_window_checkEvents(RGFW_window* win, u32 waitMS) { - RGFW_window_eventWait(win, waitMS); - - while (RGFW_window_checkEvent(win) != NULL && RGFW_window_shouldClose(win) == 0) { - if (win->event.type == RGFW_quit) return; - } - - #ifdef RGFW_WASM /* WASM needs to run the sleep function for asyncify */ - RGFW_sleep(0); - #endif -} - -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_windowresizefunc RGFW_setWindowMaximizedCallback(RGFW_windowresizefunc func) { - RGFW_windowresizefunc prev = (RGFW_windowMaximizedCallback == RGFW_windowresizefuncEMPTY) ? NULL : RGFW_windowMaximizedCallback; - RGFW_windowMaximizedCallback = func; - return prev; -} -RGFW_windowresizefunc RGFW_setWindowMinimizedCallback(RGFW_windowresizefunc func) { - RGFW_windowresizefunc prev = (RGFW_windowMinimizedCallback == RGFW_windowresizefuncEMPTY) ? NULL : RGFW_windowMinimizedCallback; - RGFW_windowMinimizedCallback = func; - return prev; -} -RGFW_windowresizefunc RGFW_setWindowRestoredCallback(RGFW_windowresizefunc func) { - RGFW_windowresizefunc prev = (RGFW_windowRestoredCallback == RGFW_windowresizefuncEMPTY) ? NULL : RGFW_windowRestoredCallback; - RGFW_windowRestoredCallback = 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_gamepadButtonfunc RGFW_setgamepadButtonCallback(RGFW_gamepadButtonfunc func) { - RGFW_gamepadButtonfunc prev = (RGFW_gamepadButtonCallback == RGFW_gamepadButtonfuncEMPTY) ? NULL : RGFW_gamepadButtonCallback; - RGFW_gamepadButtonCallback = func; - return prev; -} -RGFW_gamepadAxisfunc RGFW_setgamepadAxisCallback(RGFW_gamepadAxisfunc func) { - RGFW_gamepadAxisfunc prev = (RGFW_gamepadAxisCallback == RGFW_gamepadAxisfuncEMPTY) ? NULL : RGFW_gamepadAxisCallback; - RGFW_gamepadAxisCallback = func; - return prev; -} -RGFW_gamepadfunc RGFW_setGamepadCallback(RGFW_gamepadfunc func) { - RGFW_gamepadfunc prev = (RGFW_gamepadCallback == RGFW_gamepadfuncEMPTY) ? NULL : RGFW_gamepadCallback; - RGFW_gamepadCallback = func; - return prev; -} - -void RGFW_window_checkMode(RGFW_window* win) { - if (RGFW_window_isMinimized(win)) { - win->_flags |= RGFW_windowMinimize; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowMinimized, ._win = win}); - RGFW_windowMinimizedCallback(win, win->r); - } else if (RGFW_window_isMaximized(win)) { - win->_flags |= RGFW_windowMaximize; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowMaximized, ._win = win}); - RGFW_windowMaximizedCallback(win, win->r); - } else if (((win->_flags & RGFW_windowMinimize) && !RGFW_window_isMaximized(win)) || - (win->_flags & RGFW_windowMaximize && !RGFW_window_isMaximized(win))) { - win->_flags &= ~RGFW_windowMinimize; - if (RGFW_window_isMaximized(win) == RGFW_FALSE) win->_flags &= ~RGFW_windowMaximize; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowRestored, ._win = win}); - RGFW_windowRestoredCallback(win, win->r); - } -} - -/* -no more event call back defines -*/ - -#define SET_ATTRIB(a, v) { \ - RGFW_ASSERT(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ - attribs[index++] = a; \ - attribs[index++] = v; \ -} - -#define RGFW_EVENT_PASSED RGFW_BIT(24) /* if a queued event was passed */ -#define RGFW_NO_GPU_RENDER RGFW_BIT(25) /* don't render (using the GPU based API) */ -#define RGFW_NO_CPU_RENDER RGFW_BIT(26) /* don't render (using the CPU based buffer rendering) */ -#define RGFW_HOLD_MOUSE RGFW_BIT(27) /*!< hold the moues still */ -#define RGFW_MOUSE_LEFT RGFW_BIT(28) /* if mouse left the window */ -#define RGFW_WINDOW_ALLOC RGFW_BIT(29) /* if window was allocated by RGFW */ -#define RGFW_BUFFER_ALLOC RGFW_BIT(30) /* if window.buffer was allocated by RGFW */ -#define RGFW_WINDOW_INIT RGFW_BIT(31) /* if window.buffer was allocated by RGFW */ -#define RGFW_INTERNAL_FLAGS (RGFW_EVENT_PASSED | RGFW_NO_GPU_RENDER | RGFW_NO_CPU_RENDER | RGFW_HOLD_MOUSE | RGFW_MOUSE_LEFT | RGFW_WINDOW_ALLOC | RGFW_BUFFER_ALLOC | RGFW_windowFocus) - - -RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, RGFW_windowFlags flags) { - RGFW_window* win = (RGFW_window*)RGFW_ALLOC(sizeof(RGFW_window)); - win->_flags = RGFW_WINDOW_ALLOC; - return RGFW_createWindowPtr(name, rect, flags, win); -} - -#if defined(RGFW_USE_XDL) && defined(RGFW_X11) - #define XDL_IMPLEMENTATION - #include "XDL.h" -#endif - -RGFWDEF void RGFW_window_basic_init(RGFW_window* win, RGFW_rect rect, RGFW_windowFlags flags); -#if defined(RGFW_X11) || defined(RGFW_WINDOWS) -RGFW_mouse* RGFW_hiddenMouse = NULL; -#endif - -RGFW_window* RGFW_root = NULL; -void RGFW_setRootWindow(RGFW_window* win) { RGFW_root = win; } -RGFW_window* RGFW_getRootWindow(void) { return RGFW_root; } - -/* do a basic initialization for RGFW_window, this is to standard it for each OS */ -void RGFW_window_basic_init(RGFW_window* win, RGFW_rect rect, RGFW_windowFlags flags) { - RGFW_UNUSED(flags); - /* rect based the requested flags */ - if (RGFW_root == NULL) { - RGFW_setRootWindow(win); - RGFW_setTime(0); - #ifdef RGFW_X11 - RGFW_root->src.display = XOpenDisplay(NULL); - #endif - } - - #ifdef RGFW_X11 - win->src.clipboard = NULL; - win->src.display = RGFW_root->src.display; - RGFW_ASSERT(win->src.display != NULL); - #endif - - #if defined(RGFW_X11) || defined(RGFW_WINDOWS) - if (RGFW_hiddenMouse == NULL) { - u8 RGFW_blk[] = { 0, 0, 0, 0 }; - RGFW_hiddenMouse = RGFW_loadMouse(RGFW_blk, RGFW_AREA(1, 1), 4); - } - #endif - - if (!(win->_flags & RGFW_WINDOW_ALLOC)) win->_flags = 0; - - /* set and init the new window's data */ - win->r = rect; - win->event.droppedFilesCount = 0; - win->_flags |= flags; - win->event.keyMod = 0; - - win->event.droppedFiles = (char**)RGFW_ALLOC(RGFW_MAX_PATH * RGFW_MAX_DROPS); - for (u32 i = 0; i < RGFW_MAX_DROPS; i++) - win->event.droppedFiles[i] = (char*)(win->event.droppedFiles + RGFW_MAX_DROPS + (i * RGFW_MAX_PATH)); -} - -void RGFW_window_setFlags(RGFW_window* win, RGFW_windowFlags flags) { - RGFW_windowFlags cmpFlags = win->_flags; - if (win->_flags & RGFW_WINDOW_INIT) cmpFlags = 0; - - #ifndef RGFW_NO_MONITOR - if (flags & RGFW_windowScaleToMonitor) RGFW_window_scaleToMonitor(win); - #endif - - if (flags & RGFW_windowCenter) RGFW_window_center(win); - if (flags & RGFW_windowCenterCursor) - RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2))); - if (flags & RGFW_windowNoBorder) RGFW_window_setBorder(win, 0); - else RGFW_window_setBorder(win, 1); - if (flags & RGFW_windowFullscreen) RGFW_window_setFullscreen(win, RGFW_TRUE); - else if (cmpFlags & RGFW_windowFullscreen) RGFW_window_setFullscreen(win, 0); - if (flags & RGFW_windowMaximize) RGFW_window_maximize(win); - else if (cmpFlags & RGFW_windowMaximize) RGFW_window_restore(win); - if (flags & RGFW_windowMinimize) RGFW_window_minimize(win); - else if (cmpFlags & RGFW_windowMinimize) RGFW_window_restore(win); - if (flags & RGFW_windowHideMouse) RGFW_window_showMouse(win, 0); - else if (cmpFlags & RGFW_windowHideMouse) RGFW_window_showMouse(win, 1); - if (flags & RGFW_windowCocoaCHDirToRes) RGFW_moveToMacOSResourceDir(); - if (flags & RGFW_windowFloating) RGFW_window_setFloating(win, 1); - else if (cmpFlags & RGFW_windowFloating) RGFW_window_setFloating(win, 0); - if (flags & RGFW_windowFocus) RGFW_window_focus(win); - win->_flags = flags | (win->_flags & RGFW_INTERNAL_FLAGS); -} - -RGFW_bool RGFW_window_isInFocus(RGFW_window* win) { return RGFW_BOOL(win->_flags & RGFW_windowFocus); } - -void RGFW_window_initBuffer(RGFW_window* win) { - RGFW_window_initBufferSize(win, RGFW_getScreenSize()); -} - -void RGFW_window_initBufferSize(RGFW_window* win, RGFW_area area) { - win->_flags |= RGFW_BUFFER_ALLOC; - #ifndef RGFW_WINDOWS - RGFW_window_initBufferPtr(win, (u8*)RGFW_ALLOC(area.w * area.h * 4), area); - #else /* windows's bitmap allocs memory for us */ - RGFW_window_initBufferPtr(win, (u8*)NULL, area); - #endif -} - -#ifdef RGFW_MACOS -RGFWDEF void RGFW_window_cocoaSetLayer(RGFW_window* win, void* layer); -RGFWDEF void* RGFW_cocoaGetLayer(void); -#endif - -const char* RGFW_className = NULL; -void RGFW_setClassName(const char* name) { RGFW_className = name; } - -#ifndef RGFW_X11 -void RGFW_setXInstName(const char* name) { RGFW_UNUSED(name); } -#endif - -RGFW_keyState RGFW_mouseButtons[RGFW_mouseFinal] = { {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0} }; - -RGFW_bool RGFW_isMousePressed(RGFW_window* win, RGFW_mouseButton button) { - return RGFW_mouseButtons[button].current && (win == NULL || RGFW_window_isInFocus(win)); -} -RGFW_bool RGFW_wasMousePressed(RGFW_window* win, RGFW_mouseButton button) { - return RGFW_mouseButtons[button].prev && (win != NULL || RGFW_window_isInFocus(win)); -} -RGFW_bool RGFW_isMouseHeld(RGFW_window* win, RGFW_mouseButton button) { - return (RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button)); -} -RGFW_bool RGFW_isMouseReleased(RGFW_window* win, RGFW_mouseButton button) { - return (!RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button)); -} - -RGFW_point RGFW_window_getMousePoint(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - return win->_lastMousePoint; -} - -RGFW_bool RGFW_isPressed(RGFW_window* win, RGFW_key key) { - return RGFW_keyboard[key].current && (win == NULL || RGFW_window_isInFocus(win)); -} - -RGFW_bool RGFW_wasPressed(RGFW_window* win, RGFW_key key) { - return RGFW_keyboard[key].prev && (win == NULL || RGFW_window_isInFocus(win)); -} - -RGFW_bool RGFW_isHeld(RGFW_window* win, RGFW_key key) { - return (RGFW_isPressed(win, key) && RGFW_wasPressed(win, key)); -} - -RGFW_bool RGFW_isClicked(RGFW_window* win, RGFW_key key) { - return (RGFW_wasPressed(win, key) && !RGFW_isPressed(win, key)); -} - -RGFW_bool RGFW_isReleased(RGFW_window* win, RGFW_key key) { - return (!RGFW_isPressed(win, key) && RGFW_wasPressed(win, key)); -} - -#ifndef RGFW_CUSTOM_BACKEND -void RGFW_window_makeCurrent(RGFW_window* win) { -#if defined(RGFW_OPENGL) - RGFW_window_makeCurrent_OpenGL(win); -#else - RGFW_UNUSED(win); -#endif -} -#endif - -RGFWDEF void RGFW_setBit(u32* data, u32 bit, RGFW_bool value); -void RGFW_setBit(u32* data, u32 bit, RGFW_bool value) { - if (value) - *data |= bit; - else if (!value && (*(data) & bit)) - *data ^= bit; -} - -void RGFW_window_setGPURender(RGFW_window* win, RGFW_bool set) { - RGFW_setBit(&win->_flags, RGFW_NO_GPU_RENDER, !set); -} - -void RGFW_window_setCPURender(RGFW_window* win, RGFW_bool set) { - RGFW_setBit(&win->_flags, RGFW_NO_CPU_RENDER, !set); -} - -void RGFW_window_center(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - RGFW_area screenR = RGFW_getScreenSize(); - RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2)); -} - -RGFW_bool RGFW_monitor_scaleToWindow(RGFW_monitor mon, RGFW_window* win) { - RGFW_ASSERT(win != NULL); - - RGFW_monitorMode mode; - mode.area = RGFW_AREA(win->r.w, win->r.h); - return RGFW_monitor_requestMode(mon, mode, RGFW_monitorScale); -} - - -void RGFW_splitBPP(u32 bpp, RGFW_monitorMode* mode) { - if (bpp == 32) bpp = 24; - mode->red = mode->green = mode->blue = bpp / 3; - - u32 delta = bpp - (mode->red * 3); // handle leftovers - if (delta >= 1) mode->green = mode->green + 1; - if (delta == 2) mode->red = mode->red + 1; -} - -RGFW_bool RGFW_monitorModeCompare(RGFW_monitorMode mon, RGFW_monitorMode mon2, RGFW_modeRequest request) { - return (((mon.area.w == mon2.area.w && mon.area.h == mon2.area.h) || !(request & RGFW_monitorScale)) && - ((mon.refreshRate == mon2.refreshRate) || !(request & RGFW_monitorRefresh)) && - ((mon.red == mon2.red && mon.green == mon2.green && mon.blue == mon2.blue) || !(request & RGFW_monitorRGB))); -} - -RGFW_bool RGFW_window_shouldClose(RGFW_window* win) { - return (win == NULL || win->event.type == RGFW_quit || RGFW_isPressed(win, RGFW_escape)); -} - -void RGFW_window_setShouldClose(RGFW_window* win) { win->event.type = RGFW_quit; RGFW_windowQuitCallback(win); } - -#ifndef RGFW_NO_MONITOR -void RGFW_window_scaleToMonitor(RGFW_window* win) { - RGFW_monitor monitor = RGFW_window_getMonitor(win); - if (monitor.scaleX == 0 && monitor.scaleY == 0) - return; - - RGFW_window_resize(win, RGFW_AREA((u32)(monitor.scaleX * (float)win->r.w), (u32)(monitor.scaleY * (float)win->r.h))); -} - -void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m) { - RGFW_window_move(win, RGFW_POINT(m.x + win->r.x, m.y + win->r.y)); -} -#endif - -RGFW_bool RGFW_window_setIcon(RGFW_window* win, u8* icon, RGFW_area a, i32 channels) { - return RGFW_window_setIconEx(win, icon, a, channels, RGFW_iconBoth); -} - -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->_flags & RGFW_HOLD_MOUSE)) - return; - - if (!area.w && !area.h) - area = RGFW_AREA(win->r.w / 2, win->r.h / 2); - - win->_flags |= RGFW_HOLD_MOUSE; - RGFW_captureCursor(win, win->r); - 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) { - win->_flags &= ~RGFW_HOLD_MOUSE; - RGFW_releaseCursor(win); -} - -u32 RGFW_checkFPS(double startTime, u32 frameCount, u32 fpsCap) { - double deltaTime = RGFW_getTime() - startTime; - if (deltaTime == 0) return 0; - - double fps = (frameCount / deltaTime); /* the numer of frames over the time it took for them to render */ - if (fpsCap && fps > fpsCap) { - double frameTime = frameCount / (float)fpsCap; /* how long it should take to finish the frames */ - double sleepTime = frameTime - deltaTime; /* subtract how long it should have taken with how long it did take */ - - if (sleepTime > 0) RGFW_sleep((u32)(sleepTime * 1000)); - } - - return (u32) fps; -} - -#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) -void RGFW_RGB_to_BGR(RGFW_window* win, u8* data) { - #if !defined(RGFW_BUFFER_BGR) && !defined(RGFW_OSMESA) - 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->bufferSize.w) + x * 4; - - u8 red = data[index]; - data[index] = win->buffer[index + 2]; - data[index + 2] = red; - } - } - #endif -} -#endif - -u32 RGFW_isPressedGamepad(RGFW_window* win, u8 c, RGFW_gamepadCodes button) { - RGFW_UNUSED(win); - return RGFW_gamepadPressed[c][button].current; -} -u32 RGFW_wasPressedGamepad(RGFW_window* win, u8 c, RGFW_gamepadCodes button) { - RGFW_UNUSED(win); - return RGFW_gamepadPressed[c][button].prev; -} -u32 RGFW_isReleasedGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button) { - RGFW_UNUSED(win); - return !RGFW_isPressedGamepad(win, controller, button) && RGFW_wasPressedGamepad(win, controller, button); -} -u32 RGFW_isHeldGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button) { - RGFW_UNUSED(win); - return RGFW_isPressedGamepad(win, controller, button) && RGFW_wasPressedGamepad(win, controller, button); -} - -RGFW_point RGFW_getGamepadAxis(RGFW_window* win, u16 controller, u16 whichAxis) { - RGFW_UNUSED(win); - return RGFW_gamepadAxes[controller][whichAxis]; -} -const char* RGFW_getGamepadName(RGFW_window* win, u16 controller) { - RGFW_UNUSED(win); - return (const char*)RGFW_gamepads_name[controller]; -} - -size_t RGFW_getGamepadCount(RGFW_window* win) { - RGFW_UNUSED(win); - return RGFW_gamepadCount; -} - -RGFW_gamepadType RGFW_getGamepadType(RGFW_window* win, u16 controller) { - RGFW_UNUSED(win); - return RGFW_gamepads_type[controller]; -} - -RGFWDEF void RGFW_updateKeyMod(RGFW_window* win, RGFW_keymod mod, RGFW_bool value); -void RGFW_updateKeyMod(RGFW_window* win, RGFW_keymod mod, RGFW_bool value) { - RGFW_setBit((u32*)&win->event.keyMod, mod, value); -} - -RGFWDEF void RGFW_updateKeyModsPro(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll); -void RGFW_updateKeyModsPro(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll) { - RGFW_updateKeyMod(win, RGFW_modCapsLock, capital); - RGFW_updateKeyMod(win, RGFW_modNumLock, numlock); - RGFW_updateKeyMod(win, RGFW_modControl, control); - RGFW_updateKeyMod(win, RGFW_modAlt, alt); - RGFW_updateKeyMod(win, RGFW_modShift, shift); - RGFW_updateKeyMod(win, RGFW_modSuper, super); - RGFW_updateKeyMod(win, RGFW_modScrollLock, scroll); -} - -RGFWDEF void RGFW_updateKeyMods(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool scroll); -void RGFW_updateKeyMods(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool scroll) { - RGFW_updateKeyModsPro(win, capital, numlock, - RGFW_isPressed(win, RGFW_controlL) || RGFW_isPressed(win, RGFW_controlR), - RGFW_isPressed(win, RGFW_altL) || RGFW_isPressed(win, RGFW_altR), - RGFW_isPressed(win, RGFW_shiftL) || RGFW_isPressed(win, RGFW_shiftR), - RGFW_isPressed(win, RGFW_superL) || RGFW_isPressed(win, RGFW_superR), - scroll); -} - -RGFWDEF void RGFW_window_showMouseFlags(RGFW_window* win, RGFW_bool show); -void RGFW_window_showMouseFlags(RGFW_window* win, RGFW_bool show) { - if (show && (win->_flags & RGFW_windowHideMouse)) - win->_flags ^= RGFW_windowHideMouse; - else if (!show && !(win->_flags & RGFW_windowHideMouse)) - win->_flags |= RGFW_windowHideMouse; -} - -RGFW_bool RGFW_window_mouseHidden(RGFW_window* win) { - return (RGFW_bool)RGFW_BOOL(win->_flags & RGFW_windowHideMouse); -} - -RGFW_bool RGFW_window_borderless(RGFW_window* win) { - return (RGFW_bool)RGFW_BOOL(win->_flags & RGFW_windowNoBorder); -} - -RGFW_bool RGFW_window_isFullscreen(RGFW_window* win){ return RGFW_BOOL(win->_flags & RGFW_windowFullscreen); } -RGFW_bool RGFW_window_allowsDND(RGFW_window* win) { return RGFW_BOOL(win->_flags & RGFW_windowAllowDND); } - -#ifndef RGFW_WINDOWS -void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow) { - RGFW_setBit(&win->_flags, RGFW_windowAllowDND, allow); -} -#endif - -#if defined(RGFW_X11) || defined(RGFW_MACOS) || defined(RGFW_WASM) || defined(RGFW_WAYLAND) -#include -struct timespec; - -#ifndef RGFW_NO_UNIX_CLOCK -int nanosleep(const struct timespec* duration, struct timespec* rem); -int clock_gettime(clockid_t clk_id, struct timespec* tp); -#endif - -int setenv(const char *name, const char *value, int overwrite); -#endif - -#if defined(RGFW_X11) || defined(RGFW_WINDOWS) -void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) { - RGFW_window_showMouseFlags(win, show); - if (show == 0) - RGFW_window_setMouse(win, RGFW_hiddenMouse); - else - RGFW_window_setMouseDefault(win); -} -#endif - -#ifndef RGFW_MACOS -void RGFW_moveToMacOSResourceDir(void) { } -#endif - -/* - graphics API specific code (end of generic code) - starts here -*/ - - -/* - OpenGL defines start here (Normal, EGL, OSMesa) -*/ - -#if defined(RGFW_OPENGL) || defined(RGFW_EGL) - -#ifdef RGFW_WINDOWS - #define WIN32_LEAN_AND_MEAN - #define OEMRESOURCE - #include -#endif - -#if !defined(__APPLE__) && !defined(RGFW_NO_GL_HEADER) - #include -#elif defined(__APPLE__) - #ifndef GL_SILENCE_DEPRECATION - #define GL_SILENCE_DEPRECATION - #endif - #include - #include -#endif - -/* EGL, normal OpenGL only */ -#ifndef RGFW_EGL -i32 RGFW_GL_HINTS[RGFW_glFinalHint] = {8, 4, -#else -i32 RGFW_GL_HINTS[RGFW_glFinalHint] = {0, 0, -#endif - 0, 0, 1, 8, 8, 8, 8, 24, 0, 0, 0, 0, 0, 0, 0, 0, RGFW_glReleaseNone, RGFW_glCore, 0, 0}; - -void RGFW_setGLHint(RGFW_glHints hint, i32 value) { - if (hint < RGFW_glFinalHint && hint) RGFW_GL_HINTS[hint] = value; -} - -/* OPENGL normal only (no EGL / OSMesa) */ -#if !defined(RGFW_EGL) && !defined(RGFW_CUSTOM_BACKEND) - -#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) - -#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_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_ACCUM_RED_SIZE RGFW_OS_BASED_VALUE(14, 0x201E, 0, 0) - #define RGFW_GL_ACCUM_GREEN_SIZE RGFW_OS_BASED_VALUE(15, 0x201F, 0, 0) - #define RGFW_GL_ACCUM_BLUE_SIZE RGFW_OS_BASED_VALUE(16, 0x2020, 0, 0) - #define RGFW_GL_ACCUM_ALPHA_SIZE RGFW_OS_BASED_VALUE(17, 0x2021, 0, 0) - #define RGFW_GL_SRGB RGFW_OS_BASED_VALUE(0x20b2, 0x3089, 0, 0) - #define RGFW_GL_NOERROR RGFW_OS_BASED_VALUE(0x31b3, 0x31b3, 0, 0) - #define RGFW_GL_FLAGS RGFW_OS_BASED_VALUE(GLX_CONTEXT_FLAGS_ARB, 0x2094, 0, 0) - #define RGFW_GL_RELEASE_BEHAVIOR RGFW_OS_BASED_VALUE(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, 0x2097 , 0, 0) - #define RGFW_GL_CONTEXT_RELEASE RGFW_OS_BASED_VALUE(GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB, 0x2098, 0, 0) - #define RGFW_GL_CONTEXT_NONE RGFW_OS_BASED_VALUE(GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB, 0x0000, 0, 0) - #define RGFW_GL_FLAGS RGFW_OS_BASED_VALUE(GLX_CONTEXT_FLAGS_ARB, 0x2094, 0, 0) - #define RGFW_GL_DEBUG_BIT RGFW_OS_BASED_VALUE(GLX_CONTEXT_FLAGS_ARB, 0x2094, 0, 0) - #define RGFW_GL_ROBUST_BIT RGFW_OS_BASED_VALUE(GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB, 0x00000004, 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 - #define WGL_PIXEL_TYPE_ARB 0x2013 - #define WGL_TYPE_RGBA_ARB 0x202B - - #define WGL_TRANSPARENT_ARB 0x200A -#endif - -/* 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 */ -u32* RGFW_initFormatAttribs(u32 useSoftware) { - RGFW_UNUSED(useSoftware); - static u32 attribs[] = { - #if defined(RGFW_X11) || defined(RGFW_WINDOWS) - RGFW_GL_RENDER_TYPE, - RGFW_GL_FULL_FORMAT, - RGFW_GL_DRAW, 1, - RGFW_GL_DRAW_TYPE , RGFW_GL_USE_RGBA, - #endif - - #ifdef RGFW_X11 - GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, - #endif - - #ifdef RGFW_MACOS - 72, - 8, 24, - #endif - - #ifdef RGFW_WINDOWS - WGL_SUPPORT_OPENGL_ARB, 1, - WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, - WGL_COLOR_BITS_ARB, 32, - #endif - 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, 0, 0 - }; - - size_t index = (sizeof(attribs) / sizeof(attribs[0])) - 27; - - #define RGFW_GL_ADD_ATTRIB(attrib, attVal) \ - if (attVal) { \ - attribs[index] = attrib;\ - attribs[index + 1] = attVal;\ - index += 2;\ - } - - #if defined(RGFW_MACOS) && defined(RGFW_COCOA_GRAPHICS_SWITCHING) - RGFW_GL_ADD_ATTRIB(96, kCGLPFASupportsAutomaticGraphicsSwitching); - #endif - - RGFW_GL_ADD_ATTRIB(RGFW_GL_DOUBLEBUFFER, 1); - - RGFW_GL_ADD_ATTRIB(RGFW_GL_ALPHA_SIZE, RGFW_GL_HINTS[RGFW_glAlpha]); - RGFW_GL_ADD_ATTRIB(RGFW_GL_DEPTH_SIZE, RGFW_GL_HINTS[RGFW_glDepth]); - RGFW_GL_ADD_ATTRIB(RGFW_GL_STENCIL_SIZE, RGFW_GL_HINTS[RGFW_glStencil]); - RGFW_GL_ADD_ATTRIB(RGFW_GL_STEREO, RGFW_GL_HINTS[RGFW_glStereo]); - RGFW_GL_ADD_ATTRIB(RGFW_GL_AUX_BUFFERS, RGFW_GL_HINTS[RGFW_glAuxBuffers]); - - #if defined(RGFW_X11) || defined(RGFW_WINDOWS) - RGFW_GL_ADD_ATTRIB(RGFW_GL_RED_SIZE, RGFW_GL_HINTS[RGFW_glRed]); - RGFW_GL_ADD_ATTRIB(RGFW_GL_GREEN_SIZE, RGFW_GL_HINTS[RGFW_glBlue]); - RGFW_GL_ADD_ATTRIB(RGFW_GL_BLUE_SIZE, RGFW_GL_HINTS[RGFW_glGreen]); - #endif - - #if defined(RGFW_X11) || defined(RGFW_WINDOWS) - RGFW_GL_ADD_ATTRIB(RGFW_GL_ACCUM_RED_SIZE, RGFW_GL_HINTS[RGFW_glAccumRed]); - RGFW_GL_ADD_ATTRIB(RGFW_GL_ACCUM_GREEN_SIZE, RGFW_GL_HINTS[RGFW_glAccumBlue]); - RGFW_GL_ADD_ATTRIB(RGFW_GL_ACCUM_BLUE_SIZE, RGFW_GL_HINTS[RGFW_glAccumGreen]); - RGFW_GL_ADD_ATTRIB(RGFW_GL_ACCUM_ALPHA_SIZE, RGFW_GL_HINTS[RGFW_glAccumAlpha]); - RGFW_GL_ADD_ATTRIB(RGFW_GL_SRGB, RGFW_GL_HINTS[RGFW_glSRGB]); - RGFW_GL_ADD_ATTRIB(RGFW_GL_NOERROR, RGFW_GL_HINTS[RGFW_glNoError]); - - if (RGFW_GL_HINTS[RGFW_glReleaseBehavior] == RGFW_releaseFlush) { - RGFW_GL_ADD_ATTRIB(RGFW_GL_RELEASE_BEHAVIOR, RGFW_GL_CONTEXT_RELEASE); - } else if (RGFW_GL_HINTS[RGFW_glReleaseBehavior] == RGFW_glReleaseNone) { - RGFW_GL_ADD_ATTRIB(RGFW_GL_RELEASE_BEHAVIOR, RGFW_GL_CONTEXT_NONE); - } - - u32 flags = 0; - if (RGFW_GL_HINTS[RGFW_glDebug]) flags |= RGFW_GL_DEBUG_BIT; - if (RGFW_GL_HINTS[RGFW_glRobustness]) flags |= RGFW_GL_ROBUST_BIT; - RGFW_GL_ADD_ATTRIB(RGFW_GL_FLAGS, flags); - #else - u32 accumSize = (RGFW_GL_HINTS[RGFW_glAccumRed] + RGFW_GL_HINTS[RGFW_glAccumGreen] + RGFW_GL_HINTS[RGFW_glAccumBlue] + RGFW_GL_HINTS[RGFW_glAccumAlpha]) / 4; - RGFW_GL_ADD_ATTRIB(14, accumSize); - #endif - - #ifndef RGFW_X11 - RGFW_GL_ADD_ATTRIB(RGFW_GL_SAMPLES, RGFW_GL_HINTS[RGFW_glSamples]); - #endif - - #ifdef RGFW_MACOS - if (useSoftware) { - RGFW_GL_ADD_ATTRIB(70, kCGLRendererGenericFloatID); - } else { - attribs[index] = RGFW_GL_RENDER_TYPE; - index += 1; - } - #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_GL_HINTS[RGFW_glMinor] >= 4 || RGFW_GL_HINTS[RGFW_glMinor] >= 3) { - attribs[index + 1] = (u32) ((RGFW_GL_HINTS[RGFW_glMinor] >= 4) ? 0x4100 : 0x3200); - } - #endif - - RGFW_GL_ADD_ATTRIB(0, 0); - - return attribs; -} - -/* EGL only (no OSMesa nor normal OPENGL) */ -#elif defined(RGFW_EGL) - -#include - -#if defined(RGFW_LINK_EGL) - typedef EGLBoolean(EGLAPIENTRY* PFN_eglInitialize)(EGLDisplay, EGLint*, EGLint*); - - PFNEGLINITIALIZEPROC eglInitializeSource; - PFNEGLGETCONFIGSPROC eglGetConfigsSource; - PFNEGLCHOOSECONFIgamepadROC eglChooseConfigSource; - PFNEGLCREATEWINDOWSURFACEPROC eglCreateWindowSurfaceSource; - PFNEGLCREATECONTEXTPROC eglCreateContextSource; - PFNEGLMAKECURRENTPROC eglMakeCurrentSource; - PFNEGLGETDISPLAYPROC eglGetDisplaySource; - PFNEGLSWAPBUFFERSPROC eglSwapBuffersSource; - PFNEGLSWAPINTERVALPROC eglSwapIntervalSource; - PFNEGLBINDAPIPROC eglBindAPISource; - PFNEGLDESTROYCONTEXTPROC eglDestroyContextSource; - PFNEGLTERMINATEPROC eglTerminateSource; - PFNEGLDESTROYSURFACEPROC eglDestroySurfaceSource; - - #define eglInitialize eglInitializeSource - #define eglGetConfigs eglGetConfigsSource - #define eglChooseConfig eglChooseConfigSource - #define eglCreateWindowSurface eglCreateWindowSurfaceSource - #define eglCreateContext eglCreateContextSource - #define eglMakeCurrent eglMakeCurrentSource - #define eglGetDisplay eglGetDisplaySource - #define eglSwapBuffers eglSwapBuffersSource - #define eglSwapInterval eglSwapIntervalSource - #define eglBindAPI eglBindAPISource - #define eglDestroyContext eglDestroyContextSource - #define eglTerminate eglTerminateSource - #define eglDestroySurface eglDestroySurfaceSource; -#endif - - -#define EGL_SURFACE_MAJOR_VERSION_KHR 0x3098 -#define EGL_SURFACE_MINOR_VERSION_KHR 0x30fb - -#ifndef RGFW_GL_ADD_ATTRIB -#define RGFW_GL_ADD_ATTRIB(attrib, attVal) \ - if (attVal) { \ - attribs[index] = attrib;\ - attribs[index + 1] = attVal;\ - index += 2;\ - } -#endif - - -void RGFW_createOpenGLContext(RGFW_window* win) { -#if defined(RGFW_LINK_EGL) - eglInitializeSource = (PFNEGLINITIALIZEPROC) eglGetProcAddress("eglInitialize"); - eglGetConfigsSource = (PFNEGLGETCONFIGSPROC) eglGetProcAddress("eglGetConfigs"); - eglChooseConfigSource = (PFNEGLCHOOSECONFIgamepadROC) eglGetProcAddress("eglChooseConfig"); - eglCreateWindowSurfaceSource = (PFNEGLCREATEWINDOWSURFACEPROC) eglGetProcAddress("eglCreateWindowSurface"); - eglCreateContextSource = (PFNEGLCREATECONTEXTPROC) eglGetProcAddress("eglCreateContext"); - eglMakeCurrentSource = (PFNEGLMAKECURRENTPROC) eglGetProcAddress("eglMakeCurrent"); - eglGetDisplaySource = (PFNEGLGETDISPLAYPROC) eglGetProcAddress("eglGetDisplay"); - eglSwapBuffersSource = (PFNEGLSWAPBUFFERSPROC) eglGetProcAddress("eglSwapBuffers"); - eglSwapIntervalSource = (PFNEGLSWAPINTERVALPROC) eglGetProcAddress("eglSwapInterval"); - eglBindAPISource = (PFNEGLBINDAPIPROC) eglGetProcAddress("eglBindAPI"); - eglDestroyContextSource = (PFNEGLDESTROYCONTEXTPROC) eglGetProcAddress("eglDestroyContext"); - eglTerminateSource = (PFNEGLTERMINATEPROC) eglGetProcAddress("eglTerminate"); - eglDestroySurfaceSource = (PFNEGLDESTROYSURFACEPROC) eglGetProcAddress("eglDestroySurface"); -#endif /* RGFW_LINK_EGL */ - - #ifdef RGFW_WINDOWS - win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.hdc); - #elif defined(RGFW_MACOS) - win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType)0); - #elif defined(RGFW_WAYLAND) - if (RGFW_useWaylandBool) - win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.wl_display); - else - win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.display); - #else - win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.display); - #endif - - EGLint major, minor; - - eglInitialize(win->src.EGL_display, &major, &minor); - - #ifndef EGL_OPENGL_ES1_BIT - #define EGL_OPENGL_ES1_BIT 0x1 - #endif - - EGLint egl_config[] = { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RENDERABLE_TYPE, - #ifdef RGFW_OPENGL_ES1 - EGL_OPENGL_ES1_BIT, - #elif defined(RGFW_OPENGL_ES3) - EGL_OPENGL_ES3_BIT, - #elif defined(RGFW_OPENGL_ES2) - EGL_OPENGL_ES2_BIT, - #else - EGL_OPENGL_BIT, - #endif - EGL_NONE, EGL_NONE - }; - - { - size_t index = 7; - EGLint* attribs = egl_config; - - RGFW_GL_ADD_ATTRIB(EGL_RED_SIZE, RGFW_GL_HINTS[RGFW_glRed]); - RGFW_GL_ADD_ATTRIB(EGL_GREEN_SIZE, RGFW_GL_HINTS[RGFW_glBlue]); - RGFW_GL_ADD_ATTRIB(EGL_BLUE_SIZE, RGFW_GL_HINTS[RGFW_glGreen]); - RGFW_GL_ADD_ATTRIB(EGL_ALPHA_SIZE, RGFW_GL_HINTS[RGFW_glAlpha]); - RGFW_GL_ADD_ATTRIB(EGL_DEPTH_SIZE, RGFW_GL_HINTS[RGFW_glDepth]); - - if (RGFW_GL_HINTS[RGFW_glSRGB]) - RGFW_GL_ADD_ATTRIB(0x3089, RGFW_GL_HINTS[RGFW_glSRGB]); - - RGFW_GL_ADD_ATTRIB(EGL_NONE, EGL_NONE); - } - - EGLConfig config; - EGLint numConfigs; - eglChooseConfig(win->src.EGL_display, egl_config, &config, 1, &numConfigs); - - #if defined(RGFW_MACOS) - void* layer = RGFW_cocoaGetLayer(); - - RGFW_window_cocoaSetLayer(win, layer); - - win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) layer, NULL); - #elif defined(RGFW_WINDOWS) - win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL); - #elif defined(RGFW_WAYLAND) - if (RGFW_useWaylandBool) - win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.eglWindow, NULL); - else - win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL); - #else - win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL); - #endif - - EGLint attribs[] = { - EGL_CONTEXT_CLIENT_VERSION, - #ifdef RGFW_OPENGL_ES1 - 1, - #else - 2, - #endif - 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_GL_HINTS[RGFW_glStencil]); - RGFW_GL_ADD_ATTRIB(EGL_SAMPLES, RGFW_GL_HINTS[RGFW_glSamples]); - - if (RGFW_GL_HINTS[RGFW_glDoubleBuffer]) - RGFW_GL_ADD_ATTRIB(EGL_RENDER_BUFFER, EGL_BACK_BUFFER); - - if (RGFW_GL_HINTS[RGFW_glMinor]) { - attribs[1] = RGFW_GL_HINTS[RGFW_glMinor]; - - RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MAJOR_VERSION, RGFW_GL_HINTS[RGFW_glMinor]); - RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MINOR_VERSION, RGFW_GL_HINTS[RGFW_glMajor]); - - if (RGFW_GL_HINTS[RGFW_glProfile] == RGFW_glCore) { - 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); - } - } - - RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_ROBUST_ACCESS, RGFW_GL_HINTS[RGFW_glRobustness]); - RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_DEBUG, RGFW_GL_HINTS[RGFW_glDebug]); - if (RGFW_GL_HINTS[RGFW_glReleaseBehavior] == RGFW_releaseFlush) { - RGFW_GL_ADD_ATTRIB(0x2097, 0x2098); - } else { - RGFW_GL_ADD_ATTRIB(0x2097, 0x0000); - } - - #if defined(RGFW_OPENGL_ES1) || defined(RGFW_OPENGL_ES2) || defined(RGFW_OPENGL_ES3) - eglBindAPI(EGL_OPENGL_ES_API); - #else - eglBindAPI(EGL_OPENGL_API); - #endif - - win->src.EGL_context = eglCreateContext(win->src.EGL_display, config, EGL_NO_CONTEXT, attribs); - - if (win->src.EGL_context == NULL) { - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errEGLContext, RGFW_DEBUG_CTX(win, 0), "failed to create an EGL opengl context"); - return; - } - - 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); -} - -void* RGFW_getCurrent_OpenGL(void) { return eglGetCurrentContext(); } - -#ifdef RGFW_APPLE -void* RGFWnsglFramework = NULL; -#elif defined(RGFW_WINDOWS) -static HMODULE RGFW_wgl_dll = NULL; -#endif - -void* RGFW_getProcAddress(const char* procname) { - #if defined(RGFW_WINDOWS) - void* proc = (void*) GetProcAddress(RGFW_wgl_dll, procname); - - if (proc) - return proc; - #endif - - return (void*) eglGetProcAddress(procname); -} - -void RGFW_closeEGL(RGFW_window* win) { - eglDestroySurface(win->src.EGL_display, win->src.EGL_surface); - eglDestroyContext(win->src.EGL_display, win->src.EGL_context); - - eglTerminate(win->src.EGL_display); -} - -void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { - RGFW_ASSERT(win != NULL); - - eglSwapInterval(win->src.EGL_display, swapInterval); - -} - -#endif /* RGFW_EGL */ - -/* - end of RGFW_EGL defines -*/ -/* end of RGFW_GL (OpenGL, EGL, OSMesa )*/ - -/* - RGFW_VULKAN defines -*/ -#elif defined(RGFW_VULKAN) - -VkResult RGFW_window_createVKSurface(RGFW_window* win, VkInstance instance, VkSurfaceKHR* surface) { - assert(win != NULL); assert(instance); - assert(surface != NULL); - - *surface = VK_NULL_HANDLE; - -#ifdef RGFW_X11 - VkXlibSurfaceCreateInfoKHR x11 = { VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, 0, 0, (Display*) win->src.display, (Window) win->src.window }; - return vkCreateXlibSurfaceKHR(instance, &x11, NULL, surface); -#elif defined(RGFW_WINDOWS) - VkWin32SurfaceCreateInfoKHR win32 = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, 0, 0, GetModuleHandle(NULL), (HWND)win->src.window }; - - return vkCreateWin32SurfaceKHR(instance, &win32, NULL, surface); -#elif defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11) - VkMacOSSurfaceCreateFlagsMVK macos = { VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK, 0, 0, vulkWin->display, (void *)win->src.window }; - - return vkCreateMacOSSurfaceMVK(instance, &macos, NULL, surface); -#endif -} -#endif /* end of RGFW_vulkan */ - -/* -This is where OS specific stuff starts -*/ - - -#if (defined(RGFW_WAYLAND) || defined(RGFW_X11)) && !defined(RGFW_NO_LINUX) - int RGFW_eventWait_forceStop[] = {0, 0, 0}; /* for wait events */ - - #if defined(__linux__) - #include - #include - #include - #include - - u32 RGFW_linux_updateGamepad(RGFW_window* win) { - /* check for new gamepads */ - static const char* str[] = {"/dev/input/js0", "/dev/input/js1", "/dev/input/js2", "/dev/input/js3", "/dev/input/js4", "/dev/input/js5"}; - static u8 RGFW_rawGamepads[6]; - - for (size_t i = 0; i < 6; i++) { - size_t index = RGFW_gamepadCount; - if (RGFW_rawGamepads[i]) { - struct input_id device_info; - if (ioctl(RGFW_rawGamepads[i], EVIOCGID, &device_info) == -1) { - if (errno == ENODEV) { - RGFW_rawGamepads[i] = 0; - } - } - continue; - } - - i32 js = open(str[i], O_RDONLY); - - if (js <= 0) - break; - - if (RGFW_gamepadCount >= 4) { - close(js); - break; - } - - RGFW_rawGamepads[i] = 1; - - int axes, buttons; - if (ioctl(js, JSIOCGAXES, &axes) < 0 || ioctl(js, JSIOCGBUTTONS, &buttons) < 0) { - close(js); - continue; - } - - if (buttons <= 5 || buttons >= 30) { - close(js); - continue; - } - - RGFW_gamepadCount++; - - RGFW_gamepads[index] = js; - - ioctl(js, JSIOCGNAME(sizeof(RGFW_gamepads_name[index])), RGFW_gamepads_name[index]); - RGFW_gamepads_name[index][sizeof(RGFW_gamepads_name[index]) - 1] = 0; - - u8 j; - for (j = 0; j < 16; j++) - RGFW_gamepadPressed[index][j] = (RGFW_keyState){0, 0}; - - win->event.type = RGFW_gamepadConnected; - - RGFW_gamepads_type[index] = RGFW_gamepadUnknown; - if (RGFW_STRSTR(RGFW_gamepads_name[index], "Microsoft") || RGFW_STRSTR(RGFW_gamepads_name[index], "X-Box")) - RGFW_gamepads_type[index] = RGFW_gamepadMicrosoft; - else if (RGFW_STRSTR(RGFW_gamepads_name[index], "PlayStation") || RGFW_STRSTR(RGFW_gamepads_name[index], "PS3") || RGFW_STRSTR(RGFW_gamepads_name[index], "PS4") || RGFW_STRSTR(RGFW_gamepads_name[index], "PS5")) - RGFW_gamepads_type[index] = RGFW_gamepadSony; - else if (RGFW_STRSTR(RGFW_gamepads_name[index], "Nintendo")) - RGFW_gamepads_type[index] = RGFW_gamepadNintendo; - else if (RGFW_STRSTR(RGFW_gamepads_name[index], "Logitech")) - RGFW_gamepads_type[index] = RGFW_gamepadLogitech; - - win->event.gamepad = index; - RGFW_gamepadCallback(win, index, 1); - return 1; - } - - /* check gamepad events */ - u8 i; - - for (i = 0; i < RGFW_gamepadCount; i++) { - struct js_event e; - if (RGFW_gamepads[i] == 0) - continue; - - i32 flags = fcntl(RGFW_gamepads[i], F_GETFL, 0); - fcntl(RGFW_gamepads[i], F_SETFL, flags | O_NONBLOCK); - - ssize_t bytes; - while ((bytes = read(RGFW_gamepads[i], &e, sizeof(e))) > 0) { - switch (e.type) { - case JS_EVENT_BUTTON: { - size_t typeIndex = 0; - if (RGFW_gamepads_type[i] == RGFW_gamepadMicrosoft) typeIndex = 1; - else if (RGFW_gamepads_type[i] == RGFW_gamepadLogitech) typeIndex = 2; - - win->event.type = e.value ? RGFW_gamepadButtonPressed : RGFW_gamepadButtonReleased; - u8 RGFW_linux2RGFW[3][RGFW_gamepadR3 + 8] = {{ /* ps */ - RGFW_gamepadA, RGFW_gamepadB, RGFW_gamepadY, RGFW_gamepadX, RGFW_gamepadL1, RGFW_gamepadR1, RGFW_gamepadL2, RGFW_gamepadR2, - RGFW_gamepadSelect, RGFW_gamepadStart, RGFW_gamepadHome, RGFW_gamepadL3, RGFW_gamepadR3, RGFW_gamepadUp, RGFW_gamepadDown, RGFW_gamepadLeft, RGFW_gamepadRight, - },{ /* xbox */ - RGFW_gamepadA, RGFW_gamepadB, RGFW_gamepadX, RGFW_gamepadY, RGFW_gamepadL1, RGFW_gamepadR1, RGFW_gamepadSelect, RGFW_gamepadStart, - RGFW_gamepadHome, RGFW_gamepadL3, RGFW_gamepadR3, 255, 255, RGFW_gamepadUp, RGFW_gamepadDown, RGFW_gamepadLeft, RGFW_gamepadRight - },{ /* Logitech */ - RGFW_gamepadA, RGFW_gamepadB, RGFW_gamepadX, RGFW_gamepadY, RGFW_gamepadL1, RGFW_gamepadR1, RGFW_gamepadL2, RGFW_gamepadR2, - RGFW_gamepadSelect, RGFW_gamepadStart, RGFW_gamepadHome, RGFW_gamepadL3, RGFW_gamepadR3, RGFW_gamepadUp, RGFW_gamepadDown, RGFW_gamepadLeft, RGFW_gamepadRight - } - }; - - win->event.button = RGFW_linux2RGFW[typeIndex][e.number]; - win->event.gamepad = i; - if (win->event.button == 255) break; - - RGFW_gamepadPressed[i][win->event.button].prev = RGFW_gamepadPressed[i][win->event.button].current; - RGFW_gamepadPressed[i][win->event.button].current = e.value; - RGFW_gamepadButtonCallback(win, i, win->event.button, e.value); - - return 1; - } - case JS_EVENT_AXIS: { - size_t axis = e.number / 2; - if (axis == 2) axis = 1; - - ioctl(RGFW_gamepads[i], JSIOCGAXES, &win->event.axisesCount); - win->event.axisesCount = 2; - - if (axis < 3) { - if (e.number == 0 || e.number == 3) - RGFW_gamepadAxes[i][axis].x = (e.value / 32767.0f) * 100; - else if (e.number == 1 || e.number == 4) { - RGFW_gamepadAxes[i][axis].y = (e.value / 32767.0f) * 100; - } - } - - win->event.axis[axis] = RGFW_gamepadAxes[i][axis]; - win->event.type = RGFW_gamepadAxisMove; - win->event.gamepad = i; - win->event.whichAxis = axis; - RGFW_gamepadAxisCallback(win, i, win->event.axis, win->event.axisesCount, win->event.whichAxis); - return 1; - } - default: break; - } - } - if (bytes == -1 && errno == ENODEV) { - RGFW_gamepadCount--; - close(RGFW_gamepads[i]); - RGFW_gamepads[i] = 0; - - win->event.type = RGFW_gamepadDisconnected; - win->event.gamepad = i; - RGFW_gamepadCallback(win, i, 0); - return 1; - } - } - return 0; - } - - #endif -#endif - - - -/* - - Start of Wayland defines - - -*/ - -#ifdef RGFW_WAYLAND -/* -Wayland TODO: (out of date) -- fix RGFW_keyPressed lock state - - RGFW_windowMoved, the window was moved (by the user) - RGFW_windowResized the window was resized (by the user), [on WASM 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_DNDInit - -- window args: - #define RGFW_windowNoResize the window cannot be resized by the user - #define RGFW_windowAllowDND the window supports drag and drop - #define RGFW_scaleToMonitor 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; - -/* wayland global garbage (wayland bad, X11 is fine (ish) (not really)) */ -#include "xdg-shell.h" -#include "xdg-decoration-unstable-v1.h" - -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, -}; - -RGFW_bool 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); - 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); -} - -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_eventQueuePush((RGFW_event){.type = RGFW_quit, ._win = win}); - RGFW_windowQuitCallback(win); -} - -static void shm_format_handler(void *data, - struct wl_shm *shm, uint32_t format) -{ - RGFW_UNUSED(data); RGFW_UNUSED(shm); -} - -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_eventQueuePush((RGFW_event){.type = RGFW_mouseEnter, - .point = RGFW_POINT(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)), - ._win = win}); - - 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_eventQueuePush((RGFW_event){.type = RGFW_mouseLeave, - .point = win->event.point, - ._win = win}); - - 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); - - RGFW_ASSERT(RGFW_mouse_win != NULL); - RGFW_eventQueuePush((RGFW_event){.type = RGFW_mousePosChanged, - .point = RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y)), - ._win = RGFW_mouse_win}); - - RGFW_mousePosCallback(RGFW_mouse_win, RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y)), RGFW_mouse_win->event.vector); -} -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); - RGFW_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_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonPressed + state, - .button = b, - ._win = RGFW_mouse_win}); - 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); - RGFW_ASSERT(RGFW_mouse_win != NULL); - - double scroll = wl_fixed_to_double(value); - - RGFW_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonPressed, - .button = RGFW_mouseScrollUp + (scroll < 0), - .scroll = scroll, - ._win = RGFW_mouse_win}); - - 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_key_win->_flags |= RGFW_windowFocus; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_focusIn, ._win = RGFW_key_win}); - 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_eventQueuePush((RGFW_event){.type = RGFW_focusOut, ._win = win}); - win->_flags &= ~RGFW_windowFocus; - 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); - - if (RGFW_key_win == NULL) return; - - xkb_keysym_t keysym = xkb_state_key_get_one_sym(xkb_state, key + 8); - - u32 RGFW_key = RGFW_apiKeyToRGFW(key + 8); - RGFW_keyboard[RGFW_key].prev = RGFW_keyboard[RGFW_key].current; - RGFW_keyboard[RGFW_key].current = state; - - RGFW_eventQueuePush((RGFW_event){.type = RGFW_keyPressed + state, - .key = RGFW_key, - .keyChar = (u8)keysym, - .repeat = RGFW_isHeld(RGFW_key_win, RGFW_key), - ._win = RGFW_key_win}); - - RGFW_updateKeyMods(RGFW_key_win, xkb_keymap_mod_get_index(keymap, "Lock"), xkb_keymap_mod_get_index(keymap, "Mod2"), xkb_keymap_mod_get_index(keymap, "ScrollLock")); - RGFW_keyCallback(RGFW_key_win, RGFW_key, (u8)keysym, RGFW_key_win->event.keyMod, 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_window* win = (RGFW_window*)data; - RGFW_UNUSED(version); - if (RGFW_STRNCMP(interface, "wl_compositor", 16) == 0) { - win->src.compositor = wl_registry_bind(registry, - id, &wl_compositor_interface, 4); - } else if (RGFW_STRNCMP(interface, "xdg_wm_base", 12) == 0) { - win->src.xdg_wm_base = wl_registry_bind(registry, - id, &xdg_wm_base_interface, 1); - } else if (RGFW_STRNCMP(interface, zxdg_decoration_manager_v1_interface.name, 255) == 0) { - decoration_manager = wl_registry_bind(registry, id, &zxdg_decoration_manager_v1_interface, 1); - } else if (RGFW_STRNCMP(interface, "wl_shm", 7) == 0) { - win->src.shm = wl_registry_bind(registry, - id, &wl_shm_interface, 1); - wl_shm_add_listener(win->src.shm, &shm_listener, NULL); - } else if (RGFW_STRNCMP(interface,"wl_seat", 8) == 0) { - win->src.seat = wl_registry_bind(registry, id, &wl_seat_interface, 1); - wl_seat_add_listener(win->src.seat, &seat_listener, NULL); - } -} - -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); - 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; - } -} - -size_t wl_stringlen(char* name) { - size_t i = 0; - for (i; name[i]; i++); - return i; -} - -static int anonymous_shm_open(void) { - char name[] = "/RGFW-wayland-XXXXXX"; - int retries = 100; - - do { - randname(name + wl_stringlen(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) { - RGFW_UNUSED(data); RGFW_UNUSED(cb); RGFW_UNUSED(time); - - #ifdef RGFW_BUFFER - RGFW_window* win = (RGFW_window*)data; - if ((win->_flags & RGFW_NO_CPU_RENDER)) - return; - - 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, -}; -#endif /* RGFW_WAYLAND */ -#if !defined(RGFW_NO_X11) && defined(RGFW_WAYLAND) -void RGFW_useWayland(RGFW_bool wayland) { RGFW_useWaylandBool = wayland; } -#define RGFW_GOTO_WAYLAND(fallback) if (RGFW_useWaylandBool && fallback == 0) goto wayland -#else -#define RGFW_GOTO_WAYLAND(fallback) -void RGFW_useWayland(RGFW_bool wayland) { RGFW_UNUSED(wayland); } -#endif - -/* - End of Wayland defines -*/ - -/* - - -Start of Linux / Unix defines - - -*/ - -#ifdef RGFW_UNIX -#if !defined(RGFW_NO_X11_CURSOR) && defined(RGFW_X11) -#include -#endif - -#include - -#ifndef RGFW_NO_DPI -#include -#include -#endif - -#include -#include -#include -#include - -#include /* for converting keycode to string */ -#include /* for hiding */ -#include -#include -#include - -#include /* for data limits (mainly used in drag and drop functions) */ -#include - - -#if defined(__linux__) && !defined(RGFW_NO_LINUX) -#include -#endif - -/* atoms needed for drag and drop */ -Atom XdndAware, XtextPlain, XtextUriList; -Atom RGFW_XUTF8_STRING = 0; - -Atom wm_delete_window = 0, RGFW_XCLIPBOARD = 0; - -#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) - typedef XcursorImage* (*PFN_XcursorImageCreate)(int, int); - typedef void (*PFN_XcursorImageDestroy)(XcursorImage*); - typedef Cursor(*PFN_XcursorImageLoadCursor)(Display*, const XcursorImage*); -#endif -#ifdef RGFW_OPENGL - typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); -#endif - -#if !defined(RGFW_NO_X11_XI_PRELOAD) - typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int); - PFN_XISelectEvents XISelectEventsSRC = NULL; - #define XISelectEvents XISelectEventsSRC - - void* X11Xihandle = NULL; -#endif - -#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) - PFN_XcursorImageLoadCursor XcursorImageLoadCursorSRC = NULL; - PFN_XcursorImageCreate XcursorImageCreateSRC = NULL; - PFN_XcursorImageDestroy XcursorImageDestroySRC = NULL; - - #define XcursorImageLoadCursor XcursorImageLoadCursorSRC - #define XcursorImageCreate XcursorImageCreateSRC - #define XcursorImageDestroy XcursorImageDestroySRC - - void* X11Cursorhandle = NULL; -#endif - -const char* RGFW_instName = NULL; -void RGFW_setXInstName(const char* name) { - RGFW_instName = name; -} - -#if defined(RGFW_OPENGL) && !defined(RGFW_EGL) - void* RGFW_getProcAddress(const char* procname) { return (void*) glXGetProcAddress((GLubyte*) procname); } -#endif - -void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area) { - RGFW_GOTO_WAYLAND(0); - - #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - win->buffer = (u8*)buffer; - win->bufferSize = area; - - RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoBuffer, RGFW_DEBUG_CTX(win, 0), "createing a 4 channel buffer"); - #ifdef RGFW_X11 - #ifdef RGFW_OSMESA - win->src.ctx = OSMesaCreateContext(OSMESA_BGRA, NULL); - OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, area.w, area.h); - #endif - - win->src.bitmap = XCreateImage( - win->src.display, XDefaultVisual(win->src.display, XDefaultScreen(win->src.display)), - 32, ZPixmap, 0, NULL, area.w, area.h, - 32, 0 - ); - #endif - #ifdef RGFW_WAYLAND - wayland: - size_t size = win->r.w * win->r.h * 4; - int fd = create_shm_file(size); - if (fd < 0) { - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, RGFW_DEBUG_CTX(win, fd),"Failed to create a buffer."); - exit(1); - - win->src.buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (win->src.buffer == MAP_FAILED) { - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, RGFW_DEBUG_CTX(win, MAP_FAILED), "mmap failed!"); - close(fd); - exit(1); - } - - win->_flags |= RGFW_BUFFER_ALLOC; - - struct wl_shm_pool* pool = wl_shm_create_pool(win->src.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_ARGRGFW_bool888); - 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 < area.w * area.h * 4; i += 4) { - RGFW_MEMCPY(&win->buffer[i], color, 4); - } - - RGFW_MEMCPY(win->src.buffer, win->buffer, win->r.w * win->r.h * 4); - - #if defined(RGFW_OSMESA) - win->src.ctx = OSMesaCreateContext(OSMESA_BGRA, NULL); - OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, area.w, area.h); - #endif - #endif - #else - #ifdef RGFW_WAYLAND - wayland: - #endif - - RGFW_UNUSED(win); RGFW_UNUSED(buffer); RGFW_UNUSED(area); - #endif -} - -#define RGFW_LOAD_ATOM(name) \ - static Atom name = 0; \ - if (name == 0) name = XInternAtom(RGFW_root->src.display, #name, False); - -void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { - RGFW_setBit(&win->_flags, RGFW_windowNoBorder, !border); - - RGFW_GOTO_WAYLAND(0); - #ifdef RGFW_X11 - RGFW_LOAD_ATOM(_MOTIF_WM_HINTS); - - struct __x11WindowHints { - unsigned long flags, functions, decorations, status; - long input_mode; - } hints; - hints.flags = 2; - hints.decorations = border; - - XChangeProperty(win->src.display, win->src.window, _MOTIF_WM_HINTS, _MOTIF_WM_HINTS, 32, - PropModeReplace, (u8*)&hints, 5 - ); - - if (RGFW_window_isHidden(win) == 0) { - RGFW_window_hide(win); - RGFW_window_show(win); - } - - #endif - #ifdef RGFW_WAYLAND - wayland: - #endif -} - -void RGFW_releaseCursor(RGFW_window* win) { -RGFW_GOTO_WAYLAND(0); -#ifdef RGFW_X11 - XUngrabPointer(win->src.display, CurrentTime); - - /* disable raw input */ - unsigned char mask[] = { 0 }; - XIEventMask em; - em.deviceid = XIAllMasterDevices; - em.mask_len = sizeof(mask); - em.mask = mask; - - XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1); -#endif -#ifdef RGFW_WAYLAND - wayland: -#endif -} - -void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { -RGFW_GOTO_WAYLAND(0); -#ifdef RGFW_X11 - /* 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; - - XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1); - - XGrabPointer(win->src.display, win->src.window, True, PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); - RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (i32)(r.w / 2), win->r.y + (i32)(r.h / 2))); -#endif -#ifdef RGFW_WAYLAND - wayland: -#endif -} - -#define RGFW_LOAD_LIBRARY(x, lib) if (x == NULL) x = dlopen(lib, RTLD_LAZY | RTLD_LOCAL) -#define RGFW_PROC_DEF(proc, name) if (name##SRC == NULL && proc != NULL) name##SRC = (PFN_##name)(void*)dlsym(proc, #name) - -RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) { - #ifdef RGFW_USE_XDL - XDL_init(); - #endif - - #if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) - #if defined(__CYGWIN__) - RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor-1.so"); - #elif defined(__OpenBSD__) || defined(__NetBSD__) - RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor.so"); - #else - RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor.so.1"); - #endif - RGFW_PROC_DEF(X11Cursorhandle, XcursorImageCreate); - RGFW_PROC_DEF(X11Cursorhandle, XcursorImageDestroy); - RGFW_PROC_DEF(X11Cursorhandle, XcursorImageLoadCursor); - #endif - - #if !defined(RGFW_NO_X11_XI_PRELOAD) - #if defined(__CYGWIN__) - RGFW_LOAD_LIBRARY(X11Xihandle, "libXi-6.so"); - #elif defined(__OpenBSD__) || defined(__NetBSD__) - RGFW_LOAD_LIBRARY(X11Xihandle, "libXi.so"); - #else - RGFW_LOAD_LIBRARY(X11Xihandle, "libXi.so.6"); - #endif - RGFW_PROC_DEF(X11Xihandle, XISelectEvents); - #endif - - XInitThreads(); /*!< init X11 threading */ - - if (flags & RGFW_windowOpenglSoftware) - setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1); - - RGFW_window_basic_init(win, rect, flags); - -#ifdef RGFW_WAYLAND - win->src.compositor = NULL; -#endif - RGFW_GOTO_WAYLAND(0); -#ifdef RGFW_X11 - u64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask | LeaveWindowMask | EnterWindowMask | ExposureMask; /*!< X11 events accepted */ - - #if defined(RGFW_OPENGL) && !defined(RGFW_EGL) - u32* visual_attribs = (u32*)RGFW_initFormatAttribs(flags & RGFW_windowOpenglSoftware); - i32 fbcount; - GLXFBConfig* fbc = glXChooseFBConfig(win->src.display, DefaultScreen(win->src.display), (i32*) visual_attribs, &fbcount); - - i32 best_fbc = -1; - - if (fbcount == 0) { - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to find any valid GLX visual configs"); - return NULL; - } - - u32 i; - for (i = 0; i < (u32)fbcount; i++) { - XVisualInfo* vi = glXGetVisualFromFBConfig(win->src.display, fbc[i]); - if (vi == NULL) - continue; - - XFree(vi); - - i32 samp_buf, samples; - glXGetFBConfigAttrib(win->src.display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf); - glXGetFBConfigAttrib(win->src.display, fbc[i], GLX_SAMPLES, &samples); - - if ((!(flags & RGFW_windowTransparent) || vi->depth == 32) && - (best_fbc < 0 || samp_buf) && (samples == RGFW_GL_HINTS[RGFW_glSamples] || best_fbc == -1)) { - best_fbc = i; - } - } - - if (best_fbc == -1) { - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to get a valid GLX visual"); - return NULL; - } - - GLXFBConfig bestFbc = fbc[best_fbc]; - - /* Get a visual */ - XVisualInfo* vi = glXGetVisualFromFBConfig(win->src.display, bestFbc); - - XFree(fbc); - #else - XVisualInfo viNorm; - - viNorm.visual = DefaultVisual(win->src.display, DefaultScreen(win->src.display)); - - viNorm.depth = 0; - XVisualInfo* vi = &viNorm; - - XMatchVisualInfo(win->src.display, DefaultScreen(win->src.display), 32, TrueColor, vi); /*!< for RGBA backgrounds */ - #endif - /* make X window attrubutes */ - XSetWindowAttributes swa; - Colormap cmap; - - swa.colormap = cmap = XCreateColormap(win->src.display, - DefaultRootWindow(win->src.display), - vi->visual, AllocNone); - - swa.background_pixmap = None; - swa.border_pixel = 0; - swa.event_mask = event_mask; - - swa.background_pixel = 0; - - /* create the window */ - win->src.window = XCreateWindow(win->src.display, DefaultRootWindow(win->src.display), win->r.x, win->r.y, win->r.w, win->r.h, - 0, vi->depth, InputOutput, vi->visual, - CWColormap | CWBorderPixel | CWBackPixel | CWEventMask, &swa); - - XFreeColors(win->src.display, cmap, NULL, 0, 0); - - win->src.gc = XCreateGC(win->src.display, win->src.window, 0, NULL); - - #if defined(RGFW_OPENGL) && !defined(RGFW_EGL) - XFree(vi); - #endif - - // In your .desktop app, if you set the property - // StartupWMClass=RGFW that will assoicate the launcher icon - // with your application - robrohan - - if (RGFW_className == NULL) - RGFW_className = (char*)name; - - XClassHint hint; - hint.res_class = (char*)RGFW_className; - if (RGFW_instName == NULL) hint.res_name = (char*)name; - else hint.res_name = (char*)RGFW_instName; - XSetClassHint(win->src.display, win->src.window, &hint); - - if ((flags & RGFW_windowNoInitAPI) == 0) { - #if defined(RGFW_OPENGL) && !defined(RGFW_EGL) /* 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; - if (RGFW_GL_HINTS[RGFW_glProfile] == RGFW_glCore) - context_attribs[1] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB; - else - context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; - - if (RGFW_GL_HINTS[RGFW_glMinor] || RGFW_GL_HINTS[RGFW_glMajor]) { - context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB; - context_attribs[3] = RGFW_GL_HINTS[RGFW_glMinor]; - context_attribs[4] = GLX_CONTEXT_MINOR_VERSION_ARB; - context_attribs[5] = RGFW_GL_HINTS[RGFW_glMajor]; - } - - glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; - glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) - glXGetProcAddressARB((GLubyte*) "glXCreateContextAttribsARB"); - - GLXContext ctx = NULL; - - if (RGFW_root != NULL && RGFW_root != win) - ctx = RGFW_root->src.ctx; - - win->src.ctx = glXCreateContextAttribsARB(win->src.display, bestFbc, ctx, True, context_attribs); - #endif - } - - #ifndef RGFW_NO_MONITOR - if (flags & RGFW_windowScaleToMonitor) - RGFW_window_scaleToMonitor(win); - #endif - - if (flags & RGFW_windowNoResize) { /* make it so the user can't resize the window */ - XSizeHints sh; - sh.flags = (1L << 4) | (1L << 5); - sh.min_width = sh.max_width = win->r.w; - sh.min_height = sh.max_height = win->r.h; - - XSetWMSizeHints(win->src.display, (Drawable) win->src.window, &sh, XA_WM_NORMAL_HINTS); - - win->_flags |= RGFW_windowNoResize; - } - - XSelectInput(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) { - wm_delete_window = XInternAtom(win->src.display, "WM_DELETE_WINDOW", False); - RGFW_XUTF8_STRING = XInternAtom(win->src.display, "UTF8_STRING", False); - RGFW_XCLIPBOARD = XInternAtom(win->src.display, "CLIPBOARD", False); - } - - XSetWMProtocols(win->src.display, (Drawable) win->src.window, &wm_delete_window, 1); - - /* connect the context to the window */ - #if defined(RGFW_OPENGL) && !defined(RGFW_EGL) - if ((flags & RGFW_windowNoInitAPI) == 0) - glXMakeCurrent(win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx); - #endif - - /* set the background */ - RGFW_window_setName(win, name); - - XMapWindow(win->src.display, (Drawable) win->src.window); /* draw the window */ - XMoveWindow(win->src.display, (Drawable) win->src.window, win->r.x, win->r.y); /*!< move the window to it's proper cords */ - - if (flags & RGFW_windowAllowDND) { /* init drag and drop atoms and turn on drag and drop for this window */ - win->_flags |= RGFW_windowAllowDND; - - /* actions */ - XtextUriList = XInternAtom(win->src.display, "text/uri-list", False); - XtextPlain = XInternAtom(win->src.display, "text/plain", False); - XdndAware = XInternAtom(win->src.display, "XdndAware", False); - const u8 version = 5; - - XChangeProperty(win->src.display, win->src.window, - XdndAware, 4, 32, - PropModeReplace, &version, 1); /*!< turns on drag and drop */ - } - - #ifdef RGFW_EGL - if ((flags & RGFW_windowNoInitAPI) == 0) - RGFW_createOpenGLContext(win); - #endif - RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new window was created"); - RGFW_window_setMouseDefault(win); - RGFW_window_setFlags(win, flags); - - return win; /*return newly created window */ -#endif -#ifdef RGFW_WAYLAND - wayland: - RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningWayland, RGFW_DEBUG_CTX(win, 0), "RGFW Wayland support is experimental"); - - win->src.wl_display = wl_display_connect(NULL); - if (win->src.wl_display == NULL) { - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errWayland, RGFW_DEBUG_CTX(win, 0), "Failed to load Wayland display"); - #ifdef RGFW_X11 - RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningWayland, RGFW_DEBUG_CTX(win, 0), "Falling back to X11"); - RGFW_useWayland(0); - return RGFW_createWindowPtr(name, rect, flags, win); - #endif - return NULL; - } - - - #ifdef RGFW_X11 - XSetWindowAttributes attributes; - attributes.background_pixel = 0; - attributes.override_redirect = True; - - win->src.window = XCreateWindow(win->src.display, DefaultRootWindow(win->src.display), 0, 0, 1, 1, 0, CopyFromParent, InputOutput, CopyFromParent, - CWBackPixel | CWOverrideRedirect, &attributes); - - XMapWindow(win->src.display, win->src.window); - XFlush(win->src.display); - if (wm_delete_window == 0) { - wm_delete_window = XInternAtom(win->src.display, "WM_DELETE_WINDOW", False); - RGFW_XUTF8_STRING = XInternAtom(win->src.display, "UTF8_STRING", False); - RGFW_XCLIPBOARD = XInternAtom(win->src.display, "CLIPBOARD", False); - } - #endif - - struct wl_registry *registry = wl_display_get_registry(win->src.wl_display); - wl_registry_add_listener(registry, ®istry_listener, win); - - wl_display_roundtrip(win->src.wl_display); - wl_display_dispatch(win->src.wl_display); - - if (win->src.compositor == NULL) { - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errWayland, RGFW_DEBUG_CTX(win, 0), "Can't find compositor."); - return NULL; - } - - if (RGFW_wl_cursor_theme == NULL) { - RGFW_wl_cursor_theme = wl_cursor_theme_load(NULL, 24, win->src.shm); - RGFW_cursor_surface = wl_compositor_create_surface(win->src.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); - } - - xdg_wm_base_add_listener(win->src.xdg_wm_base, &xdg_wm_base_listener, NULL); - - xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - - win->src.surface = wl_compositor_create_surface(win->src.compositor); - wl_surface_set_user_data(win->src.surface, win); - - win->src.xdg_surface = xdg_wm_base_get_xdg_surface(win->src.xdg_wm_base, win->src.surface); - xdg_surface_add_listener(win->src.xdg_surface, &xdg_surface_listener, NULL); - - xdg_wm_base_set_user_data(win->src.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 (!(flags & RGFW_windowNoBorder)) { - win->src.decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( - decoration_manager, win->src.xdg_toplevel); - } - - if (flags & RGFW_windowOpenglSoftware) - setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1); - - wl_display_roundtrip(win->src.wl_display); - - wl_surface_commit(win->src.surface); - - /* wait for the surface to be configured */ - while (wl_display_dispatch(win->src.wl_display) != -1 && !RGFW_wl_configured) { } - - #ifdef RGFW_OPENGL - if ((flags & RGFW_windowNoInitAPI) == 0) { - win->src.eglWindow = wl_egl_window_create(win->src.surface, win->r.w, win->r.h); - RGFW_createOpenGLContext(win); - } - #endif - 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); - RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new window was created"); - - #ifndef RGFW_NO_MONITOR - if (flags & RGFW_windowScaleToMonitor) - RGFW_window_scaleToMonitor(win); - #endif - - RGFW_window_setMouseDefault(win); - RGFW_window_setFlags(win, flags); - return win; /* return newly created window */ -#endif -} - -RGFW_area RGFW_getScreenSize(void) { - RGFW_GOTO_WAYLAND(1); - RGFW_ASSERT(RGFW_root != NULL); - - #ifdef RGFW_X11 - Screen* scrn = DefaultScreenOfDisplay(RGFW_root->src.display); - return RGFW_AREA(scrn->width, scrn->height); - #endif - #ifdef RGFW_WAYLAND - wayland: return RGFW_AREA(RGFW_root->r.w, RGFW_root->r.h); // TODO - #endif -} - -RGFW_point RGFW_getGlobalMousePoint(void) { - RGFW_ASSERT(RGFW_root != NULL); - - RGFW_point RGFWMouse; - - i32 x, y; - u32 z; - Window window1, window2; - XQueryPointer(RGFW_root->src.display, XDefaultRootWindow(RGFW_root->src.display), &window1, &window2, &RGFWMouse.x, &RGFWMouse.y, &x, &y, &z); - - return RGFWMouse; -} - -RGFWDEF void RGFW_XHandleClipboardSelection(RGFW_window* win, XEvent* event); - -void RGFW_XHandleClipboardSelection(RGFW_window* win, XEvent* event) { - RGFW_LOAD_ATOM(ATOM_PAIR); - RGFW_LOAD_ATOM(MULTIPLE); - RGFW_LOAD_ATOM(TARGETS); - RGFW_LOAD_ATOM(SAVE_TARGETS); - - const XSelectionRequestEvent* request = &event->xselectionrequest; - const Atom formats[] = { RGFW_XUTF8_STRING, XA_STRING }; - const int formatCount = sizeof(formats) / sizeof(formats[0]); - - if (request->target == TARGETS) { - const Atom targets[] = { TARGETS, MULTIPLE, RGFW_XUTF8_STRING, XA_STRING }; - - XChangeProperty(win->src.display, request->requestor, request->property, - XA_ATOM, 32, PropModeReplace, (u8*) targets, sizeof(targets) / sizeof(Atom)); - } else if (request->target == MULTIPLE) { - Atom* targets = NULL; - - Atom actualType = 0; - int actualFormat = 0; - unsigned long count = 0, bytesAfter = 0; - - XGetWindowProperty(RGFW_root->src.display, request->requestor, request->property, 0, LONG_MAX, - False, ATOM_PAIR, &actualType, &actualFormat, &count, &bytesAfter, (u8**) &targets); - - unsigned long i; - for (i = 0; i < (u32)count; i += 2) { - if (targets[i] == RGFW_XUTF8_STRING || targets[i] == XA_STRING) - XChangeProperty(RGFW_root->src.display, request->requestor, targets[i + 1], targets[i], - 8, PropModeReplace, (const unsigned char *)win->src.clipboard, win->src.clipboard_len); - else - targets[i + 1] = None; - } - - XChangeProperty(RGFW_root->src.display, - request->requestor, request->property, ATOM_PAIR, 32, - PropModeReplace, (u8*) targets, count); - - XFlush(RGFW_root->src.display); - XFree(targets); - } else if (request->target == SAVE_TARGETS) - XChangeProperty(win->src.display, request->requestor, request->property, 0, 32, PropModeReplace, NULL, 0); - else { - for (int i = 0; i < formatCount; i++) { - if (request->target != formats[i]) - continue; - XChangeProperty(win->src.display, request->requestor, request->property, request->target, - 8, PropModeReplace, (u8*) win->src.clipboard, win->src.clipboard_len); - } - } - - XEvent reply = { SelectionNotify }; - reply.xselection.property = request->property; - reply.xselection.display = request->display; - reply.xselection.requestor = request->requestor; - reply.xselection.selection = request->selection; - reply.xselection.target = request->target; - reply.xselection.time = request->time; - - XSendEvent(win->src.display, request->requestor, False, 0, &reply); -} - -char* RGFW_strtok(char* str, const char* delimStr) { - static char* static_str = NULL; - - if (str != NULL) - static_str = str; - - if (static_str == NULL) { - return NULL; - } - - while (*static_str != '\0') { - RGFW_bool delim = 0; - for (const char* d = delimStr; *d != '\0'; d++) { - if (*static_str == *d) { - delim = 1; - break; - } - } - if (!delim) - break; - static_str++; - } - - if (*static_str == '\0') - return NULL; - - char* token_start = static_str; - while (*static_str != '\0') { - int delim = 0; - for (const char* d = delimStr; *d != '\0'; d++) { - if (*static_str == *d) { - delim = 1; - break; - } - } - - if (delim) { - *static_str = '\0'; - static_str++; - break; - } - static_str++; - } - - return token_start; -} - -RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { - RGFW_event* ev = RGFW_window_checkEventCore(win); - if (ev) { - if (ev == (RGFW_event*)-1) return NULL; - return ev; - } - - #if defined(__linux__) && !defined(RGFW_NO_LINUX) - if (RGFW_linux_updateGamepad(win)) return &win->event; - #endif - RGFW_GOTO_WAYLAND(0); -#ifdef RGFW_X11 - RGFW_LOAD_ATOM(XdndTypeList); - RGFW_LOAD_ATOM(XdndSelection); - RGFW_LOAD_ATOM(XdndEnter); - RGFW_LOAD_ATOM(XdndPosition); - RGFW_LOAD_ATOM(XdndStatus); - RGFW_LOAD_ATOM(XdndLeave); - RGFW_LOAD_ATOM(XdndDrop); - RGFW_LOAD_ATOM(XdndFinished); - RGFW_LOAD_ATOM(XdndActionCopy); - - XPending(win->src.display); - - XEvent E; /*!< raw X11 event */ - - /* if there is no unread qued events, get a new one */ - if ((QLength(win->src.display) || XEventsQueued(win->src.display, QueuedAlready) + XEventsQueued(win->src.display, QueuedAfterReading)) - && win->event.type != RGFW_quit - ) - XNextEvent(win->src.display, &E); - else { - return NULL; - } - - win->event.type = 0; - - /* xdnd data */ - static Window source = 0; - static long version = 0; - static i32 format = 0; - - XEvent reply = { ClientMessage }; - - switch (E.type) { - case KeyPress: - case KeyRelease: { - win->event.repeat = RGFW_FALSE; - /* check if it's a real key release */ - if (E.type == KeyRelease && XEventsQueued(win->src.display, QueuedAfterReading)) { /* get next event if there is one */ - XEvent NE; - XPeekEvent(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 */ - win->event.repeat = RGFW_TRUE; - } - - /* set event key data */ - win->event.key = RGFW_apiKeyToRGFW(E.xkey.keycode); - KeySym sym = (KeySym)XkbKeycodeToKeysym(win->src.display, E.xkey.keycode, 0, E.xkey.state & ShiftMask ? 1 : 0); - - if ((E.xkey.state & LockMask) && sym >= XK_a && sym <= XK_z) - sym = (E.xkey.state & ShiftMask) ? sym + 32 : sym - 32; - if ((u8)sym != (u32)sym) - sym = 0; - - win->event.keyChar = (u8)sym; - - RGFW_keyboard[win->event.key].prev = RGFW_isPressed(win, win->event.key); - - /* get keystate data */ - win->event.type = (E.type == KeyPress) ? RGFW_keyPressed : RGFW_keyReleased; - - XKeyboardState keystate; - XGetKeyboardControl(win->src.display, &keystate); - - RGFW_keyboard[win->event.key].current = (E.type == KeyPress); - - XkbStateRec state; - XkbGetState(win->src.display, XkbUseCoreKbd, &state); - RGFW_updateKeyMods(win, (state.locked_mods & LockMask), (state.locked_mods & Mod2Mask), (state.locked_mods & Mod3Mask)); - - RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, (E.type == KeyPress)); - break; - } - case ButtonPress: - case ButtonRelease: - if (E.xbutton.button > RGFW_mouseFinal) { /* skip this event */ - XFlush(win->src.display); - return RGFW_window_checkEvent(win); - } - - win->event.type = RGFW_mouseButtonPressed + (E.type == ButtonRelease); // the events match - win->event.button = E.xbutton.button - 1; - switch(win->event.button) { - case RGFW_mouseScrollUp: - win->event.scroll = 1; - break; - case RGFW_mouseScrollDown: - win->event.scroll = -1; - break; - default: break; - } - - 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.key); - - RGFW_mouseButtons[win->event.button].current = (E.type == ButtonPress); - RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, (E.type == ButtonPress)); - break; - - case MotionNotify: - win->event.point.x = E.xmotion.x; - win->event.point.y = E.xmotion.y; - - win->event.vector.x = win->event.point.x - win->_lastMousePoint.x; - win->event.vector.y = win->event.point.y - win->_lastMousePoint.y; - win->_lastMousePoint = win->event.point; - - win->event.type = RGFW_mousePosChanged; - RGFW_mousePosCallback(win, win->event.point, win->event.vector); - break; - - case GenericEvent: { - /* MotionNotify is used for mouse events if the mouse isn't held */ - if (!(win->_flags & RGFW_HOLD_MOUSE)) { - XFreeEventData(win->src.display, &E.xcookie); - break; - } - - XGetEventData(win->src.display, &E.xcookie); - if (E.xcookie.evtype == XI_RawMotion) { - XIRawEvent *raw = (XIRawEvent *)E.xcookie.data; - if (raw->valuators.mask_len == 0) { - XFreeEventData(win->src.display, &E.xcookie); - break; - } - - double deltaX = 0.0f; - double deltaY = 0.0f; - - /* check if relative motion data exists where we think it does */ - if (XIMaskIsSet(raw->valuators.mask, 0) != 0) - deltaX += raw->raw_values[0]; - if (XIMaskIsSet(raw->valuators.mask, 1) != 0) - deltaY += raw->raw_values[1]; - - win->event.vector = RGFW_POINT((i32)deltaX, (i32)deltaY); - win->event.point.x = win->_lastMousePoint.x + win->event.vector.x; - win->event.point.y = win->_lastMousePoint.y + win->event.vector.y; - win->_lastMousePoint = win->event.point; - - 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, win->event.vector); - } - - XFreeEventData(win->src.display, &E.xcookie); - break; - } - - case Expose: - win->event.type = RGFW_windowRefresh; - RGFW_windowRefreshCallback(win); - break; - case MapNotify: case UnmapNotify: RGFW_window_checkMode(win); break; - case ClientMessage: { - /* if the client closed the window */ - if (E.xclient.data.l[0] == (long)wm_delete_window) { - win->event.type = RGFW_quit; - RGFW_windowQuitCallback(win); - break; - } - - for (size_t i = 0; i < win->event.droppedFilesCount; i++) { - win->event.droppedFiles[i][0] = '\0'; - } - win->event.droppedFilesCount = 0; - - if ((win->_flags & RGFW_windowAllowDND) == 0) - break; - - reply.xclient.window = source; - reply.xclient.format = 32; - reply.xclient.data.l[0] = (long)win->src.window; - reply.xclient.data.l[1] = 0; - reply.xclient.data.l[2] = None; - - if (E.xclient.message_type == XdndEnter) { - if (version > 5) - break; - - unsigned long count; - Atom* formats; - Atom real_formats[6]; - Bool list = E.xclient.data.l[1] & 1; - - source = E.xclient.data.l[0]; - version = E.xclient.data.l[1] >> 24; - format = None; - if (list) { - Atom actualType; - i32 actualFormat; - unsigned long bytesAfter; - - XGetWindowProperty( - win->src.display, source, XdndTypeList, - 0, LONG_MAX, False, 4, - &actualType, &actualFormat, &count, &bytesAfter, (u8**)&formats - ); - } else { - count = 0; - - for (size_t i = 2; i < 5; i++) { - Window format = E.xclient.data.l[i]; - if (format != None) { - real_formats[count] = format; - count += 1; - } - } - - formats = real_formats; - } - - for (size_t i = 0; i < count; i++) { - if (formats[i] == XtextUriList || formats[i] == XtextPlain) { - format = (int)formats[i]; - break; - } - } - - if (list) { - XFree(formats); - } - - break; - } - - if (E.xclient.message_type == XdndPosition) { - const i32 xabs = (E.xclient.data.l[2] >> 16) & 0xffff; - const i32 yabs = (E.xclient.data.l[2]) & 0xffff; - Window dummy; - i32 xpos, ypos; - - if (version > 5) - break; - - XTranslateCoordinates( - win->src.display, XDefaultRootWindow(win->src.display), win->src.window, - xabs, yabs, &xpos, &ypos, &dummy - ); - - win->event.point.x = xpos; - win->event.point.y = ypos; - - reply.xclient.window = source; - reply.xclient.message_type = XdndStatus; - - if (format) { - reply.xclient.data.l[1] = 1; - if (version >= 2) - reply.xclient.data.l[4] = (long)XdndActionCopy; - } - - XSendEvent(win->src.display, source, False, NoEventMask, &reply); - XFlush(win->src.display); - break; - } - if (E.xclient.message_type != XdndDrop) - break; - - if (version > 5) - break; - - win->event.type = RGFW_DNDInit; - - if (format) { - Time time = (version >= 1) - ? (Time)E.xclient.data.l[2] - : CurrentTime; - - XConvertSelection( - win->src.display, XdndSelection, (Atom)format, - XdndSelection, win->src.window, time - ); - } else if (version >= 2) { - XEvent reply = { ClientMessage }; - - XSendEvent(win->src.display, source, False, NoEventMask, &reply); - XFlush(win->src.display); - } - - RGFW_dndInitCallback(win, win->event.point); - } break; - case SelectionRequest: - RGFW_XHandleClipboardSelection(win, &E); - XFlush(win->src.display); - return RGFW_window_checkEvent(win); - case SelectionNotify: { - /* this is only for checking for xdnd drops */ - if (E.xselection.property != XdndSelection || !(win->_flags & RGFW_windowAllowDND)) - break; - char* data; - unsigned long result; - - Atom actualType; - i32 actualFormat; - unsigned long bytesAfter; - - XGetWindowProperty(win->src.display, E.xselection.requestor, E.xselection.property, 0, LONG_MAX, False, E.xselection.target, &actualType, &actualFormat, &result, &bytesAfter, (u8**) &data); - - if (result == 0) - break; - - const char* prefix = (const char*)"file://"; - - char* line; - - win->event.droppedFilesCount = 0; - - win->event.type = RGFW_DND; - - while ((line = (char*)RGFW_strtok(data, "\r\n"))) { - char path[RGFW_MAX_PATH]; - - data = NULL; - - if (line[0] == '#') - continue; - - char* l; - for (l = line; 1; l++) { - if ((l - line) > 7) - break; - else if (*l != prefix[(l - line)]) - break; - else if (*l == '\0' && prefix[(l - line)] == '\0') { - line += 7; - while (*line != '/') - line++; - break; - } else if (*l == '\0') - break; - } - - win->event.droppedFilesCount++; - - size_t index = 0; - while (*line) { - if (line[0] == '%' && line[1] && line[2]) { - const char digits[3] = { line[1], line[2], '\0' }; - path[index] = (char) RGFW_STRTOL(digits, NULL, 16); - line += 2; - } else - path[index] = *line; - - index++; - line++; - } - path[index] = '\0'; - RGFW_MEMCPY(win->event.droppedFiles[win->event.droppedFilesCount - 1], path, index + 1); - } - - if (data) - XFree(data); - - if (version >= 2) { - XEvent reply = { ClientMessage }; - reply.xclient.format = 32; - reply.xclient.message_type = XdndFinished; - reply.xclient.data.l[1] = result; - reply.xclient.data.l[2] = XdndActionCopy; - - XSendEvent(win->src.display, source, False, NoEventMask, &reply); - XFlush(win->src.display); - } - - RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount); - break; - } - case FocusIn: - if ((win->_flags & RGFW_windowFullscreen)) - XMapRaised(win->src.display, win->src.window); - - win->_flags |= RGFW_windowFocus; - win->event.type = RGFW_focusIn; - RGFW_focusCallback(win, 1); - break; - case FocusOut: - if ((win->_flags & RGFW_windowFullscreen)) - RGFW_window_minimize(win); - - win->_flags &= ~RGFW_windowFocus; - win->event.type = RGFW_focusOut; - RGFW_focusCallback(win, 0); - break; - case PropertyNotify: RGFW_window_checkMode(win); break; - case EnterNotify: { - win->event.type = RGFW_mouseEnter; - win->event.point.x = E.xcrossing.x; - win->event.point.y = E.xcrossing.y; - RGFW_mouseNotifyCallBack(win, win->event.point, 1); - break; - } - - case LeaveNotify: { - win->event.type = RGFW_mouseLeave; - RGFW_mouseNotifyCallBack(win, win->event.point, 0); - break; - } - - case ConfigureNotify: { - /* detect resize */ - RGFW_window_checkMode(win); - if (E.xconfigure.width != win->r.w || E.xconfigure.height != win->r.h) { - win->event.type = RGFW_windowResized; - win->r = RGFW_RECT(win->r.x, win->r.y, E.xconfigure.width, E.xconfigure.height); - RGFW_windowResizeCallback(win, win->r); - break; - } - - /* detect move */ - if (E.xconfigure.x != win->r.x || E.xconfigure.y != win->r.y) { - win->event.type = RGFW_windowMoved; - win->r = RGFW_RECT(E.xconfigure.x, E.xconfigure.y, win->r.w, win->r.h); - RGFW_windowMoveCallback(win, win->r); - break; - } - - break; - } - default: - XFlush(win->src.display); - return RGFW_window_checkEvent(win); - } - - XFlush(win->src.display); - if (win->event.type) return &win->event; - else return NULL; -#endif -#ifdef RGFW_WAYLAND - wayland: - if (win->_flags & RGFW_windowHide) - return NULL; - - if (wl_display_roundtrip(win->src.wl_display) == -1) - return NULL; - return NULL; -#endif -} - -void RGFW_window_move(RGFW_window* win, RGFW_point v) { - RGFW_ASSERT(win != NULL); - win->r.x = v.x; - win->r.y = v.y; - RGFW_GOTO_WAYLAND(0); -#ifdef RGFW_X11 - XMoveWindow(win->src.display, win->src.window, v.x, v.y); -#endif -#ifdef RGFW_WAYLAND - wayland: - RGFW_ASSERT(win != NULL); - - if (win->src.compositor) { - struct wl_pointer *pointer = wl_seat_get_pointer(win->src.seat); - if (!pointer) { - return; - } - - wl_display_flush(win->src.wl_display); - } -#endif -} - - -void RGFW_window_resize(RGFW_window* win, RGFW_area a) { - RGFW_ASSERT(win != NULL); - win->r.w = a.w; - win->r.h = a.h; - RGFW_GOTO_WAYLAND(0); -#ifdef RGFW_X11 - XResizeWindow(win->src.display, win->src.window, a.w, a.h); - - if (!(win->_flags & RGFW_windowNoResize)) - return; - - XSizeHints sh; - sh.flags = (1L << 4) | (1L << 5); - sh.min_width = sh.max_width = a.w; - sh.min_height = sh.max_height = a.h; - - XSetWMSizeHints(win->src.display, (Drawable) win->src.window, &sh, XA_WM_NORMAL_HINTS); -#endif -#ifdef RGFW_WAYLAND - wayland: - if (win->src.compositor) { - xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->r.w, win->r.h); - #ifdef RGFW_OPENGL - wl_egl_window_resize(win->src.eglWindow, a.w, a.h, 0, 0); - #endif - } -#endif -} - -void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a) { - RGFW_ASSERT(win != NULL); - - if (a.w == 0 && a.h == 0) - return; - - XSizeHints hints; - long flags; - - XGetWMNormalHints(win->src.display, win->src.window, &hints, &flags); - - hints.flags |= PAspect; - - hints.min_aspect.x = hints.max_aspect.x = a.w; - hints.min_aspect.y = hints.max_aspect.y = a.h; - - XSetWMNormalHints(win->src.display, win->src.window, &hints); -} - -void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { - RGFW_ASSERT(win != NULL); - - if (a.w == 0 && a.h == 0) - return; - - XSizeHints hints; - long flags; - - XGetWMNormalHints(win->src.display, win->src.window, &hints, &flags); - - hints.flags |= PMinSize; - - hints.min_width = a.w; - hints.min_height = a.h; - - XSetWMNormalHints(win->src.display, win->src.window, &hints); -} - -void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { - RGFW_ASSERT(win != NULL); - - if (a.w == 0 && a.h == 0) - return; - - XSizeHints hints; - long flags; - - XGetWMNormalHints(win->src.display, win->src.window, &hints, &flags); - - hints.flags |= PMaxSize; - - hints.max_width = a.w; - hints.max_height = a.h; - - XSetWMNormalHints(win->src.display, win->src.window, &hints); -} - -void RGFW_toggleXMaximized(RGFW_window* win, RGFW_bool maximized) { - RGFW_ASSERT(win != NULL); - RGFW_LOAD_ATOM(_NET_WM_STATE); - RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_VERT); - RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ); - - XEvent xev = {0}; - xev.type = ClientMessage; - xev.xclient.window = win->src.window; - xev.xclient.message_type = _NET_WM_STATE; - xev.xclient.format = 32; - xev.xclient.data.l[0] = maximized; - xev.xclient.data.l[1] = _NET_WM_STATE_MAXIMIZED_HORZ; - xev.xclient.data.l[2] = _NET_WM_STATE_MAXIMIZED_VERT; - xev.xclient.data.l[3] = 0; - xev.xclient.data.l[4] = 0; - - XSendEvent(win->src.display, DefaultRootWindow(win->src.display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); -} - -void RGFW_window_maximize(RGFW_window* win) { - win->_oldRect = win->r; - RGFW_toggleXMaximized(win, 1); -} - -void RGFW_window_focus(RGFW_window* win) { - RGFW_ASSERT(win); - - XWindowAttributes attr; - XGetWindowAttributes(win->src.display, win->src.window, &attr); - if (attr.map_state != IsViewable) return; - - XSetInputFocus(win->src.display, win->src.window, RevertToPointerRoot, CurrentTime); - XFlush(win->src.display); -} - -void RGFW_window_raise(RGFW_window* win) { - RGFW_ASSERT(win); - XRaiseWindow(win->src.display, win->src.window); - XMapRaised(win->src.display, win->src.window); -} - -void RGFW_window_setXAtom(RGFW_window* win, Atom netAtom, RGFW_bool fullscreen) { - RGFW_ASSERT(win != NULL); - RGFW_LOAD_ATOM(_NET_WM_STATE); - - XEvent xev = {0}; - xev.xclient.type = ClientMessage; - xev.xclient.serial = 0; - xev.xclient.send_event = True; - xev.xclient.message_type = _NET_WM_STATE; - xev.xclient.window = win->src.window; - xev.xclient.format = 32; - xev.xclient.data.l[0] = fullscreen; - xev.xclient.data.l[1] = netAtom; - xev.xclient.data.l[2] = 0; - - XSendEvent(win->src.display, DefaultRootWindow(win->src.display), False, SubstructureNotifyMask | SubstructureRedirectMask, &xev); -} - -void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) { - RGFW_ASSERT(win != NULL); - if (fullscreen) { - win->_flags |= RGFW_windowFullscreen; - win->_oldRect = win->r; - } - else win->_flags &= ~RGFW_windowFullscreen; - - RGFW_LOAD_ATOM(_NET_WM_STATE_FULLSCREEN); - - RGFW_window_setXAtom(win, _NET_WM_STATE_FULLSCREEN, fullscreen); - - XRaiseWindow(win->src.display, win->src.window); - XMapRaised(win->src.display, win->src.window); -} - -void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) { - RGFW_ASSERT(win != NULL); - - RGFW_LOAD_ATOM(_NET_WM_STATE_ABOVE); - RGFW_window_setXAtom(win, _NET_WM_STATE_ABOVE, floating); -} - -void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) { - RGFW_ASSERT(win != NULL); - const u32 value = (u32) (0xffffffffu * (double) opacity); - RGFW_LOAD_ATOM(NET_WM_WINDOW_OPACITY); - XChangeProperty(win->src.display, win->src.window, - NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &value, 1); -} - -void RGFW_window_minimize(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - if (RGFW_window_isMaximized(win)) return; - - win->_oldRect = win->r; - XIconifyWindow(win->src.display, win->src.window, DefaultScreen(win->src.display)); - XFlush(win->src.display); -} - -void RGFW_window_restore(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - RGFW_toggleXMaximized(win, 0); - - win->r = win->_oldRect; - RGFW_window_move(win, RGFW_POINT(win->r.x, win->r.y)); - RGFW_window_resize(win, RGFW_AREA(win->r.w, win->r.h)); - - RGFW_window_show(win); - XFlush(win->src.display); -} - -RGFW_bool RGFW_window_isFloating(RGFW_window* win) { - RGFW_LOAD_ATOM(_NET_WM_STATE); - RGFW_LOAD_ATOM(_NET_WM_STATE_ABOVE); - - Atom actual_type; - int actual_format; - unsigned long nitems, bytes_after; - Atom* prop_return = NULL; - - int status = XGetWindowProperty(win->src.display, win->src.window, _NET_WM_STATE, 0, (~0L), False, XA_ATOM, - &actual_type, &actual_format, &nitems, &bytes_after, - (unsigned char **)&prop_return); - - if (status != Success || actual_type != XA_ATOM) - return RGFW_FALSE; - - for (unsigned long i = 0; i < nitems; i++) - if (prop_return[i] == _NET_WM_STATE_ABOVE) return RGFW_TRUE; - - if (prop_return) - XFree(prop_return); - - return RGFW_FALSE; -} - -void RGFW_window_setName(RGFW_window* win, const char* name) { - RGFW_ASSERT(win != NULL); - RGFW_GOTO_WAYLAND(0); - #ifdef RGFW_X11 - XStoreName(win->src.display, win->src.window, name); - - RGFW_LOAD_ATOM(_NET_WM_NAME); - XChangeProperty( - win->src.display, win->src.window, _NET_WM_NAME, RGFW_XUTF8_STRING, - 8, PropModeReplace, (u8*)name, 256 - ); - #endif - #ifdef RGFW_WAYLAND - wayland: - if (win->src.compositor) - xdg_toplevel_set_title(win->src.xdg_toplevel, name); - #endif -} - -void* RGFW_libxshape = NULL; - -#ifndef RGFW_NO_PASSTHROUGH - -void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) { - RGFW_ASSERT(win != NULL); - - #if defined(__CYGWIN__) - RGFW_LOAD_LIBRARY(RGFW_libxshape, "libXext-6.so"); - #elif defined(__OpenBSD__) || defined(__NetBSD__) - RGFW_LOAD_LIBRARY(RGFW_libxshape, "libXext.so"); - #else - RGFW_LOAD_LIBRARY(RGFW_libxshape, "libXext.so.6"); - #endif - - typedef void (* PFN_XShapeCombineMask)(Display*,Window,int,int,int,Pixmap,int); - static PFN_XShapeCombineMask XShapeCombineMaskSRC; - - typedef void (* PFN_XShapeCombineRegion)(Display*,Window,int,int,int,Region,int); - static PFN_XShapeCombineRegion XShapeCombineRegionSRC; - - RGFW_PROC_DEF(RGFW_libxshape, XShapeCombineRegion); - RGFW_PROC_DEF(RGFW_libxshape, XShapeCombineMask); - - if (passthrough) { - Region region = XCreateRegion(); - XShapeCombineRegionSRC(win->src.display, win->src.window, ShapeInput, 0, 0, region, ShapeSet); - XDestroyRegion(region); - - return; - } - - XShapeCombineMaskSRC(win->src.display, win->src.window, ShapeInput, 0, 0, None, ShapeSet); -} - -#endif /* RGFW_NO_PASSTHROUGH */ - -RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* icon, RGFW_area a, i32 channels, u8 type) { - RGFW_ASSERT(win != NULL); - RGFW_GOTO_WAYLAND(0); -#ifdef RGFW_X11 - RGFW_LOAD_ATOM(_NET_WM_ICON); - if (icon == NULL || (channels != 3 && channels != 4)) { - RGFW_bool res = (RGFW_bool)XChangeProperty( - win->src.display, win->src.window, _NET_WM_ICON, XA_CARDINAL, 32, - PropModeReplace, (u8*)NULL, 0 - ); - return res; - } - - i32 count = 2 + (a.w * a.h); - - unsigned long* data = (unsigned long*) RGFW_ALLOC(count * sizeof(unsigned long)); - data[0] = (unsigned long)a.w; - data[1] = (unsigned long)a.h; - - unsigned long* target = &data[2]; - u32 x, y; - - for (x = 0; x < a.w; x++) { - for (y = 0; y < a.h; y++) { - size_t i = y * a.w + x; - u32 alpha = (channels == 4) ? icon[i * 4 + 3] : 0xFF; - - target[i] = (unsigned long)((icon[i * 4 + 0]) << 16) | - (unsigned long)((icon[i * 4 + 1]) << 8) | - (unsigned long)((icon[i * 4 + 2]) << 0) | - (unsigned long)(alpha << 24); - } - } - - RGFW_bool res = RGFW_TRUE; - if (type & RGFW_iconTaskbar) { - res = (RGFW_bool)XChangeProperty( - win->src.display, win->src.window, _NET_WM_ICON, XA_CARDINAL, 32, - PropModeReplace, (u8*)data, count - ); - } - - if (type & RGFW_iconWindow) { - XWMHints wm_hints; - wm_hints.flags = IconPixmapHint; - - int depth = DefaultDepth(win->src.display, DefaultScreen(win->src.display)); - XImage *image = XCreateImage(win->src.display, DefaultVisual(win->src.display, DefaultScreen(win->src.display)), - depth, ZPixmap, 0, (char *)target, a.w, a.h, 32, 0); - - wm_hints.icon_pixmap = XCreatePixmap(win->src.display, win->src.window, a.w, a.h, depth); - XPutImage(win->src.display, wm_hints.icon_pixmap, win->src.gc, image, 0, 0, 0, 0, a.w, a.h); - image->data = NULL; - XDestroyImage(image); - - XSetWMHints(win->src.display, win->src.window, &wm_hints); - } - - RGFW_FREE(data); - XFlush(win->src.display); - return RGFW_BOOL(res); -#endif -#ifdef RGFW_WAYLAND - wayland: - return RGFW_FALSE; -#endif -} - -RGFW_mouse* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels) { - RGFW_ASSERT(icon); - RGFW_ASSERT(channels == 3 || channels == 4); - RGFW_GOTO_WAYLAND(0); - -#ifdef RGFW_X11 -#ifndef RGFW_NO_X11_CURSOR - XcursorImage* native = XcursorImageCreate(a.w, a.h); - native->xhot = 0; - native->yhot = 0; - - XcursorPixel* target = native->pixels; - for (size_t x = 0; x < a.w; x++) { - for (size_t y = 0; y < a.h; y++) { - size_t i = y * a.w + x; - u32 alpha = (channels == 4) ? icon[i * 4 + 3] : 0xFF; - - target[i] = (u32)((icon[i * 4 + 0]) << 16) - | (u32)((icon[i * 4 + 1]) << 8) - | (u32)((icon[i * 4 + 2]) << 0) - | (u32)(alpha << 24); - } - } - - Cursor cursor = XcursorImageLoadCursor(RGFW_root->src.display, native); - XcursorImageDestroy(native); - - return (void*)cursor; -#else - RGFW_UNUSED(image); RGFW_UNUSED(a.w); RGFW_UNUSED(channels); - return NULL; -#endif -#endif -#ifdef RGFW_WAYLAND - wayland: - RGFW_UNUSED(icon); RGFW_UNUSED(a); RGFW_UNUSED(channels); - return NULL; // TODO -#endif -} - -void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) { -RGFW_GOTO_WAYLAND(0); -#ifdef RGFW_X11 - RGFW_ASSERT(win && mouse); - XDefineCursor(win->src.display, win->src.window, (Cursor)mouse); -#endif -#ifdef RGFW_WAYLAND - wayland: -#endif -} - -void RGFW_freeMouse(RGFW_mouse* mouse) { -RGFW_GOTO_WAYLAND(0); -#ifdef RGFW_X11 - RGFW_ASSERT(mouse); - XFreeCursor(RGFW_root->src.display, (Cursor)mouse); -#endif -#ifdef RGFW_WAYLAND - wayland: -#endif -} - -void RGFW_window_moveMouse(RGFW_window* win, RGFW_point p) { -RGFW_GOTO_WAYLAND(1); -#ifdef RGFW_X11 - RGFW_ASSERT(win != NULL); - - XEvent event; - XQueryPointer(win->src.display, DefaultRootWindow(win->src.display), - &event.xbutton.root, &event.xbutton.window, - &event.xbutton.x_root, &event.xbutton.y_root, - &event.xbutton.x, &event.xbutton.y, - &event.xbutton.state); - - win->_lastMousePoint = RGFW_POINT(p.x - win->r.x, p.y - win->r.y); - if (event.xbutton.x == p.x && event.xbutton.y == p.y) - return; - - XWarpPointer(win->src.display, None, win->src.window, 0, 0, 0, 0, (int) p.x - win->r.x, (int) p.y - win->r.y); -#endif -#ifdef RGFW_WAYLAND - wayland: -#endif -} - -RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) { - return RGFW_window_setMouseStandard(win, RGFW_mouseArrow); -} - -RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { - RGFW_ASSERT(win != NULL); - RGFW_GOTO_WAYLAND(0); -#ifdef RGFW_X11 - static const u8 mouseIconSrc[] = { XC_arrow, XC_left_ptr, XC_xterm, XC_crosshair, XC_hand2, XC_sb_h_double_arrow, XC_sb_v_double_arrow, XC_bottom_left_corner, XC_bottom_right_corner, XC_fleur, XC_X_cursor}; - - if (mouse > (sizeof(mouseIconSrc) / sizeof(u8))) - return RGFW_FALSE; - - mouse = mouseIconSrc[mouse]; - - Cursor cursor = XCreateFontCursor(win->src.display, mouse); - XDefineCursor(win->src.display, win->src.window, (Cursor) cursor); - - XFreeCursor(win->src.display, (Cursor) cursor); - return RGFW_TRUE; -#endif -#ifdef RGFW_WAYLAND - wayland: - 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* wlcursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, iconStrings[mouse]); - RGFW_cursor_image = wlcursor->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); - return RGFW_TRUE; -#endif -} - -void RGFW_window_hide(RGFW_window* win) { - RGFW_GOTO_WAYLAND(0); -#ifdef RGFW_X11 - XUnmapWindow(win->src.display, win->src.window); -#endif -#ifdef RGFW_WAYLAND - wayland: - wl_surface_attach(win->src.surface, NULL, 0, 0); - wl_surface_commit(win->src.surface); - win->_flags |= RGFW_windowHide; -#endif -} - -void RGFW_window_show(RGFW_window* win) { - win->_flags &= ~RGFW_windowHide; - if (win->_flags & RGFW_windowFocusOnShow) RGFW_window_focus(win); - RGFW_GOTO_WAYLAND(0); -#ifdef RGFW_X11 - XMapWindow(win->src.display, win->src.window); -#endif -#ifdef RGFW_WAYLAND - wayland: - //wl_surface_attach(win->src.surface, win->rc., 0, 0); - wl_surface_commit(win->src.surface); -#endif -} - -RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { - RGFW_GOTO_WAYLAND(1); - #ifdef RGFW_X11 - - if (XGetSelectionOwner(RGFW_root->src.display, RGFW_XCLIPBOARD) == RGFW_root->src.window) { - if (str != NULL) - RGFW_STRNCPY(str, RGFW_root->src.clipboard, RGFW_root->src.clipboard_len); - return (RGFW_ssize_t)RGFW_root->src.clipboard_len; - } - - XEvent event; - int format; - unsigned long N, sizeN; - char* data; - Atom target; - - RGFW_LOAD_ATOM(XSEL_DATA); - - XConvertSelection(RGFW_root->src.display, RGFW_XCLIPBOARD, RGFW_XUTF8_STRING, XSEL_DATA, RGFW_root->src.window, CurrentTime); - XSync(RGFW_root->src.display, 0); - XNextEvent(RGFW_root->src.display, &event); - - if (event.type != SelectionNotify || event.xselection.selection != RGFW_XCLIPBOARD || event.xselection.property == 0) - return -1; - - XGetWindowProperty(event.xselection.display, event.xselection.requestor, - event.xselection.property, 0L, (~0L), 0, AnyPropertyType, &target, - &format, &sizeN, &N, (u8**) &data); - - RGFW_ssize_t size; - if (sizeN > strCapacity && str != NULL) - size = -1; - - if ((target == RGFW_XUTF8_STRING || target == XA_STRING) && str != NULL) { - RGFW_MEMCPY(str, data, sizeN); - str[sizeN] = '\0'; - XFree(data); - } else if (str != NULL) size = -1; - - XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property); - size = sizeN; - return size; - #endif - #if defined(RGFW_WAYLAND) - wayland: return 0; - #endif -} - -void RGFW_XHandleClipboardSelectionLoop(RGFW_window* win) { - RGFW_LOAD_ATOM(SAVE_TARGETS); - - for (;;) { - XEvent event; - XNextEvent(win->src.display, &event); - switch (event.type) { - case SelectionRequest: - return RGFW_XHandleClipboardSelection(win, &event); - case SelectionNotify: - if (event.xselection.target == SAVE_TARGETS) - return; - break; - default: break; - } - } -} - -void RGFW_writeClipboard(const char* text, u32 textLen) { - RGFW_GOTO_WAYLAND(1); - #ifdef RGFW_X11 - RGFW_LOAD_ATOM(SAVE_TARGETS); - - /* request ownership of the clipboard section and request to convert it, this means its our job to convert it */ - XSetSelectionOwner(RGFW_root->src.display, RGFW_XCLIPBOARD, RGFW_root->src.window, CurrentTime); - if (XGetSelectionOwner(RGFW_root->src.display, RGFW_XCLIPBOARD) != RGFW_root->src.window) { - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errClipboard, RGFW_DEBUG_CTX(RGFW_root, 0), "X11 failed to become owner of clipboard selection"); - return; - } - - if (RGFW_root->src.clipboard) - RGFW_FREE(RGFW_root->src.clipboard); - - RGFW_root->src.clipboard = (char*)RGFW_ALLOC(textLen); - RGFW_STRNCPY(RGFW_root->src.clipboard, text, textLen); - RGFW_root->src.clipboard_len = textLen; -#ifdef RGFW_WAYLAND - if (RGFW_useWaylandBool) - RGFW_XHandleClipboardSelectionLoop(RGFW_root); -#endif - - #endif - #if defined(RGFW_WAYLAND) - wayland: - #endif -} - -RGFW_bool RGFW_window_isHidden(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - - XWindowAttributes windowAttributes; - XGetWindowAttributes(win->src.display, win->src.window, &windowAttributes); - - return (windowAttributes.map_state == IsUnmapped && !RGFW_window_isMinimized(win)); -} - -RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - RGFW_LOAD_ATOM(WM_STATE); - - Atom actual_type; - i32 actual_format; - unsigned long nitems, bytes_after; - unsigned char* prop_data; - - i16 status = XGetWindowProperty(win->src.display, win->src.window, WM_STATE, 0, 2, False, - AnyPropertyType, &actual_type, &actual_format, - &nitems, &bytes_after, &prop_data); - - if (status == Success && nitems >= 1 && *((int*) prop_data) == IconicState) { - XFree(prop_data); - return RGFW_TRUE; - } - - if (prop_data != NULL) - XFree(prop_data); - - XWindowAttributes windowAttributes; - XGetWindowAttributes(win->src.display, win->src.window, &windowAttributes); - return windowAttributes.map_state != IsViewable; -} - -RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - RGFW_LOAD_ATOM(_NET_WM_STATE); - RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_VERT); - RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ); - - Atom actual_type; - i32 actual_format; - unsigned long nitems, bytes_after; - unsigned char* prop_data; - - i16 status = XGetWindowProperty(win->src.display, win->src.window, _NET_WM_STATE, 0, 1024, False, - XA_ATOM, &actual_type, &actual_format, - &nitems, &bytes_after, &prop_data); - - if (status != Success) { - if (prop_data != NULL) - XFree(prop_data); - - return RGFW_FALSE; - } - - Atom* atoms = (Atom*) prop_data; - u64 i; - for (i = 0; i < nitems; ++i) { - if (atoms[i] == _NET_WM_STATE_MAXIMIZED_VERT || - atoms[i] == _NET_WM_STATE_MAXIMIZED_HORZ) { - XFree(prop_data); - return RGFW_TRUE; - } - } - - return RGFW_FALSE; -} - -#ifndef RGFW_NO_DPI -static u32 RGFW_XCalculateRefreshRate(XRRModeInfo mi) { - if (mi.hTotal == 0 || mi.vTotal == 0) return 0; - - return (u32) RGFW_ROUND((double) mi.dotClock / ((double) mi.hTotal * (double) mi.vTotal)); -} -#endif - - -static float XGetSystemContentDPI(Display* display, i32 screen) { - float dpi = 96.0f; - - #ifndef RGFW_NO_DPI - RGFW_UNUSED(screen); - char* rms = XResourceManagerString(display); - XrmDatabase db = NULL; - if (rms) db = XrmGetStringDatabase(rms); - - if (rms && db) { - XrmValue value; - char* type = NULL; - - if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value) && type && RGFW_STRNCMP(type, "String", 7) == 0) - dpi = (float)atof(value.addr); - XrmDestroyDatabase(db); - } - #else - dpi = RGFW_ROUND(DisplayWidth(display, screen) / (DisplayWidthMM(display, screen) / 25.4)); - #endif - - return dpi; -} - -RGFW_monitor RGFW_XCreateMonitor(i32 screen) { - RGFW_monitor monitor; - - Display* display; - if (RGFW_root == NULL) - display = XOpenDisplay(NULL); - else - display = RGFW_root->src.display; - - if (screen == -1) screen = DefaultScreen(display); - - Screen* scrn = DefaultScreenOfDisplay(display); - RGFW_area size = RGFW_AREA(scrn->width, scrn->height); - - monitor.x = 0; - monitor.y = 0; - monitor.mode.area = RGFW_AREA(size.w, size.h); - monitor.physW = DisplayWidthMM(display, screen) / 25.4; - monitor.physH = DisplayHeightMM(display, screen) / 25.4; - - RGFW_splitBPP(DefaultDepth(display, DefaultScreen(display)), &monitor.mode); - - char* name = XDisplayName((const char*)display); - RGFW_MEMCPY(monitor.name, name, 128); - - float dpi = XGetSystemContentDPI(display, screen); - monitor.pixelRatio = dpi >= 192.0f ? 2 : 1; - monitor.scaleX = (float) (dpi) / 96.0f; - monitor.scaleY = (float) (dpi) / 96.0f; - - #ifndef RGFW_NO_DPI - XRRScreenResources* sr = XRRGetScreenResourcesCurrent(display, RootWindow(display, screen)); - monitor.mode.refreshRate = RGFW_XCalculateRefreshRate(sr->modes[screen]); - - XRRCrtcInfo* ci = NULL; - int crtc = screen; - - if (sr->ncrtc > crtc) { - ci = XRRGetCrtcInfo(display, sr, sr->crtcs[crtc]); - } - #endif - - #ifndef RGFW_NO_DPI - XRROutputInfo* info = XRRGetOutputInfo (display, sr, sr->outputs[screen]); - - if (info == NULL || ci == NULL) { - XRRFreeScreenResources(sr); - XCloseDisplay(display); - RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, RGFW_DEBUG_CTX_MON(monitor), "monitor found"); - return monitor; - } - - - float physW = info->mm_width / 25.4; - float physH = info->mm_height / 25.4; - - RGFW_MEMCPY(monitor.name, info->name, 128); - - if (physW && physH) { - monitor.physW = physW; - monitor.physH = physH; - } - - monitor.x = ci->x; - monitor.y = ci->y; - - float w = ci->width; - float h = ci->height; - - if (w && h) { - monitor.mode.area.w = w; - monitor.mode.area.h = h; - } - #endif - - #ifndef RGFW_NO_DPI - XRRFreeCrtcInfo(ci); - XRRFreeScreenResources(sr); - #endif - - if (RGFW_root == NULL) XCloseDisplay(display); - - RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, RGFW_DEBUG_CTX_MON(monitor), "monitor found"); - return monitor; -} - -RGFW_monitor* RGFW_getMonitors(void) { - static RGFW_monitor monitors[7]; - - RGFW_GOTO_WAYLAND(1); - #ifdef RGFW_X11 - - Display* display; - if (RGFW_root == NULL) display = XOpenDisplay(NULL); - else display = RGFW_root->src.display; - - size_t i; - for (i = 0; i < (size_t)ScreenCount(display) && i < 6; i++) - monitors[i] = RGFW_XCreateMonitor(i); - - if (RGFW_root == NULL) XCloseDisplay(display); - - return monitors; - #endif - #ifdef RGFW_WAYLAND - wayland: return monitors; // TODO WAYLAND - #endif -} - -RGFW_monitor RGFW_getPrimaryMonitor(void) { - RGFW_GOTO_WAYLAND(1); - #ifdef RGFW_X11 - return RGFW_XCreateMonitor(-1); - #endif - #ifdef RGFW_WAYLAND - wayland: return (RGFW_monitor){ }; // TODO WAYLAND - #endif -} - -RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) { - RGFW_GOTO_WAYLAND(1); -#ifdef RGFW_X11 - #ifndef RGFW_NO_DPI - XRRScreenResources* screenRes = XRRGetScreenResources(RGFW_root->src.display, DefaultRootWindow(RGFW_root->src.display)); - if (screenRes == NULL) return RGFW_FALSE; - for (int i = 0; i < screenRes->ncrtc; i++) { - XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(RGFW_root->src.display, screenRes, screenRes->crtcs[i]); - if (!crtcInfo) continue; - - if (mon.x == crtcInfo->x && mon.y == crtcInfo->y && (u32)mon.mode.area.w == crtcInfo->width && (u32)mon.mode.area.h == crtcInfo->height) { - RRMode rmode = None; - for (int index = 0; index < screenRes->nmode; index++) { - RGFW_monitorMode foundMode; - foundMode.area = RGFW_AREA(screenRes->modes[index].width, screenRes->modes[index].height); - foundMode.refreshRate = RGFW_XCalculateRefreshRate(screenRes->modes[index]); - RGFW_splitBPP(DefaultDepth(RGFW_root->src.display, DefaultScreen(RGFW_root->src.display)), &foundMode); - - if (RGFW_monitorModeCompare(mode, foundMode, request)) { - rmode = screenRes->modes[index].id; - - RROutput output = screenRes->outputs[i]; - XRROutputInfo* info = XRRGetOutputInfo(RGFW_root->src.display, screenRes, output); - if (info) { - XRRSetCrtcConfig(RGFW_root->src.display, screenRes, screenRes->crtcs[i], - CurrentTime, 0, 0, rmode, RR_Rotate_0, &output, 1); - XRRFreeOutputInfo(info); - XRRFreeCrtcInfo(crtcInfo); - XRRFreeScreenResources(screenRes); - return RGFW_TRUE; - } - } - } - - XRRFreeCrtcInfo(crtcInfo); - XRRFreeScreenResources(screenRes); - return RGFW_FALSE; - } - - XRRFreeCrtcInfo(crtcInfo); - } - - XRRFreeScreenResources(screenRes); - return RGFW_FALSE; - #endif -#endif -#ifdef RGFW_WAYLAND -wayland: -#endif - return RGFW_FALSE; -} - -RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - RGFW_GOTO_WAYLAND(1); -#ifdef RGFW_X11 - XWindowAttributes attrs; - if (!XGetWindowAttributes(win->src.display, win->src.window, &attrs)) { - return (RGFW_monitor){}; - } - - size_t i; - for (i = 0; i < (size_t)ScreenCount(win->src.display) && i < 6; i++) { - Screen* screen = ScreenOfDisplay(win->src.display, i); - if (attrs.x >= 0 && attrs.x < XWidthOfScreen(screen) && - attrs.y >= 0 && attrs.y < XHeightOfScreen(screen)) - return RGFW_XCreateMonitor(i); - } -#endif -#ifdef RGFW_WAYLAND -wayland: -#endif - return (RGFW_monitor){}; - -} - -#if defined(RGFW_OPENGL) && !defined(RGFW_EGL) -void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { - if (win == NULL) - glXMakeCurrent(NULL, (Drawable)NULL, (GLXContext) NULL); - else - glXMakeCurrent(win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx); -} -void* RGFW_getCurrent_OpenGL(void) { return glXGetCurrentContext(); } -#endif - -void RGFW_window_swapBuffers(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - RGFW_GOTO_WAYLAND(0); -#ifdef RGFW_X11 - /* clear the window */ - if (!(win->_flags & RGFW_NO_CPU_RENDER)) { - #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - win->src.bitmap->data = (char*) win->buffer; - RGFW_RGB_to_BGR(win, (u8*)win->src.bitmap->data); - XPutImage(win->src.display, win->src.window, win->src.gc, win->src.bitmap, 0, 0, 0, 0, win->bufferSize.w, win->bufferSize.h); - win->src.bitmap->data = NULL; - #endif - } - - if (!(win->_flags & RGFW_NO_GPU_RENDER)) { - #ifdef RGFW_EGL - eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); - #elif defined(RGFW_OPENGL) - glXSwapBuffers(win->src.display, win->src.window); - #endif - } - return; -#endif -#ifdef RGFW_WAYLAND - wayland: - #if defined(RGFW_BUFFER) || defined(RGFW_OSMESA) - #if !defined(RGFW_BUFFER_BGR) && !defined(RGFW_OSMESA) - RGFW_RGB_to_BGR(win, win->src.buffer); - #else - for (size_t y = 0; y < win->r.h; y++) { - u32 index = (y * 4 * win->r.w); - u32 index2 = (y * 4 * win->bufferSize.w); - RGFW_MEMCPY(&win->src.buffer[index], &win->buffer[index2], win->r.w * 4); - } - #endif - - wl_surface_frame_done(win, NULL, 0); - if (!(win->_flags & RGFW_NO_GPU_RENDER)) - #endif - { - #ifdef RGFW_OPENGL - eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); - #endif - } - - wl_surface_commit(win->src.surface); -#endif -} - -#if !defined(RGFW_EGL) - -void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { - RGFW_ASSERT(win != NULL); - - #if defined(RGFW_OPENGL) - ((PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress((GLubyte*) "glXSwapIntervalEXT"))(win->src.display, win->src.window, swapInterval); - #else - RGFW_UNUSED(swapInterval); - #endif -} -#endif - - -void RGFW_window_close(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - #ifdef RGFW_X11 - /* to save the clipboard on the x server after the window is closed */ - RGFW_LOAD_ATOM(CLIPBOARD_MANAGER); - RGFW_LOAD_ATOM(SAVE_TARGETS); - if (XGetSelectionOwner(win->src.display, RGFW_XCLIPBOARD) == win->src.window) { - XConvertSelection(win->src.display, CLIPBOARD_MANAGER, SAVE_TARGETS, None, win->src.window, CurrentTime); - RGFW_XHandleClipboardSelectionLoop(win); - } - if (win->src.clipboard) { - RGFW_FREE(win->src.clipboard); - win->src.clipboard = NULL; - } - - RGFW_GOTO_WAYLAND(0); - - /* ungrab pointer if it was grabbed */ - if (win->_flags & RGFW_HOLD_MOUSE) - XUngrabPointer(win->src.display, CurrentTime); - - #ifdef RGFW_EGL - RGFW_closeEGL(win); - #endif - - if (RGFW_hiddenMouse != NULL && win == RGFW_root) { - RGFW_freeMouse(RGFW_hiddenMouse); - RGFW_hiddenMouse = 0; - } - - #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - if (win->buffer != NULL) { - if ((win->_flags & RGFW_BUFFER_ALLOC)) - RGFW_FREE(win->buffer); - XDestroyImage((XImage*) win->src.bitmap); - } - #endif - - if (win->src.display) { - XFreeGC(win->src.display, win->src.gc); - #if defined(RGFW_OPENGL) && !defined(RGFW_EGL) - glXDestroyContext(win->src.display, win->src.ctx); - #endif - if ((Drawable) win->src.window) - XDestroyWindow(win->src.display, (Drawable) win->src.window); /*!< close the window */ - - if (win == RGFW_root) { - XCloseDisplay(win->src.display); /*!< kill the x server connection */ - RGFW_root = NULL; - } - } - - /* set cleared display / window to NULL for error checking */ - win->src.display = 0; - win->src.window = 0; - - #define RGFW_FREE_LIBRARY(x) if (x != NULL) dlclose(x); x = NULL; - if (win == RGFW_root) { - #if !defined(RGFW_NO_X11_CURSOR_PRELOAD) && !defined(RGFW_NO_X11_CURSOR) - RGFW_FREE_LIBRARY(X11Cursorhandle); - #endif - #if !defined(RGFW_NO_X11_XI_PRELOAD) - RGFW_FREE_LIBRARY(X11Xihandle); - #endif - - #ifdef RGFW_USE_XDL - XDL_close(); - #endif - - #ifndef RGFW_NO_PASSTHROUGH - RGFW_FREE_LIBRARY(RGFW_libxshape); - #endif - - #ifndef RGFW_NO_LINUX - 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_gamepadCount; i++) { - if(RGFW_gamepads[i]) - close(RGFW_gamepads[i]); - } - #endif - } - RGFW_clipboard_switch(NULL); - RGFW_FREE(win->event.droppedFiles); - if ((win->_flags & RGFW_WINDOW_ALLOC)) - RGFW_FREE(win); - return; - #endif - - #ifdef RGFW_WAYLAND - wayland: - - #ifdef RGFW_X11 - XDestroyWindow(win->src.display, (Drawable) win->src.window); - #endif - - #ifdef RGFW_EGL - RGFW_closeEGL(win); - #endif - - if (RGFW_root == win) { - #ifdef RGFW_X11 - XCloseDisplay(win->src.display); /*!< kill connection to the x server */ - #endif - RGFW_root = NULL; - } - - xdg_toplevel_destroy(win->src.xdg_toplevel); - xdg_surface_destroy(win->src.xdg_surface); - wl_surface_destroy(win->src.surface); - - #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - wl_buffer_destroy(win->src.wl_buffer); - if ((win->_flags & RGFW_BUFFER_ALLOC)) - RGFW_FREE(win->buffer); - - munmap(win->src.buffer, win->r.w * win->r.h * 4); - #endif - - wl_display_disconnect(win->src.wl_display); - RGFW_clipboard_switch(NULL); - RGFW_FREE(win->event.droppedFiles); - if ((win->_flags & RGFW_WINDOW_ALLOC)) - RGFW_FREE(win); - #endif -} - - -/* - End of X11 linux / wayland / unix defines -*/ - -#include -#include -#include - -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, u32 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.wl_display), POLLIN, 0 }, - #else - { ConnectionNumber(win->src.display), POLLIN, 0 }, - #endif - { RGFW_eventWait_forceStop[0], POLLIN, 0 }, - #if defined(__linux__) - { -1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0} - #endif - }; - - u8 index = 2; - - #if defined(__linux__) - for (i = 0; i < RGFW_gamepadCount; i++) { - if (RGFW_gamepads[i] == 0) - continue; - - fds[index].fd = RGFW_gamepads[i]; - index++; - } - #endif - - - u64 start = RGFW_getTimeNS(); - - - #ifdef RGFW_WAYLAND - while (wl_display_dispatch(win->src.wl_display) <= 0 && waitMS != RGFW_eventWaitNext) { - #else - while (XPending(win->src.display) == 0 && waitMS != RGFW_eventWaitNext) { - #endif - if (poll(fds, index, (int)waitMS) <= 0) - break; - - if (waitMS != RGFW_eventWaitNext) { - 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; - } -} - -i32 RGFW_getClock(void) { - static i32 clock = -1; - if (clock != -1) return clock; - - #if defined(_POSIX_MONOTONIC_CLOCK) - struct timespec ts; - if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) - clock = CLOCK_MONOTONIC; - #else - clock = CLOCK_REALTIME; - #endif - - return clock; -} - -u64 RGFW_getTimerFreq(void) { return 1000000000LLU; } -u64 RGFW_getTimerValue(void) { - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - return (u64)ts.tv_sec * RGFW_getTimerFreq() + (u64)ts.tv_nsec; -} -#endif /* end of wayland or X11 defines */ - - -/* - - Start of Windows defines - - -*/ - -#ifdef RGFW_WINDOWS -#define WIN32_LEAN_AND_MEAN -#define OEMRESOURCE -#include - -#include -#include -#include -#include -#include -#include -#include - -__declspec(dllimport) int __stdcall WideCharToMultiByte( UINT CodePage, DWORD dwFlags, const WCHAR* lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar); - -#ifndef RGFW_NO_XINPUT - typedef DWORD (WINAPI * PFN_XInputGetState)(DWORD,XINPUT_STATE*); - PFN_XInputGetState XInputGetStateSRC = NULL; - #define XInputGetState XInputGetStateSRC - - typedef DWORD (WINAPI * PFN_XInputGetKeystroke)(DWORD, DWORD, PXINPUT_KEYSTROKE); - PFN_XInputGetKeystroke XInputGetKeystrokeSRC = NULL; - #define XInputGetKeystroke XInputGetKeystrokeSRC - - static HMODULE RGFW_XInput_dll = NULL; -#endif - -char* RGFW_createUTF8FromWideStringWin32(const WCHAR* source); - -#define GL_FRONT 0x0404 -#define GL_BACK 0x0405 -#define GL_LEFT 0x0406 -#define GL_RIGHT 0x0407 - -typedef int (*PFN_wglGetSwapIntervalEXT)(void); -PFN_wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc = NULL; -#define wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc - - -void* RGFWgamepadApi = NULL; - -/* these two wgl functions need to be preloaded */ -typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList); -PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL; - -#ifndef RGFW_EGL - static HMODULE RGFW_wgl_dll = NULL; -#endif - -#ifndef RGFW_NO_LOAD_WGL - typedef HGLRC(WINAPI* PFN_wglCreateContext)(HDC); - typedef BOOL(WINAPI* PFN_wglDeleteContext)(HGLRC); - typedef PROC(WINAPI* PFN_wglGetProcAddress)(LPCSTR); - typedef BOOL(WINAPI* PFN_wglMakeCurrent)(HDC, HGLRC); - typedef HDC(WINAPI* PFN_wglGetCurrentDC)(void); - typedef HGLRC(WINAPI* PFN_wglGetCurrentContext)(void); - typedef BOOL(WINAPI* PFN_wglShareLists)(HGLRC, HGLRC); - - PFN_wglCreateContext wglCreateContextSRC; - PFN_wglDeleteContext wglDeleteContextSRC; - PFN_wglGetProcAddress wglGetProcAddressSRC; - PFN_wglMakeCurrent wglMakeCurrentSRC; - PFN_wglGetCurrentDC wglGetCurrentDCSRC; - PFN_wglGetCurrentContext wglGetCurrentContextSRC; - PFN_wglShareLists wglShareListsSRC; - - #define wglCreateContext wglCreateContextSRC - #define wglDeleteContext wglDeleteContextSRC - #define wglGetProcAddress wglGetProcAddressSRC - #define wglMakeCurrent wglMakeCurrentSRC - #define wglGetCurrentDC wglGetCurrentDCSRC - #define wglGetCurrentContext wglGetCurrentContextSRC - #define wglShareLists wglShareListsSRC -#endif - -#ifdef RGFW_OPENGL - void* RGFW_getProcAddress(const char* procname) { - void* proc = (void*) wglGetProcAddress(procname); - if (proc) - return proc; - - return (void*) GetProcAddress(RGFW_wgl_dll, procname); - } - - typedef HRESULT (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats); - static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL; -#endif - -LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { - RGFW_window* win = (RGFW_window*)GetPropA(hWnd, "RGFW"); - - RECT windowRect; - GetWindowRect(hWnd, &windowRect); - - switch (message) { - case WM_CLOSE: - RGFW_windowQuitCallback(win); - win->event.type = RGFW_quit; - return 0; - case WM_ACTIVATE: { - if (win == NULL) return DefWindowProcW(hWnd, message, wParam, lParam); - - RGFW_bool inFocus = RGFW_BOOL(LOWORD(wParam) != WA_INACTIVE); - if (inFocus) win->_flags |= RGFW_windowFocus; - else win->_flags &= ~RGFW_windowFocus; - RGFW_eventQueuePush((RGFW_event){.type = (RGFW_eventType)((u8)RGFW_focusOut - inFocus), ._win = win}); - - RGFW_focusCallback(win, inFocus); - - if ((win->_flags & RGFW_windowFullscreen) == 0) - return DefWindowProcW(hWnd, message, wParam, lParam); - - win->_flags &= ~RGFW_EVENT_PASSED; - if (inFocus == RGFW_FALSE) RGFW_window_minimize(win); - else RGFW_window_setFullscreen(win, 1); - return DefWindowProcW(hWnd, message, wParam, lParam); - } - case WM_MOVE: - if (win == NULL) return DefWindowProcW(hWnd, message, wParam, lParam); - - win->r.x = windowRect.left; - win->r.y = windowRect.top; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowMoved, ._win = win}); - RGFW_windowMoveCallback(win, win->r); - return DefWindowProcW(hWnd, message, wParam, lParam); - case WM_SIZE: { - if (win == NULL) return DefWindowProcW(hWnd, message, wParam, lParam); - - if (win->src.aspectRatio.w != 0 && win->src.aspectRatio.h != 0) { - double aspectRatio = (double)win->src.aspectRatio.w / win->src.aspectRatio.h; - - int width = windowRect.right - windowRect.left; - int height = windowRect.bottom - windowRect.top; - int newHeight = (int)(width / aspectRatio); - int newWidth = (int)(height * aspectRatio); - - if (win->r.w > windowRect.right - windowRect.left || - win->r.h > (i32)((windowRect.bottom - windowRect.top) - win->src.hOffset)) - { - if (newHeight > height) windowRect.right = windowRect.left + newWidth; - else windowRect.bottom = windowRect.top + newHeight; - } else { - if (newHeight < height) windowRect.right = windowRect.left + newWidth; - else windowRect.bottom = windowRect.top + newHeight; - } - - RGFW_window_resize(win, RGFW_AREA((windowRect.right - windowRect.left), - (windowRect.bottom - windowRect.top) - win->src.hOffset)); - } - - win->r.w = windowRect.right - windowRect.left; - win->r.h = (windowRect.bottom - windowRect.top) - win->src.hOffset; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowResized, ._win = win}); - RGFW_windowResizeCallback(win, win->r); - RGFW_window_checkMode(win); - return DefWindowProcW(hWnd, message, wParam, lParam); - } - case WM_GETMINMAXINFO: { - if (win == NULL) - return DefWindowProcW(hWnd, message, wParam, lParam); - - MINMAXINFO* mmi = (MINMAXINFO*) lParam; - mmi->ptMinTrackSize.x = win->src.minSize.w; - mmi->ptMinTrackSize.y = win->src.minSize.h; - if (win->src.maxSize.w == 0 && win->src.maxSize.h == 0) - return DefWindowProcW(hWnd, message, wParam, lParam); - - mmi->ptMaxTrackSize.x = win->src.maxSize.w; - mmi->ptMaxTrackSize.y = win->src.maxSize.h; - return DefWindowProcW(hWnd, message, wParam, lParam); - } - case WM_PAINT: { - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowRefresh, ._win = win}); - RGFW_windowRefreshCallback(win); - return DefWindowProcW(hWnd, message, wParam, lParam); - } - default: break; - } - return DefWindowProcW(hWnd, message, wParam, lParam); -} - -#ifndef RGFW_NO_DPI - static HMODULE RGFW_Shcore_dll = NULL; - typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*); - PFN_GetDpiForMonitor GetDpiForMonitorSRC = NULL; - #define GetDpiForMonitor GetDpiForMonitorSRC -#endif - -#ifndef RGFW_NO_DWM -static HMODULE RGFW_dwm_dll = NULL; -typedef struct { DWORD dwFlags; int fEnable; HRGN hRgnBlur; int fTransitionOnMaximized;} DWM_BLURBEHIND; -typedef HRESULT (WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND, const DWM_BLURBEHIND*); -PFN_DwmEnableBlurBehindWindow DwmEnableBlurBehindWindowSRC = NULL; -#endif - -#if !defined(RGFW_NO_LOAD_WINMM) && !defined(RGFW_NO_WINMM) - static HMODULE RGFW_winmm_dll = NULL; - typedef u32 (WINAPI * PFN_timeBeginPeriod)(u32); - typedef PFN_timeBeginPeriod PFN_timeEndPeriod; - PFN_timeBeginPeriod timeBeginPeriodSRC, timeEndPeriodSRC; - #define timeBeginPeriod timeBeginPeriodSRC - #define timeEndPeriod timeEndPeriodSRC -#elif !defined(RGFW_NO_WINMM) - __declspec(dllimport) u32 __stdcall timeBeginPeriod(u32 uPeriod); - __declspec(dllimport) u32 __stdcall timeEndPeriod(u32 uPeriod); -#endif - -#define RGFW_PROC_DEF(proc, name) if (name##SRC == NULL && proc != NULL) name##SRC = (PFN_##name)(void*)GetProcAddress(proc, #name) - -#ifndef RGFW_NO_XINPUT -void RGFW_loadXInput(void) { - u32 i; - static const char* names[] = {"xinput1_4.dll", "xinput9_1_0.dll", "xinput1_2.dll", "xinput1_1.dll"}; - - for (i = 0; i < sizeof(names) / sizeof(const char*) && (XInputGetStateSRC == NULL || XInputGetStateSRC != NULL); i++) { - RGFW_XInput_dll = LoadLibraryA(names[i]); - RGFW_PROC_DEF(RGFW_XInput_dll, XInputGetState); - RGFW_PROC_DEF(RGFW_XInput_dll, XInputGetKeystroke); - } - - if (XInputGetStateSRC == NULL) - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errFailedFuncLoad, RGFW_DEBUG_CTX(.win = RGFW_root, .srcError = 0), "Failed to load XInputGetState"); - if (XInputGetKeystrokeSRC == NULL) - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errFailedFuncLoad, RGFW_DEBUG_CTX(.win = RGFW_root, .srcError = 0), "Failed to load XInputGetKeystroke"); -} -#endif - -void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area){ -#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - win->buffer = buffer; - win->bufferSize = area; - - BITMAPV5HEADER bi = { 0 }; - ZeroMemory(&bi, sizeof(bi)); - bi.bV5Size = sizeof(bi); - bi.bV5Width = area.w; - bi.bV5Height = -((LONG) area.h); - bi.bV5Planes = 1; - bi.bV5BitCount = 32; - bi.bV5Compression = BI_RGB; - - win->src.bitmap = CreateDIBSection(win->src.hdc, - (BITMAPINFO*) &bi, DIB_RGB_COLORS, - (void**) &win->src.bitmapBits, - NULL, (DWORD) 0); - - if (win->buffer == NULL) - win->buffer = win->src.bitmapBits; - - win->src.hdcMem = CreateCompatibleDC(win->src.hdc); - SelectObject(win->src.hdcMem, win->src.bitmap); - - #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); RGFW_UNUSED(buffer); RGFW_UNUSED(area); /*!< if buffer rendering is not being used */ - #endif -} - -void RGFW_releaseCursor(RGFW_window* win) { - RGFW_UNUSED(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); RGFW_UNUSED(rect); - - RECT clipRect; - GetClientRect(win->src.window, &clipRect); - ClientToScreen(win->src.window, (POINT*) &clipRect.left); - ClientToScreen(win->src.window, (POINT*) &clipRect.right); - ClipCursor(&clipRect); - - const RAWINPUTDEVICE id = { 0x01, 0x02, 0, win->src.window }; - RegisterRawInputDevices(&id, 1, sizeof(id)); -} - -#define RGFW_LOAD_LIBRARY(x, lib) if (x == NULL) x = LoadLibraryA(lib) - -#ifdef RGFW_DIRECTX - -#define OEMRESOURCE -#include - -#ifndef __cplusplus - #define __uuidof(T) IID_##T -#endif - -int RGFW_window_createDXSwapChain(RGFW_window* win, IDXGIFactory* pFactory, IUnknown* pDevice, IDXGISwapChain** swapchain) { - RGFW_ASSERT(win && pFactory && pDevice && swapchain); - - static DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 }; - swapChainDesc.BufferCount = 2; - swapChainDesc.BufferDesc.Width = win->r.w; - swapChainDesc.BufferDesc.Height = win->r.h; - swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapChainDesc.OutputWindow = (HWND)win->src.window; - swapChainDesc.SampleDesc.Count = 1; - swapChainDesc.SampleDesc.Quality = 0; - swapChainDesc.Windowed = TRUE; - swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; - - HRESULT hr = pFactory->lpVtbl->CreateSwapChain(pFactory, (IUnknown*)pDevice, &swapChainDesc, swapchain); - if (FAILED(hr)) { - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errDirectXContext, RGFW_DEBUG_CTX(.win = win, .srcError = hr), "Failed to create DirectX swap chain!"); - return -2; - } - - return 0; -} -#endif - -void RGFW_win32_makeWindowTransparent(RGFW_window* win) { - if (!(win->_flags & RGFW_windowTransparent)) return; - - #ifndef RGFW_NO_DWM - if (DwmEnableBlurBehindWindowSRC != NULL) { - DWM_BLURBEHIND bb = {0, 0, 0, 0}; - bb.dwFlags = 0x1; - bb.fEnable = TRUE; - bb.hRgnBlur = NULL; - DwmEnableBlurBehindWindowSRC(win->src.window, &bb); - - } else - #endif - { - SetWindowLong(win->src.window, GWL_EXSTYLE, WS_EX_LAYERED); - SetLayeredWindowAttributes(win->src.window, 0, 128, LWA_ALPHA); - } -} - -RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) { - #ifndef RGFW_NO_XINPUT - if (RGFW_XInput_dll == NULL) - RGFW_loadXInput(); - #endif - - #ifndef RGFW_NO_DPI - #if (_WIN32_WINNT >= 0x0600) - SetProcessDPIAware(); - #endif - #endif - - #ifndef RGFW_NO_WINMM - #ifndef RGFW_NO_LOAD_WINMM - RGFW_LOAD_LIBRARY(RGFW_winmm_dll, "winmm.dll"); - RGFW_PROC_DEF(RGFW_winmm_dll, timeBeginPeriod); - RGFW_PROC_DEF(RGFW_winmm_dll, timeEndPeriod); - #endif - timeBeginPeriod(1); - #endif - - #ifndef RGFW_NO_DWM - RGFW_LOAD_LIBRARY(RGFW_dwm_dll, "dwmapi.dll"); - RGFW_PROC_DEF(RGFW_dwm_dll, DwmEnableBlurBehindWindow); - #endif - - RGFW_LOAD_LIBRARY(RGFW_wgl_dll, "opengl32.dll"); - #ifndef RGFW_NO_LOAD_WGL - RGFW_PROC_DEF(RGFW_wgl_dll, wglCreateContext); - RGFW_PROC_DEF(RGFW_wgl_dll, wglDeleteContext); - RGFW_PROC_DEF(RGFW_wgl_dll, wglDeleteContext); - RGFW_PROC_DEF(RGFW_wgl_dll, wglGetProcAddress); - RGFW_PROC_DEF(RGFW_wgl_dll, wglMakeCurrent); - RGFW_PROC_DEF(RGFW_wgl_dll, wglGetCurrentDC); - RGFW_PROC_DEF(RGFW_wgl_dll, wglGetCurrentContext); - RGFW_PROC_DEF(RGFW_wgl_dll, wglShareLists); - #endif - - if (name[0] == 0) name = (char*) " "; - - RGFW_window_basic_init(win, rect, flags); - - win->src.hIconSmall = win->src.hIconBig = NULL; - win->src.maxSize = RGFW_AREA(0, 0); - win->src.minSize = RGFW_AREA(0, 0); - win->src.aspectRatio = RGFW_AREA(0, 0); - - HINSTANCE inh = GetModuleHandleA(NULL); - - #ifndef __cplusplus - WNDCLASSW Class = { 0 }; /*!< Setup the Window class. */ - #else - WNDCLASSW Class = { }; - #endif - - if (RGFW_className == NULL) - RGFW_className = (char*)name; - - wchar_t wide_class[255]; - MultiByteToWideChar(CP_UTF8, 0, RGFW_className, -1, wide_class, 255); - - Class.lpszClassName = wide_class; - Class.hInstance = inh; - Class.hCursor = LoadCursor(NULL, IDC_ARROW); - Class.lpfnWndProc = WndProcW; - Class.cbClsExtra = sizeof(RGFW_window*); - - Class.hIcon = (HICON)LoadImageA(GetModuleHandleW(NULL), "RGFW_ICON", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); - if (Class.hIcon == NULL) - Class.hIcon = (HICON)LoadImageA(NULL, (LPCSTR)IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); - - RegisterClassW(&Class); - - DWORD window_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN; - - RECT windowRect, clientRect; - - if (!(flags & RGFW_windowNoBorder)) { - window_style |= WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_MINIMIZEBOX; - - if (!(flags & RGFW_windowNoResize)) - window_style |= WS_SIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME; - } else - window_style |= WS_POPUP | WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX; - - wchar_t wide_name[255]; - MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_name, 255); - - HWND dummyWin = CreateWindowW(Class.lpszClassName, (wchar_t*)wide_name, window_style, win->r.x, win->r.y, win->r.w, win->r.h, 0, 0, inh, 0); - - GetWindowRect(dummyWin, &windowRect); - GetClientRect(dummyWin, &clientRect); - - win->src.hOffset = (windowRect.bottom - windowRect.top) - (clientRect.bottom - clientRect.top); - win->src.window = CreateWindowW(Class.lpszClassName, (wchar_t*)wide_name, window_style, win->r.x, win->r.y, win->r.w, win->r.h + win->src.hOffset, 0, 0, inh, 0); - - SetPropA(win->src.window, "RGFW", win); - - if (flags & RGFW_windowAllowDND) { - win->_flags |= RGFW_windowAllowDND; - RGFW_window_setDND(win, 1); - } - win->src.hdc = GetDC(win->src.window); - - if ((flags & RGFW_windowNoInitAPI) == 0) { - #ifdef RGFW_OPENGL - HDC dummy_dc = GetDC(dummyWin); - - u32 pfd_flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; - - //if (RGFW_DOUBLE_BUFFER) - pfd_flags |= PFD_DOUBLEBUFFER; - - PIXELFORMATDESCRIPTOR pfd = { - sizeof(PIXELFORMATDESCRIPTOR), // Size of the descriptor - 1, // Version - pfd_flags, // Flags to specify what the pixel format supports (e.g., PFD_SUPPORT_OPENGL) - PFD_TYPE_RGBA, // Pixel type is RGBA - 32, // Color bits (red, green, blue channels) - 0, 0, 0, 0, 0, 0, // No color bits for unused channels - 8, // Alpha bits (important for transparency) - 0, // No accumulation buffer bits needed - 0, 0, 0, 0, // No accumulation bits - 32, // Depth buffer bits - 8, // Stencil buffer bits - 0, // Auxiliary buffer bits (unused) - PFD_MAIN_PLANE, // Use the main plane for rendering - 0, 0, 0, 0, 0 // Reserved fields - }; - - - int pixel_format = ChoosePixelFormat(dummy_dc, &pfd); - SetPixelFormat(dummy_dc, pixel_format, &pfd); - - HGLRC dummy_context = wglCreateContext(dummy_dc); - wglMakeCurrent(dummy_dc, dummy_context); - - if (wglChoosePixelFormatARB == NULL) { - wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) (void*) wglGetProcAddress("wglCreateContextAttribsARB"); - wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) (void*)wglGetProcAddress("wglChoosePixelFormatARB"); - } - - 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 = {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 (flags & RGFW_windowOpenglSoftware) - pfd.dwFlags |= PFD_GENERIC_FORMAT | PFD_GENERIC_ACCELERATED; - - if (wglChoosePixelFormatARB != NULL) { - i32* pixel_format_attribs = (i32*)RGFW_initFormatAttribs(flags & RGFW_windowOpenglSoftware); - - int pixel_format; - UINT num_formats; - wglChoosePixelFormatARB(win->src.hdc, pixel_format_attribs, 0, 1, &pixel_format, &num_formats); - if (!num_formats) - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to create a pixel format for WGL"); - - DescribePixelFormat(win->src.hdc, pixel_format, sizeof(pfd), &pfd); - if (!SetPixelFormat(win->src.hdc, pixel_format, &pfd)) - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to set the WGL pixel format"); - } - - /* create opengl/WGL context for the specified version */ - u32 index = 0; - i32 attribs[40]; - - if (RGFW_GL_HINTS[RGFW_glProfile]== RGFW_glCore) { - SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB); - } - else { - SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB); - } - - if (RGFW_GL_HINTS[RGFW_glMinor] || RGFW_GL_HINTS[RGFW_glMajor]) { - SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_GL_HINTS[RGFW_glMinor]); - SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_GL_HINTS[RGFW_glMajor]); - } - - SET_ATTRIB(0, 0); - - win->src.ctx = (HGLRC)wglCreateContextAttribsARB(win->src.hdc, NULL, attribs); - } else { /* fall back to a default context (probably opengl 2 or something) */ - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to create an accelerated OpenGL Context"); - - int pixel_format = ChoosePixelFormat(win->src.hdc, &pfd); - SetPixelFormat(win->src.hdc, pixel_format, &pfd); - - win->src.ctx = wglCreateContext(win->src.hdc); - } - - wglMakeCurrent(win->src.hdc, win->src.ctx); - #endif - } - - #ifdef RGFW_OPENGL - if ((flags & RGFW_windowNoInitAPI) == 0) { - ReleaseDC(win->src.window, win->src.hdc); - win->src.hdc = GetDC(win->src.window); - wglMakeCurrent(win->src.hdc, win->src.ctx); - } - #endif - - DestroyWindow(dummyWin); - - #ifdef RGFW_EGL - if ((flags & RGFW_windowNoInitAPI) == 0) - RGFW_createOpenGLContext(win); - #endif - - ShowWindow(win->src.window, SW_SHOWNORMAL); - RGFW_window_setFlags(win, flags); - - RGFW_win32_makeWindowTransparent(win); - - #ifdef RGFW_OPENGL - if (RGFW_root != win) - wglShareLists(RGFW_root->src.ctx, win->src.ctx); - #endif - - RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new window was created"); - return win; -} - -void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { - RGFW_setBit(&win->_flags, RGFW_windowNoBorder, !border); - DWORD style = GetWindowLong(win->src.window, GWL_STYLE); - - if (border == 0) { - SetWindowLong(win->src.window, GWL_STYLE, style & ~WS_OVERLAPPEDWINDOW); - SetWindowPos( - win->src.window, HWND_TOP, 0, 0, 0, 0, - SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE - ); - } - else { - SetWindowLong(win->src.window, GWL_STYLE, style | WS_OVERLAPPEDWINDOW); - SetWindowPos( - win->src.window, HWND_TOP, 0, 0, 0, 0, - SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE - ); - } -} - -void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow) { - RGFW_setBit(&win->_flags, RGFW_windowAllowDND, allow); - DragAcceptFiles(win->src.window, allow); -} - -RGFW_area RGFW_getScreenSize(void) { - HDC dc = GetDC(NULL); - RGFW_area area = RGFW_AREA(GetDeviceCaps(dc, HORZRES), GetDeviceCaps(dc, VERTRES)); - ReleaseDC(NULL, dc); - return area; -} - -RGFW_point RGFW_getGlobalMousePoint(void) { - POINT p; - GetCursorPos(&p); - - return RGFW_POINT(p.x, p.y); -} - -void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a) { - RGFW_ASSERT(win != NULL); - win->src.aspectRatio = a; -} - -void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { - RGFW_ASSERT(win != NULL); - win->src.minSize = a; -} - -void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { - RGFW_ASSERT(win != NULL); - win->src.maxSize = a; -} - -void RGFW_window_focus(RGFW_window* win) { - RGFW_ASSERT(win); - SetForegroundWindow(win->src.window); - SetFocus(win->src.window); -} - -void RGFW_window_raise(RGFW_window* win) { - RGFW_ASSERT(win); - BringWindowToTop(win->src.window); - SetWindowPos(win->src.window, HWND_TOP, win->r.x, win->r.y, win->r.w, win->r.h, SWP_NOSIZE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); -} - -void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) { - RGFW_ASSERT(win != NULL); - - if (fullscreen == RGFW_FALSE) { - RGFW_window_setBorder(win, 1); - SetWindowPos(win->src.window, HWND_NOTOPMOST, win->_oldRect.x, win->_oldRect.y, win->_oldRect.w, win->_oldRect.h + win->src.hOffset, - SWP_NOOWNERZORDER | SWP_FRAMECHANGED); - - win->_flags &= ~RGFW_windowFullscreen; - win->r = win->_oldRect; - return; - } - - win->_flags |= RGFW_windowFullscreen; - - RGFW_monitor mon = RGFW_window_getMonitor(win); - RGFW_window_setBorder(win, 0); - SetWindowPos(win->src.window, HWND_TOPMOST, 0, 0, mon.mode.area.w, mon.mode.area.h, SWP_NOOWNERZORDER | SWP_FRAMECHANGED); - - win->_oldRect = win->r; - win->r = RGFW_RECT(0, 0, mon.mode.area.w, mon.mode.area.h); -} - -void RGFW_window_maximize(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - RGFW_window_hide(win); - ShowWindow(win->src.window, SW_MAXIMIZE); -} - -void RGFW_window_minimize(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - ShowWindow(win->src.window, SW_MINIMIZE); -} - -void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) { - RGFW_ASSERT(win != NULL); - if (floating) SetWindowPos(win->src.window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); - else SetWindowPos(win->src.window, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); -} - -void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) { - SetWindowLong(win->src.window, GWL_EXSTYLE, WS_EX_LAYERED); - SetLayeredWindowAttributes(win->src.window, 0, opacity, LWA_ALPHA); -} - -void RGFW_window_restore(RGFW_window* win) { RGFW_window_show(win); } - -RGFW_bool RGFW_window_isFloating(RGFW_window* win) { - return (GetWindowLongPtr(win->src.window, GWL_EXSTYLE) & WS_EX_TOPMOST) != 0; -} - -u8 RGFW_xinput2RGFW[] = { - RGFW_gamepadA, /* or PS X button */ - RGFW_gamepadB, /* or PS circle button */ - RGFW_gamepadX, /* or PS square button */ - RGFW_gamepadY, /* or PS triangle button */ - RGFW_gamepadR1, /* right bumper */ - RGFW_gamepadL1, /* left bump */ - RGFW_gamepadL2, /* left trigger */ - RGFW_gamepadR2, /* right trigger */ - 0, 0, 0, 0, 0, 0, 0, 0, - RGFW_gamepadUp, /* dpad up */ - RGFW_gamepadDown, /* dpad down */ - RGFW_gamepadLeft, /* dpad left */ - RGFW_gamepadRight, /* dpad right */ - RGFW_gamepadStart, /* start button */ - RGFW_gamepadSelect,/* select button */ - RGFW_gamepadL3, - RGFW_gamepadR3, -}; - -static i32 RGFW_checkXInput(RGFW_window* win, RGFW_event* e) { - #ifndef RGFW_NO_XINPUT - - RGFW_UNUSED(win); - size_t i; - for (i = 0; i < 4; i++) { - XINPUT_KEYSTROKE keystroke; - - if (XInputGetKeystroke == NULL) - return 0; - - DWORD result = XInputGetKeystroke((DWORD)i, 0, &keystroke); - - if ((keystroke.Flags & XINPUT_KEYSTROKE_REPEAT) == 0 && result != ERROR_EMPTY) { - if (result != ERROR_SUCCESS) - return 0; - - if (keystroke.VirtualKey > VK_PAD_RTHUMB_PRESS) - continue; - - //gamepad + 1 = RGFW_gamepadButtonReleased - e->type = RGFW_gamepadButtonPressed + !(keystroke.Flags & XINPUT_KEYSTROKE_KEYDOWN); - e->button = RGFW_xinput2RGFW[keystroke.VirtualKey - 0x5800]; - RGFW_gamepadPressed[i][e->button].prev = RGFW_gamepadPressed[i][e->button].current; - RGFW_gamepadPressed[i][e->button].current = (keystroke.Flags & XINPUT_KEYSTROKE_KEYDOWN); - - RGFW_gamepadButtonCallback(win, i, e->button, e->type == RGFW_gamepadButtonPressed); - return 1; - } - - XINPUT_STATE state; - if (XInputGetState == NULL || - XInputGetState((DWORD) i, &state) == ERROR_DEVICE_NOT_CONNECTED - ) { - if (RGFW_gamepads[i] == 0) - continue; - - RGFW_gamepads[i] = 0; - RGFW_gamepadCount--; - - win->event.type = RGFW_gamepadDisconnected; - win->event.gamepad = i; - RGFW_gamepadCallback(win, i, 0); - return 1; - } - - if (RGFW_gamepads[i] == 0) { - RGFW_gamepads[i] = 1; - RGFW_gamepadCount++; - - char str[] = "Microsoft X-Box (XInput device)"; - RGFW_MEMCPY(RGFW_gamepads_name[i], str, sizeof(str)); - RGFW_gamepads_name[i][sizeof(RGFW_gamepads_name[i]) - 1] = '\0'; - win->event.type = RGFW_gamepadConnected; - win->event.gamepad = i; - RGFW_gamepads_type[i] = RGFW_gamepadMicrosoft; - - RGFW_gamepadCallback(win, i, 1); - return 1; - } - -#define INPUT_DEADZONE ( 0.24f * (float)(0x7FFF) ) // Default to 24% of the +/- 32767 range. This is a reasonable default value but can be altered if needed. - - if ((state.Gamepad.sThumbLX < INPUT_DEADZONE && - state.Gamepad.sThumbLX > -INPUT_DEADZONE) && - (state.Gamepad.sThumbLY < INPUT_DEADZONE && - state.Gamepad.sThumbLY > -INPUT_DEADZONE)) - { - state.Gamepad.sThumbLX = 0; - state.Gamepad.sThumbLY = 0; - } - - if ((state.Gamepad.sThumbRX < INPUT_DEADZONE && - state.Gamepad.sThumbRX > -INPUT_DEADZONE) && - (state.Gamepad.sThumbRY < INPUT_DEADZONE && - state.Gamepad.sThumbRY > -INPUT_DEADZONE)) - { - state.Gamepad.sThumbRX = 0; - state.Gamepad.sThumbRY = 0; - } - - e->axisesCount = 2; - RGFW_point axis1 = RGFW_POINT(((float)state.Gamepad.sThumbLX / 32768.0f) * 100, ((float)state.Gamepad.sThumbLY / -32768.0f) * 100); - RGFW_point axis2 = RGFW_POINT(((float)state.Gamepad.sThumbRX / 32768.0f) * 100, ((float)state.Gamepad.sThumbRY / -32768.0f) * 100); - - if (axis1.x != e->axis[0].x || axis1.y != e->axis[0].y){ - win->event.whichAxis = 0; - - e->type = RGFW_gamepadAxisMove; - e->axis[0] = axis1; - RGFW_gamepadAxes[i][0] = e->axis[0]; - - RGFW_gamepadAxisCallback(win, e->gamepad, e->axis, e->axisesCount, e->whichAxis); - return 1; - } - - if (axis2.x != e->axis[1].x || axis2.y != e->axis[1].y) { - win->event.whichAxis = 1; - e->type = RGFW_gamepadAxisMove; - e->axis[1] = axis2; - RGFW_gamepadAxes[i][1] = e->axis[1]; - - RGFW_gamepadAxisCallback(win, e->gamepad, e->axis, e->axisesCount, e->whichAxis); - return 1; - } - } - - #endif - - return 0; -} - -void RGFW_stopCheckEvents(void) { - PostMessageW(RGFW_root->src.window, WM_NULL, 0, 0); -} - -void RGFW_window_eventWait(RGFW_window* win, u32 waitMS) { - RGFW_UNUSED(win); - MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD)waitMS, QS_ALLINPUT); -} - -RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { - RGFW_event* ev = RGFW_window_checkEventCore(win); - if (ev) { - if (ev == (RGFW_event*)-1) return NULL; - return ev; - } - - static HDROP drop; - if (win->event.type == RGFW_DNDInit) { - if (win->event.droppedFilesCount) { - u32 i; - for (i = 0; i < win->event.droppedFilesCount; i++) - win->event.droppedFiles[i][0] = '\0'; - } - - win->event.droppedFilesCount = 0; - win->event.droppedFilesCount = DragQueryFileW(drop, 0xffffffff, NULL, 0); - - u32 i; - for (i = 0; i < win->event.droppedFilesCount; i++) { - UINT length = DragQueryFileW(drop, i, NULL, 0); - if (length == 0) - continue; - - WCHAR buffer[RGFW_MAX_PATH * 2]; - if (length > (RGFW_MAX_PATH * 2) - 1) - length = RGFW_MAX_PATH * 2; - - DragQueryFileW(drop, i, buffer, length + 1); - - char* str = RGFW_createUTF8FromWideStringWin32(buffer); - if (str != NULL) - RGFW_MEMCPY(win->event.droppedFiles[i], str, length + 1); - - win->event.droppedFiles[i][RGFW_MAX_PATH - 1] = '\0'; - } - - DragFinish(drop); - RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount); - - win->event.type = RGFW_DND; - return &win->event; - } - - if (RGFW_checkXInput(win, &win->event)) - return &win->event; - - static BYTE keyboardState[256]; - GetKeyboardState(keyboardState); - - if (!IsWindow(win->src.window)) { - win->event.type = RGFW_quit; - RGFW_windowQuitCallback(win); - return &win->event; - } - - MSG msg; - if (PeekMessageA(&msg, win->src.window, 0u, 0u, PM_REMOVE) == 0) - return NULL; - - switch (msg.message) { - case WM_CLOSE: - case WM_QUIT: - RGFW_windowQuitCallback(win); - win->event.type = RGFW_quit; - break; - #if(_WIN32_WINNT >= 0x0600) - case WM_DWMCOMPOSITIONCHANGED: - case WM_DWMCOLORIZATIONCOLORCHANGED: - RGFW_win32_makeWindowTransparent(win); - break; - #endif - - case WM_MOUSELEAVE: - win->event.type = RGFW_mouseLeave; - win->_flags |= RGFW_MOUSE_LEFT; - RGFW_mouseNotifyCallBack(win, win->event.point, 0); - break; - case WM_SYSKEYUP: case WM_KEYUP: { - i32 scancode = (HIWORD(msg.lParam) & (KF_EXTENDED | 0xff)); - if (scancode == 0) - scancode = MapVirtualKeyW((u32)msg.wParam, MAPVK_VK_TO_VSC); - - switch (scancode) { - case 0x54: scancode = 0x137; break; /* Alt+PrtS */ - case 0x146: scancode = 0x45; break; /* Ctrl+Pause */ - case 0x136: scancode = 0x36; break; /* CJK IME sets the extended bit for right Shift */ - default: break; - } - - win->event.key = RGFW_apiKeyToRGFW((u32) scancode); - - if (msg.wParam == VK_CONTROL) { - if (HIWORD(msg.lParam) & KF_EXTENDED) - win->event.key = RGFW_controlR; - else win->event.key = RGFW_controlL; - } - - wchar_t charBuffer; - ToUnicodeEx(msg.wParam, scancode, keyboardState, (wchar_t*)&charBuffer, 1, 0, NULL); - - win->event.keyChar = (u8)charBuffer; - - RGFW_keyboard[win->event.key].prev = RGFW_isPressed(win, win->event.key); - win->event.type = RGFW_keyReleased; - RGFW_keyboard[win->event.key].current = 0; - - RGFW_updateKeyMods(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001), (GetKeyState(VK_SCROLL) & 0x0001)); - - RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, 0); - break; - } - case WM_SYSKEYDOWN: case WM_KEYDOWN: { - i32 scancode = (HIWORD(msg.lParam) & (KF_EXTENDED | 0xff)); - if (scancode == 0) - scancode = MapVirtualKeyW((u32)msg.wParam, MAPVK_VK_TO_VSC); - - switch (scancode) { - case 0x54: scancode = 0x137; break; /* Alt+PrtS */ - case 0x146: scancode = 0x45; break; /* Ctrl+Pause */ - case 0x136: scancode = 0x36; break; /* CJK IME sets the extended bit for right Shift */ - default: break; - } - - win->event.key = RGFW_apiKeyToRGFW((u32) scancode); - - if (msg.wParam == VK_CONTROL) { - if (HIWORD(msg.lParam) & KF_EXTENDED) - win->event.key = RGFW_controlR; - else win->event.key = RGFW_controlL; - } - - wchar_t charBuffer; - ToUnicodeEx(msg.wParam, scancode, keyboardState, &charBuffer, 1, 0, NULL); - win->event.keyChar = (u8)charBuffer; - - RGFW_keyboard[win->event.key].prev = RGFW_isPressed(win, win->event.key); - - win->event.type = RGFW_keyPressed; - win->event.repeat = RGFW_isPressed(win, win->event.key); - RGFW_keyboard[win->event.key].current = 1; - RGFW_updateKeyMods(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001), (GetKeyState(VK_SCROLL) & 0x0001)); - - RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, 1); - break; - } - - case WM_MOUSEMOVE: { - if ((win->_flags & RGFW_HOLD_MOUSE)) - break; - - win->event.type = RGFW_mousePosChanged; - - i32 x = GET_X_LPARAM(msg.lParam); - i32 y = GET_Y_LPARAM(msg.lParam); - - RGFW_mousePosCallback(win, win->event.point, win->event.vector); - - if (win->_flags & RGFW_MOUSE_LEFT) { - win->_flags &= ~RGFW_MOUSE_LEFT; - win->event.type = RGFW_mouseEnter; - RGFW_mouseNotifyCallBack(win, win->event.point, 1); - } - - win->event.point.x = x; - win->event.point.y = y; - win->_lastMousePoint = RGFW_POINT(x, y); - - break; - } - case WM_INPUT: { - if (!(win->_flags & RGFW_HOLD_MOUSE)) - break; - - unsigned size = sizeof(RAWINPUT); - static RAWINPUT raw = {}; - - GetRawInputData((HRAWINPUT)msg.lParam, RID_INPUT, &raw, &size, sizeof(RAWINPUTHEADER)); - - if (raw.header.dwType != RIM_TYPEMOUSE || (raw.data.mouse.lLastX == 0 && raw.data.mouse.lLastY == 0) ) - break; - - if (raw.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) { - POINT pos = {0, 0}; - int width, height; - - if (raw.data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) { - pos.x += GetSystemMetrics(SM_XVIRTUALSCREEN); - pos.y += GetSystemMetrics(SM_YVIRTUALSCREEN); - width = GetSystemMetrics(SM_CXVIRTUALSCREEN); - height = GetSystemMetrics(SM_CYVIRTUALSCREEN); - } - else { - width = GetSystemMetrics(SM_CXSCREEN); - height = GetSystemMetrics(SM_CYSCREEN); - } - - pos.x += (int) ((raw.data.mouse.lLastX / 65535.f) * width); - pos.y += (int) ((raw.data.mouse.lLastY / 65535.f) * height); - ScreenToClient(win->src.window, &pos); - - win->event.vector.x = pos.x - win->_lastMousePoint.x; - win->event.vector.y = pos.y - win->_lastMousePoint.y; - } else { - win->event.vector.x = raw.data.mouse.lLastX; - win->event.vector.y = raw.data.mouse.lLastY; - } - - win->event.type = RGFW_mousePosChanged; - win->_lastMousePoint.x += win->event.vector.x; - win->_lastMousePoint.y += win->event.vector.y; - win->event.point = win->_lastMousePoint; - RGFW_mousePosCallback(win, win->event.point, win->event.vector); - break; - } - case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: case WM_XBUTTONDOWN: - if (msg.message == WM_XBUTTONDOWN) - win->event.button = RGFW_mouseMisc1 + (GET_XBUTTON_WPARAM(msg.wParam) == XBUTTON2); - else win->event.button = (msg.message == WM_LBUTTONDOWN) ? RGFW_mouseLeft : - (msg.message == WM_RBUTTONDOWN) ? RGFW_mouseRight : RGFW_mouseMiddle; - - win->event.type = RGFW_mouseButtonPressed; - RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; - RGFW_mouseButtons[win->event.button].current = 1; - RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); - break; - case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: case WM_XBUTTONUP: - if (msg.message == WM_XBUTTONUP) - win->event.button = RGFW_mouseMisc1 + (GET_XBUTTON_WPARAM(msg.wParam) == XBUTTON2); - else win->event.button = (msg.message == WM_LBUTTONUP) ? RGFW_mouseLeft : - (msg.message == WM_RBUTTONUP) ? RGFW_mouseRight : RGFW_mouseMiddle; - win->event.type = RGFW_mouseButtonReleased; - RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; - RGFW_mouseButtons[win->event.button].current = 0; - RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0); - break; - case WM_MOUSEWHEEL: - if (msg.wParam > 0) - win->event.button = RGFW_mouseScrollUp; - else - win->event.button = RGFW_mouseScrollDown; - - RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; - RGFW_mouseButtons[win->event.button].current = 1; - - win->event.scroll = (SHORT) HIWORD(msg.wParam) / (double) WHEEL_DELTA; - - win->event.type = RGFW_mouseButtonPressed; - RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); - break; - case WM_DROPFILES: { - win->event.type = RGFW_DNDInit; - - drop = (HDROP) msg.wParam; - POINT pt; - - /* Move the mouse to the position of the drop */ - DragQueryPoint(drop, &pt); - - win->event.point.x = pt.x; - win->event.point.y = pt.y; - - RGFW_dndInitCallback(win, win->event.point); - } - break; - default: - TranslateMessage(&msg); - DispatchMessageA(&msg); - return RGFW_window_checkEvent(win); - } - - TranslateMessage(&msg); - DispatchMessageA(&msg); - - return &win->event; -} - -RGFW_bool RGFW_window_isHidden(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - - return IsWindowVisible(win->src.window) == 0 && !RGFW_window_isMinimized(win); -} - -RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - - #ifndef __cplusplus - WINDOWPLACEMENT placement = { 0 }; - #else - WINDOWPLACEMENT placement = { }; - #endif - GetWindowPlacement(win->src.window, &placement); - return placement.showCmd == SW_SHOWMINIMIZED; -} - -RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - - #ifndef __cplusplus - WINDOWPLACEMENT placement = { 0 }; - #else - WINDOWPLACEMENT placement = { }; - #endif - GetWindowPlacement(win->src.window, &placement); - return placement.showCmd == SW_SHOWMAXIMIZED || IsZoomed(win->src.window); -} - -typedef struct { int iIndex; HMONITOR hMonitor; } RGFW_mInfo; -BOOL CALLBACK GetMonitorByHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { - RGFW_UNUSED(hdcMonitor); - RGFW_UNUSED(lprcMonitor); - - RGFW_mInfo* info = (RGFW_mInfo*) dwData; - if (info->hMonitor == hMonitor) - return RGFW_FALSE; - - info->iIndex++; - return RGFW_TRUE; -} - -#ifndef RGFW_NO_MONITOR - -RGFW_monitor win32CreateMonitor(HMONITOR src) { - RGFW_monitor monitor; - MONITORINFOEX monitorInfo; - - monitorInfo.cbSize = sizeof(MONITORINFOEX); - GetMonitorInfoA(src, (LPMONITORINFO)&monitorInfo); - - RGFW_mInfo info; - info.iIndex = 0; - info.hMonitor = src; - - /* get the monitor's index */ - DISPLAY_DEVICEA dd; - dd.cb = sizeof(dd); - - for (DWORD deviceNum = 0; EnumDisplayDevicesA(NULL, deviceNum, &dd, 0); deviceNum++) { - if (!(dd.StateFlags & DISPLAY_DEVICE_ACTIVE)) - continue; - - DEVMODE dm; - ZeroMemory(&dm, sizeof(dm)); - dm.dmSize = sizeof(dm); - - if (EnumDisplaySettingsA(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm)) { - monitor.mode.refreshRate = dm.dmDisplayFrequency; - RGFW_splitBPP(dm.dmBitsPerPel, &monitor.mode); - } - - DISPLAY_DEVICEA mdd; - mdd.cb = sizeof(mdd); - - if (EnumDisplayDevicesA(dd.DeviceName, info.iIndex, &mdd, 0)) { - RGFW_MEMCPY(monitor.name, mdd.DeviceString, 128); - break; - } - } - - - monitor.x = monitorInfo.rcWork.left; - monitor.y = monitorInfo.rcWork.top; - monitor.mode.area.w = monitorInfo.rcWork.right - monitorInfo.rcWork.left; - monitor.mode.area.h = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top; - - HDC hdc = CreateDC(monitorInfo.szDevice, NULL, NULL, NULL); - /* get pixels per inch */ - float dpiX = (float)GetDeviceCaps(hdc, LOGPIXELSX); - float dpiY = (float)GetDeviceCaps(hdc, LOGPIXELSX); - - monitor.scaleX = dpiX / 96.0f; - monitor.scaleY = dpiY / 96.0f; - monitor.pixelRatio = dpiX >= 192.0f ? 2 : 1; - - monitor.physW = GetDeviceCaps(hdc, HORZSIZE) / 25.4; - monitor.physH = GetDeviceCaps(hdc, VERTSIZE) / 25.4; - DeleteDC(hdc); - - #ifndef RGFW_NO_DPI - RGFW_LOAD_LIBRARY(RGFW_Shcore_dll, "shcore.dll"); - RGFW_PROC_DEF(RGFW_Shcore_dll, GetDpiForMonitor); - - if (GetDpiForMonitor != NULL) { - u32 x, y; - GetDpiForMonitor(src, MDT_EFFECTIVE_DPI, &x, &y); - monitor.scaleX = (float) (x) / (float) 96.0f; - monitor.scaleY = (float) (y) / (float) 96.0f; - monitor.pixelRatio = dpiX >= 192.0f ? 2 : 1; - } - #endif - - RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, RGFW_DEBUG_CTX_MON(monitor), "monitor found"); - return monitor; -} -#endif /* RGFW_NO_MONITOR */ - -#ifndef RGFW_NO_MONITOR - -RGFW_monitor RGFW_monitors[6]; -BOOL CALLBACK GetMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { - RGFW_UNUSED(hdcMonitor); - RGFW_UNUSED(lprcMonitor); - - RGFW_mInfo* info = (RGFW_mInfo*) dwData; - - if (info->iIndex >= 6) - return FALSE; - - RGFW_monitors[info->iIndex] = win32CreateMonitor(hMonitor); - info->iIndex++; - - return TRUE; -} - -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; - info.iIndex = 0; - while (EnumDisplayMonitors(NULL, NULL, GetMonitorHandle, (LPARAM) &info)); - - return RGFW_monitors; -} - -RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { - HMONITOR src = MonitorFromWindow(win->src.window, MONITOR_DEFAULTTOPRIMARY); - return win32CreateMonitor(src); -} - -RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) { - HMONITOR src = MonitorFromPoint((POINT) { mon.x, mon.y }, MONITOR_DEFAULTTOPRIMARY); - - MONITORINFOEX monitorInfo; - monitorInfo.cbSize = sizeof(MONITORINFOEX); - GetMonitorInfoA(src, (LPMONITORINFO)&monitorInfo); - - DISPLAY_DEVICE dd; - dd.cb = sizeof(dd); - - // Enumerate display devices - for (DWORD deviceNum = 0; EnumDisplayDevicesA(NULL, deviceNum, &dd, 0); deviceNum++) { - if (!(dd.StateFlags & DISPLAY_DEVICE_ACTIVE)) - continue; - - DEVMODE dm; - ZeroMemory(&dm, sizeof(dm)); - dm.dmSize = sizeof(dm); - - if (EnumDisplaySettingsA(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm)) { - if (request & RGFW_monitorScale) { - dm.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT; - dm.dmPelsWidth = mode.area.w; - dm.dmPelsHeight = mode.area.h; - } - - if (request & RGFW_monitorRefresh) { - dm.dmFields |= DM_DISPLAYFREQUENCY; - dm.dmDisplayFrequency = mode.refreshRate; - } - - if (request & RGFW_monitorRGB) { - dm.dmFields |= DM_BITSPERPEL; - dm.dmBitsPerPel = mode.red + mode.green + mode.blue; - } - - if (ChangeDisplaySettingsEx(dd.DeviceName, &dm, NULL, CDS_TEST, NULL) == DISP_CHANGE_SUCCESSFUL) { - if (ChangeDisplaySettingsEx(dd.DeviceName, &dm, NULL, CDS_UPDATEREGISTRY, NULL) == DISP_CHANGE_SUCCESSFUL) - return RGFW_TRUE; - return RGFW_FALSE; - } else return RGFW_FALSE; - } - } - - return RGFW_FALSE; -} - -#endif -HICON RGFW_loadHandleImage(u8* src, RGFW_area a, BOOL icon) { - BITMAPV5HEADER bi; - ZeroMemory(&bi, sizeof(bi)); - bi.bV5Size = sizeof(bi); - bi.bV5Width = a.w; - bi.bV5Height = -((LONG) a.h); - bi.bV5Planes = 1; - bi.bV5BitCount = 32; - bi.bV5Compression = BI_BITFIELDS; - bi.bV5RedMask = 0x000000ff; - bi.bV5GreenMask = 0x0000ff00; - bi.bV5BlueMask = 0x00ff0000; - bi.bV5AlphaMask = 0xff000000; - - HDC dc = GetDC(NULL); - u8* target = NULL; - - HBITMAP color = CreateDIBSection(dc, - (BITMAPINFO*) &bi, DIB_RGB_COLORS, (void**) &target, - NULL, (DWORD) 0); - - memcpy(target, src, a.w * a.h * 4); - ReleaseDC(NULL, dc); - - HBITMAP mask = CreateBitmap(a.w, a.h, 1, 1, NULL); - - ICONINFO ii; - ZeroMemory(&ii, sizeof(ii)); - ii.fIcon = icon; - ii.xHotspot = a.w / 2; - ii.yHotspot = a.h / 2; - ii.hbmMask = mask; - ii.hbmColor = color; - - HICON handle = CreateIconIndirect(&ii); - - DeleteObject(color); - DeleteObject(mask); - - return handle; -} - -void* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels) { - RGFW_UNUSED(channels); - - HCURSOR cursor = (HCURSOR) RGFW_loadHandleImage(icon, a, FALSE); - return cursor; -} - -void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) { - RGFW_ASSERT(win && mouse); - SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) mouse); - SetCursor((HCURSOR)mouse); -} - -void RGFW_freeMouse(RGFW_mouse* mouse) { - RGFW_ASSERT(mouse); - DestroyCursor((HCURSOR)mouse); -} - -RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) { - return RGFW_window_setMouseStandard(win, RGFW_mouseArrow); -} - -RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { - RGFW_ASSERT(win != NULL); - - static const u32 mouseIconSrc[] = {OCR_NORMAL, OCR_NORMAL, OCR_IBEAM, OCR_CROSS, OCR_HAND, OCR_SIZEWE, OCR_SIZENS, OCR_SIZENWSE, OCR_SIZENESW, OCR_SIZEALL, OCR_NO}; - if (mouse > (sizeof(mouseIconSrc) / sizeof(u32))) - return RGFW_FALSE; - - char* icon = MAKEINTRESOURCEA(mouseIconSrc[mouse]); - - SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) LoadCursorA(NULL, icon)); - SetCursor(LoadCursorA(NULL, icon)); - return RGFW_TRUE; -} - -void RGFW_window_hide(RGFW_window* win) { - ShowWindow(win->src.window, SW_HIDE); -} - -void RGFW_window_show(RGFW_window* win) { - if (win->_flags & RGFW_windowFocusOnShow) RGFW_window_focus(win); - ShowWindow(win->src.window, SW_RESTORE); -} - -#define RGFW_FREE_LIBRARY(x) if (x != NULL) FreeLibrary(x); x = NULL; - -void RGFW_window_close(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - - #ifdef RGFW_EGL - RGFW_closeEGL(win); - #endif - - #ifdef RGFW_BUFFER - DeleteDC(win->src.hdcMem); - DeleteObject(win->src.bitmap); - #endif - - #ifdef RGFW_OPENGL - wglDeleteContext((HGLRC) win->src.ctx); /*!< delete opengl context */ - #endif - ReleaseDC(win->src.window, win->src.hdc); /*!< delete device context */ - DestroyWindow(win->src.window); /*!< delete window */ - - if (win->src.hIconSmall) DestroyIcon(win->src.hIconSmall); - if (win->src.hIconBig) DestroyIcon(win->src.hIconBig); - - if (win == RGFW_root) { - #ifndef RGFW_NO_XINPUT - RGFW_FREE_LIBRARY(RGFW_XInput_dll); - #endif - - #ifndef RGFW_NO_DPI - RGFW_FREE_LIBRARY(RGFW_Shcore_dll); - #endif - - #ifndef RGFW_NO_WINMM - timeEndPeriod(1); - #ifndef RGFW_NO_LOAD_WINMM - RGFW_FREE_LIBRARY(RGFW_winmm_dll); - #endif - #endif - - RGFW_FREE_LIBRARY(RGFW_wgl_dll); - RGFW_root = NULL; - - if (RGFW_hiddenMouse != NULL) { - RGFW_freeMouse(RGFW_hiddenMouse); - RGFW_hiddenMouse = 0; - } - } - - RGFW_clipboard_switch(NULL); - RGFW_FREE(win->event.droppedFiles); - - if ((win->_flags & RGFW_WINDOW_ALLOC)) - RGFW_FREE(win); -} - -void RGFW_window_move(RGFW_window* win, RGFW_point v) { - RGFW_ASSERT(win != NULL); - - win->r.x = v.x; - win->r.y = v.y; - SetWindowPos(win->src.window, HWND_TOP, win->r.x, win->r.y, 0, 0, SWP_NOSIZE); -} - -void RGFW_window_resize(RGFW_window* win, RGFW_area a) { - RGFW_ASSERT(win != NULL); - - win->r.w = a.w; - win->r.h = a.h; - SetWindowPos(win->src.window, HWND_TOP, 0, 0, win->r.w, win->r.h + win->src.hOffset, SWP_NOMOVE); -} - - -void RGFW_window_setName(RGFW_window* win, const char* name) { - RGFW_ASSERT(win != NULL); - - wchar_t wide_name[255]; - MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_name, 255); - SetWindowTextW(win->src.window, wide_name); -} - -#ifndef RGFW_NO_PASSTHROUGH - -void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) { - RGFW_ASSERT(win != NULL); - - COLORREF key = 0; - BYTE alpha = 0; - DWORD flags = 0; - DWORD exStyle = GetWindowLongW(win->src.window, GWL_EXSTYLE); - - if (exStyle & WS_EX_LAYERED) - GetLayeredWindowAttributes(win->src.window, &key, &alpha, &flags); - - if (passthrough) - exStyle |= (WS_EX_TRANSPARENT | WS_EX_LAYERED); - else { - exStyle &= ~WS_EX_TRANSPARENT; - if (exStyle & WS_EX_LAYERED && !(flags & LWA_ALPHA)) - exStyle &= ~WS_EX_LAYERED; - } - - SetWindowLongW(win->src.window, GWL_EXSTYLE, exStyle); - - if (passthrough) - SetLayeredWindowAttributes(win->src.window, key, alpha, flags); -} -#endif - -RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* src, RGFW_area a, i32 channels, u8 type) { - RGFW_ASSERT(win != NULL); - #ifndef RGFW_WIN95 - RGFW_UNUSED(channels); - - if (win->src.hIconSmall && (type & RGFW_iconWindow)) DestroyIcon(win->src.hIconSmall); - if (win->src.hIconBig && (type & RGFW_iconTaskbar)) DestroyIcon(win->src.hIconBig); - - if (src == NULL) { - HICON defaultIcon = LoadIcon(NULL, IDI_APPLICATION); - if (type & RGFW_iconWindow) - SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)defaultIcon); - if (type & RGFW_iconTaskbar) - SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)defaultIcon); - return RGFW_TRUE; - } - - if (type & RGFW_iconWindow) { - win->src.hIconSmall = RGFW_loadHandleImage(src, a, TRUE); - SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)win->src.hIconSmall); - } - if (type & RGFW_iconTaskbar) { - win->src.hIconBig = RGFW_loadHandleImage(src, a, TRUE); - SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)win->src.hIconBig); - } - return RGFW_TRUE; - #else - RGFW_UNUSED(src); - RGFW_UNUSED(a); - RGFW_UNUSED(channels); - return RGFW_FALSE; - #endif -} - -RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { - /* Open the clipboard */ - if (OpenClipboard(NULL) == 0) - return -1; - - /* Get the clipboard data as a Unicode string */ - HANDLE hData = GetClipboardData(CF_UNICODETEXT); - if (hData == NULL) { - CloseClipboard(); - return -1; - } - - wchar_t* wstr = (wchar_t*) GlobalLock(hData); - - RGFW_ssize_t textLen = 0; - - { - setlocale(LC_ALL, "en_US.UTF-8"); - - textLen = wcstombs(NULL, wstr, 0) + 1; - if (str != NULL && (RGFW_ssize_t)strCapacity <= textLen - 1) - textLen = 0; - - if (str != NULL && textLen) { - if (textLen > 1) - wcstombs(str, wstr, (textLen) ); - - str[textLen] = '\0'; - } - } - - /* Release the clipboard data */ - GlobalUnlock(hData); - CloseClipboard(); - - return textLen; -} - -void RGFW_writeClipboard(const char* text, u32 textLen) { - HANDLE object; - WCHAR* buffer; - - object = GlobalAlloc(GMEM_MOVEABLE, (1 + textLen) * sizeof(WCHAR)); - if (!object) - return; - - buffer = (WCHAR*) GlobalLock(object); - if (!buffer) { - GlobalFree(object); - return; - } - - MultiByteToWideChar(CP_UTF8, 0, text, -1, buffer, textLen); - GlobalUnlock(object); - - if (!OpenClipboard(RGFW_root->src.window)) { - GlobalFree(object); - return; - } - - EmptyClipboard(); - SetClipboardData(CF_UNICODETEXT, object); - CloseClipboard(); -} - -void RGFW_window_moveMouse(RGFW_window* win, RGFW_point p) { - RGFW_ASSERT(win != NULL); - win->_lastMousePoint = RGFW_POINT(p.x - win->r.x, p.y - win->r.y); - SetCursorPos(p.x, p.y); -} - -#ifdef RGFW_OPENGL -void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { - if (win == NULL) - wglMakeCurrent(NULL, NULL); - else - wglMakeCurrent(win->src.hdc, (HGLRC) win->src.ctx); -} -void* RGFW_getCurrent_OpenGL(void) { return wglGetCurrentContext(); } -#endif - -#ifndef RGFW_EGL - -void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { - RGFW_ASSERT(win != NULL); - - #if defined(RGFW_OPENGL) - typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval); - static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; - static void* loadSwapFunc = (void*) 1; - - if (loadSwapFunc == NULL) { - RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, RGFW_DEBUG_CTX(win, 0), "wglSwapIntervalEXT not supported"); - return; - } - - if (wglSwapIntervalEXT == NULL) { - loadSwapFunc = (void*) wglGetProcAddress("wglSwapIntervalEXT"); - wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) loadSwapFunc; - } - - if (wglSwapIntervalEXT(swapInterval) == FALSE) - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to set swap interval"); - #else - RGFW_UNUSED(swapInterval); - #endif -} - -#endif - -void RGFW_window_swapBuffers(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - /* clear the window */ - - if (!(win->_flags & RGFW_NO_CPU_RENDER)) { - #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - if (win->buffer != win->src.bitmapBits) - memcpy(win->src.bitmapBits, win->buffer, win->bufferSize.w * win->bufferSize.h * 4); - - RGFW_RGB_to_BGR(win, win->src.bitmapBits); - BitBlt(win->src.hdc, 0, 0, win->r.w, win->r.h, win->src.hdcMem, 0, 0, SRCCOPY); - #endif - } - - if (!(win->_flags & RGFW_NO_GPU_RENDER)) { - #ifdef RGFW_EGL - eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); - #elif defined(RGFW_OPENGL) - SwapBuffers(win->src.hdc); - #endif - } -} - -char* RGFW_createUTF8FromWideStringWin32(const WCHAR* source) { - if (source == NULL) { - return NULL; - } - i32 size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL); - if (!size) { - return NULL; - } - - static char target[RGFW_MAX_PATH * 2]; - if (size > RGFW_MAX_PATH * 2) - size = RGFW_MAX_PATH * 2; - - target[size] = 0; - - if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, size, NULL, NULL)) { - return NULL; - } - - return target; -} - -u64 RGFW_getTimerFreq(void) { - static u64 frequency = 0; - if (frequency == 0) QueryPerformanceFrequency((LARGE_INTEGER*)&frequency); - - return frequency; -} - -u64 RGFW_getTimerValue(void) { - u64 value; - QueryPerformanceCounter((LARGE_INTEGER*)&value); - return value; -} - -void RGFW_sleep(u64 ms) { - Sleep(ms); -} - -#ifndef RGFW_NO_THREADS - -RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args) { return CreateThread(NULL, 0, ptr, args, 0, NULL); } -void RGFW_cancelThread(RGFW_thread thread) { CloseHandle((HANDLE) thread); } -void RGFW_joinThread(RGFW_thread thread) { WaitForSingleObject((HANDLE) thread, INFINITE); } -void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { SetThreadPriority((HANDLE) thread, priority); } - -#endif -#endif /* RGFW_WINDOWS */ - -/* - End of Windows defines -*/ - - - -/* - - Start of MacOS defines - - -*/ - -#if defined(RGFW_MACOS) -/* - based on silicon.h - start of cocoa wrapper -*/ - -#include -#include -#include -#include -#include -#include - -typedef CGRect NSRect; -typedef CGPoint NSPoint; -typedef CGSize NSSize; - -typedef const char* NSPasteboardType; -typedef unsigned long NSUInteger; -typedef long NSInteger; -typedef NSInteger NSModalResponse; - -#ifdef __arm64__ - /* ARM just uses objc_msgSend */ -#define abi_objc_msgSend_stret objc_msgSend -#define abi_objc_msgSend_fpret objc_msgSend -#else /* __i386__ */ - /* x86 just uses abi_objc_msgSend_fpret and (NSColor *)objc_msgSend_id respectively */ -#define abi_objc_msgSend_stret objc_msgSend_stret -#define abi_objc_msgSend_fpret objc_msgSend_fpret -#endif - -#define NSAlloc(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("alloc")) -#define objc_msgSend_bool(x, y) ((BOOL (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y) -#define objc_msgSend_void(x, y) ((void (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y) -#define objc_msgSend_void_id(x, y, z) ((void (*)(id, SEL, id))objc_msgSend) ((id)x, (SEL)y, (id)z) -#define objc_msgSend_uint(x, y) ((NSUInteger (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y) -#define objc_msgSend_void_bool(x, y, z) ((void (*)(id, SEL, BOOL))objc_msgSend) ((id)(x), (SEL)y, (BOOL)z) -#define objc_msgSend_bool_void(x, y) ((BOOL (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y) -#define objc_msgSend_void_SEL(x, y, z) ((void (*)(id, SEL, SEL))objc_msgSend) ((id)(x), (SEL)y, (SEL)z) -#define objc_msgSend_id(x, y) ((id (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y) -#define objc_msgSend_id_id(x, y, z) ((id (*)(id, SEL, id))objc_msgSend) ((id)(x), (SEL)y, (id)z) -#define objc_msgSend_id_bool(x, y, z) ((BOOL (*)(id, SEL, id))objc_msgSend) ((id)(x), (SEL)y, (id)z) -#define objc_msgSend_int(x, y, z) ((id (*)(id, SEL, int))objc_msgSend) ((id)(x), (SEL)y, (int)z) -#define objc_msgSend_arr(x, y, z) ((id (*)(id, SEL, int))objc_msgSend) ((id)(x), (SEL)y, (int)z) -#define objc_msgSend_ptr(x, y, z) ((id (*)(id, SEL, void*))objc_msgSend) ((id)(x), (SEL)y, (void*)z) -#define objc_msgSend_class(x, y) ((id (*)(Class, SEL))objc_msgSend) ((Class)(x), (SEL)y) -#define objc_msgSend_class_char(x, y, z) ((id (*)(Class, SEL, char*))objc_msgSend) ((Class)(x), (SEL)y, (char*)z) - -id NSApp = NULL; - -#define NSRelease(obj) objc_msgSend_void((id)obj, sel_registerName("release")) - -id NSString_stringWithUTF8String(const char* str) { - return ((id(*)(id, SEL, const char*))objc_msgSend) - ((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), str); -} - -const char* NSString_to_char(id str) { - return ((const char* (*)(id, SEL)) objc_msgSend) ((id)(id)str, sel_registerName("UTF8String")); -} - -void si_impl_func_to_SEL_with_name(const char* class_name, const char* register_name, void* function) { - Class selected_class; - - if (RGFW_STRNCMP(class_name, "NSView", 6) == 0) { - selected_class = objc_getClass("ViewClass"); - } else if (RGFW_STRNCMP(class_name, "NSWindow", 8) == 0) { - selected_class = objc_getClass("WindowClass"); - } else { - selected_class = objc_getClass(class_name); - } - - class_addMethod(selected_class, sel_registerName(register_name), (IMP) function, 0); -} - -/* Header for the array. */ -typedef struct siArrayHeader { - size_t count; - /* TODO(EimaMei): Add a `type_width` later on. */ -} siArrayHeader; - -/* Gets the header of the siArray. */ -#define SI_ARRAY_HEADER(s) ((siArrayHeader*)s - 1) -#define si_array_len(array) (SI_ARRAY_HEADER(array)->count) -#define si_func_to_SEL(class_name, function) si_impl_func_to_SEL_with_name(class_name, #function":", (void*)function) -/* Creates an Objective-C method (SEL) from a regular C function with the option to set the register name.*/ -#define si_func_to_SEL_with_name(class_name, register_name, function) si_impl_func_to_SEL_with_name(class_name, register_name":", (void*)function) - -unsigned char* NSBitmapImageRep_bitmapData(id imageRep) { - return ((unsigned char* (*)(id, SEL))objc_msgSend) ((id)imageRep, sel_registerName("bitmapData")); -} - -typedef RGFW_ENUM(NSUInteger, NSBitmapFormat) { - NSBitmapFormatAlphaFirst = 1 << 0, // 0 means is alpha last (RGBA, CMYKA, etc.) - NSBitmapFormatAlphaNonpremultiplied = 1 << 1, // 0 means is premultiplied - NSBitmapFormatFloatingpointSamples = 1 << 2, // 0 is integer - - NSBitmapFormatSixteenBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 8), - NSBitmapFormatThirtyTwoBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 9), - NSBitmapFormatSixteenBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 10), - NSBitmapFormatThirtyTwoBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 11) -}; - -id NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits) { - SEL func = sel_registerName("initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bitmapFormat:bytesPerRow:bitsPerPixel:"); - - return (id) ((id(*)(id, SEL, unsigned char**, NSInteger, NSInteger, NSInteger, NSInteger, bool, bool, id, NSBitmapFormat, NSInteger, NSInteger))objc_msgSend) - (NSAlloc((id)objc_getClass("NSBitmapImageRep")), func, planes, width, height, bps, spp, alpha, isPlanar, NSString_stringWithUTF8String(colorSpaceName), bitmapFormat, rowBytes, pixelBits); -} - -id NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha) { - void* nsclass = objc_getClass("NSColor"); - SEL func = sel_registerName("colorWithSRGBRed:green:blue:alpha:"); - return ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend) - ((id)nsclass, func, red, green, blue, alpha); -} - -id NSCursor_initWithImage(id newImage, NSPoint aPoint) { - SEL func = sel_registerName("initWithImage:hotSpot:"); - void* nsclass = objc_getClass("NSCursor"); - - return (id) ((id(*)(id, SEL, id, NSPoint))objc_msgSend) - (NSAlloc(nsclass), func, newImage, aPoint); -} - -void NSImage_addRepresentation(id image, id imageRep) { - SEL func = sel_registerName("addRepresentation:"); - objc_msgSend_void_id(image, func, (id)imageRep); -} - -id NSImage_initWithSize(NSSize size) { - SEL func = sel_registerName("initWithSize:"); - return ((id(*)(id, SEL, NSSize))objc_msgSend) - (NSAlloc((id)objc_getClass("NSImage")), func, size); -} -#define NS_OPENGL_ENUM_DEPRECATED(minVers, maxVers) API_AVAILABLE(macos(minVers)) -typedef RGFW_ENUM(NSInteger, NSOpenGLContextParameter) { - NSOpenGLContextParameterSwapInterval NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 222, /* 1 param. 0 -> Don't sync, 1 -> Sync to vertical retrace */ - NSOpenGLContextParametectxaceOrder NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 235, /* 1 param. 1 -> Above Window (default), -1 -> Below Window */ - NSOpenGLContextParametectxaceOpacity NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 236, /* 1 param. 1-> Surface is opaque (default), 0 -> non-opaque */ - NSOpenGLContextParametectxaceBackingSize NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 304, /* 2 params. Width/height of surface backing size */ - NSOpenGLContextParameterReclaimResources NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 308, /* 0 params. */ - NSOpenGLContextParameterCurrentRendererID NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 309, /* 1 param. Retrieves the current renderer ID */ - NSOpenGLContextParameterGPUVertexProcessing NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 310, /* 1 param. Currently processing vertices with GPU (get) */ - NSOpenGLContextParameterGPUFragmentProcessing NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 311, /* 1 param. Currently processing fragments with GPU (get) */ - NSOpenGLContextParameterHasDrawable NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 314, /* 1 param. Boolean returned if drawable is attached */ - NSOpenGLContextParameterMPSwapsInFlight NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 315, /* 1 param. Max number of swaps queued by the MP GL engine */ - - NSOpenGLContextParameterSwapRectangle API_DEPRECATED("", macos(10.0, 10.14)) = 200, /* 4 params. Set or get the swap rectangle {x, y, w, h} */ - NSOpenGLContextParameterSwapRectangleEnable API_DEPRECATED("", macos(10.0, 10.14)) = 201, /* Enable or disable the swap rectangle */ - NSOpenGLContextParameterRasterizationEnable API_DEPRECATED("", macos(10.0, 10.14)) = 221, /* Enable or disable all rasterization */ - NSOpenGLContextParameterStateValidation API_DEPRECATED("", macos(10.0, 10.14)) = 301, /* Validate state for multi-screen functionality */ - NSOpenGLContextParametectxaceSurfaceVolatile API_DEPRECATED("", macos(10.0, 10.14)) = 306, /* 1 param. Surface volatile state */ -}; - -typedef RGFW_ENUM(NSInteger, NSWindowButton) { - NSWindowCloseButton = 0, - NSWindowMiniaturizeButton = 1, - NSWindowZoomButton = 2, - NSWindowToolbarButton = 3, - NSWindowDocumentIconButton = 4, - NSWindowDocumentVersionsButton = 6, - NSWindowFullScreenButton = 7, -}; - - -void NSOpenGLContext_setValues(id context, const int* vals, NSOpenGLContextParameter param) { - SEL func = sel_registerName("setValues:forParameter:"); - ((void (*)(id, SEL, const int*, NSOpenGLContextParameter))objc_msgSend) - (context, func, vals, param); -} - -void* NSOpenGLPixelFormat_initWithAttributes(const uint32_t* attribs) { - SEL func = sel_registerName("initWithAttributes:"); - return (void*) ((id(*)(id, SEL, const uint32_t*))objc_msgSend) - (NSAlloc((id)objc_getClass("NSOpenGLPixelFormat")), func, attribs); -} - -id NSOpenGLView_initWithFrame(NSRect frameRect, uint32_t* format) { - SEL func = sel_registerName("initWithFrame:pixelFormat:"); - return (id) ((id(*)(id, SEL, NSRect, uint32_t*))objc_msgSend) - (NSAlloc((id)objc_getClass("NSOpenGLView")), func, frameRect, format); -} - -void NSCursor_performSelector(id cursor, SEL selector) { - SEL func = sel_registerName("performSelector:"); - objc_msgSend_void_SEL(cursor, func, selector); -} - -id NSPasteboard_generalPasteboard(void) { - return (id) objc_msgSend_id((id)objc_getClass("NSPasteboard"), sel_registerName("generalPasteboard")); -} - -id* cstrToNSStringArray(char** strs, size_t len) { - static id nstrs[6]; - size_t i; - for (i = 0; i < len; i++) - nstrs[i] = NSString_stringWithUTF8String(strs[i]); - - return nstrs; -} - -const char* NSPasteboard_stringForType(id pasteboard, NSPasteboardType dataType, size_t* len) { - SEL func = sel_registerName("stringForType:"); - id nsstr = NSString_stringWithUTF8String(dataType); - id nsString = ((id(*)(id, SEL, id))objc_msgSend)(pasteboard, func, nsstr); - const char* str = NSString_to_char(nsString); - if (len != NULL) - *len = (size_t)((NSUInteger(*)(id, SEL, int))objc_msgSend)(nsString, sel_registerName("maximumLengthOfBytesUsingEncoding:"), 4); - return str; -} - -id c_array_to_NSArray(void* array, size_t len) { - SEL func = sel_registerName("initWithObjects:count:"); - void* nsclass = objc_getClass("NSArray"); - return ((id (*)(id, SEL, void*, NSUInteger))objc_msgSend) - (NSAlloc(nsclass), func, array, len); -} - -void NSregisterForDraggedTypes(id view, NSPasteboardType* newTypes, size_t len) { - id* ntypes = cstrToNSStringArray((char**)newTypes, len); - - id array = c_array_to_NSArray(ntypes, len); - objc_msgSend_void_id(view, sel_registerName("registerForDraggedTypes:"), array); - NSRelease(array); -} - -NSInteger NSPasteBoard_declareTypes(id pasteboard, NSPasteboardType* newTypes, size_t len, void* owner) { - id* ntypes = cstrToNSStringArray((char**)newTypes, len); - - SEL func = sel_registerName("declareTypes:owner:"); - - id array = c_array_to_NSArray(ntypes, len); - - NSInteger output = ((NSInteger(*)(id, SEL, id, void*))objc_msgSend) - (pasteboard, func, array, owner); - NSRelease(array); - - return output; -} - -bool NSPasteBoard_setString(id pasteboard, const char* stringToWrite, NSPasteboardType dataType) { - SEL func = sel_registerName("setString:forType:"); - return ((bool (*)(id, SEL, id, id))objc_msgSend) - (pasteboard, func, NSString_stringWithUTF8String(stringToWrite), NSString_stringWithUTF8String(dataType)); -} - -#define NSRetain(obj) objc_msgSend_void((id)obj, sel_registerName("retain")) - -typedef enum NSApplicationActivationPolicy { - NSApplicationActivationPolicyRegular, - NSApplicationActivationPolicyAccessory, - NSApplicationActivationPolicyProhibited -} NSApplicationActivationPolicy; - -typedef RGFW_ENUM(u32, NSBackingStoreType) { - NSBackingStoreRetained = 0, - NSBackingStoreNonretained = 1, - NSBackingStoreBuffered = 2 -}; - -typedef RGFW_ENUM(u32, NSWindowStyleMask) { - NSWindowStyleMaskBorderless = 0, - NSWindowStyleMaskTitled = 1 << 0, - NSWindowStyleMaskClosable = 1 << 1, - NSWindowStyleMaskMiniaturizable = 1 << 2, - NSWindowStyleMaskResizable = 1 << 3, - NSWindowStyleMaskTexturedBackground = 1 << 8, /* deprecated */ - NSWindowStyleMaskUnifiedTitleAndToolbar = 1 << 12, - NSWindowStyleMaskFullScreen = 1 << 14, - NSWindowStyleMaskFullSizeContentView = 1 << 15, - NSWindowStyleMaskUtilityWindow = 1 << 4, - NSWindowStyleMaskDocModalWindow = 1 << 6, - NSWindowStyleMaskNonactivatingpanel = 1 << 7, - NSWindowStyleMaskHUDWindow = 1 << 13 -}; - -NSPasteboardType const NSPasteboardTypeString = "public.utf8-plain-text"; // Replaces NSStringPasteboardType - - -typedef RGFW_ENUM(i32, NSDragOperation) { - NSDragOperationNone = 0, - NSDragOperationCopy = 1, - NSDragOperationLink = 2, - NSDragOperationGeneric = 4, - NSDragOperationPrivate = 8, - NSDragOperationMove = 16, - NSDragOperationDelete = 32, - NSDragOperationEvery = ULONG_MAX, - - //NSDragOperationAll_Obsolete API_DEPRECATED("", macos(10.0,10.10)) = 15, // Use NSDragOperationEvery - //NSDragOperationAll API_DEPRECATED("", macos(10.0,10.10)) = NSDragOperationAll_Obsolete, // Use NSDragOperationEvery -}; - -void* NSArray_objectAtIndex(id array, NSUInteger index) { - SEL func = sel_registerName("objectAtIndex:"); - return ((id(*)(id, SEL, NSUInteger))objc_msgSend)(array, func, index); -} - -id NSWindow_contentView(id window) { - SEL func = sel_registerName("contentView"); - return objc_msgSend_id(window, func); -} - -/* - End of cocoa wrapper -*/ - -#ifdef RGFW_OPENGL -CFBundleRef RGFWnsglFramework = NULL; - -void* RGFW_getProcAddress(const char* procname) { - if (RGFWnsglFramework == NULL) - RGFWnsglFramework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); - - CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, procname, kCFStringEncodingASCII); - - void* symbol = (void*)CFBundleGetFunctionPointerForName(RGFWnsglFramework, symbolName); - - CFRelease(symbolName); - - return symbol; -} -#endif - -id NSWindow_delegate(RGFW_window* win) { - return (id) objc_msgSend_id((id)win->src.window, sel_registerName("delegate")); -} - -u32 RGFW_OnClose(id self) { - RGFW_window* win = NULL; - object_getInstanceVariable(self, (const char*)"RGFW_window", (void**)&win); - if (win == NULL) - return true; - - win->event.type = RGFW_quit; - RGFW_windowQuitCallback(win); - - return false; -} - -/* NOTE(EimaMei): Fixes the constant clicking when the app is running under a terminal. */ -bool acceptsFirstResponder(void) { return true; } -bool performKeyEquivalent(id event) { RGFW_UNUSED(event); return true; } - -NSDragOperation draggingEntered(id self, SEL sel, id sender) { - RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); - - return NSDragOperationCopy; -} -NSDragOperation draggingUpdated(id self, SEL sel, id sender) { - RGFW_UNUSED(sel); - - RGFW_window* win = NULL; - object_getInstanceVariable(self, "RGFW_window", (void**)&win); - if (win == NULL || (!(win->_flags & RGFW_windowAllowDND))) - return 0; - - NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation")); - RGFW_eventQueuePush((RGFW_event){.type = RGFW_DNDInit, - .point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y)), - ._win = win}); - - RGFW_dndInitCallback(win, win->event.point); - return NSDragOperationCopy; -} -bool prepareForDragOperation(id self) { - RGFW_window* win = NULL; - object_getInstanceVariable(self, "RGFW_window", (void**)&win); - if (win == NULL) - return true; - - if (!(win->_flags & RGFW_windowAllowDND)) { - return false; - } - - return true; -} - -void RGFW__osxDraggingEnded(id self, SEL sel, id sender) { RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); return; } - -/* NOTE(EimaMei): Usually, you never need 'id self, SEL cmd' for C -> Obj-C methods. This isn't the case. */ -bool performDragOperation(id self, SEL sel, id sender) { - RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); - - RGFW_window* win = NULL; - object_getInstanceVariable(self, "RGFW_window", (void**)&win); - - if (win == NULL) - return false; - - // id pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard")); - - ///////////////////////////// - id pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard")); - - // Get the types of data available on the pasteboard - id types = objc_msgSend_id(pasteBoard, sel_registerName("types")); - - // Get the string type for file URLs - id fileURLsType = objc_msgSend_class_char(objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "NSFilenamesPboardType"); - - // Check if the pasteboard contains file URLs - if (objc_msgSend_id_bool(types, sel_registerName("containsObject:"), fileURLsType) == 0) { - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errClipboard, RGFW_DEBUG_CTX(win, 0), "No files found on the pasteboard."); - return 0; - } - - id fileURLs = objc_msgSend_id_id(pasteBoard, sel_registerName("propertyListForType:"), fileURLsType); - int count = ((int (*)(id, SEL))objc_msgSend)(fileURLs, sel_registerName("count")); - - if (count == 0) - return 0; - - for (int i = 0; i < count; i++) { - id fileURL = objc_msgSend_arr(fileURLs, sel_registerName("objectAtIndex:"), i); - const char *filePath = ((const char* (*)(id, SEL))objc_msgSend)(fileURL, sel_registerName("UTF8String")); - RGFW_MEMCPY(win->event.droppedFiles[i], filePath, RGFW_MAX_PATH); - win->event.droppedFiles[i][RGFW_MAX_PATH - 1] = '\0'; - } - NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation")); - RGFW_eventQueuePush((RGFW_event){.type = RGFW_DND, - .point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y)), - .droppedFilesCount = count, - ._win = win}); - - RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount); - - return false; -} - -#ifndef RGFW_NO_IOKIT -#include -#include - -IOHIDDeviceRef RGFW_osxControllers[4] = {NULL}; - -int findControllerIndex(IOHIDDeviceRef device) { - for (int i = 0; i < 4; i++) - if (RGFW_osxControllers[i] == device) - return i; - return -1; -} - -void RGFW__osxInputValueChangedCallback(void *context, IOReturn result, void *sender, IOHIDValueRef value) { - RGFW_UNUSED(context); RGFW_UNUSED(result); RGFW_UNUSED(sender); - IOHIDElementRef element = IOHIDValueGetElement(value); - - IOHIDDeviceRef device = IOHIDElementGetDevice(element); - size_t index = findControllerIndex(device); - - uint32_t usagePage = IOHIDElementGetUsagePage(element); - uint32_t usage = IOHIDElementGetUsage(element); - - CFIndex intValue = IOHIDValueGetIntegerValue(value); - - u8 RGFW_osx2RGFWSrc[2][RGFW_gamepadFinal] = {{ - 0, RGFW_gamepadSelect, RGFW_gamepadL3, RGFW_gamepadR3, RGFW_gamepadStart, - RGFW_gamepadUp, RGFW_gamepadRight, RGFW_gamepadDown, RGFW_gamepadLeft, - RGFW_gamepadL2, RGFW_gamepadR2, RGFW_gamepadL1, RGFW_gamepadR1, - RGFW_gamepadY, RGFW_gamepadB, RGFW_gamepadA, RGFW_gamepadX, RGFW_gamepadHome}, - {0, RGFW_gamepadA, RGFW_gamepadB, RGFW_gamepadR3, RGFW_gamepadX, - RGFW_gamepadY, RGFW_gamepadRight, RGFW_gamepadL1, RGFW_gamepadR1, - RGFW_gamepadL2, RGFW_gamepadR2, RGFW_gamepadDown, RGFW_gamepadStart, - RGFW_gamepadUp, RGFW_gamepadL3, RGFW_gamepadSelect, RGFW_gamepadStart, RGFW_gamepadHome} - }; - - u8* RGFW_osx2RGFW = RGFW_osx2RGFWSrc[0]; - if (RGFW_gamepads_type[index] == RGFW_gamepadMicrosoft) - RGFW_osx2RGFW = RGFW_osx2RGFWSrc[1]; - - switch (usagePage) { - case kHIDPage_Button: { - u8 button = 0; - if (usage < sizeof(RGFW_osx2RGFW)) - button = RGFW_osx2RGFW[usage]; - - RGFW_gamepadButtonCallback(RGFW_root, index, button, intValue); - RGFW_gamepadPressed[index][button].prev = RGFW_gamepadPressed[index][button].current; - RGFW_gamepadPressed[index][button].current = intValue; - RGFW_eventQueuePush((RGFW_event){.type = intValue ? RGFW_gamepadButtonPressed: RGFW_gamepadButtonReleased, - .button = button, - .gamepad = index, - ._win = RGFW_root}); - break; - } - case kHIDPage_GenericDesktop: { - CFIndex logicalMin = IOHIDElementGetLogicalMin(element); - CFIndex logicalMax = IOHIDElementGetLogicalMax(element); - - if (logicalMax <= logicalMin) return; - if (intValue < logicalMin) intValue = logicalMin; - if (intValue > logicalMax) intValue = logicalMax; - - i8 value = (i8)(-100.0 + ((intValue - logicalMin) * 200.0) / (logicalMax - logicalMin)); - - u8 whichAxis = 0; - switch (usage) { - case kHIDUsage_GD_X: RGFW_gamepadAxes[index][0].x = value; whichAxis = 0; break; - case kHIDUsage_GD_Y: RGFW_gamepadAxes[index][0].y = value; whichAxis = 0; break; - case kHIDUsage_GD_Z: RGFW_gamepadAxes[index][1].x = value; whichAxis = 1; break; - case kHIDUsage_GD_Rz: RGFW_gamepadAxes[index][1].y = value; whichAxis = 1; break; - default: return; - } - - RGFW_eventQueuePush((RGFW_event){.type = RGFW_gamepadAxisMove, - .gamepad = index, - .axis = {RGFW_gamepadAxes[index][0], RGFW_gamepadAxes[index][1], - RGFW_gamepadAxes[index][2], RGFW_gamepadAxes[index][3]}, - .whichAxis = whichAxis, - ._win = RGFW_root}); - - RGFW_gamepadAxisCallback(RGFW_root, index, RGFW_gamepadAxes[index], 2, whichAxis); - } - } -} - -void RGFW__osxDeviceAddedCallback(void* context, IOReturn result, void *sender, IOHIDDeviceRef device) { - RGFW_UNUSED(context); RGFW_UNUSED(result); RGFW_UNUSED(sender); - CFTypeRef usageRef = (CFTypeRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDPrimaryUsageKey)); - int usage = 0; - if (usageRef) - CFNumberGetValue((CFNumberRef)usageRef, kCFNumberIntType, (void*)&usage); - - if (usage != kHIDUsage_GD_Joystick && usage != kHIDUsage_GD_GamePad && usage != kHIDUsage_GD_MultiAxisController) { - return; - } - - for (size_t i = 0; i < 4; i++) { - if (RGFW_osxControllers[i] != NULL) - continue; - - RGFW_osxControllers[i] = device; - - IOHIDDeviceRegisterInputValueCallback(device, RGFW__osxInputValueChangedCallback, NULL); - - CFStringRef deviceName = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); - if (deviceName) - CFStringGetCString(deviceName, RGFW_gamepads_name[i], sizeof(RGFW_gamepads_name[i]), kCFStringEncodingUTF8); - - RGFW_gamepads_type[i] = RGFW_gamepadUnknown; - if (RGFW_STRSTR(RGFW_gamepads_name[i], "Microsoft") || RGFW_STRSTR(RGFW_gamepads_name[i], "X-Box") || RGFW_STRSTR(RGFW_gamepads_name[i], "Xbox")) - RGFW_gamepads_type[i] = RGFW_gamepadMicrosoft; - else if (RGFW_STRSTR(RGFW_gamepads_name[i], "PlayStation") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS3") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS4") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS5")) - RGFW_gamepads_type[i] = RGFW_gamepadSony; - else if (RGFW_STRSTR(RGFW_gamepads_name[i], "Nintendo")) - RGFW_gamepads_type[i] = RGFW_gamepadNintendo; - else if (RGFW_STRSTR(RGFW_gamepads_name[i], "Logitech")) - RGFW_gamepads_type[i] = RGFW_gamepadLogitech; - - RGFW_gamepads[i] = i; - RGFW_gamepadCount++; - - RGFW_eventQueuePush((RGFW_event){.type = RGFW_gamepadConnected, - .gamepad = i, - ._win = RGFW_root}); - - RGFW_gamepadCallback(RGFW_root, i, 1); - break; - } -} - -void RGFW__osxDeviceRemovedCallback(void *context, IOReturn result, void *sender, IOHIDDeviceRef device) { - RGFW_UNUSED(context); RGFW_UNUSED(result); RGFW_UNUSED(sender); RGFW_UNUSED(device); - CFNumberRef usageRef = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDPrimaryUsageKey)); - int usage = 0; - if (usageRef) - CFNumberGetValue(usageRef, kCFNumberIntType, &usage); - - if (usage != kHIDUsage_GD_Joystick && usage != kHIDUsage_GD_GamePad && usage != kHIDUsage_GD_MultiAxisController) { - return; - } - - i32 index = findControllerIndex(device); - if (index != -1) - RGFW_osxControllers[index] = NULL; - - RGFW_eventQueuePush((RGFW_event){.type = RGFW_gamepadDisconnected, - .gamepad = index, - ._win = RGFW_root}); - RGFW_gamepadCallback(RGFW_root, index, 0); - - RGFW_gamepadCount--; -} - -RGFWDEF void RGFW_osxInitIOKit(void); -void RGFW_osxInitIOKit(void) { - IOHIDManagerRef hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); - if (!hidManager) { - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errIOKit, RGFW_DEBUG_CTX(RGFW_root, 0), "Failed to create IOHIDManager."); - return; - } - - CFMutableDictionaryRef matchingDictionary = CFDictionaryCreateMutable( - kCFAllocatorDefault, - 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks - ); - if (!matchingDictionary) { - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errIOKit, RGFW_DEBUG_CTX(RGFW_root, 0), "Failed to create matching dictionary for IOKit."); - CFRelease(hidManager); - return; - } - - CFDictionarySetValue( - matchingDictionary, - CFSTR(kIOHIDDeviceUsagePageKey), - CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, (int[]){kHIDPage_GenericDesktop}) - ); - - IOHIDManagerSetDeviceMatching(hidManager, matchingDictionary); - - IOHIDManagerRegisterDeviceMatchingCallback(hidManager, RGFW__osxDeviceAddedCallback, NULL); - IOHIDManagerRegisterDeviceRemovalCallback(hidManager, RGFW__osxDeviceRemovedCallback, NULL); - - IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); - - IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone); - - // Execute the run loop once in order to register any initially-attached joysticks - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false); -} -#endif - -void RGFW_moveToMacOSResourceDir(void) { - char resourcesPath[255]; - - CFBundleRef bundle = CFBundleGetMainBundle(); - if (!bundle) - return; - - CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle); - CFStringRef last = CFURLCopyLastPathComponent(resourcesURL); - - if ( - CFStringCompare(CFSTR("Resources"), last, 0) != kCFCompareEqualTo || - CFURLGetFileSystemRepresentation(resourcesURL, true, (u8*) resourcesPath, 255) == 0 - ) { - CFRelease(last); - CFRelease(resourcesURL); - return; - } - - CFRelease(last); - CFRelease(resourcesURL); - - chdir(resourcesPath); -} - - -void RGFW__osxWindowDeminiaturize(id self, SEL sel) { - RGFW_UNUSED(sel); - RGFW_window* win = NULL; - object_getInstanceVariable(self, "RGFW_window", (void**)&win); - if (win == NULL) return; - - win->_flags |= RGFW_windowMinimize; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowRestored, ._win = win}); - RGFW_windowRestoredCallback(win, win->r); - -} -void RGFW__osxWindowMiniaturize(id self, SEL sel) { - RGFW_UNUSED(sel); - RGFW_window* win = NULL; - object_getInstanceVariable(self, "RGFW_window", (void**)&win); - if (win == NULL) return; - - win->_flags &= ~RGFW_windowMinimize; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowMinimized, ._win = win}); - RGFW_windowMinimizedCallback(win, win->r); - -} - -void RGFW__osxWindowBecameKey(id self, SEL sel) { - RGFW_UNUSED(sel); - RGFW_window* win = NULL; - object_getInstanceVariable(self, "RGFW_window", (void**)&win); - if (win == NULL) return; - - win->_flags |= RGFW_windowFocus; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_focusIn, ._win = win}); - RGFW_focusCallback(win, RGFW_TRUE); -} - -void RGFW__osxWindowResignKey(id self, SEL sel) { - RGFW_UNUSED(sel); - RGFW_window* win = NULL; - object_getInstanceVariable(self, "RGFW_window", (void**)&win); - if (win == NULL) return; - - win->_flags &= ~RGFW_windowFocus; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_focusOut, ._win = win}); - RGFW_focusCallback(win, RGFW_FALSE); -} - -NSSize RGFW__osxWindowResize(id self, SEL sel, NSSize frameSize) { - RGFW_UNUSED(sel); - - RGFW_window* win = NULL; - object_getInstanceVariable(self, "RGFW_window", (void**)&win); - if (win == NULL) return frameSize; - - win->r.w = frameSize.width; - win->r.h = frameSize.height; - - RGFW_monitor mon = RGFW_window_getMonitor(win); - if ((i32)mon.mode.area.w == win->r.w && (i32)mon.mode.area.h - 102 <= win->r.h) { - win->_flags |= RGFW_windowMaximize; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowMaximized, ._win = win}); - RGFW_windowMaximizedCallback(win, win->r); - } else if (win->_flags & RGFW_windowMaximize) { - win->_flags &= ~RGFW_windowMaximize; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowRestored, ._win = win}); - RGFW_windowRestoredCallback(win, win->r); - - } - - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowResized, ._win = win}); - RGFW_windowResizeCallback(win, win->r); - return frameSize; -} - -void RGFW__osxWindowMove(id self, SEL sel) { - RGFW_UNUSED(sel); - - RGFW_window* win = NULL; - object_getInstanceVariable(self, "RGFW_window", (void**)&win); - if (win == NULL) return; - - NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame")); - win->r.x = (i32) frame.origin.x; - win->r.y = (i32) frame.origin.y; - - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowMoved, ._win = win}); - RGFW_windowMoveCallback(win, win->r); -} - -void RGFW__osxUpdateLayer(id self, SEL sel) { - RGFW_UNUSED(sel); - - RGFW_window* win = NULL; - object_getInstanceVariable(self, "RGFW_window", (void**)&win); - if (win == NULL) - return; - - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowRefresh, ._win = win}); - RGFW_windowRefreshCallback(win); -} - -void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area) { - #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - win->buffer = buffer; - win->bufferSize = area; - win->_flags |= RGFW_BUFFER_ALLOC; - #ifdef 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); RGFW_UNUSED(buffer); RGFW_UNUSED(area); /*!< if buffer rendering is not being used */ - #endif -} - -void RGFW_window_cocoaSetLayer(RGFW_window* win, void* layer) { - objc_msgSend_void_id((id)win->src.view, sel_registerName("setLayer"), (id)layer); -} - -void* RGFW_cocoaGetLayer(void) { - return objc_msgSend_class((id)objc_getClass("CAMetalLayer"), (SEL)sel_registerName("layer")); -} - - -NSPasteboardType const NSPasteboardTypeURL = "public.url"; -NSPasteboardType const NSPasteboardTypeFileURL = "public.file-url"; - -RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) { - static u8 RGFW_loaded = 0; - - /* NOTE(EimaMei): Why does Apple hate good code? Like wtf, who thought of methods being a great idea??? - Imagine a universe, where MacOS had a proper system API (we would probably have like 20% better performance). - */ - si_func_to_SEL_with_name("NSObject", "windowShouldClose", (void*)RGFW_OnClose); - - /* NOTE(EimaMei): Fixes the 'Boop' sfx from constantly playing each time you click a key. Only a problem when running in the terminal. */ - si_func_to_SEL("NSWindow", acceptsFirstResponder); - si_func_to_SEL("NSWindow", performKeyEquivalent); - - // RR Create an autorelease pool - id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); - pool = objc_msgSend_id(pool, sel_registerName("init")); - - if (NSApp == NULL) { - NSApp = objc_msgSend_id((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication")); - - ((void (*)(id, SEL, NSUInteger))objc_msgSend) - (NSApp, sel_registerName("setActivationPolicy:"), NSApplicationActivationPolicyRegular); - - #ifndef RGFW_NO_IOKIT - RGFW_osxInitIOKit(); - #endif - } - - RGFW_window_basic_init(win, rect, flags); - - RGFW_window_setMouseDefault(win); - - NSRect windowRect; - windowRect.origin.x = win->r.x; - windowRect.origin.y = win->r.y; - windowRect.size.width = win->r.w; - windowRect.size.height = win->r.h; - - NSBackingStoreType macArgs = NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSBackingStoreBuffered | NSWindowStyleMaskTitled; - - if (!(flags & RGFW_windowNoResize)) - macArgs |= NSWindowStyleMaskResizable; - if (!(flags & RGFW_windowNoBorder)) - macArgs |= NSWindowStyleMaskTitled; - { - void* nsclass = objc_getClass("NSWindow"); - SEL func = sel_registerName("initWithContentRect:styleMask:backing:defer:"); - - win->src.window = ((id(*)(id, SEL, NSRect, NSWindowStyleMask, NSBackingStoreType, bool))objc_msgSend) - (NSAlloc(nsclass), func, windowRect, macArgs, macArgs, false); - } - - id str = NSString_stringWithUTF8String(name); - objc_msgSend_void_id((id)win->src.window, sel_registerName("setTitle:"), str); - - #ifdef RGFW_EGL - if ((flags & RGFW_windowNoInitAPI) == 0) - RGFW_createOpenGLContext(win); - #endif - - #ifdef RGFW_OPENGL - - if ((flags & RGFW_windowNoInitAPI) == 0) { - void* attrs = RGFW_initFormatAttribs(flags & RGFW_windowOpenglSoftware); - void* format = NSOpenGLPixelFormat_initWithAttributes((uint32_t*)attrs); - - if (format == NULL) { - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to load pixel format for OpenGL"); - void* attrs = RGFW_initFormatAttribs(1); - format = NSOpenGLPixelFormat_initWithAttributes((uint32_t*)attrs); - - if (format == NULL) - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "and loading software rendering OpenGL failed"); - else - RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, RGFW_DEBUG_CTX(win, 0), "Switching to software rendering"); - } - - /* 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}}, (uint32_t*)format); - objc_msgSend_void(win->src.view, sel_registerName("prepareOpenGL")); - win->src.ctx = objc_msgSend_id(win->src.view, sel_registerName("openGLContext")); - } else - #endif - { - NSRect contentRect = (NSRect){{0, 0}, {win->r.w, win->r.h}}; - win->src.view = ((id(*)(id, SEL, NSRect))objc_msgSend) - (NSAlloc((id)objc_getClass("NSView")), sel_registerName("initWithFrame:"), - contentRect); - } - - void* contentView = NSWindow_contentView((id)win->src.window); - objc_msgSend_void_bool(contentView, sel_registerName("setWantsLayer:"), true); - - objc_msgSend_void_id((id)win->src.window, sel_registerName("setContentView:"), win->src.view); - - #ifdef RGFW_OPENGL - if ((flags & RGFW_windowNoInitAPI) == 0) - objc_msgSend_void(win->src.ctx, sel_registerName("makeCurrentContext")); - #endif - - if (flags & RGFW_windowTransparent) { - #ifdef RGFW_OPENGL - if ((flags & RGFW_windowNoInitAPI) == 0) { - i32 opacity = 0; - #define NSOpenGLCPSurfaceOpacity 236 - NSOpenGLContext_setValues((id)win->src.ctx, &opacity, NSOpenGLCPSurfaceOpacity); - } - #endif - - objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), false); - - objc_msgSend_void_id((id)win->src.window, sel_registerName("setBackgroundColor:"), - NSColor_colorWithSRGB(0, 0, 0, 0)); - } - - Class delegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "WindowDelegate", 0); - - class_addIvar( - delegateClass, "RGFW_window", - sizeof(RGFW_window*), rint(log2(sizeof(RGFW_window*))), - "L" - ); - - - class_addMethod(delegateClass, sel_registerName("windowWillResize:toSize:"), (IMP) RGFW__osxWindowResize, "{NSSize=ff}@:{NSSize=ff}"); - class_addMethod(delegateClass, sel_registerName("updateLayer:"), (IMP) RGFW__osxUpdateLayer, ""); - class_addMethod(delegateClass, sel_registerName("windowWillMove:"), (IMP) RGFW__osxWindowMove, ""); - class_addMethod(delegateClass, sel_registerName("windowDidMove:"), (IMP) RGFW__osxWindowMove, ""); - class_addMethod(delegateClass, sel_registerName("windowDidMiniaturize:"), (IMP) RGFW__osxWindowMiniaturize, ""); - class_addMethod(delegateClass, sel_registerName("windowDidDeminiaturize:"), (IMP) RGFW__osxWindowDeminiaturize, ""); - class_addMethod(delegateClass, sel_registerName("windowDidBecomeKey:"), (IMP) RGFW__osxWindowBecameKey, ""); - class_addMethod(delegateClass, sel_registerName("windowDidResignKey:"), (IMP) RGFW__osxWindowResignKey, ""); - class_addMethod(delegateClass, sel_registerName("draggingEntered:"), (IMP)draggingEntered, "l@:@"); - class_addMethod(delegateClass, sel_registerName("draggingUpdated:"), (IMP)draggingUpdated, "l@:@"); - class_addMethod(delegateClass, sel_registerName("draggingExited:"), (IMP)RGFW__osxDraggingEnded, "v@:@"); - class_addMethod(delegateClass, sel_registerName("draggingEnded:"), (IMP)RGFW__osxDraggingEnded, "v@:@"); - class_addMethod(delegateClass, sel_registerName("prepareForDragOperation:"), (IMP)prepareForDragOperation, "B@:@"); - class_addMethod(delegateClass, sel_registerName("performDragOperation:"), (IMP)performDragOperation, "B@:@"); - - id delegate = objc_msgSend_id(NSAlloc(delegateClass), sel_registerName("init")); - - if (RGFW_COCOA_FRAME_NAME) - objc_msgSend_ptr(win->src.view, sel_registerName("setFrameAutosaveName:"), RGFW_COCOA_FRAME_NAME); - - object_setInstanceVariable(delegate, "RGFW_window", win); - - objc_msgSend_void_id((id)win->src.window, sel_registerName("setDelegate:"), delegate); - - if (flags & RGFW_windowAllowDND) { - win->_flags |= RGFW_windowAllowDND; - - NSPasteboardType types[] = {NSPasteboardTypeURL, NSPasteboardTypeFileURL, NSPasteboardTypeString}; - NSregisterForDraggedTypes((id)win->src.window, types, 3); - } - - // Show the window - objc_msgSend_void_bool(NSApp, sel_registerName("activateIgnoringOtherApps:"), true); - ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("makeKeyAndOrderFront:"), (SEL)NULL); - objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true); - - if (!RGFW_loaded) { - objc_msgSend_void(win->src.window, sel_registerName("makeMainWindow")); - - RGFW_loaded = 1; - } - - objc_msgSend_void(win->src.window, sel_registerName("makeKeyWindow")); - - objc_msgSend_void(NSApp, sel_registerName("finishLaunching")); - - RGFW_window_setFlags(win, flags); - - NSRetain(win->src.window); - NSRetain(NSApp); - - RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new window was created"); - return win; -} - -void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { - NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame")); - NSRect content = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame")); - float offset = 0; - - RGFW_setBit(&win->_flags, RGFW_windowNoBorder, !border); - NSBackingStoreType storeType = NSWindowStyleMaskBorderless | NSWindowStyleMaskFullSizeContentView; - if (border) - storeType = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable; - if (!(win->_flags & RGFW_windowNoResize)) { - storeType |= NSWindowStyleMaskResizable; - } - - ((void (*)(id, SEL, NSBackingStoreType))objc_msgSend)((id)win->src.window, sel_registerName("setStyleMask:"), storeType); - - if (!border) { - id miniaturizeButton = objc_msgSend_int((id)win->src.window, sel_registerName("standardWindowButton:"), NSWindowMiniaturizeButton); - id titleBarView = objc_msgSend_id(miniaturizeButton, sel_registerName("superview")); - objc_msgSend_void_bool(titleBarView, sel_registerName("setHidden:"), true); - - offset = frame.size.height - content.size.height; - } - - RGFW_window_resize(win, RGFW_AREA(win->r.w, win->r.h + offset)); - win->r.h -= offset; -} - -RGFW_area RGFW_getScreenSize(void) { - static CGDirectDisplayID display = 0; - - if (display == 0) - display = CGMainDisplayID(); - - return RGFW_AREA(CGDisplayPixelsWide(display), CGDisplayPixelsHigh(display)); -} - -RGFW_point RGFW_getGlobalMousePoint(void) { - RGFW_ASSERT(RGFW_root != NULL); - - CGEventRef e = CGEventCreate(NULL); - CGPoint point = CGEventGetLocation(e); - CFRelease(e); - - return RGFW_POINT((u32) point.x, (u32) point.y); /*!< the point is loaded during event checks */ -} - -typedef RGFW_ENUM(u32, NSEventType) { /* various types of events */ - NSEventTypeLeftMouseDown = 1, - NSEventTypeLeftMouseUp = 2, - NSEventTypeRightMouseDown = 3, - NSEventTypeRightMouseUp = 4, - NSEventTypeMouseMoved = 5, - NSEventTypeLeftMouseDragged = 6, - NSEventTypeRightMouseDragged = 7, - NSEventTypeMouseEntered = 8, - NSEventTypeMouseExited = 9, - NSEventTypeKeyDown = 10, - NSEventTypeKeyUp = 11, - NSEventTypeFlagsChanged = 12, - NSEventTypeAppKitDefined = 13, - NSEventTypeSystemDefined = 14, - NSEventTypeApplicationDefined = 15, - NSEventTypePeriodic = 16, - NSEventTypeCursorUpdate = 17, - NSEventTypeScrollWheel = 22, - NSEventTypeTabletPoint = 23, - NSEventTypeTabletProximity = 24, - NSEventTypeOtherMouseDown = 25, - NSEventTypeOtherMouseUp = 26, - NSEventTypeOtherMouseDragged = 27, - /* The following event types are available on some hardware on 10.5.2 and later */ - NSEventTypeGesture API_AVAILABLE(macos(10.5)) = 29, - NSEventTypeMagnify API_AVAILABLE(macos(10.5)) = 30, - NSEventTypeSwipe API_AVAILABLE(macos(10.5)) = 31, - NSEventTypeRotate API_AVAILABLE(macos(10.5)) = 18, - NSEventTypeBeginGesture API_AVAILABLE(macos(10.5)) = 19, - NSEventTypeEndGesture API_AVAILABLE(macos(10.5)) = 20, - - NSEventTypeSmartMagnify API_AVAILABLE(macos(10.8)) = 32, - NSEventTypeQuickLook API_AVAILABLE(macos(10.8)) = 33, - - NSEventTypePressure API_AVAILABLE(macos(10.10.3)) = 34, - NSEventTypeDirectTouch API_AVAILABLE(macos(10.10)) = 37, - - NSEventTypeChangeMode API_AVAILABLE(macos(10.15)) = 38, -}; - -typedef RGFW_ENUM(unsigned long long, NSEventMask) { /* masks for the types of events */ - NSEventMaskLeftMouseDown = 1ULL << NSEventTypeLeftMouseDown, - NSEventMaskLeftMouseUp = 1ULL << NSEventTypeLeftMouseUp, - NSEventMaskRightMouseDown = 1ULL << NSEventTypeRightMouseDown, - NSEventMaskRightMouseUp = 1ULL << NSEventTypeRightMouseUp, - NSEventMaskMouseMoved = 1ULL << NSEventTypeMouseMoved, - NSEventMaskLeftMouseDragged = 1ULL << NSEventTypeLeftMouseDragged, - NSEventMaskRightMouseDragged = 1ULL << NSEventTypeRightMouseDragged, - NSEventMaskMouseEntered = 1ULL << NSEventTypeMouseEntered, - NSEventMaskMouseExited = 1ULL << NSEventTypeMouseExited, - NSEventMaskKeyDown = 1ULL << NSEventTypeKeyDown, - NSEventMaskKeyUp = 1ULL << NSEventTypeKeyUp, - NSEventMaskFlagsChanged = 1ULL << NSEventTypeFlagsChanged, - NSEventMaskAppKitDefined = 1ULL << NSEventTypeAppKitDefined, - NSEventMaskSystemDefined = 1ULL << NSEventTypeSystemDefined, - NSEventMaskApplicationDefined = 1ULL << NSEventTypeApplicationDefined, - NSEventMaskPeriodic = 1ULL << NSEventTypePeriodic, - NSEventMaskCursorUpdate = 1ULL << NSEventTypeCursorUpdate, - NSEventMaskScrollWheel = 1ULL << NSEventTypeScrollWheel, - NSEventMaskTabletPoint = 1ULL << NSEventTypeTabletPoint, - NSEventMaskTabletProximity = 1ULL << NSEventTypeTabletProximity, - NSEventMaskOtherMouseDown = 1ULL << NSEventTypeOtherMouseDown, - NSEventMaskOtherMouseUp = 1ULL << NSEventTypeOtherMouseUp, - NSEventMaskOtherMouseDragged = 1ULL << NSEventTypeOtherMouseDragged, - /* The following event masks are available on some hardware on 10.5.2 and later */ - NSEventMaskGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeGesture, - NSEventMaskMagnify API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeMagnify, - NSEventMaskSwipe API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeSwipe, - NSEventMaskRotate API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeRotate, - NSEventMaskBeginGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeBeginGesture, - NSEventMaskEndGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeEndGesture, - - /* Note: You can only use these event masks on 64 bit. In other words, you cannot setup a local, nor global, event monitor for these event types on 32 bit. Also, you cannot search the event queue for them (nextEventMatchingMask:...) on 32 bit. - */ - NSEventMaskSmartMagnify API_AVAILABLE(macos(10.8)) = 1ULL << NSEventTypeSmartMagnify, - NSEventMaskPressure API_AVAILABLE(macos(10.10.3)) = 1ULL << NSEventTypePressure, - NSEventMaskDirectTouch API_AVAILABLE(macos(10.12.2)) = 1ULL << NSEventTypeDirectTouch, - - NSEventMaskChangeMode API_AVAILABLE(macos(10.15)) = 1ULL << NSEventTypeChangeMode, - - NSEventMaskAny = ULONG_MAX, - -}; - -typedef enum NSEventModifierFlags { - NSEventModifierFlagCapsLock = 1 << 16, - NSEventModifierFlagShift = 1 << 17, - NSEventModifierFlagControl = 1 << 18, - NSEventModifierFlagOption = 1 << 19, - NSEventModifierFlagCommand = 1 << 20, - NSEventModifierFlagNumericPad = 1 << 21 -} NSEventModifierFlags; - -void RGFW_stopCheckEvents(void) { - id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); - eventPool = objc_msgSend_id(eventPool, sel_registerName("init")); - - id e = (id) ((id(*)(id, SEL, NSEventType, NSPoint, NSEventModifierFlags, void*, NSInteger, void**, short, NSInteger, NSInteger))objc_msgSend) - (NSApp, sel_registerName("otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:"), - NSEventTypeApplicationDefined, (NSPoint){0, 0}, (NSEventModifierFlags)0, NULL, (NSInteger)0, NULL, 0, 0, 0); - - ((void (*)(id, SEL, id, bool))objc_msgSend) - (NSApp, sel_registerName("postEvent:atStart:"), e, 1); - - objc_msgSend_bool_void(eventPool, sel_registerName("drain")); -} - -void RGFW_window_eventWait(RGFW_window* win, u32 waitMS) { - RGFW_UNUSED(win); - - id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); - eventPool = objc_msgSend_id(eventPool, sel_registerName("init")); - - void* date = (void*) ((id(*)(Class, SEL, double))objc_msgSend) - (objc_getClass("NSDate"), sel_registerName("dateWithTimeIntervalSinceNow:"), waitMS); - - id e = (id) ((id(*)(id, SEL, NSEventMask, void*, id, bool))objc_msgSend) - (NSApp, sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"), - ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true); - - - if (e) { - ((void (*)(id, SEL, id, bool))objc_msgSend) - (NSApp, sel_registerName("postEvent:atStart:"), e, 1); - } - - objc_msgSend_bool_void(eventPool, sel_registerName("drain")); -} - -RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { - RGFW_event* ev = RGFW_window_checkEventCore(win); - if (ev) { - if (ev == (RGFW_event*)-1) return NULL; - ((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows")); - return ev; - } - - id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); - eventPool = objc_msgSend_id(eventPool, sel_registerName("init")); - - static SEL eventFunc = (SEL)NULL; - if (eventFunc == NULL) - eventFunc = sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"); - - void* date = NULL; - - id e = (id) ((id(*)(id, SEL, NSEventMask, void*, id, bool))objc_msgSend) - (NSApp, eventFunc, ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true); - - if (e == NULL) { - objc_msgSend_bool_void(eventPool, sel_registerName("drain")); - objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e); - ((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows")); - return NULL; - } - - if (objc_msgSend_id(e, sel_registerName("window")) != win->src.window) { - ((void (*)(id, SEL, id, bool))objc_msgSend) - (NSApp, sel_registerName("postEvent:atStart:"), e, 0); - - objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e); - objc_msgSend_bool_void(eventPool, sel_registerName("drain")); - ((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows")); - return NULL; - } - - if (win->event.droppedFilesCount) { - u32 i; - for (i = 0; i < win->event.droppedFilesCount; i++) - win->event.droppedFiles[i][0] = '\0'; - } - - win->event.droppedFilesCount = 0; - win->event.type = 0; - - u32 type = objc_msgSend_uint(e, sel_registerName("type")); - switch (type) { - case NSEventTypeMouseEntered: { - win->event.type = RGFW_mouseEnter; - NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow")); - - win->event.point = RGFW_POINT((i32) p.x, (i32) (win->r.h - p.y)); - RGFW_mouseNotifyCallBack(win, win->event.point, 1); - break; - } - - case NSEventTypeMouseExited: - win->event.type = RGFW_mouseLeave; - RGFW_mouseNotifyCallBack(win, win->event.point, 0); - break; - - case NSEventTypeKeyDown: { - u32 key = (u16) objc_msgSend_uint(e, sel_registerName("keyCode")); - - u32 mappedKey = *((u32*)((char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("charactersIgnoringModifiers"))))); - if (((u8)mappedKey) == 239) - mappedKey = 0; - - win->event.keyChar = (u8)mappedKey; - - win->event.key = RGFW_apiKeyToRGFW(key); - RGFW_keyboard[win->event.key].prev = RGFW_keyboard[win->event.key].current; - - win->event.type = RGFW_keyPressed; - win->event.repeat = RGFW_isPressed(win, win->event.key); - RGFW_keyboard[win->event.key].current = 1; - - RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, 1); - break; - } - - case NSEventTypeKeyUp: { - u32 key = (u16) objc_msgSend_uint(e, sel_registerName("keyCode")); - - u32 mappedKey = *((u32*)((char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("charactersIgnoringModifiers"))))); - if (((u8)mappedKey) == 239) - mappedKey = 0; - - win->event.keyChar = (u8)mappedKey; - - win->event.key = RGFW_apiKeyToRGFW(key); - - RGFW_keyboard[win->event.key].prev = RGFW_keyboard[win->event.key].current; - - win->event.type = RGFW_keyReleased; - - RGFW_keyboard[win->event.key].current = 0; - RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, 0); - break; - } - - case NSEventTypeFlagsChanged: { - u32 flags = objc_msgSend_uint(e, sel_registerName("modifierFlags")); - RGFW_updateKeyModsPro(win, ((u32)(flags & NSEventModifierFlagCapsLock) % 255), ((flags & NSEventModifierFlagNumericPad) % 255), - ((flags & NSEventModifierFlagControl) % 255), ((flags & NSEventModifierFlagOption) % 255), - ((flags & NSEventModifierFlagShift) % 255), ((flags & NSEventModifierFlagCommand) % 255), 0); - u8 i; - for (i = 0; i < 9; i++) - RGFW_keyboard[i + RGFW_capsLock].prev = 0; - - for (i = 0; i < 5; i++) { - u32 shift = (1 << (i + 16)); - u32 key = i + RGFW_capsLock; - - if ((flags & shift) && !RGFW_wasPressed(win, key)) { - RGFW_keyboard[key].current = 1; - - if (key != RGFW_capsLock) - RGFW_keyboard[key+ 4].current = 1; - - win->event.type = RGFW_keyPressed; - win->event.key = key; - break; - } - - if (!(flags & shift) && RGFW_wasPressed(win, key)) { - RGFW_keyboard[key].current = 0; - - if (key != RGFW_capsLock) - RGFW_keyboard[key + 4].current = 0; - - win->event.type = RGFW_keyReleased; - win->event.key = key; - break; - } - } - - RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, win->event.type == RGFW_keyPressed); - - break; - } - case NSEventTypeLeftMouseDragged: - case NSEventTypeOtherMouseDragged: - case NSEventTypeRightMouseDragged: - case NSEventTypeMouseMoved: { - win->event.type = RGFW_mousePosChanged; - 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)); - - 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.vector = RGFW_POINT((i32)p.x, (i32)p.y); - - win->_lastMousePoint = win->event.point; - RGFW_mousePosCallback(win, win->event.point, win->event.vector); - break; - } - case NSEventTypeLeftMouseDown: case NSEventTypeRightMouseDown: case NSEventTypeOtherMouseDown: { - u32 buttonNumber = objc_msgSend_uint(e, sel_registerName("buttonNumber")); - switch (buttonNumber) { - case 0: win->event.button = RGFW_mouseLeft; break; - case 1: win->event.button = RGFW_mouseRight; break; - case 2: win->event.button = RGFW_mouseMiddle; break; - default: win->event.button = buttonNumber; - } - - win->event.type = RGFW_mouseButtonPressed; - RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; - RGFW_mouseButtons[win->event.button].current = 1; - RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); - break; - } - case NSEventTypeLeftMouseUp: case NSEventTypeRightMouseUp: case NSEventTypeOtherMouseUp: { - u32 buttonNumber = objc_msgSend_uint(e, sel_registerName("buttonNumber")); - switch (buttonNumber) { - case 0: win->event.button = RGFW_mouseLeft; break; - case 1: win->event.button = RGFW_mouseRight; break; - case 2: win->event.button = RGFW_mouseMiddle; break; - default: win->event.button = buttonNumber; - } - RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; - RGFW_mouseButtons[win->event.button].current = 0; - win->event.type = RGFW_mouseButtonReleased; - RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0); - break; - } - case NSEventTypeScrollWheel: { - double deltaY = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaY")); - - if (deltaY > 0) { - win->event.button = RGFW_mouseScrollUp; - } - else if (deltaY < 0) { - win->event.button = RGFW_mouseScrollDown; - } - - RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; - RGFW_mouseButtons[win->event.button].current = 1; - - win->event.scroll = deltaY; - - win->event.type = RGFW_mouseButtonPressed; - RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); - break; - } - - default: - objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e); - ((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows")); - return RGFW_window_checkEvent(win); - } - - objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e); - ((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows")); - objc_msgSend_bool_void(eventPool, sel_registerName("drain")); - return &win->event; -} - - -void RGFW_window_move(RGFW_window* win, RGFW_point v) { - RGFW_ASSERT(win != NULL); - - win->r.x = v.x; - win->r.y = v.y; - ((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend) - ((id)win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{win->r.x, win->r.y}, {win->r.w, win->r.h}}, true, true); -} - -void RGFW_window_resize(RGFW_window* win, RGFW_area a) { - RGFW_ASSERT(win != NULL); - - NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame")); - NSRect content = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame")); - float offset = frame.size.height - content.size.height; - - win->r.w = a.w; - win->r.h = a.h; - - ((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend) - ((id)win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{win->r.x, win->r.y}, {win->r.w, win->r.h + offset}}, true, true); -} - -void RGFW_window_focus(RGFW_window* win) { - RGFW_ASSERT(win); - objc_msgSend_void_bool(NSApp, sel_registerName("activateIgnoringOtherApps:"), true); - ((void (*)(id, SEL))objc_msgSend)((id)win->src.window, sel_registerName("makeKeyWindow")); -} - -void RGFW_window_raise(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("orderFront:"), (SEL)NULL); - objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGNormalWindowLevelKey); -} - -void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) { - RGFW_ASSERT(win != NULL); - if (fullscreen && (win->_flags & RGFW_windowFullscreen)) return; - if (!fullscreen && !(win->_flags & RGFW_windowFullscreen)) return; - - if (fullscreen) { - win->_oldRect = win->r; - RGFW_monitor mon = RGFW_window_getMonitor(win); - win->r = RGFW_RECT(0, 0, mon.x, mon.y); - win->_flags |= RGFW_windowFullscreen; - RGFW_window_resize(win, RGFW_AREA(mon.mode.area.w, mon.mode.area.h)); - RGFW_window_move(win, RGFW_POINT(0, 0)); - } - objc_msgSend_void_SEL(win->src.window, sel_registerName("toggleFullScreen:"), NULL); - - if (!fullscreen) { - win->r = win->_oldRect; - win->_flags &= ~RGFW_windowFullscreen; - - RGFW_window_resize(win, RGFW_AREA(win->r.w, win->r.h)); - RGFW_window_move(win, RGFW_POINT(win->r.x, win->r.y)); - } -} - -void RGFW_window_maximize(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - if (RGFW_window_isMaximized(win)) return; - - win->_flags |= RGFW_windowMaximize; - objc_msgSend_void_SEL(win->src.window, sel_registerName("zoom:"), NULL); -} - -void RGFW_window_minimize(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - objc_msgSend_void_SEL(win->src.window, sel_registerName("performMiniaturize:"), NULL); -} - -void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) { - RGFW_ASSERT(win != NULL); - if (floating) objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGFloatingWindowLevelKey); - else objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGNormalWindowLevelKey); -} - -void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) { - objc_msgSend_int(win->src.window, sel_registerName("setAlphaValue:"), opacity); - objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), (opacity < (u8)255)); - - if (opacity) - objc_msgSend_void_id((id)win->src.window, sel_registerName("setBackgroundColor:"), NSColor_colorWithSRGB(0, 0, 0, opacity)); - -} - -void RGFW_window_restore(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - - if (RGFW_window_isMaximized(win)) - objc_msgSend_void_SEL(win->src.window, sel_registerName("zoom:"), NULL); - - objc_msgSend_void_SEL(win->src.window, sel_registerName("deminiaturize:"), NULL); - RGFW_window_show(win); -} - -RGFW_bool RGFW_window_isFloating(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - int level = ((int (*)(id, SEL))objc_msgSend) ((id)(win->src.window), (SEL)sel_registerName("level")); - return level > kCGNormalWindowLevelKey; -} - -void RGFW_window_setName(RGFW_window* win, const char* name) { - RGFW_ASSERT(win != NULL); - - id str = NSString_stringWithUTF8String(name); - objc_msgSend_void_id((id)win->src.window, sel_registerName("setTitle:"), str); -} - -#ifndef RGFW_NO_PASSTHROUGH -void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) { - objc_msgSend_void_bool(win->src.window, sel_registerName("setIgnoresMouseEvents:"), passthrough); -} -#endif - -void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a) { - if (a.w == 0 && a.h == 0) a = RGFW_AREA(1, 1); - - ((void (*)(id, SEL, NSSize))objc_msgSend) - ((id)win->src.window, sel_registerName("setContentAspectRatio:"), (NSSize){a.w, a.h}); -} - -void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { - ((void (*)(id, SEL, NSSize))objc_msgSend) - ((id)win->src.window, sel_registerName("setMinSize:"), (NSSize){a.w, a.h}); -} - -void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { - if (a.w == 0 && a.h == 0) { - a = RGFW_getScreenSize(); - } - - ((void (*)(id, SEL, NSSize))objc_msgSend) - ((id)win->src.window, sel_registerName("setMaxSize:"), (NSSize){a.w, a.h}); -} - -RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, RGFW_area area, i32 channels, u8 type) { - RGFW_ASSERT(win != NULL); - RGFW_UNUSED(type); - - if (data == NULL) { - objc_msgSend_void_id(NSApp, sel_registerName("setApplicationIconImage:"), NULL); - return RGFW_TRUE; - } - - /* code by EimaMei */ - // Make a bitmap representation, then copy the loaded image into it. - id representation = NSBitmapImageRep_initWithBitmapData(NULL, area.w, area.h, 8, channels, (channels == 4), false, "NSCalibratedRGBColorSpace", 1 << 1, area.w * channels, 8 * channels); - RGFW_MEMCPY(NSBitmapImageRep_bitmapData(representation), data, area.w * area.h * channels); - - // Add ze representation. - id dock_image = NSImage_initWithSize((NSSize){area.w, area.h}); - NSImage_addRepresentation(dock_image, representation); - - // Finally, set the dock image to it. - objc_msgSend_void_id(NSApp, sel_registerName("setApplicationIconImage:"), dock_image); - // Free the garbage. - NSRelease(dock_image); - NSRelease(representation); - - return RGFW_TRUE; -} - -id NSCursor_arrowStr(const char* str) { - void* nclass = objc_getClass("NSCursor"); - SEL func = sel_registerName(str); - return (id) objc_msgSend_id(nclass, func); -} - -RGFW_mouse* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels) { - if (icon == NULL) { - objc_msgSend_void(NSCursor_arrowStr("arrowCursor"), sel_registerName("set")); - return NULL; - } - - /* NOTE(EimaMei): Code by yours truly. */ - // Make a bitmap representation, then copy the loaded image into it. - id representation = NSBitmapImageRep_initWithBitmapData(NULL, a.w, a.h, 8, channels, (channels == 4), false, "NSCalibratedRGBColorSpace", 1 << 1, a.w * channels, 8 * channels); - RGFW_MEMCPY(NSBitmapImageRep_bitmapData(representation), icon, a.w * a.h * channels); - - // Add ze representation. - id cursor_image = NSImage_initWithSize((NSSize){a.w, a.h}); - NSImage_addRepresentation(cursor_image, representation); - - // Finally, set the cursor image. - id cursor = NSCursor_initWithImage(cursor_image, (NSPoint){0.0, 0.0}); - - // Free the garbage. - NSRelease(cursor_image); - NSRelease(representation); - - return (void*)cursor; -} - -void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) { - RGFW_ASSERT(win != NULL); RGFW_ASSERT(mouse); - objc_msgSend_void((id)mouse, sel_registerName("set")); -} - -void RGFW_freeMouse(RGFW_mouse* mouse) { - RGFW_ASSERT(mouse); - NSRelease((id)mouse); -} - -RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) { - return RGFW_window_setMouseStandard(win, RGFW_mouseArrow); -} - -void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) { - RGFW_window_showMouseFlags(win, show); - if (show) CGDisplayShowCursor(kCGDirectMainDisplay); - else CGDisplayHideCursor(kCGDirectMainDisplay); -} - -RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 stdMouses) { - static const char* mouseIconSrc[] = {"arrowCursor", "arrowCursor", "IBeamCursor", "crosshairCursor", "pointingHandCursor", "resizeLeftRightCursor", "resizeUpDownCursor", "_windowResizeNorthWestSouthEastCursor", "_windowResizeNorthEastSouthWestCursor", "closedHandCursor", "operationNotAllowedCursor"}; - if (stdMouses > ((sizeof(mouseIconSrc)) / (sizeof(char*)))) - return RGFW_FALSE; - - const char* mouseStr = mouseIconSrc[stdMouses]; - id mouse = NSCursor_arrowStr(mouseStr); - - if (mouse == NULL) - return RGFW_FALSE; - - RGFW_UNUSED(win); - CGDisplayShowCursor(kCGDirectMainDisplay); - objc_msgSend_void(mouse, sel_registerName("set")); - - return RGFW_TRUE; -} - -void RGFW_releaseCursor(RGFW_window* win) { - RGFW_UNUSED(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(0); -} - -void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) { - RGFW_UNUSED(win); - - win->_lastMousePoint = RGFW_POINT(v.x - win->r.x, v.y - win->r.y); - CGWarpMouseCursorPosition(CGPointMake(v.x, v.y)); -} - - -void RGFW_window_hide(RGFW_window* win) { - objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), false); -} - -void RGFW_window_show(RGFW_window* win) { - if (win->_flags & RGFW_windowFocusOnShow) - ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL); - - ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("orderFront:"), NULL); - objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true); -} - -RGFW_bool RGFW_window_isHidden(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - - bool visible = objc_msgSend_bool(win->src.window, sel_registerName("isVisible")); - return visible == NO && !RGFW_window_isMinimized(win); -} - -RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - - return objc_msgSend_bool(win->src.window, sel_registerName("isMiniaturized")) == YES; -} - -RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - RGFW_bool b = objc_msgSend_bool(win->src.window, sel_registerName("isZoomed")); - return b; -} - -id RGFW_getNSScreenForDisplayID(CGDirectDisplayID display) { - Class NSScreenClass = objc_getClass("NSScreen"); - - id screens = objc_msgSend_id(NSScreenClass, sel_registerName("screens")); - - NSUInteger count = (NSUInteger)objc_msgSend_uint(screens, sel_registerName("count")); - - for (NSUInteger i = 0; i < count; i++) { - id screen = ((id (*)(id, SEL, int))objc_msgSend) (screens, sel_registerName("objectAtIndex:"), (int)i); - id description = objc_msgSend_id(screen, sel_registerName("deviceDescription")); - id screenNumberKey = NSString_stringWithUTF8String("NSScreenNumber"); - id screenNumber = objc_msgSend_id_id(description, sel_registerName("objectForKey:"), screenNumberKey); - - if ((CGDirectDisplayID)objc_msgSend_uint(screenNumber, sel_registerName("unsignedIntValue")) == display) { - return screen; - } - } - - return NULL; -} - - -u32 RGFW_osx_getRefreshRate(CGDirectDisplayID display, CGDisplayModeRef mode) { - if (mode) { - u32 refreshRate = (int)CGDisplayModeGetRefreshRate(mode); - if (refreshRate != 0) return refreshRate; - } - - CVDisplayLinkRef link; - CVDisplayLinkCreateWithCGDisplay(display, &link); - const CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link); - if (!(time.flags & kCVTimeIsIndefinite)) - return (int) (time.timeScale / (double) time.timeValue); - - return 0; -} - -RGFW_monitor RGFW_NSCreateMonitor(CGDirectDisplayID display, id screen) { - RGFW_monitor monitor; - - const char name[] = "MacOS\0"; - RGFW_MEMCPY(monitor.name, name, 6); - - CGRect bounds = CGDisplayBounds(display); - monitor.x = bounds.origin.x; - monitor.y = bounds.origin.y; - monitor.mode.area = RGFW_AREA((int) bounds.size.width, (int) bounds.size.height); - - monitor.mode.red = 8; monitor.mode.green = 8; monitor.mode.blue = 8; - - CGDisplayModeRef mode = CGDisplayCopyDisplayMode(display); - monitor.mode.refreshRate = RGFW_osx_getRefreshRate(display, mode); - CFRelease(mode); - - CGSize screenSizeMM = CGDisplayScreenSize(display); - monitor.physW = (float)screenSizeMM.width / 25.4f; - monitor.physH = (float)screenSizeMM.height / 25.4f; - - float ppi_width = (monitor.mode.area.w/monitor.physW); - float ppi_height = (monitor.mode.area.h/monitor.physH); - - monitor.pixelRatio = ((CGFloat (*)(id, SEL))abi_objc_msgSend_fpret) (screen, sel_registerName("backingScaleFactor")); - float dpi = 96.0f * monitor.pixelRatio; - - monitor.scaleX = ((i32)(((float) (ppi_width) / dpi) * 10.0f)) / 10.0f; - monitor.scaleY = ((i32)(((float) (ppi_height) / dpi) * 10.0f)) / 10.0f; - - RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, RGFW_DEBUG_CTX_MON(monitor), "monitor found"); - return monitor; -} - - -RGFW_monitor* RGFW_getMonitors(void) { - static CGDirectDisplayID displays[7]; - u32 count; - - if (CGGetActiveDisplayList(6, displays, &count) != kCGErrorSuccess) - return NULL; - - static RGFW_monitor monitors[7]; - - for (u32 i = 0; i < count; i++) - monitors[i] = RGFW_NSCreateMonitor(displays[i], RGFW_getNSScreenForDisplayID(displays[i])); - - return monitors; -} - -RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) { - CGPoint point = { mon.x, mon.y }; - - CGDirectDisplayID display; - uint32_t displayCount = 0; - CGError err = CGGetDisplaysWithPoint(point, 1, &display, &displayCount); - if (err != kCGErrorSuccess || displayCount != 1) - return RGFW_FALSE; - - CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, NULL); - - if (allModes == NULL) - return RGFW_FALSE; - - for (CFIndex i = 0; i < CFArrayGetCount(allModes); i++) { - CGDisplayModeRef cmode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i); - - RGFW_monitorMode foundMode; - foundMode.area = RGFW_AREA(CGDisplayModeGetWidth(cmode), CGDisplayModeGetHeight(cmode)); - foundMode.refreshRate = RGFW_osx_getRefreshRate(display, cmode); - foundMode.red = 8; foundMode.green = 8; foundMode.blue = 8; - - if (RGFW_monitorModeCompare(mode, foundMode, request)) { - CGError err = CGDisplaySetDisplayMode(display, cmode, NULL); - if (err == kCGErrorSuccess) { - CFRelease(allModes); - return RGFW_TRUE; - } - break; - } - } - - CFRelease(allModes); - - return RGFW_FALSE; -} - -RGFW_monitor RGFW_getPrimaryMonitor(void) { - CGDirectDisplayID primary = CGMainDisplayID(); - return RGFW_NSCreateMonitor(primary, RGFW_getNSScreenForDisplayID(primary)); -} - -RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { - id screen = objc_msgSend_id(win->src.window, sel_registerName("screen")); - id description = objc_msgSend_id(screen, sel_registerName("deviceDescription")); - id screenNumberKey = NSString_stringWithUTF8String("NSScreenNumber"); - id screenNumber = objc_msgSend_id_id(description, sel_registerName("objectForKey:"), screenNumberKey); - - CGDirectDisplayID display = (CGDirectDisplayID)objc_msgSend_uint(screenNumber, sel_registerName("unsignedIntValue")); - - return RGFW_NSCreateMonitor(display, screen); -} - -RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { - size_t clip_len; - char* clip = (char*)NSPasteboard_stringForType(NSPasteboard_generalPasteboard(), NSPasteboardTypeString, &clip_len); - if (clip == NULL) return -1; - - if (str != NULL) { - if (strCapacity < clip_len) - return 0; - - RGFW_MEMCPY(str, clip, clip_len); - - str[clip_len] = '\0'; - } - - return (RGFW_ssize_t)clip_len; -} - -void RGFW_writeClipboard(const char* text, u32 textLen) { - RGFW_UNUSED(textLen); - - NSPasteboardType array[] = { NSPasteboardTypeString, NULL }; - NSPasteBoard_declareTypes(NSPasteboard_generalPasteboard(), array, 1, NULL); - - NSPasteBoard_setString(NSPasteboard_generalPasteboard(), text, NSPasteboardTypeString); -} - - #ifdef RGFW_OPENGL - void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - objc_msgSend_void(win->src.ctx, sel_registerName("makeCurrentContext")); - } - void* RGFW_getCurrent_OpenGL(void) { - return objc_msgSend_id(objc_getClass("NSOpenGLContext"), sel_registerName("currentContext")); - } - #endif - - #if !defined(RGFW_EGL) - - void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { - RGFW_ASSERT(win != NULL); - #if defined(RGFW_OPENGL) - - NSOpenGLContext_setValues((id)win->src.ctx, &swapInterval, 222); - #else - RGFW_UNUSED(swapInterval); - #endif - } - - #endif - -// Function to create a CGImageRef from an array of bytes -CGImageRef createImageFromBytes(unsigned char *buffer, int width, int height) -{ - // Define color space - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - // Create bitmap context - CGContextRef context = CGBitmapContextCreate( - buffer, - width, height, - 8, - width * 4, - colorSpace, - kCGImageAlphaPremultipliedLast); - // Create image from bitmap context - CGImageRef image = CGBitmapContextCreateImage(context); - // Release the color space and context - CGColorSpaceRelease(colorSpace); - CGContextRelease(context); - - return image; -} - -void RGFW_window_swapBuffers(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - /* clear the window */ - - if (!(win->_flags & RGFW_NO_CPU_RENDER)) { -#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - id view = NSWindow_contentView((id)win->src.window); - id layer = objc_msgSend_id(view, sel_registerName("layer")); - - ((void(*)(id, SEL, NSRect))objc_msgSend)(layer, - sel_registerName("setFrame:"), - (NSRect){{0, 0}, {win->r.w, win->r.h}}); - - CGImageRef image = createImageFromBytes(win->buffer, win->r.w, win->r.h); - // Get the current graphics context - id graphicsContext = objc_msgSend_class(objc_getClass("NSGraphicsContext"), sel_registerName("currentContext")); - // Get the CGContext from the current NSGraphicsContext - id cgContext = objc_msgSend_id(graphicsContext, sel_registerName("graphicsPort")); - // Draw the image in the context - NSRect bounds = (NSRect){{0,0}, {win->r.w, win->r.h}}; - CGContextDrawImage((CGContextRef)cgContext, *(CGRect*)&bounds, image); - // Flush the graphics context to ensure the drawing is displayed - objc_msgSend_id(graphicsContext, sel_registerName("flushGraphics")); - - objc_msgSend_void_id(layer, sel_registerName("setContents:"), (id)image); - objc_msgSend_id(layer, sel_registerName("setNeedsDisplay")); - - CGImageRelease(image); -#endif - } - - if (!(win->_flags & RGFW_NO_GPU_RENDER)) { - #ifdef RGFW_EGL - eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); - #elif defined(RGFW_OPENGL) - objc_msgSend_void(win->src.ctx, sel_registerName("flushBuffer")); - #endif - } -} - -void RGFW_window_close(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - NSRelease(win->src.view); - - #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - NSRelease(win->src.bitmap); - NSRelease(win->src.image); - if ((win->_flags & RGFW_BUFFER_ALLOC)) - RGFW_FREE(win->buffer); - #endif - - RGFW_clipboard_switch(NULL); - RGFW_FREE(win->event.droppedFiles); - - if ((win->_flags & RGFW_WINDOW_ALLOC)) - RGFW_FREE(win); -} - -u64 RGFW_getTimerFreq(void) { - static u64 freq = 0; - if (freq == 0) { - mach_timebase_info_data_t info; - mach_timebase_info(&info); - freq = (info.denom * 1e9) / info.numer; - } - - return freq; -} - -u64 RGFW_getTimerValue(void) { return (u64)mach_absolute_time(); } - -#endif /* RGFW_MACOS */ - -/* - End of MaOS defines -*/ - -/* - WASM defines -*/ - -#ifdef RGFW_WASM -EM_BOOL Emscripten_on_resize(int eventType, const EmscriptenUiEvent* e, void* userData) { - RGFW_UNUSED(eventType); RGFW_UNUSED(userData); - - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowResized, ._win = RGFW_root}); - RGFW_windowResizeCallback(RGFW_root, RGFW_RECT(0, 0, e->windowInnerWidth, e->windowInnerHeight)); - return EM_TRUE; -} - -EM_BOOL Emscripten_on_fullscreenchange(int eventType, const EmscriptenFullscreenChangeEvent* e, void* userData) { - RGFW_UNUSED(eventType); RGFW_UNUSED(userData); - static u8 fullscreen = RGFW_FALSE; - static RGFW_rect ogRect; - - if (fullscreen == RGFW_FALSE) { - ogRect = RGFW_root->r; - } - - fullscreen = !fullscreen; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowResized, ._win = RGFW_root}); - RGFW_root->r = RGFW_RECT(0, 0, e->screenWidth, e->screenHeight); - - EM_ASM("Module.canvas.focus();"); - - if (fullscreen == RGFW_FALSE) { - RGFW_root->r = RGFW_RECT(0, 0, ogRect.w, ogRect.h); - // emscripten_request_fullscreen("#canvas", 0); - } else { - #if __EMSCRIPTEN_major__ >= 1 && __EMSCRIPTEN_minor__ >= 29 && __EMSCRIPTEN_tiny__ >= 0 - EmscriptenFullscreenStrategy FSStrat = {0}; - FSStrat.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH;//EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT;// : EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; - FSStrat.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF; - FSStrat.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT; - emscripten_request_fullscreen_strategy("#canvas", 1, &FSStrat); - #else - emscripten_request_fullscreen("#canvas", 1); - #endif - } - - emscripten_set_canvas_element_size("#canvas", RGFW_root->r.w, RGFW_root->r.h); - - RGFW_windowResizeCallback(RGFW_root, RGFW_root->r); - return EM_TRUE; -} - - - -EM_BOOL Emscripten_on_focusin(int eventType, const EmscriptenFocusEvent* e, void* userData) { - RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(e); - - RGFW_eventQueuePush((RGFW_event){.type = RGFW_focusIn, ._win = RGFW_root}); - RGFW_root->_flags |= RGFW_windowFocus; - RGFW_focusCallback(RGFW_root, 1); - return EM_TRUE; -} - -EM_BOOL Emscripten_on_focusout(int eventType, const EmscriptenFocusEvent* e, void* userData) { - RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(e); - - RGFW_eventQueuePush((RGFW_event){.type = RGFW_focusOut, ._win = RGFW_root}); - RGFW_root->_flags &= ~RGFW_windowFocus; - RGFW_focusCallback(RGFW_root, 0); - return EM_TRUE; -} - -EM_BOOL Emscripten_on_mousemove(int eventType, const EmscriptenMouseEvent* e, void* userData) { - RGFW_UNUSED(eventType); RGFW_UNUSED(userData); - RGFW_eventQueuePush((RGFW_event){.type = RGFW_mousePosChanged, - .point = RGFW_POINT(e->targetX, e->targetY), - .vector = RGFW_POINT(e->movementX, e->movementY), - ._win = RGFW_root}); - - RGFW_root->_lastMousePoint = RGFW_POINT(e->targetX, e->targetY); - RGFW_mousePosCallback(RGFW_root, RGFW_POINT(e->targetX, e->targetY), RGFW_POINT(e->movementX, e->movementY)); - return EM_TRUE; -} - -EM_BOOL Emscripten_on_mousedown(int eventType, const EmscriptenMouseEvent* e, void* userData) { - RGFW_UNUSED(eventType); RGFW_UNUSED(userData); - - int button = e->button; - if (button > 2) - button += 2; - - RGFW_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonPressed, - .point = RGFW_POINT(e->targetX, e->targetY), - .vector = RGFW_POINT(e->movementX, e->movementY), - .button = (u8)button, - .scroll = 0, - ._win = RGFW_root}); - RGFW_mouseButtons[button].prev = RGFW_mouseButtons[button].current; - RGFW_mouseButtons[button].current = 1; - - RGFW_mouseButtonCallback(RGFW_root, button, 0, 1); - return EM_TRUE; -} - -EM_BOOL Emscripten_on_mouseup(int eventType, const EmscriptenMouseEvent* e, void* userData) { - RGFW_UNUSED(eventType); RGFW_UNUSED(userData); - - int button = e->button; - if (button > 2) - button += 2; - - RGFW_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonReleased, - .point = RGFW_POINT(e->targetX, e->targetY), - .vector = RGFW_POINT(e->movementX, e->movementY), - .button = (u8)button, - .scroll = 0, - ._win = RGFW_root}); - RGFW_mouseButtons[button].prev = RGFW_mouseButtons[button].current; - RGFW_mouseButtons[button].current = 0; - - RGFW_mouseButtonCallback(RGFW_root, button, 0, 0); - return EM_TRUE; -} - -EM_BOOL Emscripten_on_wheel(int eventType, const EmscriptenWheelEvent* e, void* userData) { - RGFW_UNUSED(eventType); RGFW_UNUSED(userData); - - int button = RGFW_mouseScrollUp + (e->deltaY < 0); - RGFW_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonPressed, - .button = (u8)button, - .scroll = (double)(e->deltaY < 0 ? 1 : -1), - ._win = RGFW_root}); - RGFW_mouseButtons[button].prev = RGFW_mouseButtons[button].current; - RGFW_mouseButtons[button].current = 1; - RGFW_mouseButtonCallback(RGFW_root, button, e->deltaY < 0 ? 1 : -1, 1); - - return EM_TRUE; -} - -EM_BOOL Emscripten_on_touchstart(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_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonPressed, - .point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY), - .button = RGFW_mouseLeft, - ._win = RGFW_root}); - - RGFW_mouseButtons[RGFW_mouseLeft].prev = RGFW_mouseButtons[RGFW_mouseLeft].current; - RGFW_mouseButtons[RGFW_mouseLeft].current = 1; - - RGFW_root->_lastMousePoint = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY); - RGFW_mousePosCallback(RGFW_root, RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY), RGFW_root->event.vector); - RGFW_mouseButtonCallback(RGFW_root, RGFW_mouseLeft, 0, 1); - } - - return EM_TRUE; -} -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_eventQueuePush((RGFW_event){.type = RGFW_mousePosChanged, - .point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY), - .button = RGFW_mouseLeft, - ._win = RGFW_root}); - - RGFW_root->_lastMousePoint = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY); - RGFW_mousePosCallback(RGFW_root, RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY), RGFW_root->event.vector); - } - return EM_TRUE; -} - -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_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonReleased, - .point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY), - .button = RGFW_mouseLeft, - ._win = RGFW_root}); - - RGFW_mouseButtons[RGFW_mouseLeft].prev = RGFW_mouseButtons[RGFW_mouseLeft].current; - RGFW_mouseButtons[RGFW_mouseLeft].current = 0; - - RGFW_root->_lastMousePoint = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY); - RGFW_mousePosCallback(RGFW_root, RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY), RGFW_root->event.vector); - RGFW_mouseButtonCallback(RGFW_root, RGFW_mouseLeft, 0, 0); - } - return EM_TRUE; -} - -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); - - if (gamepadEvent->index >= 4) - return 0; - - size_t i = gamepadEvent->index; - if (gamepadEvent->connected) { - RGFW_MEMCPY(RGFW_gamepads_name[gamepadEvent->index], gamepadEvent->id, sizeof(RGFW_gamepads_name[gamepadEvent->index])); - RGFW_gamepads_type[i] = RGFW_gamepadUnknown; - if (RGFW_STRSTR(RGFW_gamepads_name[i], "Microsoft") || RGFW_STRSTR(RGFW_gamepads_name[i], "X-Box")) - RGFW_gamepads_type[i] = RGFW_gamepadMicrosoft; - else if (RGFW_STRSTR(RGFW_gamepads_name[i], "PlayStation") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS3") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS4") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS5")) - RGFW_gamepads_type[i] = RGFW_gamepadSony; - else if (RGFW_STRSTR(RGFW_gamepads_name[i], "Nintendo")) - RGFW_gamepads_type[i] = RGFW_gamepadNintendo; - else if (RGFW_STRSTR(RGFW_gamepads_name[i], "Logitech")) - RGFW_gamepads_type[i] = RGFW_gamepadLogitech; - RGFW_gamepadCount++; - } else { - RGFW_gamepadCount--; - } - - RGFW_eventQueuePush((RGFW_event){.type = (RGFW_eventType)(gamepadEvent->connected ? RGFW_gamepadConnected : RGFW_gamepadConnected), - .gamepad = (u16)gamepadEvent->index, - ._win = RGFW_root}); - - RGFW_gamepadCallback(RGFW_root, gamepadEvent->index, gamepadEvent->connected); - RGFW_gamepads[gamepadEvent->index] = gamepadEvent->connected; - - return 1; // The event was consumed by the callback handler -} - -u32 RGFW_wASMPhysicalToRGFW(u32 hash) { - switch(hash) { /* 0x0000 */ - case 0x67243A2DU /* Escape */: return RGFW_escape; /* 0x0001 */ - case 0x67251058U /* Digit0 */: return RGFW_0; /* 0x0002 */ - case 0x67251059U /* Digit1 */: return RGFW_1; /* 0x0003 */ - case 0x6725105AU /* Digit2 */: return RGFW_2; /* 0x0004 */ - case 0x6725105BU /* Digit3 */: return RGFW_3; /* 0x0005 */ - case 0x6725105CU /* Digit4 */: return RGFW_4; /* 0x0006 */ - case 0x6725105DU /* Digit5 */: return RGFW_5; /* 0x0007 */ - case 0x6725105EU /* Digit6 */: return RGFW_6; /* 0x0008 */ - case 0x6725105FU /* Digit7 */: return RGFW_7; /* 0x0009 */ - case 0x67251050U /* Digit8 */: return RGFW_8; /* 0x000A */ - case 0x67251051U /* Digit9 */: return RGFW_9; /* 0x000B */ - case 0x92E14DD3U /* Minus */: return RGFW_minus; /* 0x000C */ - case 0x92E1FBACU /* Equal */: return RGFW_equals; /* 0x000D */ - case 0x36BF1CB5U /* Backspace */: return RGFW_backSpace; /* 0x000E */ - case 0x7B8E51E2U /* Tab */: return RGFW_tab; /* 0x000F */ - case 0x2C595B51U /* KeyQ */: return RGFW_q; /* 0x0010 */ - case 0x2C595B57U /* KeyW */: return RGFW_w; /* 0x0011 */ - case 0x2C595B45U /* KeyE */: return RGFW_e; /* 0x0012 */ - case 0x2C595B52U /* KeyR */: return RGFW_r; /* 0x0013 */ - case 0x2C595B54U /* KeyT */: return RGFW_t; /* 0x0014 */ - case 0x2C595B59U /* KeyY */: return RGFW_y; /* 0x0015 */ - case 0x2C595B55U /* KeyU */: return RGFW_u; /* 0x0016 */ - case 0x2C595B4FU /* KeyO */: return RGFW_o; /* 0x0018 */ - case 0x2C595B50U /* KeyP */: return RGFW_p; /* 0x0019 */ - case 0x45D8158CU /* BracketLeft */: return RGFW_closeBracket; /* 0x001A */ - case 0xDEEABF7CU /* BracketRight */: return RGFW_bracket; /* 0x001B */ - case 0x92E1C5D2U /* Enter */: return RGFW_return; /* 0x001C */ - case 0xE058958CU /* ControlLeft */: return RGFW_controlL; /* 0x001D */ - case 0x2C595B41U /* KeyA */: return RGFW_a; /* 0x001E */ - case 0x2C595B53U /* KeyS */: return RGFW_s; /* 0x001F */ - case 0x2C595B44U /* KeyD */: return RGFW_d; /* 0x0020 */ - case 0x2C595B46U /* KeyF */: return RGFW_f; /* 0x0021 */ - case 0x2C595B47U /* KeyG */: return RGFW_g; /* 0x0022 */ - case 0x2C595B48U /* KeyH */: return RGFW_h; /* 0x0023 */ - case 0x2C595B4AU /* KeyJ */: return RGFW_j; /* 0x0024 */ - case 0x2C595B4BU /* KeyK */: return RGFW_k; /* 0x0025 */ - case 0x2C595B4CU /* KeyL */: return RGFW_l; /* 0x0026 */ - case 0x2707219EU /* Semicolon */: return RGFW_semicolon; /* 0x0027 */ - case 0x92E0B58DU /* Quote */: return RGFW_apostrophe; /* 0x0028 */ - case 0x36BF358DU /* Backquote */: return RGFW_backtick; /* 0x0029 */ - case 0x26B1958CU /* ShiftLeft */: return RGFW_shiftL; /* 0x002A */ - case 0x36BF2438U /* Backslash */: return RGFW_backSlash; /* 0x002B */ - case 0x2C595B5AU /* KeyZ */: return RGFW_z; /* 0x002C */ - case 0x2C595B58U /* KeyX */: return RGFW_x; /* 0x002D */ - case 0x2C595B43U /* KeyC */: return RGFW_c; /* 0x002E */ - case 0x2C595B56U /* KeyV */: return RGFW_v; /* 0x002F */ - case 0x2C595B42U /* KeyB */: return RGFW_b; /* 0x0030 */ - case 0x2C595B4EU /* KeyN */: return RGFW_n; /* 0x0031 */ - case 0x2C595B4DU /* KeyM */: return RGFW_m; /* 0x0032 */ - case 0x92E1A1C1U /* Comma */: return RGFW_comma; /* 0x0033 */ - case 0x672FFAD4U /* Period */: return RGFW_period; /* 0x0034 */ - case 0x92E0A438U /* Slash */: return RGFW_slash; /* 0x0035 */ - case 0xC5A6BF7CU /* ShiftRight */: return RGFW_shiftR; - case 0x5D64DA91U /* NumpadMultiply */: return RGFW_multiply; - case 0xC914958CU /* AltLeft */: return RGFW_altL; /* 0x0038 */ - case 0x92E09CB5U /* Space */: return RGFW_space; /* 0x0039 */ - case 0xB8FAE73BU /* CapsLock */: return RGFW_capsLock; /* 0x003A */ - case 0x7174B789U /* F1 */: return RGFW_F1; /* 0x003B */ - case 0x7174B78AU /* F2 */: return RGFW_F2; /* 0x003C */ - case 0x7174B78BU /* F3 */: return RGFW_F3; /* 0x003D */ - case 0x7174B78CU /* F4 */: return RGFW_F4; /* 0x003E */ - case 0x7174B78DU /* F5 */: return RGFW_F5; /* 0x003F */ - case 0x7174B78EU /* F6 */: return RGFW_F6; /* 0x0040 */ - case 0x7174B78FU /* F7 */: return RGFW_F7; /* 0x0041 */ - case 0x7174B780U /* F8 */: return RGFW_F8; /* 0x0042 */ - case 0x7174B781U /* F9 */: return RGFW_F9; /* 0x0043 */ - case 0x7B8E57B0U /* F10 */: return RGFW_F10; /* 0x0044 */ - case 0xC925FCDFU /* Numpad7 */: return RGFW_multiply; /* 0x0047 */ - case 0xC925FCD0U /* Numpad8 */: return RGFW_KP_8; /* 0x0048 */ - case 0xC925FCD1U /* Numpad9 */: return RGFW_KP_9; /* 0x0049 */ - case 0x5EA3E8A4U /* NumpadSubtract */: return RGFW_minus; /* 0x004A */ - case 0xC925FCDCU /* Numpad4 */: return RGFW_KP_4; /* 0x004B */ - case 0xC925FCDDU /* Numpad5 */: return RGFW_KP_5; /* 0x004C */ - case 0xC925FCDEU /* Numpad6 */: return RGFW_KP_6; /* 0x004D */ - case 0xC925FCD9U /* Numpad1 */: return RGFW_KP_1; /* 0x004F */ - case 0xC925FCDAU /* Numpad2 */: return RGFW_KP_2; /* 0x0050 */ - case 0xC925FCDBU /* Numpad3 */: return RGFW_KP_3; /* 0x0051 */ - case 0xC925FCD8U /* Numpad0 */: return RGFW_KP_0; /* 0x0052 */ - case 0x95852DACU /* NumpadDecimal */: return RGFW_period; /* 0x0053 */ - case 0x7B8E57B1U /* F11 */: return RGFW_F11; /* 0x0057 */ - case 0x7B8E57B2U /* F12 */: return RGFW_F12; /* 0x0058 */ - case 0x7393FBACU /* NumpadEqual */: return RGFW_KP_Return; - case 0xB88EBF7CU /* AltRight */: return RGFW_altR; /* 0xE038 */ - case 0xC925873BU /* NumLock */: return RGFW_numLock; /* 0xE045 */ - case 0x2C595F45U /* Home */: return RGFW_home; /* 0xE047 */ - case 0xC91BB690U /* ArrowUp */: return RGFW_up; /* 0xE048 */ - case 0x672F9210U /* PageUp */: return RGFW_pageUp; /* 0xE049 */ - case 0x3799258CU /* ArrowLeft */: return RGFW_left; /* 0xE04B */ - case 0x4CE33F7CU /* ArrowRight */: return RGFW_right; /* 0xE04D */ - case 0x7B8E55DCU /* End */: return RGFW_end; /* 0xE04F */ - case 0x3799379EU /* ArrowDown */: return RGFW_down; /* 0xE050 */ - case 0xBA90179EU /* PageDown */: return RGFW_pageDown; /* 0xE051 */ - case 0x6723CB2CU /* Insert */: return RGFW_insert; /* 0xE052 */ - case 0x6725C50DU /* Delete */: return RGFW_delete; /* 0xE053 */ - case 0x6723658CU /* OSLeft */: return RGFW_superL; /* 0xE05B */ - case 0x39643F7CU /* MetaRight */: return RGFW_superR; /* 0xE05C */ - } - - return 0; -} - -void EMSCRIPTEN_KEEPALIVE RGFW_handleKeyEvent(char* key, char* code, RGFW_bool press) { - const char* iCode = code; - - u32 hash = 0; - while(*iCode) hash = ((hash ^ 0x7E057D79U) << 3) ^ (unsigned int)*iCode++; - - u32 physicalKey = RGFW_wASMPhysicalToRGFW(hash); - - u8 mappedKey = (u8)(*((u32*)key)); - - if (*((u16*)key) != mappedKey) { - mappedKey = 0; - if (*((u32*)key) == *((u32*)"Tab")) mappedKey = RGFW_tab; - } - - RGFW_eventQueuePush((RGFW_event){.type = (RGFW_eventType)(press ? RGFW_keyPressed : RGFW_keyReleased), - .key = (u8)physicalKey, - .keyChar = (u8)mappedKey, - .keyMod = RGFW_root->event.keyMod, - ._win = RGFW_root}); - - RGFW_keyboard[physicalKey].prev = RGFW_keyboard[physicalKey].current; - RGFW_keyboard[physicalKey].current = press; - - RGFW_keyCallback(RGFW_root, physicalKey, mappedKey, RGFW_root->event.keyMod, press); -} - -void EMSCRIPTEN_KEEPALIVE RGFW_handleKeyMods(RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll) { - RGFW_updateKeyModsPro(RGFW_root, capital, numlock, control, alt, shift, super, scroll); -} - -void EMSCRIPTEN_KEEPALIVE Emscripten_onDrop(size_t count) { - if (!(RGFW_root->_flags & RGFW_windowAllowDND)) - return; - - RGFW_eventQueuePush((RGFW_event){.type = RGFW_DND, - .droppedFilesCount = count, - ._win = RGFW_root}); - RGFW_dndCallback(RGFW_root, RGFW_root->event.droppedFiles, count); -} - -RGFW_bool RGFW_stopCheckEvents_bool = RGFW_FALSE; -void RGFW_stopCheckEvents(void) { - RGFW_stopCheckEvents_bool = RGFW_TRUE; -} - -void RGFW_window_eventWait(RGFW_window* win, u32 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 && - (waitMS != RGFW_eventWaitNext || (RGFW_getTimeNS() / 1e+6) - start < waitMS) - ) { - emscripten_sleep(0); - } - - RGFW_stopCheckEvents_bool = RGFW_FALSE; -} - -void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area){ - #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - win->buffer = buffer; - win->bufferSize = area; - #ifdef 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); RGFW_UNUSED(buffer); RGFW_UNUSED(area); /*!< 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 - */ - RGFW_MEMCPY((char*)RGFW_root->event.droppedFiles[index], file, RGFW_MAX_PATH); -} - -#include -#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_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) { - RGFW_UNUSED(name); - - RGFW_window_basic_init(win, rect, flags); - - #ifndef RGFW_WEBGPU - EmscriptenWebGLContextAttributes attrs; - attrs.alpha = RGFW_GL_HINTS[RGFW_glDepth]; - attrs.depth = RGFW_GL_HINTS[RGFW_glAlpha]; - attrs.stencil = RGFW_GL_HINTS[RGFW_glStencil]; - attrs.antialias = RGFW_GL_HINTS[RGFW_glSamples]; - attrs.premultipliedAlpha = EM_TRUE; - attrs.preserveDrawingBuffer = EM_FALSE; - - if (RGFW_GL_HINTS[RGFW_glDoubleBuffer] == 0) - attrs.renderViaOffscreenBackBuffer = 0; - else - attrs.renderViaOffscreenBackBuffer = RGFW_GL_HINTS[RGFW_glAuxBuffers]; - - attrs.failIfMajorPerformanceCaveat = EM_FALSE; - attrs.majorVersion = (RGFW_GL_HINTS[RGFW_glMinor] == 0) ? 1 : RGFW_GL_HINTS[RGFW_glMinor]; - attrs.minorVersion = RGFW_GL_HINTS[RGFW_glMajor]; - - attrs.enableExtensionsByDefault = EM_TRUE; - attrs.explicitSwapControl = EM_TRUE; - - emscripten_webgl_init_context_attributes(&attrs); - win->src.ctx = emscripten_webgl_create_context("#canvas", &attrs); - emscripten_webgl_make_context_current(win->src.ctx); - - #ifdef LEGACY_GL_EMULATION - EM_ASM("Module.useWebGL = true; GLImmediate.init();"); - #endif - #else - win->src.ctx = wgpuCreateInstance(NULL); - win->src.device = emscripten_webgpu_get_device(); - win->src.queue = wgpuDeviceGetQueue(win->src.device); - #endif - - emscripten_set_canvas_element_size("#canvas", rect.w, rect.h); - emscripten_set_window_title(name); - - /* load callbacks */ - 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 (flags & RGFW_windowAllowDND) { - win->_flags |= RGFW_windowAllowDND; - } - - EM_ASM({ - window.addEventListener("keydown", - (event) => { - var key = stringToNewUTF8(event.key); var code = stringToNewUTF8(event.code); - Module._RGFW_handleKeyMods(event.getModifierState("CapsLock"), event.getModifierState("NumLock"), event.getModifierState("Control"), event.getModifierState("Alt"), event.getModifierState("Shift"), event.getModifierState("Meta"), event.getModifierState("ScrollLock")); - Module._RGFW_handleKeyEvent(key, code, 1); - _free(key); _free(code); - }, - true); - window.addEventListener("keyup", - (event) => { - var key = stringToNewUTF8(event.key); var code = stringToNewUTF8(event.code); - Module._RGFW_handleKeyMods(event.getModifierState("CapsLock"), event.getModifierState("NumLock"), event.getModifierState("Control"), event.getModifierState("Alt"), event.getModifierState("Shift"), event.getModifierState("Meta"), event.getModifierState("ScrollLock")); - Module._RGFW_handleKeyEvent(key, code, 0); - _free(key); _free(code); - }, - true); - }); - - 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); - }); - - glViewport(0, 0, rect.w, rect.h); - - RGFW_window_setFlags(win, flags); - - RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new window was created"); - return win; -} - -RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { - RGFW_event* ev = RGFW_window_checkEventCore(win); - if (ev) { - if (ev == (RGFW_event*)-1) return NULL; - return ev; - } - - emscripten_sample_gamepad_data(); - /* check gamepads */ - for (int i = 0; (i < emscripten_get_num_gamepads()) && (i < 4); i++) { - if (RGFW_gamepads[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_gamepadA, RGFW_gamepadB, RGFW_gamepadX, RGFW_gamepadY, - RGFW_gamepadL1, RGFW_gamepadR1, RGFW_gamepadL2, RGFW_gamepadR2, - RGFW_gamepadSelect, RGFW_gamepadStart, - RGFW_gamepadL3, RGFW_gamepadR3, - RGFW_gamepadUp, RGFW_gamepadDown, RGFW_gamepadLeft, RGFW_gamepadRight, RGFW_gamepadHome - }; - - - u32 button = map[j]; - if (button == 404) - continue; - - if (RGFW_gamepadPressed[i][button].current != gamepadState.digitalButton[j]) { - if (gamepadState.digitalButton[j]) - win->event.type = RGFW_gamepadButtonPressed; - else - win->event.type = RGFW_gamepadButtonReleased; - - win->event.gamepad = i; - win->event.button = map[j]; - - RGFW_gamepadPressed[i][button].prev = RGFW_gamepadPressed[i][button].current; - RGFW_gamepadPressed[i][button].current = gamepadState.digitalButton[j]; - - RGFW_gamepadButtonCallback(win, win->event.gamepad, win->event.button, gamepadState.digitalButton[j]); - return &win->event; - } - } - - for (int j = 0; (j < gamepadState.numAxes) && (j < 4); j += 2) { - win->event.axisesCount = gamepadState.numAxes / 2; - if (RGFW_gamepadAxes[i][(size_t)(j / 2)].x != (i8)(gamepadState.axis[j] * 100.0f) || - RGFW_gamepadAxes[i][(size_t)(j / 2)].y != (i8)(gamepadState.axis[j + 1] * 100.0f) - ) { - - RGFW_gamepadAxes[i][(size_t)(j / 2)].x = (i8)(gamepadState.axis[j] * 100.0f); - RGFW_gamepadAxes[i][(size_t)(j / 2)].y = (i8)(gamepadState.axis[j + 1] * 100.0f); - win->event.axis[(size_t)(j / 2)] = RGFW_gamepadAxes[i][(size_t)(j / 2)]; - - win->event.type = RGFW_gamepadAxisMove; - win->event.gamepad = i; - win->event.whichAxis = j / 2; - - RGFW_gamepadAxisCallback(win, win->event.gamepad, win->event.axis, win->event.axisesCount, win->event.whichAxis); - return &win->event; - } - } - } - - return NULL; -} - -void RGFW_window_resize(RGFW_window* win, RGFW_area a) { - RGFW_UNUSED(win); - emscripten_set_canvas_element_size("#canvas", a.w, a.h); -} - -/* NOTE: I don't know if this is possible */ -void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) { RGFW_UNUSED(win); RGFW_UNUSED(v); } -/* this one might be possible but it looks iffy */ -RGFW_mouse* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels) { RGFW_UNUSED(channels); RGFW_UNUSED(a); RGFW_UNUSED(icon); return NULL; } - -void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) { RGFW_UNUSED(win); RGFW_UNUSED(mouse); } -void RGFW_freeMouse(RGFW_mouse* mouse) { RGFW_UNUSED(mouse); } - -RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { - static const char cursors[11][12] = { - "default", "default", "text", "crosshair", - "pointer", "ew-resize", "ns-resize", "nwse-resize", "nesw-resize", - "move", "not-allowed" - }; - - RGFW_UNUSED(win); - EM_ASM( { document.getElementById("canvas").style.cursor = UTF8ToString($0); }, cursors[mouse]); - return RGFW_TRUE; -} - -RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) { - return RGFW_window_setMouseStandard(win, RGFW_mouseNormal); -} - -void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) { - RGFW_window_showMouseFlags(win, show); - if (show) - RGFW_window_setMouseDefault(win); - else - EM_ASM(document.getElementById('canvas').style.cursor = 'none';); -} - -RGFW_point RGFW_getGlobalMousePoint(void) { - RGFW_point point; - point.x = EM_ASM_INT({ - return window.mouseX || 0; - }); - point.y = EM_ASM_INT({ - return window.mouseY || 0; - }); - return point; -} - -void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) { - RGFW_UNUSED(win); - - EM_ASM_({ - var canvas = document.getElementById('canvas'); - if ($0) { - canvas.style.pointerEvents = 'none'; - } else { - canvas.style.pointerEvents = 'auto'; - } - }, passthrough); -} - -void RGFW_writeClipboard(const char* text, u32 textLen) { - RGFW_UNUSED(textLen); - EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, text); -} - - -RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { - RGFW_UNUSED(str); RGFW_UNUSED(strCapacity); - /* - placeholder code for later - I'm not sure if this is possible do the the async stuff - */ - return 0; -} - -void RGFW_window_swapBuffers(RGFW_window* win) { - RGFW_UNUSED(win); - - #ifdef RGFW_BUFFER - if (!(win->_flags & RGFW_NO_CPU_RENDER)) { - glEnable(GL_TEXTURE_2D); - - GLuint texture; - glGenTextures(1,&texture); - - glBindTexture(GL_TEXTURE_2D,texture); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - #ifdef RGFW_BUFFER_BGR - glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA, win->bufferSize.w, win->bufferSize.h, 0, GL_BGRA, GL_UNSIGNED_BYTE, win->buffer); - #else - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, win->bufferSize.w, win->bufferSize.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, win->buffer); - #endif - - float ratioX = ((float)win->r.w / (float)win->bufferSize.w); - float ratioY = ((float)win->r.h / (float)win->bufferSize.h); - - // Set up the viewport - glClear(GL_COLOR_BUFFER_BIT); - - glBegin(GL_TRIANGLES); - glTexCoord2f(0, ratioY); glColor3f(1, 1, 1); glVertex2f(-1, -1); - glTexCoord2f(0, 0); glColor3f(1, 1, 1); glVertex2f(-1, 1); - glTexCoord2f(ratioX, ratioY); glColor3f(1, 1, 1); glVertex2f(1, -1); - - glTexCoord2f(ratioX, 0); glColor3f(1, 1, 1); glVertex2f(1, 1); - glTexCoord2f(ratioX, ratioY); glColor3f(1, 1, 1); glVertex2f(1, -1); - glTexCoord2f(0, 0); glColor3f(1, 1, 1); glVertex2f(-1, 1); - glEnd(); - - glDeleteTextures(1, &texture); - } - #endif - -#ifndef RGFW_WEBGPU - emscripten_webgl_commit_frame(); -#endif - emscripten_sleep(0); -} - - -void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { -#ifndef RGFW_WEBGPU - if (win == NULL) - emscripten_webgl_make_context_current(0); - else - emscripten_webgl_make_context_current(win->src.ctx); -#endif -} - -#ifndef RGFW_WEBGPU -void* RGFW_getCurrent_OpenGL(void) { return (void*)emscripten_webgl_get_current_context(); } -#endif - -#ifndef RGFW_EGL -void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { RGFW_UNUSED(win); RGFW_UNUSED(swapInterval); } -#endif - -void RGFW_window_close(RGFW_window* win) { -#ifndef RGFW_WEBGPU - emscripten_webgl_destroy_context(win->src.ctx); -#endif - - #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - if ((win->_flags & RGFW_BUFFER_ALLOC)) - RGFW_FREE(win->buffer); - #endif - - RGFW_clipboard_switch(NULL); - RGFW_FREE(win->event.droppedFiles); - - if ((win->_flags & RGFW_WINDOW_ALLOC)) - RGFW_FREE(win); -} - -int RGFW_innerWidth(void) { return EM_ASM_INT({ return window.innerWidth; }); } -int RGFW_innerHeight(void) { return EM_ASM_INT({ return window.innerHeight; }); } - -RGFW_area RGFW_getScreenSize(void) { - return RGFW_AREA(RGFW_innerWidth(), RGFW_innerHeight()); -} - -void* RGFW_getProcAddress(const char* procname) { - return emscripten_webgl_get_proc_address(procname); -} - -void RGFW_sleep(u64 milisecond) { - emscripten_sleep(milisecond); -} - -u64 RGFW_getTimerFreq(void) { return (u64)1000; } -u64 RGFW_getTimerValue(void) { return emscripten_get_now() * 1e+6; } - -void RGFW_releaseCursor(RGFW_window* win) { - RGFW_UNUSED(win); - emscripten_exit_pointerlock(); -} - -void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { - RGFW_UNUSED(win); RGFW_UNUSED(r); - - emscripten_request_pointerlock("#canvas", 1); -} - - -void RGFW_window_setName(RGFW_window* win, const char* name) { - RGFW_UNUSED(win); - emscripten_set_window_title(name); -} - -void RGFW_window_maximize(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - - RGFW_area screen = RGFW_getScreenSize(); - RGFW_window_move(win, RGFW_POINT(0, 0)); - RGFW_window_resize(win, screen); -} - -void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) { - RGFW_ASSERT(win != NULL); - if (fullscreen) { - win->_flags |= RGFW_windowFullscreen; - EM_ASM( Module.requestFullscreen(false, true); ); - return; - } - win->_flags &= ~RGFW_windowFullscreen; - EM_ASM( Module.exitFullscreen(false, true); ); -} - -void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) { - RGFW_UNUSED(win); - EM_ASM({ - var element = document.getElementById("canvas"); - if (element) - element.style.opacity = $1; - }, "elementId", opacity); -} - -/* unsupported functions */ -void RGFW_window_focus(RGFW_window* win) { RGFW_UNUSED(win); } -void RGFW_window_raise(RGFW_window* win) { RGFW_UNUSED(win); } -RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) { RGFW_UNUSED(mon); RGFW_UNUSED(mode); RGFW_UNUSED(request); return RGFW_FALSE; } -RGFW_monitor* RGFW_getMonitors(void) { return NULL; } -RGFW_monitor RGFW_getPrimaryMonitor(void) { return (RGFW_monitor){}; } -void RGFW_window_move(RGFW_window* win, RGFW_point v) { RGFW_UNUSED(win); RGFW_UNUSED(v); } -void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a) { RGFW_UNUSED(win); RGFW_UNUSED(a); } -void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { RGFW_UNUSED(win); RGFW_UNUSED(a); } -void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { RGFW_UNUSED(win); RGFW_UNUSED(a); } -void RGFW_window_minimize(RGFW_window* win) { RGFW_UNUSED(win); } -void RGFW_window_restore(RGFW_window* win) { RGFW_UNUSED(win); } -void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) { RGFW_UNUSED(win); RGFW_UNUSED(floating); } -void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { RGFW_UNUSED(win); RGFW_UNUSED(border); } -RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* icon, RGFW_area a, i32 channels, u8 type) { RGFW_UNUSED(win); RGFW_UNUSED(icon); RGFW_UNUSED(a); RGFW_UNUSED(channels); RGFW_UNUSED(type); return RGFW_FALSE; } -void RGFW_window_hide(RGFW_window* win) { RGFW_UNUSED(win); } -void RGFW_window_show(RGFW_window* win) {RGFW_UNUSED(win); } -RGFW_bool RGFW_window_isHidden(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; } -RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; } -RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; } -RGFW_bool RGFW_window_isFloating(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; } -RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { RGFW_UNUSED(win); return (RGFW_monitor){}; } -#endif - -/* end of web asm defines */ - -/* unix (macOS, linux, web asm) only stuff */ -#if defined(RGFW_X11) || defined(RGFW_MACOS) || defined(RGFW_WASM) || defined(RGFW_WAYLAND) -#ifndef RGFW_NO_THREADS -#include - -RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args) { - RGFW_UNUSED(args); - - RGFW_thread t; - pthread_create((pthread_t*) &t, NULL, *ptr, NULL); - return t; -} -void RGFW_cancelThread(RGFW_thread thread) { pthread_cancel((pthread_t) thread); } -void RGFW_joinThread(RGFW_thread thread) { pthread_join((pthread_t) thread, NULL); } - -#if defined(__linux__) -void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { pthread_setschedprio((pthread_t)thread, priority); } -#else -void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { RGFW_UNUSED(thread); RGFW_UNUSED(priority); } -#endif -#endif - -#ifndef RGFW_WASM -void RGFW_sleep(u64 ms) { - struct timespec time; - time.tv_sec = 0; - time.tv_nsec = ms * 1e+6; - - #ifndef RGFW_NO_UNIX_CLOCK - nanosleep(&time, NULL); - #endif -} -#endif - -#endif /* end of unix / mac stuff */ -#endif /* RGFW_IMPLEMENTATION */ - -#if defined(__cplusplus) && !defined(__EMSCRIPTEN__) -} - #ifdef __clang__ - #pragma clang diagnostic pop - #endif -#endif +/* +* +* RGFW 1.7 +* +* Copyright (C) 2022-25 ColleagueRiley +* +* libpng license +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. + +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +* +* +*/ + +/* + (MAKE SURE RGFW_IMPLEMENTATION is in exactly one header or you use -D RGFW_IMPLEMENTATION) + #define RGFW_IMPLEMENTATION - makes it so source code is included with header +*/ + +/* + #define RGFW_IMPLEMENTATION - (required) makes it so the source code is included + #define RGFW_DEBUG - (optional) makes it so RGFW prints debug messages and errors when they're found + #define RGFW_OSMESA - (optional) use OSmesa as backend (instead of system's opengl api + regular opengl) + #define RGFW_BUFFER - (optional) draw directly to (RGFW) window pixel buffer that is drawn to screen (the buffer is in the RGBA format) + #define RGFW_EGL - (optional) use EGL for loading an OpenGL context (instead of the system's opengl api) + #define RGFW_OPENGL_ES1 - (optional) use EGL to load and use Opengl ES (version 1) for backend rendering (instead of the system's opengl api) + This version doesn't work for desktops (I'm pretty sure) + #define RGFW_OPENGL_ES2 - (optional) use OpenGL ES (version 2) + #define RGFW_OPENGL_ES3 - (optional) use OpenGL ES (version 3) + #define RGFW_DIRECTX - (optional) include integration directX functions (windows only) + #define RGFW_VULKAN - (optional) include helpful vulkan integration functions and macros + #define RGFW_WEBGPU - (optional) use webGPU for rendering (Web ONLY) + #define RGFW_NO_API - (optional) don't use any rendering API (no opengl, no vulkan, no directX) + + #define RGFW_LINK_EGL (optional) (windows only) if EGL is being used, if EGL functions should be defined dymanically (using GetProcAddress) + #define RGFW_X11 (optional) (unix only) if X11 should be used. This option is turned on by default by unix systems except for MacOS + #define RGFW_WAYLAND (optional) (unix only) use Wayland. (This can be used with X11) + #define RGFW_NO_X11 (optional) (unix only) don't fallback to X11 when using Wayland + #define RGFW_NO_LOAD_WGL (optional) (windows only) if WGL should be loaded dynamically during runtime + #define RGFW_NO_X11_CURSOR (optional) (unix only) don't use XCursor + #define RGFW_NO_X11_CURSOR_PRELOAD (optional) (unix only) use XCursor, but don't link it in code, (you'll have to link it with -lXcursor) + #define RGFW_NO_X11_EXT_PRELOAD (optional) (unix only) use Xext, but don't link it in code, (you'll have to link it with -lXext) + #define RGFW_NO_LOAD_WINMM (optional) (windows only) use winmm (timeBeginPeriod), but don't link it in code, (you'll have to link it with -lwinmm) + #define RGFW_NO_WINMM (optional) (windows only) don't use winmm + #define RGFW_NO_IOKIT (optional) (macOS) don't use IOKit + #define RGFW_NO_UNIX_CLOCK (optional) (unix) don't link unix clock functions + #define RGFW_NO_DWM (windows only) - do not use or link dwmapi + #define RGFW_USE_XDL (optional) (X11) if XDL (XLib Dynamic Loader) should be used to load X11 dynamically during runtime (must include XDL.h along with RGFW) + #define RGFW_COCOA_GRAPHICS_SWITCHING - (optional) (cocoa) use automatic graphics switching (allow the system to choose to use GPU or iGPU) + #define RGFW_COCOA_FRAME_NAME (optional) (cocoa) set frame name + #define RGFW_NO_DPI - do not calculate DPI (no XRM nor libShcore included) + #define RGFW_BUFFER_BGR - use the BGR format for bufffers instead of RGB, saves processing time + #define RGFW_ADVANCED_SMOOTH_RESIZE - use advanced methods for smooth resizing (may result in a spike in memory usage or worse performance) (eg. WM_TIMER and XSyncValue) + + #define RGFW_ALLOC x - choose the default allocation function (defaults to standard malloc) + #define RGFW_FREE x - choose the default deallocation function (defaults to standard free) + #define RGFW_USERPTR x - choose the default userptr sent to the malloc call, (NULL by default) + + #define RGFW_EXPORT - use when building RGFW + #define RGFW_IMPORT - use when linking with RGFW (not as a single-header) + + #define RGFW_USE_INT - force the use c-types rather than stdint.h (for systems that might not have stdint.h (msvc)) + #define RGFW_bool x - choose what type to use for bool, by default u32 is used +*/ + +/* +Example to get you started : + +linux : gcc main.c -lX11 -lXrandr -lGL +windows : gcc main.c -lopengl32 -lgdi32 +macos : gcc main.c -framework Cocoa -framework CoreVideo -framework OpenGL -framework IOKit + +#define RGFW_IMPLEMENTATION +#include "RGFW.h" + +u8 icon[4 * 3 * 3] = {0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF}; + +int main() { + RGFW_window* win = RGFW_createWindow("name", RGFW_RECT(100, 100, 500, 500), (u64)0); + + RGFW_window_setIcon(win, icon, RGFW_AREA(3, 3), 4); + + while (RGFW_window_shouldClose(win) == RGFW_FALSE) { + while (RGFW_window_checkEvent(win)) { + if (win->event.type == RGFW_quit || RGFW_isPressed(win, RGFW_escape)) + break; + } + + RGFW_window_swapBuffers(win); + + glClearColor(1, 1, 1, 1); + glClear(GL_COLOR_BUFFER_BIT); + } + + RGFW_window_close(win); +} + + compiling : + + if you wish to compile the library all you have to do is create a new file with this in it + + rgfw.c + #define RGFW_IMPLEMENTATION + #include "RGFW.h" + + You may also want to add + `#define RGFW_EXPORT` when compiling and + `#define RGFW_IMPORT`when linking RGFW on it's own: + this reduces inline functions and prevents bloat in the object file + + then you can use gcc (or whatever compile you wish to use) to compile the library into object file + + ex. gcc -c RGFW.c -fPIC + + after you compile the library into an object file, you can also turn the object file into an static or shared library + + (commands ar and gcc can be replaced with whatever equivalent your system uses) + + static : ar rcs RGFW.a RGFW.o + shared : + windows: + gcc -shared RGFW.o -lopengl32 -lgdi32 -o RGFW.dll + linux: + gcc -shared RGFW.o -lX11 -lGL -lXrandr -o RGFW.so + macos: + gcc -shared RGFW.o -framework CoreVideo -framework Cocoa -framework OpenGL -framework IOKit +*/ + + + +/* + Credits : + EimaMei/Sacode : Much of the code for creating windows using winapi, Wrote the Silicon library, helped with MacOS Support, siliapp.h -> referencing + + stb - This project is heavily inspired by the stb single header files + + GLFW: + certain parts of winapi and X11 are very poorly documented, + GLFW's source code was referenced and used throughout the project. + + contributors : (feel free to put yourself here if you contribute) + krisvers -> code review + EimaMei (SaCode) -> code review + 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 + Joshua Rowe (omnisci3nce) - bug fix, review (macOS) + @lesleyrs -> bug fix, review (OpenGL) + Nick Porcino (meshula) - testing, organization, review (MacOS, examples) + @DarekParodia -> code review (X11) (C++) +*/ + +#if _MSC_VER + #pragma comment(lib, "gdi32") + #pragma comment(lib, "shell32") + #pragma comment(lib, "User32") + #pragma warning( push ) + #pragma warning( disable : 4996 4191 4127) + #if _MSC_VER < 600 + #define RGFW_C89 + #endif +#else + #if defined(__STDC__) && !defined(__STDC_VERSION__) + #define RGFW_C89 + #endif +#endif + +#ifndef RGFW_USERPTR + #define RGFW_USERPTR NULL +#endif + +#ifndef RGFW_UNUSED + #define RGFW_UNUSED(x) (void)(x) +#endif + +#ifndef RGFW_ROUND + #define RGFW_ROUND(x) (i32)((x) >= 0 ? (x) + 0.5f : (x) - 0.5f) +#endif + +#ifndef RGFW_ALLOC + #include + #define RGFW_ALLOC malloc + #define RGFW_FREE free +#endif + +#ifndef RGFW_ASSERT + #include + #define RGFW_ASSERT assert +#endif + +#if !defined(RGFW_MEMCPY) || !defined(RGFW_STRNCMP) || !defined(RGFW_STRNCPY) + #include +#endif + +#ifndef RGFW_MEMCPY + #define RGFW_MEMCPY(dist, src, len) memcpy(dist, src, len) +#endif + +#ifndef RGFW_STRNCMP + #define RGFW_STRNCMP(s1, s2, max) strncmp(s1, s2, max) +#endif + +#ifndef RGFW_STRNCPY + #define RGFW_STRNCPY(dist, src, len) strncpy(dist, src, len) +#endif + +#ifndef RGFW_STRSTR + #define RGFW_STRSTR(str, substr) strstr(str, substr) +#endif + +#ifndef RGFW_STRTOL + /* required for X11 XDnD and X11 Monitor DPI */ + #include + #define RGFW_STRTOL(str, endptr, base) strtol(str, endptr, base) + #define RGFW_ATOF(num) atof(num) +#endif + +#if !_MSC_VER + #ifndef inline + #ifndef __APPLE__ + #define inline __inline + #endif + #endif +#endif + +#ifdef RGFW_WIN95 /* for windows 95 testing (not that it really works) */ + #define RGFW_NO_MONITOR + #define RGFW_NO_PASSTHROUGH +#endif + +#if defined(RGFW_EXPORT) || defined(RGFW_IMPORT) + #if defined(_WIN32) + #if defined(__TINYC__) && (defined(RGFW_EXPORT) || defined(RGFW_IMPORT)) + #define __declspec(x) __attribute__((x)) + #endif + + #if defined(RGFW_EXPORT) + #define RGFWDEF __declspec(dllexport) + #else + #define RGFWDEF __declspec(dllimport) + #endif + #else + #if defined(RGFW_EXPORT) + #define RGFWDEF __attribute__((visibility("default"))) + #endif + #endif +#endif + +#ifndef RGFWDEF + #ifdef RGFW_C89 + #define RGFWDEF __inline + #else + #define RGFWDEF inline + #endif +#endif + +#ifndef RGFW_ENUM + #define RGFW_ENUM(type, name) type name; enum +#endif + + +#if defined(__cplusplus) && !defined(__EMSCRIPTEN__) + extern "C" { +#endif + + /* makes sure the header file part is only defined once by default */ +#ifndef RGFW_HEADER + +#define RGFW_HEADER + +#include +#ifndef RGFW_INT_DEFINED + #ifdef RGFW_USE_INT /* optional for any system that might not have stdint.h */ + typedef unsigned char u8; + typedef signed char i8; + typedef unsigned short u16; + typedef signed short i16; + typedef unsigned long int u32; + typedef signed long int i32; + typedef unsigned long long u64; + typedef signed long long i64; + #else /* use stdint standard types instead of c ""standard"" types */ + #include + + typedef uint8_t u8; + typedef int8_t i8; + typedef uint16_t u16; + typedef int16_t i16; + typedef uint32_t u32; + typedef int32_t i32; + typedef uint64_t u64; + typedef int64_t i64; + #endif + #define RGFW_INT_DEFINED +#endif + +#ifndef RGFW_BOOL_DEFINED + #define RGFW_BOOL_DEFINED + typedef u8 RGFW_bool; +#endif + +#define RGFW_BOOL(x) (RGFW_bool)((x) ? RGFW_TRUE : RGFW_FALSE) /* force an value to be 0 or 1 */ +#define RGFW_TRUE (RGFW_bool)1 +#define RGFW_FALSE (RGFW_bool)0 + +/* these OS macros look better & are standardized */ +/* plus it helps with cross-compiling */ + +#ifdef __EMSCRIPTEN__ + #define RGFW_WASM + + #if !defined(RGFW_NO_API) && !defined(RGFW_WEBGPU) + #define RGFW_OPENGL + #endif + + #ifdef RGFW_EGL + #undef RGFW_EGL + #endif + + #include + #include + + #ifdef RGFW_WEBGPU + #include + #endif +#endif + +#if defined(RGFW_X11) && defined(__APPLE__) && !defined(RGFW_CUSTOM_BACKEND) + #define RGFW_MACOS_X11 + #define RGFW_UNIX + #undef __APPLE__ +#endif + +#if defined(_WIN32) && !defined(RGFW_X11) && !defined(RGFW_UNIX) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND) /* (if you're using X11 on windows some how) */ + #define RGFW_WINDOWS + /* make sure the correct architecture is defined */ + #if defined(_WIN64) + #define _AMD64_ + #undef _X86_ + #else + #undef _AMD64_ + #ifndef _X86_ + #define _X86_ + #endif + #endif + + #ifndef RGFW_NO_XINPUT + #ifdef __MINGW32__ /* try to find the right header */ + #include + #else + #include + #endif + #endif +#endif +#if defined(RGFW_WAYLAND) + #define RGFW_DEBUG /* wayland will be in debug mode by default for now */ + #if !defined(RGFW_NO_API) && (!defined(RGFW_BUFFER) || defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) + #define RGFW_EGL + #define RGFW_OPENGL + #define RGFW_UNIX + #include + #endif + + #include +#endif +#if !defined(RGFW_NO_X11) && !defined(RGFW_NO_X11) && (defined(__unix__) || defined(RGFW_MACOS_X11) || defined(RGFW_X11)) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND) + #define RGFW_MACOS_X11 + #define RGFW_X11 + #define RGFW_UNIX + #include + #include +#elif defined(__APPLE__) && !defined(RGFW_MACOS_X11) && !defined(RGFW_X11) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND) + #define RGFW_MACOS + #if !defined(RGFW_BUFFER_BGR) + #define RGFW_BUFFER_BGR + #else + #undef RGFW_BUFFER_BGR + #endif +#endif + +#if (defined(RGFW_OPENGL_ES1) || defined(RGFW_OPENGL_ES2) || defined(RGFW_OPENGL_ES3)) && !defined(RGFW_EGL) + #define RGFW_EGL +#endif + +#if !defined(RGFW_OSMESA) && !defined(RGFW_EGL) && !defined(RGFW_OPENGL) && !defined(RGFW_DIRECTX) && !defined(RGFW_BUFFER) && !defined(RGFW_NO_API) + #define RGFW_OPENGL +#endif + +#ifdef RGFW_EGL + #include +#elif defined(RGFW_OSMESA) + #ifdef RGFW_WINDOWS + #define OEMRESOURCE + #include + #ifndef GLAPIENTRY + #define GLAPIENTRY APIENTRY + #endif + #ifndef GLAPI + #define GLAPI WINGDIAPI + #endif + #endif + + #ifndef __APPLE__ + #include + #else + #include + #endif +#endif + +#if (defined(RGFW_OPENGL) || defined(RGFW_WEGL)) && defined(_MSC_VER) + #pragma comment(lib, "opengl32") +#endif + +#if defined(RGFW_OPENGL) && defined(RGFW_X11) + #ifndef GLX_MESA_swap_control + #define GLX_MESA_swap_control + #endif + #include /* GLX defs, xlib.h, gl.h */ +#endif + +#define RGFW_COCOA_FRAME_NAME NULL + +/*! (unix) Toggle use of wayland. This will be on by default if you use `RGFW_WAYLAND` (if you don't use RGFW_WAYLAND, you don't expose WAYLAND functions) + this is mostly used to allow you to force the use of XWayland +*/ +RGFWDEF void RGFW_useWayland(RGFW_bool wayland); +RGFWDEF RGFW_bool RGFW_usingWayland(void); +/* + regular RGFW stuff +*/ + +#define RGFW_key u8 + +typedef RGFW_ENUM(u8, RGFW_eventType) { + /*! event codes */ + RGFW_eventNone = 0, /*!< no event has been sent */ + RGFW_keyPressed, /* 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.key + !!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.keyMod holds the current keyMod + 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 + + RGFW_event.button holds which mouse button was pressed + */ + RGFW_gamepadConnected, /*!< a gamepad was connected */ + RGFW_gamepadDisconnected, /*!< a gamepad was disconnected */ + RGFW_gamepadButtonPressed, /*!< a gamepad button was pressed */ + RGFW_gamepadButtonReleased, /*!< a gamepad button was released */ + RGFW_gamepadAxisMove, /*!< an axis of a gamepad was moved */ + /*! gamepad event note + RGFW_event.gamepad holds which gamepad was altered, if any + RGFW_event.button holds which gamepad button was pressed + + RGFW_event.axis holds the data of all the axises + RGFW_event.axisesCount says how many axises there are + */ + RGFW_windowMoved, /*!< the window was moved (by the user) */ + RGFW_windowResized, /*!< the window was resized (by the user), [on WASM 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_DNDInit, /*!< 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 + + This is also the size of the array which stores all the dropped file string, + RGFW_event.droppedFiles + */ + RGFW_windowMaximized, /*!< the window was maximized */ + RGFW_windowMinimized, /*!< the window was minimized */ + RGFW_windowRestored, /*!< the window was restored */ + RGFW_scaleUpdated /*!< content scale factor changed */ +}; + +/*! mouse button codes (RGFW_event.button) */ +typedef RGFW_ENUM(u8, RGFW_mouseButton) { + RGFW_mouseLeft = 0, /*!< left mouse button is pressed */ + RGFW_mouseMiddle, /*!< mouse-wheel-button is pressed */ + RGFW_mouseRight, /*!< right mouse button is pressed */ + RGFW_mouseScrollUp, /*!< mouse wheel is scrolling up */ + RGFW_mouseScrollDown, /*!< mouse wheel is scrolling down */ + RGFW_mouseMisc1, RGFW_mouseMisc2, RGFW_mouseMisc3, RGFW_mouseMisc4, RGFW_mouseMisc5, + RGFW_mouseFinal +}; + +#ifndef RGFW_MAX_PATH +#define RGFW_MAX_PATH 260 /* max length of a path (for dnd) */ +#endif +#ifndef RGFW_MAX_DROPS +#define RGFW_MAX_DROPS 260 /* max items you can drop at once */ +#endif + +#define RGFW_BIT(x) (1 << x) + +/* for RGFW_event.lockstate */ +typedef RGFW_ENUM(u8, RGFW_keymod) { + RGFW_modCapsLock = RGFW_BIT(0), + RGFW_modNumLock = RGFW_BIT(1), + RGFW_modControl = RGFW_BIT(2), + RGFW_modAlt = RGFW_BIT(3), + RGFW_modShift = RGFW_BIT(4), + RGFW_modSuper = RGFW_BIT(5), + RGFW_modScrollLock = RGFW_BIT(6) +}; + +/*! gamepad button codes (based on xbox/playstation), you may need to change these values per controller */ +typedef RGFW_ENUM(u8, RGFW_gamepadCodes) { + RGFW_gamepadNone = 0, /*!< or PS X button */ + RGFW_gamepadA, /*!< or PS X button */ + RGFW_gamepadB, /*!< or PS circle button */ + RGFW_gamepadY, /*!< or PS triangle button */ + RGFW_gamepadX, /*!< or PS square button */ + RGFW_gamepadStart, /*!< start button */ + RGFW_gamepadSelect, /*!< select button */ + RGFW_gamepadHome, /*!< home button */ + RGFW_gamepadUp, /*!< dpad up */ + RGFW_gamepadDown, /*!< dpad down */ + RGFW_gamepadLeft, /*!< dpad left */ + RGFW_gamepadRight, /*!< dpad right */ + RGFW_gamepadL1, /*!< left bump */ + RGFW_gamepadL2, /*!< left trigger */ + RGFW_gamepadR1, /*!< right bumper */ + RGFW_gamepadR2, /*!< right trigger */ + RGFW_gamepadL3, /* left thumb stick */ + RGFW_gamepadR3, /*!< right thumb stick */ + RGFW_gamepadFinal +}; + +/*! 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 */ +#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 */ +#ifndef RGFW_area + typedef struct { u32 w, h; } RGFW_area; +#endif + +#if defined(__cplusplus) && !defined(__APPLE__) +#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} +#else +#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)} +#endif + +#ifndef RGFW_NO_MONITOR + /* monitor mode data | can be changed by the user (with functions)*/ + typedef struct RGFW_monitorMode { + RGFW_area area; /*!< monitor workarea size */ + u32 refreshRate; /*!< monitor refresh rate */ + u8 red, blue, green; + } RGFW_monitorMode; + + /*! structure for monitor data */ + typedef struct RGFW_monitor { + i32 x, y; /*!< x - y of the monitor workarea */ + char name[128]; /*!< monitor name */ + float scaleX, scaleY; /*!< monitor content scale */ + float pixelRatio; /*!< pixel ratio for monitor (1.0 for regular, 2.0 for hiDPI) */ + float physW, physH; /*!< monitor physical size in inches */ + + RGFW_monitorMode mode; + } RGFW_monitor; + + /*! get an array of all the monitors (max 6) */ + RGFWDEF RGFW_monitor* RGFW_getMonitors(size_t* len); + /*! get the primary monitor */ + RGFWDEF RGFW_monitor RGFW_getPrimaryMonitor(void); + + typedef RGFW_ENUM(u8, RGFW_modeRequest) { + RGFW_monitorScale = RGFW_BIT(0), /*!< scale the monitor size */ + RGFW_monitorRefresh = RGFW_BIT(1), /*!< change the refresh rate */ + RGFW_monitorRGB = RGFW_BIT(2), /*!< change the monitor RGB bits size */ + RGFW_monitorAll = RGFW_monitorScale | RGFW_monitorRefresh | RGFW_monitorRGB + }; + + /*! request a specific mode */ + RGFWDEF RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request); + /*! check if 2 monitor modes are the same */ + RGFWDEF RGFW_bool RGFW_monitorModeCompare(RGFW_monitorMode mon, RGFW_monitorMode mon2, RGFW_modeRequest request); +#endif + +/* RGFW mouse loading */ +typedef void RGFW_mouse; + +/*!< loads mouse icon from bitmap (similar to RGFW_window_setIcon). Icon NOT resized by default */ +RGFWDEF RGFW_mouse* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels); +/*!< frees RGFW_mouse data */ +RGFWDEF void RGFW_freeMouse(RGFW_mouse* mouse); + +/* 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 { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_point point; /*!< mouse x, y of event (or drop point) */ + RGFW_point vector; /*!< raw mouse movement */ + float scaleX, scaleY; /*!< DPI scaling */ + + RGFW_key key; /*!< the physical key of the event, refers to where key is physically !!Keycodes defined at the bottom of the RGFW_HEADER part of this file!! */ + u8 keyChar; /*!< mapped key char of the event */ + + RGFW_bool repeat; /*!< key press event repeated (the key is being held) */ + RGFW_keymod keyMod; + + u8 button; /* !< which mouse (or gamepad) button was pressed */ + double scroll; /*!< the raw mouse scroll value */ + + u16 gamepad; /*! which gamepad this event applies to (if applicable to any) */ + u8 axisesCount; /*!< number of axises */ + + u8 whichAxis; /* which axis was effected */ + RGFW_point axis[4]; /*!< x, y of axises (-100 to 100) */ + + /*! drag and drop data */ + /* 260 max paths with a max length of 260 */ + char** droppedFiles; /*!< dropped files */ + size_t droppedFilesCount; /*!< house many files were dropped */ + + void* _win; /*!< the window this event applies too (for event queue events) */ +} RGFW_event; + +/*! source data for the window (used by the APIs) */ +#ifdef RGFW_WINDOWS +typedef struct RGFW_window_src { + HWND window; /*!< source window */ + HDC hdc; /*!< source HDC */ + u32 hOffset; /*!< height offset for window */ + HICON hIconSmall, hIconBig; /*!< source window icons */ + #if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL) + HGLRC ctx; /*!< source graphics context */ + #elif defined(RGFW_OSMESA) + OSMesaContext ctx; + #elif defined(RGFW_EGL) + EGLSurface EGL_surface; + EGLDisplay EGL_display; + EGLContext EGL_context; + #endif + + #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + HDC hdcMem; + HBITMAP bitmap; + u8* bitmapBits; + #endif + RGFW_area maxSize, minSize, aspectRatio; /*!< for setting max/min resize (RGFW_WINDOWS) */ +} RGFW_window_src; +#elif defined(RGFW_UNIX) +typedef struct RGFW_window_src { +#if defined(RGFW_X11) + Display* display; /*!< source display */ + Window window; /*!< source window */ + #if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL) + GLXContext ctx; /*!< source graphics context */ + GLXFBConfig bestFbc; + #elif defined(RGFW_OSMESA) + OSMesaContext ctx; + #elif defined(RGFW_EGL) + EGLSurface EGL_surface; + EGLDisplay EGL_display; + EGLContext EGL_context; + #endif + + #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + XImage* bitmap; + #endif + GC gc; + XVisualInfo visual; + #ifdef RGFW_ADVANCED_SMOOTH_RESIZE + i64 counter_value; + XID counter; + #endif +#endif /* RGFW_X11 */ +#if defined(RGFW_WAYLAND) + struct wl_display* wl_display; + struct wl_surface* surface; + struct wl_buffer* wl_buffer; + struct wl_keyboard* keyboard; + + struct wl_compositor* compositor; + struct xdg_surface* xdg_surface; + struct xdg_toplevel* xdg_toplevel; + struct zxdg_toplevel_decoration_v1* decoration; + struct xdg_wm_base* xdg_wm_base; + struct wl_shm* shm; + struct wl_seat *seat; + u8* buffer; + #if defined(RGFW_EGL) + struct wl_egl_window* eglWindow; + #endif + #if defined(RGFW_EGL) && !defined(RGFW_X11) + EGLSurface EGL_surface; + EGLDisplay EGL_display; + EGLContext EGL_context; + #elif defined(RGFW_OSMESA) && !defined(RGFW_X11) + OSMesaContext ctx; + #endif +#endif /* RGFW_WAYLAND */ +} RGFW_window_src; +#endif /* RGFW_UNIX */ +#if defined(RGFW_MACOS) +typedef struct RGFW_window_src { + void* window; +#if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL) + void* ctx; /*!< source graphics context */ +#elif defined(RGFW_OSMESA) + OSMesaContext ctx; +#elif defined(RGFW_EGL) + EGLSurface EGL_surface; + EGLDisplay EGL_display; + EGLContext EGL_context; +#endif + + void* view; /* apple viewpoint thingy */ + void* mouse; +#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) +#endif +} RGFW_window_src; +#elif defined(RGFW_WASM) +typedef struct RGFW_window_src { + #if defined(RGFW_WEBGPU) + WGPUInstance ctx; + WGPUDevice device; + WGPUQueue queue; + #elif defined(RGFW_OSMESA) + OSMesaContext ctx; + #else + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx; + #endif +} RGFW_window_src; +#endif + +/*! Optional arguments for making a windows */ +typedef RGFW_ENUM(u32, RGFW_windowFlags) { + RGFW_windowNoInitAPI = RGFW_BIT(0), /* do NOT init an API (including the software rendering buffer) (mostly for bindings. you can also use `#define RGFW_NO_API`) */ + RGFW_windowNoBorder = RGFW_BIT(1), /*!< the window doesn't have a border */ + RGFW_windowNoResize = RGFW_BIT(2), /*!< the window cannot be resized by the user */ + RGFW_windowAllowDND = RGFW_BIT(3), /*!< the window supports drag and drop */ + RGFW_windowHideMouse = RGFW_BIT(4), /*! the window should hide the mouse (can be toggled later on using `RGFW_window_mouseShow`) */ + RGFW_windowFullscreen = RGFW_BIT(5), /*!< the window is fullscreen by default */ + RGFW_windowTransparent = RGFW_BIT(6), /*!< the window is transparent (only properly works on X11 and MacOS, although it's meant for for windows) */ + RGFW_windowCenter = RGFW_BIT(7), /*! center the window on the screen */ + RGFW_windowOpenglSoftware = RGFW_BIT(8), /*! use OpenGL software rendering */ + RGFW_windowCocoaCHDirToRes = RGFW_BIT(9), /*! (cocoa only), change directory to resource folder */ + RGFW_windowScaleToMonitor = RGFW_BIT(10), /*! scale the window to the screen */ + RGFW_windowHide = RGFW_BIT(11), /*! the window is hidden */ + RGFW_windowMaximize = RGFW_BIT(12), + RGFW_windowCenterCursor = RGFW_BIT(13), + RGFW_windowFloating = RGFW_BIT(14), /*!< create a floating window */ + RGFW_windowFreeOnClose = RGFW_BIT(15), /*!< free (RGFW_window_close) the RGFW_window struct when the window is closed (by the end user) */ + RGFW_windowFocusOnShow = RGFW_BIT(16), /*!< focus the window when it's shown */ + RGFW_windowMinimize = RGFW_BIT(17), /*!< focus the window when it's shown */ + RGFW_windowFocus = RGFW_BIT(18), /*!< if the window is in focus */ + RGFW_windowedFullscreen = RGFW_windowNoBorder | RGFW_windowMaximize +}; + +typedef struct RGFW_window { + 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) */ + /* when rendering using RGFW_BUFFER, the buffer is in the RGBA format */ + RGFW_area bufferSize; +#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_point _lastMousePoint; /*!< last cusor point (for raw mouse data) */ + + u32 _flags; /*!< windows flags (for RGFW to check) */ + RGFW_rect _oldRect; /*!< rect before fullscreen */ +} RGFW_window; /*!< window structure for managing the window */ + +#if defined(RGFW_X11) || defined(RGFW_MACOS) + typedef u64 RGFW_thread; /*!< thread type unix */ +#else + typedef void* RGFW_thread; /*!< thread type for windows */ +#endif + +/*! scale monitor to window size */ +RGFWDEF RGFW_bool RGFW_monitor_scaleToWindow(RGFW_monitor mon, RGFW_window* win); + +/** * @defgroup Window_management +* @{ */ + + +/*! + * the class name for X11 and WinAPI. apps with the same class will be grouped by the WM + * by default the class name will == the root window's name +*/ +RGFWDEF void RGFW_setClassName(const char* name); +RGFWDEF void RGFW_setXInstName(const char* name); /*!< X11 instance name (window name will by used by default) */ + +/*! (cocoa only) change directory to resource folder */ +RGFWDEF void RGFW_moveToMacOSResourceDir(void); + +/* NOTE: (windows) if the executable has an icon resource named RGFW_ICON, it will be set as the initial icon for the window */ + +RGFWDEF RGFW_window* RGFW_createWindow( + const char* name, /* name of the window */ + RGFW_rect rect, /* rect of window */ + RGFW_windowFlags flags /* extra arguments ((u32)0 means no flags used)*/ +); /*!< function to create a window and struct */ + +RGFWDEF RGFW_window* RGFW_createWindowPtr( + const char* name, /* name of the window */ + RGFW_rect rect, /* rect of window */ + RGFW_windowFlags flags, /* extra arguments (NULL / (u32)0 means no flags used) */ + RGFW_window* win /* ptr to the window struct you want to use */ +); /*!< function to create a window (without allocating a window struct) */ + +RGFWDEF void RGFW_window_initBuffer(RGFW_window* win); +RGFWDEF void RGFW_window_initBufferSize(RGFW_window* win, RGFW_area area); +RGFWDEF void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area); + +/*! set the window flags (will undo flags if they don't match the old ones) */ +RGFWDEF void RGFW_window_setFlags(RGFW_window* win, RGFW_windowFlags); + +/*! 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 + + ex. + + while (RGFW_window_checkEvent(win) != NULL) [this keeps checking events until it reaches the last one] + + this function is optional if you choose to use event callbacks, + although you still need some way to tell RGFW to process events eg. `RGFW_window_checkEvents` +*/ + +RGFWDEF 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 the function to keep checking for events even after `RGFW_window_checkEvent == NULL` + if waitMS == 0, the loop will not wait for events + if waitMS > 0, the loop will wait that many miliseconds after there are no more events until it returns + if waitMS == -1 or waitMS == the max size of an unsigned 32-bit int, the loop will not return until it gets another event +*/ +typedef RGFW_ENUM(i32, RGFW_eventWait) { + RGFW_eventNoWait = 0, + RGFW_eventWaitNext = -1 +}; + +/*! 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); + +/*! window managment functions */ +RGFWDEF void RGFW_window_close(RGFW_window* win); /*!< close the window and free leftover data */ + +/*! move a window to a given point */ +RGFWDEF void RGFW_window_move(RGFW_window* win, + RGFW_point v /*!< new pos */ +); + +#ifndef RGFW_NO_MONITOR + /*! move window 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, /*!< source window */ + RGFW_area a /*!< new size */ +); + +/*! set window aspect ratio */ +RGFWDEF void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a); +/*! set the minimum dimensions of a window */ +RGFWDEF void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a); +/*! set the maximum dimensions of a window */ +RGFWDEF void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a); + +RGFWDEF void RGFW_window_focus(RGFW_window* win); /*!< sets the focus to this window */ +RGFWDEF RGFW_bool RGFW_window_isInFocus(RGFW_window* win); /*!< checks the focus to this window */ +RGFWDEF void RGFW_window_raise(RGFW_window* win); /*!< raise the window (to the top) */ +RGFWDEF void RGFW_window_maximize(RGFW_window* win); /*!< maximize the window */ +RGFWDEF void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen); /*!< turn fullscreen on / off for a window */ +RGFWDEF void RGFW_window_center(RGFW_window* win); /*!< center the window */ +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_setFloating(RGFW_window* win, RGFW_bool floating); /*!< make the window a floating window */ +RGFWDEF void RGFW_window_setOpacity(RGFW_window* win, u8 opacity); /*!< sets the opacity of a window */ + +/*! if the window should have a border or not (borderless) based on bool value of `border` */ +RGFWDEF void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border); +RGFWDEF RGFW_bool RGFW_window_borderless(RGFW_window* win); + +/*! turn on / off dnd (RGFW_windowAllowDND stil must be passed to the window)*/ +RGFWDEF void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow); +/*! check if DND is allowed */ +RGFWDEF RGFW_bool RGFW_window_allowsDND(RGFW_window* win); + + +#ifndef RGFW_NO_PASSTHROUGH + /*! turn on / off mouse passthrough */ + RGFWDEF void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough); +#endif + +/*! rename window to a given string */ +RGFWDEF void RGFW_window_setName(RGFW_window* win, + const char* name +); + +RGFWDEF RGFW_bool RGFW_window_setIcon(RGFW_window* win, /*!< source window */ + u8* icon /*!< icon bitmap */, + RGFW_area a /*!< width and height of the bitmap */, + i32 channels /*!< how many channels the bitmap has (rgb : 3, rgba : 4) */ +); /*!< image MAY be resized by default, set both the taskbar and window icon */ + +typedef RGFW_ENUM(u8, RGFW_icon) { + RGFW_iconTaskbar = RGFW_BIT(0), + RGFW_iconWindow = RGFW_BIT(1), + RGFW_iconBoth = RGFW_iconTaskbar | RGFW_iconWindow +}; +RGFWDEF RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* icon, RGFW_area a, i32 channels, u8 type); + +/*!< sets mouse to RGFW_mouse icon (loaded from a bitmap struct) */ +RGFWDEF void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse); + +/*!< 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 RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse); + +RGFWDEF RGFW_bool 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 becomes raw mouse movement data + + 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 */ +RGFWDEF void RGFW_window_mouseUnhold(RGFW_window* win); + +/*! hide the window */ +RGFWDEF void RGFW_window_hide(RGFW_window* win); +/*! show the window */ +RGFWDEF void RGFW_window_show(RGFW_window* win); + +/* + makes it so `RGFW_window_shouldClose` returns true or overrides a window close + by modifying window flags +*/ +RGFWDEF void RGFW_window_setShouldClose(RGFW_window* win, RGFW_bool shouldClose); + +/*! where the mouse is on the screen */ +RGFWDEF RGFW_point RGFW_getGlobalMousePoint(void); + +/*! where the mouse is on the window */ +RGFWDEF RGFW_point RGFW_window_getMousePoint(RGFW_window* win); + +/*! show the mouse or hide the mouse */ +RGFWDEF void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show); +/*! if the mouse is hidden */ +RGFWDEF RGFW_bool RGFW_window_mouseHidden(RGFW_window* win); +/*! move the mouse to a given point */ +RGFWDEF void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v); + +/*! if the window should close (RGFW_close was sent or escape was pressed) */ +RGFWDEF RGFW_bool RGFW_window_shouldClose(RGFW_window* win); +/*! if the window is fullscreen */ +RGFWDEF RGFW_bool RGFW_window_isFullscreen(RGFW_window* win); +/*! if the window is hidden */ +RGFWDEF RGFW_bool RGFW_window_isHidden(RGFW_window* win); +/*! if the window is minimized */ +RGFWDEF RGFW_bool RGFW_window_isMinimized(RGFW_window* win); +/*! if the window is maximized */ +RGFWDEF RGFW_bool RGFW_window_isMaximized(RGFW_window* win); +/*! if the window is floating */ +RGFWDEF RGFW_bool RGFW_window_isFloating(RGFW_window* win); +/** @} */ + +/** * @defgroup Monitor +* @{ */ + +#ifndef RGFW_NO_MONITOR +/* + scale the window to the monitor. + This is run by default if the user uses the arg `RGFW_scaleToMonitor` during window creation +*/ +RGFWDEF void RGFW_window_scaleToMonitor(RGFW_window* win); +/*! get the struct of the window's monitor */ +RGFWDEF RGFW_monitor RGFW_window_getMonitor(RGFW_window* win); +#endif + +/** @} */ + +/** * @defgroup Input +* @{ */ + +/*! 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 RGFW_bool RGFW_isPressed(RGFW_window* win, RGFW_key key); /*!< if key is pressed (key code)*/ + +RGFWDEF RGFW_bool RGFW_wasPressed(RGFW_window* win, RGFW_key key); /*!< if key was pressed (checks previous state only) (key code) */ + +RGFWDEF RGFW_bool RGFW_isHeld(RGFW_window* win, RGFW_key key); /*!< if key is held (key code) */ +RGFWDEF RGFW_bool RGFW_isReleased(RGFW_window* win, RGFW_key key); /*!< if key is released (key code) */ + +/* if a key is pressed and then released, pretty much the same as RGFW_isReleased */ +RGFWDEF RGFW_bool RGFW_isClicked(RGFW_window* win, RGFW_key key /*!< key code */); + +/*! if a mouse button is pressed */ +RGFWDEF RGFW_bool RGFW_isMousePressed(RGFW_window* win, RGFW_mouseButton button /*!< mouse button code */ ); +/*! if a mouse button is held */ +RGFWDEF RGFW_bool RGFW_isMouseHeld(RGFW_window* win, RGFW_mouseButton button /*!< mouse button code */ ); +/*! if a mouse button was released */ +RGFWDEF RGFW_bool RGFW_isMouseReleased(RGFW_window* win, RGFW_mouseButton button /*!< mouse button code */ ); +/*! if a mouse button was pressed (checks previous state only) */ +RGFWDEF RGFW_bool RGFW_wasMousePressed(RGFW_window* win, RGFW_mouseButton button /*!< mouse button code */ ); +/** @} */ + +/** * @defgroup Clipboard +* @{ */ +typedef ptrdiff_t RGFW_ssize_t; + +RGFWDEF const char* RGFW_readClipboard(size_t* size); /*!< read clipboard data */ +/*! read clipboard data or send a NULL str to just get the length of the clipboard data */ +RGFWDEF RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity); +RGFWDEF void RGFW_writeClipboard(const char* text, u32 textLen); /*!< write text to the clipboard */ +/** @} */ + + + +/** * @defgroup error handling +* @{ */ +typedef RGFW_ENUM(u8, RGFW_debugType) { + RGFW_typeError = 0, RGFW_typeWarning, RGFW_typeInfo +}; + +typedef RGFW_ENUM(u8, RGFW_errorCode) { + RGFW_noError = 0, /*!< no error */ + RGFW_errOpenglContext, RGFW_errEGLContext, /*!< error with the OpenGL context */ + RGFW_errWayland, + RGFW_errDirectXContext, + RGFW_errIOKit, + RGFW_errClipboard, + RGFW_errFailedFuncLoad, + RGFW_errBuffer, + RGFW_infoMonitor, RGFW_infoWindow, RGFW_infoBuffer, RGFW_infoGlobal, RGFW_infoOpenGL, + RGFW_warningWayland, RGFW_warningOpenGL +}; + +typedef struct RGFW_debugContext { RGFW_window* win; RGFW_monitor monitor; u32 srcError; } RGFW_debugContext; + +#if defined(__cplusplus) && !defined(__APPLE__) +#define RGFW_DEBUG_CTX(win, err) {win, { 0 }, err} +#define RGFW_DEBUG_CTX_MON(monitor) {_RGFW.root, monitor, 0} +#else +#define RGFW_DEBUG_CTX(win, err) (RGFW_debugContext){win, (RGFW_monitor){ 0 }, err} +#define RGFW_DEBUG_CTX_MON(monitor) (RGFW_debugContext){_RGFW.root, monitor, 0} +#endif + +typedef void (* RGFW_debugfunc)(RGFW_debugType type, RGFW_errorCode err, RGFW_debugContext ctx, const char* msg); +RGFWDEF RGFW_debugfunc RGFW_setDebugCallback(RGFW_debugfunc func); +RGFWDEF void RGFW_sendDebugInfo(RGFW_debugType type, RGFW_errorCode err, RGFW_debugContext ctx, const char* msg); +/** @} */ + +/** + + + event callbacks. + These are completely optional, so you can use the normal + RGFW_checkEvent() method if you prefer that + +* @defgroup Callbacks +* @{ +*/ + +/*! RGFW_windowMoved, the window and its new rect value */ +typedef void (* RGFW_windowMovedfunc)(RGFW_window* win, RGFW_rect r); +/*! RGFW_windowResized, the window and its new rect value */ +typedef void (* RGFW_windowResizedfunc)(RGFW_window* win, RGFW_rect r); +/*! RGFW_windowRestored, the window and its new rect value */ +typedef void (* RGFW_windowRestoredfunc)(RGFW_window* win, RGFW_rect r); +/*! RGFW_windowMaximized, the window and its new rect value */ +typedef void (* RGFW_windowMaximizedfunc)(RGFW_window* win, RGFW_rect r); +/*! RGFW_windowMinimized, the window and its new rect value */ +typedef void (* RGFW_windowMinimizedfunc)(RGFW_window* win, RGFW_rect r); +/*! 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 in focus */ +typedef void (* RGFW_focusfunc)(RGFW_window* win, RGFW_bool inFocus); +/*! 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, RGFW_bool status); +/*! 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_point vector); +/*! RGFW_DNDInit, 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 */ +typedef void (* RGFW_windowRefreshfunc)(RGFW_window* win); +/*! RGFW_keyPressed / RGFW_keyReleased, the window that got the event, the mapped key, the physical key, the string version, the state of the mod keys, if it was a press (else it's a release) */ +typedef void (* RGFW_keyfunc)(RGFW_window* win, u8 key, u8 keyChar, RGFW_keymod keyMod, RGFW_bool 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) */ +typedef void (* RGFW_mouseButtonfunc)(RGFW_window* win, RGFW_mouseButton button, double scroll, RGFW_bool pressed); +/*! RGFW_gamepadButtonPressed, 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_gamepadButtonfunc)(RGFW_window* win, u16 gamepad, u8 button, RGFW_bool pressed); +/*! RGFW_gamepadAxisMove, the window that got the event, the gamepad in question, the axis values and the axis count */ +typedef void (* RGFW_gamepadAxisfunc)(RGFW_window* win, u16 gamepad, RGFW_point axis[2], u8 axisesCount, u8 whichAxis); +/*! RGFW_gamepadConnected / RGFW_gamepadDisconnected, the window that got the event, the gamepad in question, if the controller was connected (else it was disconnected) */ +typedef void (* RGFW_gamepadfunc)(RGFW_window* win, u16 gamepad, RGFW_bool connected); +/*! RGFW_dnd, the window that had the drop, the drop data and the number of files dropped */ +typedef void (* RGFW_dndfunc)(RGFW_window* win, char** droppedFiles, size_t droppedFilesCount); +/*! RGFW_scaleUpdated, the window the event was sent to, content scaleX, content scaleY */ +typedef void (* RGFW_scaleUpdatedfunc)(RGFW_window* win, float scaleX, float scaleY); + +/*! set callback for a window move event. Returns previous callback function (if it was set) */ +RGFWDEF RGFW_windowMovedfunc RGFW_setWindowMovedCallback(RGFW_windowMovedfunc func); +/*! set callback for a window resize event. Returns previous callback function (if it was set) */ +RGFWDEF RGFW_windowResizedfunc RGFW_setWindowResizedCallback(RGFW_windowResizedfunc 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_gamepadButtonfunc RGFW_setGamepadButtonCallback(RGFW_gamepadButtonfunc func); +/*! set callback for a gamepad axis move event. Returns previous callback function (if it was set) */ +RGFWDEF RGFW_gamepadAxisfunc RGFW_setGamepadAxisCallback(RGFW_gamepadAxisfunc func); +/*! set callback for when a controller is connected or disconnected. Returns the previous callback function (if it was set) */ +RGFWDEF RGFW_gamepadfunc RGFW_setGamepadCallback(RGFW_gamepadfunc func); +/*! set call back for when window is maximized. Returns the previous callback function (if it was set) */ +RGFWDEF RGFW_windowResizedfunc RGFW_setWindowMaximizedCallback(RGFW_windowResizedfunc func); +/*! set call back for when window is minimized. Returns the previous callback function (if it was set) */ +RGFWDEF RGFW_windowResizedfunc RGFW_setWindowMinimizedCallback(RGFW_windowResizedfunc func); +/*! set call back for when window is restored. Returns the previous callback function (if it was set) */ +RGFWDEF RGFW_windowResizedfunc RGFW_setWindowRestoredCallback(RGFW_windowResizedfunc func); +/*! set callback for when the DPI changes. Returns previous callback function (if it was set) */ +RGFWDEF RGFW_scaleUpdatedfunc RGFW_setScaleUpdatedCallback(RGFW_scaleUpdatedfunc func); +/** @} */ + +/** * @defgroup Threads +* @{ */ + +#ifndef RGFW_NO_THREADS +/*! threading functions */ + +/*! NOTE! (for X11/linux) : if you define a window in a thread, it must be run after the original thread's window is created or else there will be a memory error */ +/* + I'd suggest you use sili's threading functions instead + if you're going to use sili + which is a good idea generally +*/ + +#if defined(__unix__) || defined(__APPLE__) || defined(RGFW_WASM) || defined(RGFW_CUSTOM_BACKEND) + typedef void* (* RGFW_threadFunc_ptr)(void*); +#else + typedef DWORD (__stdcall *RGFW_threadFunc_ptr) (LPVOID lpThreadParameter); +#endif + +RGFWDEF RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args); /*!< create a thread */ +RGFWDEF void RGFW_cancelThread(RGFW_thread thread); /*!< cancels a thread */ +RGFWDEF void RGFW_joinThread(RGFW_thread thread); /*!< join thread to current thread */ +RGFWDEF void RGFW_setThreadPriority(RGFW_thread thread, u8 priority); /*!< sets the priority priority */ +#endif + +/** @} */ + +/** * @defgroup gamepad +* @{ */ + +typedef RGFW_ENUM(u8, RGFW_gamepadType) { + RGFW_gamepadMicrosoft = 0, RGFW_gamepadSony, RGFW_gamepadNintendo, RGFW_gamepadLogitech, RGFW_gamepadUnknown +}; + +/*! gamepad count starts at 0*/ +RGFWDEF u32 RGFW_isPressedGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button); +RGFWDEF u32 RGFW_isReleasedGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button); +RGFWDEF u32 RGFW_isHeldGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button); +RGFWDEF u32 RGFW_wasPressedGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button); +RGFWDEF RGFW_point RGFW_getGamepadAxis(RGFW_window* win, u16 controller, u16 whichAxis); +RGFWDEF const char* RGFW_getGamepadName(RGFW_window* win, u16 controller); +RGFWDEF size_t RGFW_getGamepadCount(RGFW_window* win); +RGFWDEF RGFW_gamepadType RGFW_getGamepadType(RGFW_window* win, u16 controller); + +/** @} */ + +/** * @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); + +/*! get current RGFW window graphics context */ +RGFWDEF RGFW_window* RGFW_getCurrent(void); + +/* supports openGL, directX, OSMesa, EGL and software rendering */ +RGFWDEF void RGFW_window_swapBuffers(RGFW_window* win); /*!< swap the rendering buffer */ +RGFWDEF void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval); +/*!< render the software rendering buffer (this is called by RGFW_window_swapInterval) */ +RGFWDEF void RGFW_window_swapBuffers_software(RGFW_window* win); + +typedef void (*RGFW_proc)(void); /* function pointer equivalent of void* */ + +/*! native API functions */ +#if defined(RGFW_OPENGL) || defined(RGFW_EGL) +/*!< create an opengl context for the RGFW window, run by createWindow by default (unless the RGFW_windowNoInitAPI is included) */ +RGFWDEF void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software); +/*!< called by `RGFW_window_close` by default (unless the RGFW_windowNoInitAPI is set) */ +RGFWDEF void RGFW_window_freeOpenGL(RGFW_window* win); + +/*! OpenGL init hints */ +typedef RGFW_ENUM(u8, RGFW_glHints) { + RGFW_glStencil = 0, /*!< set stencil buffer bit size (8 by default) */ + RGFW_glSamples, /*!< set number of sampiling buffers (4 by default) */ + RGFW_glStereo, /*!< use GL_STEREO (GL_FALSE by default) */ + RGFW_glAuxBuffers, /*!< number of aux buffers (0 by default) */ + RGFW_glDoubleBuffer, /*!< request double buffering */ + RGFW_glRed, RGFW_glGreen, RGFW_glBlue, RGFW_glAlpha, /*!< set RGBA bit sizes */ + RGFW_glDepth, + RGFW_glAccumRed, RGFW_glAccumGreen, RGFW_glAccumBlue,RGFW_glAccumAlpha, /*!< set accumulated RGBA bit sizes */ + RGFW_glSRGB, /*!< request sRGA */ + RGFW_glRobustness, /*!< request a robust context */ + RGFW_glDebug, /*!< request opengl debugging */ + RGFW_glNoError, /*!< request no opengl errors */ + RGFW_glReleaseBehavior, + RGFW_glProfile, + RGFW_glMajor, RGFW_glMinor, + RGFW_glFinalHint = 32, /*!< the final hint (not for setting) */ + RGFW_releaseFlush = 0, RGFW_glReleaseNone, /* RGFW_glReleaseBehavior options */ + RGFW_glCore = 0, RGFW_glCompatibility /*!< RGFW_glProfile options */ +}; +RGFWDEF void RGFW_setGLHint(RGFW_glHints hint, i32 value); +RGFWDEF RGFW_proc 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 */ +RGFWDEF void RGFW_window_swapBuffers_OpenGL(RGFW_window* win); /*!< swap opengl buffer (only) called by RGFW_window_swapInterval */ +void* RGFW_getCurrent_OpenGL(void); /*!< get the current context (OpenGL backend (GLX) (WGL) (EGL) (cocoa) (webgl))*/ +#endif +#ifdef RGFW_VULKAN + #if defined(RGFW_WAYLAND) && defined(RGFW_X11) + #define VK_USE_PLATFORM_WAYLAND_KHR + #define VK_USE_PLATFORM_XLIB_KHR + #define RGFW_VK_SURFACE ((RGFW_usingWayland()) ? ("VK_KHR_wayland_surface") : ("VK_KHR_xlib_surface")) + #elif defined(RGFW_WAYLAND) + #define VK_USE_PLATFORM_WAYLAND_KHR + #define VK_USE_PLATFORM_XLIB_KHR + #define RGFW_VK_SURFACE "VK_KHR_wayland_surface" + #elif defined(RGFW_X11) + #define VK_USE_PLATFORM_XLIB_KHR + #define RGFW_VK_SURFACE "VK_KHR_xlib_surface" + #elif defined(RGFW_WINDOWS) + #define VK_USE_PLATFORM_WIN32_KHR + #define OEMRESOURCE + #define RGFW_VK_SURFACE "VK_KHR_win32_surface" + #elif defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11) + #define VK_USE_PLATFORM_MACOS_MVK + #define RGFW_VK_SURFACE "VK_MVK_macos_surface" + #else + #define RGFW_VK_SURFACE NULL + #endif + +/* if you don't want to use the above macros */ +RGFWDEF const char** RGFW_getVKRequiredInstanceExtensions(size_t* count); /*!< gets (static) extension array (and size (which will be 2)) */ + +#include + +RGFWDEF VkResult RGFW_window_createVKSurface(RGFW_window* win, VkInstance instance, VkSurfaceKHR* surface); +RGFWDEF RGFW_bool RGFW_getVKPresentationSupport(VkInstance instance, VkPhysicalDevice physicalDevice, u32 queueFamilyIndex); +#endif +#ifdef RGFW_DIRECTX +#ifndef RGFW_WINDOWS + #undef RGFW_DIRECTX +#else + #define OEMRESOURCE + #include + + #ifndef __cplusplus + #define __uuidof(T) IID_##T + #endif +RGFWDEF int RGFW_window_createDXSwapChain(RGFW_window* win, IDXGIFactory* pFactory, IUnknown* pDevice, IDXGISwapChain** swapchain); +#endif +#endif + +/** @} */ + +/** * @defgroup Supporting +* @{ */ + +/*! optional init/deinit function */ +RGFWDEF i32 RGFW_init(void); /*!< is called by default when the first window is created by default */ +RGFWDEF void RGFW_deinit(void); /*!< is called by default when the last open window is closed */ + +RGFWDEF double RGFW_getTime(void); /*!< get time in seconds since RGFW_setTime, which ran when the first window is open */ +RGFWDEF u64 RGFW_getTimeNS(void); /*!< get time in nanoseconds RGFW_setTime, which ran when the first window is open */ +RGFWDEF void RGFW_sleep(u64 milisecond); /*!< sleep for a set time */ +RGFWDEF void RGFW_setTime(double time); /*!< set timer in seconds */ +RGFWDEF u64 RGFW_getTimerValue(void); /*!< get API timer value */ +RGFWDEF u64 RGFW_getTimerFreq(void); /*!< get API time freq */ + +/*< 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_checkFPS(double startTime, u32 frameCount, u32 fpsCap); + +/*!< change which window is the root window */ +RGFWDEF void RGFW_setRootWindow(RGFW_window* win); +RGFWDEF RGFW_window* RGFW_getRootWindow(void); + +/*! standard event queue, used for injecting events and returning source API callback events like any other queue check */ +/* these are all used internally by RGFW */ +void RGFW_eventQueuePush(RGFW_event event); +RGFW_event* RGFW_eventQueuePop(RGFW_window* win); + +/*! + key codes and mouse icon enums +*/ +#undef RGFW_key +typedef RGFW_ENUM(u8, RGFW_key) { + RGFW_keyNULL = 0, + RGFW_escape = '\033', + RGFW_backtick = '`', + RGFW_0 = '0', + RGFW_1 = '1', + RGFW_2 = '2', + RGFW_3 = '3', + RGFW_4 = '4', + RGFW_5 = '5', + RGFW_6 = '6', + RGFW_7 = '7', + RGFW_8 = '8', + RGFW_9 = '9', + + RGFW_minus = '-', + RGFW_equals = '=', + RGFW_backSpace = '\b', + RGFW_tab = '\t', + RGFW_space = ' ', + + RGFW_a = 'a', + RGFW_b = 'b', + RGFW_c = 'c', + RGFW_d = 'd', + RGFW_e = 'e', + RGFW_f = 'f', + RGFW_g = 'g', + RGFW_h = 'h', + RGFW_i = 'i', + RGFW_j = 'j', + RGFW_k = 'k', + RGFW_l = 'l', + RGFW_m = 'm', + RGFW_n = 'n', + RGFW_o = 'o', + RGFW_p = 'p', + RGFW_q = 'q', + RGFW_r = 'r', + RGFW_s = 's', + RGFW_t = 't', + RGFW_u = 'u', + RGFW_v = 'v', + RGFW_w = 'w', + RGFW_x = 'x', + RGFW_y = 'y', + RGFW_z = 'z', + + RGFW_period = '.', + RGFW_comma = ',', + RGFW_slash = '/', + RGFW_bracket = '{', + RGFW_closeBracket = '}', + RGFW_semicolon = ';', + RGFW_apostrophe = '\'', + RGFW_backSlash = '\\', + RGFW_return = '\n', + + RGFW_delete = '\177', /* 127 */ + + RGFW_F1, + RGFW_F2, + RGFW_F3, + RGFW_F4, + RGFW_F5, + RGFW_F6, + RGFW_F7, + RGFW_F8, + RGFW_F9, + RGFW_F10, + RGFW_F11, + RGFW_F12, + + RGFW_capsLock, + RGFW_shiftL, + RGFW_controlL, + RGFW_altL, + RGFW_superL, + RGFW_shiftR, + RGFW_controlR, + RGFW_altR, + RGFW_superR, + RGFW_up, + RGFW_down, + RGFW_left, + RGFW_right, + + RGFW_insert, + RGFW_end, + RGFW_home, + RGFW_pageUp, + RGFW_pageDown, + + RGFW_numLock, + RGFW_KP_Slash, + RGFW_multiply, + RGFW_KP_Minus, + RGFW_KP_1, + RGFW_KP_2, + RGFW_KP_3, + RGFW_KP_4, + RGFW_KP_5, + RGFW_KP_6, + RGFW_KP_7, + RGFW_KP_8, + RGFW_KP_9, + RGFW_KP_0, + RGFW_KP_Period, + RGFW_KP_Return, + RGFW_scrollLock, + RGFW_keyLast = 256 /* padding for alignment ~(175 by default) */ + }; + +RGFWDEF u32 RGFW_apiKeyToRGFW(u32 keycode); + +typedef RGFW_ENUM(u8, RGFW_mouseIcons) { + RGFW_mouseNormal = 0, + RGFW_mouseArrow, + RGFW_mouseIbeam, + RGFW_mouseCrosshair, + RGFW_mousePointingHand, + RGFW_mouseResizeEW, + RGFW_mouseResizeNS, + RGFW_mouseResizeNWSE, + RGFW_mouseResizeNESW, + RGFW_mouseResizeAll, + RGFW_mouseNotAllowed, + RGFW_mouseIconFinal = 16 /* padding for alignment */ +}; + +/** @} */ + +#endif /* RGFW_HEADER */ +#if defined(RGFW_X11) || defined(RGFW_WAYLAND) + #define RGFW_OS_BASED_VALUE(l, w, m, h) l +#elif defined(RGFW_WINDOWS) + #define RGFW_OS_BASED_VALUE(l, w, m, h) w +#elif defined(RGFW_MACOS) + #define RGFW_OS_BASED_VALUE(l, w, m, h) m +#elif defined(RGFW_WASM) + #define RGFW_OS_BASED_VALUE(l, w, m, h) h +#endif + + +#ifdef RGFW_IMPLEMENTATION +RGFW_bool RGFW_useWaylandBool = 1; +void RGFW_useWayland(RGFW_bool wayland) { RGFW_useWaylandBool = wayland; } +RGFW_bool RGFW_usingWayland(void) { return RGFW_useWaylandBool; } + +#if !defined(RGFW_NO_X11) && defined(RGFW_WAYLAND) +#define RGFW_GOTO_WAYLAND(fallback) if (RGFW_useWaylandBool && fallback == 0) goto wayland +#else +#define RGFW_GOTO_WAYLAND(fallback) +#endif + +char* RGFW_clipboard_data; +void RGFW_clipboard_switch(char* newstr); +void RGFW_clipboard_switch(char* newstr) { + if (RGFW_clipboard_data != NULL) + RGFW_FREE(RGFW_clipboard_data); + RGFW_clipboard_data = newstr; +} + +#define RGFW_CHECK_CLIPBOARD() \ + if (size <= 0 && RGFW_clipboard_data != NULL) \ + return (const char*)RGFW_clipboard_data; \ + else if (size <= 0) \ + return "\0"; + +const char* RGFW_readClipboard(size_t* len) { + RGFW_ssize_t size = RGFW_readClipboardPtr(NULL, 0); + RGFW_CHECK_CLIPBOARD(); + char* str = (char*)RGFW_ALLOC((size_t)size); + RGFW_ASSERT(str != NULL); + str[0] = '\0'; + + size = RGFW_readClipboardPtr(str, (size_t)size); + + RGFW_CHECK_CLIPBOARD(); + + if (len != NULL) *len = (size_t)size; + + RGFW_clipboard_switch(str); + return (const char*)str; +} + +RGFW_debugfunc RGFW_debugCallback = NULL; +RGFW_debugfunc RGFW_setDebugCallback(RGFW_debugfunc func) { + RGFW_debugfunc RGFW_debugCallbackPrev = RGFW_debugCallback; + RGFW_debugCallback = func; + return RGFW_debugCallbackPrev; +} + +#ifdef RGFW_DEBUG +#include +#endif + +void RGFW_sendDebugInfo(RGFW_debugType type, RGFW_errorCode err, RGFW_debugContext ctx, const char* msg) { + if (RGFW_debugCallback) RGFW_debugCallback(type, err, ctx, msg); + #ifdef RGFW_DEBUG + switch (type) { + case RGFW_typeInfo: printf("RGFW INFO (%i %i): %s", type, err, msg); break; + case RGFW_typeError: printf("RGFW DEBUG (%i %i): %s", type, err, msg); break; + case RGFW_typeWarning: printf("RGFW WARNING (%i %i): %s", type, err, msg); break; + default: break; + } + + switch (err) { + #ifdef RGFW_BUFFER + case RGFW_errBuffer: case RGFW_infoBuffer: printf(" buffer size: %i %i\n", ctx.win->bufferSize.w, ctx.win->bufferSize.h); break; + #endif + case RGFW_infoMonitor: printf(": scale (%s):\n rect: {%i, %i, %i, %i}\n physical size:%f %f\n scale: %f %f\n pixelRatio: %f\n refreshRate: %i\n depth: %i\n", ctx.monitor.name, ctx.monitor.x, ctx.monitor.y, ctx.monitor.mode.area.w, ctx.monitor.mode.area.h, ctx.monitor.physW, ctx.monitor.physH, ctx.monitor.scaleX, ctx.monitor.scaleY, ctx.monitor.pixelRatio, ctx.monitor.mode.refreshRate, ctx.monitor.mode.red + ctx.monitor.mode.green + ctx.monitor.mode.blue); break; + case RGFW_infoWindow: printf(" with rect of {%i, %i, %i, %i} \n", ctx.win->r.x, ctx.win->r.y,ctx. win->r.w, ctx.win->r.h); break; + case RGFW_errDirectXContext: printf(" srcError %i\n", ctx.srcError); break; + default: printf("\n"); + } + #endif +} + +u64 RGFW_timerOffset = 0; +void RGFW_setTime(double time) { + RGFW_timerOffset = RGFW_getTimerValue() - (u64)(time * (double)RGFW_getTimerFreq()); +} + +double RGFW_getTime(void) { + return (double) ((double)(RGFW_getTimerValue() - RGFW_timerOffset) / (double)RGFW_getTimerFreq()); +} + +u64 RGFW_getTimeNS(void) { + return (u64)(((double)((RGFW_getTimerValue() - RGFW_timerOffset)) * 1e9) / (double)RGFW_getTimerFreq()); +} + +/* +RGFW_IMPLEMENTATION starts with generic RGFW defines + +This is the start of keycode data + + Why not use macros instead of the numbers itself? + Windows -> Not all scancodes keys are macros + Linux -> Only symcodes are values, (XK_0 - XK_1, XK_a - XK_z) are larger than 0xFF00, I can't find any way to work with them without making the array an unreasonable size + MacOS -> windows and linux already don't have keycodes as macros, so there's no point +*/ + + + +/* + 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 RGFW_CUSTOM_BACKEND + +#if !defined(__cplusplus) && !defined(RGFW_C89) +#define RGFW_NEXT , +#define RGFW_MAP +#else +#define RGFW_NEXT ; +#define RGFW_MAP RGFW_keycodes +#endif + +u8 RGFW_keycodes [RGFW_OS_BASED_VALUE(256, 512, 128, 256)] = { +#if defined(__cplusplus) || defined(RGFW_C89) + 0 +}; +void RGFW_init_keys(void); +void RGFW_init_keys(void) { +#endif + RGFW_MAP [RGFW_OS_BASED_VALUE(49, 0x029, 50, DOM_VK_BACK_QUOTE)] = RGFW_backtick RGFW_NEXT + + RGFW_MAP [RGFW_OS_BASED_VALUE(19, 0x00B, 29, DOM_VK_0)] = RGFW_0 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(10, 0x002, 18, DOM_VK_1)] = RGFW_1 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(11, 0x003, 19, DOM_VK_2)] = RGFW_2 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(12, 0x004, 20, DOM_VK_3)] = RGFW_3 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(13, 0x005, 21, DOM_VK_4)] = RGFW_4 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(14, 0x006, 23, DOM_VK_5)] = RGFW_5 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(15, 0x007, 22, DOM_VK_6)] = RGFW_6 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(16, 0x008, 26, DOM_VK_7)] = RGFW_7 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(17, 0x009, 28, DOM_VK_8)] = RGFW_8 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(18, 0x00A, 25, DOM_VK_9)] = RGFW_9, + RGFW_MAP [RGFW_OS_BASED_VALUE(65, 0x039, 49, DOM_VK_SPACE)] = RGFW_space, + RGFW_MAP [RGFW_OS_BASED_VALUE(38, 0x01E, 0, DOM_VK_A)] = RGFW_a RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(56, 0x030, 11, DOM_VK_B)] = RGFW_b RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(54, 0x02E, 8, DOM_VK_C)] = RGFW_c RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(40, 0x020, 2, DOM_VK_D)] = RGFW_d RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(26, 0x012, 14, DOM_VK_E)] = RGFW_e RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(41, 0x021, 3, DOM_VK_F)] = RGFW_f RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(42, 0x022, 5, DOM_VK_G)] = RGFW_g RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(43, 0x023, 4, DOM_VK_H)] = RGFW_h RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(31, 0x017, 34, DOM_VK_I)] = RGFW_i RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(44, 0x024, 38, DOM_VK_J)] = RGFW_j RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(45, 0x025, 40, DOM_VK_K)] = RGFW_k RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(46, 0x026, 37, DOM_VK_L)] = RGFW_l RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(58, 0x032, 46, DOM_VK_M)] = RGFW_m RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(57, 0x031, 45, DOM_VK_N)] = RGFW_n RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(32, 0x018, 31, DOM_VK_O)] = RGFW_o RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(33, 0x019, 35, DOM_VK_P)] = RGFW_p RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(24, 0x010, 12, DOM_VK_Q)] = RGFW_q RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(27, 0x013, 15, DOM_VK_R)] = RGFW_r RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(39, 0x01F, 1, DOM_VK_S)] = RGFW_s RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(28, 0x014, 17, DOM_VK_T)] = RGFW_t RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(30, 0x016, 32, DOM_VK_U)] = RGFW_u RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(55, 0x02F, 9, DOM_VK_V)] = RGFW_v RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(25, 0x011, 13, DOM_VK_W)] = RGFW_w RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(53, 0x02D, 7, DOM_VK_X)] = RGFW_x RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(29, 0x015, 16, DOM_VK_Y)] = RGFW_y RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(52, 0x02C, 6, DOM_VK_Z)] = RGFW_z, + RGFW_MAP [RGFW_OS_BASED_VALUE(60, 0x034, 47, DOM_VK_PERIOD)] = RGFW_period RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(59, 0x033, 43, DOM_VK_COMMA)] = RGFW_comma RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(61, 0x035, 44, DOM_VK_SLASH)] = RGFW_slash RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(34, 0x01A, 33, DOM_VK_OPEN_BRACKET)] = RGFW_bracket RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(35, 0x01B, 30, DOM_VK_CLOSE_BRACKET)] = RGFW_closeBracket RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(47, 0x027, 41, DOM_VK_SEMICOLON)] = RGFW_semicolon RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(48, 0x028, 39, DOM_VK_QUOTE)] = RGFW_apostrophe RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(51, 0x02B, 42, DOM_VK_BACK_SLASH)] = RGFW_backSlash, + RGFW_MAP [RGFW_OS_BASED_VALUE(36, 0x01C, 36, DOM_VK_RETURN)] = RGFW_return RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(119, 0x153, 118, DOM_VK_DELETE)] = RGFW_delete RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(77, 0x145, 72, DOM_VK_NUM_LOCK)] = RGFW_numLock RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(106, 0x135, 82, DOM_VK_DIVIDE)] = RGFW_KP_Slash RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(63, 0x037, 76, DOM_VK_MULTIPLY)] = RGFW_multiply RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(82, 0x04A, 67, DOM_VK_SUBTRACT)] = RGFW_KP_Minus RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(87, 0x04F, 84, DOM_VK_NUMPAD1)] = RGFW_KP_1 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(88, 0x050, 85, DOM_VK_NUMPAD2)] = RGFW_KP_2 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(89, 0x051, 86, DOM_VK_NUMPAD3)] = RGFW_KP_3 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(83, 0x04B, 87, DOM_VK_NUMPAD4)] = RGFW_KP_4 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(84, 0x04C, 88, DOM_VK_NUMPAD5)] = RGFW_KP_5 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(85, 0x04D, 89, DOM_VK_NUMPAD6)] = RGFW_KP_6 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(79, 0x047, 90, DOM_VK_NUMPAD7)] = RGFW_KP_7 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(80, 0x048, 92, DOM_VK_NUMPAD8)] = RGFW_KP_8 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(81, 0x049, 93, DOM_VK_NUMPAD9)] = RGFW_KP_9 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(90, 0x052, 83, DOM_VK_NUMPAD0)] = RGFW_KP_0 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(91, 0x053, 65, DOM_VK_DECIMAL)] = RGFW_KP_Period RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(104, 0x11C, 77, 0)] = RGFW_KP_Return, + RGFW_MAP [RGFW_OS_BASED_VALUE(20, 0x00C, 27, DOM_VK_HYPHEN_MINUS)] = RGFW_minus RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(21, 0x00D, 24, DOM_VK_EQUALS)] = RGFW_equals RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(22, 0x00E, 51, DOM_VK_BACK_SPACE)] = RGFW_backSpace RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(23, 0x00F, 48, DOM_VK_TAB)] = RGFW_tab RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(66, 0x03A, 57, DOM_VK_CAPS_LOCK)] = RGFW_capsLock RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(50, 0x02A, 56, DOM_VK_SHIFT)] = RGFW_shiftL RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(37, 0x01D, 59, DOM_VK_CONTROL)] = RGFW_controlL RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(64, 0x038, 58, DOM_VK_ALT)] = RGFW_altL RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(133, 0x15B, 55, DOM_VK_WIN)] = RGFW_superL, + #if !defined(RGFW_MACOS) && !defined(RGFW_WASM) + RGFW_MAP [RGFW_OS_BASED_VALUE(105, 0x11D, 59, 0)] = RGFW_controlR RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(135, 0x15C, 55, 0)] = RGFW_superR, + RGFW_MAP [RGFW_OS_BASED_VALUE(62, 0x036, 56, 0)] = RGFW_shiftR RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(108, 0x138, 58, 0)] = RGFW_altR, + #endif + RGFW_MAP [RGFW_OS_BASED_VALUE(67, 0x03B, 127, DOM_VK_F1)] = RGFW_F1 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(68, 0x03C, 121, DOM_VK_F2)] = RGFW_F2 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(69, 0x03D, 100, DOM_VK_F3)] = RGFW_F3 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(70, 0x03E, 119, DOM_VK_F4)] = RGFW_F4 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(71, 0x03F, 97, DOM_VK_F5)] = RGFW_F5 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(72, 0x040, 98, DOM_VK_F6)] = RGFW_F6 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(73, 0x041, 99, DOM_VK_F7)] = RGFW_F7 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(74, 0x042, 101, DOM_VK_F8)] = RGFW_F8 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(75, 0x043, 102, DOM_VK_F9)] = RGFW_F9 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(76, 0x044, 110, DOM_VK_F10)] = RGFW_F10 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(95, 0x057, 104, DOM_VK_F11)] = RGFW_F11 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(96, 0x058, 112, DOM_VK_F12)] = RGFW_F12 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(111, 0x148, 126, DOM_VK_UP)] = RGFW_up RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(116, 0x150, 125, DOM_VK_DOWN)] = RGFW_down RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(113, 0x14B, 123, DOM_VK_LEFT)] = RGFW_left RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(114, 0x14D, 124, DOM_VK_RIGHT)] = RGFW_right RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(118, 0x152, 115, DOM_VK_INSERT)] = RGFW_insert RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(115, 0x14F, 120, DOM_VK_END)] = RGFW_end RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(112, 0x149, 117, DOM_VK_PAGE_UP)] = RGFW_pageUp RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(117, 0x151, 122, DOM_VK_PAGE_DOWN)] = RGFW_pageDown RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(9, 0x001, 53, DOM_VK_ESCAPE)] = RGFW_escape RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(110, 0x147, 116, DOM_VK_HOME)] = RGFW_home RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(78, 0x046, 107, DOM_VK_SCROLL_LOCK)] = RGFW_scrollLock RGFW_NEXT +#if defined(__cplusplus) || defined(RGFW_C89) +} +#else +}; +#endif + +#undef RGFW_NEXT +#undef RGFW_MAP + +u32 RGFW_apiKeyToRGFW(u32 keycode) { + #if defined(__cplusplus) || defined(RGFW_C89) + if (RGFW_keycodes[RGFW_OS_BASED_VALUE(49, 0x029, 50, DOM_VK_BACK_QUOTE)] != RGFW_backtick) { + RGFW_init_keys(); + } + #endif + + /* make sure the key isn't out of bounds */ + if (keycode > sizeof(RGFW_keycodes) / sizeof(u8)) + return 0; + + return RGFW_keycodes[keycode]; +} +#endif /* RGFW_CUSTOM_BACKEND */ + +typedef struct { + RGFW_bool current : 1; + RGFW_bool prev : 1; +} RGFW_keyState; + +RGFW_keyState RGFW_keyboard[RGFW_keyLast] = { {0, 0} }; + +RGFWDEF void RGFW_resetKey(void); +void RGFW_resetKey(void) { + size_t len = RGFW_keyLast; /*!< last_key == length */ + + size_t i; /*!< reset each previous state */ + for (i = 0; i < len; i++) + RGFW_keyboard[i].prev = 0; +} + +/* + this is the end of keycode data +*/ + +/* gamepad data */ +RGFW_keyState RGFW_gamepadPressed[4][32]; /*!< if a key is currently pressed or not (per gamepad) */ +RGFW_point RGFW_gamepadAxes[4][4]; /*!< if a key is currently pressed or not (per gamepad) */ + +RGFW_gamepadType RGFW_gamepads_type[4]; /*!< if a key is currently pressed or not (per gamepad) */ +i32 RGFW_gamepads[4] = {0, 0, 0, 0}; /*!< limit of 4 gamepads at a time */ +char RGFW_gamepads_name[4][128]; /*!< gamepad names */ +u16 RGFW_gamepadCount = 0; /*!< the actual amount of gamepads */ + +/* + event callback defines start here +*/ + + +/* + These exist to avoid the + if (func == NULL) check + for (allegedly) better performance + + RGFW_EMPTY_DEF exists to prevent the missing-prototypes warning +*/ +static void RGFW_windowMovedfuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); } +static void RGFW_windowResizedfuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); } +static void RGFW_windowRestoredfuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); } +static void RGFW_windowMinimizedfuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); } +static void RGFW_windowMaximizedfuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); } +static void RGFW_windowQuitfuncEMPTY(RGFW_window* win) { RGFW_UNUSED(win); } +static void RGFW_focusfuncEMPTY(RGFW_window* win, RGFW_bool inFocus) {RGFW_UNUSED(win); RGFW_UNUSED(inFocus);} +static void RGFW_mouseNotifyfuncEMPTY(RGFW_window* win, RGFW_point point, RGFW_bool status) {RGFW_UNUSED(win); RGFW_UNUSED(point); RGFW_UNUSED(status);} +static void RGFW_mousePosfuncEMPTY(RGFW_window* win, RGFW_point point, RGFW_point vector) {RGFW_UNUSED(win); RGFW_UNUSED(point); RGFW_UNUSED(vector);} +static void RGFW_dndInitfuncEMPTY(RGFW_window* win, RGFW_point point) {RGFW_UNUSED(win); RGFW_UNUSED(point);} +static void RGFW_windowRefreshfuncEMPTY(RGFW_window* win) {RGFW_UNUSED(win); } +static void RGFW_keyfuncEMPTY(RGFW_window* win, RGFW_key key, u8 keyChar, RGFW_keymod keyMod, RGFW_bool pressed) {RGFW_UNUSED(win); RGFW_UNUSED(key); RGFW_UNUSED(keyChar); RGFW_UNUSED(keyMod); RGFW_UNUSED(pressed);} +static void RGFW_mouseButtonfuncEMPTY(RGFW_window* win, RGFW_mouseButton button, double scroll, RGFW_bool pressed) {RGFW_UNUSED(win); RGFW_UNUSED(button); RGFW_UNUSED(scroll); RGFW_UNUSED(pressed);} +static void RGFW_gamepadButtonfuncEMPTY(RGFW_window* win, u16 gamepad, u8 button, RGFW_bool pressed) {RGFW_UNUSED(win); RGFW_UNUSED(gamepad); RGFW_UNUSED(button); RGFW_UNUSED(pressed); } +static void RGFW_gamepadAxisfuncEMPTY(RGFW_window* win, u16 gamepad, RGFW_point axis[2], u8 axisesCount, u8 whichAxis) {RGFW_UNUSED(win); RGFW_UNUSED(gamepad); RGFW_UNUSED(axis); RGFW_UNUSED(axisesCount); RGFW_UNUSED(whichAxis); } +static void RGFW_gamepadfuncEMPTY(RGFW_window* win, u16 gamepad, RGFW_bool connected) {RGFW_UNUSED(win); RGFW_UNUSED(gamepad); RGFW_UNUSED(connected);} +static void RGFW_dndfuncEMPTY(RGFW_window* win, char** droppedFiles, size_t droppedFilesCount) {RGFW_UNUSED(win); RGFW_UNUSED(droppedFiles); RGFW_UNUSED(droppedFilesCount);} +static void RGFW_scaleUpdatedfuncEMPTY(RGFW_window* win, float scaleX, float scaleY) {RGFW_UNUSED(win); RGFW_UNUSED(scaleX); RGFW_UNUSED(scaleY); } + +#define RGFW_CALLBACK_DEFINE(x, x2) \ +RGFW_##x##func RGFW_##x##Callback = RGFW_##x##funcEMPTY; \ +RGFW_##x##func RGFW_set##x2##Callback(RGFW_##x##func func) { \ + RGFW_##x##func prev = RGFW_##x##Callback; \ + RGFW_##x##Callback = func; \ + return prev; \ +} +RGFW_CALLBACK_DEFINE(windowMaximized, WindowMaximized) +RGFW_CALLBACK_DEFINE(windowMinimized, WindowMinimized) +RGFW_CALLBACK_DEFINE(windowRestored, WindowRestored) +RGFW_CALLBACK_DEFINE(windowMoved, WindowMoved) +RGFW_CALLBACK_DEFINE(windowResized, WindowResized) +RGFW_CALLBACK_DEFINE(windowQuit, WindowQuit) +RGFW_CALLBACK_DEFINE(mousePos, MousePos) +RGFW_CALLBACK_DEFINE(windowRefresh, WindowRefresh) +RGFW_CALLBACK_DEFINE(focus, Focus) +RGFW_CALLBACK_DEFINE(mouseNotify, MouseNotify) +RGFW_CALLBACK_DEFINE(dnd, Dnd) +RGFW_CALLBACK_DEFINE(dndInit, DndInit) +RGFW_CALLBACK_DEFINE(key, Key) +RGFW_CALLBACK_DEFINE(mouseButton, MouseButton) +RGFW_CALLBACK_DEFINE(gamepadButton, GamepadButton) +RGFW_CALLBACK_DEFINE(gamepadAxis, GamepadAxis) +RGFW_CALLBACK_DEFINE(gamepad, Gamepad) +RGFW_CALLBACK_DEFINE(scaleUpdated, ScaleUpdated) +#undef RGFW_CALLBACK_DEFINE + +void RGFW_window_checkEvents(RGFW_window* win, i32 waitMS) { + RGFW_window_eventWait(win, waitMS); + + while (RGFW_window_checkEvent(win) != NULL && RGFW_window_shouldClose(win) == 0) { + if (win->event.type == RGFW_quit) return; + } + + #ifdef RGFW_WASM /* WASM needs to run the sleep function for asyncify */ + RGFW_sleep(0); + #endif +} + +void RGFW_window_checkMode(RGFW_window* win); +void RGFW_window_checkMode(RGFW_window* win) { + if (RGFW_window_isMinimized(win)) { + win->_flags |= RGFW_windowMinimize; + RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowMinimized, ._win = win}); + RGFW_windowMinimizedCallback(win, win->r); + } else if (RGFW_window_isMaximized(win)) { + win->_flags |= RGFW_windowMaximize; + RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowMaximized, ._win = win}); + RGFW_windowMaximizedCallback(win, win->r); + } else if (((win->_flags & RGFW_windowMinimize) && !RGFW_window_isMaximized(win)) || + (win->_flags & RGFW_windowMaximize && !RGFW_window_isMaximized(win))) { + win->_flags &= ~(u32)RGFW_windowMinimize; + if (RGFW_window_isMaximized(win) == RGFW_FALSE) win->_flags &= ~(u32)RGFW_windowMaximize; + RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowRestored, ._win = win}); + RGFW_windowRestoredCallback(win, win->r); + } +} + +/* +no more event call back defines +*/ + +#define SET_ATTRIB(a, v) { \ + RGFW_ASSERT(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[index++] = a; \ + attribs[index++] = v; \ +} + +#define RGFW_EVENT_PASSED RGFW_BIT(24) /* if a queued event was passed */ +#define RGFW_EVENT_QUIT RGFW_BIT(25) /* the window close button was pressed */ +#define RGFW_HOLD_MOUSE RGFW_BIT(26) /*!< hold the moues still */ +#define RGFW_MOUSE_LEFT RGFW_BIT(27) /* if mouse left the window */ +#define RGFW_WINDOW_ALLOC RGFW_BIT(28) /* if window was allocated by RGFW */ +#define RGFW_BUFFER_ALLOC RGFW_BIT(29) /* if window.buffer was allocated by RGFW */ +#define RGFW_WINDOW_INIT RGFW_BIT(30) /* if window.buffer was allocated by RGFW */ +#define RGFW_INTERNAL_FLAGS (RGFW_EVENT_QUIT | RGFW_EVENT_PASSED | RGFW_HOLD_MOUSE | RGFW_MOUSE_LEFT | RGFW_WINDOW_ALLOC | RGFW_BUFFER_ALLOC | RGFW_windowFocus) + +RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, RGFW_windowFlags flags) { + RGFW_window* win = (RGFW_window*)RGFW_ALLOC(sizeof(RGFW_window)); + RGFW_ASSERT(win != NULL); + win->_flags = RGFW_WINDOW_ALLOC; + return RGFW_createWindowPtr(name, rect, flags, win); +} + +#if defined(RGFW_USE_XDL) && defined(RGFW_X11) + #define XDL_IMPLEMENTATION + #include "XDL.h" +#endif + +#define RGFW_MAX_EVENTS 32 +typedef struct RGFW_globalStruct { + RGFW_window* root; + RGFW_window* current; + i32 windowCount; + i32 eventLen; + i32 eventIndex; + + #ifdef RGFW_X11 + Display* display; + Window helperWindow; + char* clipboard; /* for writing to the clipboard selection */ + size_t clipboard_len; + #endif + #ifdef RGFW_WAYLAND + struct wl_display* wl_display; + #endif + #if defined(RGFW_X11) || defined(RGFW_WINDOWS) + RGFW_mouse* hiddenMouse; + #endif + RGFW_event events[RGFW_MAX_EVENTS]; + +} RGFW_globalStruct; +#ifndef RGFW_C89 +RGFW_globalStruct _RGFW = {.root = NULL, .current = NULL, .windowCount = -1, .eventLen = 0, .eventIndex = 0}; +#else +RGFW_globalStruct _RGFW = {NULL, NULL, -1, 0, 0}; +#endif + +void RGFW_eventQueuePush(RGFW_event event) { + if (_RGFW.eventLen >= RGFW_MAX_EVENTS) return; + _RGFW.events[_RGFW.eventLen] = event; + _RGFW.eventLen++; +} + +RGFW_event* RGFW_eventQueuePop(RGFW_window* win) { + if (_RGFW.eventLen == 0) return NULL; + + RGFW_event* ev = (RGFW_event*)&_RGFW.events[_RGFW.eventIndex]; + + _RGFW.eventLen--; + if (_RGFW.eventLen && _RGFW.eventIndex < (_RGFW.eventLen - 1)) + _RGFW.eventIndex++; + else if (_RGFW.eventLen == 0) + _RGFW.eventIndex = 0; + + if (ev->_win != win && ev->_win != NULL) { + RGFW_eventQueuePush(*ev); + return NULL; + } + + ev->droppedFiles = win->event.droppedFiles; + return ev; +} + +RGFW_event* RGFW_window_checkEventCore(RGFW_window* win); +RGFW_event* RGFW_window_checkEventCore(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + if (win->event.type == 0 && _RGFW.eventLen == 0) + RGFW_resetKey(); + + if (win->event.type == RGFW_quit && win->_flags & RGFW_windowFreeOnClose) { + static RGFW_event ev; + ev = win->event; + RGFW_window_close(win); + return &ev; + } + + if (win->event.type != RGFW_DNDInit) win->event.type = 0; + + /* check queued events */ + RGFW_event* ev = RGFW_eventQueuePop(win); + if (ev != NULL) { + if (ev->type == RGFW_quit) RGFW_window_setShouldClose(win, RGFW_TRUE); + win->event = *ev; + } + else return NULL; + + return &win->event; +} + + +RGFWDEF void RGFW_window_basic_init(RGFW_window* win, RGFW_rect rect, RGFW_windowFlags flags); +void RGFW_setRootWindow(RGFW_window* win) { _RGFW.root = win; } +RGFW_window* RGFW_getRootWindow(void) { return _RGFW.root; } + +/* do a basic initialization for RGFW_window, this is to standard it for each OS */ +void RGFW_window_basic_init(RGFW_window* win, RGFW_rect rect, RGFW_windowFlags flags) { + RGFW_UNUSED(flags); + if (_RGFW.windowCount == -1) RGFW_init(); + _RGFW.windowCount++; + + /* rect based the requested flags */ + if (_RGFW.root == NULL) { + RGFW_setRootWindow(win); + RGFW_setTime(0); + } + + if (!(win->_flags & RGFW_WINDOW_ALLOC)) win->_flags = 0; + + /* set and init the new window's data */ + win->r = rect; + win->event.droppedFilesCount = 0; + + win->_flags = 0 | (win->_flags & RGFW_WINDOW_ALLOC); + win->_flags |= flags; + win->event.keyMod = 0; + win->_lastMousePoint = RGFW_POINT(0, 0); + + win->event.droppedFiles = (char**)RGFW_ALLOC(RGFW_MAX_PATH * RGFW_MAX_DROPS); + RGFW_ASSERT(win->event.droppedFiles != NULL); + + u32 i; + for (i = 0; i < RGFW_MAX_DROPS; i++) + win->event.droppedFiles[i] = (char*)(win->event.droppedFiles + RGFW_MAX_DROPS + (i * RGFW_MAX_PATH)); +} + +void RGFW_window_setFlags(RGFW_window* win, RGFW_windowFlags flags) { + RGFW_windowFlags cmpFlags = win->_flags; + if (win->_flags & RGFW_WINDOW_INIT) cmpFlags = 0; + + #ifndef RGFW_NO_MONITOR + if (flags & RGFW_windowScaleToMonitor) RGFW_window_scaleToMonitor(win); + #endif + + if (flags & RGFW_windowCenter) RGFW_window_center(win); + if (flags & RGFW_windowCenterCursor) + RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2))); + if (flags & RGFW_windowNoBorder) RGFW_window_setBorder(win, 0); + else RGFW_window_setBorder(win, 1); + if (flags & RGFW_windowFullscreen) RGFW_window_setFullscreen(win, RGFW_TRUE); + else if (cmpFlags & RGFW_windowFullscreen) RGFW_window_setFullscreen(win, 0); + if (flags & RGFW_windowMaximize) RGFW_window_maximize(win); + else if (cmpFlags & RGFW_windowMaximize) RGFW_window_restore(win); + if (flags & RGFW_windowMinimize) RGFW_window_minimize(win); + else if (cmpFlags & RGFW_windowMinimize) RGFW_window_restore(win); + if (flags & RGFW_windowHideMouse) RGFW_window_showMouse(win, 0); + else if (cmpFlags & RGFW_windowHideMouse) RGFW_window_showMouse(win, 1); + if (flags & RGFW_windowHide) RGFW_window_hide(win); + else if (cmpFlags & RGFW_windowHide) RGFW_window_show(win); + if (flags & RGFW_windowCocoaCHDirToRes) RGFW_moveToMacOSResourceDir(); + if (flags & RGFW_windowFloating) RGFW_window_setFloating(win, 1); + else if (cmpFlags & RGFW_windowFloating) RGFW_window_setFloating(win, 0); + if (flags & RGFW_windowFocus) RGFW_window_focus(win); + + if (flags & RGFW_windowNoResize) { + RGFW_window_setMaxSize(win, RGFW_AREA(win->r.w, win->r.h)); + RGFW_window_setMinSize(win, RGFW_AREA(win->r.w, win->r.h)); + } else if (cmpFlags & RGFW_windowNoResize) { + RGFW_window_setMaxSize(win, RGFW_AREA(0, 0)); + RGFW_window_setMinSize(win, RGFW_AREA(0, 0)); + } + + win->_flags = flags | (win->_flags & RGFW_INTERNAL_FLAGS); +} + +RGFW_bool RGFW_window_isInFocus(RGFW_window* win) { +#ifdef RGFW_WASM + return RGFW_TRUE; +#else + return RGFW_BOOL(win->_flags & RGFW_windowFocus); +#endif +} + +void RGFW_window_initBuffer(RGFW_window* win) { + RGFW_area area = RGFW_getScreenSize(); + if ((win->_flags & RGFW_windowNoResize)) + area = RGFW_AREA(win->r.w, win->r.h); + + RGFW_window_initBufferSize(win, area); +} + +void RGFW_window_initBufferSize(RGFW_window* win, RGFW_area area) { +#if defined(RGFW_BUFFER) || defined(RGFW_OSMESA) + win->_flags |= RGFW_BUFFER_ALLOC; + #ifndef RGFW_WINDOWS + u8* buffer = (u8*)RGFW_ALLOC(area.w * area.h * 4); + RGFW_ASSERT(buffer != NULL); + + RGFW_window_initBufferPtr(win, buffer, area); + #else /* windows's bitmap allocs memory for us */ + RGFW_window_initBufferPtr(win, (u8*)NULL, area); + #endif +#else + RGFW_UNUSED(win); RGFW_UNUSED(area); +#endif +} + +#ifdef RGFW_MACOS +RGFWDEF void RGFW_window_cocoaSetLayer(RGFW_window* win, void* layer); +RGFWDEF void* RGFW_cocoaGetLayer(void); +#endif + +const char* RGFW_className = NULL; +void RGFW_setClassName(const char* name) { RGFW_className = name; } + +#ifndef RGFW_X11 +void RGFW_setXInstName(const char* name) { RGFW_UNUSED(name); } +#endif + +RGFW_keyState RGFW_mouseButtons[RGFW_mouseFinal] = { {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0} }; + +RGFW_bool RGFW_isMousePressed(RGFW_window* win, RGFW_mouseButton button) { + return RGFW_mouseButtons[button].current && (win == NULL || RGFW_window_isInFocus(win)); +} +RGFW_bool RGFW_wasMousePressed(RGFW_window* win, RGFW_mouseButton button) { + return RGFW_mouseButtons[button].prev && (win != NULL || RGFW_window_isInFocus(win)); +} +RGFW_bool RGFW_isMouseHeld(RGFW_window* win, RGFW_mouseButton button) { + return (RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button)); +} +RGFW_bool RGFW_isMouseReleased(RGFW_window* win, RGFW_mouseButton button) { + return (!RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button)); +} + +RGFW_point RGFW_window_getMousePoint(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + return win->_lastMousePoint; +} + +RGFW_bool RGFW_isPressed(RGFW_window* win, RGFW_key key) { + return RGFW_keyboard[key].current && (win == NULL || RGFW_window_isInFocus(win)); +} + +RGFW_bool RGFW_wasPressed(RGFW_window* win, RGFW_key key) { + return RGFW_keyboard[key].prev && (win == NULL || RGFW_window_isInFocus(win)); +} + +RGFW_bool RGFW_isHeld(RGFW_window* win, RGFW_key key) { + return (RGFW_isPressed(win, key) && RGFW_wasPressed(win, key)); +} + +RGFW_bool RGFW_isClicked(RGFW_window* win, RGFW_key key) { + return (RGFW_wasPressed(win, key) && !RGFW_isPressed(win, key)); +} + +RGFW_bool RGFW_isReleased(RGFW_window* win, RGFW_key key) { + return (!RGFW_isPressed(win, key) && RGFW_wasPressed(win, key)); +} + +void RGFW_window_makeCurrent(RGFW_window* win) { + _RGFW.current = win; +#if defined(RGFW_OPENGL) || defined(RGFW_EGL) + RGFW_window_makeCurrent_OpenGL(win); +#endif +} + +RGFW_window* RGFW_getCurrent(void) { + return _RGFW.current; +} + +void RGFW_window_swapBuffers(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_window_swapBuffers_software(win); +#if defined(RGFW_OPENGL) || defined(RGFW_EGL) + RGFW_window_swapBuffers_OpenGL(win); +#endif +} + +RGFWDEF void RGFW_setBit(u32* data, u32 bit, RGFW_bool value); +void RGFW_setBit(u32* data, u32 bit, RGFW_bool value) { + if (value) + *data |= bit; + else if (!value && (*(data) & bit)) + *data ^= bit; +} + +void RGFW_window_center(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_area screenR = RGFW_getScreenSize(); + RGFW_window_move(win, RGFW_POINT((i32)(screenR.w - (u32)win->r.w) / 2, (screenR.h - (u32)win->r.h) / 2)); +} + +RGFW_bool RGFW_monitor_scaleToWindow(RGFW_monitor mon, RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + RGFW_monitorMode mode; + mode.area = RGFW_AREA(win->r.w, win->r.h); + return RGFW_monitor_requestMode(mon, mode, RGFW_monitorScale); +} + +void RGFW_splitBPP(u32 bpp, RGFW_monitorMode* mode); +void RGFW_splitBPP(u32 bpp, RGFW_monitorMode* mode) { + if (bpp == 32) bpp = 24; + mode->red = mode->green = mode->blue = (u8)(bpp / 3); + + u32 delta = bpp - (mode->red * 3); /* handle leftovers */ + if (delta >= 1) mode->green = mode->green + 1; + if (delta == 2) mode->red = mode->red + 1; +} + +RGFW_bool RGFW_monitorModeCompare(RGFW_monitorMode mon, RGFW_monitorMode mon2, RGFW_modeRequest request) { + return (((mon.area.w == mon2.area.w && mon.area.h == mon2.area.h) || !(request & RGFW_monitorScale)) && + ((mon.refreshRate == mon2.refreshRate) || !(request & RGFW_monitorRefresh)) && + ((mon.red == mon2.red && mon.green == mon2.green && mon.blue == mon2.blue) || !(request & RGFW_monitorRGB))); +} + +RGFW_bool RGFW_window_shouldClose(RGFW_window* win) { + return (win == NULL || (win->_flags & RGFW_EVENT_QUIT)|| RGFW_isPressed(win, RGFW_escape)); +} + +void RGFW_window_setShouldClose(RGFW_window* win, RGFW_bool shouldClose) { + if (shouldClose) { + win->_flags |= RGFW_EVENT_QUIT; + RGFW_windowQuitCallback(win); + } else { + win->_flags &= ~(u32)RGFW_EVENT_QUIT; + } +} + +#ifndef RGFW_NO_MONITOR +void RGFW_window_scaleToMonitor(RGFW_window* win) { + RGFW_monitor monitor = RGFW_window_getMonitor(win); + if (monitor.scaleX == 0 && monitor.scaleY == 0) + return; + + RGFW_window_resize(win, RGFW_AREA((u32)(monitor.scaleX * (float)win->r.w), (u32)(monitor.scaleY * (float)win->r.h))); +} + +void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m) { + RGFW_window_move(win, RGFW_POINT(m.x + win->r.x, m.y + win->r.y)); +} +#endif + +RGFW_bool RGFW_window_setIcon(RGFW_window* win, u8* icon, RGFW_area a, i32 channels) { + return RGFW_window_setIconEx(win, icon, a, channels, RGFW_iconBoth); +} + +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->_flags & RGFW_HOLD_MOUSE)) + return; + + if (!area.w && !area.h) + area = RGFW_AREA(win->r.w / 2, win->r.h / 2); + + win->_flags |= RGFW_HOLD_MOUSE; + RGFW_captureCursor(win, win->r); + 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) { + win->_flags &= ~(u32)RGFW_HOLD_MOUSE; + RGFW_releaseCursor(win); +} + +u32 RGFW_checkFPS(double startTime, u32 frameCount, u32 fpsCap) { + double deltaTime = RGFW_getTime() - startTime; + if (deltaTime == 0) return 0; + + double fps = (frameCount / deltaTime); /* the numer of frames over the time it took for them to render */ + if (fpsCap && fps > fpsCap) { + double frameTime = (double)frameCount / (double)fpsCap; /* how long it should take to finish the frames */ + double sleepTime = frameTime - deltaTime; /* subtract how long it should have taken with how long it did take */ + + if (sleepTime > 0) RGFW_sleep((u32)(sleepTime * 1000)); + } + + return (u32) fps; +} + +#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) +void RGFW_RGB_to_BGR(RGFW_window* win, u8* data) { + #if !defined(RGFW_BUFFER_BGR) && !defined(RGFW_OSMESA) + 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->bufferSize.w) + x * 4; + + u8 red = data[index]; + data[index] = win->buffer[index + 2]; + data[index + 2] = red; + } + } + #elif defined(RGFW_OSMESA) + u32 y; + for(y = 0; y < (u32)win->r.h; y++){ + u32 index_from = (y + (win->bufferSize.h - win->r.h)) * 4 * win->bufferSize.w; + u32 index_to = y * 4 * win->bufferSize.w; + memcpy(&data[index_to], &data[index_from], 4 * win->bufferSize.w); + } + #else + RGFW_UNUSED(win); RGFW_UNUSED(data); + #endif +} +#endif + +u32 RGFW_isPressedGamepad(RGFW_window* win, u8 c, RGFW_gamepadCodes button) { + RGFW_UNUSED(win); + return RGFW_gamepadPressed[c][button].current; +} +u32 RGFW_wasPressedGamepad(RGFW_window* win, u8 c, RGFW_gamepadCodes button) { + RGFW_UNUSED(win); + return RGFW_gamepadPressed[c][button].prev; +} +u32 RGFW_isReleasedGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button) { + RGFW_UNUSED(win); + return !RGFW_isPressedGamepad(win, controller, button) && RGFW_wasPressedGamepad(win, controller, button); +} +u32 RGFW_isHeldGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button) { + RGFW_UNUSED(win); + return RGFW_isPressedGamepad(win, controller, button) && RGFW_wasPressedGamepad(win, controller, button); +} + +RGFW_point RGFW_getGamepadAxis(RGFW_window* win, u16 controller, u16 whichAxis) { + RGFW_UNUSED(win); + return RGFW_gamepadAxes[controller][whichAxis]; +} +const char* RGFW_getGamepadName(RGFW_window* win, u16 controller) { + RGFW_UNUSED(win); + return (const char*)RGFW_gamepads_name[controller]; +} + +size_t RGFW_getGamepadCount(RGFW_window* win) { + RGFW_UNUSED(win); + return RGFW_gamepadCount; +} + +RGFW_gamepadType RGFW_getGamepadType(RGFW_window* win, u16 controller) { + RGFW_UNUSED(win); + return RGFW_gamepads_type[controller]; +} + +RGFWDEF void RGFW_updateKeyMod(RGFW_window* win, RGFW_keymod mod, RGFW_bool value); +void RGFW_updateKeyMod(RGFW_window* win, RGFW_keymod mod, RGFW_bool value) { + if (value) win->event.keyMod |= mod; + else win->event.keyMod &= ~mod; +} + +RGFWDEF void RGFW_updateKeyModsPro(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll); +void RGFW_updateKeyModsPro(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll) { + RGFW_updateKeyMod(win, RGFW_modCapsLock, capital); + RGFW_updateKeyMod(win, RGFW_modNumLock, numlock); + RGFW_updateKeyMod(win, RGFW_modControl, control); + RGFW_updateKeyMod(win, RGFW_modAlt, alt); + RGFW_updateKeyMod(win, RGFW_modShift, shift); + RGFW_updateKeyMod(win, RGFW_modSuper, super); + RGFW_updateKeyMod(win, RGFW_modScrollLock, scroll); +} + +RGFWDEF void RGFW_updateKeyMods(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool scroll); +void RGFW_updateKeyMods(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool scroll) { + RGFW_updateKeyModsPro(win, capital, numlock, + RGFW_isPressed(win, RGFW_controlL) || RGFW_isPressed(win, RGFW_controlR), + RGFW_isPressed(win, RGFW_altL) || RGFW_isPressed(win, RGFW_altR), + RGFW_isPressed(win, RGFW_shiftL) || RGFW_isPressed(win, RGFW_shiftR), + RGFW_isPressed(win, RGFW_superL) || RGFW_isPressed(win, RGFW_superR), + scroll); +} + +RGFWDEF void RGFW_window_showMouseFlags(RGFW_window* win, RGFW_bool show); +void RGFW_window_showMouseFlags(RGFW_window* win, RGFW_bool show) { + if (show && (win->_flags & RGFW_windowHideMouse)) + win->_flags ^= RGFW_windowHideMouse; + else if (!show && !(win->_flags & RGFW_windowHideMouse)) + win->_flags |= RGFW_windowHideMouse; +} + +RGFW_bool RGFW_window_mouseHidden(RGFW_window* win) { + return (RGFW_bool)RGFW_BOOL(win->_flags & RGFW_windowHideMouse); +} + +RGFW_bool RGFW_window_borderless(RGFW_window* win) { + return (RGFW_bool)RGFW_BOOL(win->_flags & RGFW_windowNoBorder); +} + +RGFW_bool RGFW_window_isFullscreen(RGFW_window* win){ return RGFW_BOOL(win->_flags & RGFW_windowFullscreen); } +RGFW_bool RGFW_window_allowsDND(RGFW_window* win) { return RGFW_BOOL(win->_flags & RGFW_windowAllowDND); } + +#ifndef RGFW_WINDOWS +void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow) { + RGFW_setBit(&win->_flags, RGFW_windowAllowDND, allow); +} +#endif + +#if defined(RGFW_X11) || defined(RGFW_MACOS) || defined(RGFW_WASM) || defined(RGFW_WAYLAND) +#ifndef __USE_POSIX199309 + #define __USE_POSIX199309 +#endif +#include +struct timespec; +#endif + +#if defined(RGFW_X11) || defined(RGFW_WINDOWS) +void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) { + RGFW_window_showMouseFlags(win, show); + if (show == 0) + RGFW_window_setMouse(win, _RGFW.hiddenMouse); + else + RGFW_window_setMouseDefault(win); +} +#endif + +#ifndef RGFW_MACOS +void RGFW_moveToMacOSResourceDir(void) { } +#endif + +/* + graphics API specific code (end of generic code) + starts here +*/ + + +/* + OpenGL defines start here (Normal, EGL, OSMesa) +*/ + +#if defined(RGFW_OPENGL) || defined(RGFW_EGL) + +#ifdef RGFW_WINDOWS + #define WIN32_LEAN_AND_MEAN + #define OEMRESOURCE + #include +#endif + +#if !defined(__APPLE__) && !defined(RGFW_NO_GL_HEADER) + #include +#elif defined(__APPLE__) + #ifndef GL_SILENCE_DEPRECATION + #define GL_SILENCE_DEPRECATION + #endif + #include + #include +#endif + +/* EGL, normal OpenGL only */ +#ifndef RGFW_EGL +i32 RGFW_GL_HINTS[RGFW_glFinalHint] = {8, +#else +i32 RGFW_GL_HINTS[RGFW_glFinalHint] = {0, +#endif + 0, 0, 0, 1, 8, 8, 8, 8, 24, 0, 0, 0, 0, 0, 0, 0, 0, RGFW_glReleaseNone, RGFW_glCore, 0, 0}; + +void RGFW_setGLHint(RGFW_glHints hint, i32 value) { + if (hint < RGFW_glFinalHint && hint) RGFW_GL_HINTS[hint] = value; +} + +/* OPENGL normal only (no EGL / OSMesa) */ +#if defined(RGFW_OPENGL) && !defined(RGFW_EGL) && !defined(RGFW_CUSTOM_BACKEND) && !defined(RGFW_WASM) + +#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) + +#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_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_ACCUM_RED_SIZE RGFW_OS_BASED_VALUE(14, 0x201E, 0, 0) + #define RGFW_GL_ACCUM_GREEN_SIZE RGFW_OS_BASED_VALUE(15, 0x201F, 0, 0) + #define RGFW_GL_ACCUM_BLUE_SIZE RGFW_OS_BASED_VALUE(16, 0x2020, 0, 0) + #define RGFW_GL_ACCUM_ALPHA_SIZE RGFW_OS_BASED_VALUE(17, 0x2021, 0, 0) + #define RGFW_GL_SRGB RGFW_OS_BASED_VALUE(0x20b2, 0x3089, 0, 0) + #define RGFW_GL_NOERROR RGFW_OS_BASED_VALUE(0x31b3, 0x31b3, 0, 0) + #define RGFW_GL_FLAGS RGFW_OS_BASED_VALUE(GLX_CONTEXT_FLAGS_ARB, 0x2094, 0, 0) + #define RGFW_GL_RELEASE_BEHAVIOR RGFW_OS_BASED_VALUE(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, 0x2097 , 0, 0) + #define RGFW_GL_CONTEXT_RELEASE RGFW_OS_BASED_VALUE(GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB, 0x2098, 0, 0) + #define RGFW_GL_CONTEXT_NONE RGFW_OS_BASED_VALUE(GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB, 0x0000, 0, 0) + #define RGFW_GL_FLAGS RGFW_OS_BASED_VALUE(GLX_CONTEXT_FLAGS_ARB, 0x2094, 0, 0) + #define RGFW_GL_DEBUG_BIT RGFW_OS_BASED_VALUE(GLX_CONTEXT_FLAGS_ARB, 0x2094, 0, 0) + #define RGFW_GL_ROBUST_BIT RGFW_OS_BASED_VALUE(GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB, 0x00000004, 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 + #define WGL_PIXEL_TYPE_ARB 0x2013 + #define WGL_TYPE_RGBA_ARB 0x202B + + #define WGL_TRANSPARENT_ARB 0x200A +#endif + +/* 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 */ +i32* RGFW_initFormatAttribs(u32 useSoftware); +i32* RGFW_initFormatAttribs(u32 useSoftware) { + RGFW_UNUSED(useSoftware); + static i32 attribs[] = { + #if defined(RGFW_X11) || defined(RGFW_WINDOWS) + RGFW_GL_RENDER_TYPE, + RGFW_GL_FULL_FORMAT, + RGFW_GL_DRAW, 1, + RGFW_GL_DRAW_TYPE , RGFW_GL_USE_RGBA, + #endif + + #ifdef RGFW_X11 + GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, + #endif + + #ifdef RGFW_MACOS + 72, + 8, 24, + #endif + + #ifdef RGFW_WINDOWS + WGL_SUPPORT_OPENGL_ARB, 1, + WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, + WGL_COLOR_BITS_ARB, 32, + #endif + 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, 0, 0 + }; + + size_t index = (sizeof(attribs) / sizeof(attribs[0])) - 27; + + #define RGFW_GL_ADD_ATTRIB(attrib, attVal) \ + if (attVal) { \ + attribs[index] = attrib;\ + attribs[index + 1] = attVal;\ + index += 2;\ + } + + #if defined(RGFW_MACOS) && defined(RGFW_COCOA_GRAPHICS_SWITCHING) + RGFW_GL_ADD_ATTRIB(96, kCGLPFASupportsAutomaticGraphicsSwitching); + #endif + + RGFW_GL_ADD_ATTRIB(RGFW_GL_DOUBLEBUFFER, 1); + + RGFW_GL_ADD_ATTRIB(RGFW_GL_ALPHA_SIZE, RGFW_GL_HINTS[RGFW_glAlpha]); + RGFW_GL_ADD_ATTRIB(RGFW_GL_DEPTH_SIZE, RGFW_GL_HINTS[RGFW_glDepth]); + RGFW_GL_ADD_ATTRIB(RGFW_GL_STENCIL_SIZE, RGFW_GL_HINTS[RGFW_glStencil]); + RGFW_GL_ADD_ATTRIB(RGFW_GL_STEREO, RGFW_GL_HINTS[RGFW_glStereo]); + RGFW_GL_ADD_ATTRIB(RGFW_GL_AUX_BUFFERS, RGFW_GL_HINTS[RGFW_glAuxBuffers]); + + #if defined(RGFW_X11) || defined(RGFW_WINDOWS) + RGFW_GL_ADD_ATTRIB(RGFW_GL_RED_SIZE, RGFW_GL_HINTS[RGFW_glRed]); + RGFW_GL_ADD_ATTRIB(RGFW_GL_GREEN_SIZE, RGFW_GL_HINTS[RGFW_glBlue]); + RGFW_GL_ADD_ATTRIB(RGFW_GL_BLUE_SIZE, RGFW_GL_HINTS[RGFW_glGreen]); + #endif + + #if defined(RGFW_X11) || defined(RGFW_WINDOWS) + RGFW_GL_ADD_ATTRIB(RGFW_GL_ACCUM_RED_SIZE, RGFW_GL_HINTS[RGFW_glAccumRed]); + RGFW_GL_ADD_ATTRIB(RGFW_GL_ACCUM_GREEN_SIZE, RGFW_GL_HINTS[RGFW_glAccumBlue]); + RGFW_GL_ADD_ATTRIB(RGFW_GL_ACCUM_BLUE_SIZE, RGFW_GL_HINTS[RGFW_glAccumGreen]); + RGFW_GL_ADD_ATTRIB(RGFW_GL_ACCUM_ALPHA_SIZE, RGFW_GL_HINTS[RGFW_glAccumAlpha]); + RGFW_GL_ADD_ATTRIB(RGFW_GL_SRGB, RGFW_GL_HINTS[RGFW_glSRGB]); + RGFW_GL_ADD_ATTRIB(RGFW_GL_NOERROR, RGFW_GL_HINTS[RGFW_glNoError]); + + if (RGFW_GL_HINTS[RGFW_glReleaseBehavior] == RGFW_releaseFlush) { + RGFW_GL_ADD_ATTRIB(RGFW_GL_RELEASE_BEHAVIOR, RGFW_GL_CONTEXT_RELEASE); + } else if (RGFW_GL_HINTS[RGFW_glReleaseBehavior] == RGFW_glReleaseNone) { + RGFW_GL_ADD_ATTRIB(RGFW_GL_RELEASE_BEHAVIOR, RGFW_GL_CONTEXT_NONE); + } + + i32 flags = 0; + if (RGFW_GL_HINTS[RGFW_glDebug]) flags |= RGFW_GL_DEBUG_BIT; + if (RGFW_GL_HINTS[RGFW_glRobustness]) flags |= RGFW_GL_ROBUST_BIT; + RGFW_GL_ADD_ATTRIB(RGFW_GL_FLAGS, flags); + #else + i32 accumSize = (i32)(RGFW_GL_HINTS[RGFW_glAccumRed] + RGFW_GL_HINTS[RGFW_glAccumGreen] + RGFW_GL_HINTS[RGFW_glAccumBlue] + RGFW_GL_HINTS[RGFW_glAccumAlpha]) / 4; + RGFW_GL_ADD_ATTRIB(14, accumSize); + #endif + + #ifndef RGFW_X11 + RGFW_GL_ADD_ATTRIB(RGFW_GL_SAMPLES, RGFW_GL_HINTS[RGFW_glSamples]); + #endif + + #ifdef RGFW_MACOS + if (useSoftware) { + RGFW_GL_ADD_ATTRIB(70, kCGLRendererGenericFloatID); + } else { + attribs[index] = RGFW_GL_RENDER_TYPE; + index += 1; + } + #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_GL_HINTS[RGFW_glMajor] >= 4 || RGFW_GL_HINTS[RGFW_glMajor] >= 3) { + attribs[index + 1] = (i32) ((RGFW_GL_HINTS[RGFW_glMajor] >= 4) ? 0x4100 : 0x3200); + } + #endif + + RGFW_GL_ADD_ATTRIB(0, 0); + + return attribs; +} + +/* EGL only (no OSMesa nor normal OPENGL) */ +#elif defined(RGFW_EGL) + +#include + +#if defined(RGFW_LINK_EGL) + typedef EGLBoolean(EGLAPIENTRY* PFN_eglInitialize)(EGLDisplay, EGLint*, EGLint*); + + PFNEGLINITIALIZEPROC eglInitializeSource; + PFNEGLGETCONFIGSPROC eglGetConfigsSource; + PFNEGLCHOOSECONFIgamepadROC eglChooseConfigSource; + PFNEGLCREATEWINDOWSURFACEPROC eglCreateWindowSurfaceSource; + PFNEGLCREATECONTEXTPROC eglCreateContextSource; + PFNEGLMAKECURRENTPROC eglMakeCurrentSource; + PFNEGLGETDISPLAYPROC eglGetDisplaySource; + PFNEGLSWAPBUFFERSPROC eglSwapBuffersSource; + PFNEGLSWAPINTERVALPROC eglSwapIntervalSource; + PFNEGLBINDAPIPROC eglBindAPISource; + PFNEGLDESTROYCONTEXTPROC eglDestroyContextSource; + PFNEGLTERMINATEPROC eglTerminateSource; + PFNEGLDESTROYSURFACEPROC eglDestroySurfaceSource; + + #define eglInitialize eglInitializeSource + #define eglGetConfigs eglGetConfigsSource + #define eglChooseConfig eglChooseConfigSource + #define eglCreateWindowSurface eglCreateWindowSurfaceSource + #define eglCreateContext eglCreateContextSource + #define eglMakeCurrent eglMakeCurrentSource + #define eglGetDisplay eglGetDisplaySource + #define eglSwapBuffers eglSwapBuffersSource + #define eglSwapInterval eglSwapIntervalSource + #define eglBindAPI eglBindAPISource + #define eglDestroyContext eglDestroyContextSource + #define eglTerminate eglTerminateSource + #define eglDestroySurface eglDestroySurfaceSource; +#endif + + +#define EGL_SURFACE_MAJOR_VERSION_KHR 0x3098 +#define EGL_SURFACE_MINOR_VERSION_KHR 0x30fb + +#ifndef RGFW_GL_ADD_ATTRIB +#define RGFW_GL_ADD_ATTRIB(attrib, attVal) \ + if (attVal) { \ + attribs[index] = attrib;\ + attribs[index + 1] = attVal;\ + index += 2;\ + } +#endif + + +void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software) { + RGFW_UNUSED(software); +#if defined(RGFW_LINK_EGL) + eglInitializeSource = (PFNEGLINITIALIZEPROC) eglGetProcAddress("eglInitialize"); + eglGetConfigsSource = (PFNEGLGETCONFIGSPROC) eglGetProcAddress("eglGetConfigs"); + eglChooseConfigSource = (PFNEGLCHOOSECONFIgamepadROC) eglGetProcAddress("eglChooseConfig"); + eglCreateWindowSurfaceSource = (PFNEGLCREATEWINDOWSURFACEPROC) eglGetProcAddress("eglCreateWindowSurface"); + eglCreateContextSource = (PFNEGLCREATECONTEXTPROC) eglGetProcAddress("eglCreateContext"); + eglMakeCurrentSource = (PFNEGLMAKECURRENTPROC) eglGetProcAddress("eglMakeCurrent"); + eglGetDisplaySource = (PFNEGLGETDISPLAYPROC) eglGetProcAddress("eglGetDisplay"); + eglSwapBuffersSource = (PFNEGLSWAPBUFFERSPROC) eglGetProcAddress("eglSwapBuffers"); + eglSwapIntervalSource = (PFNEGLSWAPINTERVALPROC) eglGetProcAddress("eglSwapInterval"); + eglBindAPISource = (PFNEGLBINDAPIPROC) eglGetProcAddress("eglBindAPI"); + eglDestroyContextSource = (PFNEGLDESTROYCONTEXTPROC) eglGetProcAddress("eglDestroyContext"); + eglTerminateSource = (PFNEGLTERMINATEPROC) eglGetProcAddress("eglTerminate"); + eglDestroySurfaceSource = (PFNEGLDESTROYSURFACEPROC) eglGetProcAddress("eglDestroySurface"); + + RGFW_ASSERT(eglInitializeSource != NULL && + eglGetConfigsSource != NULL && + eglChooseConfigSource != NULL && + eglCreateWindowSurfaceSource != NULL && + eglCreateContextSource != NULL && + eglMakeCurrentSource != NULL && + eglGetDisplaySource != NULL && + eglSwapBuffersSource != NULL && + eglSwapIntervalsSource != NULL && + eglBindAPISource != NULL && + eglDestroyContextSource != NULL && + eglTerminateSource != NULL && + eglDestroySurfaceSource != NULL); +#endif /* RGFW_LINK_EGL */ + +#ifdef RGFW_WAYLAND + if (RGFW_useWaylandBool) + win->src.eglWindow = wl_egl_window_create(win->src.surface, win->r.w, win->r.h); +#endif + + #ifdef RGFW_WINDOWS + win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.hdc); + #elif defined(RGFW_MACOS) + win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType)0); + #elif defined(RGFW_WAYLAND) + if (RGFW_useWaylandBool) + win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.wl_display); + else + win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.display); + #else + win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.display); + #endif + + EGLint major, minor; + + eglInitialize(win->src.EGL_display, &major, &minor); + + #ifndef EGL_OPENGL_ES1_BIT + #define EGL_OPENGL_ES1_BIT 0x1 + #endif + + EGLint egl_config[24] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, + #ifdef RGFW_OPENGL_ES1 + EGL_OPENGL_ES1_BIT, + #elif defined(RGFW_OPENGL_ES3) + EGL_OPENGL_ES3_BIT, + #elif defined(RGFW_OPENGL_ES2) + EGL_OPENGL_ES2_BIT, + #else + EGL_OPENGL_BIT, + #endif + EGL_NONE, EGL_NONE + }; + + { + size_t index = 7; + EGLint* attribs = egl_config; + + RGFW_GL_ADD_ATTRIB(EGL_RED_SIZE, RGFW_GL_HINTS[RGFW_glRed]); + RGFW_GL_ADD_ATTRIB(EGL_GREEN_SIZE, RGFW_GL_HINTS[RGFW_glBlue]); + RGFW_GL_ADD_ATTRIB(EGL_BLUE_SIZE, RGFW_GL_HINTS[RGFW_glGreen]); + RGFW_GL_ADD_ATTRIB(EGL_ALPHA_SIZE, RGFW_GL_HINTS[RGFW_glAlpha]); + RGFW_GL_ADD_ATTRIB(EGL_DEPTH_SIZE, RGFW_GL_HINTS[RGFW_glDepth]); + + if (RGFW_GL_HINTS[RGFW_glSRGB]) + RGFW_GL_ADD_ATTRIB(0x3089, RGFW_GL_HINTS[RGFW_glSRGB]); + + RGFW_GL_ADD_ATTRIB(EGL_NONE, EGL_NONE); + } + + EGLConfig config; + EGLint numConfigs; + eglChooseConfig(win->src.EGL_display, egl_config, &config, 1, &numConfigs); + + #if defined(RGFW_MACOS) + void* layer = RGFW_cocoaGetLayer(); + + RGFW_window_cocoaSetLayer(win, layer); + + win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) layer, NULL); + #elif defined(RGFW_WINDOWS) + win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL); + #elif defined(RGFW_WAYLAND) + if (RGFW_useWaylandBool) + win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.eglWindow, NULL); + else + win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL); + #else + win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL); + #endif + + EGLint attribs[12]; + size_t index = 0; + +#ifdef RGFW_OPENGL_ES1 + RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_CLIENT_VERSION, 1); +#elif defined(RGFW_OPENGL_ES2) + RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_CLIENT_VERSION, 2); +#elif defined(RGFW_OPENGL_ES3) + RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_CLIENT_VERSION, 3); +#endif + + RGFW_GL_ADD_ATTRIB(EGL_STENCIL_SIZE, RGFW_GL_HINTS[RGFW_glStencil]); + RGFW_GL_ADD_ATTRIB(EGL_SAMPLES, RGFW_GL_HINTS[RGFW_glSamples]); + + if (RGFW_GL_HINTS[RGFW_glDoubleBuffer] == 0) + RGFW_GL_ADD_ATTRIB(EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER); + + if (RGFW_GL_HINTS[RGFW_glMajor]) { + RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MAJOR_VERSION, RGFW_GL_HINTS[RGFW_glMajor]); + RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MINOR_VERSION, RGFW_GL_HINTS[RGFW_glMinor]); + + if (RGFW_GL_HINTS[RGFW_glProfile] == RGFW_glCore) { + 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); + } + } + + RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_ROBUST_ACCESS, RGFW_GL_HINTS[RGFW_glRobustness]); + RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_DEBUG, RGFW_GL_HINTS[RGFW_glDebug]); + if (RGFW_GL_HINTS[RGFW_glReleaseBehavior] == RGFW_releaseFlush) { + RGFW_GL_ADD_ATTRIB(0x2097, 0x2098); + } else { + RGFW_GL_ADD_ATTRIB(0x2097, 0x0000); + } + + RGFW_GL_ADD_ATTRIB(EGL_NONE, EGL_NONE); + + #if defined(RGFW_OPENGL_ES1) || defined(RGFW_OPENGL_ES2) || defined(RGFW_OPENGL_ES3) + eglBindAPI(EGL_OPENGL_ES_API); + #else + eglBindAPI(EGL_OPENGL_API); + #endif + + win->src.EGL_context = eglCreateContext(win->src.EGL_display, config, EGL_NO_CONTEXT, attribs); + + if (win->src.EGL_context == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errEGLContext, RGFW_DEBUG_CTX(win, 0), "failed to create an EGL opengl context"); + return; + } + + 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); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "EGL opengl context initalized"); +} + +void RGFW_window_freeOpenGL(RGFW_window* win) { + if (win->src.EGL_display == NULL) return; + + eglDestroySurface(win->src.EGL_display, win->src.EGL_surface); + eglDestroyContext(win->src.EGL_display, win->src.EGL_context); + eglTerminate(win->src.EGL_display); + win->src.EGL_display = NULL; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "EGL opengl context freed"); +} + +void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { + if (win == NULL) + eglMakeCurrent(_RGFW.root->src.EGL_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + else { + eglMakeCurrent(win->src.EGL_display, win->src.EGL_surface, win->src.EGL_surface, win->src.EGL_context); + } +} + +void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) { eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); } + +void* RGFW_getCurrent_OpenGL(void) { return eglGetCurrentContext(); } + +#ifdef RGFW_APPLE +void* RGFWnsglFramework = NULL; +#elif defined(RGFW_WINDOWS) +HMODULE RGFW_wgl_dll = NULL; +#endif + +RGFW_proc RGFW_getProcAddress(const char* procname) { + #if defined(RGFW_WINDOWS) + RGFW_proc proc = (RGFW_proc) GetProcAddress(RGFW_wgl_dll, procname); + + if (proc) + return proc; + #endif + + return (RGFW_proc) eglGetProcAddress(procname); +} + +void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { + RGFW_ASSERT(win != NULL); + + eglSwapInterval(win->src.EGL_display, swapInterval); + +} + +#endif /* RGFW_EGL */ + +/* + end of RGFW_EGL defines +*/ +#endif /* end of RGFW_GL (OpenGL, EGL, OSMesa )*/ + +/* + RGFW_VULKAN defines +*/ +#ifdef RGFW_VULKAN +#ifdef RGFW_MACOS +#include +#endif + +const char** RGFW_getVKRequiredInstanceExtensions(size_t* count) { + static const char* arr[2] = {VK_KHR_SURFACE_EXTENSION_NAME}; + arr[1] = RGFW_VK_SURFACE; + if (count != NULL) *count = 2; + + return (const char**)arr; +} + +VkResult RGFW_window_createVKSurface(RGFW_window* win, VkInstance instance, VkSurfaceKHR* surface) { + RGFW_ASSERT(win != NULL); RGFW_ASSERT(instance); + RGFW_ASSERT(surface != NULL); + + *surface = VK_NULL_HANDLE; + +#ifdef RGFW_X11 + RGFW_GOTO_WAYLAND(0); + VkXlibSurfaceCreateInfoKHR x11 = { VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, 0, 0, (Display*) win->src.display, (Window) win->src.window }; + return vkCreateXlibSurfaceKHR(instance, &x11, NULL, surface); +#endif +#if defined(RGFW_WAYLAND) +wayland: + VkWaylandSurfaceCreateInfoKHR wayland = { VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, 0, 0, (struct wl_display*) win->src.wl_display, (struct wl_surface*) win->src.surface }; + return vkCreateWaylandSurfaceKHR(instance, &wayland, NULL, surface); +#elif defined(RGFW_WINDOWS) + VkWin32SurfaceCreateInfoKHR win32 = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, 0, 0, GetModuleHandle(NULL), (HWND)win->src.window }; + + return vkCreateWin32SurfaceKHR(instance, &win32, NULL, surface); +#elif defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11) + void* contentView = ((void* (*)(id, SEL))objc_msgSend)((id)win->src.window, sel_getUid("contentView")); + VkMacOSSurfaceCreateFlagsMVK macos = { VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK, 0, 0, win->src.display, (void*)contentView }; + + return vkCreateMacOSSurfaceMVK(instance, &macos, NULL, surface); +#endif +} + + +RGFW_bool RGFW_getVKPresentationSupport(VkInstance instance, VkPhysicalDevice physicalDevice, u32 queueFamilyIndex) { + RGFW_ASSERT(instance); + if (_RGFW.windowCount == -1) RGFW_init(); +#ifdef RGFW_X11 + RGFW_GOTO_WAYLAND(0); + Visual* visual = DefaultVisual(_RGFW.display, DefaultScreen(_RGFW.display)); + if (_RGFW.root) + visual = _RGFW.root->src.visual.visual; + + RGFW_bool out = vkGetPhysicalDeviceXlibPresentationSupportKHR(physicalDevice, queueFamilyIndex, _RGFW.display, XVisualIDFromVisual(visual)); + return out; +#endif +#if defined(RGFW_WAYLAND) +wayland: + RGFW_bool wlout = vkGetPhysicalDeviceWaylandPresentationSupportKHR(physicalDevice, queueFamilyIndex, _RGFW.wl_display); + return wlout; +#elif defined(RGFW_WINDOWS) +#elif defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11) + return RGFW_FALSE; /* TODO */ +#endif +} +#endif /* end of RGFW_vulkan */ + +/* +This is where OS specific stuff starts +*/ + + +#if (defined(RGFW_WAYLAND) || defined(RGFW_X11)) && !defined(RGFW_NO_LINUX) + int RGFW_eventWait_forceStop[] = {0, 0, 0}; /* for wait events */ + + #if defined(__linux__) + #include + #include + #include + #include + + u32 RGFW_linux_updateGamepad(RGFW_window* win); + u32 RGFW_linux_updateGamepad(RGFW_window* win) { + /* check for new gamepads */ + static const char* str[] = {"/dev/input/js0", "/dev/input/js1", "/dev/input/js2", "/dev/input/js3", "/dev/input/js4", "/dev/input/js5"}; + static u8 RGFW_rawGamepads[6]; + { + u16 i; + for (i = 0; i < 6; i++) { + u16 index = RGFW_gamepadCount; + if (RGFW_rawGamepads[i]) { + struct input_id device_info; + if (ioctl(RGFW_rawGamepads[i], EVIOCGID, &device_info) == -2) { + if (errno == ENODEV) { + RGFW_rawGamepads[i] = 0; + } + } + continue; + } + + i32 js = open(str[i], O_RDONLY); + + if (js <= 0) + break; + + if (RGFW_gamepadCount >= 4) { + close(js); + break; + } + + RGFW_rawGamepads[i] = 1; + + int axes, buttons; + if (ioctl(js, JSIOCGAXES, &axes) < 0 || ioctl(js, JSIOCGBUTTONS, &buttons) < 0) { + close(js); + continue; + } + + if (buttons <= 5 || buttons >= 30) { + close(js); + continue; + } + + RGFW_gamepadCount++; + + RGFW_gamepads[index] = js; + + ioctl(js, JSIOCGNAME(sizeof(RGFW_gamepads_name[index])), RGFW_gamepads_name[index]); + RGFW_gamepads_name[index][sizeof(RGFW_gamepads_name[index]) - 1] = 0; + + u8 j; + for (j = 0; j < 16; j++) + RGFW_gamepadPressed[index][j] = (RGFW_keyState){0, 0}; + + win->event.type = RGFW_gamepadConnected; + + RGFW_gamepads_type[index] = RGFW_gamepadUnknown; + if (RGFW_STRSTR(RGFW_gamepads_name[index], "Microsoft") || RGFW_STRSTR(RGFW_gamepads_name[index], "X-Box")) + RGFW_gamepads_type[index] = RGFW_gamepadMicrosoft; + else if (RGFW_STRSTR(RGFW_gamepads_name[index], "PlayStation") || RGFW_STRSTR(RGFW_gamepads_name[index], "PS3") || RGFW_STRSTR(RGFW_gamepads_name[index], "PS4") || RGFW_STRSTR(RGFW_gamepads_name[index], "PS5")) + RGFW_gamepads_type[index] = RGFW_gamepadSony; + else if (RGFW_STRSTR(RGFW_gamepads_name[index], "Nintendo")) + RGFW_gamepads_type[index] = RGFW_gamepadNintendo; + else if (RGFW_STRSTR(RGFW_gamepads_name[index], "Logitech")) + RGFW_gamepads_type[index] = RGFW_gamepadLogitech; + + win->event.gamepad = index; + RGFW_gamepadCallback(win, index, 1); + return 1; + } + } + /* check gamepad events */ + u8 i; + + for (i = 0; i < RGFW_gamepadCount; i++) { + struct js_event e; + if (RGFW_gamepads[i] == 0) + continue; + + i32 flags = fcntl(RGFW_gamepads[i], F_GETFL, 0); + fcntl(RGFW_gamepads[i], F_SETFL, flags | O_NONBLOCK); + + ssize_t bytes; + while ((bytes = read(RGFW_gamepads[i], &e, sizeof(e))) > 0) { + switch (e.type) { + case JS_EVENT_BUTTON: { + size_t typeIndex = 0; + if (RGFW_gamepads_type[i] == RGFW_gamepadMicrosoft) typeIndex = 1; + else if (RGFW_gamepads_type[i] == RGFW_gamepadLogitech) typeIndex = 2; + + win->event.type = e.value ? RGFW_gamepadButtonPressed : RGFW_gamepadButtonReleased; + u8 RGFW_linux2RGFW[3][RGFW_gamepadR3 + 8] = {{ /* ps */ + RGFW_gamepadA, RGFW_gamepadB, RGFW_gamepadY, RGFW_gamepadX, RGFW_gamepadL1, RGFW_gamepadR1, RGFW_gamepadL2, RGFW_gamepadR2, + RGFW_gamepadSelect, RGFW_gamepadStart, RGFW_gamepadHome, RGFW_gamepadL3, RGFW_gamepadR3, RGFW_gamepadUp, RGFW_gamepadDown, RGFW_gamepadLeft, RGFW_gamepadRight, + },{ /* xbox */ + RGFW_gamepadA, RGFW_gamepadB, RGFW_gamepadX, RGFW_gamepadY, RGFW_gamepadL1, RGFW_gamepadR1, RGFW_gamepadSelect, RGFW_gamepadStart, + RGFW_gamepadHome, RGFW_gamepadL3, RGFW_gamepadR3, 255, 255, RGFW_gamepadUp, RGFW_gamepadDown, RGFW_gamepadLeft, RGFW_gamepadRight + },{ /* Logitech */ + RGFW_gamepadA, RGFW_gamepadB, RGFW_gamepadX, RGFW_gamepadY, RGFW_gamepadL1, RGFW_gamepadR1, RGFW_gamepadL2, RGFW_gamepadR2, + RGFW_gamepadSelect, RGFW_gamepadStart, RGFW_gamepadHome, RGFW_gamepadL3, RGFW_gamepadR3, RGFW_gamepadUp, RGFW_gamepadDown, RGFW_gamepadLeft, RGFW_gamepadRight + } + }; + + win->event.button = RGFW_linux2RGFW[typeIndex][e.number]; + win->event.gamepad = i; + if (win->event.button == 255) break; + + RGFW_gamepadPressed[i][win->event.button].prev = RGFW_gamepadPressed[i][win->event.button].current; + RGFW_gamepadPressed[i][win->event.button].current = RGFW_BOOL(e.value); + RGFW_gamepadButtonCallback(win, i, win->event.button, RGFW_BOOL(e.value)); + + return 1; + } + case JS_EVENT_AXIS: { + size_t axis = e.number / 2; + if (axis == 2) axis = 1; + + ioctl(RGFW_gamepads[i], JSIOCGAXES, &win->event.axisesCount); + win->event.axisesCount = 2; + + if (axis < 3) { + if (e.number == 0 || e.number == 3) + RGFW_gamepadAxes[i][axis].x = (i32)((e.value / 32767.0f) * 100); + else if (e.number == 1 || e.number == 4) { + RGFW_gamepadAxes[i][axis].y = (i32)((e.value / 32767.0f) * 100); + } + } + + win->event.axis[axis] = RGFW_gamepadAxes[i][axis]; + win->event.type = RGFW_gamepadAxisMove; + win->event.gamepad = i; + win->event.whichAxis = (u8)axis; + RGFW_gamepadAxisCallback(win, i, win->event.axis, win->event.axisesCount, win->event.whichAxis); + return 1; + } + default: break; + } + } + if (bytes == -1 && errno == ENODEV) { + RGFW_gamepadCount--; + close(RGFW_gamepads[i]); + RGFW_gamepads[i] = 0; + + win->event.type = RGFW_gamepadDisconnected; + win->event.gamepad = i; + RGFW_gamepadCallback(win, i, 0); + return 1; + } + } + return 0; + } + + #endif +#endif + + + +/* + + Start of Wayland defines + + +*/ + +#ifdef RGFW_WAYLAND +/* +Wayland TODO: (out of date) +- fix RGFW_keyPressed lock state + + RGFW_windowMoved, the window was moved (by the user) + RGFW_windowResized the window was resized (by the user), [on WASM 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_DNDInit + +- window args: + #define RGFW_windowNoResize the window cannot be resized by the user + #define RGFW_windowAllowDND the window supports drag and drop + #define RGFW_scaleToMonitor 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; + +/* wayland global garbage (wayland bad, X11 is fine (ish) (not really)) */ +#include "xdg-shell.h" +#include "xdg-decoration-unstable-v1.h" + +struct xkb_context *xkb_context; +struct xkb_keymap *keymap = NULL; +struct xkb_state *xkb_state = NULL; +enum zxdg_toplevel_decoration_v1_mode client_preferred_mode, RGFW_current_mode; +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; + +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); +} + +const struct xdg_wm_base_listener xdg_wm_base_listener = { + .ping = xdg_wm_base_ping_handler, +}; + +RGFW_bool RGFW_wl_configured = 0; + +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); + RGFW_wl_configured = 1; +} + +const struct xdg_surface_listener xdg_surface_listener = { + .configure = xdg_surface_configure_handler, +}; + +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); + RGFW_UNUSED(width); RGFW_UNUSED(height); +} + +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_eventQueuePush((RGFW_event){.type = RGFW_quit, ._win = win}); + RGFW_windowQuitCallback(win); +} + +void shm_format_handler(void *data, + struct wl_shm *shm, uint32_t format) +{ + RGFW_UNUSED(data); RGFW_UNUSED(shm); RGFW_UNUSED(format); +} + +const struct wl_shm_listener shm_listener = { + .format = shm_format_handler, +}; + +const struct xdg_toplevel_listener xdg_toplevel_listener = { + .configure = xdg_toplevel_configure_handler, + .close = xdg_toplevel_close_handler, +}; + +RGFW_window* RGFW_mouse_win = NULL; + +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_eventQueuePush((RGFW_event){.type = RGFW_mouseEnter, + .point = RGFW_POINT(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)), + ._win = win}); + + RGFW_mouseNotifyCallback(win, win->event.point, RGFW_TRUE); +} +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_eventQueuePush((RGFW_event){.type = RGFW_mouseLeave, + .point = win->event.point, + ._win = win}); + + RGFW_mouseNotifyCallback(win, win->event.point, RGFW_FALSE); +} +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); + + RGFW_ASSERT(RGFW_mouse_win != NULL); + RGFW_eventQueuePush((RGFW_event){.type = RGFW_mousePosChanged, + .point = RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y)), + ._win = RGFW_mouse_win}); + + RGFW_mousePosCallback(RGFW_mouse_win, RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y)), RGFW_mouse_win->event.vector); +} +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); + RGFW_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 = RGFW_BOOL(state); + + RGFW_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonPressed + RGFW_BOOL(state), + .button = (u8)b, + ._win = RGFW_mouse_win}); + RGFW_mouseButtonCallback(RGFW_mouse_win, (u8)b, 0, RGFW_BOOL(state)); +} +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); + RGFW_ASSERT(RGFW_mouse_win != NULL); + + double scroll = wl_fixed_to_double(value); + + RGFW_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonPressed, + .button = RGFW_mouseScrollUp + (scroll < 0), + .scroll = scroll, + ._win = RGFW_mouse_win}); + + RGFW_mouseButtonCallback(RGFW_mouse_win, RGFW_mouseScrollUp + (scroll < 0), scroll, 1); +} + +void RGFW_doNothing(void) { } + +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); +} +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_key_win->_flags |= RGFW_windowFocus; + RGFW_eventQueuePush((RGFW_event){.type = RGFW_focusIn, ._win = RGFW_key_win}); + RGFW_focusCallback(RGFW_key_win, RGFW_TRUE); +} +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_eventQueuePush((RGFW_event){.type = RGFW_focusOut, ._win = win}); + win->_flags &= ~(u32)RGFW_windowFocus; + RGFW_focusCallback(win, RGFW_FALSE); +} +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); + + if (RGFW_key_win == NULL) return; + + xkb_keysym_t keysym = xkb_state_key_get_one_sym(xkb_state, key + 8); + + u32 RGFWkey = RGFW_apiKeyToRGFW(key + 8); + RGFW_keyboard[RGFWkey].prev = RGFW_keyboard[RGFWkey].current; + RGFW_keyboard[RGFWkey].current = RGFW_BOOL(state); + + RGFW_eventQueuePush((RGFW_event){.type = (u8)(RGFW_keyPressed + state), + .key = (u8)RGFWkey, + .keyChar = (u8)keysym, + .repeat = RGFW_isHeld(RGFW_key_win, (u8)RGFWkey), + ._win = RGFW_key_win}); + + RGFW_updateKeyMods(RGFW_key_win, RGFW_BOOL(xkb_keymap_mod_get_index(keymap, "Lock")), RGFW_BOOL(xkb_keymap_mod_get_index(keymap, "Mod2")), RGFW_BOOL(xkb_keymap_mod_get_index(keymap, "ScrollLock"))); + RGFW_keyCallback(RGFW_key_win, (u8)RGFWkey, (u8)keysym, RGFW_key_win->event.keyMod, RGFW_BOOL(state)); +} +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); +} +struct wl_keyboard_listener keyboard_listener = {&keyboard_keymap, &keyboard_enter, &keyboard_leave, &keyboard_key, &keyboard_modifiers, (void (*)(void *, struct wl_keyboard *, +int, int))&RGFW_doNothing}; + +void seat_capabilities (void *data, struct wl_seat *seat, uint32_t capabilities) { + RGFW_UNUSED(data); + struct wl_pointer_listener pointer_listener = (struct wl_pointer_listener){&pointer_enter, &pointer_leave, &pointer_motion, &pointer_button, &pointer_axis, (void (*)(void *, struct wl_pointer *))&RGFW_doNothing, (void (*)(void *, struct wl_pointer *, uint32_t))&RGFW_doNothing, (void (*)(void *, struct wl_pointer *, uint32_t, uint32_t))&RGFW_doNothing, (void (*)(void *, struct wl_pointer *, uint32_t, int32_t))&RGFW_doNothing, (void (*)(void *, struct wl_pointer *, uint32_t, int32_t))&RGFW_doNothing}; + + 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); + } +} +struct wl_seat_listener seat_listener = {&seat_capabilities, (void (*)(void *, struct wl_seat *, const char *))&RGFW_doNothing}; + +void wl_global_registry_handler(void *data, + struct wl_registry *registry, uint32_t id, const char *interface, + uint32_t version) +{ + RGFW_window* win = (RGFW_window*)data; + RGFW_UNUSED(version); + if (RGFW_STRNCMP(interface, "wl_compositor", 16) == 0) { + win->src.compositor = wl_registry_bind(registry, + id, &wl_compositor_interface, 4); + } else if (RGFW_STRNCMP(interface, "xdg_wm_base", 12) == 0) { + win->src.xdg_wm_base = wl_registry_bind(registry, + id, &xdg_wm_base_interface, 1); + } else if (RGFW_STRNCMP(interface, zxdg_decoration_manager_v1_interface.name, 255) == 0) { + decoration_manager = wl_registry_bind(registry, id, &zxdg_decoration_manager_v1_interface, 1); + } else if (RGFW_STRNCMP(interface, "wl_shm", 7) == 0) { + win->src.shm = wl_registry_bind(registry, + id, &wl_shm_interface, 1); + wl_shm_add_listener(win->src.shm, &shm_listener, NULL); + } else if (RGFW_STRNCMP(interface,"wl_seat", 8) == 0) { + win->src.seat = wl_registry_bind(registry, id, &wl_seat_interface, 1); + wl_seat_add_listener(win->src.seat, &seat_listener, NULL); + } +} + +void wl_global_registry_remove(void *data, struct wl_registry *registry, uint32_t name) { RGFW_UNUSED(data); RGFW_UNUSED(registry); RGFW_UNUSED(name); } +const struct wl_registry_listener registry_listener = { + .global = wl_global_registry_handler, + .global_remove = wl_global_registry_remove, +}; + +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); + RGFW_current_mode = mode; +} + +const struct zxdg_toplevel_decoration_v1_listener decoration_listener = { + .configure = decoration_handle_configure, +}; + +void randname(char *buf) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + long r = ts.tv_nsec; + + int i; + for (i = 0; i < 6; ++i) { + buf[i] = (char)('A'+(r&15)+(r&16)*2); + r >>= 5; + } +} + +size_t wl_stringlen(char* name) { + size_t i = 0; + while (name[i]) { i++; } + return i; +} + +int anonymous_shm_open(void) { + char name[] = "/RGFW-wayland-XXXXXX"; + int retries = 100; + + do { + randname(name + wl_stringlen(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; +} + +void wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) { + RGFW_UNUSED(data); RGFW_UNUSED(cb); RGFW_UNUSED(time); + + #ifdef RGFW_BUFFER + RGFW_window* win = (RGFW_window*)data; + 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 +} + +const struct wl_callback_listener wl_surface_frame_listener = { + .done = wl_surface_frame_done, +}; +#endif /* RGFW_WAYLAND */ +/* + End of Wayland defines +*/ + +/* + + +Start of Linux / Unix defines + + +*/ + +#ifdef RGFW_UNIX +#if !defined(RGFW_NO_X11_CURSOR) && defined(RGFW_X11) +#include +#endif + +#include + +#ifndef RGFW_NO_DPI +#include +#include +#endif + +#include +#include +#include +#include + +#include /* for converting keycode to string */ +#include /* for hiding */ +#include +#include +#include + +#include /* for data limits (mainly used in drag and drop functions) */ +#include + +/* atoms needed for drag and drop */ +Atom XdndAware, XtextPlain, XtextUriList; +Atom RGFW_XUTF8_STRING = 0; + +Atom wm_delete_window = 0, RGFW_XCLIPBOARD = 0; + +#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) + typedef XcursorImage* (*PFN_XcursorImageCreate)(int, int); + typedef void (*PFN_XcursorImageDestroy)(XcursorImage*); + typedef Cursor(*PFN_XcursorImageLoadCursor)(Display*, const XcursorImage*); +#endif +#ifdef RGFW_OPENGL + typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); +#endif + +#if !defined(RGFW_NO_X11_XI_PRELOAD) + typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int); + PFN_XISelectEvents XISelectEventsSRC = NULL; + #define XISelectEvents XISelectEventsSRC + + void* X11Xihandle = NULL; +#endif + +#if !defined(RGFW_NO_X11_EXT_PRELOAD) + typedef void (* PFN_XSyncIntToValue)(XSyncValue*, int); + PFN_XSyncIntToValue XSyncIntToValueSRC = NULL; + #define XSyncIntToValue XSyncIntToValueSRC + + typedef Status (* PFN_XSyncSetCounter)(Display*, XSyncCounter, XSyncValue); + PFN_XSyncSetCounter XSyncSetCounterSRC = NULL; + #define XSyncSetCounter XSyncSetCounterSRC + + typedef XSyncCounter (* PFN_XSyncCreateCounter)(Display*, XSyncValue); + PFN_XSyncCreateCounter XSyncCreateCounterSRC = NULL; + #define XSyncCreateCounter XSyncCreateCounterSRC + + typedef void (* PFN_XShapeCombineMask)(Display*,Window,int,int,int,Pixmap,int); + PFN_XShapeCombineMask XShapeCombineMaskSRC; + #define XShapeCombineMask XShapeCombineMaskSRC + + typedef void (* PFN_XShapeCombineRegion)(Display*,Window,int,int,int,Region,int); + PFN_XShapeCombineRegion XShapeCombineRegionSRC; + #define XShapeCombineRegion XShapeCombineRegionSRC + void* X11XEXThandle = NULL; +#endif + +#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) + PFN_XcursorImageLoadCursor XcursorImageLoadCursorSRC = NULL; + PFN_XcursorImageCreate XcursorImageCreateSRC = NULL; + PFN_XcursorImageDestroy XcursorImageDestroySRC = NULL; + + #define XcursorImageLoadCursor XcursorImageLoadCursorSRC + #define XcursorImageCreate XcursorImageCreateSRC + #define XcursorImageDestroy XcursorImageDestroySRC + + void* X11Cursorhandle = NULL; +#endif + +const char* RGFW_instName = NULL; +void RGFW_setXInstName(const char* name) { + RGFW_instName = name; +} + +#if defined(RGFW_OPENGL) && !defined(RGFW_EGL) +RGFW_proc RGFW_getProcAddress(const char* procname) { return (RGFW_proc) glXGetProcAddress((GLubyte*) procname); } +#endif + +void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area) { + RGFW_GOTO_WAYLAND(0); + +#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + win->buffer = (u8*)buffer; + win->bufferSize = area; + + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoBuffer, RGFW_DEBUG_CTX(win, 0), "createing a 4 channel buffer"); + #ifdef RGFW_X11 + #ifdef RGFW_OSMESA + win->src.ctx = OSMesaCreateContext(OSMESA_BGRA, NULL); + OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, area.w, area.h); + OSMesaPixelStore(OSMESA_Y_UP, 0); + #endif + + win->src.bitmap = XCreateImage( + win->src.display, win->src.visual.visual, (u32)win->src.visual.depth, + ZPixmap, 0, NULL, area.w, area.h, 32, 0 + ); + #endif + #ifdef RGFW_WAYLAND + wayland: {} + u32 size = (u32)(win->r.w * win->r.h * 4); + int fd = create_shm_file(size); + if (fd < 0) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, RGFW_DEBUG_CTX(win, (u32)fd),"Failed to create a buffer."); + exit(1); + } + + win->src.buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (win->src.buffer == MAP_FAILED) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, RGFW_DEBUG_CTX(win, 0), "mmap failed!"); + close(fd); + exit(1); + } + + win->_flags |= RGFW_BUFFER_ALLOC; + + struct wl_shm_pool* pool = wl_shm_create_pool(win->src.shm, fd, (i32)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 < area.w * area.h * 4; i += 4) { + RGFW_MEMCPY(&win->buffer[i], color, 4); + } + + RGFW_MEMCPY(win->src.buffer, win->buffer, (size_t)(win->r.w * win->r.h * 4)); + + #if defined(RGFW_OSMESA) + win->src.ctx = OSMesaCreateContext(OSMESA_BGRA, NULL); + OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, area.w, area.h); + OSMesaPixelStore(OSMESA_Y_UP, 0); + #endif + #endif +#else + #ifdef RGFW_WAYLAND + wayland:{} + #endif + + RGFW_UNUSED(win); RGFW_UNUSED(buffer); RGFW_UNUSED(area); +#endif +} + +#define RGFW_LOAD_ATOM(name) \ + static Atom name = 0; \ + if (name == 0) name = XInternAtom(_RGFW.display, #name, False); + +void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { + RGFW_setBit(&win->_flags, RGFW_windowNoBorder, !border); + + RGFW_GOTO_WAYLAND(0); + #ifdef RGFW_X11 + RGFW_LOAD_ATOM(_MOTIF_WM_HINTS); + + struct __x11WindowHints { + unsigned long flags, functions, decorations, status; + long input_mode; + } hints; + hints.flags = 2; + hints.decorations = border; + + XChangeProperty(win->src.display, win->src.window, _MOTIF_WM_HINTS, _MOTIF_WM_HINTS, 32, + PropModeReplace, (u8*)&hints, 5 + ); + + if (RGFW_window_isHidden(win) == 0) { + RGFW_window_hide(win); + RGFW_window_show(win); + } + + #endif + #ifdef RGFW_WAYLAND + wayland: + RGFW_UNUSED(win); RGFW_UNUSED(border); + #endif +} + +void RGFW_releaseCursor(RGFW_window* win) { +RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 + XUngrabPointer(win->src.display, CurrentTime); + + /* disable raw input */ + unsigned char mask[] = { 0 }; + XIEventMask em; + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; + + XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1); +#endif +#ifdef RGFW_WAYLAND + wayland: + RGFW_UNUSED(win); +#endif +} + +void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { +RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 + /* 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; + + XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1); + + XGrabPointer(win->src.display, win->src.window, True, PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); + RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (i32)(r.w / 2), win->r.y + (i32)(r.h / 2))); +#endif +#ifdef RGFW_WAYLAND + wayland: + RGFW_UNUSED(win); RGFW_UNUSED(r); +#endif +} + +#define RGFW_LOAD_LIBRARY(x, lib) if (x == NULL) x = dlopen(lib, RTLD_LAZY | RTLD_LOCAL) +#define RGFW_PROC_DEF(proc, name) if (name##SRC == NULL && proc != NULL) { \ + void* ptr = dlsym(proc, #name); \ + if (ptr != NULL) memcpy(&name##SRC, &ptr, sizeof(PFN_##name)); \ +} + + + +void RGFW_window_getVisual(RGFW_window* win, RGFW_bool software) { +#if defined(RGFW_OPENGL) && !defined(RGFW_EGL) + i32* visual_attribs = RGFW_initFormatAttribs(software); + i32 fbcount; + GLXFBConfig* fbc = glXChooseFBConfig(win->src.display, DefaultScreen(win->src.display), visual_attribs, &fbcount); + + i32 best_fbc = -1; + i32 best_depth = 0; + i32 best_samples = 0; + + if (fbcount == 0) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to find any valid GLX visual configs"); + return; + } + + i32 i; + for (i = 0; i < fbcount; i++) { + XVisualInfo* vi = glXGetVisualFromFBConfig(win->src.display, fbc[i]); + if (vi == NULL) + continue; + + i32 samp_buf, samples; + glXGetFBConfigAttrib(win->src.display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf); + glXGetFBConfigAttrib(win->src.display, fbc[i], GLX_SAMPLES, &samples); + + if (best_fbc == -1) best_fbc = i; + if ((!(win->_flags & RGFW_windowTransparent) || vi->depth == 32) && best_depth == 0) { + best_fbc = i; + best_depth = vi->depth; + } + if ((!(win->_flags & RGFW_windowTransparent) || vi->depth == 32) && samples <= RGFW_GL_HINTS[RGFW_glSamples] && samples > best_samples) { + best_fbc = i; + best_depth = vi->depth; + best_samples = samples; + } + XFree(vi); + } + + if (best_fbc == -1) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to get a valid GLX visual"); + return; + } + + win->src.bestFbc = fbc[best_fbc]; + XVisualInfo* vi = glXGetVisualFromFBConfig(win->src.display, win->src.bestFbc); + if (vi->depth != 32 && (win->_flags & RGFW_windowTransparent)) + RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, RGFW_DEBUG_CTX(win, 0), "Failed to to find a matching visual with a 32-bit depth"); + + if (best_samples < RGFW_GL_HINTS[RGFW_glSamples]) + RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, RGFW_DEBUG_CTX(win, 0), "Failed to load matching sampiling"); + + XFree(fbc); + win->src.visual = *vi; +#else + RGFW_UNUSED(software); + win->src.visual.visual = DefaultVisual(win->src.display, DefaultScreen(win->src.display)); + win->src.visual.depth = DefaultDepth(win->src.display, DefaultScreen(win->src.display)); + if (win->_flags & RGFW_windowTransparent) { + XMatchVisualInfo(win->src.display, DefaultScreen(win->src.display), 32, TrueColor, &win->src.visual); /*!< for RGBA backgrounds */ + if (win->src.visual.depth != 32) + RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, RGFW_DEBUG_CTX(win, 0), "Failed to load a 32-bit depth"); + } +#endif +} + +#ifndef RGFW_EGL +void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software) { + RGFW_UNUSED(software); +#ifdef RGFW_OPENGL + i32 context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 }; + context_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB; + if (RGFW_GL_HINTS[RGFW_glProfile] == RGFW_glCore) + context_attribs[1] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB; + else + context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + + if (RGFW_GL_HINTS[RGFW_glMinor] || RGFW_GL_HINTS[RGFW_glMajor]) { + context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB; + context_attribs[3] = RGFW_GL_HINTS[RGFW_glMajor]; + context_attribs[4] = GLX_CONTEXT_MINOR_VERSION_ARB; + context_attribs[5] = RGFW_GL_HINTS[RGFW_glMinor]; + } + + glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; + glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) + glXGetProcAddressARB((GLubyte*) "glXCreateContextAttribsARB"); + + GLXContext ctx = NULL; + if (_RGFW.root != NULL && _RGFW.root != win) { + ctx = _RGFW.root->src.ctx; + RGFW_window_makeCurrent_OpenGL(_RGFW.root); + } + + if (glXCreateContextAttribsARB == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "failed to load proc address 'glXCreateContextAttribsARB', loading a generic opengl context"); + win->src.ctx = glXCreateContext(win->src.display, &win->src.visual, ctx, True); + } + else { + win->src.ctx = glXCreateContextAttribsARB(win->src.display, win->src.bestFbc, ctx, True, context_attribs); + XSync(win->src.display, False); + if (win->src.ctx == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "failed to create an opengl context with AttribsARB, loading a generic opengl context"); + win->src.ctx = glXCreateContext(win->src.display, &win->src.visual, ctx, True); + } + } + + glXMakeCurrent(win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context initalized"); +#else + RGFW_UNUSED(win); RGFW_UNUSED(software); +#endif +} + +void RGFW_window_freeOpenGL(RGFW_window* win) { +#ifdef RGFW_OPENGL + if (win->src.ctx == NULL) return; + glXDestroyContext(win->src.display, win->src.ctx); + win->src.ctx = NULL; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context freed"); +#else +RGFW_UNUSED(win); +#endif +} +#endif + + +i32 RGFW_init(void) { + RGFW_GOTO_WAYLAND(1); +#ifdef RGFW_X11 + if (_RGFW.windowCount != -1) return 0; + #ifdef RGFW_USE_XDL + XDL_init(); + #endif + + #if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) + #if defined(__CYGWIN__) + RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor-1.so"); + #elif defined(__OpenBSD__) || defined(__NetBSD__) + RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor.so"); + #else + RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor.so.1"); + #endif + RGFW_PROC_DEF(X11Cursorhandle, XcursorImageCreate); + RGFW_PROC_DEF(X11Cursorhandle, XcursorImageDestroy); + RGFW_PROC_DEF(X11Cursorhandle, XcursorImageLoadCursor); + #endif + + #if !defined(RGFW_NO_X11_XI_PRELOAD) + #if defined(__CYGWIN__) + RGFW_LOAD_LIBRARY(X11Xihandle, "libXi-6.so"); + #elif defined(__OpenBSD__) || defined(__NetBSD__) + RGFW_LOAD_LIBRARY(X11Xihandle, "libXi.so"); + #else + RGFW_LOAD_LIBRARY(X11Xihandle, "libXi.so.6"); + #endif + RGFW_PROC_DEF(X11Xihandle, XISelectEvents); + #endif + + #if !defined(RGFW_NO_X11_EXT_PRELOAD) + #if defined(__CYGWIN__) + RGFW_LOAD_LIBRARY(X11XEXThandle, "libXext-6.so"); + #elif defined(__OpenBSD__) || defined(__NetBSD__) + RGFW_LOAD_LIBRARY(X11XEXThandle, "libXext.so"); + #else + RGFW_LOAD_LIBRARY(X11XEXThandle, "libXext.so.6"); + #endif + RGFW_PROC_DEF(X11XEXThandle, XSyncCreateCounter); + RGFW_PROC_DEF(X11XEXThandle, XSyncIntToValue); + RGFW_PROC_DEF(X11XEXThandle, XSyncSetCounter); + RGFW_PROC_DEF(X11XEXThandle, XShapeCombineRegion); + RGFW_PROC_DEF(X11XEXThandle, XShapeCombineMask); + #endif + + XInitThreads(); /*!< init X11 threading */ + _RGFW.display = XOpenDisplay(0); + XSetWindowAttributes wa; + wa.event_mask = PropertyChangeMask; + _RGFW.helperWindow = XCreateWindow(_RGFW.display, XDefaultRootWindow(_RGFW.display), 0, 0, 1, 1, 0, 0, + InputOnly, DefaultVisual(_RGFW.display, DefaultScreen(_RGFW.display)), CWEventMask, &wa); + + _RGFW.windowCount = 0; + u8 RGFW_blk[] = { 0, 0, 0, 0 }; + _RGFW.hiddenMouse = RGFW_loadMouse(RGFW_blk, RGFW_AREA(1, 1), 4); + _RGFW.clipboard = NULL; + + XkbComponentNamesRec rec; + XkbDescPtr desc = XkbGetMap(_RGFW.display, 0, XkbUseCoreKbd); + XkbDescPtr evdesc; + u8 old[sizeof(RGFW_keycodes) / sizeof(RGFW_keycodes[0])]; + + XkbGetNames(_RGFW.display, XkbKeyNamesMask, desc); + + memset(&rec, 0, sizeof(rec)); + rec.keycodes = (char*)"evdev"; + evdesc = XkbGetKeyboardByName(_RGFW.display, XkbUseCoreKbd, &rec, XkbGBN_KeyNamesMask, XkbGBN_KeyNamesMask, False); + /* memo: RGFW_keycodes[x11 keycode] = rgfw keycode */ + if(evdesc != NULL && desc != NULL){ + for(int i = 0; i < (int)sizeof(RGFW_keycodes) / (int)sizeof(RGFW_keycodes[0]); i++){ + old[i] = RGFW_keycodes[i]; + RGFW_keycodes[i] = 0; + } + for(int i = evdesc->min_key_code; i <= evdesc->max_key_code; i++){ + for(int j = desc->min_key_code; j <= desc->max_key_code; j++){ + if(strncmp(evdesc->names->keys[i].name, desc->names->keys[j].name, XkbKeyNameLength) == 0){ + RGFW_keycodes[j] = old[i]; + break; + } + } + } + XkbFreeKeyboard(desc, 0, True); + XkbFreeKeyboard(evdesc, 0, True); + } +#endif +#ifdef RGFW_WAYLAND +wayland: + _RGFW.wl_display = wl_display_connect(NULL); +#endif + _RGFW.windowCount = 0; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, (RGFW_debugContext){0}, "global context initialized"); + return 0; +} + + +RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) { + RGFW_window_basic_init(win, rect, flags); + +#ifdef RGFW_WAYLAND + win->src.compositor = NULL; +#endif + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 + i64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask | LeaveWindowMask | EnterWindowMask | ExposureMask; /*!< X11 events accepted */ + + win->src.display = XOpenDisplay(NULL); + RGFW_window_getVisual(win, RGFW_BOOL(flags & RGFW_windowOpenglSoftware)); + + /* make X window attrubutes */ + XSetWindowAttributes swa; + Colormap cmap; + swa.colormap = cmap = XCreateColormap(win->src.display, + DefaultRootWindow(win->src.display), + win->src.visual.visual, AllocNone); + + swa.background_pixmap = None; + swa.border_pixel = 0; + swa.event_mask = event_mask; + + swa.background_pixel = 0; + + /* create the window */ + win->src.window = XCreateWindow(win->src.display, DefaultRootWindow(win->src.display), win->r.x, win->r.y, (u32)win->r.w, (u32)win->r.h, + 0, win->src.visual.depth, InputOutput, win->src.visual.visual, + CWColormap | CWBorderPixel | CWBackPixel | CWEventMask, &swa); + + XFreeColors(win->src.display, cmap, NULL, 0, 0); + + win->src.gc = XCreateGC(win->src.display, win->src.window, 0, NULL); + + /* In your .desktop app, if you set the property + StartupWMClass=RGFW that will assoicate the launcher icon + with your application - robrohan */ + if (RGFW_className == NULL) + RGFW_className = (char*)name; + + XClassHint hint; + hint.res_class = (char*)RGFW_className; + if (RGFW_instName == NULL) hint.res_name = (char*)name; + else hint.res_name = (char*)RGFW_instName; + XSetClassHint(win->src.display, win->src.window, &hint); + + #ifndef RGFW_NO_MONITOR + if (flags & RGFW_windowScaleToMonitor) + RGFW_window_scaleToMonitor(win); + #endif + XSelectInput(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) { + wm_delete_window = XInternAtom(win->src.display, "WM_DELETE_WINDOW", False); + RGFW_XUTF8_STRING = XInternAtom(win->src.display, "UTF8_STRING", False); + RGFW_XCLIPBOARD = XInternAtom(win->src.display, "CLIPBOARD", False); + } + + XSetWMProtocols(win->src.display, (Drawable) win->src.window, &wm_delete_window, 1); + /* set the background */ + RGFW_window_setName(win, name); + + XMoveWindow(win->src.display, (Drawable) win->src.window, win->r.x, win->r.y); /*!< move the window to it's proper cords */ + + if (flags & RGFW_windowAllowDND) { /* init drag and drop atoms and turn on drag and drop for this window */ + win->_flags |= RGFW_windowAllowDND; + + /* actions */ + XtextUriList = XInternAtom(win->src.display, "text/uri-list", False); + XtextPlain = XInternAtom(win->src.display, "text/plain", False); + XdndAware = XInternAtom(win->src.display, "XdndAware", False); + const u8 version = 5; + + XChangeProperty(win->src.display, win->src.window, + XdndAware, 4, 32, + PropModeReplace, &version, 1); /*!< turns on drag and drop */ + } + +#ifdef RGFW_ADVANCED_SMOOTH_RESIZE + RGFW_LOAD_ATOM(_NET_WM_SYNC_REQUEST_COUNTER) + RGFW_LOAD_ATOM(_NET_WM_SYNC_REQUEST) + Atom protcols[2] = {_NET_WM_SYNC_REQUEST, wm_delete_window}; + XSetWMProtocols(win->src.display, win->src.window, protcols, 2); + + XSyncValue initial_value; + XSyncIntToValue(&initial_value, 0); + win->src.counter = XSyncCreateCounter(win->src.display, initial_value); + + XChangeProperty(win->src.display, win->src.window, _NET_WM_SYNC_REQUEST_COUNTER, XA_CARDINAL, 32, PropModeReplace, (uint8_t*)&win->src.counter, 1); +#endif + + if ((flags & RGFW_windowNoInitAPI) == 0) { + RGFW_window_initOpenGL(win, RGFW_BOOL(flags & RGFW_windowOpenglSoftware)); + RGFW_window_initBuffer(win); + } + + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new window was created"); + RGFW_window_setMouseDefault(win); + RGFW_window_setFlags(win, flags); + + RGFW_window_show(win); + return win; /*return newly created window */ +#endif +#ifdef RGFW_WAYLAND + wayland: + RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningWayland, RGFW_DEBUG_CTX(win, 0), "RGFW Wayland support is experimental"); + + win->src.wl_display = _RGFW.wl_display; + if (win->src.wl_display == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errWayland, RGFW_DEBUG_CTX(win, 0), "Failed to load Wayland display"); + #ifdef RGFW_X11 + RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningWayland, RGFW_DEBUG_CTX(win, 0), "Falling back to X11"); + RGFW_useWayland(0); + return RGFW_createWindowPtr(name, rect, flags, win); + #endif + return NULL; + } + + + #ifdef RGFW_X11 + win->src.window = _RGFW.helperWindow; + XMapWindow(win->src.display, win->src.window); + XFlush(win->src.display); + if (wm_delete_window == 0) { + wm_delete_window = XInternAtom(win->src.display, "WM_DELETE_WINDOW", False); + RGFW_XUTF8_STRING = XInternAtom(win->src.display, "UTF8_STRING", False); + RGFW_XCLIPBOARD = XInternAtom(win->src.display, "CLIPBOARD", False); + } + #endif + + struct wl_registry *registry = wl_display_get_registry(win->src.wl_display); + wl_registry_add_listener(registry, ®istry_listener, win); + + wl_display_roundtrip(win->src.wl_display); + wl_display_dispatch(win->src.wl_display); + + if (win->src.compositor == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errWayland, RGFW_DEBUG_CTX(win, 0), "Can't find compositor."); + return NULL; + } + + if (RGFW_wl_cursor_theme == NULL) { + RGFW_wl_cursor_theme = wl_cursor_theme_load(NULL, 24, win->src.shm); + RGFW_cursor_surface = wl_compositor_create_surface(win->src.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); + } + + xdg_wm_base_add_listener(win->src.xdg_wm_base, &xdg_wm_base_listener, NULL); + + xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + + win->src.surface = wl_compositor_create_surface(win->src.compositor); + wl_surface_set_user_data(win->src.surface, win); + + win->src.xdg_surface = xdg_wm_base_get_xdg_surface(win->src.xdg_wm_base, win->src.surface); + xdg_surface_add_listener(win->src.xdg_surface, &xdg_surface_listener, NULL); + + xdg_wm_base_set_user_data(win->src.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_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 (!(flags & RGFW_windowNoBorder)) { + win->src.decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( + decoration_manager, win->src.xdg_toplevel); + } + + wl_display_roundtrip(win->src.wl_display); + + wl_surface_commit(win->src.surface); + RGFW_window_show(win); + + /* wait for the surface to be configured */ + while (wl_display_dispatch(win->src.wl_display) != -1 && !RGFW_wl_configured) { } + + if ((flags & RGFW_windowNoInitAPI) == 0) { + RGFW_window_initOpenGL(win, RGFW_BOOL(flags & RGFW_windowOpenglSoftware)); + RGFW_window_initBuffer(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); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new window was created"); + + #ifndef RGFW_NO_MONITOR + if (flags & RGFW_windowScaleToMonitor) + RGFW_window_scaleToMonitor(win); + #endif + + RGFW_window_setMouseDefault(win); + RGFW_window_setFlags(win, flags); + return win; /* return newly created window */ +#endif +} + +RGFW_area RGFW_getScreenSize(void) { + RGFW_GOTO_WAYLAND(1); + RGFW_init(); + + #ifdef RGFW_X11 + Screen* scrn = DefaultScreenOfDisplay(_RGFW.display); + return RGFW_AREA(scrn->width, scrn->height); + #endif + #ifdef RGFW_WAYLAND + wayland: return RGFW_AREA(_RGFW.root->r.w, _RGFW.root->r.h); /* TODO */ + #endif +} + +RGFW_point RGFW_getGlobalMousePoint(void) { + RGFW_init(); + RGFW_point RGFWMouse; + + i32 x, y; + u32 z; + Window window1, window2; + XQueryPointer(_RGFW.display, XDefaultRootWindow(_RGFW.display), &window1, &window2, &RGFWMouse.x, &RGFWMouse.y, &x, &y, &z); + + return RGFWMouse; +} + +RGFWDEF void RGFW_XHandleClipboardSelection(XEvent* event); +void RGFW_XHandleClipboardSelection(XEvent* event) { + RGFW_LOAD_ATOM(ATOM_PAIR); + RGFW_LOAD_ATOM(MULTIPLE); + RGFW_LOAD_ATOM(TARGETS); + RGFW_LOAD_ATOM(SAVE_TARGETS); + + const XSelectionRequestEvent* request = &event->xselectionrequest; + const Atom formats[] = { RGFW_XUTF8_STRING, XA_STRING }; + const int formatCount = sizeof(formats) / sizeof(formats[0]); + + if (request->target == TARGETS) { + const Atom targets[] = { TARGETS, MULTIPLE, RGFW_XUTF8_STRING, XA_STRING }; + + XChangeProperty(_RGFW.display, request->requestor, request->property, + XA_ATOM, 32, PropModeReplace, (u8*) targets, sizeof(targets) / sizeof(Atom)); + } else if (request->target == MULTIPLE) { + Atom* targets = NULL; + + Atom actualType = 0; + int actualFormat = 0; + unsigned long count = 0, bytesAfter = 0; + + XGetWindowProperty(_RGFW.display, request->requestor, request->property, 0, LONG_MAX, + False, ATOM_PAIR, &actualType, &actualFormat, &count, &bytesAfter, (u8**) &targets); + + unsigned long i; + for (i = 0; i < (u32)count; i += 2) { + if (targets[i] == RGFW_XUTF8_STRING || targets[i] == XA_STRING) + XChangeProperty(_RGFW.display, request->requestor, targets[i + 1], targets[i], + 8, PropModeReplace, (const unsigned char *)_RGFW.clipboard, (i32)_RGFW.clipboard_len); + else + targets[i + 1] = None; + } + + XChangeProperty(_RGFW.display, + request->requestor, request->property, ATOM_PAIR, 32, + PropModeReplace, (u8*) targets, (i32)count); + + XFlush(_RGFW.display); + XFree(targets); + } else if (request->target == SAVE_TARGETS) + XChangeProperty(_RGFW.display, request->requestor, request->property, 0, 32, PropModeReplace, NULL, 0); + else { + int i; + for (i = 0; i < formatCount; i++) { + if (request->target != formats[i]) + continue; + XChangeProperty(_RGFW.display, request->requestor, request->property, request->target, + 8, PropModeReplace, (u8*) _RGFW.clipboard, (i32)_RGFW.clipboard_len); + } + } + + XEvent reply = { SelectionNotify }; + reply.xselection.property = request->property; + reply.xselection.display = request->display; + reply.xselection.requestor = request->requestor; + reply.xselection.selection = request->selection; + reply.xselection.target = request->target; + reply.xselection.time = request->time; + + XSendEvent(_RGFW.display, request->requestor, False, 0, &reply); +} + +char* RGFW_strtok(char* str, const char* delimStr); +char* RGFW_strtok(char* str, const char* delimStr) { + static char* static_str = NULL; + + if (str != NULL) + static_str = str; + + if (static_str == NULL) { + return NULL; + } + + while (*static_str != '\0') { + RGFW_bool delim = 0; + const char* d; + for (d = delimStr; *d != '\0'; d++) { + if (*static_str == *d) { + delim = 1; + break; + } + } + if (!delim) + break; + static_str++; + } + + if (*static_str == '\0') + return NULL; + + char* token_start = static_str; + while (*static_str != '\0') { + int delim = 0; + const char* d; + for (d = delimStr; *d != '\0'; d++) { + if (*static_str == *d) { + delim = 1; + break; + } + } + + if (delim) { + *static_str = '\0'; + static_str++; + break; + } + static_str++; + } + + return token_start; +} + +i32 RGFW_XHandleClipboardSelectionHelper(void); + +RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { + RGFW_XHandleClipboardSelectionHelper(); + + if (win == NULL || ((win->_flags & RGFW_windowFreeOnClose) && (win->_flags & RGFW_EVENT_QUIT))) return NULL; + RGFW_event* ev = RGFW_window_checkEventCore(win); + if (ev) return ev; + + #if defined(__linux__) && !defined(RGFW_NO_LINUX) + if (RGFW_linux_updateGamepad(win)) return &win->event; + #endif + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 + RGFW_LOAD_ATOM(XdndTypeList); + RGFW_LOAD_ATOM(XdndSelection); + RGFW_LOAD_ATOM(XdndEnter); + RGFW_LOAD_ATOM(XdndPosition); + RGFW_LOAD_ATOM(XdndStatus); + RGFW_LOAD_ATOM(XdndLeave); + RGFW_LOAD_ATOM(XdndDrop); + RGFW_LOAD_ATOM(XdndFinished); + RGFW_LOAD_ATOM(XdndActionCopy); + RGFW_LOAD_ATOM(_NET_WM_SYNC_REQUEST); + RGFW_LOAD_ATOM(WM_PROTOCOLS); + XPending(win->src.display); + + XEvent E; /*!< raw X11 event */ + + /* if there is no unread qued events, get a new one */ + if ((QLength(win->src.display) || XEventsQueued(win->src.display, QueuedAlready) + XEventsQueued(win->src.display, QueuedAfterReading)) + && win->event.type != RGFW_quit + ) + XNextEvent(win->src.display, &E); + else { + return NULL; + } + + win->event.type = 0; + + /* xdnd data */ + static Window source = 0; + static long version = 0; + static i32 format = 0; + + XEvent reply = { ClientMessage }; + + switch (E.type) { + case KeyPress: + case KeyRelease: { + win->event.repeat = RGFW_FALSE; + /* check if it's a real key release */ + if (E.type == KeyRelease && XEventsQueued(win->src.display, QueuedAfterReading)) { /* get next event if there is one */ + XEvent NE; + XPeekEvent(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 */ + win->event.repeat = RGFW_TRUE; + } + + /* set event key data */ + win->event.key = (u8)RGFW_apiKeyToRGFW(E.xkey.keycode); + KeySym sym = (KeySym)XkbKeycodeToKeysym(win->src.display, (KeyCode)E.xkey.keycode, 0, (KeyCode)E.xkey.state & ShiftMask ? 1 : 0); + + if ((E.xkey.state & LockMask) && sym >= XK_a && sym <= XK_z) + sym = (E.xkey.state & ShiftMask) ? sym + 32 : sym - 32; + if ((u8)sym != (u32)sym) + sym = 0; + + win->event.keyChar = (u8)sym; + + RGFW_keyboard[win->event.key].prev = RGFW_keyboard[win->event.key].current; + + /* get keystate data */ + win->event.type = (E.type == KeyPress) ? RGFW_keyPressed : RGFW_keyReleased; + + XKeyboardState keystate; + XGetKeyboardControl(win->src.display, &keystate); + + RGFW_keyboard[win->event.key].current = (E.type == KeyPress); + + XkbStateRec state; + XkbGetState(win->src.display, XkbUseCoreKbd, &state); + RGFW_updateKeyMods(win, (state.locked_mods & LockMask), (state.locked_mods & Mod2Mask), (state.locked_mods & Mod3Mask)); + + RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, (E.type == KeyPress)); + break; + } + case ButtonPress: + case ButtonRelease: + if (E.xbutton.button > RGFW_mouseFinal) { /* skip this event */ + XFlush(win->src.display); + return RGFW_window_checkEvent(win); + } + + win->event.type = RGFW_mouseButtonPressed + (E.type == ButtonRelease); /* the events match */ + win->event.button = (u8)(E.xbutton.button - 1); + switch(win->event.button) { + case RGFW_mouseScrollUp: + win->event.scroll = 1; + break; + case RGFW_mouseScrollDown: + win->event.scroll = -1; + break; + default: break; + } + + 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.key); + + RGFW_mouseButtons[win->event.button].current = (E.type == ButtonPress); + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, (E.type == ButtonPress)); + break; + + case MotionNotify: + win->event.point.x = E.xmotion.x; + win->event.point.y = E.xmotion.y; + + win->event.vector.x = win->event.point.x - win->_lastMousePoint.x; + win->event.vector.y = win->event.point.y - win->_lastMousePoint.y; + win->_lastMousePoint = win->event.point; + + win->event.type = RGFW_mousePosChanged; + RGFW_mousePosCallback(win, win->event.point, win->event.vector); + break; + + case GenericEvent: { + /* MotionNotify is used for mouse events if the mouse isn't held */ + if (!(win->_flags & RGFW_HOLD_MOUSE)) { + XFreeEventData(win->src.display, &E.xcookie); + break; + } + + XGetEventData(win->src.display, &E.xcookie); + if (E.xcookie.evtype == XI_RawMotion) { + XIRawEvent *raw = (XIRawEvent *)E.xcookie.data; + if (raw->valuators.mask_len == 0) { + XFreeEventData(win->src.display, &E.xcookie); + break; + } + + double deltaX = 0.0f; + double deltaY = 0.0f; + + /* check if relative motion data exists where we think it does */ + if (XIMaskIsSet(raw->valuators.mask, 0) != 0) + deltaX += raw->raw_values[0]; + if (XIMaskIsSet(raw->valuators.mask, 1) != 0) + deltaY += raw->raw_values[1]; + + win->event.vector = RGFW_POINT((i32)deltaX, (i32)deltaY); + win->event.point.x = win->_lastMousePoint.x + win->event.vector.x; + win->event.point.y = win->_lastMousePoint.y + win->event.vector.y; + win->_lastMousePoint = win->event.point; + + 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, win->event.vector); + } + + XFreeEventData(win->src.display, &E.xcookie); + break; + } + + case Expose: { + win->event.type = RGFW_windowRefresh; + RGFW_windowRefreshCallback(win); + +#ifdef RGFW_ADVANCED_SMOOTH_RESIZE + XSyncValue value; + XSyncIntToValue(&value, (i32)win->src.counter_value); + XSyncSetCounter(win->src.display, win->src.counter, value); +#endif + break; + } + case MapNotify: case UnmapNotify: RGFW_window_checkMode(win); break; + case ClientMessage: { + /* if the client closed the window */ + if (E.xclient.data.l[0] == (long)wm_delete_window) { + win->event.type = RGFW_quit; + RGFW_window_setShouldClose(win, RGFW_TRUE); + RGFW_windowQuitCallback(win); + break; + } +#ifdef RGFW_ADVANCED_SMOOTH_RESIZE + if (E.xclient.message_type == WM_PROTOCOLS && (Atom)E.xclient.data.l[0] == _NET_WM_SYNC_REQUEST) { + RGFW_windowRefreshCallback(win); + win->src.counter_value = 0; + win->src.counter_value |= E.xclient.data.l[2]; + win->src.counter_value |= (E.xclient.data.l[3] << 32); + + XSyncValue value; + XSyncIntToValue(&value, (i32)win->src.counter_value); + XSyncSetCounter(win->src.display, win->src.counter, value); + break; + } +#endif + if ((win->_flags & RGFW_windowAllowDND) == 0) + break; + + reply.xclient.window = source; + reply.xclient.format = 32; + reply.xclient.data.l[0] = (long)win->src.window; + reply.xclient.data.l[1] = 0; + reply.xclient.data.l[2] = None; + + if (E.xclient.message_type == XdndEnter) { + if (version > 5) + break; + + unsigned long count; + Atom* formats; + Atom real_formats[6]; + Bool list = E.xclient.data.l[1] & 1; + + source = (unsigned long int)E.xclient.data.l[0]; + version = E.xclient.data.l[1] >> 24; + format = None; + if (list) { + Atom actualType; + i32 actualFormat; + unsigned long bytesAfter; + + XGetWindowProperty( + win->src.display, source, XdndTypeList, + 0, LONG_MAX, False, 4, + &actualType, &actualFormat, &count, &bytesAfter, (u8**)&formats + ); + } else { + count = 0; + + size_t i; + for (i = 2; i < 5; i++) { + if (E.xclient.data.l[i] != None) { + real_formats[count] = (unsigned long int)E.xclient.data.l[i]; + count += 1; + } + } + + formats = real_formats; + } + + size_t i; + for (i = 0; i < count; i++) { + if (formats[i] == XtextUriList || formats[i] == XtextPlain) { + format = (int)formats[i]; + break; + } + } + + if (list) { + XFree(formats); + } + + break; + } + + if (E.xclient.message_type == XdndPosition) { + const i32 xabs = (E.xclient.data.l[2] >> 16) & 0xffff; + const i32 yabs = (E.xclient.data.l[2]) & 0xffff; + Window dummy; + i32 xpos, ypos; + + if (version > 5) + break; + + XTranslateCoordinates( + win->src.display, XDefaultRootWindow(win->src.display), win->src.window, + xabs, yabs, &xpos, &ypos, &dummy + ); + + win->event.point.x = xpos; + win->event.point.y = ypos; + + reply.xclient.window = source; + reply.xclient.message_type = XdndStatus; + + if (format) { + reply.xclient.data.l[1] = 1; + if (version >= 2) + reply.xclient.data.l[4] = (long)XdndActionCopy; + } + + XSendEvent(win->src.display, source, False, NoEventMask, &reply); + XFlush(win->src.display); + break; + } + if (E.xclient.message_type != XdndDrop) + break; + + if (version > 5) + break; + + size_t i; + for (i = 0; i < win->event.droppedFilesCount; i++) + win->event.droppedFiles[i][0] = '\0'; + + win->event.droppedFilesCount = 0; + + + win->event.type = RGFW_DNDInit; + + if (format) { + Time time = (version >= 1) + ? (Time)E.xclient.data.l[2] + : CurrentTime; + + XConvertSelection( + win->src.display, XdndSelection, (Atom)format, + XdndSelection, win->src.window, time + ); + } else if (version >= 2) { + XEvent new_reply = { ClientMessage }; + + XSendEvent(win->src.display, source, False, NoEventMask, &new_reply); + XFlush(win->src.display); + } + + RGFW_dndInitCallback(win, win->event.point); + } break; + case SelectionRequest: + RGFW_XHandleClipboardSelection(&E); + XFlush(win->src.display); + return RGFW_window_checkEvent(win); + case SelectionNotify: { + /* this is only for checking for xdnd drops */ + if (E.xselection.property != XdndSelection || !(win->_flags & RGFW_windowAllowDND)) + break; + char* data; + unsigned long result; + + Atom actualType; + i32 actualFormat; + unsigned long bytesAfter; + + XGetWindowProperty(win->src.display, E.xselection.requestor, E.xselection.property, 0, LONG_MAX, False, E.xselection.target, &actualType, &actualFormat, &result, &bytesAfter, (u8**) &data); + + if (result == 0) + break; + + const char* prefix = (const char*)"file://"; + + char* line; + + win->event.droppedFilesCount = 0; + win->event.type = RGFW_DND; + + while ((line = (char*)RGFW_strtok(data, "\r\n"))) { + char path[RGFW_MAX_PATH]; + + data = NULL; + + if (line[0] == '#') + continue; + + char* l; + for (l = line; 1; l++) { + if ((l - line) > 7) + break; + else if (*l != prefix[(l - line)]) + break; + else if (*l == '\0' && prefix[(l - line)] == '\0') { + line += 7; + while (*line != '/') + line++; + break; + } else if (*l == '\0') + break; + } + + win->event.droppedFilesCount++; + + size_t index = 0; + while (*line) { + if (line[0] == '%' && line[1] && line[2]) { + const char digits[3] = { line[1], line[2], '\0' }; + path[index] = (char) RGFW_STRTOL(digits, NULL, 16); + line += 2; + } else + path[index] = *line; + + index++; + line++; + } + path[index] = '\0'; + RGFW_MEMCPY(win->event.droppedFiles[win->event.droppedFilesCount - 1], path, index + 1); + } + + RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount); + if (data) + XFree(data); + + if (version >= 2) { + XEvent new_reply = { ClientMessage }; + new_reply.xclient.format = 32; + new_reply.xclient.message_type = XdndFinished; + new_reply.xclient.data.l[1] = (long int)result; + new_reply.xclient.data.l[2] = (long int)XdndActionCopy; + XSendEvent(win->src.display, source, False, NoEventMask, &new_reply); + XFlush(win->src.display); + } + break; + } + case FocusIn: + if ((win->_flags & RGFW_windowFullscreen)) + XMapRaised(win->src.display, win->src.window); + + win->_flags |= RGFW_windowFocus; + win->event.type = RGFW_focusIn; + RGFW_focusCallback(win, 1); + break; + case FocusOut: + if ((win->_flags & RGFW_windowFullscreen)) + RGFW_window_minimize(win); + + win->_flags &= ~(u32)RGFW_windowFocus; + win->event.type = RGFW_focusOut; + RGFW_focusCallback(win, 0); + break; + case PropertyNotify: RGFW_window_checkMode(win); break; + case EnterNotify: { + win->event.type = RGFW_mouseEnter; + win->event.point.x = E.xcrossing.x; + win->event.point.y = E.xcrossing.y; + RGFW_mouseNotifyCallback(win, win->event.point, 1); + break; + } + + case LeaveNotify: { + win->event.type = RGFW_mouseLeave; + RGFW_mouseNotifyCallback(win, win->event.point, 0); + break; + } + + case ConfigureNotify: { + /* detect resize */ + RGFW_window_checkMode(win); + if (E.xconfigure.width != win->r.w || E.xconfigure.height != win->r.h) { + win->event.type = RGFW_windowResized; + win->r = RGFW_RECT(win->r.x, win->r.y, E.xconfigure.width, E.xconfigure.height); + RGFW_windowResizedCallback(win, win->r); + break; + } + + /* detect move */ + if (E.xconfigure.x != win->r.x || E.xconfigure.y != win->r.y) { + win->event.type = RGFW_windowMoved; + win->r = RGFW_RECT(E.xconfigure.x, E.xconfigure.y, win->r.w, win->r.h); + RGFW_windowMovedCallback(win, win->r); + break; + } + + break; + } + default: + XFlush(win->src.display); + return RGFW_window_checkEvent(win); + } + XFlush(win->src.display); + if (win->event.type) return &win->event; + else return NULL; +#endif +#ifdef RGFW_WAYLAND + wayland: + if ((win->_flags & RGFW_windowHide) == 0) + wl_display_roundtrip(win->src.wl_display); + return NULL; +#endif +} + +void RGFW_window_move(RGFW_window* win, RGFW_point v) { + RGFW_ASSERT(win != NULL); + win->r.x = v.x; + win->r.y = v.y; + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 + XMoveWindow(win->src.display, win->src.window, v.x, v.y); +#endif +#ifdef RGFW_WAYLAND + wayland: + RGFW_ASSERT(win != NULL); + + if (win->src.compositor) { + struct wl_pointer *pointer = wl_seat_get_pointer(win->src.seat); + if (!pointer) { + return; + } + + wl_display_flush(win->src.wl_display); + } +#endif +} + + +void RGFW_window_resize(RGFW_window* win, RGFW_area a) { + RGFW_ASSERT(win != NULL); + win->r.w = (i32)a.w; + win->r.h = (i32)a.h; + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 + XResizeWindow(win->src.display, win->src.window, a.w, a.h); + + if ((win->_flags & RGFW_windowNoResize)) { + XSizeHints sh; + sh.flags = (1L << 4) | (1L << 5); + sh.min_width = sh.max_width = (i32)a.w; + sh.min_height = sh.max_height = (i32)a.h; + + XSetWMSizeHints(win->src.display, (Drawable) win->src.window, &sh, XA_WM_NORMAL_HINTS); + } +#endif +#ifdef RGFW_WAYLAND + wayland: + if (win->src.compositor) { + xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->r.w, win->r.h); + #ifdef RGFW_OPENGL + wl_egl_window_resize(win->src.eglWindow, (i32)a.w, (i32)a.h, 0, 0); + #endif + } +#endif +} + +void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a) { + RGFW_ASSERT(win != NULL); + + if (a.w == 0 && a.h == 0) + return; + + XSizeHints hints; + long flags; + + XGetWMNormalHints(win->src.display, win->src.window, &hints, &flags); + + hints.flags |= PAspect; + + hints.min_aspect.x = hints.max_aspect.x = (i32)a.w; + hints.min_aspect.y = hints.max_aspect.y = (i32)a.h; + + XSetWMNormalHints(win->src.display, win->src.window, &hints); +} + +void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { + RGFW_ASSERT(win != NULL); + + if (a.w == 0 && a.h == 0) + return; + + XSizeHints hints; + long flags; + + XGetWMNormalHints(win->src.display, win->src.window, &hints, &flags); + + hints.flags |= PMinSize; + + hints.min_width = (i32)a.w; + hints.min_height = (i32)a.h; + + XSetWMNormalHints(win->src.display, win->src.window, &hints); +} + +void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { + RGFW_ASSERT(win != NULL); + + if (a.w == 0 && a.h == 0) + a = RGFW_getScreenSize(); + + XSizeHints hints; + long flags; + + XGetWMNormalHints(win->src.display, win->src.window, &hints, &flags); + + hints.flags |= PMaxSize; + + hints.max_width = (i32)a.w; + hints.max_height = (i32)a.h; + + XSetWMNormalHints(win->src.display, win->src.window, &hints); +} + +void RGFW_toggleXMaximized(RGFW_window* win, RGFW_bool maximized); +void RGFW_toggleXMaximized(RGFW_window* win, RGFW_bool maximized) { + RGFW_ASSERT(win != NULL); + RGFW_LOAD_ATOM(_NET_WM_STATE); + RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_VERT); + RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ); + + XEvent xev = {0}; + xev.type = ClientMessage; + xev.xclient.window = win->src.window; + xev.xclient.message_type = _NET_WM_STATE; + xev.xclient.format = 32; + xev.xclient.data.l[0] = maximized; + xev.xclient.data.l[1] = (long int)_NET_WM_STATE_MAXIMIZED_HORZ; + xev.xclient.data.l[2] = (long int)_NET_WM_STATE_MAXIMIZED_VERT; + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = 0; + + XSendEvent(win->src.display, DefaultRootWindow(win->src.display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); +} + +void RGFW_window_maximize(RGFW_window* win) { + win->_oldRect = win->r; + RGFW_toggleXMaximized(win, 1); +} + +void RGFW_window_focus(RGFW_window* win) { + RGFW_ASSERT(win); + + XWindowAttributes attr; + XGetWindowAttributes(win->src.display, win->src.window, &attr); + if (attr.map_state != IsViewable) return; + + XSetInputFocus(win->src.display, win->src.window, RevertToPointerRoot, CurrentTime); + XFlush(win->src.display); +} + +void RGFW_window_raise(RGFW_window* win) { + RGFW_ASSERT(win); + XRaiseWindow(win->src.display, win->src.window); + XMapRaised(win->src.display, win->src.window); +} + +void RGFW_window_setXAtom(RGFW_window* win, Atom netAtom, RGFW_bool fullscreen); +void RGFW_window_setXAtom(RGFW_window* win, Atom netAtom, RGFW_bool fullscreen) { + RGFW_ASSERT(win != NULL); + RGFW_LOAD_ATOM(_NET_WM_STATE); + + XEvent xev = {0}; + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.message_type = _NET_WM_STATE; + xev.xclient.window = win->src.window; + xev.xclient.format = 32; + xev.xclient.data.l[0] = fullscreen; + xev.xclient.data.l[1] = (long int)netAtom; + xev.xclient.data.l[2] = 0; + + XSendEvent(win->src.display, DefaultRootWindow(win->src.display), False, SubstructureNotifyMask | SubstructureRedirectMask, &xev); +} + +void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) { + RGFW_ASSERT(win != NULL); + if (fullscreen) { + win->_flags |= RGFW_windowFullscreen; + win->_oldRect = win->r; + } + else win->_flags &= ~(u32)RGFW_windowFullscreen; + + RGFW_LOAD_ATOM(_NET_WM_STATE_FULLSCREEN); + + RGFW_window_setXAtom(win, _NET_WM_STATE_FULLSCREEN, fullscreen); + + XRaiseWindow(win->src.display, win->src.window); + XMapRaised(win->src.display, win->src.window); +} + +void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) { + RGFW_ASSERT(win != NULL); + + RGFW_LOAD_ATOM(_NET_WM_STATE_ABOVE); + RGFW_window_setXAtom(win, _NET_WM_STATE_ABOVE, floating); +} + +void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) { + RGFW_ASSERT(win != NULL); + const u32 value = (u32) (0xffffffffu * (double) opacity); + RGFW_LOAD_ATOM(NET_WM_WINDOW_OPACITY); + XChangeProperty(win->src.display, win->src.window, + NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &value, 1); +} + +void RGFW_window_minimize(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + if (RGFW_window_isMaximized(win)) return; + + win->_oldRect = win->r; + XIconifyWindow(win->src.display, win->src.window, DefaultScreen(win->src.display)); + XFlush(win->src.display); +} + +void RGFW_window_restore(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_toggleXMaximized(win, 0); + + win->r = win->_oldRect; + RGFW_window_move(win, RGFW_POINT(win->r.x, win->r.y)); + RGFW_window_resize(win, RGFW_AREA(win->r.w, win->r.h)); + + RGFW_window_show(win); + XFlush(win->src.display); +} + +RGFW_bool RGFW_window_isFloating(RGFW_window* win) { + RGFW_LOAD_ATOM(_NET_WM_STATE); + RGFW_LOAD_ATOM(_NET_WM_STATE_ABOVE); + + Atom actual_type; + int actual_format; + unsigned long nitems, bytes_after; + Atom* prop_return = NULL; + + int status = XGetWindowProperty(win->src.display, win->src.window, _NET_WM_STATE, 0, (~0L), False, XA_ATOM, + &actual_type, &actual_format, &nitems, &bytes_after, + (unsigned char **)&prop_return); + + if (status != Success || actual_type != XA_ATOM) + return RGFW_FALSE; + + unsigned long i; + for (i = 0; i < nitems; i++) + if (prop_return[i] == _NET_WM_STATE_ABOVE) return RGFW_TRUE; + + if (prop_return) + XFree(prop_return); + + return RGFW_FALSE; +} + +void RGFW_window_setName(RGFW_window* win, const char* name) { + RGFW_ASSERT(win != NULL); + RGFW_GOTO_WAYLAND(0); + #ifdef RGFW_X11 + XStoreName(win->src.display, win->src.window, name); + + RGFW_LOAD_ATOM(_NET_WM_NAME); + XChangeProperty( + win->src.display, win->src.window, _NET_WM_NAME, RGFW_XUTF8_STRING, + 8, PropModeReplace, (u8*)name, 256 + ); + #endif + #ifdef RGFW_WAYLAND + wayland: + if (win->src.compositor) + xdg_toplevel_set_title(win->src.xdg_toplevel, name); + #endif +} + +#ifndef RGFW_NO_PASSTHROUGH +void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) { + RGFW_ASSERT(win != NULL); + if (passthrough) { + Region region = XCreateRegion(); + XShapeCombineRegion(win->src.display, win->src.window, ShapeInput, 0, 0, region, ShapeSet); + XDestroyRegion(region); + + return; + } + + XShapeCombineMask(win->src.display, win->src.window, ShapeInput, 0, 0, None, ShapeSet); +} +#endif /* RGFW_NO_PASSTHROUGH */ + +RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* icon, RGFW_area a, i32 channels, u8 type) { + RGFW_ASSERT(win != NULL); + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 + RGFW_LOAD_ATOM(_NET_WM_ICON); + if (icon == NULL || (channels != 3 && channels != 4)) { + RGFW_bool res = (RGFW_bool)XChangeProperty( + win->src.display, win->src.window, _NET_WM_ICON, XA_CARDINAL, 32, + PropModeReplace, (u8*)NULL, 0 + ); + return res; + } + + i32 count = (i32)(2 + (a.w * a.h)); + + unsigned long* data = (unsigned long*) RGFW_ALLOC((u32)count * sizeof(unsigned long)); + RGFW_ASSERT(data != NULL); + + data[0] = (unsigned long)a.w; + data[1] = (unsigned long)a.h; + + unsigned long* target = &data[2]; + u32 x, y; + + for (x = 0; x < a.w; x++) { + for (y = 0; y < a.h; y++) { + size_t i = y * a.w + x; + u32 alpha = (channels == 4) ? icon[i * 4 + 3] : 0xFF; + + target[i] = (unsigned long)((icon[i * 4 + 0]) << 16) | + (unsigned long)((icon[i * 4 + 1]) << 8) | + (unsigned long)((icon[i * 4 + 2]) << 0) | + (unsigned long)(alpha << 24); + } + } + + RGFW_bool res = RGFW_TRUE; + if (type & RGFW_iconTaskbar) { + res = (RGFW_bool)XChangeProperty( + win->src.display, win->src.window, _NET_WM_ICON, XA_CARDINAL, 32, + PropModeReplace, (u8*)data, count + ); + } + + if (type & RGFW_iconWindow) { + XWMHints wm_hints; + wm_hints.flags = IconPixmapHint; + + i32 depth = DefaultDepth(win->src.display, DefaultScreen(win->src.display)); + XImage *image = XCreateImage(win->src.display, DefaultVisual(win->src.display, DefaultScreen(win->src.display)), + (u32)depth, ZPixmap, 0, (char *)target, a.w, a.h, 32, 0); + + wm_hints.icon_pixmap = XCreatePixmap(win->src.display, win->src.window, a.w, a.h, (u32)depth); + XPutImage(win->src.display, wm_hints.icon_pixmap, DefaultGC(win->src.display, DefaultScreen(win->src.display)), image, 0, 0, 0, 0, a.w, a.h); + image->data = NULL; + XDestroyImage(image); + + XSetWMHints(win->src.display, win->src.window, &wm_hints); + } + + RGFW_FREE(data); + XFlush(win->src.display); + return RGFW_BOOL(res); +#endif +#ifdef RGFW_WAYLAND + wayland: + return RGFW_FALSE; +#endif +} + +RGFW_mouse* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels) { + RGFW_ASSERT(icon); + RGFW_ASSERT(channels == 3 || channels == 4); + RGFW_GOTO_WAYLAND(0); + +#ifdef RGFW_X11 +#ifndef RGFW_NO_X11_CURSOR + RGFW_init(); + XcursorImage* native = XcursorImageCreate((i32)a.w, (i32)a.h); + native->xhot = 0; + native->yhot = 0; + + XcursorPixel* target = native->pixels; + size_t x, y; + for (x = 0; x < a.w; x++) { + for (y = 0; y < a.h; y++) { + size_t i = y * a.w + x; + u32 alpha = (channels == 4) ? icon[i * 4 + 3] : 0xFF; + + target[i] = (u32)((icon[i * 4 + 0]) << 16) + | (u32)((icon[i * 4 + 1]) << 8) + | (u32)((icon[i * 4 + 2]) << 0) + | (u32)(alpha << 24); + } + } + + Cursor cursor = XcursorImageLoadCursor(_RGFW.display, native); + XcursorImageDestroy(native); + + return (void*)cursor; +#else + RGFW_UNUSED(image); RGFW_UNUSED(a.w); RGFW_UNUSED(channels); + return NULL; +#endif +#endif +#ifdef RGFW_WAYLAND + wayland: + RGFW_UNUSED(icon); RGFW_UNUSED(a); RGFW_UNUSED(channels); + return NULL; /* TODO */ +#endif +} + +void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) { +RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 + RGFW_ASSERT(win && mouse); + XDefineCursor(win->src.display, win->src.window, (Cursor)mouse); +#endif +#ifdef RGFW_WAYLAND + wayland: + RGFW_UNUSED(win); RGFW_UNUSED(mouse); +#endif +} + +void RGFW_freeMouse(RGFW_mouse* mouse) { +RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 + RGFW_ASSERT(mouse); + XFreeCursor(_RGFW.display, (Cursor)mouse); +#endif +#ifdef RGFW_WAYLAND + wayland: + RGFW_UNUSED(mouse); +#endif +} + +void RGFW_window_moveMouse(RGFW_window* win, RGFW_point p) { +RGFW_GOTO_WAYLAND(1); +#ifdef RGFW_X11 + RGFW_ASSERT(win != NULL); + + XEvent event; + XQueryPointer(win->src.display, DefaultRootWindow(win->src.display), + &event.xbutton.root, &event.xbutton.window, + &event.xbutton.x_root, &event.xbutton.y_root, + &event.xbutton.x, &event.xbutton.y, + &event.xbutton.state); + + win->_lastMousePoint = RGFW_POINT(p.x - win->r.x, p.y - win->r.y); + if (event.xbutton.x == p.x && event.xbutton.y == p.y) + return; + + XWarpPointer(win->src.display, None, win->src.window, 0, 0, 0, 0, (int) p.x - win->r.x, (int) p.y - win->r.y); +#endif +#ifdef RGFW_WAYLAND + wayland: + RGFW_UNUSED(win); RGFW_UNUSED(p); +#endif +} + +RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) { + return RGFW_window_setMouseStandard(win, RGFW_mouseArrow); +} + +RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { + RGFW_ASSERT(win != NULL); + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 + static const u8 mouseIconSrc[16] = { XC_arrow, XC_left_ptr, XC_xterm, XC_crosshair, XC_hand2, XC_sb_h_double_arrow, XC_sb_v_double_arrow, XC_bottom_left_corner, XC_bottom_right_corner, XC_fleur, XC_X_cursor}; + + if (mouse > (sizeof(mouseIconSrc) / sizeof(u8))) + return RGFW_FALSE; + + mouse = mouseIconSrc[mouse]; + + Cursor cursor = XCreateFontCursor(win->src.display, mouse); + XDefineCursor(win->src.display, win->src.window, (Cursor) cursor); + + XFreeCursor(win->src.display, (Cursor) cursor); + return RGFW_TRUE; +#endif +#ifdef RGFW_WAYLAND + wayland: { } + static const char* iconStrings[16] = { "left_ptr", "left_ptr", "text", "cross", "pointer", "e-resize", "n-resize", "nw-resize", "ne-resize", "all-resize", "not-allowed" }; + + struct wl_cursor* wlcursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, iconStrings[mouse]); + RGFW_cursor_image = wlcursor->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); + return RGFW_TRUE; + +#endif +} + +void RGFW_window_hide(RGFW_window* win) { + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 + XUnmapWindow(win->src.display, win->src.window); +#endif +#ifdef RGFW_WAYLAND + wayland: + wl_surface_attach(win->src.surface, NULL, 0, 0); + wl_surface_commit(win->src.surface); + win->_flags |= RGFW_windowHide; +#endif +} + +void RGFW_window_show(RGFW_window* win) { + win->_flags &= ~(u32)RGFW_windowHide; + if (win->_flags & RGFW_windowFocusOnShow) RGFW_window_focus(win); + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 + XMapWindow(win->src.display, win->src.window); +#endif +#ifdef RGFW_WAYLAND + wayland: + /* wl_surface_attach(win->src.surface, win->rc., 0, 0); */ + wl_surface_commit(win->src.surface); +#endif +} + +RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { + RGFW_GOTO_WAYLAND(1); + #ifdef RGFW_X11 + RGFW_init(); + if (XGetSelectionOwner(_RGFW.display, RGFW_XCLIPBOARD) == _RGFW.helperWindow) { + if (str != NULL) + RGFW_STRNCPY(str, _RGFW.clipboard, _RGFW.clipboard_len); + return (RGFW_ssize_t)_RGFW.clipboard_len; + } + + XEvent event; + int format; + unsigned long N, sizeN; + char* data; + Atom target; + + RGFW_LOAD_ATOM(XSEL_DATA); + + XConvertSelection(_RGFW.display, RGFW_XCLIPBOARD, RGFW_XUTF8_STRING, XSEL_DATA, _RGFW.helperWindow, CurrentTime); + XSync(_RGFW.display, 0); + while (1) { + XNextEvent(_RGFW.display, &event); + if (event.type != SelectionNotify) continue; + + if (event.xselection.selection != RGFW_XCLIPBOARD || event.xselection.property == 0) + return -1; + break; + } + + XGetWindowProperty(event.xselection.display, event.xselection.requestor, + event.xselection.property, 0L, (~0L), 0, AnyPropertyType, &target, + &format, &sizeN, &N, (u8**) &data); + + RGFW_ssize_t size; + if (sizeN > strCapacity && str != NULL) + size = -1; + + if ((target == RGFW_XUTF8_STRING || target == XA_STRING) && str != NULL) { + RGFW_MEMCPY(str, data, sizeN); + str[sizeN] = '\0'; + XFree(data); + } else if (str != NULL) size = -1; + + XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property); + size = (RGFW_ssize_t)sizeN; + + return size; + #endif + #if defined(RGFW_WAYLAND) + wayland: return 0; + #endif +} + +i32 RGFW_XHandleClipboardSelectionHelper(void) { +#ifdef RGFW_X11 + RGFW_LOAD_ATOM(SAVE_TARGETS); + + XEvent event; + XPending(_RGFW.display); + + if (QLength(_RGFW.display) || XEventsQueued(_RGFW.display, QueuedAlready) + XEventsQueued(_RGFW.display, QueuedAfterReading)) + XNextEvent(_RGFW.display, &event); + else + return 0; + + switch (event.type) { + case SelectionRequest: + RGFW_XHandleClipboardSelection(&event); + return 0; + case SelectionNotify: + if (event.xselection.target == SAVE_TARGETS) + return 0; + break; + default: break; + } + + return 0; +#else + return 1; +#endif +} + +void RGFW_writeClipboard(const char* text, u32 textLen) { + RGFW_GOTO_WAYLAND(1); + #ifdef RGFW_X11 + RGFW_LOAD_ATOM(SAVE_TARGETS); + RGFW_init(); + + /* request ownership of the clipboard section and request to convert it, this means its our job to convert it */ + XSetSelectionOwner(_RGFW.display, RGFW_XCLIPBOARD, _RGFW.helperWindow, CurrentTime); + if (XGetSelectionOwner(_RGFW.display, RGFW_XCLIPBOARD) != _RGFW.helperWindow) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errClipboard, RGFW_DEBUG_CTX(_RGFW.root, 0), "X11 failed to become owner of clipboard selection"); + return; + } + + if (_RGFW.clipboard) + RGFW_FREE(_RGFW.clipboard); + + _RGFW.clipboard = (char*)RGFW_ALLOC(textLen); + RGFW_ASSERT(_RGFW.clipboard != NULL); + + RGFW_STRNCPY(_RGFW.clipboard, text, textLen); + _RGFW.clipboard_len = textLen; + #endif + #ifdef RGFW_WAYLAND + wayland: + RGFW_UNUSED(text); RGFW_UNUSED(textLen); + #endif +} + +RGFW_bool RGFW_window_isHidden(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + XWindowAttributes windowAttributes; + XGetWindowAttributes(win->src.display, win->src.window, &windowAttributes); + + return (windowAttributes.map_state == IsUnmapped && !RGFW_window_isMinimized(win)); +} + +RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_LOAD_ATOM(WM_STATE); + + Atom actual_type; + i32 actual_format; + unsigned long nitems, bytes_after; + unsigned char* prop_data; + + i32 status = XGetWindowProperty(win->src.display, win->src.window, WM_STATE, 0, 2, False, + AnyPropertyType, &actual_type, &actual_format, + &nitems, &bytes_after, &prop_data); + + if (status == Success && nitems >= 1 && prop_data == (unsigned char*)IconicState) { + XFree(prop_data); + return RGFW_TRUE; + } + + if (prop_data != NULL) + XFree(prop_data); + + XWindowAttributes windowAttributes; + XGetWindowAttributes(win->src.display, win->src.window, &windowAttributes); + return windowAttributes.map_state != IsViewable; +} + +RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_LOAD_ATOM(_NET_WM_STATE); + RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_VERT); + RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ); + + Atom actual_type; + i32 actual_format; + unsigned long nitems, bytes_after; + unsigned char* prop_data; + + i32 status = XGetWindowProperty(win->src.display, win->src.window, _NET_WM_STATE, 0, 1024, False, + XA_ATOM, &actual_type, &actual_format, + &nitems, &bytes_after, &prop_data); + + if (status != Success) { + if (prop_data != NULL) + XFree(prop_data); + + return RGFW_FALSE; + } + + u64 i; + for (i = 0; i < nitems; ++i) { + if (prop_data[i] == _NET_WM_STATE_MAXIMIZED_VERT || + prop_data[i] == _NET_WM_STATE_MAXIMIZED_HORZ) { + XFree(prop_data); + return RGFW_TRUE; + } + } + + if (prop_data != NULL) + XFree(prop_data); + + return RGFW_FALSE; +} + +#ifndef RGFW_NO_DPI +u32 RGFW_XCalculateRefreshRate(XRRModeInfo mi); +u32 RGFW_XCalculateRefreshRate(XRRModeInfo mi) { + if (mi.hTotal == 0 || mi.vTotal == 0) return 0; + return (u32) RGFW_ROUND((double) mi.dotClock / ((double) mi.hTotal * (double) mi.vTotal)); +} +#endif + + +static float XGetSystemContentDPI(Display* display, i32 screen) { + float dpi = 96.0f; + + #ifndef RGFW_NO_DPI + RGFW_UNUSED(screen); + char* rms = XResourceManagerString(display); + XrmDatabase db = NULL; + if (rms) db = XrmGetStringDatabase(rms); + + if (rms && db) { + XrmValue value; + char* type = NULL; + + if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value) && type && RGFW_STRNCMP(type, "String", 7) == 0) + dpi = (float)RGFW_ATOF(value.addr); + XrmDestroyDatabase(db); + } + #else + dpi = RGFW_ROUND(DisplayWidth(display, screen) / (DisplayWidthMM(display, screen) / 25.4)); + #endif + + return dpi; +} + +RGFW_monitor RGFW_XCreateMonitor(i32 screen); +RGFW_monitor RGFW_XCreateMonitor(i32 screen) { + RGFW_monitor monitor; + RGFW_init(); + Display* display = _RGFW.display; + + if (screen == -1) screen = DefaultScreen(display); + + Screen* scrn = DefaultScreenOfDisplay(display); + RGFW_area size = RGFW_AREA(scrn->width, scrn->height); + + monitor.x = 0; + monitor.y = 0; + monitor.mode.area = RGFW_AREA(size.w, size.h); + monitor.physW = (float)DisplayWidthMM(display, screen) / 25.4f; + monitor.physH = (float)DisplayHeightMM(display, screen) / 25.4f; + + RGFW_splitBPP((u32)DefaultDepth(display, DefaultScreen(display)), &monitor.mode); + + char* name = XDisplayName((const char*)display); + RGFW_MEMCPY(monitor.name, name, 128); + + float dpi = XGetSystemContentDPI(display, screen); + monitor.pixelRatio = dpi >= 192.0f ? 2 : 1; + monitor.scaleX = (float) (dpi) / 96.0f; + monitor.scaleY = (float) (dpi) / 96.0f; + + #ifndef RGFW_NO_DPI + XRRScreenResources* sr = XRRGetScreenResourcesCurrent(display, RootWindow(display, screen)); + monitor.mode.refreshRate = RGFW_XCalculateRefreshRate(sr->modes[screen]); + + XRRCrtcInfo* ci = NULL; + int crtc = screen; + + if (sr->ncrtc > crtc) { + ci = XRRGetCrtcInfo(display, sr, sr->crtcs[crtc]); + } + #endif + + #ifndef RGFW_NO_DPI + XRROutputInfo* info = XRRGetOutputInfo (display, sr, sr->outputs[screen]); + + if (info == NULL || ci == NULL) { + XRRFreeScreenResources(sr); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, RGFW_DEBUG_CTX_MON(monitor), "monitor found"); + return monitor; + } + + + float physW = (float)info->mm_width / 25.4f; + float physH = (float)info->mm_height / 25.4f; + + RGFW_MEMCPY(monitor.name, info->name, 128); + + if ((u8)physW && (u8)physH) { + monitor.physW = physW; + monitor.physH = physH; + } + + monitor.x = ci->x; + monitor.y = ci->y; + + if (ci->width && ci->height) { + monitor.mode.area.w = (u32)ci->width; + monitor.mode.area.h = (u32)ci->height; + } + #endif + + #ifndef RGFW_NO_DPI + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(sr); + #endif + + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, RGFW_DEBUG_CTX_MON(monitor), "monitor found"); + return monitor; +} + +RGFW_monitor* RGFW_getMonitors(size_t* len) { + static RGFW_monitor monitors[7]; + + RGFW_GOTO_WAYLAND(1); + #ifdef RGFW_X11 + RGFW_init(); + + Display* display = _RGFW.display; + i32 max = ScreenCount(display); + + i32 i; + for (i = 0; i < max && i < 6; i++) + monitors[i] = RGFW_XCreateMonitor(i); + + if (len != NULL) *len = (size_t)((max <= 6) ? (max) : (6)); + + return monitors; + #endif + #ifdef RGFW_WAYLAND + wayland: return monitors; /* TODO WAYLAND */ + #endif +} + +RGFW_monitor RGFW_getPrimaryMonitor(void) { + RGFW_GOTO_WAYLAND(1); + #ifdef RGFW_X11 + return RGFW_XCreateMonitor(-1); + #endif + #ifdef RGFW_WAYLAND + wayland: return (RGFW_monitor){ 0 }; /* TODO WAYLAND */ + #endif +} + +RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) { + RGFW_GOTO_WAYLAND(1); +#ifdef RGFW_X11 + #ifndef RGFW_NO_DPI + RGFW_init(); + XRRScreenResources* screenRes = XRRGetScreenResources(_RGFW.display, DefaultRootWindow(_RGFW.display)); + if (screenRes == NULL) return RGFW_FALSE; + + int i; + for (i = 0; i < screenRes->ncrtc; i++) { + XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(_RGFW.display, screenRes, screenRes->crtcs[i]); + if (!crtcInfo) continue; + + if (mon.x == crtcInfo->x && mon.y == crtcInfo->y && (u32)mon.mode.area.w == crtcInfo->width && (u32)mon.mode.area.h == crtcInfo->height) { + RRMode rmode = None; + int index; + for (index = 0; index < screenRes->nmode; index++) { + RGFW_monitorMode foundMode; + foundMode.area = RGFW_AREA(screenRes->modes[index].width, screenRes->modes[index].height); + foundMode.refreshRate = RGFW_XCalculateRefreshRate(screenRes->modes[index]); + RGFW_splitBPP((u32)DefaultDepth(_RGFW.display, DefaultScreen(_RGFW.display)), &foundMode); + + if (RGFW_monitorModeCompare(mode, foundMode, request)) { + rmode = screenRes->modes[index].id; + + RROutput output = screenRes->outputs[i]; + XRROutputInfo* info = XRRGetOutputInfo(_RGFW.display, screenRes, output); + if (info) { + XRRSetCrtcConfig(_RGFW.display, screenRes, screenRes->crtcs[i], + CurrentTime, 0, 0, rmode, RR_Rotate_0, &output, 1); + XRRFreeOutputInfo(info); + XRRFreeCrtcInfo(crtcInfo); + XRRFreeScreenResources(screenRes); + return RGFW_TRUE; + } + } + } + + XRRFreeCrtcInfo(crtcInfo); + XRRFreeScreenResources(screenRes); + return RGFW_FALSE; + } + + XRRFreeCrtcInfo(crtcInfo); + } + + XRRFreeScreenResources(screenRes); + return RGFW_FALSE; + #endif +#endif +#ifdef RGFW_WAYLAND +wayland: +#endif + return RGFW_FALSE; +} + +RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_GOTO_WAYLAND(1); +#ifdef RGFW_X11 + XWindowAttributes attrs; + if (!XGetWindowAttributes(win->src.display, win->src.window, &attrs)) { + return (RGFW_monitor){0}; + } + + i32 i; + for (i = 0; i < ScreenCount(win->src.display) && i < 6; i++) { + Screen* screen = ScreenOfDisplay(win->src.display, i); + if (attrs.x >= 0 && attrs.x < XWidthOfScreen(screen) && + attrs.y >= 0 && attrs.y < XHeightOfScreen(screen)) + return RGFW_XCreateMonitor(i); + } +#endif +#ifdef RGFW_WAYLAND +wayland: +#endif + return (RGFW_monitor){0}; + +} + +#if defined(RGFW_OPENGL) && !defined(RGFW_EGL) +void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { + if (win == NULL) + glXMakeCurrent(NULL, (Drawable)NULL, (GLXContext) NULL); + else + glXMakeCurrent(win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx); +} +void* RGFW_getCurrent_OpenGL(void) { return glXGetCurrentContext(); } +void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) { glXSwapBuffers(win->src.display, win->src.window); } +#endif + +void RGFW_window_swapBuffers_software(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_GOTO_WAYLAND(0); +#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + #ifdef RGFW_X11 + win->src.bitmap->data = (char*) win->buffer; + RGFW_RGB_to_BGR(win, (u8*)win->src.bitmap->data); + XPutImage(win->src.display, win->src.window, win->src.gc, win->src.bitmap, 0, 0, 0, 0, win->bufferSize.w, win->bufferSize.h); + win->src.bitmap->data = NULL; + return; + #endif + #ifdef RGFW_WAYLAND + wayland: + #if !defined(RGFW_BUFFER_BGR) && !defined(RGFW_OSMESA) + RGFW_RGB_to_BGR(win, win->src.buffer); + #else + size_t y; + for (y = 0; y < win->r.h; y++) { + u32 index = (y * 4 * win->r.w); + u32 index2 = (y * 4 * win->bufferSize.w); + RGFW_MEMCPY(&win->src.buffer[index], &win->buffer[index2], win->r.w * 4); + } + #endif + + wl_surface_frame_done(win, NULL, 0); + wl_surface_commit(win->src.surface); + #endif +#else +#ifdef RGFW_WAYLAND + wayland: +#endif + RGFW_UNUSED(win); +#endif +} + +#if !defined(RGFW_EGL) + +void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { + RGFW_ASSERT(win != NULL); + + #if defined(RGFW_OPENGL) + // cached pfn to avoid calling glXGetProcAddress more than once + static PFNGLXSWAPINTERVALEXTPROC pfn = (PFNGLXSWAPINTERVALEXTPROC)123; + static int (*pfn2)(int) = NULL; + + if (pfn == (PFNGLXSWAPINTERVALEXTPROC)123) { + pfn = ((PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress((GLubyte*) "glXSwapIntervalEXT")); + if (pfn == NULL) { + const char* array[] = {"GLX_MESA_swap_control", "GLX_SGI_swap_control"}; + u32 i; + for (i = 0; i < sizeof(array) / sizeof(char*) && pfn2 == NULL; i++) + pfn2 = ((int(*)(int))glXGetProcAddress((GLubyte*) array[i])); + + if (pfn2 != NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to load swap interval function, fallingback to the native swapinterval function"); + } else { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to load swap interval function"); + } + } + } + if (pfn != NULL) + pfn(win->src.display, win->src.window, swapInterval); + else if (pfn2 != NULL) { + pfn2(swapInterval); + } + #else + RGFW_UNUSED(swapInterval); + #endif +} +#endif + +void RGFW_deinit(void) { + if (_RGFW.windowCount == -1) return; + #define RGFW_FREE_LIBRARY(x) if (x != NULL) dlclose(x); x = NULL; +#ifdef RGFW_X11 + /* to save the clipboard on the x server after the window is closed */ + RGFW_LOAD_ATOM(CLIPBOARD_MANAGER); + RGFW_LOAD_ATOM(SAVE_TARGETS); + if (XGetSelectionOwner(_RGFW.display, RGFW_XCLIPBOARD) == _RGFW.helperWindow) { + XConvertSelection(_RGFW.display, CLIPBOARD_MANAGER, SAVE_TARGETS, None, _RGFW.helperWindow, CurrentTime); + while (RGFW_XHandleClipboardSelectionHelper()); + } + if (_RGFW.clipboard) { + RGFW_FREE(_RGFW.clipboard); + _RGFW.clipboard = NULL; + } + + RGFW_freeMouse(_RGFW.hiddenMouse); + XCloseDisplay(_RGFW.display); /*!< kill connection to the x server */ + + #if !defined(RGFW_NO_X11_CURSOR_PRELOAD) && !defined(RGFW_NO_X11_CURSOR) + RGFW_FREE_LIBRARY(X11Cursorhandle); + #endif + #if !defined(RGFW_NO_X11_XI_PRELOAD) + RGFW_FREE_LIBRARY(X11Xihandle); + #endif + + #ifdef RGFW_USE_XDL + XDL_close(); + #endif + + #if !defined(RGFW_NO_X11_EXT_PRELOAD) + RGFW_FREE_LIBRARY(X11XEXThandle); + #endif +#endif +#ifdef RGFW_WAYLAND + wl_display_disconnect(_RGFW.wl_display); +#endif + #ifndef RGFW_NO_LINUX + 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_gamepadCount; i++) { + if(RGFW_gamepads[i]) + close(RGFW_gamepads[i]); + } + #endif + + _RGFW.root = NULL; + _RGFW.windowCount = -1; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, (RGFW_debugContext){0}, "global context deinitialized"); +} + +void RGFW_window_close(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + if ((win->_flags & RGFW_windowNoInitAPI) == 0) RGFW_window_freeOpenGL(win); + + #ifdef RGFW_X11 + RGFW_GOTO_WAYLAND(0); + + /* ungrab pointer if it was grabbed */ + if (win->_flags & RGFW_HOLD_MOUSE) + XUngrabPointer(win->src.display, CurrentTime); + + #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + if (win->buffer != NULL) { + if ((win->_flags & RGFW_BUFFER_ALLOC)) + RGFW_FREE(win->buffer); + XDestroyImage((XImage*) win->src.bitmap); + } + #endif + + XFreeGC(win->src.display, win->src.gc); + XDestroyWindow(win->src.display, (Drawable) win->src.window); /*!< close the window */ + win->src.window = 0; + XCloseDisplay(win->src.display); + + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a window was freed"); + _RGFW.windowCount--; + if (_RGFW.windowCount == 0) RGFW_deinit(); + + RGFW_clipboard_switch(NULL); + RGFW_FREE(win->event.droppedFiles); + if ((win->_flags & RGFW_WINDOW_ALLOC)) { + RGFW_FREE(win); + win = NULL; + } + return; + #endif + + #ifdef RGFW_WAYLAND + wayland: + + #ifdef RGFW_X11 + XDestroyWindow(win->src.display, (Drawable) win->src.window); + #endif + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a window was freed"); + _RGFW.windowCount--; + if (_RGFW.windowCount == 0) RGFW_deinit(); + + xdg_toplevel_destroy(win->src.xdg_toplevel); + xdg_surface_destroy(win->src.xdg_surface); + wl_surface_destroy(win->src.surface); + + #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + wl_buffer_destroy(win->src.wl_buffer); + if ((win->_flags & RGFW_BUFFER_ALLOC)) + RGFW_FREE(win->buffer); + + munmap(win->src.buffer, (size_t)(win->r.w * win->r.h * 4)); + #endif + + RGFW_clipboard_switch(NULL); + RGFW_FREE(win->event.droppedFiles); + if ((win->_flags & RGFW_WINDOW_ALLOC)) { + RGFW_FREE(win); + win = NULL; + } + #endif +} + + +/* + End of X11 linux / wayland / unix defines +*/ + +#include +#include +#include + +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.wl_display), POLLIN, 0 }, + #else + { ConnectionNumber(win->src.display), POLLIN, 0 }, + #endif + #ifdef RGFW_X11 + { ConnectionNumber(_RGFW.display), POLLIN, 0 }, + #endif + { RGFW_eventWait_forceStop[0], POLLIN, 0 }, + #if defined(__linux__) + { -1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0} + #endif + }; + + u8 index = 2; +#ifdef RGFW_X11 + index++; +#endif + + #if defined(__linux__) || defined(__NetBSD__) + for (i = 0; i < RGFW_gamepadCount; i++) { + if (RGFW_gamepads[i] == 0) + continue; + + fds[index].fd = RGFW_gamepads[i]; + index++; + } + #endif + + + u64 start = RGFW_getTimeNS(); + + + #ifdef RGFW_WAYLAND + while (wl_display_dispatch(win->src.wl_display) <= 0 + #else + while (XPending(win->src.display) == 0 + #endif + #ifdef RGFW_X11 + && XPending(_RGFW.display) == 0 + #endif + ) { + if (poll(fds, index, waitMS) <= 0) + break; + + if (waitMS != RGFW_eventWaitNext) + waitMS -= (i32)(RGFW_getTimeNS() - start) / (i32)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; + } +} + +i32 RGFW_getClock(void); +i32 RGFW_getClock(void) { + static i32 clock = -1; + if (clock != -1) return clock; + + #if defined(_POSIX_MONOTONIC_CLOCK) + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + clock = CLOCK_MONOTONIC; + #else + clock = CLOCK_REALTIME; + #endif + + return clock; +} + +u64 RGFW_getTimerFreq(void) { return 1000000000LLU; } +u64 RGFW_getTimerValue(void) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return (u64)ts.tv_sec * RGFW_getTimerFreq() + (u64)ts.tv_nsec; +} +#endif /* end of wayland or X11 defines */ + + + +/* + + Start of Windows defines + + +*/ + +#ifdef RGFW_WINDOWS +#define WIN32_LEAN_AND_MEAN +#define OEMRESOURCE +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef WM_DPICHANGED +#define WM_DPICHANGED 0x02E0 +#endif + +#ifndef RGFW_NO_XINPUT + typedef DWORD (WINAPI * PFN_XInputGetState)(DWORD,XINPUT_STATE*); + PFN_XInputGetState XInputGetStateSRC = NULL; + #define XInputGetState XInputGetStateSRC + + typedef DWORD (WINAPI * PFN_XInputGetKeystroke)(DWORD, DWORD, PXINPUT_KEYSTROKE); + PFN_XInputGetKeystroke XInputGetKeystrokeSRC = NULL; + #define XInputGetKeystroke XInputGetKeystrokeSRC + + HMODULE RGFW_XInput_dll = NULL; +#endif + +char* RGFW_createUTF8FromWideStringWin32(const WCHAR* source); + +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 +#define GL_LEFT 0x0406 +#define GL_RIGHT 0x0407 + +typedef int (*PFN_wglGetSwapIntervalEXT)(void); +PFN_wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc = NULL; +#define wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc + + +void* RGFWgamepadApi = NULL; + +/* these two wgl functions need to be preloaded */ +typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList); +PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL; + +#ifndef RGFW_EGL + HMODULE RGFW_wgl_dll = NULL; +#endif + +#ifndef RGFW_NO_LOAD_WGL + typedef HGLRC(WINAPI* PFN_wglCreateContext)(HDC); + typedef BOOL(WINAPI* PFN_wglDeleteContext)(HGLRC); + typedef PROC(WINAPI* PFN_wglGetProcAddress)(LPCSTR); + typedef BOOL(WINAPI* PFN_wglMakeCurrent)(HDC, HGLRC); + typedef HDC(WINAPI* PFN_wglGetCurrentDC)(void); + typedef HGLRC(WINAPI* PFN_wglGetCurrentContext)(void); + typedef BOOL(WINAPI* PFN_wglShareLists)(HGLRC, HGLRC); + + PFN_wglCreateContext wglCreateContextSRC; + PFN_wglDeleteContext wglDeleteContextSRC; + PFN_wglGetProcAddress wglGetProcAddressSRC; + PFN_wglMakeCurrent wglMakeCurrentSRC; + PFN_wglGetCurrentDC wglGetCurrentDCSRC; + PFN_wglGetCurrentContext wglGetCurrentContextSRC; + PFN_wglShareLists wglShareListsSRC; + + #define wglCreateContext wglCreateContextSRC + #define wglDeleteContext wglDeleteContextSRC + #define wglGetProcAddress wglGetProcAddressSRC + #define wglMakeCurrent wglMakeCurrentSRC + #define wglGetCurrentDC wglGetCurrentDCSRC + #define wglGetCurrentContext wglGetCurrentContextSRC + #define wglShareLists wglShareListsSRC +#endif + +#ifdef RGFW_OPENGL + +RGFW_proc RGFW_getProcAddress(const char* procname) { + RGFW_proc proc = (RGFW_proc)wglGetProcAddress(procname); + if (proc) + return proc; + + return (RGFW_proc) GetProcAddress(RGFW_wgl_dll, procname); +} + +typedef HRESULT (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats); +PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL; + +typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval); +PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; +#endif + +#ifndef RGFW_NO_DWM +HMODULE RGFW_dwm_dll = NULL; +typedef struct { DWORD dwFlags; int fEnable; HRGN hRgnBlur; int fTransitionOnMaximized;} DWM_BLURBEHIND; +typedef HRESULT (WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND, const DWM_BLURBEHIND*); +PFN_DwmEnableBlurBehindWindow DwmEnableBlurBehindWindowSRC = NULL; +#endif +void RGFW_win32_makeWindowTransparent(RGFW_window* win); +void RGFW_win32_makeWindowTransparent(RGFW_window* win) { + if (!(win->_flags & RGFW_windowTransparent)) return; + + #ifndef RGFW_NO_DWM + if (DwmEnableBlurBehindWindowSRC != NULL) { + DWM_BLURBEHIND bb = {0, 0, 0, 0}; + bb.dwFlags = 0x1; + bb.fEnable = TRUE; + bb.hRgnBlur = NULL; + DwmEnableBlurBehindWindowSRC(win->src.window, &bb); + + } else + #endif + { + SetWindowLong(win->src.window, GWL_EXSTYLE, WS_EX_LAYERED); + SetLayeredWindowAttributes(win->src.window, 0, 128, LWA_ALPHA); + } +} + +LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { + RGFW_window* win = (RGFW_window*)GetPropW(hWnd, L"RGFW"); + if (win == NULL) return DefWindowProcW(hWnd, message, wParam, lParam); + + RECT windowRect; + GetWindowRect(hWnd, &windowRect); + + switch (message) { + case WM_CLOSE: + case WM_QUIT: + RGFW_eventQueuePush((RGFW_event){.type = RGFW_quit, ._win = win}); + RGFW_windowQuitCallback(win); + return 0; + case WM_ACTIVATE: { + RGFW_bool inFocus = RGFW_BOOL(LOWORD(wParam) != WA_INACTIVE); + if (inFocus) win->_flags |= RGFW_windowFocus; + else win->_flags &= ~ (u32)RGFW_windowFocus; + RGFW_eventQueuePush((RGFW_event){.type = (RGFW_eventType)((u8)RGFW_focusOut - inFocus), ._win = win}); + + RGFW_focusCallback(win, inFocus); + + if ((win->_flags & RGFW_windowFullscreen) == 0) + return DefWindowProcW(hWnd, message, wParam, lParam); + + win->_flags &= ~(u32)RGFW_EVENT_PASSED; + if (inFocus == RGFW_FALSE) RGFW_window_minimize(win); + else RGFW_window_setFullscreen(win, 1); + return DefWindowProcW(hWnd, message, wParam, lParam); + } + case WM_MOVE: + win->r.x = windowRect.left; + win->r.y = windowRect.top; + RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowMoved, ._win = win}); + RGFW_windowMovedCallback(win, win->r); + return DefWindowProcW(hWnd, message, wParam, lParam); + case WM_SIZE: { + if (win->src.aspectRatio.w != 0 && win->src.aspectRatio.h != 0) { + double aspectRatio = (double)win->src.aspectRatio.w / win->src.aspectRatio.h; + + int width = windowRect.right - windowRect.left; + int height = windowRect.bottom - windowRect.top; + int newHeight = (int)(width / aspectRatio); + int newWidth = (int)(height * aspectRatio); + + if (win->r.w > windowRect.right - windowRect.left || + win->r.h > (i32)((u32)(windowRect.bottom - windowRect.top) - win->src.hOffset)) + { + if (newHeight > height) windowRect.right = windowRect.left + newWidth; + else windowRect.bottom = windowRect.top + newHeight; + } else { + if (newHeight < height) windowRect.right = windowRect.left + newWidth; + else windowRect.bottom = windowRect.top + newHeight; + } + + RGFW_window_resize(win, RGFW_AREA((windowRect.right - windowRect.left), + (u32)(windowRect.bottom - windowRect.top) - (u32)win->src.hOffset)); + } + + win->r.w = windowRect.right - windowRect.left; + win->r.h = (windowRect.bottom - windowRect.top) - (i32)win->src.hOffset; + RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowResized, ._win = win}); + RGFW_windowResizedCallback(win, win->r); + RGFW_window_checkMode(win); + return DefWindowProcW(hWnd, message, wParam, lParam); + } + #ifndef RGFW_NO_MONITOR + case WM_DPICHANGED: { + if (win->_flags & RGFW_windowScaleToMonitor) RGFW_window_scaleToMonitor(win); + + const float scaleX = HIWORD(wParam) / (float) 96; + const float scaleY = LOWORD(wParam) / (float) 96; + RGFW_scaleUpdatedCallback(win, scaleX, scaleY); + RGFW_eventQueuePush((RGFW_event){.type = RGFW_scaleUpdated, .scaleX = scaleX, .scaleY = scaleY , ._win = win}); + return DefWindowProcW(hWnd, message, wParam, lParam); + } + #endif + case WM_GETMINMAXINFO: { + MINMAXINFO* mmi = (MINMAXINFO*) lParam; + mmi->ptMinTrackSize.x = (LONG)win->src.minSize.w; + mmi->ptMinTrackSize.y = (LONG)(win->src.minSize.h + win->src.hOffset); + if (win->src.maxSize.w == 0 && win->src.maxSize.h == 0) + return DefWindowProcW(hWnd, message, wParam, lParam); + + mmi->ptMaxTrackSize.x = (LONG)win->src.maxSize.w; + mmi->ptMaxTrackSize.y = (LONG)(win->src.maxSize.h + win->src.hOffset); + return DefWindowProcW(hWnd, message, wParam, lParam); + } + case WM_PAINT: { + PAINTSTRUCT ps; + BeginPaint(hWnd, &ps); + RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowRefresh, ._win = win}); + RGFW_windowRefreshCallback(win); + EndPaint(hWnd, &ps); + + return DefWindowProcW(hWnd, message, wParam, lParam); + } + #if(_WIN32_WINNT >= 0x0600) + case WM_DWMCOMPOSITIONCHANGED: + case WM_DWMCOLORIZATIONCOLORCHANGED: + RGFW_win32_makeWindowTransparent(win); + break; + #endif +/* based on sokol_app.h */ +#ifdef RGFW_ADVANCED_SMOOTH_RESIZE + case WM_ENTERSIZEMOVE: SetTimer(win->src.window, 1, USER_TIMER_MINIMUM, NULL); break; + case WM_EXITSIZEMOVE: KillTimer(win->src.window, 1); break; + case WM_TIMER: RGFW_windowRefreshCallback(win); break; +#endif + case WM_NCLBUTTONDOWN: { + /* workaround for half-second pause when starting to move window + see: https://gamedev.net/forums/topic/672094-keeping-things-moving-during-win32-moveresize-events/5254386/ + */ + POINT point = { 0, 0 }; + if (SendMessage(win->src.window, WM_NCHITTEST, wParam, lParam) != HTCAPTION || GetCursorPos(&point) == FALSE) + break; + + ScreenToClient(win->src.window, &point); + PostMessage(win->src.window, WM_MOUSEMOVE, 0, ((uint32_t)point.x)|(((uint32_t)point.y) << 16)); + break; + } + default: break; + } + return DefWindowProcW(hWnd, message, wParam, lParam); +} + +#ifndef RGFW_NO_DPI + HMODULE RGFW_Shcore_dll = NULL; + typedef HRESULT (WINAPI *PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*); + PFN_GetDpiForMonitor GetDpiForMonitorSRC = NULL; + #define GetDpiForMonitor GetDpiForMonitorSRC +#endif + +#if !defined(RGFW_NO_LOAD_WINMM) && !defined(RGFW_NO_WINMM) + HMODULE RGFW_winmm_dll = NULL; + typedef u32 (WINAPI * PFN_timeBeginPeriod)(u32); + typedef PFN_timeBeginPeriod PFN_timeEndPeriod; + PFN_timeBeginPeriod timeBeginPeriodSRC, timeEndPeriodSRC; + #define timeBeginPeriod timeBeginPeriodSRC + #define timeEndPeriod timeEndPeriodSRC +#elif !defined(RGFW_NO_WINMM) + __declspec(dllimport) u32 __stdcall timeBeginPeriod(u32 uPeriod); + __declspec(dllimport) u32 __stdcall timeEndPeriod(u32 uPeriod); +#endif +#define RGFW_PROC_DEF(proc, name) if (name##SRC == NULL && proc != NULL) name##SRC = (PFN_##name)(RGFW_proc)GetProcAddress((proc), (#name)); + +#ifndef RGFW_NO_XINPUT +void RGFW_loadXInput(void); +void RGFW_loadXInput(void) { + u32 i; + static const char* names[] = {"xinput1_4.dll", "xinput9_1_0.dll", "xinput1_2.dll", "xinput1_1.dll"}; + + for (i = 0; i < sizeof(names) / sizeof(const char*) && (XInputGetStateSRC == NULL || XInputGetKeystrokeSRC != NULL); i++) { + RGFW_XInput_dll = LoadLibraryA(names[i]); + RGFW_PROC_DEF(RGFW_XInput_dll, XInputGetState); + RGFW_PROC_DEF(RGFW_XInput_dll, XInputGetKeystroke); + } + + if (XInputGetStateSRC == NULL) + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errFailedFuncLoad, RGFW_DEBUG_CTX(.win = _RGFW.root, .srcError = 0), "Failed to load XInputGetState"); + if (XInputGetKeystrokeSRC == NULL) + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errFailedFuncLoad, RGFW_DEBUG_CTX(.win = _RGFW.root, .srcError = 0), "Failed to load XInputGetKeystroke"); +} +#endif + +void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area){ +#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + win->buffer = buffer; + win->bufferSize = area; + + BITMAPV5HEADER bi = { 0 }; + ZeroMemory(&bi, sizeof(bi)); + bi.bV5Size = sizeof(bi); + bi.bV5Width = (i32)area.w; + bi.bV5Height = -((LONG) area.h); + bi.bV5Planes = 1; + bi.bV5BitCount = 32; + bi.bV5Compression = BI_RGB; + + win->src.bitmap = CreateDIBSection(win->src.hdc, + (BITMAPINFO*) &bi, DIB_RGB_COLORS, + (void**) &win->src.bitmapBits, + NULL, (DWORD) 0); + + if (win->buffer == NULL) + win->buffer = win->src.bitmapBits; + + win->src.hdcMem = CreateCompatibleDC(win->src.hdc); + SelectObject(win->src.hdcMem, win->src.bitmap); + + #if defined(RGFW_OSMESA) + win->src.ctx = OSMesaCreateContext(OSMESA_BGRA, NULL); + OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, area.w, area.h); + OSMesaPixelStore(OSMESA_Y_UP, 0); + #endif + #else + RGFW_UNUSED(win); RGFW_UNUSED(buffer); RGFW_UNUSED(area); /*!< if buffer rendering is not being used */ + #endif +} + +void RGFW_releaseCursor(RGFW_window* win) { + RGFW_UNUSED(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); RGFW_UNUSED(rect); + + RECT clipRect; + GetClientRect(win->src.window, &clipRect); + ClientToScreen(win->src.window, (POINT*) &clipRect.left); + ClientToScreen(win->src.window, (POINT*) &clipRect.right); + ClipCursor(&clipRect); + + const RAWINPUTDEVICE id = { 0x01, 0x02, 0, win->src.window }; + RegisterRawInputDevices(&id, 1, sizeof(id)); +} + +#define RGFW_LOAD_LIBRARY(x, lib) if (x == NULL) x = LoadLibraryA(lib) + +#ifdef RGFW_DIRECTX +int RGFW_window_createDXSwapChain(RGFW_window* win, IDXGIFactory* pFactory, IUnknown* pDevice, IDXGISwapChain** swapchain) { + RGFW_ASSERT(win && pFactory && pDevice && swapchain); + + static DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 }; + swapChainDesc.BufferCount = 2; + swapChainDesc.BufferDesc.Width = win->r.w; + swapChainDesc.BufferDesc.Height = win->r.h; + swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.OutputWindow = (HWND)win->src.window; + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.SampleDesc.Quality = 0; + swapChainDesc.Windowed = TRUE; + swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + + HRESULT hr = pFactory->lpVtbl->CreateSwapChain(pFactory, (IUnknown*)pDevice, &swapChainDesc, swapchain); + if (FAILED(hr)) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errDirectXContext, RGFW_DEBUG_CTX(.win = win, .srcError = hr), "Failed to create DirectX swap chain!"); + return -2; + } + + return 0; +} +#endif + +void RGFW_win32_loadOpenGLFuncs(HWND dummyWin); +void RGFW_win32_loadOpenGLFuncs(HWND dummyWin) { +#ifdef RGFW_OPENGL + if (wglSwapIntervalEXT != NULL && wglChoosePixelFormatARB != NULL && wglChoosePixelFormatARB != NULL) + return; + + HDC dummy_dc = GetDC(dummyWin); + u32 pfd_flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + + PIXELFORMATDESCRIPTOR pfd = {sizeof(pfd), 1, pfd_flags, PFD_TYPE_RGBA, 32, 8, PFD_MAIN_PLANE, 32, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 32, 8, 0, PFD_MAIN_PLANE, 0, 0, 0, 0}; + + int dummy_pixel_format = ChoosePixelFormat(dummy_dc, &pfd); + SetPixelFormat(dummy_dc, dummy_pixel_format, &pfd); + + HGLRC dummy_context = wglCreateContext(dummy_dc); + wglMakeCurrent(dummy_dc, dummy_context); + + wglCreateContextAttribsARB = ((PFNWGLCREATECONTEXTATTRIBSARBPROC(WINAPI *)(const char*)) wglGetProcAddress)("wglCreateContextAttribsARB"); + wglChoosePixelFormatARB = ((PFNWGLCHOOSEPIXELFORMATARBPROC(WINAPI *)(const char*)) wglGetProcAddress)("wglChoosePixelFormatARB"); + + wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)(RGFW_proc)wglGetProcAddress("wglSwapIntervalEXT"); + if (wglSwapIntervalEXT == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to load swap interval function"); + } + + wglMakeCurrent(dummy_dc, 0); + wglDeleteContext(dummy_context); + ReleaseDC(dummyWin, dummy_dc); +#else + RGFW_UNUSED(dummyWin); +#endif +} + +#ifndef RGFW_EGL +void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software) { +#ifdef RGFW_OPENGL + PIXELFORMATDESCRIPTOR pfd; + pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.iLayerType = PFD_MAIN_PLANE; + pfd.cColorBits = 32; + pfd.cAlphaBits = 8; + pfd.cDepthBits = 24; + pfd.cStencilBits = (BYTE)RGFW_GL_HINTS[RGFW_glStencil]; + pfd.cAuxBuffers = (BYTE)RGFW_GL_HINTS[RGFW_glAuxBuffers]; + if (RGFW_GL_HINTS[RGFW_glStereo]) pfd.dwFlags |= PFD_STEREO; + + /* try to create the pixel format we want for opengl and then try to create an opengl context for the specified version */ + if (software) + pfd.dwFlags |= PFD_GENERIC_FORMAT | PFD_GENERIC_ACCELERATED; + + /* get pixel format, default to a basic pixel format */ + int pixel_format = ChoosePixelFormat(win->src.hdc, &pfd); + if (wglChoosePixelFormatARB != NULL) { + i32* pixel_format_attribs = (i32*)RGFW_initFormatAttribs(software); + + int new_pixel_format; + UINT num_formats; + wglChoosePixelFormatARB(win->src.hdc, pixel_format_attribs, 0, 1, &new_pixel_format, &num_formats); + if (!num_formats) + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to create a pixel format for WGL"); + else pixel_format = new_pixel_format; + } + + PIXELFORMATDESCRIPTOR suggested; + if (!DescribePixelFormat(win->src.hdc, pixel_format, sizeof(suggested), &suggested) || + !SetPixelFormat(win->src.hdc, pixel_format, &pfd)) + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to set the WGL pixel format"); + + if (wglCreateContextAttribsARB != NULL) { + /* create opengl/WGL context for the specified version */ + u32 index = 0; + i32 attribs[40]; + + if (RGFW_GL_HINTS[RGFW_glProfile]== RGFW_glCore) { + SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB); + } + else { + SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB); + } + + if (RGFW_GL_HINTS[RGFW_glMinor] || RGFW_GL_HINTS[RGFW_glMajor]) { + SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_GL_HINTS[RGFW_glMajor]); + SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_GL_HINTS[RGFW_glMinor]); + } + + SET_ATTRIB(0, 0); + + win->src.ctx = (HGLRC)wglCreateContextAttribsARB(win->src.hdc, NULL, attribs); + } else { /* fall back to a default context (probably opengl 2 or something) */ + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to create an accelerated OpenGL Context"); + win->src.ctx = wglCreateContext(win->src.hdc); + } + + ReleaseDC(win->src.window, win->src.hdc); + win->src.hdc = GetDC(win->src.window); + wglMakeCurrent(win->src.hdc, win->src.ctx); + + if (_RGFW.root != win) + wglShareLists(_RGFW.root->src.ctx, win->src.ctx); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context initalized"); +#else + RGFW_UNUSED(win); RGFW_UNUSED(software); +#endif +} + +void RGFW_window_freeOpenGL(RGFW_window* win) { +#ifdef RGFW_OPENGL + if (win->src.ctx == NULL) return; + wglDeleteContext((HGLRC) win->src.ctx); /*!< delete opengl context */ + win->src.ctx = NULL; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context freed"); +#else + RGFW_UNUSED(win); +#endif +} +#endif + + +i32 RGFW_init(void) { + #ifndef RGFW_NO_XINPUT + if (RGFW_XInput_dll == NULL) + RGFW_loadXInput(); + #endif + +#ifndef RGFW_NO_DPI + #if (_WIN32_WINNT >= 0x0600) + SetProcessDPIAware(); + #endif +#endif + + #ifndef RGFW_NO_WINMM + #ifndef RGFW_NO_LOAD_WINMM + RGFW_LOAD_LIBRARY(RGFW_winmm_dll, "winmm.dll"); + RGFW_PROC_DEF(RGFW_winmm_dll, timeBeginPeriod); + RGFW_PROC_DEF(RGFW_winmm_dll, timeEndPeriod); + #endif + timeBeginPeriod(1); + #endif + + #ifndef RGFW_NO_DWM + RGFW_LOAD_LIBRARY(RGFW_dwm_dll, "dwmapi.dll"); + RGFW_PROC_DEF(RGFW_dwm_dll, DwmEnableBlurBehindWindow); + #endif + + RGFW_LOAD_LIBRARY(RGFW_wgl_dll, "opengl32.dll"); + #ifndef RGFW_NO_LOAD_WGL + RGFW_PROC_DEF(RGFW_wgl_dll, wglCreateContext); + RGFW_PROC_DEF(RGFW_wgl_dll, wglDeleteContext); + RGFW_PROC_DEF(RGFW_wgl_dll, wglDeleteContext); + RGFW_PROC_DEF(RGFW_wgl_dll, wglGetProcAddress); + RGFW_PROC_DEF(RGFW_wgl_dll, wglMakeCurrent); + RGFW_PROC_DEF(RGFW_wgl_dll, wglGetCurrentDC); + RGFW_PROC_DEF(RGFW_wgl_dll, wglGetCurrentContext); + RGFW_PROC_DEF(RGFW_wgl_dll, wglShareLists); + #endif + + u8 RGFW_blk[] = { 0, 0, 0, 0 }; + _RGFW.hiddenMouse = RGFW_loadMouse(RGFW_blk, RGFW_AREA(1, 1), 4); + + _RGFW.windowCount = 0; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, (RGFW_debugContext){0}, "global context initialized"); + return 1; +} + +RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) { + if (name[0] == 0) name = (char*) " "; + + RGFW_window_basic_init(win, rect, flags); + + win->src.hIconSmall = win->src.hIconBig = NULL; + win->src.maxSize = RGFW_AREA(0, 0); + win->src.minSize = RGFW_AREA(0, 0); + win->src.aspectRatio = RGFW_AREA(0, 0); + + HINSTANCE inh = GetModuleHandleA(NULL); + + #ifndef __cplusplus + WNDCLASSW Class = { 0 }; /*!< Setup the Window class. */ + #else + WNDCLASSW Class = { }; + #endif + + if (RGFW_className == NULL) + RGFW_className = (char*)name; + + wchar_t wide_class[256]; + MultiByteToWideChar(CP_UTF8, 0, RGFW_className, -1, wide_class, 255); + + Class.lpszClassName = wide_class; + Class.hInstance = inh; + Class.hCursor = LoadCursor(NULL, IDC_ARROW); + Class.lpfnWndProc = WndProcW; + Class.cbClsExtra = sizeof(RGFW_window*); + + Class.hIcon = (HICON)LoadImageA(GetModuleHandleW(NULL), "RGFW_ICON", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); + if (Class.hIcon == NULL) + Class.hIcon = (HICON)LoadImageA(NULL, (LPCSTR)IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); + + RegisterClassW(&Class); + + DWORD window_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + + RECT windowRect, clientRect; + + if (!(flags & RGFW_windowNoBorder)) { + window_style |= WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_MINIMIZEBOX | WS_THICKFRAME; + + if (!(flags & RGFW_windowNoResize)) + window_style |= WS_SIZEBOX | WS_MAXIMIZEBOX; + } else + window_style |= WS_POPUP | WS_VISIBLE | WS_SYSMENU; + + wchar_t wide_name[256]; + MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_name, 255); + HWND dummyWin = CreateWindowW(Class.lpszClassName, (wchar_t*)wide_name, window_style, win->r.x, win->r.y, win->r.w, win->r.h, 0, 0, inh, 0); + + GetWindowRect(dummyWin, &windowRect); + GetClientRect(dummyWin, &clientRect); + + RGFW_win32_loadOpenGLFuncs(dummyWin); + DestroyWindow(dummyWin); + + win->src.hOffset = (u32)(windowRect.bottom - windowRect.top) - (u32)(clientRect.bottom - clientRect.top); + win->src.window = CreateWindowW(Class.lpszClassName, (wchar_t*)wide_name, window_style, win->r.x, win->r.y, win->r.w, win->r.h + (i32)win->src.hOffset, 0, 0, inh, 0); + SetPropW(win->src.window, L"RGFW", win); + RGFW_window_resize(win, RGFW_AREA(win->r.w, win->r.h)); /* so WM_GETMINMAXINFO gets called again */ + + if (flags & RGFW_windowAllowDND) { + win->_flags |= RGFW_windowAllowDND; + RGFW_window_setDND(win, 1); + } + win->src.hdc = GetDC(win->src.window); + + if ((flags & RGFW_windowNoInitAPI) == 0) { + RGFW_window_initOpenGL(win, RGFW_BOOL(flags & RGFW_windowOpenglSoftware)); + RGFW_window_initBuffer(win); + } + + RGFW_window_setFlags(win, flags); + RGFW_win32_makeWindowTransparent(win); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new window was created"); + RGFW_window_show(win); + + return win; +} + +void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { + RGFW_setBit(&win->_flags, RGFW_windowNoBorder, !border); + LONG style = GetWindowLong(win->src.window, GWL_STYLE); + + + if (border == 0) { + SetWindowLong(win->src.window, GWL_STYLE, style & ~WS_OVERLAPPEDWINDOW); + SetWindowPos( + win->src.window, HWND_TOP, 0, 0, 0, 0, + SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE + ); + } + else { + style |= WS_OVERLAPPEDWINDOW; + if (win->_flags & RGFW_windowNoResize) style &= ~WS_MAXIMIZEBOX; + SetWindowPos( + win->src.window, HWND_TOP, 0, 0, 0, 0, + SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE + ); + } +} + +void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow) { + RGFW_setBit(&win->_flags, RGFW_windowAllowDND, allow); + DragAcceptFiles(win->src.window, allow); +} + +RGFW_area RGFW_getScreenSize(void) { + HDC dc = GetDC(NULL); + RGFW_area area = RGFW_AREA(GetDeviceCaps(dc, HORZRES), GetDeviceCaps(dc, VERTRES)); + ReleaseDC(NULL, dc); + return area; +} + +RGFW_point RGFW_getGlobalMousePoint(void) { + POINT p; + GetCursorPos(&p); + + return RGFW_POINT(p.x, p.y); +} + +void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a) { + RGFW_ASSERT(win != NULL); + win->src.aspectRatio = a; +} + +void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { + RGFW_ASSERT(win != NULL); + win->src.minSize = a; +} + +void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { + RGFW_ASSERT(win != NULL); + win->src.maxSize = a; +} + +void RGFW_window_focus(RGFW_window* win) { + RGFW_ASSERT(win); + SetForegroundWindow(win->src.window); + SetFocus(win->src.window); +} + +void RGFW_window_raise(RGFW_window* win) { + RGFW_ASSERT(win); + BringWindowToTop(win->src.window); + SetWindowPos(win->src.window, HWND_TOP, win->r.x, win->r.y, win->r.w, win->r.h, SWP_NOSIZE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); +} + +void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) { + RGFW_ASSERT(win != NULL); + + if (fullscreen == RGFW_FALSE) { + RGFW_window_setBorder(win, 1); + SetWindowPos(win->src.window, HWND_NOTOPMOST, win->_oldRect.x, win->_oldRect.y, win->_oldRect.w, win->_oldRect.h + (i32)win->src.hOffset, + SWP_NOOWNERZORDER | SWP_FRAMECHANGED); + + win->_flags &= ~(u32)RGFW_windowFullscreen; + win->r = win->_oldRect; + return; + } + + win->_oldRect = win->r; + win->_flags |= RGFW_windowFullscreen; + + RGFW_monitor mon = RGFW_window_getMonitor(win); + RGFW_window_setBorder(win, 0); + + SetWindowPos(win->src.window, HWND_TOPMOST, 0, 0, (i32)mon.mode.area.w, (i32)mon.mode.area.h, SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW); + RGFW_monitor_scaleToWindow(mon, win); + + win->r = RGFW_RECT(0, 0, mon.mode.area.w, mon.mode.area.h); +} + +void RGFW_window_maximize(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_window_hide(win); + ShowWindow(win->src.window, SW_MAXIMIZE); +} + +void RGFW_window_minimize(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + ShowWindow(win->src.window, SW_MINIMIZE); +} + +void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) { + RGFW_ASSERT(win != NULL); + if (floating) SetWindowPos(win->src.window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + else SetWindowPos(win->src.window, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); +} + +void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) { + SetWindowLong(win->src.window, GWL_EXSTYLE, WS_EX_LAYERED); + SetLayeredWindowAttributes(win->src.window, 0, opacity, LWA_ALPHA); +} + +void RGFW_window_restore(RGFW_window* win) { RGFW_window_show(win); } + +RGFW_bool RGFW_window_isFloating(RGFW_window* win) { + return (GetWindowLongPtr(win->src.window, GWL_EXSTYLE) & WS_EX_TOPMOST) != 0; +} + +u8 RGFW_xinput2RGFW[] = { + RGFW_gamepadA, /* or PS X button */ + RGFW_gamepadB, /* or PS circle button */ + RGFW_gamepadX, /* or PS square button */ + RGFW_gamepadY, /* or PS triangle button */ + RGFW_gamepadR1, /* right bumper */ + RGFW_gamepadL1, /* left bump */ + RGFW_gamepadL2, /* left trigger */ + RGFW_gamepadR2, /* right trigger */ + 0, 0, 0, 0, 0, 0, 0, 0, + RGFW_gamepadUp, /* dpad up */ + RGFW_gamepadDown, /* dpad down */ + RGFW_gamepadLeft, /* dpad left */ + RGFW_gamepadRight, /* dpad right */ + RGFW_gamepadStart, /* start button */ + RGFW_gamepadSelect,/* select button */ + RGFW_gamepadL3, + RGFW_gamepadR3, +}; +i32 RGFW_checkXInput(RGFW_window* win, RGFW_event* e); +i32 RGFW_checkXInput(RGFW_window* win, RGFW_event* e) { + #ifndef RGFW_NO_XINPUT + + RGFW_UNUSED(win); + u16 i; + for (i = 0; i < 4; i++) { + XINPUT_KEYSTROKE keystroke; + + if (XInputGetKeystroke == NULL) + return 0; + + DWORD result = XInputGetKeystroke((DWORD)i, 0, &keystroke); + + if ((keystroke.Flags & XINPUT_KEYSTROKE_REPEAT) == 0 && result != ERROR_EMPTY) { + if (result != ERROR_SUCCESS) + return 0; + + if (keystroke.VirtualKey > VK_PAD_RTHUMB_PRESS) + continue; + + /* gamepad + 1 = RGFW_gamepadButtonReleased */ + e->type = RGFW_gamepadButtonPressed + !(keystroke.Flags & XINPUT_KEYSTROKE_KEYDOWN); + e->button = RGFW_xinput2RGFW[keystroke.VirtualKey - 0x5800]; + RGFW_gamepadPressed[i][e->button].prev = RGFW_gamepadPressed[i][e->button].current; + RGFW_gamepadPressed[i][e->button].current = RGFW_BOOL(keystroke.Flags & XINPUT_KEYSTROKE_KEYDOWN); + + RGFW_gamepadButtonCallback(win, i, e->button, e->type == RGFW_gamepadButtonPressed); + return 1; + } + + XINPUT_STATE state; + if (XInputGetState == NULL || + XInputGetState((DWORD) i, &state) == ERROR_DEVICE_NOT_CONNECTED + ) { + if (RGFW_gamepads[i] == 0) + continue; + + RGFW_gamepads[i] = 0; + RGFW_gamepadCount--; + + win->event.type = RGFW_gamepadDisconnected; + win->event.gamepad = (u16)i; + RGFW_gamepadCallback(win, i, 0); + return 1; + } + + if (RGFW_gamepads[i] == 0) { + RGFW_gamepads[i] = 1; + RGFW_gamepadCount++; + + char str[] = "Microsoft X-Box (XInput device)"; + RGFW_MEMCPY(RGFW_gamepads_name[i], str, sizeof(str)); + RGFW_gamepads_name[i][sizeof(RGFW_gamepads_name[i]) - 1] = '\0'; + win->event.type = RGFW_gamepadConnected; + win->event.gamepad = i; + RGFW_gamepads_type[i] = RGFW_gamepadMicrosoft; + + RGFW_gamepadCallback(win, i, 1); + return 1; + } + +#define INPUT_DEADZONE ( 0.24f * (float)(0x7FFF) ) /* Default to 24% of the +/- 32767 range. This is a reasonable default value but can be altered if needed. */ + + if ((state.Gamepad.sThumbLX < INPUT_DEADZONE && + state.Gamepad.sThumbLX > -INPUT_DEADZONE) && + (state.Gamepad.sThumbLY < INPUT_DEADZONE && + state.Gamepad.sThumbLY > -INPUT_DEADZONE)) + { + state.Gamepad.sThumbLX = 0; + state.Gamepad.sThumbLY = 0; + } + + if ((state.Gamepad.sThumbRX < INPUT_DEADZONE && + state.Gamepad.sThumbRX > -INPUT_DEADZONE) && + (state.Gamepad.sThumbRY < INPUT_DEADZONE && + state.Gamepad.sThumbRY > -INPUT_DEADZONE)) + { + state.Gamepad.sThumbRX = 0; + state.Gamepad.sThumbRY = 0; + } + + e->axisesCount = 2; + RGFW_point axis1 = RGFW_POINT(((float)state.Gamepad.sThumbLX / 32768.0f) * 100, ((float)state.Gamepad.sThumbLY / -32768.0f) * 100); + RGFW_point axis2 = RGFW_POINT(((float)state.Gamepad.sThumbRX / 32768.0f) * 100, ((float)state.Gamepad.sThumbRY / -32768.0f) * 100); + + if (axis1.x != e->axis[0].x || axis1.y != e->axis[0].y){ + win->event.whichAxis = 0; + + e->type = RGFW_gamepadAxisMove; + e->axis[0] = axis1; + RGFW_gamepadAxes[i][0] = e->axis[0]; + + RGFW_gamepadAxisCallback(win, e->gamepad, e->axis, e->axisesCount, e->whichAxis); + return 1; + } + + if (axis2.x != e->axis[1].x || axis2.y != e->axis[1].y) { + win->event.whichAxis = 1; + e->type = RGFW_gamepadAxisMove; + e->axis[1] = axis2; + RGFW_gamepadAxes[i][1] = e->axis[1]; + + RGFW_gamepadAxisCallback(win, e->gamepad, e->axis, e->axisesCount, e->whichAxis); + return 1; + } + } + + #endif + + return 0; +} + +void RGFW_stopCheckEvents(void) { + PostMessageW(_RGFW.root->src.window, WM_NULL, 0, 0); +} + +void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) { + RGFW_UNUSED(win); + MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD)waitMS, QS_ALLINPUT); +} + +RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { + if (win == NULL || ((win->_flags & RGFW_windowFreeOnClose) && (win->_flags & RGFW_EVENT_QUIT))) return NULL; + RGFW_event* ev = RGFW_window_checkEventCore(win); + if (ev) return ev; + + static HDROP drop; + if (win->event.type == RGFW_DNDInit) { + if (win->event.droppedFilesCount) { + u32 i; + for (i = 0; i < win->event.droppedFilesCount; i++) + win->event.droppedFiles[i][0] = '\0'; + } + + win->event.droppedFilesCount = 0; + win->event.droppedFilesCount = DragQueryFileW(drop, 0xffffffff, NULL, 0); + + u32 i; + for (i = 0; i < win->event.droppedFilesCount; i++) { + UINT length = DragQueryFileW(drop, i, NULL, 0); + if (length == 0) + continue; + + WCHAR buffer[RGFW_MAX_PATH * 2]; + if (length > (RGFW_MAX_PATH * 2) - 1) + length = RGFW_MAX_PATH * 2; + + DragQueryFileW(drop, i, buffer, length + 1); + + char* str = RGFW_createUTF8FromWideStringWin32(buffer); + if (str != NULL) + RGFW_MEMCPY(win->event.droppedFiles[i], str, length + 1); + + win->event.droppedFiles[i][RGFW_MAX_PATH - 1] = '\0'; + } + + DragFinish(drop); + RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount); + + win->event.type = RGFW_DND; + return &win->event; + } + + if (RGFW_checkXInput(win, &win->event)) + return &win->event; + + static BYTE keyboardState[256]; + GetKeyboardState(keyboardState); + + MSG msg; + if (PeekMessageA(&msg, NULL, 0u, 0u, PM_REMOVE)) { + if (msg.hwnd != win->src.window && msg.hwnd != NULL) { + TranslateMessage(&msg); + DispatchMessageA(&msg); + return RGFW_window_checkEvent(win); + } + } else { + return NULL; + } + + switch (msg.message) { + case WM_MOUSELEAVE: + win->event.type = RGFW_mouseLeave; + win->_flags |= RGFW_MOUSE_LEFT; + RGFW_mouseNotifyCallback(win, win->event.point, 0); + break; + case WM_SYSKEYUP: case WM_KEYUP: { + i32 scancode = (HIWORD(msg.lParam) & (KF_EXTENDED | 0xff)); + if (scancode == 0) + scancode = (i32)MapVirtualKeyW((UINT)msg.wParam, MAPVK_VK_TO_VSC); + + switch (scancode) { + case 0x54: scancode = 0x137; break; /* Alt+PrtS */ + case 0x146: scancode = 0x45; break; /* Ctrl+Pause */ + case 0x136: scancode = 0x36; break; /* CJK IME sets the extended bit for right Shift */ + default: break; + } + + win->event.key = (u8)RGFW_apiKeyToRGFW((u32) scancode); + + if (msg.wParam == VK_CONTROL) { + if (HIWORD(msg.lParam) & KF_EXTENDED) + win->event.key = RGFW_controlR; + else win->event.key = RGFW_controlL; + } + + wchar_t charBuffer; + ToUnicodeEx((UINT)msg.wParam, (UINT)scancode, keyboardState, (wchar_t*)&charBuffer, 1, 0, NULL); + + win->event.keyChar = (u8)charBuffer; + + RGFW_keyboard[win->event.key].prev = RGFW_keyboard[win->event.key].current; + win->event.type = RGFW_keyReleased; + RGFW_keyboard[win->event.key].current = 0; + + RGFW_updateKeyMods(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001), (GetKeyState(VK_SCROLL) & 0x0001)); + + RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, 0); + break; + } + case WM_SYSKEYDOWN: case WM_KEYDOWN: { + i32 scancode = (HIWORD(msg.lParam) & (KF_EXTENDED | 0xff)); + if (scancode == 0) + scancode = (i32)MapVirtualKeyW((u32)msg.wParam, MAPVK_VK_TO_VSC); + + switch (scancode) { + case 0x54: scancode = 0x137; break; /* Alt+PrtS */ + case 0x146: scancode = 0x45; break; /* Ctrl+Pause */ + case 0x136: scancode = 0x36; break; /* CJK IME sets the extended bit for right Shift */ + default: break; + } + + win->event.key = (u8)RGFW_apiKeyToRGFW((u32) scancode); + if (msg.wParam == VK_CONTROL) { + if (HIWORD(msg.lParam) & KF_EXTENDED) + win->event.key = RGFW_controlR; + else win->event.key = RGFW_controlL; + } + + wchar_t charBuffer; + ToUnicodeEx((UINT)msg.wParam, (UINT)scancode, keyboardState, &charBuffer, 1, 0, NULL); + win->event.keyChar = (u8)charBuffer; + + RGFW_keyboard[win->event.key].prev = RGFW_keyboard[win->event.key].current; + + win->event.type = RGFW_keyPressed; + win->event.repeat = RGFW_isPressed(win, win->event.key); + RGFW_keyboard[win->event.key].current = 1; + RGFW_updateKeyMods(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001), (GetKeyState(VK_SCROLL) & 0x0001)); + + RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, 1); + break; + } + case WM_MOUSEMOVE: { + if ((win->_flags & RGFW_HOLD_MOUSE)) + break; + + win->event.type = RGFW_mousePosChanged; + + i32 x = GET_X_LPARAM(msg.lParam); + i32 y = GET_Y_LPARAM(msg.lParam); + + RGFW_mousePosCallback(win, win->event.point, win->event.vector); + + if (win->_flags & RGFW_MOUSE_LEFT) { + win->_flags &= ~(u32)RGFW_MOUSE_LEFT; + win->event.type = RGFW_mouseEnter; + RGFW_mouseNotifyCallback(win, win->event.point, 1); + } + + win->event.point.x = x; + win->event.point.y = y; + win->_lastMousePoint = RGFW_POINT(x, y); + + break; + } + case WM_INPUT: { + if (!(win->_flags & RGFW_HOLD_MOUSE)) + break; + + unsigned size = sizeof(RAWINPUT); + static RAWINPUT raw = {0}; + + GetRawInputData((HRAWINPUT)msg.lParam, RID_INPUT, &raw, &size, sizeof(RAWINPUTHEADER)); + + if (raw.header.dwType != RIM_TYPEMOUSE || (raw.data.mouse.lLastX == 0 && raw.data.mouse.lLastY == 0) ) + break; + + if (raw.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) { + POINT pos = {0, 0}; + int width, height; + + if (raw.data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) { + pos.x += GetSystemMetrics(SM_XVIRTUALSCREEN); + pos.y += GetSystemMetrics(SM_YVIRTUALSCREEN); + width = GetSystemMetrics(SM_CXVIRTUALSCREEN); + height = GetSystemMetrics(SM_CYVIRTUALSCREEN); + } + else { + width = GetSystemMetrics(SM_CXSCREEN); + height = GetSystemMetrics(SM_CYSCREEN); + } + + pos.x += (int) (((float)raw.data.mouse.lLastX / 65535.f) * (float)width); + pos.y += (int) (((float)raw.data.mouse.lLastY / 65535.f) * (float)height); + ScreenToClient(win->src.window, &pos); + + win->event.vector.x = pos.x - win->_lastMousePoint.x; + win->event.vector.y = pos.y - win->_lastMousePoint.y; + } else { + win->event.vector.x = raw.data.mouse.lLastX; + win->event.vector.y = raw.data.mouse.lLastY; + } + + win->event.type = RGFW_mousePosChanged; + win->_lastMousePoint.x += win->event.vector.x; + win->_lastMousePoint.y += win->event.vector.y; + win->event.point = win->_lastMousePoint; + RGFW_mousePosCallback(win, win->event.point, win->event.vector); + break; + } + case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: case WM_XBUTTONDOWN: + if (msg.message == WM_XBUTTONDOWN) + win->event.button = RGFW_mouseMisc1 + (GET_XBUTTON_WPARAM(msg.wParam) == XBUTTON2); + else win->event.button = (msg.message == WM_LBUTTONDOWN) ? RGFW_mouseLeft : + (msg.message == WM_RBUTTONDOWN) ? RGFW_mouseRight : RGFW_mouseMiddle; + + win->event.type = RGFW_mouseButtonPressed; + RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; + RGFW_mouseButtons[win->event.button].current = 1; + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); + break; + case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: case WM_XBUTTONUP: + if (msg.message == WM_XBUTTONUP) + win->event.button = RGFW_mouseMisc1 + (GET_XBUTTON_WPARAM(msg.wParam) == XBUTTON2); + else win->event.button = (msg.message == WM_LBUTTONUP) ? RGFW_mouseLeft : + (msg.message == WM_RBUTTONUP) ? RGFW_mouseRight : RGFW_mouseMiddle; + win->event.type = RGFW_mouseButtonReleased; + RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; + RGFW_mouseButtons[win->event.button].current = 0; + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0); + break; + case WM_MOUSEWHEEL: + if (msg.wParam > 0) + win->event.button = RGFW_mouseScrollUp; + else + win->event.button = RGFW_mouseScrollDown; + + RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; + RGFW_mouseButtons[win->event.button].current = 1; + + win->event.scroll = (SHORT) HIWORD(msg.wParam) / (double) WHEEL_DELTA; + + win->event.type = RGFW_mouseButtonPressed; + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); + break; + case WM_DROPFILES: { + win->event.type = RGFW_DNDInit; + + drop = (HDROP) msg.wParam; + POINT pt; + + /* Move the mouse to the position of the drop */ + DragQueryPoint(drop, &pt); + + win->event.point.x = pt.x; + win->event.point.y = pt.y; + + RGFW_dndInitCallback(win, win->event.point); + } + break; + default: + TranslateMessage(&msg); + DispatchMessageA(&msg); + return RGFW_window_checkEvent(win); + } + + TranslateMessage(&msg); + DispatchMessageA(&msg); + + return &win->event; +} + +RGFW_bool RGFW_window_isHidden(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + return IsWindowVisible(win->src.window) == 0 && !RGFW_window_isMinimized(win); +} + +RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + #ifndef __cplusplus + WINDOWPLACEMENT placement = { 0 }; + #else + WINDOWPLACEMENT placement = { }; + #endif + GetWindowPlacement(win->src.window, &placement); + return placement.showCmd == SW_SHOWMINIMIZED; +} + +RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + #ifndef __cplusplus + WINDOWPLACEMENT placement = { 0 }; + #else + WINDOWPLACEMENT placement = { }; + #endif + GetWindowPlacement(win->src.window, &placement); + return placement.showCmd == SW_SHOWMAXIMIZED || IsZoomed(win->src.window); +} + +typedef struct { int iIndex; HMONITOR hMonitor; RGFW_monitor* monitors; } RGFW_mInfo; +#ifndef RGFW_NO_MONITOR +RGFW_monitor win32CreateMonitor(HMONITOR src); +RGFW_monitor win32CreateMonitor(HMONITOR src) { + RGFW_monitor monitor; + MONITORINFOEX monitorInfo; + + monitorInfo.cbSize = sizeof(MONITORINFOEX); + GetMonitorInfoA(src, (LPMONITORINFO)&monitorInfo); + + /* get the monitor's index */ + DISPLAY_DEVICEA dd; + dd.cb = sizeof(dd); + + DWORD deviceNum; + for (deviceNum = 0; EnumDisplayDevicesA(NULL, deviceNum, &dd, 0); deviceNum++) { + if (!(dd.StateFlags & DISPLAY_DEVICE_ACTIVE)) + continue; + + DEVMODE dm; + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + + if (EnumDisplaySettingsA(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm)) { + monitor.mode.refreshRate = dm.dmDisplayFrequency; + RGFW_splitBPP(dm.dmBitsPerPel, &monitor.mode); + } + + DISPLAY_DEVICEA mdd; + mdd.cb = sizeof(mdd); + + if (EnumDisplayDevicesA(dd.DeviceName, (DWORD)deviceNum, &mdd, 0)) { + RGFW_MEMCPY(monitor.name, mdd.DeviceString, 128); + break; + } + } + + + + + monitor.x = monitorInfo.rcWork.left; + monitor.y = monitorInfo.rcWork.top; + monitor.mode.area.w = (u32)(monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left); + monitor.mode.area.h = (u32)(monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top); + + HDC hdc = CreateDC(monitorInfo.szDevice, NULL, NULL, NULL); + /* get pixels per inch */ + float dpiX = (float)GetDeviceCaps(hdc, LOGPIXELSX); + float dpiY = (float)GetDeviceCaps(hdc, LOGPIXELSX); + + monitor.scaleX = dpiX / 96.0f; + monitor.scaleY = dpiY / 96.0f; + monitor.pixelRatio = dpiX >= 192.0f ? 2.0f : 1.0f; + + monitor.physW = (float)GetDeviceCaps(hdc, HORZSIZE) / 25.4f; + monitor.physH = (float)GetDeviceCaps(hdc, VERTSIZE) / 25.4f; + DeleteDC(hdc); + + #ifndef RGFW_NO_DPI + RGFW_LOAD_LIBRARY(RGFW_Shcore_dll, "shcore.dll"); + RGFW_PROC_DEF(RGFW_Shcore_dll, GetDpiForMonitor); + + if (GetDpiForMonitor != NULL) { + u32 x, y; + GetDpiForMonitor(src, MDT_EFFECTIVE_DPI, &x, &y); + monitor.scaleX = (float) (x) / (float) 96.0f; + monitor.scaleY = (float) (y) / (float) 96.0f; + monitor.pixelRatio = dpiX >= 192.0f ? 2.0f : 1.0f; + } + #endif + + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, RGFW_DEBUG_CTX_MON(monitor), "monitor found"); + return monitor; +} +#endif /* RGFW_NO_MONITOR */ + +#ifndef RGFW_NO_MONITOR +BOOL CALLBACK GetMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData); +BOOL CALLBACK GetMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { + RGFW_UNUSED(hdcMonitor); + RGFW_UNUSED(lprcMonitor); + + RGFW_mInfo* info = (RGFW_mInfo*) dwData; + + + if (info->iIndex >= 6) + return FALSE; + + info->monitors[info->iIndex] = win32CreateMonitor(hMonitor); + info->iIndex++; + + return TRUE; +} + +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(size_t* len) { + static RGFW_monitor monitors[6]; + RGFW_mInfo info; + info.iIndex = 0; + info.monitors = monitors; + + EnumDisplayMonitors(NULL, NULL, GetMonitorHandle, (LPARAM) &info); + + if (len != NULL) *len = (size_t)info.iIndex; + return monitors; +} + +RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { + HMONITOR src = MonitorFromWindow(win->src.window, MONITOR_DEFAULTTOPRIMARY); + return win32CreateMonitor(src); +} + +RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) { + HMONITOR src = MonitorFromPoint((POINT) { mon.x, mon.y }, MONITOR_DEFAULTTOPRIMARY); + + MONITORINFOEX monitorInfo; + monitorInfo.cbSize = sizeof(MONITORINFOEX); + GetMonitorInfoA(src, (LPMONITORINFO)&monitorInfo); + + DISPLAY_DEVICE dd; + dd.cb = sizeof(dd); + + /* Enumerate display devices */ + DWORD deviceNum; + for (deviceNum = 0; EnumDisplayDevicesA(NULL, deviceNum, &dd, 0); deviceNum++) { + if (!(dd.StateFlags & DISPLAY_DEVICE_ACTIVE)) + continue; + + if (strcmp(dd.DeviceName, monitorInfo.szDevice) != 0) + continue; + + DEVMODE dm; + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + + if (EnumDisplaySettingsA(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm)) { + if (request & RGFW_monitorScale) { + dm.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT; + dm.dmPelsWidth = mode.area.w; + dm.dmPelsHeight = mode.area.h; + } + + if (request & RGFW_monitorRefresh) { + dm.dmFields |= DM_DISPLAYFREQUENCY; + dm.dmDisplayFrequency = mode.refreshRate; + } + + if (request & RGFW_monitorRGB) { + dm.dmFields |= DM_BITSPERPEL; + dm.dmBitsPerPel = (DWORD)(mode.red + mode.green + mode.blue); + } + + if (ChangeDisplaySettingsEx(dd.DeviceName, &dm, NULL, CDS_TEST, NULL) == DISP_CHANGE_SUCCESSFUL) { + if (ChangeDisplaySettingsEx(dd.DeviceName, &dm, NULL, CDS_UPDATEREGISTRY, NULL) == DISP_CHANGE_SUCCESSFUL) + return RGFW_TRUE; + return RGFW_FALSE; + } else return RGFW_FALSE; + } + } + + return RGFW_FALSE; +} + +#endif +HICON RGFW_loadHandleImage(u8* src, i32 c, RGFW_area a, BOOL icon); +HICON RGFW_loadHandleImage(u8* src, i32 c, RGFW_area a, BOOL icon) { + size_t channels = (size_t)c; + + BITMAPV5HEADER bi; + ZeroMemory(&bi, sizeof(bi)); + bi.bV5Size = sizeof(bi); + bi.bV5Width = (i32)a.w; + bi.bV5Height = -((LONG) a.h); + bi.bV5Planes = 1; + bi.bV5BitCount = (WORD)(channels * 8); + bi.bV5Compression = BI_RGB; + HDC dc = GetDC(NULL); + u8* target = NULL; + + HBITMAP color = CreateDIBSection(dc, + (BITMAPINFO*) &bi, DIB_RGB_COLORS, (void**) &target, + NULL, (DWORD) 0); + + size_t x, y; + for (y = 0; y < a.h; y++) { + for (x = 0; x < a.w; x++) { + size_t index = (y * 4 * (size_t)a.w) + x * channels; + target[index] = src[index + 2]; + target[index + 1] = src[index + 1]; + target[index + 2] = src[index]; + target[index + 3] = src[index + 3]; + } + } + + ReleaseDC(NULL, dc); + + HBITMAP mask = CreateBitmap((i32)a.w, (i32)a.h, 1, 1, NULL); + + ICONINFO ii; + ZeroMemory(&ii, sizeof(ii)); + ii.fIcon = icon; + ii.xHotspot = a.w / 2; + ii.yHotspot = a.h / 2; + ii.hbmMask = mask; + ii.hbmColor = color; + + HICON handle = CreateIconIndirect(&ii); + + DeleteObject(color); + DeleteObject(mask); + + return handle; +} + +void* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels) { + HCURSOR cursor = (HCURSOR) RGFW_loadHandleImage(icon, channels, a, FALSE); + return cursor; +} + +void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) { + RGFW_ASSERT(win && mouse); + SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) mouse); + SetCursor((HCURSOR)mouse); +} + +void RGFW_freeMouse(RGFW_mouse* mouse) { + RGFW_ASSERT(mouse); + DestroyCursor((HCURSOR)mouse); +} + +RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) { + return RGFW_window_setMouseStandard(win, RGFW_mouseArrow); +} + +RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { + RGFW_ASSERT(win != NULL); + + static const u32 mouseIconSrc[16] = {OCR_NORMAL, OCR_NORMAL, OCR_IBEAM, OCR_CROSS, OCR_HAND, OCR_SIZEWE, OCR_SIZENS, OCR_SIZENWSE, OCR_SIZENESW, OCR_SIZEALL, OCR_NO}; + if (mouse > (sizeof(mouseIconSrc) / sizeof(u32))) + return RGFW_FALSE; + + char* icon = MAKEINTRESOURCEA(mouseIconSrc[mouse]); + + SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) LoadCursorA(NULL, icon)); + SetCursor(LoadCursorA(NULL, icon)); + return RGFW_TRUE; +} + +void RGFW_window_hide(RGFW_window* win) { + ShowWindow(win->src.window, SW_HIDE); +} + +void RGFW_window_show(RGFW_window* win) { + if (win->_flags & RGFW_windowFocusOnShow) RGFW_window_focus(win); + ShowWindow(win->src.window, SW_RESTORE); +} + +#define RGFW_FREE_LIBRARY(x) if (x != NULL) FreeLibrary(x); x = NULL; +void RGFW_deinit(void) { + #ifndef RGFW_NO_XINPUT + RGFW_FREE_LIBRARY(RGFW_XInput_dll); + #endif + + #ifndef RGFW_NO_DPI + RGFW_FREE_LIBRARY(RGFW_Shcore_dll); + #endif + + #ifndef RGFW_NO_WINMM + timeEndPeriod(1); + #ifndef RGFW_NO_LOAD_WINMM + RGFW_FREE_LIBRARY(RGFW_winmm_dll); + #endif + #endif + + RGFW_FREE_LIBRARY(RGFW_wgl_dll); + _RGFW.root = NULL; + + RGFW_freeMouse(_RGFW.hiddenMouse); + _RGFW.windowCount = -1; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, (RGFW_debugContext){0}, "global context deinitialized"); +} + + +void RGFW_window_close(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + #ifdef RGFW_BUFFER + DeleteDC(win->src.hdcMem); + DeleteObject(win->src.bitmap); + #endif + + if ((win->_flags & RGFW_windowNoInitAPI) == 0) RGFW_window_freeOpenGL(win); + RemovePropW(win->src.window, L"RGFW"); + ReleaseDC(win->src.window, win->src.hdc); /*!< delete device context */ + DestroyWindow(win->src.window); /*!< delete window */ + + if (win->src.hIconSmall) DestroyIcon(win->src.hIconSmall); + if (win->src.hIconBig) DestroyIcon(win->src.hIconBig); + + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a window was freed"); + _RGFW.windowCount--; + if (_RGFW.windowCount == 0) RGFW_deinit(); + + RGFW_clipboard_switch(NULL); + RGFW_FREE(win->event.droppedFiles); + if ((win->_flags & RGFW_WINDOW_ALLOC)) { + RGFW_FREE(win); + win = NULL; + } +} + +void RGFW_window_move(RGFW_window* win, RGFW_point v) { + RGFW_ASSERT(win != NULL); + + win->r.x = v.x; + win->r.y = v.y; + SetWindowPos(win->src.window, HWND_TOP, win->r.x, win->r.y, 0, 0, SWP_NOSIZE); +} + +void RGFW_window_resize(RGFW_window* win, RGFW_area a) { + RGFW_ASSERT(win != NULL); + + win->r.w = (i32)a.w; + win->r.h = (i32)a.h; + SetWindowPos(win->src.window, HWND_TOP, 0, 0, win->r.w, win->r.h + (i32)win->src.hOffset, SWP_NOMOVE); +} + + +void RGFW_window_setName(RGFW_window* win, const char* name) { + RGFW_ASSERT(win != NULL); + + wchar_t wide_name[256]; + MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_name, 256); + SetWindowTextW(win->src.window, wide_name); +} + +#ifndef RGFW_NO_PASSTHROUGH +void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) { + RGFW_ASSERT(win != NULL); + COLORREF key = 0; + BYTE alpha = 0; + DWORD flags = 0; + i32 exStyle = GetWindowLongW(win->src.window, GWL_EXSTYLE); + + if (exStyle & WS_EX_LAYERED) + GetLayeredWindowAttributes(win->src.window, &key, &alpha, &flags); + + if (passthrough) + exStyle |= (WS_EX_TRANSPARENT | WS_EX_LAYERED); + else { + exStyle &= ~WS_EX_TRANSPARENT; + if (exStyle & WS_EX_LAYERED && !(flags & LWA_ALPHA)) + exStyle &= ~WS_EX_LAYERED; + } + + SetWindowLongW(win->src.window, GWL_EXSTYLE, exStyle); + + if (passthrough) + SetLayeredWindowAttributes(win->src.window, key, alpha, flags); +} +#endif + +RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* src, RGFW_area a, i32 channels, u8 type) { + RGFW_ASSERT(win != NULL); + #ifndef RGFW_WIN95 + RGFW_UNUSED(channels); + + if (win->src.hIconSmall && (type & RGFW_iconWindow)) DestroyIcon(win->src.hIconSmall); + if (win->src.hIconBig && (type & RGFW_iconTaskbar)) DestroyIcon(win->src.hIconBig); + + if (src == NULL) { + HICON defaultIcon = LoadIcon(NULL, IDI_APPLICATION); + if (type & RGFW_iconWindow) + SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)defaultIcon); + if (type & RGFW_iconTaskbar) + SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)defaultIcon); + return RGFW_TRUE; + } + + if (type & RGFW_iconWindow) { + win->src.hIconSmall = RGFW_loadHandleImage(src, channels, a, TRUE); + SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)win->src.hIconSmall); + } + if (type & RGFW_iconTaskbar) { + win->src.hIconBig = RGFW_loadHandleImage(src, channels, a, TRUE); + SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)win->src.hIconBig); + } + return RGFW_TRUE; + #else + RGFW_UNUSED(src); + RGFW_UNUSED(a); + RGFW_UNUSED(channels); + return RGFW_FALSE; + #endif +} + +RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { + /* Open the clipboard */ + if (OpenClipboard(NULL) == 0) + return -1; + + /* Get the clipboard data as a Unicode string */ + HANDLE hData = GetClipboardData(CF_UNICODETEXT); + if (hData == NULL) { + CloseClipboard(); + return -1; + } + + wchar_t* wstr = (wchar_t*) GlobalLock(hData); + + RGFW_ssize_t textLen = 0; + + { + setlocale(LC_ALL, "en_US.UTF-8"); + + textLen = (RGFW_ssize_t)wcstombs(NULL, wstr, 0) + 1; + if (str != NULL && (RGFW_ssize_t)strCapacity <= textLen - 1) + textLen = 0; + + if (str != NULL && textLen) { + if (textLen > 1) + wcstombs(str, wstr, (size_t)(textLen)); + + str[textLen] = '\0'; + } + } + + /* Release the clipboard data */ + GlobalUnlock(hData); + CloseClipboard(); + + return textLen; +} + +void RGFW_writeClipboard(const char* text, u32 textLen) { + HANDLE object; + WCHAR* buffer; + + object = GlobalAlloc(GMEM_MOVEABLE, (1 + textLen) * sizeof(WCHAR)); + if (!object) + return; + + buffer = (WCHAR*) GlobalLock(object); + if (!buffer) { + GlobalFree(object); + return; + } + + MultiByteToWideChar(CP_UTF8, 0, text, -1, buffer, (i32)textLen); + GlobalUnlock(object); + + if (!OpenClipboard(_RGFW.root->src.window)) { + GlobalFree(object); + return; + } + + EmptyClipboard(); + SetClipboardData(CF_UNICODETEXT, object); + CloseClipboard(); +} + +void RGFW_window_moveMouse(RGFW_window* win, RGFW_point p) { + RGFW_ASSERT(win != NULL); + win->_lastMousePoint = RGFW_POINT(p.x - win->r.x, p.y - win->r.y); + SetCursorPos(p.x, p.y); +} + +#ifdef RGFW_OPENGL +void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { + if (win == NULL) + wglMakeCurrent(NULL, NULL); + else + wglMakeCurrent(win->src.hdc, (HGLRC) win->src.ctx); +} +void* RGFW_getCurrent_OpenGL(void) { return wglGetCurrentContext(); } +void RGFW_window_swapBuffers_OpenGL(RGFW_window* win){ SwapBuffers(win->src.hdc); } +#endif + +#ifndef RGFW_EGL +void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { + RGFW_ASSERT(win != NULL); +#if defined(RGFW_OPENGL) + if (wglSwapIntervalEXT == NULL || wglSwapIntervalEXT(swapInterval) == FALSE) + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to set swap interval"); +#else + RGFW_UNUSED(swapInterval); +#endif +} +#endif + +void RGFW_window_swapBuffers_software(RGFW_window* win) { +#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + if (win->buffer != win->src.bitmapBits) + memcpy(win->src.bitmapBits, win->buffer, win->bufferSize.w * win->bufferSize.h * 4); + + RGFW_RGB_to_BGR(win, win->src.bitmapBits); + BitBlt(win->src.hdc, 0, 0, win->r.w, win->r.h, win->src.hdcMem, 0, 0, SRCCOPY); +#else + RGFW_UNUSED(win); +#endif +} + +char* RGFW_createUTF8FromWideStringWin32(const WCHAR* source) { + if (source == NULL) { + return NULL; + } + i32 size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL); + if (!size) { + return NULL; + } + + static char target[RGFW_MAX_PATH * 2]; + if (size > RGFW_MAX_PATH * 2) + size = RGFW_MAX_PATH * 2; + + target[size] = 0; + + if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, size, NULL, NULL)) { + return NULL; + } + + return target; +} + +u64 RGFW_getTimerFreq(void) { + static u64 frequency = 0; + if (frequency == 0) QueryPerformanceFrequency((LARGE_INTEGER*)&frequency); + + return frequency; +} + +u64 RGFW_getTimerValue(void) { + u64 value; + QueryPerformanceCounter((LARGE_INTEGER*)&value); + return value; +} + +void RGFW_sleep(u64 ms) { + Sleep((u32)ms); +} + +#ifndef RGFW_NO_THREADS + +RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args) { return CreateThread(NULL, 0, ptr, args, 0, NULL); } +void RGFW_cancelThread(RGFW_thread thread) { CloseHandle((HANDLE) thread); } +void RGFW_joinThread(RGFW_thread thread) { WaitForSingleObject((HANDLE) thread, INFINITE); } +void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { SetThreadPriority((HANDLE) thread, priority); } + +#endif +#endif /* RGFW_WINDOWS */ + +/* + End of Windows defines +*/ + + + +/* + + Start of MacOS defines + + +*/ + +#if defined(RGFW_MACOS) +/* + based on silicon.h + start of cocoa wrapper +*/ + +#include +#include +#include +#include +#include +#include + +typedef CGRect NSRect; +typedef CGPoint NSPoint; +typedef CGSize NSSize; + +typedef const char* NSPasteboardType; +typedef unsigned long NSUInteger; +typedef long NSInteger; +typedef NSInteger NSModalResponse; + +#ifdef __arm64__ + /* ARM just uses objc_msgSend */ +#define abi_objc_msgSend_stret objc_msgSend +#define abi_objc_msgSend_fpret objc_msgSend +#else /* __i386__ */ + /* x86 just uses abi_objc_msgSend_fpret and (NSColor *)objc_msgSend_id respectively */ +#define abi_objc_msgSend_stret objc_msgSend_stret +#define abi_objc_msgSend_fpret objc_msgSend_fpret +#endif + +#define NSAlloc(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("alloc")) +#define objc_msgSend_bool(x, y) ((BOOL (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y) +#define objc_msgSend_void(x, y) ((void (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y) +#define objc_msgSend_void_id(x, y, z) ((void (*)(id, SEL, id))objc_msgSend) ((id)x, (SEL)y, (id)z) +#define objc_msgSend_uint(x, y) ((NSUInteger (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y) +#define objc_msgSend_void_bool(x, y, z) ((void (*)(id, SEL, BOOL))objc_msgSend) ((id)(x), (SEL)y, (BOOL)z) +#define objc_msgSend_bool_void(x, y) ((BOOL (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y) +#define objc_msgSend_void_SEL(x, y, z) ((void (*)(id, SEL, SEL))objc_msgSend) ((id)(x), (SEL)y, (SEL)z) +#define objc_msgSend_id(x, y) ((id (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y) +#define objc_msgSend_id_id(x, y, z) ((id (*)(id, SEL, id))objc_msgSend) ((id)(x), (SEL)y, (id)z) +#define objc_msgSend_id_bool(x, y, z) ((BOOL (*)(id, SEL, id))objc_msgSend) ((id)(x), (SEL)y, (id)z) +#define objc_msgSend_int(x, y, z) ((id (*)(id, SEL, int))objc_msgSend) ((id)(x), (SEL)y, (int)z) +#define objc_msgSend_arr(x, y, z) ((id (*)(id, SEL, int))objc_msgSend) ((id)(x), (SEL)y, (int)z) +#define objc_msgSend_ptr(x, y, z) ((id (*)(id, SEL, void*))objc_msgSend) ((id)(x), (SEL)y, (void*)z) +#define objc_msgSend_class(x, y) ((id (*)(Class, SEL))objc_msgSend) ((Class)(x), (SEL)y) +#define objc_msgSend_class_char(x, y, z) ((id (*)(Class, SEL, char*))objc_msgSend) ((Class)(x), (SEL)y, (char*)z) + +id NSApp = NULL; + +#define NSRelease(obj) objc_msgSend_void((id)obj, sel_registerName("release")) +id NSString_stringWithUTF8String(const char* str); +id NSString_stringWithUTF8String(const char* str) { + return ((id(*)(id, SEL, const char*))objc_msgSend) + ((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), str); +} + +const char* NSString_to_char(id str); +const char* NSString_to_char(id str) { + return ((const char* (*)(id, SEL)) objc_msgSend) ((id)(id)str, sel_registerName("UTF8String")); +} + +void si_impl_func_to_SEL_with_name(const char* class_name, const char* register_name, void* function); +void si_impl_func_to_SEL_with_name(const char* class_name, const char* register_name, void* function) { + Class selected_class; + + if (RGFW_STRNCMP(class_name, "NSView", 6) == 0) { + selected_class = objc_getClass("ViewClass"); + } else if (RGFW_STRNCMP(class_name, "NSWindow", 8) == 0) { + selected_class = objc_getClass("WindowClass"); + } else { + selected_class = objc_getClass(class_name); + } + + class_addMethod(selected_class, sel_registerName(register_name), (IMP) function, 0); +} + +/* Header for the array. */ +typedef struct siArrayHeader { + size_t count; + /* TODO(EimaMei): Add a `type_width` later on. */ +} siArrayHeader; + +/* Gets the header of the siArray. */ +#define SI_ARRAY_HEADER(s) ((siArrayHeader*)s - 1) +#define si_array_len(array) (SI_ARRAY_HEADER(array)->count) +#define si_func_to_SEL(class_name, function) si_impl_func_to_SEL_with_name(class_name, #function":", (void*)function) +/* Creates an Objective-C method (SEL) from a regular C function with the option to set the register name.*/ +#define si_func_to_SEL_with_name(class_name, register_name, function) si_impl_func_to_SEL_with_name(class_name, register_name":", (void*)function) + +unsigned char* NSBitmapImageRep_bitmapData(id imageRep); +unsigned char* NSBitmapImageRep_bitmapData(id imageRep) { + return ((unsigned char* (*)(id, SEL))objc_msgSend) ((id)imageRep, sel_registerName("bitmapData")); +} + +typedef RGFW_ENUM(NSUInteger, NSBitmapFormat) { + NSBitmapFormatAlphaFirst = 1 << 0, /* 0 means is alpha last (RGBA, CMYKA, etc.) */ + NSBitmapFormatAlphaNonpremultiplied = 1 << 1, /* 0 means is premultiplied */ + NSBitmapFormatFloatingpointSamples = 1 << 2, /* 0 is integer */ + + NSBitmapFormatSixteenBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 8), + NSBitmapFormatThirtyTwoBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 9), + NSBitmapFormatSixteenBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 10), + NSBitmapFormatThirtyTwoBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 11) +}; + +id NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits); +id NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits) { + SEL func = sel_registerName("initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bitmapFormat:bytesPerRow:bitsPerPixel:"); + + return (id) ((id(*)(id, SEL, unsigned char**, NSInteger, NSInteger, NSInteger, NSInteger, bool, bool, id, NSBitmapFormat, NSInteger, NSInteger))objc_msgSend) + (NSAlloc((id)objc_getClass("NSBitmapImageRep")), func, planes, width, height, bps, spp, alpha, isPlanar, NSString_stringWithUTF8String(colorSpaceName), bitmapFormat, rowBytes, pixelBits); +} + +id NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha); +id NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha) { + void* nsclass = objc_getClass("NSColor"); + SEL func = sel_registerName("colorWithSRGBRed:green:blue:alpha:"); + return ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend) + ((id)nsclass, func, red, green, blue, alpha); +} + +#define NS_OPENGL_ENUM_DEPRECATED(minVers, maxVers) API_AVAILABLE(macos(minVers)) +typedef RGFW_ENUM(NSInteger, NSOpenGLContextParameter) { + NSOpenGLContextParameterSwapInterval NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 222, /* 1 param. 0 -> Don't sync, 1 -> Sync to vertical retrace */ + NSOpenGLContextParametectxaceOrder NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 235, /* 1 param. 1 -> Above Window (default), -1 -> Below Window */ + NSOpenGLContextParametectxaceOpacity NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 236, /* 1 param. 1-> Surface is opaque (default), 0 -> non-opaque */ + NSOpenGLContextParametectxaceBackingSize NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 304, /* 2 params. Width/height of surface backing size */ + NSOpenGLContextParameterReclaimResources NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 308, /* 0 params. */ + NSOpenGLContextParameterCurrentRendererID NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 309, /* 1 param. Retrieves the current renderer ID */ + NSOpenGLContextParameterGPUVertexProcessing NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 310, /* 1 param. Currently processing vertices with GPU (get) */ + NSOpenGLContextParameterGPUFragmentProcessing NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 311, /* 1 param. Currently processing fragments with GPU (get) */ + NSOpenGLContextParameterHasDrawable NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 314, /* 1 param. Boolean returned if drawable is attached */ + NSOpenGLContextParameterMPSwapsInFlight NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 315, /* 1 param. Max number of swaps queued by the MP GL engine */ + + NSOpenGLContextParameterSwapRectangle API_DEPRECATED("", macos(10.0, 10.14)) = 200, /* 4 params. Set or get the swap rectangle {x, y, w, h} */ + NSOpenGLContextParameterSwapRectangleEnable API_DEPRECATED("", macos(10.0, 10.14)) = 201, /* Enable or disable the swap rectangle */ + NSOpenGLContextParameterRasterizationEnable API_DEPRECATED("", macos(10.0, 10.14)) = 221, /* Enable or disable all rasterization */ + NSOpenGLContextParameterStateValidation API_DEPRECATED("", macos(10.0, 10.14)) = 301, /* Validate state for multi-screen functionality */ + NSOpenGLContextParametectxaceSurfaceVolatile API_DEPRECATED("", macos(10.0, 10.14)) = 306, /* 1 param. Surface volatile state */ +}; + +typedef RGFW_ENUM(NSInteger, NSWindowButton) { + NSWindowCloseButton = 0, + NSWindowMiniaturizeButton = 1, + NSWindowZoomButton = 2, + NSWindowToolbarButton = 3, + NSWindowDocumentIconButton = 4, + NSWindowDocumentVersionsButton = 6, + NSWindowFullScreenButton = 7, +}; +void NSOpenGLContext_setValues(id context, const int* vals, NSOpenGLContextParameter param); +void NSOpenGLContext_setValues(id context, const int* vals, NSOpenGLContextParameter param) { + ((void (*)(id, SEL, const int*, NSOpenGLContextParameter))objc_msgSend) + (context, sel_registerName("setValues:forParameter:"), vals, param); +} +void* NSOpenGLPixelFormat_initWithAttributes(const uint32_t* attribs); +void* NSOpenGLPixelFormat_initWithAttributes(const uint32_t* attribs) { + return (void*) ((id(*)(id, SEL, const uint32_t*))objc_msgSend) + (NSAlloc((id)objc_getClass("NSOpenGLPixelFormat")), sel_registerName("initWithAttributes:"), attribs); +} + +id NSPasteboard_generalPasteboard(void); +id NSPasteboard_generalPasteboard(void) { + return (id) objc_msgSend_id((id)objc_getClass("NSPasteboard"), sel_registerName("generalPasteboard")); +} + +id* cstrToNSStringArray(char** strs, size_t len); +id* cstrToNSStringArray(char** strs, size_t len) { + static id nstrs[6]; + size_t i; + for (i = 0; i < len; i++) + nstrs[i] = NSString_stringWithUTF8String(strs[i]); + + return nstrs; +} + +const char* NSPasteboard_stringForType(id pasteboard, NSPasteboardType dataType, size_t* len); +const char* NSPasteboard_stringForType(id pasteboard, NSPasteboardType dataType, size_t* len) { + SEL func = sel_registerName("stringForType:"); + id nsstr = NSString_stringWithUTF8String(dataType); + id nsString = ((id(*)(id, SEL, id))objc_msgSend)(pasteboard, func, nsstr); + const char* str = NSString_to_char(nsString); + if (len != NULL) + *len = (size_t)((NSUInteger(*)(id, SEL, int))objc_msgSend)(nsString, sel_registerName("maximumLengthOfBytesUsingEncoding:"), 4); + return str; +} + +id c_array_to_NSArray(void* array, size_t len); +id c_array_to_NSArray(void* array, size_t len) { + SEL func = sel_registerName("initWithObjects:count:"); + void* nsclass = objc_getClass("NSArray"); + return ((id (*)(id, SEL, void*, NSUInteger))objc_msgSend) + (NSAlloc(nsclass), func, array, len); +} + + +void NSregisterForDraggedTypes(id view, NSPasteboardType* newTypes, size_t len); +void NSregisterForDraggedTypes(id view, NSPasteboardType* newTypes, size_t len) { + id* ntypes = cstrToNSStringArray((char**)newTypes, len); + + id array = c_array_to_NSArray(ntypes, len); + objc_msgSend_void_id(view, sel_registerName("registerForDraggedTypes:"), array); + NSRelease(array); +} + +NSInteger NSPasteBoard_declareTypes(id pasteboard, NSPasteboardType* newTypes, size_t len, void* owner); +NSInteger NSPasteBoard_declareTypes(id pasteboard, NSPasteboardType* newTypes, size_t len, void* owner) { + id* ntypes = cstrToNSStringArray((char**)newTypes, len); + + SEL func = sel_registerName("declareTypes:owner:"); + + id array = c_array_to_NSArray(ntypes, len); + + NSInteger output = ((NSInteger(*)(id, SEL, id, void*))objc_msgSend) + (pasteboard, func, array, owner); + NSRelease(array); + + return output; +} + +#define NSRetain(obj) objc_msgSend_void((id)obj, sel_registerName("retain")) + +typedef enum NSApplicationActivationPolicy { + NSApplicationActivationPolicyRegular, + NSApplicationActivationPolicyAccessory, + NSApplicationActivationPolicyProhibited +} NSApplicationActivationPolicy; + +typedef RGFW_ENUM(u32, NSBackingStoreType) { + NSBackingStoreRetained = 0, + NSBackingStoreNonretained = 1, + NSBackingStoreBuffered = 2 +}; + +typedef RGFW_ENUM(u32, NSWindowStyleMask) { + NSWindowStyleMaskBorderless = 0, + NSWindowStyleMaskTitled = 1 << 0, + NSWindowStyleMaskClosable = 1 << 1, + NSWindowStyleMaskMiniaturizable = 1 << 2, + NSWindowStyleMaskResizable = 1 << 3, + NSWindowStyleMaskTexturedBackground = 1 << 8, /* deprecated */ + NSWindowStyleMaskUnifiedTitleAndToolbar = 1 << 12, + NSWindowStyleMaskFullScreen = 1 << 14, + NSWindowStyleMaskFullSizeContentView = 1 << 15, + NSWindowStyleMaskUtilityWindow = 1 << 4, + NSWindowStyleMaskDocModalWindow = 1 << 6, + NSWindowStyleMaskNonactivatingpanel = 1 << 7, + NSWindowStyleMaskHUDWindow = 1 << 13 +}; + +NSPasteboardType const NSPasteboardTypeString = "public.utf8-plain-text"; /* Replaces NSStringPasteboardType */ + + +typedef RGFW_ENUM(i32, NSDragOperation) { + NSDragOperationNone = 0, + NSDragOperationCopy = 1, + NSDragOperationLink = 2, + NSDragOperationGeneric = 4, + NSDragOperationPrivate = 8, + NSDragOperationMove = 16, + NSDragOperationDelete = 32, + NSDragOperationEvery = (int)ULONG_MAX +}; + +void* NSArray_objectAtIndex(id array, NSUInteger index) { + SEL func = sel_registerName("objectAtIndex:"); + return ((id(*)(id, SEL, NSUInteger))objc_msgSend)(array, func, index); +} + +id NSWindow_contentView(id window) { + SEL func = sel_registerName("contentView"); + return objc_msgSend_id(window, func); +} + +/* + End of cocoa wrapper +*/ + +#ifdef RGFW_OPENGL +CFBundleRef RGFWnsglFramework = NULL; + +RGFW_proc RGFW_getProcAddress(const char* procname) { + if (RGFWnsglFramework == NULL) + RGFWnsglFramework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); + + CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, procname, kCFStringEncodingASCII); + + RGFW_proc symbol = (RGFW_proc)CFBundleGetFunctionPointerForName(RGFWnsglFramework, symbolName); + + CFRelease(symbolName); + + return symbol; +} +#endif + +id NSWindow_delegate(RGFW_window* win) { + return (id) objc_msgSend_id((id)win->src.window, sel_registerName("delegate")); +} + +u32 RGFW_OnClose(id self) { + RGFW_window* win = NULL; + object_getInstanceVariable(self, (const char*)"RGFW_window", (void**)&win); + if (win == NULL) + return true; + + RGFW_eventQueuePush((RGFW_event){.type = RGFW_quit, ._win = win}); + RGFW_windowQuitCallback(win); + + return false; +} + +/* NOTE(EimaMei): Fixes the constant clicking when the app is running under a terminal. */ +bool acceptsFirstResponder(void) { return true; } +bool performKeyEquivalent(id event) { RGFW_UNUSED(event); return true; } + +NSDragOperation draggingEntered(id self, SEL sel, id sender) { + RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); + + return NSDragOperationCopy; +} +NSDragOperation draggingUpdated(id self, SEL sel, id sender) { + RGFW_UNUSED(sel); + + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL || (!(win->_flags & RGFW_windowAllowDND))) + return 0; + + NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation")); + RGFW_eventQueuePush((RGFW_event){.type = RGFW_DNDInit, + .point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y)), + ._win = win}); + + RGFW_dndInitCallback(win, win->event.point); + return NSDragOperationCopy; +} +bool prepareForDragOperation(id self) { + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) + return true; + + if (!(win->_flags & RGFW_windowAllowDND)) { + return false; + } + + return true; +} + +void RGFW__osxDraggingEnded(id self, SEL sel, id sender); +void RGFW__osxDraggingEnded(id self, SEL sel, id sender) { RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); return; } + +/* NOTE(EimaMei): Usually, you never need 'id self, SEL cmd' for C -> Obj-C methods. This isn't the case. */ +bool performDragOperation(id self, SEL sel, id sender) { + RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); + + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + + if (win == NULL) + return false; + + /* id pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard")); */ + + id pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard")); + + /* Get the types of data available on the pasteboard */ + id types = objc_msgSend_id(pasteBoard, sel_registerName("types")); + + /* Get the string type for file URLs */ + id fileURLsType = objc_msgSend_class_char(objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "NSFilenamesPboardType"); + + /* Check if the pasteboard contains file URLs */ + if (objc_msgSend_id_bool(types, sel_registerName("containsObject:"), fileURLsType) == 0) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errClipboard, RGFW_DEBUG_CTX(win, 0), "No files found on the pasteboard."); + return 0; + } + + id fileURLs = objc_msgSend_id_id(pasteBoard, sel_registerName("propertyListForType:"), fileURLsType); + int count = ((int (*)(id, SEL))objc_msgSend)(fileURLs, sel_registerName("count")); + + if (count == 0) + return 0; + + int i; + for (i = 0; i < count; i++) { + id fileURL = objc_msgSend_arr(fileURLs, sel_registerName("objectAtIndex:"), i); + const char *filePath = ((const char* (*)(id, SEL))objc_msgSend)(fileURL, sel_registerName("UTF8String")); + RGFW_MEMCPY(win->event.droppedFiles[i], filePath, RGFW_MAX_PATH); + win->event.droppedFiles[i][RGFW_MAX_PATH - 1] = '\0'; + } + NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation")); + RGFW_eventQueuePush((RGFW_event){.type = RGFW_DND, + .point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y)), + .droppedFilesCount = (size_t)count, + ._win = win}); + + RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount); + + return false; +} + +#ifndef RGFW_NO_IOKIT +#include +#include + +IOHIDDeviceRef RGFW_osxControllers[4] = {NULL}; + +size_t findControllerIndex(IOHIDDeviceRef device) { + size_t i; + for (i = 0; i < 4; i++) + if (RGFW_osxControllers[i] == device) + return i; + return (size_t)-1; +} + +void RGFW__osxInputValueChangedCallback(void *context, IOReturn result, void *sender, IOHIDValueRef value) { + RGFW_UNUSED(context); RGFW_UNUSED(result); RGFW_UNUSED(sender); + IOHIDElementRef element = IOHIDValueGetElement(value); + + IOHIDDeviceRef device = IOHIDElementGetDevice(element); + size_t index = findControllerIndex(device); + if (index == (size_t)-1) return; + + uint32_t usagePage = IOHIDElementGetUsagePage(element); + uint32_t usage = IOHIDElementGetUsage(element); + + CFIndex intValue = IOHIDValueGetIntegerValue(value); + + u8 RGFW_osx2RGFWSrc[2][RGFW_gamepadFinal] = {{ + 0, RGFW_gamepadSelect, RGFW_gamepadL3, RGFW_gamepadR3, RGFW_gamepadStart, + RGFW_gamepadUp, RGFW_gamepadRight, RGFW_gamepadDown, RGFW_gamepadLeft, + RGFW_gamepadL2, RGFW_gamepadR2, RGFW_gamepadL1, RGFW_gamepadR1, + RGFW_gamepadY, RGFW_gamepadB, RGFW_gamepadA, RGFW_gamepadX, RGFW_gamepadHome}, + {0, RGFW_gamepadA, RGFW_gamepadB, RGFW_gamepadR3, RGFW_gamepadX, + RGFW_gamepadY, RGFW_gamepadRight, RGFW_gamepadL1, RGFW_gamepadR1, + RGFW_gamepadL2, RGFW_gamepadR2, RGFW_gamepadDown, RGFW_gamepadStart, + RGFW_gamepadUp, RGFW_gamepadL3, RGFW_gamepadSelect, RGFW_gamepadStart, RGFW_gamepadHome} + }; + + u8* RGFW_osx2RGFW = RGFW_osx2RGFWSrc[0]; + if (RGFW_gamepads_type[index] == RGFW_gamepadMicrosoft) + RGFW_osx2RGFW = RGFW_osx2RGFWSrc[1]; + + switch (usagePage) { + case kHIDPage_Button: { + u8 button = 0; + if (usage < sizeof(RGFW_osx2RGFW)) + button = RGFW_osx2RGFW[usage]; + + RGFW_gamepadButtonCallback(_RGFW.root, (u16)index, button, (u8)intValue); + RGFW_gamepadPressed[index][button].prev = RGFW_gamepadPressed[index][button].current; + RGFW_gamepadPressed[index][button].current = RGFW_BOOL(intValue); + RGFW_eventQueuePush((RGFW_event){.type = intValue ? RGFW_gamepadButtonPressed: RGFW_gamepadButtonReleased, + .button = button, + .gamepad = (u16)index, + ._win = _RGFW.root}); + break; + } + case kHIDPage_GenericDesktop: { + CFIndex logicalMin = IOHIDElementGetLogicalMin(element); + CFIndex logicalMax = IOHIDElementGetLogicalMax(element); + + if (logicalMax <= logicalMin) return; + if (intValue < logicalMin) intValue = logicalMin; + if (intValue > logicalMax) intValue = logicalMax; + + i8 axisValue = (i8)(-100.0 + ((intValue - logicalMin) * 200.0) / (logicalMax - logicalMin)); + + u8 whichAxis = 0; + switch (usage) { + case kHIDUsage_GD_X: RGFW_gamepadAxes[index][0].x = axisValue; whichAxis = 0; break; + case kHIDUsage_GD_Y: RGFW_gamepadAxes[index][0].y = axisValue; whichAxis = 0; break; + case kHIDUsage_GD_Z: RGFW_gamepadAxes[index][1].x = axisValue; whichAxis = 1; break; + case kHIDUsage_GD_Rz: RGFW_gamepadAxes[index][1].y = axisValue; whichAxis = 1; break; + default: return; + } + + RGFW_eventQueuePush((RGFW_event){.type = RGFW_gamepadAxisMove, + .gamepad = (u16)index, + .axis = {RGFW_gamepadAxes[index][0], RGFW_gamepadAxes[index][1], + RGFW_gamepadAxes[index][2], RGFW_gamepadAxes[index][3]}, + .whichAxis = whichAxis, + ._win = _RGFW.root}); + + RGFW_gamepadAxisCallback(_RGFW.root, (u16)index, RGFW_gamepadAxes[index], 2, whichAxis); + } + } +} + +void RGFW__osxDeviceAddedCallback(void* context, IOReturn result, void *sender, IOHIDDeviceRef device) { + RGFW_UNUSED(context); RGFW_UNUSED(result); RGFW_UNUSED(sender); + CFTypeRef usageRef = (CFTypeRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDPrimaryUsageKey)); + int usage = 0; + if (usageRef) + CFNumberGetValue((CFNumberRef)usageRef, kCFNumberIntType, (void*)&usage); + + if (usage != kHIDUsage_GD_Joystick && usage != kHIDUsage_GD_GamePad && usage != kHIDUsage_GD_MultiAxisController) { + return; + } + + size_t i; + for (i = 0; i < 4; i++) { + if (RGFW_osxControllers[i] != NULL) + continue; + + RGFW_osxControllers[i] = device; + + IOHIDDeviceRegisterInputValueCallback(device, RGFW__osxInputValueChangedCallback, NULL); + + CFStringRef deviceName = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); + if (deviceName) + CFStringGetCString(deviceName, RGFW_gamepads_name[i], sizeof(RGFW_gamepads_name[i]), kCFStringEncodingUTF8); + + RGFW_gamepads_type[i] = RGFW_gamepadUnknown; + if (RGFW_STRSTR(RGFW_gamepads_name[i], "Microsoft") || RGFW_STRSTR(RGFW_gamepads_name[i], "X-Box") || RGFW_STRSTR(RGFW_gamepads_name[i], "Xbox")) + RGFW_gamepads_type[i] = RGFW_gamepadMicrosoft; + else if (RGFW_STRSTR(RGFW_gamepads_name[i], "PlayStation") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS3") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS4") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS5")) + RGFW_gamepads_type[i] = RGFW_gamepadSony; + else if (RGFW_STRSTR(RGFW_gamepads_name[i], "Nintendo")) + RGFW_gamepads_type[i] = RGFW_gamepadNintendo; + else if (RGFW_STRSTR(RGFW_gamepads_name[i], "Logitech")) + RGFW_gamepads_type[i] = RGFW_gamepadLogitech; + + RGFW_gamepads[i] = (u16)i; + RGFW_gamepadCount++; + + RGFW_eventQueuePush((RGFW_event){.type = RGFW_gamepadConnected, + .gamepad = (u16)i, + ._win = _RGFW.root}); + + RGFW_gamepadCallback(_RGFW.root, (u16)i, 1); + break; + } +} + +void RGFW__osxDeviceRemovedCallback(void *context, IOReturn result, void *sender, IOHIDDeviceRef device) { + RGFW_UNUSED(context); RGFW_UNUSED(result); RGFW_UNUSED(sender); RGFW_UNUSED(device); + CFNumberRef usageRef = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDPrimaryUsageKey)); + int usage = 0; + if (usageRef) + CFNumberGetValue(usageRef, kCFNumberIntType, &usage); + + if (usage != kHIDUsage_GD_Joystick && usage != kHIDUsage_GD_GamePad && usage != kHIDUsage_GD_MultiAxisController) { + return; + } + + size_t index = findControllerIndex(device); + if (index != (size_t)-1) + RGFW_osxControllers[index] = NULL; + + RGFW_eventQueuePush((RGFW_event){.type = RGFW_gamepadDisconnected, + .gamepad = (u16)index, + ._win = _RGFW.root}); + RGFW_gamepadCallback(_RGFW.root, (u16)index, 0); + + RGFW_gamepadCount--; +} + +RGFWDEF void RGFW_osxInitIOKit(void); +void RGFW_osxInitIOKit(void) { + IOHIDManagerRef hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + if (!hidManager) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errIOKit, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to create IOHIDManager."); + return; + } + + CFMutableDictionaryRef matchingDictionary = CFDictionaryCreateMutable( + kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks + ); + if (!matchingDictionary) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errIOKit, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to create matching dictionary for IOKit."); + CFRelease(hidManager); + return; + } + + CFDictionarySetValue( + matchingDictionary, + CFSTR(kIOHIDDeviceUsagePageKey), + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, (int[]){kHIDPage_GenericDesktop}) + ); + + IOHIDManagerSetDeviceMatching(hidManager, matchingDictionary); + + IOHIDManagerRegisterDeviceMatchingCallback(hidManager, RGFW__osxDeviceAddedCallback, NULL); + IOHIDManagerRegisterDeviceRemovalCallback(hidManager, RGFW__osxDeviceRemovedCallback, NULL); + + IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + + IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone); + + /* Execute the run loop once in order to register any initially-attached joysticks */ + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false); +} +#endif + +void RGFW_moveToMacOSResourceDir(void) { + char resourcesPath[256]; + + CFBundleRef bundle = CFBundleGetMainBundle(); + if (!bundle) + return; + + CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle); + CFStringRef last = CFURLCopyLastPathComponent(resourcesURL); + + if ( + CFStringCompare(CFSTR("Resources"), last, 0) != kCFCompareEqualTo || + CFURLGetFileSystemRepresentation(resourcesURL, true, (u8*) resourcesPath, 255) == 0 + ) { + CFRelease(last); + CFRelease(resourcesURL); + return; + } + + CFRelease(last); + CFRelease(resourcesURL); + + chdir(resourcesPath); +} + + +void RGFW__osxWindowDeminiaturize(id self, SEL sel) { + RGFW_UNUSED(sel); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + win->_flags |= RGFW_windowMinimize; + RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowRestored, ._win = win}); + RGFW_windowRestoredCallback(win, win->r); + +} +void RGFW__osxWindowMiniaturize(id self, SEL sel) { + RGFW_UNUSED(sel); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + win->_flags &= ~(u32)RGFW_windowMinimize; + RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowMinimized, ._win = win}); + RGFW_windowMinimizedCallback(win, win->r); + +} + +void RGFW__osxWindowBecameKey(id self, SEL sel) { + RGFW_UNUSED(sel); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + win->_flags |= RGFW_windowFocus; + RGFW_eventQueuePush((RGFW_event){.type = RGFW_focusIn, ._win = win}); + + RGFW_focusCallback(win, RGFW_TRUE); +} + +void RGFW__osxWindowResignKey(id self, SEL sel) { + RGFW_UNUSED(sel); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + win->_flags &= ~(u32)RGFW_windowFocus; + RGFW_eventQueuePush((RGFW_event){.type = RGFW_focusOut, ._win = win}); + RGFW_focusCallback(win, RGFW_FALSE); +} + +NSSize RGFW__osxWindowResize(id self, SEL sel, NSSize frameSize) { + RGFW_UNUSED(sel); + + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return frameSize; + + win->r.w = (i32)frameSize.width; + win->r.h = (i32)frameSize.height; + + RGFW_monitor mon = RGFW_window_getMonitor(win); + if ((i32)mon.mode.area.w == win->r.w && (i32)mon.mode.area.h - 102 <= win->r.h) { + win->_flags |= RGFW_windowMaximize; + RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowMaximized, ._win = win}); + RGFW_windowMaximizedCallback(win, win->r); + } else if (win->_flags & RGFW_windowMaximize) { + win->_flags &= ~(u32)RGFW_windowMaximize; + RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowRestored, ._win = win}); + RGFW_windowRestoredCallback(win, win->r); + + } + + + RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowResized, ._win = win}); + RGFW_windowResizedCallback(win, win->r); + return frameSize; +} + +void RGFW__osxWindowMove(id self, SEL sel) { + RGFW_UNUSED(sel); + + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame")); + win->r.x = (i32) frame.origin.x; + win->r.y = (i32) frame.origin.y; + + RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowMoved, ._win = win}); + RGFW_windowMovedCallback(win, win->r); +} + +void RGFW__osxViewDidChangeBackingProperties(id self, SEL _cmd) { + RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + RGFW_monitor mon = RGFW_window_getMonitor(win); + RGFW_scaleUpdatedCallback(win, mon.scaleX, mon.scaleY); + RGFW_eventQueuePush((RGFW_event){.type = RGFW_scaleUpdated, .scaleX = mon.scaleX, .scaleY = mon.scaleY , ._win = win}); +} + +void RGFW__osxDrawRect(id self, SEL _cmd, CGRect rect) { + RGFW_UNUSED(rect); RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowRefresh, ._win = win}); + RGFW_windowRefreshCallback(win); +} + +void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area) { + #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + win->buffer = buffer; + win->bufferSize = area; + win->_flags |= RGFW_BUFFER_ALLOC; + #ifdef RGFW_OSMESA + win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL); + OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, area.w, area.h); + OSMesaPixelStore(OSMESA_Y_UP, 0); + #endif + #else + RGFW_UNUSED(win); RGFW_UNUSED(buffer); RGFW_UNUSED(area); /*!< if buffer rendering is not being used */ + #endif +} + +void RGFW_window_cocoaSetLayer(RGFW_window* win, void* layer) { + objc_msgSend_void_id((id)win->src.view, sel_registerName("setLayer"), (id)layer); +} + +void* RGFW_cocoaGetLayer(void) { + return objc_msgSend_class((id)objc_getClass("CAMetalLayer"), (SEL)sel_registerName("layer")); +} + +NSPasteboardType const NSPasteboardTypeURL = "public.url"; +NSPasteboardType const NSPasteboardTypeFileURL = "public.file-url"; + +id RGFW__osx_generateViewClass(const char* subclass, RGFW_window* win) { + Class customViewClass; + customViewClass = objc_allocateClassPair(objc_getClass(subclass), "RGFWCustomView", 0); + + class_addIvar( customViewClass, "RGFW_window", sizeof(RGFW_window*), (u8)rint(log2(sizeof(RGFW_window*))), "L"); + class_addMethod(customViewClass, sel_registerName("drawRect:"), (IMP)RGFW__osxDrawRect, "v@:{CGRect=ffff}"); + class_addMethod(customViewClass, sel_registerName("viewDidChangeBackingProperties"), (IMP)RGFW__osxViewDidChangeBackingProperties, ""); + + id customView = objc_msgSend_id(NSAlloc(customViewClass), sel_registerName("init")); + object_setInstanceVariable(customView, "RGFW_window", win); + + return customView; +} + +#ifndef RGFW_EGL +void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software) { +#ifdef RGFW_OPENGL + void* attrs = RGFW_initFormatAttribs(software); + void* format = NSOpenGLPixelFormat_initWithAttributes((uint32_t*)attrs); + + if (format == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to load pixel format for OpenGL"); + void* subAttrs = RGFW_initFormatAttribs(1); + format = NSOpenGLPixelFormat_initWithAttributes((uint32_t*)subAttrs); + + if (format == NULL) + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "and loading software rendering OpenGL failed"); + else + RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, RGFW_DEBUG_CTX(win, 0), "Switching to software rendering"); + } + + /* 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 = (id) ((id(*)(id, SEL, NSRect, uint32_t*))objc_msgSend) (RGFW__osx_generateViewClass("NSOpenGLView", win), + sel_registerName("initWithFrame:pixelFormat:"), (NSRect){{0, 0}, {win->r.w, win->r.h}}, (uint32_t*)format); + + objc_msgSend_void(win->src.view, sel_registerName("prepareOpenGL")); + win->src.ctx = objc_msgSend_id(win->src.view, sel_registerName("openGLContext")); + + if (win->_flags & RGFW_windowTransparent) { + i32 opacity = 0; + #define NSOpenGLCPSurfaceOpacity 236 + NSOpenGLContext_setValues((id)win->src.ctx, &opacity, NSOpenGLCPSurfaceOpacity); + } + + objc_msgSend_void(win->src.ctx, sel_registerName("makeCurrentContext")); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context initalized"); +#else + RGFW_UNUSED(win); RGFW_UNUSED(software); +#endif +} + +void RGFW_window_freeOpenGL(RGFW_window* win) { +#ifdef RGFW_OPENGL + if (win->src.ctx == NULL) return; + objc_msgSend_void(win->src.ctx, sel_registerName("release")); + win->src.ctx = NULL; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context freed"); +#else + RGFW_UNUSED(win); +#endif +} +#endif + + +i32 RGFW_init(void) { + /* NOTE(EimaMei): Why does Apple hate good code? Like wtf, who thought of methods being a great idea??? + Imagine a universe, where MacOS had a proper system API (we would probably have like 20% better performance). + */ + si_func_to_SEL_with_name("NSObject", "windowShouldClose", (void*)RGFW_OnClose); + + /* NOTE(EimaMei): Fixes the 'Boop' sfx from constantly playing each time you click a key. Only a problem when running in the terminal. */ + si_func_to_SEL("NSWindow", acceptsFirstResponder); + si_func_to_SEL("NSWindow", performKeyEquivalent); + + if (NSApp == NULL) { + NSApp = objc_msgSend_id((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication")); + + ((void (*)(id, SEL, NSUInteger))objc_msgSend) + (NSApp, sel_registerName("setActivationPolicy:"), NSApplicationActivationPolicyRegular); + + #ifndef RGFW_NO_IOKIT + RGFW_osxInitIOKit(); + #endif + } + + _RGFW.windowCount = 0; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, (RGFW_debugContext){0}, "global context initialized"); + return 0; +} + +RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) { + static u8 RGFW_loaded = 0; + RGFW_window_basic_init(win, rect, flags); + + /* RR Create an autorelease pool */ + id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); + pool = objc_msgSend_id(pool, sel_registerName("init")); + + RGFW_window_setMouseDefault(win); + + NSRect windowRect; + windowRect.origin.x = win->r.x; + windowRect.origin.y = win->r.y; + windowRect.size.width = win->r.w; + windowRect.size.height = win->r.h; + + NSBackingStoreType macArgs = NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSBackingStoreBuffered | NSWindowStyleMaskTitled; + + if (!(flags & RGFW_windowNoResize)) + macArgs |= NSWindowStyleMaskResizable; + if (!(flags & RGFW_windowNoBorder)) + macArgs |= NSWindowStyleMaskTitled; + { + void* nsclass = objc_getClass("NSWindow"); + SEL func = sel_registerName("initWithContentRect:styleMask:backing:defer:"); + + win->src.window = ((id(*)(id, SEL, NSRect, NSWindowStyleMask, NSBackingStoreType, bool))objc_msgSend) + (NSAlloc(nsclass), func, windowRect, macArgs, macArgs, false); + } + + id str = NSString_stringWithUTF8String(name); + objc_msgSend_void_id((id)win->src.window, sel_registerName("setTitle:"), str); + + if ((flags & RGFW_windowNoInitAPI) == 0) { + RGFW_window_initOpenGL(win, RGFW_BOOL(flags & RGFW_windowOpenglSoftware)); + RGFW_window_initBuffer(win); + } + + #ifdef RGFW_OPENGL + else + #endif + { + NSRect contentRect = (NSRect){{0, 0}, {win->r.w, win->r.h}}; + win->src.view = ((id(*)(id, SEL, NSRect))objc_msgSend) (NSAlloc(objc_getClass("NSView")), sel_registerName("initWithFrame:"), contentRect); + } + + void* contentView = NSWindow_contentView((id)win->src.window); + objc_msgSend_void_bool(contentView, sel_registerName("setWantsLayer:"), true); + objc_msgSend_int((id)win->src.view, sel_registerName("setLayerContentsPlacement:"), 4); + objc_msgSend_void_id((id)win->src.window, sel_registerName("setContentView:"), win->src.view); + + if (flags & RGFW_windowTransparent) { + objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), false); + + objc_msgSend_void_id((id)win->src.window, sel_registerName("setBackgroundColor:"), + NSColor_colorWithSRGB(0, 0, 0, 0)); + } + + Class delegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "WindowDelegate", 0); + + class_addIvar( + delegateClass, "RGFW_window", + sizeof(RGFW_window*), (u8)rint(log2(sizeof(RGFW_window*))), + "L" + ); + + class_addMethod(delegateClass, sel_registerName("windowWillResize:toSize:"), (IMP) RGFW__osxWindowResize, "{NSSize=ff}@:{NSSize=ff}"); + class_addMethod(delegateClass, sel_registerName("windowWillMove:"), (IMP) RGFW__osxWindowMove, ""); + class_addMethod(delegateClass, sel_registerName("windowDidMove:"), (IMP) RGFW__osxWindowMove, ""); + class_addMethod(delegateClass, sel_registerName("windowDidMiniaturize:"), (IMP) RGFW__osxWindowMiniaturize, ""); + class_addMethod(delegateClass, sel_registerName("windowDidDeminiaturize:"), (IMP) RGFW__osxWindowDeminiaturize, ""); + class_addMethod(delegateClass, sel_registerName("windowDidBecomeKey:"), (IMP) RGFW__osxWindowBecameKey, ""); + class_addMethod(delegateClass, sel_registerName("windowDidResignKey:"), (IMP) RGFW__osxWindowResignKey, ""); + class_addMethod(delegateClass, sel_registerName("draggingEntered:"), (IMP)draggingEntered, "l@:@"); + class_addMethod(delegateClass, sel_registerName("draggingUpdated:"), (IMP)draggingUpdated, "l@:@"); + class_addMethod(delegateClass, sel_registerName("draggingExited:"), (IMP)RGFW__osxDraggingEnded, "v@:@"); + class_addMethod(delegateClass, sel_registerName("draggingEnded:"), (IMP)RGFW__osxDraggingEnded, "v@:@"); + class_addMethod(delegateClass, sel_registerName("prepareForDragOperation:"), (IMP)prepareForDragOperation, "B@:@"); + class_addMethod(delegateClass, sel_registerName("performDragOperation:"), (IMP)performDragOperation, "B@:@"); + + id delegate = objc_msgSend_id(NSAlloc(delegateClass), sel_registerName("init")); + + if (RGFW_COCOA_FRAME_NAME) + objc_msgSend_ptr(win->src.view, sel_registerName("setFrameAutosaveName:"), RGFW_COCOA_FRAME_NAME); + + object_setInstanceVariable(delegate, "RGFW_window", win); + + objc_msgSend_void_id((id)win->src.window, sel_registerName("setDelegate:"), delegate); + + if (flags & RGFW_windowAllowDND) { + win->_flags |= RGFW_windowAllowDND; + + NSPasteboardType types[] = {NSPasteboardTypeURL, NSPasteboardTypeFileURL, NSPasteboardTypeString}; + NSregisterForDraggedTypes((id)win->src.window, types, 3); + } + + RGFW_window_setFlags(win, flags); + + /* Show the window */ + objc_msgSend_void_bool(NSApp, sel_registerName("activateIgnoringOtherApps:"), true); + ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL); + RGFW_window_show(win); + + if (!RGFW_loaded) { + objc_msgSend_void(win->src.window, sel_registerName("makeMainWindow")); + + RGFW_loaded = 1; + } + + objc_msgSend_void(win->src.window, sel_registerName("makeKeyWindow")); + + objc_msgSend_void(NSApp, sel_registerName("finishLaunching")); + NSRetain(win->src.window); + NSRetain(NSApp); + + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new window was created"); + return win; +} + +void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { + NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame")); + NSRect content = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame")); + float offset = 0; + + RGFW_setBit(&win->_flags, RGFW_windowNoBorder, !border); + NSBackingStoreType storeType = NSWindowStyleMaskBorderless | NSWindowStyleMaskFullSizeContentView; + if (border) + storeType = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable; + if (!(win->_flags & RGFW_windowNoResize)) { + storeType |= NSWindowStyleMaskResizable; + } + + ((void (*)(id, SEL, NSBackingStoreType))objc_msgSend)((id)win->src.window, sel_registerName("setStyleMask:"), storeType); + + if (!border) { + id miniaturizeButton = objc_msgSend_int((id)win->src.window, sel_registerName("standardWindowButton:"), NSWindowMiniaturizeButton); + id titleBarView = objc_msgSend_id(miniaturizeButton, sel_registerName("superview")); + objc_msgSend_void_bool(titleBarView, sel_registerName("setHidden:"), true); + + offset = (float)(frame.size.height - content.size.height); + } + + RGFW_window_resize(win, RGFW_AREA(win->r.w, win->r.h + offset)); + win->r.h -= (i32)offset; +} + +RGFW_area RGFW_getScreenSize(void) { + static CGDirectDisplayID display = 0; + + if (display == 0) + display = CGMainDisplayID(); + + return RGFW_AREA(CGDisplayPixelsWide(display), CGDisplayPixelsHigh(display)); +} + +RGFW_point RGFW_getGlobalMousePoint(void) { + RGFW_ASSERT(_RGFW.root != NULL); + + CGEventRef e = CGEventCreate(NULL); + CGPoint point = CGEventGetLocation(e); + CFRelease(e); + + return RGFW_POINT((u32) point.x, (u32) point.y); /*!< the point is loaded during event checks */ +} + +typedef RGFW_ENUM(u32, NSEventType) { /* various types of events */ + NSEventTypeLeftMouseDown = 1, + NSEventTypeLeftMouseUp = 2, + NSEventTypeRightMouseDown = 3, + NSEventTypeRightMouseUp = 4, + NSEventTypeMouseMoved = 5, + NSEventTypeLeftMouseDragged = 6, + NSEventTypeRightMouseDragged = 7, + NSEventTypeMouseEntered = 8, + NSEventTypeMouseExited = 9, + NSEventTypeKeyDown = 10, + NSEventTypeKeyUp = 11, + NSEventTypeFlagsChanged = 12, + NSEventTypeAppKitDefined = 13, + NSEventTypeSystemDefined = 14, + NSEventTypeApplicationDefined = 15, + NSEventTypePeriodic = 16, + NSEventTypeCursorUpdate = 17, + NSEventTypeScrollWheel = 22, + NSEventTypeTabletPoint = 23, + NSEventTypeTabletProximity = 24, + NSEventTypeOtherMouseDown = 25, + NSEventTypeOtherMouseUp = 26, + NSEventTypeOtherMouseDragged = 27, + /* The following event types are available on some hardware on 10.5.2 and later */ + NSEventTypeGesture API_AVAILABLE(macos(10.5)) = 29, + NSEventTypeMagnify API_AVAILABLE(macos(10.5)) = 30, + NSEventTypeSwipe API_AVAILABLE(macos(10.5)) = 31, + NSEventTypeRotate API_AVAILABLE(macos(10.5)) = 18, + NSEventTypeBeginGesture API_AVAILABLE(macos(10.5)) = 19, + NSEventTypeEndGesture API_AVAILABLE(macos(10.5)) = 20, + + NSEventTypeSmartMagnify API_AVAILABLE(macos(10.8)) = 32, + NSEventTypeQuickLook API_AVAILABLE(macos(10.8)) = 33, + + NSEventTypePressure API_AVAILABLE(macos(10.10.3)) = 34, + NSEventTypeDirectTouch API_AVAILABLE(macos(10.10)) = 37, + + NSEventTypeChangeMode API_AVAILABLE(macos(10.15)) = 38, +}; + +typedef unsigned long long NSEventMask; + +typedef enum NSEventModifierFlags { + NSEventModifierFlagCapsLock = 1 << 16, + NSEventModifierFlagShift = 1 << 17, + NSEventModifierFlagControl = 1 << 18, + NSEventModifierFlagOption = 1 << 19, + NSEventModifierFlagCommand = 1 << 20, + NSEventModifierFlagNumericPad = 1 << 21 +} NSEventModifierFlags; + +void RGFW_stopCheckEvents(void) { + id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); + eventPool = objc_msgSend_id(eventPool, sel_registerName("init")); + + id e = (id) ((id(*)(Class, SEL, NSEventType, NSPoint, NSEventModifierFlags, void*, NSInteger, void**, short, NSInteger, NSInteger))objc_msgSend) + (objc_getClass("NSEvent"), sel_registerName("otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:"), + NSEventTypeApplicationDefined, (NSPoint){0, 0}, (NSEventModifierFlags)0, NULL, (NSInteger)0, NULL, 0, 0, 0); + + ((void (*)(id, SEL, id, bool))objc_msgSend) + (NSApp, sel_registerName("postEvent:atStart:"), e, 1); + + objc_msgSend_bool_void(eventPool, sel_registerName("drain")); +} + +void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) { + RGFW_UNUSED(win); + + id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); + eventPool = objc_msgSend_id(eventPool, sel_registerName("init")); + + void* date = (void*) ((id(*)(Class, SEL, double))objc_msgSend) + (objc_getClass("NSDate"), sel_registerName("dateWithTimeIntervalSinceNow:"), waitMS); + + SEL eventFunc = sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"); + id e = (id) ((id(*)(id, SEL, NSEventMask, void*, id, bool))objc_msgSend) + (NSApp, eventFunc, + ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true); + + + if (e) { + ((void (*)(id, SEL, id, bool))objc_msgSend) + (NSApp, sel_registerName("postEvent:atStart:"), e, 1); + } + + objc_msgSend_bool_void(eventPool, sel_registerName("drain")); +} + +RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { + if (win == NULL || ((win->_flags & RGFW_windowFreeOnClose) && (win->_flags & RGFW_EVENT_QUIT))) return NULL; + + objc_msgSend_void((id)win->src.mouse, sel_registerName("set")); + RGFW_event* ev = RGFW_window_checkEventCore(win); + if (ev) { + ((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows")); + return ev; + } + + id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); + eventPool = objc_msgSend_id(eventPool, sel_registerName("init")); + + SEL eventFunc = sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"); + + void* date = NULL; + + id e = (id) ((id(*)(id, SEL, NSEventMask, void*, id, bool))objc_msgSend) + (NSApp, eventFunc, ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true); + + if (e == NULL) { + objc_msgSend_bool_void(eventPool, sel_registerName("drain")); + objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e); + ((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows")); + return NULL; + } + + if (objc_msgSend_id(e, sel_registerName("window")) != win->src.window) { + ((void (*)(id, SEL, id, bool))objc_msgSend) + (NSApp, sel_registerName("postEvent:atStart:"), e, 0); + + objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e); + objc_msgSend_bool_void(eventPool, sel_registerName("drain")); + ((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows")); + return NULL; + } + + if (win->event.droppedFilesCount) { + u32 i; + for (i = 0; i < win->event.droppedFilesCount; i++) + win->event.droppedFiles[i][0] = '\0'; + } + + win->event.droppedFilesCount = 0; + win->event.type = 0; + + u32 type = (u32)objc_msgSend_uint(e, sel_registerName("type")); + switch (type) { + case NSEventTypeMouseEntered: { + win->event.type = RGFW_mouseEnter; + NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow")); + + win->event.point = RGFW_POINT((i32) p.x, (i32) (win->r.h - p.y)); + RGFW_mouseNotifyCallback(win, win->event.point, 1); + break; + } + + case NSEventTypeMouseExited: + win->event.type = RGFW_mouseLeave; + RGFW_mouseNotifyCallback(win, win->event.point, 0); + break; + + case NSEventTypeKeyDown: { + u32 key = (u16) objc_msgSend_uint(e, sel_registerName("keyCode")); + + u32 mappedKey = (u32)*(((char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("charactersIgnoringModifiers"))))); + if (((u8)mappedKey) == 239) + mappedKey = 0; + + win->event.keyChar = (u8)mappedKey; + + win->event.key = (u8)RGFW_apiKeyToRGFW(key); + RGFW_keyboard[win->event.key].prev = RGFW_keyboard[win->event.key].current; + + win->event.type = RGFW_keyPressed; + win->event.repeat = RGFW_isPressed(win, win->event.key); + RGFW_keyboard[win->event.key].current = 1; + + RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, 1); + break; + } + + case NSEventTypeKeyUp: { + u32 key = (u16) objc_msgSend_uint(e, sel_registerName("keyCode")); + + + u32 mappedKey = (u32)*(((char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("charactersIgnoringModifiers"))))); + if (((u8)mappedKey) == 239) + mappedKey = 0; + + win->event.keyChar = (u8)mappedKey; + + win->event.key = (u8)RGFW_apiKeyToRGFW(key); + + RGFW_keyboard[win->event.key].prev = RGFW_keyboard[win->event.key].current; + + win->event.type = RGFW_keyReleased; + + RGFW_keyboard[win->event.key].current = 0; + RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, 0); + break; + } + + case NSEventTypeFlagsChanged: { + u32 flags = (u32)objc_msgSend_uint(e, sel_registerName("modifierFlags")); + RGFW_updateKeyModsPro(win, ((u32)(flags & NSEventModifierFlagCapsLock) % 255), ((flags & NSEventModifierFlagNumericPad) % 255), + ((flags & NSEventModifierFlagControl) % 255), ((flags & NSEventModifierFlagOption) % 255), + ((flags & NSEventModifierFlagShift) % 255), ((flags & NSEventModifierFlagCommand) % 255), 0); + u8 i; + for (i = 0; i < 9; i++) + RGFW_keyboard[i + RGFW_capsLock].prev = 0; + + for (i = 0; i < 5; i++) { + u32 shift = (1 << (i + 16)); + u32 key = i + RGFW_capsLock; + + if ((flags & shift) && !RGFW_wasPressed(win, (u8)key)) { + RGFW_keyboard[key].current = 1; + + if (key != RGFW_capsLock) + RGFW_keyboard[key+ 4].current = 1; + + win->event.type = RGFW_keyPressed; + win->event.key = (u8)key; + break; + } + + if (!(flags & shift) && RGFW_wasPressed(win, (u8)key)) { + RGFW_keyboard[key].current = 0; + + if (key != RGFW_capsLock) + RGFW_keyboard[key + 4].current = 0; + + win->event.type = RGFW_keyReleased; + win->event.key = (u8)key; + break; + } + } + + RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, win->event.type == RGFW_keyPressed); + + break; + } + case NSEventTypeLeftMouseDragged: + case NSEventTypeOtherMouseDragged: + case NSEventTypeRightMouseDragged: + case NSEventTypeMouseMoved: { + win->event.type = RGFW_mousePosChanged; + 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)); + + 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.vector = RGFW_POINT((i32)p.x, (i32)p.y); + + win->_lastMousePoint = win->event.point; + RGFW_mousePosCallback(win, win->event.point, win->event.vector); + break; + } + case NSEventTypeLeftMouseDown: case NSEventTypeRightMouseDown: case NSEventTypeOtherMouseDown: { + u32 buttonNumber = (u32)objc_msgSend_uint(e, sel_registerName("buttonNumber")); + switch (buttonNumber) { + case 0: win->event.button = RGFW_mouseLeft; break; + case 1: win->event.button = RGFW_mouseRight; break; + case 2: win->event.button = RGFW_mouseMiddle; break; + default: win->event.button = (u8)buttonNumber; + } + + win->event.type = RGFW_mouseButtonPressed; + RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; + RGFW_mouseButtons[win->event.button].current = 1; + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); + break; + } + case NSEventTypeLeftMouseUp: case NSEventTypeRightMouseUp: case NSEventTypeOtherMouseUp: { + u32 buttonNumber = (u32)objc_msgSend_uint(e, sel_registerName("buttonNumber")); + switch (buttonNumber) { + case 0: win->event.button = RGFW_mouseLeft; break; + case 1: win->event.button = RGFW_mouseRight; break; + case 2: win->event.button = RGFW_mouseMiddle; break; + default: win->event.button = (u8)buttonNumber; + } + RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; + RGFW_mouseButtons[win->event.button].current = 0; + win->event.type = RGFW_mouseButtonReleased; + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0); + break; + } + case NSEventTypeScrollWheel: { + double deltaY = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaY")); + + if (deltaY > 0) { + win->event.button = RGFW_mouseScrollUp; + } + else if (deltaY < 0) { + win->event.button = RGFW_mouseScrollDown; + } + + RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; + RGFW_mouseButtons[win->event.button].current = 1; + + win->event.scroll = deltaY; + + win->event.type = RGFW_mouseButtonPressed; + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); + break; + } + + default: + objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e); + ((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows")); + return RGFW_window_checkEvent(win); + } + + objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e); + ((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows")); + objc_msgSend_bool_void(eventPool, sel_registerName("drain")); + return &win->event; +} + + +void RGFW_window_move(RGFW_window* win, RGFW_point v) { + RGFW_ASSERT(win != NULL); + + win->r.x = v.x; + win->r.y = v.y; + ((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend) + ((id)win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{win->r.x, win->r.y}, {win->r.w, win->r.h}}, true, true); +} + +void RGFW_window_resize(RGFW_window* win, RGFW_area a) { + RGFW_ASSERT(win != NULL); + + NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame")); + NSRect content = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame")); + float offset = (float)(frame.size.height - content.size.height); + + win->r.w = (i32)a.w; + win->r.h = (i32)a.h; + + ((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend) + ((id)win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{win->r.x, win->r.y}, {win->r.w, win->r.h + offset}}, true, true); +} + +void RGFW_window_focus(RGFW_window* win) { + RGFW_ASSERT(win); + objc_msgSend_void_bool(NSApp, sel_registerName("activateIgnoringOtherApps:"), true); + ((void (*)(id, SEL))objc_msgSend)((id)win->src.window, sel_registerName("makeKeyWindow")); +} + +void RGFW_window_raise(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("orderFront:"), (SEL)NULL); + objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGNormalWindowLevelKey); +} + +void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) { + RGFW_ASSERT(win != NULL); + if (fullscreen && (win->_flags & RGFW_windowFullscreen)) return; + if (!fullscreen && !(win->_flags & RGFW_windowFullscreen)) return; + + if (fullscreen) { + win->_oldRect = win->r; + RGFW_monitor mon = RGFW_window_getMonitor(win); + win->r = RGFW_RECT(0, 0, mon.x, mon.y); + win->_flags |= RGFW_windowFullscreen; + RGFW_window_resize(win, RGFW_AREA(mon.mode.area.w, mon.mode.area.h)); + RGFW_window_move(win, RGFW_POINT(0, 0)); + } + objc_msgSend_void_SEL(win->src.window, sel_registerName("toggleFullScreen:"), NULL); + + if (!fullscreen) { + win->r = win->_oldRect; + win->_flags &= ~(u32)RGFW_windowFullscreen; + + RGFW_window_resize(win, RGFW_AREA(win->r.w, win->r.h)); + RGFW_window_move(win, RGFW_POINT(win->r.x, win->r.y)); + } +} + +void RGFW_window_maximize(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + if (RGFW_window_isMaximized(win)) return; + + win->_flags |= RGFW_windowMaximize; + objc_msgSend_void_SEL(win->src.window, sel_registerName("zoom:"), NULL); +} + +void RGFW_window_minimize(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + objc_msgSend_void_SEL(win->src.window, sel_registerName("performMiniaturize:"), NULL); +} + +void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) { + RGFW_ASSERT(win != NULL); + if (floating) objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGFloatingWindowLevelKey); + else objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGNormalWindowLevelKey); +} + +void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) { + objc_msgSend_int(win->src.window, sel_registerName("setAlphaValue:"), opacity); + objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), (opacity < (u8)255)); + + if (opacity) + objc_msgSend_void_id((id)win->src.window, sel_registerName("setBackgroundColor:"), NSColor_colorWithSRGB(0, 0, 0, opacity)); + +} + +void RGFW_window_restore(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + if (RGFW_window_isMaximized(win)) + objc_msgSend_void_SEL(win->src.window, sel_registerName("zoom:"), NULL); + + objc_msgSend_void_SEL(win->src.window, sel_registerName("deminiaturize:"), NULL); + RGFW_window_show(win); +} + +RGFW_bool RGFW_window_isFloating(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + int level = ((int (*)(id, SEL))objc_msgSend) ((id)(win->src.window), (SEL)sel_registerName("level")); + return level > kCGNormalWindowLevelKey; +} + +void RGFW_window_setName(RGFW_window* win, const char* name) { + RGFW_ASSERT(win != NULL); + + id str = NSString_stringWithUTF8String(name); + objc_msgSend_void_id((id)win->src.window, sel_registerName("setTitle:"), str); +} + +#ifndef RGFW_NO_PASSTHROUGH +void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) { + objc_msgSend_void_bool(win->src.window, sel_registerName("setIgnoresMouseEvents:"), passthrough); +} +#endif + +void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a) { + if (a.w == 0 && a.h == 0) a = RGFW_AREA(1, 1); + + ((void (*)(id, SEL, NSSize))objc_msgSend) + ((id)win->src.window, sel_registerName("setContentAspectRatio:"), (NSSize){a.w, a.h}); +} + +void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { + ((void (*)(id, SEL, NSSize))objc_msgSend) + ((id)win->src.window, sel_registerName("setMinSize:"), (NSSize){a.w, a.h}); +} + +void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { + if (a.w == 0 && a.h == 0) { + a = RGFW_getScreenSize(); + } + + ((void (*)(id, SEL, NSSize))objc_msgSend) + ((id)win->src.window, sel_registerName("setMaxSize:"), (NSSize){a.w, a.h}); +} + +RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, RGFW_area area, i32 channels, u8 type) { + RGFW_ASSERT(win != NULL); + RGFW_UNUSED(type); + + if (data == NULL) { + objc_msgSend_void_id(NSApp, sel_registerName("setApplicationIconImage:"), NULL); + return RGFW_TRUE; + } + + /* code by EimaMei: Make a bitmap representation, then copy the loaded image into it. */ + id representation = NSBitmapImageRep_initWithBitmapData(NULL, area.w, area.h, 8, channels, (channels == 4), false, "NSCalibratedRGBColorSpace", 1 << 1, area.w * (u32)channels, 8 * (u32)channels); + RGFW_MEMCPY(NSBitmapImageRep_bitmapData(representation), data, area.w * area.h * (u32)channels); + + /* Add ze representation. */ + id dock_image = ((id(*)(id, SEL, NSSize))objc_msgSend) (NSAlloc((id)objc_getClass("NSImage")), sel_registerName("initWithSize:"), ((NSSize){area.w, area.h})); + + objc_msgSend_void_id(dock_image, sel_registerName("addRepresentation:"), representation); + + /* Finally, set the dock image to it. */ + objc_msgSend_void_id(NSApp, sel_registerName("setApplicationIconImage:"), dock_image); + /* Free the garbage. */ + NSRelease(dock_image); + NSRelease(representation); + + return RGFW_TRUE; +} + +id NSCursor_arrowStr(const char* str) { + void* nclass = objc_getClass("NSCursor"); + SEL func = sel_registerName(str); + return (id) objc_msgSend_id(nclass, func); +} + +RGFW_mouse* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels) { + if (icon == NULL) { + objc_msgSend_void(NSCursor_arrowStr("arrowCursor"), sel_registerName("set")); + return NULL; + } + + /* NOTE(EimaMei): Code by yours truly. */ + /* Make a bitmap representation, then copy the loaded image into it. */ + id representation = (id)NSBitmapImageRep_initWithBitmapData(NULL, a.w, a.h, 8, channels, (channels == 4), false, "NSCalibratedRGBColorSpace", 1 << 1, a.w * (u32)channels, 8 * (u32)channels); + RGFW_MEMCPY(NSBitmapImageRep_bitmapData(representation), icon, a.w * a.h * (u32)channels); + + /* Add ze representation. */ + id cursor_image = ((id(*)(id, SEL, NSSize))objc_msgSend) (NSAlloc((id)objc_getClass("NSImage")), sel_registerName("initWithSize:"), ((NSSize){a.w, a.h})); + + objc_msgSend_void_id(cursor_image, sel_registerName("addRepresentation:"), representation); + + /* Finally, set the cursor image. */ + id cursor = (id) ((id(*)(id, SEL, id, NSPoint))objc_msgSend) + (NSAlloc(objc_getClass("NSCursor")), sel_registerName("initWithImage:hotSpot:"), cursor_image, (NSPoint){0.0, 0.0}); + + /* Free the garbage. */ + NSRelease(cursor_image); + NSRelease(representation); + + return (void*)cursor; +} + +void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) { + RGFW_ASSERT(win != NULL); RGFW_ASSERT(mouse); + CGDisplayShowCursor(kCGDirectMainDisplay); + objc_msgSend_void((id)mouse, sel_registerName("set")); + win->src.mouse = mouse; +} + +void RGFW_freeMouse(RGFW_mouse* mouse) { + RGFW_ASSERT(mouse); + NSRelease((id)mouse); +} + +RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) { + return RGFW_window_setMouseStandard(win, RGFW_mouseArrow); +} + +void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) { + RGFW_window_showMouseFlags(win, show); + if (show) CGDisplayShowCursor(kCGDirectMainDisplay); + else CGDisplayHideCursor(kCGDirectMainDisplay); +} + +RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 stdMouses) { + static const char* mouseIconSrc[16] = {"arrowCursor", "arrowCursor", "IBeamCursor", "crosshairCursor", "pointingHandCursor", "resizeLeftRightCursor", "resizeUpDownCursor", "_windowResizeNorthWestSouthEastCursor", "_windowResizeNorthEastSouthWestCursor", "closedHandCursor", "operationNotAllowedCursor"}; + if (stdMouses > ((sizeof(mouseIconSrc)) / (sizeof(char*)))) + return RGFW_FALSE; + + const char* mouseStr = mouseIconSrc[stdMouses]; + id mouse = NSCursor_arrowStr(mouseStr); + + if (mouse == NULL) + return RGFW_FALSE; + + RGFW_UNUSED(win); + CGDisplayShowCursor(kCGDirectMainDisplay); + objc_msgSend_void(mouse, sel_registerName("set")); + win->src.mouse = mouse; + + return RGFW_TRUE; +} + +void RGFW_releaseCursor(RGFW_window* win) { + RGFW_UNUSED(win); + CGAssociateMouseAndMouseCursorPosition(1); +} + +void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { + RGFW_UNUSED(win); + + CGWarpMouseCursorPosition((CGPoint){r.x + (r.w / 2), r.y + (r.h / 2)}); + CGAssociateMouseAndMouseCursorPosition(0); +} + +void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) { + RGFW_UNUSED(win); + + win->_lastMousePoint = RGFW_POINT(v.x - win->r.x, v.y - win->r.y); + CGWarpMouseCursorPosition((CGPoint){v.x, v.y}); +} + + +void RGFW_window_hide(RGFW_window* win) { + objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), false); +} + +void RGFW_window_show(RGFW_window* win) { + if (win->_flags & RGFW_windowFocusOnShow) + ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL); + + ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("orderFront:"), NULL); + objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true); +} + +RGFW_bool RGFW_window_isHidden(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + bool visible = objc_msgSend_bool(win->src.window, sel_registerName("isVisible")); + return visible == NO && !RGFW_window_isMinimized(win); +} + +RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + return objc_msgSend_bool(win->src.window, sel_registerName("isMiniaturized")) == YES; +} + +RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_bool b = (RGFW_bool)objc_msgSend_bool(win->src.window, sel_registerName("isZoomed")); + return b; +} + +id RGFW_getNSScreenForDisplayID(CGDirectDisplayID display) { + Class NSScreenClass = objc_getClass("NSScreen"); + + id screens = objc_msgSend_id(NSScreenClass, sel_registerName("screens")); + + NSUInteger count = (NSUInteger)objc_msgSend_uint(screens, sel_registerName("count")); + NSUInteger i; + for (i = 0; i < count; i++) { + id screen = ((id (*)(id, SEL, int))objc_msgSend) (screens, sel_registerName("objectAtIndex:"), (int)i); + id description = objc_msgSend_id(screen, sel_registerName("deviceDescription")); + id screenNumberKey = NSString_stringWithUTF8String("NSScreenNumber"); + id screenNumber = objc_msgSend_id_id(description, sel_registerName("objectForKey:"), screenNumberKey); + + if ((CGDirectDisplayID)objc_msgSend_uint(screenNumber, sel_registerName("unsignedIntValue")) == display) { + return screen; + } + } + + return NULL; +} + + +u32 RGFW_osx_getRefreshRate(CGDirectDisplayID display, CGDisplayModeRef mode) { + if (mode) { + u32 refreshRate = (u32)CGDisplayModeGetRefreshRate(mode); + if (refreshRate != 0) return refreshRate; + } + + CVDisplayLinkRef link; + CVDisplayLinkCreateWithCGDisplay(display, &link); + const CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link); + if (!(time.flags & kCVTimeIsIndefinite)) + return (u32) (time.timeScale / (double) time.timeValue); + + return 0; +} + +RGFW_monitor RGFW_NSCreateMonitor(CGDirectDisplayID display, id screen) { + RGFW_monitor monitor; + + const char name[] = "MacOS\0"; + RGFW_MEMCPY(monitor.name, name, 6); + + CGRect bounds = CGDisplayBounds(display); + monitor.x = (i32)bounds.origin.x; + monitor.y = (i32)bounds.origin.y; + monitor.mode.area = RGFW_AREA((int) bounds.size.width, (int) bounds.size.height); + + monitor.mode.red = 8; monitor.mode.green = 8; monitor.mode.blue = 8; + + CGDisplayModeRef mode = CGDisplayCopyDisplayMode(display); + monitor.mode.refreshRate = RGFW_osx_getRefreshRate(display, mode); + CFRelease(mode); + + CGSize screenSizeMM = CGDisplayScreenSize(display); + monitor.physW = (float)screenSizeMM.width / 25.4f; + monitor.physH = (float)screenSizeMM.height / 25.4f; + + float ppi_width = (monitor.mode.area.w/monitor.physW); + float ppi_height = (monitor.mode.area.h/monitor.physH); + + monitor.pixelRatio = (float)((CGFloat (*)(id, SEL))abi_objc_msgSend_fpret) (screen, sel_registerName("backingScaleFactor")); + float dpi = 96.0f * monitor.pixelRatio; + + monitor.scaleX = ((i32)(((float) (ppi_width) / dpi) * 10.0f)) / 10.0f; + monitor.scaleY = ((i32)(((float) (ppi_height) / dpi) * 10.0f)) / 10.0f; + + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, RGFW_DEBUG_CTX_MON(monitor), "monitor found"); + return monitor; +} + + +RGFW_monitor* RGFW_getMonitors(size_t* len) { + static CGDirectDisplayID displays[7]; + u32 count; + + if (CGGetActiveDisplayList(6, displays, &count) != kCGErrorSuccess) + return NULL; + + if (count > 6) count = 6; + + static RGFW_monitor monitors[7]; + + u32 i; + for (i = 0; i < count; i++) + monitors[i] = RGFW_NSCreateMonitor(displays[i], RGFW_getNSScreenForDisplayID(displays[i])); + + if (len != NULL) *len = count; + return monitors; +} + +RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) { + CGPoint point = { mon.x, mon.y }; + + CGDirectDisplayID display; + uint32_t displayCount = 0; + CGError err = CGGetDisplaysWithPoint(point, 1, &display, &displayCount); + if (err != kCGErrorSuccess || displayCount != 1) + return RGFW_FALSE; + + CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, NULL); + + if (allModes == NULL) + return RGFW_FALSE; + + CFIndex i; + for (i = 0; i < CFArrayGetCount(allModes); i++) { + CGDisplayModeRef cmode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i); + + RGFW_monitorMode foundMode; + foundMode.area = RGFW_AREA(CGDisplayModeGetWidth(cmode), CGDisplayModeGetHeight(cmode)); + foundMode.refreshRate = RGFW_osx_getRefreshRate(display, cmode); + foundMode.red = 8; foundMode.green = 8; foundMode.blue = 8; + + if (RGFW_monitorModeCompare(mode, foundMode, request)) { + if (CGDisplaySetDisplayMode(display, cmode, NULL) == kCGErrorSuccess) { + CFRelease(allModes); + return RGFW_TRUE; + } + break; + } + } + + CFRelease(allModes); + + return RGFW_FALSE; +} + +RGFW_monitor RGFW_getPrimaryMonitor(void) { + CGDirectDisplayID primary = CGMainDisplayID(); + return RGFW_NSCreateMonitor(primary, RGFW_getNSScreenForDisplayID(primary)); +} + +RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { + id screen = objc_msgSend_id(win->src.window, sel_registerName("screen")); + id description = objc_msgSend_id(screen, sel_registerName("deviceDescription")); + id screenNumberKey = NSString_stringWithUTF8String("NSScreenNumber"); + id screenNumber = objc_msgSend_id_id(description, sel_registerName("objectForKey:"), screenNumberKey); + + CGDirectDisplayID display = (CGDirectDisplayID)objc_msgSend_uint(screenNumber, sel_registerName("unsignedIntValue")); + + return RGFW_NSCreateMonitor(display, screen); +} + +RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { + size_t clip_len; + char* clip = (char*)NSPasteboard_stringForType(NSPasteboard_generalPasteboard(), NSPasteboardTypeString, &clip_len); + if (clip == NULL) return -1; + + if (str != NULL) { + if (strCapacity < clip_len) + return 0; + + RGFW_MEMCPY(str, clip, clip_len); + + str[clip_len] = '\0'; + } + + return (RGFW_ssize_t)clip_len; +} + +void RGFW_writeClipboard(const char* text, u32 textLen) { + RGFW_UNUSED(textLen); + + NSPasteboardType array[] = { NSPasteboardTypeString, NULL }; + NSPasteBoard_declareTypes(NSPasteboard_generalPasteboard(), array, 1, NULL); + + SEL func = sel_registerName("setString:forType:"); + ((bool (*)(id, SEL, id, id))objc_msgSend) + (NSPasteboard_generalPasteboard(), func, NSString_stringWithUTF8String(text), NSString_stringWithUTF8String(NSPasteboardTypeString)); +} + + #ifdef RGFW_OPENGL + void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { + if (win != NULL) + objc_msgSend_void(win->src.ctx, sel_registerName("makeCurrentContext")); + else + objc_msgSend_id(objc_getClass("NSOpenGLContext"), sel_registerName("clearCurrentContext")); + } + void* RGFW_getCurrent_OpenGL(void) { + return objc_msgSend_id(objc_getClass("NSOpenGLContext"), sel_registerName("currentContext")); + } + + void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) { + objc_msgSend_void(win->src.ctx, sel_registerName("flushBuffer")); + } + #endif + + #if !defined(RGFW_EGL) + + void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { + RGFW_ASSERT(win != NULL); + #if defined(RGFW_OPENGL) + + NSOpenGLContext_setValues((id)win->src.ctx, &swapInterval, 222); + #else + RGFW_UNUSED(swapInterval); + #endif + } + + #endif + +void RGFW_window_swapBuffers_software(RGFW_window* win) { +#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + RGFW_RGB_to_BGR(win, win->buffer); + i32 channels = 4; + id image = ((id (*)(Class, SEL))objc_msgSend)(objc_getClass("NSImage"), sel_getUid("alloc")); + NSSize size = (NSSize){win->bufferSize.w, win->bufferSize.h}; + image = ((id (*)(id, SEL, NSSize))objc_msgSend)((id)image, sel_getUid("initWithSize:"), size); + + id rep = NSBitmapImageRep_initWithBitmapData(&win->buffer, win->r.w, win->r.h , 8, channels, (channels == 4), false, + "NSDeviceRGBColorSpace", 1 << 1, (u32)win->bufferSize.w * (u32)channels, 8 * (u32)channels); + ((void (*)(id, SEL, id))objc_msgSend)((id)image, sel_getUid("addRepresentation:"), rep); + + id contentView = ((id (*)(id, SEL))objc_msgSend)((id)win->src.window, sel_getUid("contentView")); + ((void (*)(id, SEL, BOOL))objc_msgSend)(contentView, sel_getUid("setWantsLayer:"), YES); + id layer = ((id (*)(id, SEL))objc_msgSend)(contentView, sel_getUid("layer")); + + ((void (*)(id, SEL, id))objc_msgSend)(layer, sel_getUid("setContents:"), (id)image); + ((void (*)(id, SEL, BOOL))objc_msgSend)(contentView, sel_getUid("setNeedsDisplay:"), YES); + + NSRelease(rep); + NSRelease(image); +#else + RGFW_UNUSED(win); +#endif +} + +void RGFW_deinit(void) { + _RGFW.windowCount = -1; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, (RGFW_debugContext){0}, "global context deinitialized"); +} + +void RGFW_window_close(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + NSRelease(win->src.view); + if ((win->_flags & RGFW_windowNoInitAPI) == 0) RGFW_window_freeOpenGL(win); + + #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + if ((win->_flags & RGFW_BUFFER_ALLOC)) + RGFW_FREE(win->buffer); + #endif + + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, (RGFW_debugContext){0}, "global context deinitialized"); + _RGFW.windowCount--; + if (_RGFW.windowCount == 0) RGFW_deinit(); + + RGFW_clipboard_switch(NULL); + RGFW_FREE(win->event.droppedFiles); + if ((win->_flags & RGFW_WINDOW_ALLOC)) { + RGFW_FREE(win); + win = NULL; + } +} + +u64 RGFW_getTimerFreq(void) { + static u64 freq = 0; + if (freq == 0) { + mach_timebase_info_data_t info; + mach_timebase_info(&info); + freq = (u64)((info.denom * 1e9) / info.numer); + } + + return freq; +} + +u64 RGFW_getTimerValue(void) { return (u64)mach_absolute_time(); } + +#endif /* RGFW_MACOS */ + +/* + End of MaOS defines +*/ + +/* + WASM defines +*/ + +#ifdef RGFW_WASM +EM_BOOL Emscripten_on_resize(int eventType, const EmscriptenUiEvent* e, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + + RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowResized, ._win = _RGFW.root}); + RGFW_windowResizedCallback(_RGFW.root, RGFW_RECT(0, 0, e->windowInnerWidth, e->windowInnerHeight)); + return EM_TRUE; +} + +EM_BOOL Emscripten_on_fullscreenchange(int eventType, const EmscriptenFullscreenChangeEvent* e, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + static u8 fullscreen = RGFW_FALSE; + static RGFW_rect ogRect; + + if (fullscreen == RGFW_FALSE) { + ogRect = _RGFW.root->r; + } + + fullscreen = !fullscreen; + RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowResized, ._win = _RGFW.root}); + _RGFW.root->r = RGFW_RECT(0, 0, e->screenWidth, e->screenHeight); + + EM_ASM("Module.canvas.focus();"); + + if (fullscreen == RGFW_FALSE) { + _RGFW.root->r = RGFW_RECT(0, 0, ogRect.w, ogRect.h); + /* emscripten_request_fullscreen("#canvas", 0); */ + } else { + #if __EMSCRIPTEN_major__ >= 1 && __EMSCRIPTEN_minor__ >= 29 && __EMSCRIPTEN_tiny__ >= 0 + EmscriptenFullscreenStrategy FSStrat = {0}; + FSStrat.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; /* EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT : EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; */ + FSStrat.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF; + FSStrat.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT; + emscripten_request_fullscreen_strategy("#canvas", 1, &FSStrat); + #else + emscripten_request_fullscreen("#canvas", 1); + #endif + } + + emscripten_set_canvas_element_size("#canvas", _RGFW.root->r.w, _RGFW.root->r.h); + + RGFW_windowResizedCallback(_RGFW.root, _RGFW.root->r); + return EM_TRUE; +} + + + +EM_BOOL Emscripten_on_focusin(int eventType, const EmscriptenFocusEvent* e, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(e); + + RGFW_eventQueuePush((RGFW_event){.type = RGFW_focusIn, ._win = _RGFW.root}); + _RGFW.root->_flags |= RGFW_windowFocus; + RGFW_focusCallback(_RGFW.root, 1); + return EM_TRUE; +} + +EM_BOOL Emscripten_on_focusout(int eventType, const EmscriptenFocusEvent* e, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(e); + + RGFW_eventQueuePush((RGFW_event){.type = RGFW_focusOut, ._win = _RGFW.root}); + _RGFW.root->_flags &= ~(u32)RGFW_windowFocus; + RGFW_focusCallback(_RGFW.root, 0); + return EM_TRUE; +} + +EM_BOOL Emscripten_on_mousemove(int eventType, const EmscriptenMouseEvent* e, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + RGFW_eventQueuePush((RGFW_event){.type = RGFW_mousePosChanged, + .point = RGFW_POINT(e->targetX, e->targetY), + .vector = RGFW_POINT(e->movementX, e->movementY), + ._win = _RGFW.root}); + + _RGFW.root->_lastMousePoint = RGFW_POINT(e->targetX, e->targetY); + RGFW_mousePosCallback(_RGFW.root, RGFW_POINT(e->targetX, e->targetY), RGFW_POINT(e->movementX, e->movementY)); + return EM_TRUE; +} + +EM_BOOL Emscripten_on_mousedown(int eventType, const EmscriptenMouseEvent* e, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + + int button = e->button; + if (button > 2) + button += 2; + + RGFW_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonPressed, + .point = RGFW_POINT(e->targetX, e->targetY), + .vector = RGFW_POINT(e->movementX, e->movementY), + .button = (u8)button, + .scroll = 0, + ._win = _RGFW.root}); + RGFW_mouseButtons[button].prev = RGFW_mouseButtons[button].current; + RGFW_mouseButtons[button].current = 1; + + RGFW_mouseButtonCallback(_RGFW.root, button, 0, 1); + return EM_TRUE; +} + +EM_BOOL Emscripten_on_mouseup(int eventType, const EmscriptenMouseEvent* e, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + + int button = e->button; + if (button > 2) + button += 2; + + RGFW_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonReleased, + .point = RGFW_POINT(e->targetX, e->targetY), + .vector = RGFW_POINT(e->movementX, e->movementY), + .button = (u8)button, + .scroll = 0, + ._win = _RGFW.root}); + RGFW_mouseButtons[button].prev = RGFW_mouseButtons[button].current; + RGFW_mouseButtons[button].current = 0; + + RGFW_mouseButtonCallback(_RGFW.root, button, 0, 0); + return EM_TRUE; +} + +EM_BOOL Emscripten_on_wheel(int eventType, const EmscriptenWheelEvent* e, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + + int button = RGFW_mouseScrollUp + (e->deltaY < 0); + RGFW_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonPressed, + .button = (u8)button, + .scroll = (double)(e->deltaY < 0 ? 1 : -1), + ._win = _RGFW.root}); + RGFW_mouseButtons[button].prev = RGFW_mouseButtons[button].current; + RGFW_mouseButtons[button].current = 1; + RGFW_mouseButtonCallback(_RGFW.root, button, e->deltaY < 0 ? 1 : -1, 1); + + return EM_TRUE; +} + +EM_BOOL Emscripten_on_touchstart(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_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonPressed, + .point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY), + .button = RGFW_mouseLeft, + ._win = _RGFW.root}); + + RGFW_mouseButtons[RGFW_mouseLeft].prev = RGFW_mouseButtons[RGFW_mouseLeft].current; + RGFW_mouseButtons[RGFW_mouseLeft].current = 1; + + _RGFW.root->_lastMousePoint = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY); + RGFW_mousePosCallback(_RGFW.root, RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY), _RGFW.root->event.vector); + RGFW_mouseButtonCallback(_RGFW.root, RGFW_mouseLeft, 0, 1); + } + + return EM_TRUE; +} +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_eventQueuePush((RGFW_event){.type = RGFW_mousePosChanged, + .point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY), + .button = RGFW_mouseLeft, + ._win = _RGFW.root}); + + _RGFW.root->_lastMousePoint = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY); + RGFW_mousePosCallback(_RGFW.root, RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY), _RGFW.root->event.vector); + } + return EM_TRUE; +} + +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_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonReleased, + .point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY), + .button = RGFW_mouseLeft, + ._win = _RGFW.root}); + + RGFW_mouseButtons[RGFW_mouseLeft].prev = RGFW_mouseButtons[RGFW_mouseLeft].current; + RGFW_mouseButtons[RGFW_mouseLeft].current = 0; + + _RGFW.root->_lastMousePoint = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY); + RGFW_mousePosCallback(_RGFW.root, RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY), _RGFW.root->event.vector); + RGFW_mouseButtonCallback(_RGFW.root, RGFW_mouseLeft, 0, 0); + } + return EM_TRUE; +} + +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); + + if (gamepadEvent->index >= 4) + return 0; + + size_t i = gamepadEvent->index; + if (gamepadEvent->connected) { + RGFW_MEMCPY(RGFW_gamepads_name[gamepadEvent->index], gamepadEvent->id, sizeof(RGFW_gamepads_name[gamepadEvent->index])); + RGFW_gamepads_type[i] = RGFW_gamepadUnknown; + if (RGFW_STRSTR(RGFW_gamepads_name[i], "Microsoft") || RGFW_STRSTR(RGFW_gamepads_name[i], "X-Box")) + RGFW_gamepads_type[i] = RGFW_gamepadMicrosoft; + else if (RGFW_STRSTR(RGFW_gamepads_name[i], "PlayStation") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS3") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS4") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS5")) + RGFW_gamepads_type[i] = RGFW_gamepadSony; + else if (RGFW_STRSTR(RGFW_gamepads_name[i], "Nintendo")) + RGFW_gamepads_type[i] = RGFW_gamepadNintendo; + else if (RGFW_STRSTR(RGFW_gamepads_name[i], "Logitech")) + RGFW_gamepads_type[i] = RGFW_gamepadLogitech; + RGFW_gamepadCount++; + } else { + RGFW_gamepadCount--; + } + + RGFW_eventQueuePush((RGFW_event){.type = (RGFW_eventType)(gamepadEvent->connected ? RGFW_gamepadConnected : RGFW_gamepadConnected), + .gamepad = (u16)gamepadEvent->index, + ._win = _RGFW.root}); + + RGFW_gamepadCallback(_RGFW.root, gamepadEvent->index, gamepadEvent->connected); + RGFW_gamepads[gamepadEvent->index] = gamepadEvent->connected; + + return 1; /* The event was consumed by the callback handler */ +} + +u32 RGFW_wASMPhysicalToRGFW(u32 hash) { + switch(hash) { /* 0x0000 */ + case 0x67243A2DU /* Escape */: return RGFW_escape; /* 0x0001 */ + case 0x67251058U /* Digit0 */: return RGFW_0; /* 0x0002 */ + case 0x67251059U /* Digit1 */: return RGFW_1; /* 0x0003 */ + case 0x6725105AU /* Digit2 */: return RGFW_2; /* 0x0004 */ + case 0x6725105BU /* Digit3 */: return RGFW_3; /* 0x0005 */ + case 0x6725105CU /* Digit4 */: return RGFW_4; /* 0x0006 */ + case 0x6725105DU /* Digit5 */: return RGFW_5; /* 0x0007 */ + case 0x6725105EU /* Digit6 */: return RGFW_6; /* 0x0008 */ + case 0x6725105FU /* Digit7 */: return RGFW_7; /* 0x0009 */ + case 0x67251050U /* Digit8 */: return RGFW_8; /* 0x000A */ + case 0x67251051U /* Digit9 */: return RGFW_9; /* 0x000B */ + case 0x92E14DD3U /* Minus */: return RGFW_minus; /* 0x000C */ + case 0x92E1FBACU /* Equal */: return RGFW_equals; /* 0x000D */ + case 0x36BF1CB5U /* Backspace */: return RGFW_backSpace; /* 0x000E */ + case 0x7B8E51E2U /* Tab */: return RGFW_tab; /* 0x000F */ + case 0x2C595B51U /* KeyQ */: return RGFW_q; /* 0x0010 */ + case 0x2C595B57U /* KeyW */: return RGFW_w; /* 0x0011 */ + case 0x2C595B45U /* KeyE */: return RGFW_e; /* 0x0012 */ + case 0x2C595B52U /* KeyR */: return RGFW_r; /* 0x0013 */ + case 0x2C595B54U /* KeyT */: return RGFW_t; /* 0x0014 */ + case 0x2C595B59U /* KeyY */: return RGFW_y; /* 0x0015 */ + case 0x2C595B55U /* KeyU */: return RGFW_u; /* 0x0016 */ + case 0x2C595B4FU /* KeyO */: return RGFW_o; /* 0x0018 */ + case 0x2C595B50U /* KeyP */: return RGFW_p; /* 0x0019 */ + case 0x45D8158CU /* BracketLeft */: return RGFW_closeBracket; /* 0x001A */ + case 0xDEEABF7CU /* BracketRight */: return RGFW_bracket; /* 0x001B */ + case 0x92E1C5D2U /* Enter */: return RGFW_return; /* 0x001C */ + case 0xE058958CU /* ControlLeft */: return RGFW_controlL; /* 0x001D */ + case 0x2C595B41U /* KeyA */: return RGFW_a; /* 0x001E */ + case 0x2C595B53U /* KeyS */: return RGFW_s; /* 0x001F */ + case 0x2C595B44U /* KeyD */: return RGFW_d; /* 0x0020 */ + case 0x2C595B46U /* KeyF */: return RGFW_f; /* 0x0021 */ + case 0x2C595B47U /* KeyG */: return RGFW_g; /* 0x0022 */ + case 0x2C595B48U /* KeyH */: return RGFW_h; /* 0x0023 */ + case 0x2C595B4AU /* KeyJ */: return RGFW_j; /* 0x0024 */ + case 0x2C595B4BU /* KeyK */: return RGFW_k; /* 0x0025 */ + case 0x2C595B4CU /* KeyL */: return RGFW_l; /* 0x0026 */ + case 0x2707219EU /* Semicolon */: return RGFW_semicolon; /* 0x0027 */ + case 0x92E0B58DU /* Quote */: return RGFW_apostrophe; /* 0x0028 */ + case 0x36BF358DU /* Backquote */: return RGFW_backtick; /* 0x0029 */ + case 0x26B1958CU /* ShiftLeft */: return RGFW_shiftL; /* 0x002A */ + case 0x36BF2438U /* Backslash */: return RGFW_backSlash; /* 0x002B */ + case 0x2C595B5AU /* KeyZ */: return RGFW_z; /* 0x002C */ + case 0x2C595B58U /* KeyX */: return RGFW_x; /* 0x002D */ + case 0x2C595B43U /* KeyC */: return RGFW_c; /* 0x002E */ + case 0x2C595B56U /* KeyV */: return RGFW_v; /* 0x002F */ + case 0x2C595B42U /* KeyB */: return RGFW_b; /* 0x0030 */ + case 0x2C595B4EU /* KeyN */: return RGFW_n; /* 0x0031 */ + case 0x2C595B4DU /* KeyM */: return RGFW_m; /* 0x0032 */ + case 0x92E1A1C1U /* Comma */: return RGFW_comma; /* 0x0033 */ + case 0x672FFAD4U /* Period */: return RGFW_period; /* 0x0034 */ + case 0x92E0A438U /* Slash */: return RGFW_slash; /* 0x0035 */ + case 0xC5A6BF7CU /* ShiftRight */: return RGFW_shiftR; + case 0x5D64DA91U /* NumpadMultiply */: return RGFW_multiply; + case 0xC914958CU /* AltLeft */: return RGFW_altL; /* 0x0038 */ + case 0x92E09CB5U /* Space */: return RGFW_space; /* 0x0039 */ + case 0xB8FAE73BU /* CapsLock */: return RGFW_capsLock; /* 0x003A */ + case 0x7174B789U /* F1 */: return RGFW_F1; /* 0x003B */ + case 0x7174B78AU /* F2 */: return RGFW_F2; /* 0x003C */ + case 0x7174B78BU /* F3 */: return RGFW_F3; /* 0x003D */ + case 0x7174B78CU /* F4 */: return RGFW_F4; /* 0x003E */ + case 0x7174B78DU /* F5 */: return RGFW_F5; /* 0x003F */ + case 0x7174B78EU /* F6 */: return RGFW_F6; /* 0x0040 */ + case 0x7174B78FU /* F7 */: return RGFW_F7; /* 0x0041 */ + case 0x7174B780U /* F8 */: return RGFW_F8; /* 0x0042 */ + case 0x7174B781U /* F9 */: return RGFW_F9; /* 0x0043 */ + case 0x7B8E57B0U /* F10 */: return RGFW_F10; /* 0x0044 */ + case 0xC925FCDFU /* Numpad7 */: return RGFW_multiply; /* 0x0047 */ + case 0xC925FCD0U /* Numpad8 */: return RGFW_KP_8; /* 0x0048 */ + case 0xC925FCD1U /* Numpad9 */: return RGFW_KP_9; /* 0x0049 */ + case 0x5EA3E8A4U /* NumpadSubtract */: return RGFW_minus; /* 0x004A */ + case 0xC925FCDCU /* Numpad4 */: return RGFW_KP_4; /* 0x004B */ + case 0xC925FCDDU /* Numpad5 */: return RGFW_KP_5; /* 0x004C */ + case 0xC925FCDEU /* Numpad6 */: return RGFW_KP_6; /* 0x004D */ + case 0xC925FCD9U /* Numpad1 */: return RGFW_KP_1; /* 0x004F */ + case 0xC925FCDAU /* Numpad2 */: return RGFW_KP_2; /* 0x0050 */ + case 0xC925FCDBU /* Numpad3 */: return RGFW_KP_3; /* 0x0051 */ + case 0xC925FCD8U /* Numpad0 */: return RGFW_KP_0; /* 0x0052 */ + case 0x95852DACU /* NumpadDecimal */: return RGFW_period; /* 0x0053 */ + case 0x7B8E57B1U /* F11 */: return RGFW_F11; /* 0x0057 */ + case 0x7B8E57B2U /* F12 */: return RGFW_F12; /* 0x0058 */ + case 0x7393FBACU /* NumpadEqual */: return RGFW_KP_Return; + case 0xB88EBF7CU /* AltRight */: return RGFW_altR; /* 0xE038 */ + case 0xC925873BU /* NumLock */: return RGFW_numLock; /* 0xE045 */ + case 0x2C595F45U /* Home */: return RGFW_home; /* 0xE047 */ + case 0xC91BB690U /* ArrowUp */: return RGFW_up; /* 0xE048 */ + case 0x672F9210U /* PageUp */: return RGFW_pageUp; /* 0xE049 */ + case 0x3799258CU /* ArrowLeft */: return RGFW_left; /* 0xE04B */ + case 0x4CE33F7CU /* ArrowRight */: return RGFW_right; /* 0xE04D */ + case 0x7B8E55DCU /* End */: return RGFW_end; /* 0xE04F */ + case 0x3799379EU /* ArrowDown */: return RGFW_down; /* 0xE050 */ + case 0xBA90179EU /* PageDown */: return RGFW_pageDown; /* 0xE051 */ + case 0x6723CB2CU /* Insert */: return RGFW_insert; /* 0xE052 */ + case 0x6725C50DU /* Delete */: return RGFW_delete; /* 0xE053 */ + case 0x6723658CU /* OSLeft */: return RGFW_superL; /* 0xE05B */ + case 0x39643F7CU /* MetaRight */: return RGFW_superR; /* 0xE05C */ + } + + return 0; +} + +void EMSCRIPTEN_KEEPALIVE RGFW_handleKeyEvent(char* key, char* code, RGFW_bool press) { + const char* iCode = code; + + u32 hash = 0; + while(*iCode) hash = ((hash ^ 0x7E057D79U) << 3) ^ (unsigned int)*iCode++; + + u32 physicalKey = RGFW_wASMPhysicalToRGFW(hash); + + u8 mappedKey = (u8)(*((u32*)key)); + + if (*((u16*)key) != mappedKey) { + mappedKey = 0; + if (*((u32*)key) == *((u32*)"Tab")) mappedKey = RGFW_tab; + } + + RGFW_eventQueuePush((RGFW_event){.type = (RGFW_eventType)(press ? RGFW_keyPressed : RGFW_keyReleased), + .key = (u8)physicalKey, + .keyChar = (u8)mappedKey, + .keyMod = _RGFW.root->event.keyMod, + ._win = _RGFW.root}); + + RGFW_keyboard[physicalKey].prev = RGFW_keyboard[physicalKey].current; + RGFW_keyboard[physicalKey].current = press; + + RGFW_keyCallback(_RGFW.root, physicalKey, mappedKey, _RGFW.root->event.keyMod, press); +} + +void EMSCRIPTEN_KEEPALIVE RGFW_handleKeyMods(RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll) { + RGFW_updateKeyModsPro(_RGFW.root, capital, numlock, control, alt, shift, super, scroll); +} + +void EMSCRIPTEN_KEEPALIVE Emscripten_onDrop(size_t count) { + if (!(_RGFW.root->_flags & RGFW_windowAllowDND)) + return; + + RGFW_eventQueuePush((RGFW_event){.type = RGFW_DND, + .droppedFilesCount = count, + ._win = _RGFW.root}); + RGFW_dndCallback(_RGFW.root, _RGFW.root->event.droppedFiles, count); +} + +RGFW_bool RGFW_stopCheckEvents_bool = RGFW_FALSE; +void RGFW_stopCheckEvents(void) { + RGFW_stopCheckEvents_bool = RGFW_TRUE; +} + +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 && (RGFW_getTimeNS() / 1e+6) - start < waitMS) + emscripten_sleep(0); + + RGFW_stopCheckEvents_bool = RGFW_FALSE; +} + +void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area){ + #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + win->buffer = buffer; + win->bufferSize = area; + #ifdef RGFW_OSMESA + win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL); + OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, area.w, area.h); + OSMesaPixelStore(OSMESA_Y_UP, 0); + #endif + #else + RGFW_UNUSED(win); RGFW_UNUSED(buffer); RGFW_UNUSED(area); /*!< 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 + */ + RGFW_MEMCPY((char*)_RGFW.root->event.droppedFiles[index], file, RGFW_MAX_PATH); +} + +#include +#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); +} + +void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software) { +#if defined(RGFW_OPENGL) && !defined(RGFW_WEBGPU) && !defined(RGFW_OSMESA) && !defined(RGFW_BUFFER) + EmscriptenWebGLContextAttributes attrs; + attrs.alpha = RGFW_GL_HINTS[RGFW_glDepth]; + attrs.depth = RGFW_GL_HINTS[RGFW_glAlpha]; + attrs.stencil = RGFW_GL_HINTS[RGFW_glStencil]; + attrs.antialias = RGFW_GL_HINTS[RGFW_glSamples]; + attrs.premultipliedAlpha = EM_TRUE; + attrs.preserveDrawingBuffer = EM_FALSE; + + if (RGFW_GL_HINTS[RGFW_glDoubleBuffer] == 0) + attrs.renderViaOffscreenBackBuffer = 0; + else + attrs.renderViaOffscreenBackBuffer = RGFW_GL_HINTS[RGFW_glAuxBuffers]; + + attrs.failIfMajorPerformanceCaveat = EM_FALSE; + attrs.majorVersion = (RGFW_GL_HINTS[RGFW_glMajor] == 0) ? 1 : RGFW_GL_HINTS[RGFW_glMajor]; + attrs.minorVersion = RGFW_GL_HINTS[RGFW_glMinor]; + + attrs.enableExtensionsByDefault = EM_TRUE; + attrs.explicitSwapControl = EM_TRUE; + + emscripten_webgl_init_context_attributes(&attrs); + win->src.ctx = emscripten_webgl_create_context("#canvas", &attrs); + emscripten_webgl_make_context_current(win->src.ctx); + + #ifdef LEGACY_GL_EMULATION + EM_ASM("Module.useWebGL = true; GLImmediate.init();"); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context initalized"); + #endif + glViewport(0, 0, win->r.w, win->r.h); +#endif +} + +void RGFW_window_freeOpenGL(RGFW_window* win) { +#if defined(RGFW_OPENGL) && !defined(RGFW_WEBGPU) && !defined(RGFW_OSMESA) && !defined(RGFW_OSMESA) + if (win->src.ctx == 0) return; + emscripten_webgl_destroy_context(win->src.ctx); + win->src.ctx = 0; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context freed"); +#elif defined(RGFW_OPENGL) && defined(RGFW_OSMESA) + if(win->src.ctx == 0) return; + OSMesaDestroyContext(win->src.ctx); + win->src.ctx = 0; +#else + RGFW_UNUSED(win); +#endif +} + +i32 RGFW_init(void) { _RGFW.windowCount = 0; RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, (RGFW_debugContext){0}, "global context initialized"); return 0; } + +RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) { + RGFW_window_basic_init(win, rect, flags); + RGFW_window_initOpenGL(win, 0); + + #if defined(RGFW_WEBGPU) + win->src.ctx = wgpuCreateInstance(NULL); + win->src.device = emscripten_webgpu_get_device(); + win->src.queue = wgpuDeviceGetQueue(win->src.device); + #endif + + emscripten_set_canvas_element_size("#canvas", rect.w, rect.h); + emscripten_set_window_title(name); + + /* load callbacks */ + 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 (flags & RGFW_windowAllowDND) { + win->_flags |= RGFW_windowAllowDND; + } + + EM_ASM({ + window.addEventListener("keydown", + (event) => { + var key = stringToNewUTF8(event.key); var code = stringToNewUTF8(event.code); + Module._RGFW_handleKeyMods(event.getModifierState("CapsLock"), event.getModifierState("NumLock"), event.getModifierState("Control"), event.getModifierState("Alt"), event.getModifierState("Shift"), event.getModifierState("Meta"), event.getModifierState("ScrollLock")); + Module._RGFW_handleKeyEvent(key, code, 1); + _free(key); _free(code); + }, + true); + window.addEventListener("keyup", + (event) => { + var key = stringToNewUTF8(event.key); var code = stringToNewUTF8(event.code); + Module._RGFW_handleKeyMods(event.getModifierState("CapsLock"), event.getModifierState("NumLock"), event.getModifierState("Control"), event.getModifierState("Alt"), event.getModifierState("Shift"), event.getModifierState("Meta"), event.getModifierState("ScrollLock")); + Module._RGFW_handleKeyEvent(key, code, 0); + _free(key); _free(code); + }, + true); + }); + + 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_window_setFlags(win, flags); + + if ((flags & RGFW_windowNoInitAPI) == 0) { + RGFW_window_initBuffer(win); + } + + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new window was created"); + return win; +} + +RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { + if (win == NULL || ((win->_flags & RGFW_windowFreeOnClose) && (win->_flags & RGFW_EVENT_QUIT))) return NULL; + RGFW_event* ev = RGFW_window_checkEventCore(win); + if (ev) return ev; + + emscripten_sample_gamepad_data(); + /* check gamepads */ + int i; + for (i = 0; (i < emscripten_get_num_gamepads()) && (i < 4); i++) { + if (RGFW_gamepads[i] == 0) + continue; + EmscriptenGamepadEvent gamepadState; + + if (emscripten_get_gamepad_status(i, &gamepadState) != EMSCRIPTEN_RESULT_SUCCESS) + break; + + /* Register buttons data for every connected gamepad */ + int j; + for (j = 0; (j < gamepadState.numButtons) && (j < 16); j++) { + u32 map[] = { + RGFW_gamepadA, RGFW_gamepadB, RGFW_gamepadX, RGFW_gamepadY, + RGFW_gamepadL1, RGFW_gamepadR1, RGFW_gamepadL2, RGFW_gamepadR2, + RGFW_gamepadSelect, RGFW_gamepadStart, + RGFW_gamepadL3, RGFW_gamepadR3, + RGFW_gamepadUp, RGFW_gamepadDown, RGFW_gamepadLeft, RGFW_gamepadRight, RGFW_gamepadHome + }; + + + u32 button = map[j]; + if (button == 404) + continue; + + if (RGFW_gamepadPressed[i][button].current != gamepadState.digitalButton[j]) { + if (gamepadState.digitalButton[j]) + win->event.type = RGFW_gamepadButtonPressed; + else + win->event.type = RGFW_gamepadButtonReleased; + + win->event.gamepad = i; + win->event.button = map[j]; + + RGFW_gamepadPressed[i][button].prev = RGFW_gamepadPressed[i][button].current; + RGFW_gamepadPressed[i][button].current = gamepadState.digitalButton[j]; + + RGFW_gamepadButtonCallback(win, win->event.gamepad, win->event.button, gamepadState.digitalButton[j]); + return &win->event; + } + } + + for (j = 0; (j < gamepadState.numAxes) && (j < 4); j += 2) { + win->event.axisesCount = gamepadState.numAxes / 2; + if (RGFW_gamepadAxes[i][(size_t)(j / 2)].x != (i8)(gamepadState.axis[j] * 100.0f) || + RGFW_gamepadAxes[i][(size_t)(j / 2)].y != (i8)(gamepadState.axis[j + 1] * 100.0f) + ) { + + RGFW_gamepadAxes[i][(size_t)(j / 2)].x = (i8)(gamepadState.axis[j] * 100.0f); + RGFW_gamepadAxes[i][(size_t)(j / 2)].y = (i8)(gamepadState.axis[j + 1] * 100.0f); + win->event.axis[(size_t)(j / 2)] = RGFW_gamepadAxes[i][(size_t)(j / 2)]; + + win->event.type = RGFW_gamepadAxisMove; + win->event.gamepad = i; + win->event.whichAxis = j / 2; + + RGFW_gamepadAxisCallback(win, win->event.gamepad, win->event.axis, win->event.axisesCount, win->event.whichAxis); + return &win->event; + } + } + } + + return NULL; +} + +void RGFW_window_resize(RGFW_window* win, RGFW_area a) { + RGFW_UNUSED(win); + emscripten_set_canvas_element_size("#canvas", a.w, a.h); +} + +/* NOTE: I don't know if this is possible */ +void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) { RGFW_UNUSED(win); RGFW_UNUSED(v); } +/* this one might be possible but it looks iffy */ +RGFW_mouse* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels) { RGFW_UNUSED(channels); RGFW_UNUSED(a); RGFW_UNUSED(icon); return NULL; } + +void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) { RGFW_UNUSED(win); RGFW_UNUSED(mouse); } +void RGFW_freeMouse(RGFW_mouse* mouse) { RGFW_UNUSED(mouse); } + +RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { + static const char cursors[16][16] = { + "default", "default", "text", "crosshair", + "pointer", "ew-resize", "ns-resize", "nwse-resize", "nesw-resize", + "move", "not-allowed" + }; + + RGFW_UNUSED(win); + EM_ASM( { document.getElementById("canvas").style.cursor = UTF8ToString($0); }, cursors[mouse]); + return RGFW_TRUE; +} + +RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) { + return RGFW_window_setMouseStandard(win, RGFW_mouseNormal); +} + +void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) { + RGFW_window_showMouseFlags(win, show); + if (show) + RGFW_window_setMouseDefault(win); + else + EM_ASM(document.getElementById('canvas').style.cursor = 'none';); +} + +RGFW_point RGFW_getGlobalMousePoint(void) { + RGFW_point point; + point.x = EM_ASM_INT({ + return window.mouseX || 0; + }); + point.y = EM_ASM_INT({ + return window.mouseY || 0; + }); + return point; +} + +void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) { + RGFW_UNUSED(win); + + EM_ASM_({ + var canvas = document.getElementById('canvas'); + if ($0) { + canvas.style.pointerEvents = 'none'; + } else { + canvas.style.pointerEvents = 'auto'; + } + }, passthrough); +} + +void RGFW_writeClipboard(const char* text, u32 textLen) { + RGFW_UNUSED(textLen); + EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, text); +} + + +RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { + RGFW_UNUSED(str); RGFW_UNUSED(strCapacity); + /* + placeholder code for later + I'm not sure if this is possible do the the async stuff + */ + return 0; +} + +void RGFW_window_swapBuffers_software(RGFW_window* win) { +#if defined(RGFW_OSMESA) + EM_ASM_({ + var data = Module.HEAPU8.slice($0, $0 + $1 * $2 * 4); + let context = document.getElementById("canvas").getContext("2d"); + let image = context.getImageData(0, 0, $1, $2); + image.data.set(data); + context.putImageData(image, 0, $4 - $2); + }, win->buffer, win->bufferSize.w, win->bufferSize.h, win->r.w, win->r.h); +#elif defined(RGFW_BUFFER) + EM_ASM_({ + var data = Module.HEAPU8.slice($0, $0 + $1 * $2 * 4); + let context = document.getElementById("canvas").getContext("2d"); + let image = context.getImageData(0, 0, $1, $2); + image.data.set(data); + context.putImageData(image, 0, 0); + }, win->buffer, win->bufferSize.w, win->bufferSize.h, win->r.w, win->r.h); + emscripten_sleep(0); +#else + RGFW_UNUSED(win); +#endif +} + +void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { +#if !defined(RGFW_WEBGPU) && !(defined(RGFW_OSMESA) || defined(RGFW_BUFFER)) + if (win == NULL) + emscripten_webgl_make_context_current(0); + else + emscripten_webgl_make_context_current(win->src.ctx); +#endif +} + + +void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) { +#ifndef RGFW_WEBGPU + emscripten_webgl_commit_frame(); + +#endif + emscripten_sleep(0); +} + +#ifndef RGFW_WEBGPU +void* RGFW_getCurrent_OpenGL(void) { return (void*)emscripten_webgl_get_current_context(); } +#endif + +#ifndef RGFW_EGL +void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { RGFW_UNUSED(win); RGFW_UNUSED(swapInterval); } +#endif + +void RGFW_deinit(void) { _RGFW.windowCount = -1; RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, (RGFW_debugContext){0}, "global context deinitialized"); } + +void RGFW_window_close(RGFW_window* win) { + if ((win->_flags & RGFW_windowNoInitAPI) == 0) RGFW_window_freeOpenGL(win); + + #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + if ((win->_flags & RGFW_BUFFER_ALLOC)) + RGFW_FREE(win->buffer); + #endif + + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a window was freed"); + _RGFW.windowCount--; + if (_RGFW.windowCount == 0) RGFW_deinit(); + + RGFW_clipboard_switch(NULL); + RGFW_FREE(win->event.droppedFiles); + if ((win->_flags & RGFW_WINDOW_ALLOC)) { + RGFW_FREE(win); + win = NULL; + } +} + +int RGFW_innerWidth(void) { return EM_ASM_INT({ return window.innerWidth; }); } +int RGFW_innerHeight(void) { return EM_ASM_INT({ return window.innerHeight; }); } + +RGFW_area RGFW_getScreenSize(void) { + return RGFW_AREA(RGFW_innerWidth(), RGFW_innerHeight()); +} + +RGFW_proc RGFW_getProcAddress(const char* procname) { + return (RGFW_proc)emscripten_webgl_get_proc_address(procname); +} + +void RGFW_sleep(u64 milisecond) { + emscripten_sleep(milisecond); +} + +u64 RGFW_getTimerFreq(void) { return (u64)1000; } +u64 RGFW_getTimerValue(void) { return emscripten_get_now() * 1e+6; } + +void RGFW_releaseCursor(RGFW_window* win) { + RGFW_UNUSED(win); + emscripten_exit_pointerlock(); +} + +void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { + RGFW_UNUSED(win); RGFW_UNUSED(r); + + emscripten_request_pointerlock("#canvas", 1); +} + + +void RGFW_window_setName(RGFW_window* win, const char* name) { + RGFW_UNUSED(win); + emscripten_set_window_title(name); +} + +void RGFW_window_maximize(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + RGFW_area screen = RGFW_getScreenSize(); + RGFW_window_move(win, RGFW_POINT(0, 0)); + RGFW_window_resize(win, screen); +} + +void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) { + RGFW_ASSERT(win != NULL); + if (fullscreen) { + win->_flags |= RGFW_windowFullscreen; + EM_ASM( Module.requestFullscreen(false, true); ); + return; + } + win->_flags &= ~(u32)RGFW_windowFullscreen; + EM_ASM( Module.exitFullscreen(false, true); ); +} + +void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) { + RGFW_UNUSED(win); + EM_ASM({ + var element = document.getElementById("canvas"); + if (element) + element.style.opacity = $1; + }, "elementId", opacity); +} + +/* unsupported functions */ +void RGFW_window_focus(RGFW_window* win) { RGFW_UNUSED(win); } +void RGFW_window_raise(RGFW_window* win) { RGFW_UNUSED(win); } +RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) { RGFW_UNUSED(mon); RGFW_UNUSED(mode); RGFW_UNUSED(request); return RGFW_FALSE; } +RGFW_monitor* RGFW_getMonitors(size_t* len) { RGFW_UNUSED(len); return NULL; } +RGFW_monitor RGFW_getPrimaryMonitor(void) { return (RGFW_monitor){}; } +void RGFW_window_move(RGFW_window* win, RGFW_point v) { RGFW_UNUSED(win); RGFW_UNUSED(v); } +void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a) { RGFW_UNUSED(win); RGFW_UNUSED(a); } +void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { RGFW_UNUSED(win); RGFW_UNUSED(a); } +void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { RGFW_UNUSED(win); RGFW_UNUSED(a); } +void RGFW_window_minimize(RGFW_window* win) { RGFW_UNUSED(win); } +void RGFW_window_restore(RGFW_window* win) { RGFW_UNUSED(win); } +void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) { RGFW_UNUSED(win); RGFW_UNUSED(floating); } +void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { RGFW_UNUSED(win); RGFW_UNUSED(border); } +RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* icon, RGFW_area a, i32 channels, u8 type) { RGFW_UNUSED(win); RGFW_UNUSED(icon); RGFW_UNUSED(a); RGFW_UNUSED(channels); RGFW_UNUSED(type); return RGFW_FALSE; } +void RGFW_window_hide(RGFW_window* win) { RGFW_UNUSED(win); } +void RGFW_window_show(RGFW_window* win) {RGFW_UNUSED(win); } +RGFW_bool RGFW_window_isHidden(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; } +RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; } +RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; } +RGFW_bool RGFW_window_isFloating(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; } +RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { RGFW_UNUSED(win); return (RGFW_monitor){}; } +#endif + +/* end of web asm defines */ + +/* unix (macOS, linux, web asm) only stuff */ +#if defined(RGFW_X11) || defined(RGFW_MACOS) || defined(RGFW_WASM) || defined(RGFW_WAYLAND) +#ifndef RGFW_NO_THREADS +#include + +RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args) { + RGFW_thread t; + pthread_create((pthread_t*) &t, NULL, *ptr, args); + return t; +} +void RGFW_cancelThread(RGFW_thread thread) { pthread_cancel((pthread_t) thread); } +void RGFW_joinThread(RGFW_thread thread) { pthread_join((pthread_t) thread, NULL); } + +#if defined(__linux__) +void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { pthread_setschedprio((pthread_t)thread, priority); } +#else +void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { RGFW_UNUSED(thread); RGFW_UNUSED(priority); } +#endif +#endif + +#ifndef RGFW_WASM +void RGFW_sleep(u64 ms) { + struct timespec time; + time.tv_sec = 0; + time.tv_nsec = (long int)((double)ms * 1e+6); + + #ifndef RGFW_NO_UNIX_CLOCK + nanosleep(&time, NULL); + #endif +} +#endif + +#endif /* end of unix / mac stuff */ +#endif /* RGFW_IMPLEMENTATION */ + +#if defined(__cplusplus) && !defined(__EMSCRIPTEN__) +} +#endif + +#if _MSC_VER + #pragma warning( pop ) +#endif diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 1c4f606a7..144d9f49f 100644 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -584,7 +584,7 @@ void SetWindowPosition(int x, int y) // Set monitor for the current window void SetWindowMonitor(int monitor) { - RGFW_window_moveToMonitor(platform.window, RGFW_getMonitors()[monitor]); + RGFW_window_moveToMonitor(platform.window, RGFW_getMonitors(NULL)[monitor]); } // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) @@ -641,7 +641,7 @@ int GetMonitorCount(void) #define MAX_MONITORS_SUPPORTED 6 int count = MAX_MONITORS_SUPPORTED; - RGFW_monitor *mons = RGFW_getMonitors(); + RGFW_monitor *mons = RGFW_getMonitors(NULL); for (int i = 0; i < 6; i++) { @@ -658,7 +658,7 @@ int GetMonitorCount(void) // Get current monitor where window is placed int GetCurrentMonitor(void) { - RGFW_monitor *mons = RGFW_getMonitors(); + RGFW_monitor *mons = RGFW_getMonitors(NULL); RGFW_monitor mon = { 0 }; if (platform.window) mon = RGFW_window_getMonitor(platform.window); @@ -675,7 +675,7 @@ int GetCurrentMonitor(void) // Get selected monitor position Vector2 GetMonitorPosition(int monitor) { - RGFW_monitor *mons = RGFW_getMonitors(); + RGFW_monitor *mons = RGFW_getMonitors(NULL); return (Vector2){ (float)mons[monitor].x, (float)mons[monitor].y }; } @@ -683,7 +683,7 @@ Vector2 GetMonitorPosition(int monitor) // Get selected monitor width (currently used by monitor) int GetMonitorWidth(int monitor) { - RGFW_monitor *mons = RGFW_getMonitors(); + RGFW_monitor *mons = RGFW_getMonitors(NULL); return mons[monitor].mode.area.w; } @@ -691,7 +691,7 @@ int GetMonitorWidth(int monitor) // Get selected monitor height (currently used by monitor) int GetMonitorHeight(int monitor) { - RGFW_monitor *mons = RGFW_getMonitors(); + RGFW_monitor *mons = RGFW_getMonitors(NULL); return mons[monitor].mode.area.h; } @@ -699,7 +699,7 @@ int GetMonitorHeight(int monitor) // Get selected monitor physical width in millimetres int GetMonitorPhysicalWidth(int monitor) { - RGFW_monitor *mons = RGFW_getMonitors(); + RGFW_monitor *mons = RGFW_getMonitors(NULL); return mons[monitor].physW; } @@ -707,7 +707,7 @@ int GetMonitorPhysicalWidth(int monitor) // Get selected monitor physical height in millimetres int GetMonitorPhysicalHeight(int monitor) { - RGFW_monitor *mons = RGFW_getMonitors(); + RGFW_monitor *mons = RGFW_getMonitors(NULL); return (int)mons[monitor].physH; } @@ -715,7 +715,7 @@ int GetMonitorPhysicalHeight(int monitor) // Get selected monitor refresh rate int GetMonitorRefreshRate(int monitor) { - RGFW_monitor *mons = RGFW_getMonitors(); + RGFW_monitor *mons = RGFW_getMonitors(NULL); return (int)mons[monitor].mode.refreshRate; } @@ -723,7 +723,7 @@ int GetMonitorRefreshRate(int monitor) // Get the human-readable, UTF-8 encoded name of the selected monitor const char *GetMonitorName(int monitor) { - RGFW_monitor *mons = RGFW_getMonitors(); + RGFW_monitor *mons = RGFW_getMonitors(NULL); return mons[monitor].name; } From 34181726171adeab30d23469d7fe40ae2d761aee Mon Sep 17 00:00:00 2001 From: M374LX Date: Thu, 29 May 2025 23:01:43 -0300 Subject: [PATCH 019/242] Update comments --- src/platforms/rcore_desktop_glfw.c | 6 +++--- src/platforms/rcore_desktop_sdl.c | 2 +- src/platforms/rcore_drm.c | 6 +++--- src/raylib.h | 6 +++--- src/rcore.c | 6 +++--- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 675d09f22..83c13d34e 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rcore_desktop - Functions to manage window, graphics device and inputs +* rcore_desktop_glfw - Functions to manage window, graphics device and inputs * * PLATFORM: DESKTOP: GLFW * - Windows (Win32, Win64) @@ -1238,7 +1238,7 @@ void PollInputEvents(void) } } - // Get current axis state + // Get current state of axes const float *axes = state.axes; for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1); k++) @@ -1246,7 +1246,7 @@ void PollInputEvents(void) CORE.Input.Gamepad.axisState[i][k] = axes[k]; } - // Register buttons for 2nd triggers (because GLFW doesn't count these as buttons but rather axis) + // Register buttons for 2nd triggers (because GLFW doesn't count these as buttons but rather as axes) if (CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1f) { CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = 1; diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 1621dd2a5..aacb2f800 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1825,7 +1825,7 @@ void PollInputEvents(void) { if (platform.gamepadId[i] == event.jaxis.which) { - // SDL axis value range is -32768 to 32767, we normalize it to RayLib's -1.0 to 1.0f range + // SDL axis value range is -32768 to 32767, we normalize it to raylib's -1.0 to 1.0f range float value = event.jaxis.value/(float)32767; CORE.Input.Gamepad.axisState[i][axis] = value; diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index ac56e0f9e..3ec3135e1 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -124,7 +124,7 @@ typedef struct { // Gamepad data int gamepadStreamFd[MAX_GAMEPADS]; // Gamepad device file descriptor - int gamepadAbsAxisRange[MAX_GAMEPADS][MAX_GAMEPAD_AXES][2]; // [0] = min, [1] = range value of the axis + int gamepadAbsAxisRange[MAX_GAMEPADS][MAX_GAMEPAD_AXES][2]; // [0] = min, [1] = range value of the axes int gamepadAbsAxisMap[MAX_GAMEPADS][ABS_CNT]; // Maps the axes gamepads from the evdev api to a sequential one int gamepadCount; // The number of gamepads registered } PlatformData; @@ -1460,7 +1460,7 @@ static void ConfigureEvdevDevice(char *device) // matter if we support them else if (hasAbsXY && TEST_BIT(keyBits, BTN_MOUSE)) isMouse = true; - // If any of the common joystick axis is present, we assume it's a gamepad + // If any of the common joystick axes are present, we assume it's a gamepad else { for (int axis = (hasAbsXY? ABS_Z : ABS_X); axis < ABS_PRESSURE; axis++) @@ -1546,7 +1546,7 @@ static void ConfigureEvdevDevice(char *device) if (absAxisCount > 0) { // TODO / NOTE - // So gamepad axis (as in the actual linux joydev.c) are just simply enumerated + // So gamepad axes (as in the actual linux joydev.c) are just simply enumerated // and (at least for some input drivers like xpat) it's convention to use // ABS_X, ABX_Y for one joystick ABS_RX, ABS_RY for the other and the Z axes for the // shoulder buttons diff --git a/src/raylib.h b/src/raylib.h index 563156525..8e5de3aa6 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -743,7 +743,7 @@ typedef enum { GAMEPAD_BUTTON_RIGHT_THUMB // Gamepad joystick pressed button right } GamepadButton; -// Gamepad axis +// Gamepad axes typedef enum { GAMEPAD_AXIS_LEFT_X = 0, // Gamepad left stick X axis GAMEPAD_AXIS_LEFT_Y = 1, // Gamepad left stick Y axis @@ -1192,8 +1192,8 @@ RLAPI bool IsGamepadButtonDown(int gamepad, int button); // Check if a game RLAPI bool IsGamepadButtonReleased(int gamepad, int button); // Check if a gamepad button has been released once RLAPI bool IsGamepadButtonUp(int gamepad, int button); // Check if a gamepad button is NOT being pressed RLAPI int GetGamepadButtonPressed(void); // Get the last gamepad button pressed -RLAPI int GetGamepadAxisCount(int gamepad); // Get gamepad axis count for a gamepad -RLAPI float GetGamepadAxisMovement(int gamepad, int axis); // Get axis movement value for a gamepad axis +RLAPI int GetGamepadAxisCount(int gamepad); // Get axis count for a gamepad +RLAPI float GetGamepadAxisMovement(int gamepad, int axis); // Get movement value for a gamepad axis RLAPI int SetGamepadMappings(const char *mappings); // Set internal gamepad mappings (SDL_GameControllerDB) RLAPI void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration); // Set gamepad vibration for both motors (duration in seconds) diff --git a/src/rcore.c b/src/rcore.c index bfa0cc036..f216a197a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -235,10 +235,10 @@ __declspec(dllimport) unsigned int __stdcall timeEndPeriod(unsigned int uPeriod) #define MAX_GAMEPADS 4 // Maximum number of gamepads supported #endif #ifndef MAX_GAMEPAD_NAME_LENGTH - #define MAX_GAMEPAD_NAME_LENGTH 128 // Maximum number of characters of gamepad name (byte size) + #define MAX_GAMEPAD_NAME_LENGTH 128 // Maximum number of characters in a gamepad name (byte size) #endif #ifndef MAX_GAMEPAD_AXES - #define MAX_GAMEPAD_AXES 8 // Maximum number of axis supported (per gamepad) + #define MAX_GAMEPAD_AXES 8 // Maximum number of axes supported (per gamepad) #endif #ifndef MAX_GAMEPAD_BUTTONS #define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad) @@ -354,7 +354,7 @@ typedef struct CoreData { } Touch; struct { int lastButtonPressed; // Register last gamepad button pressed - int axisCount[MAX_GAMEPADS]; // Register number of available gamepad axis + int axisCount[MAX_GAMEPADS]; // Register number of available gamepad axes bool ready[MAX_GAMEPADS]; // Flag to know if gamepad is ready char name[MAX_GAMEPADS][MAX_GAMEPAD_NAME_LENGTH]; // Gamepad name holder char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state From 8f2ecfba4db8a9a7006b6814dc2412bd8f572386 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 30 May 2025 02:04:09 +0000 Subject: [PATCH 020/242] Update raylib_api.* by CI --- parser/output/raylib_api.json | 6 +++--- parser/output/raylib_api.lua | 6 +++--- parser/output/raylib_api.txt | 6 +++--- parser/output/raylib_api.xml | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index 72a3477a4..6ef2a9157 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -2300,7 +2300,7 @@ }, { "name": "GamepadAxis", - "description": "Gamepad axis", + "description": "Gamepad axes", "values": [ { "name": "GAMEPAD_AXIS_LEFT_X", @@ -5017,7 +5017,7 @@ }, { "name": "GetGamepadAxisCount", - "description": "Get gamepad axis count for a gamepad", + "description": "Get axis count for a gamepad", "returnType": "int", "params": [ { @@ -5028,7 +5028,7 @@ }, { "name": "GetGamepadAxisMovement", - "description": "Get axis movement value for a gamepad axis", + "description": "Get movement value for a gamepad axis", "returnType": "float", "params": [ { diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index 662d45a78..e0ef9277e 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -2300,7 +2300,7 @@ return { }, { name = "GamepadAxis", - description = "Gamepad axis", + description = "Gamepad axes", values = { { name = "GAMEPAD_AXIS_LEFT_X", @@ -4426,7 +4426,7 @@ return { }, { name = "GetGamepadAxisCount", - description = "Get gamepad axis count for a gamepad", + description = "Get axis count for a gamepad", returnType = "int", params = { {type = "int", name = "gamepad"} @@ -4434,7 +4434,7 @@ return { }, { name = "GetGamepadAxisMovement", - description = "Get axis movement value for a gamepad axis", + description = "Get movement value for a gamepad axis", returnType = "float", params = { {type = "int", name = "gamepad"}, diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index c399c5291..4d83e3ca0 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -774,7 +774,7 @@ Enum 06: GamepadButton (18 values) Value[GAMEPAD_BUTTON_RIGHT_THUMB]: 17 Enum 07: GamepadAxis (6 values) Name: GamepadAxis - Description: Gamepad axis + Description: Gamepad axes Value[GAMEPAD_AXIS_LEFT_X]: 0 Value[GAMEPAD_AXIS_LEFT_Y]: 1 Value[GAMEPAD_AXIS_RIGHT_X]: 2 @@ -1943,12 +1943,12 @@ Function 176: GetGamepadButtonPressed() (0 input parameters) Function 177: GetGamepadAxisCount() (1 input parameters) Name: GetGamepadAxisCount Return type: int - Description: Get gamepad axis count for a gamepad + Description: Get axis count for a gamepad Param[1]: gamepad (type: int) Function 178: GetGamepadAxisMovement() (2 input parameters) Name: GetGamepadAxisMovement Return type: float - Description: Get axis movement value for a gamepad axis + Description: Get movement value for a gamepad axis Param[1]: gamepad (type: int) Param[2]: axis (type: int) Function 179: SetGamepadMappings() (1 input parameters) diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index 511d5d121..b761f8b56 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -486,7 +486,7 @@ - + @@ -1216,10 +1216,10 @@ - + - + From b26f6d34baccd479a4a41a8348debd75d434b646 Mon Sep 17 00:00:00 2001 From: Nikolas Date: Fri, 30 May 2025 14:30:24 +0200 Subject: [PATCH 021/242] Allow passing options to raygui in build.zig --- build.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.zig b/build.zig index 0b086dc4d..e445bf576 100644 --- a/build.zig +++ b/build.zig @@ -366,8 +366,8 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. return raylib; } -pub fn addRaygui(b: *std.Build, raylib: *std.Build.Step.Compile, raygui_dep: *std.Build.Dependency) void { - const raylib_dep = b.dependencyFromBuildZig(@This(), .{}); +pub fn addRaygui(b: *std.Build, raylib: *std.Build.Step.Compile, raygui_dep: *std.Build.Dependency, options: Options) void { + const raylib_dep = b.dependencyFromBuildZig(@This(), options); var gen_step = b.addWriteFiles(); raylib.step.dependOn(&gen_step.step); From bc2b2864e08407ca0eefab7ab6d05d000c0c0186 Mon Sep 17 00:00:00 2001 From: M374LX Date: Sat, 31 May 2025 14:24:38 -0300 Subject: [PATCH 022/242] RGFW: fix Escape always closing the window --- src/platforms/rcore_desktop_rgfw.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 1c4f606a7..8dc1cf54c 100644 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -1320,6 +1320,13 @@ int InitPlatform(void) platform.window = RGFW_createWindow(CORE.Window.title, RGFW_RECT(0, 0, CORE.Window.screen.width, CORE.Window.screen.height), flags); platform.mon.mode.area.w = 0; + if (platform.window != NULL) + { + // NOTE: RGFW's exit key is distinct from raylib's exit key (which can + // be set with SetExitKey()) and defaults to Escape + platform.window->exitKey = RGFW_keyNULL; + } + #ifndef PLATFORM_WEB_RGFW RGFW_area screenSize = RGFW_getScreenSize(); CORE.Window.display.width = screenSize.w; From 3414d96eafb639946eb352b2eb6a6d04ae3557e5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 31 May 2025 18:41:49 +0000 Subject: [PATCH 023/242] Update raylib_api.* by CI --- parser/output/raylib_api.json | 46 +++ parser/output/raylib_api.lua | 22 + parser/output/raylib_api.txt | 728 +++++++++++++++++----------------- parser/output/raylib_api.xml | 14 +- 4 files changed, 453 insertions(+), 357 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index 6ef2a9157..efbac71b0 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -5702,6 +5702,29 @@ } ] }, + { + "name": "DrawEllipseV", + "description": "Draw ellipse (Vector version)", + "returnType": "void", + "params": [ + { + "type": "Vector2", + "name": "center" + }, + { + "type": "float", + "name": "radiusH" + }, + { + "type": "float", + "name": "radiusV" + }, + { + "type": "Color", + "name": "color" + } + ] + }, { "name": "DrawEllipseLines", "description": "Draw ellipse outline", @@ -5729,6 +5752,29 @@ } ] }, + { + "name": "DrawEllipseLinesV", + "description": "Draw ellipse outline (Vector version)", + "returnType": "void", + "params": [ + { + "type": "Vector2", + "name": "center" + }, + { + "type": "float", + "name": "radiusH" + }, + { + "type": "float", + "name": "radiusV" + }, + { + "type": "Color", + "name": "color" + } + ] + }, { "name": "DrawRing", "description": "Draw ring", diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index e0ef9277e..08615aa22 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -4838,6 +4838,17 @@ return { {type = "Color", name = "color"} } }, + { + name = "DrawEllipseV", + description = "Draw ellipse (Vector version)", + returnType = "void", + params = { + {type = "Vector2", name = "center"}, + {type = "float", name = "radiusH"}, + {type = "float", name = "radiusV"}, + {type = "Color", name = "color"} + } + }, { name = "DrawEllipseLines", description = "Draw ellipse outline", @@ -4850,6 +4861,17 @@ return { {type = "Color", name = "color"} } }, + { + name = "DrawEllipseLinesV", + description = "Draw ellipse outline (Vector version)", + returnType = "void", + params = { + {type = "Vector2", name = "center"}, + {type = "float", name = "radiusH"}, + {type = "float", name = "radiusV"}, + {type = "Color", name = "color"} + } + }, { name = "DrawRing", description = "Draw ring", diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index 4d83e3ca0..2f44d94cc 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -993,7 +993,7 @@ Callback 006: AudioCallback() (2 input parameters) Param[1]: bufferData (type: void *) Param[2]: frames (type: unsigned int) -Functions found: 582 +Functions found: 584 Function 001: InitWindow() (3 input parameters) Name: InitWindow @@ -2252,7 +2252,15 @@ Function 227: DrawEllipse() (5 input parameters) Param[3]: radiusH (type: float) Param[4]: radiusV (type: float) Param[5]: color (type: Color) -Function 228: DrawEllipseLines() (5 input parameters) +Function 228: DrawEllipseV() (4 input parameters) + Name: DrawEllipseV + Return type: void + Description: Draw ellipse (Vector version) + Param[1]: center (type: Vector2) + Param[2]: radiusH (type: float) + Param[3]: radiusV (type: float) + Param[4]: color (type: Color) +Function 229: DrawEllipseLines() (5 input parameters) Name: DrawEllipseLines Return type: void Description: Draw ellipse outline @@ -2261,7 +2269,15 @@ Function 228: DrawEllipseLines() (5 input parameters) Param[3]: radiusH (type: float) Param[4]: radiusV (type: float) Param[5]: color (type: Color) -Function 229: DrawRing() (7 input parameters) +Function 230: DrawEllipseLinesV() (4 input parameters) + Name: DrawEllipseLinesV + Return type: void + Description: Draw ellipse outline (Vector version) + Param[1]: center (type: Vector2) + Param[2]: radiusH (type: float) + Param[3]: radiusV (type: float) + Param[4]: color (type: Color) +Function 231: DrawRing() (7 input parameters) Name: DrawRing Return type: void Description: Draw ring @@ -2272,7 +2288,7 @@ Function 229: DrawRing() (7 input parameters) Param[5]: endAngle (type: float) Param[6]: segments (type: int) Param[7]: color (type: Color) -Function 230: DrawRingLines() (7 input parameters) +Function 232: DrawRingLines() (7 input parameters) Name: DrawRingLines Return type: void Description: Draw ring outline @@ -2283,7 +2299,7 @@ Function 230: DrawRingLines() (7 input parameters) Param[5]: endAngle (type: float) Param[6]: segments (type: int) Param[7]: color (type: Color) -Function 231: DrawRectangle() (5 input parameters) +Function 233: DrawRectangle() (5 input parameters) Name: DrawRectangle Return type: void Description: Draw a color-filled rectangle @@ -2292,20 +2308,20 @@ Function 231: DrawRectangle() (5 input parameters) Param[3]: width (type: int) Param[4]: height (type: int) Param[5]: color (type: Color) -Function 232: DrawRectangleV() (3 input parameters) +Function 234: DrawRectangleV() (3 input parameters) Name: DrawRectangleV Return type: void Description: Draw a color-filled rectangle (Vector version) Param[1]: position (type: Vector2) Param[2]: size (type: Vector2) Param[3]: color (type: Color) -Function 233: DrawRectangleRec() (2 input parameters) +Function 235: DrawRectangleRec() (2 input parameters) Name: DrawRectangleRec Return type: void Description: Draw a color-filled rectangle Param[1]: rec (type: Rectangle) Param[2]: color (type: Color) -Function 234: DrawRectanglePro() (4 input parameters) +Function 236: DrawRectanglePro() (4 input parameters) Name: DrawRectanglePro Return type: void Description: Draw a color-filled rectangle with pro parameters @@ -2313,7 +2329,7 @@ Function 234: DrawRectanglePro() (4 input parameters) Param[2]: origin (type: Vector2) Param[3]: rotation (type: float) Param[4]: color (type: Color) -Function 235: DrawRectangleGradientV() (6 input parameters) +Function 237: DrawRectangleGradientV() (6 input parameters) Name: DrawRectangleGradientV Return type: void Description: Draw a vertical-gradient-filled rectangle @@ -2323,7 +2339,7 @@ Function 235: DrawRectangleGradientV() (6 input parameters) Param[4]: height (type: int) Param[5]: top (type: Color) Param[6]: bottom (type: Color) -Function 236: DrawRectangleGradientH() (6 input parameters) +Function 238: DrawRectangleGradientH() (6 input parameters) Name: DrawRectangleGradientH Return type: void Description: Draw a horizontal-gradient-filled rectangle @@ -2333,7 +2349,7 @@ Function 236: DrawRectangleGradientH() (6 input parameters) Param[4]: height (type: int) Param[5]: left (type: Color) Param[6]: right (type: Color) -Function 237: DrawRectangleGradientEx() (5 input parameters) +Function 239: DrawRectangleGradientEx() (5 input parameters) Name: DrawRectangleGradientEx Return type: void Description: Draw a gradient-filled rectangle with custom vertex colors @@ -2342,7 +2358,7 @@ Function 237: DrawRectangleGradientEx() (5 input parameters) Param[3]: bottomLeft (type: Color) Param[4]: topRight (type: Color) Param[5]: bottomRight (type: Color) -Function 238: DrawRectangleLines() (5 input parameters) +Function 240: DrawRectangleLines() (5 input parameters) Name: DrawRectangleLines Return type: void Description: Draw rectangle outline @@ -2351,14 +2367,14 @@ Function 238: DrawRectangleLines() (5 input parameters) Param[3]: width (type: int) Param[4]: height (type: int) Param[5]: color (type: Color) -Function 239: DrawRectangleLinesEx() (3 input parameters) +Function 241: DrawRectangleLinesEx() (3 input parameters) Name: DrawRectangleLinesEx Return type: void Description: Draw rectangle outline with extended parameters Param[1]: rec (type: Rectangle) Param[2]: lineThick (type: float) Param[3]: color (type: Color) -Function 240: DrawRectangleRounded() (4 input parameters) +Function 242: DrawRectangleRounded() (4 input parameters) Name: DrawRectangleRounded Return type: void Description: Draw rectangle with rounded edges @@ -2366,7 +2382,7 @@ Function 240: DrawRectangleRounded() (4 input parameters) Param[2]: roundness (type: float) Param[3]: segments (type: int) Param[4]: color (type: Color) -Function 241: DrawRectangleRoundedLines() (4 input parameters) +Function 243: DrawRectangleRoundedLines() (4 input parameters) Name: DrawRectangleRoundedLines Return type: void Description: Draw rectangle lines with rounded edges @@ -2374,7 +2390,7 @@ Function 241: DrawRectangleRoundedLines() (4 input parameters) Param[2]: roundness (type: float) Param[3]: segments (type: int) Param[4]: color (type: Color) -Function 242: DrawRectangleRoundedLinesEx() (5 input parameters) +Function 244: DrawRectangleRoundedLinesEx() (5 input parameters) Name: DrawRectangleRoundedLinesEx Return type: void Description: Draw rectangle with rounded edges outline @@ -2383,7 +2399,7 @@ Function 242: DrawRectangleRoundedLinesEx() (5 input parameters) Param[3]: segments (type: int) Param[4]: lineThick (type: float) Param[5]: color (type: Color) -Function 243: DrawTriangle() (4 input parameters) +Function 245: DrawTriangle() (4 input parameters) Name: DrawTriangle Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -2391,7 +2407,7 @@ Function 243: DrawTriangle() (4 input parameters) Param[2]: v2 (type: Vector2) Param[3]: v3 (type: Vector2) Param[4]: color (type: Color) -Function 244: DrawTriangleLines() (4 input parameters) +Function 246: DrawTriangleLines() (4 input parameters) Name: DrawTriangleLines Return type: void Description: Draw triangle outline (vertex in counter-clockwise order!) @@ -2399,21 +2415,21 @@ Function 244: DrawTriangleLines() (4 input parameters) Param[2]: v2 (type: Vector2) Param[3]: v3 (type: Vector2) Param[4]: color (type: Color) -Function 245: DrawTriangleFan() (3 input parameters) +Function 247: DrawTriangleFan() (3 input parameters) Name: DrawTriangleFan Return type: void Description: Draw a triangle fan defined by points (first vertex is the center) Param[1]: points (type: const Vector2 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 246: DrawTriangleStrip() (3 input parameters) +Function 248: DrawTriangleStrip() (3 input parameters) Name: DrawTriangleStrip Return type: void Description: Draw a triangle strip defined by points Param[1]: points (type: const Vector2 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 247: DrawPoly() (5 input parameters) +Function 249: DrawPoly() (5 input parameters) Name: DrawPoly Return type: void Description: Draw a regular polygon (Vector version) @@ -2422,7 +2438,7 @@ Function 247: DrawPoly() (5 input parameters) Param[3]: radius (type: float) Param[4]: rotation (type: float) Param[5]: color (type: Color) -Function 248: DrawPolyLines() (5 input parameters) +Function 250: DrawPolyLines() (5 input parameters) Name: DrawPolyLines Return type: void Description: Draw a polygon outline of n sides @@ -2431,7 +2447,7 @@ Function 248: DrawPolyLines() (5 input parameters) Param[3]: radius (type: float) Param[4]: rotation (type: float) Param[5]: color (type: Color) -Function 249: DrawPolyLinesEx() (6 input parameters) +Function 251: DrawPolyLinesEx() (6 input parameters) Name: DrawPolyLinesEx Return type: void Description: Draw a polygon outline of n sides with extended parameters @@ -2441,7 +2457,7 @@ Function 249: DrawPolyLinesEx() (6 input parameters) Param[4]: rotation (type: float) Param[5]: lineThick (type: float) Param[6]: color (type: Color) -Function 250: DrawSplineLinear() (4 input parameters) +Function 252: DrawSplineLinear() (4 input parameters) Name: DrawSplineLinear Return type: void Description: Draw spline: Linear, minimum 2 points @@ -2449,7 +2465,7 @@ Function 250: DrawSplineLinear() (4 input parameters) Param[2]: pointCount (type: int) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 251: DrawSplineBasis() (4 input parameters) +Function 253: DrawSplineBasis() (4 input parameters) Name: DrawSplineBasis Return type: void Description: Draw spline: B-Spline, minimum 4 points @@ -2457,7 +2473,7 @@ Function 251: DrawSplineBasis() (4 input parameters) Param[2]: pointCount (type: int) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 252: DrawSplineCatmullRom() (4 input parameters) +Function 254: DrawSplineCatmullRom() (4 input parameters) Name: DrawSplineCatmullRom Return type: void Description: Draw spline: Catmull-Rom, minimum 4 points @@ -2465,7 +2481,7 @@ Function 252: DrawSplineCatmullRom() (4 input parameters) Param[2]: pointCount (type: int) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 253: DrawSplineBezierQuadratic() (4 input parameters) +Function 255: DrawSplineBezierQuadratic() (4 input parameters) Name: DrawSplineBezierQuadratic Return type: void Description: Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...] @@ -2473,7 +2489,7 @@ Function 253: DrawSplineBezierQuadratic() (4 input parameters) Param[2]: pointCount (type: int) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 254: DrawSplineBezierCubic() (4 input parameters) +Function 256: DrawSplineBezierCubic() (4 input parameters) Name: DrawSplineBezierCubic Return type: void Description: Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...] @@ -2481,7 +2497,7 @@ Function 254: DrawSplineBezierCubic() (4 input parameters) Param[2]: pointCount (type: int) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 255: DrawSplineSegmentLinear() (4 input parameters) +Function 257: DrawSplineSegmentLinear() (4 input parameters) Name: DrawSplineSegmentLinear Return type: void Description: Draw spline segment: Linear, 2 points @@ -2489,7 +2505,7 @@ Function 255: DrawSplineSegmentLinear() (4 input parameters) Param[2]: p2 (type: Vector2) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 256: DrawSplineSegmentBasis() (6 input parameters) +Function 258: DrawSplineSegmentBasis() (6 input parameters) Name: DrawSplineSegmentBasis Return type: void Description: Draw spline segment: B-Spline, 4 points @@ -2499,7 +2515,7 @@ Function 256: DrawSplineSegmentBasis() (6 input parameters) Param[4]: p4 (type: Vector2) Param[5]: thick (type: float) Param[6]: color (type: Color) -Function 257: DrawSplineSegmentCatmullRom() (6 input parameters) +Function 259: DrawSplineSegmentCatmullRom() (6 input parameters) Name: DrawSplineSegmentCatmullRom Return type: void Description: Draw spline segment: Catmull-Rom, 4 points @@ -2509,7 +2525,7 @@ Function 257: DrawSplineSegmentCatmullRom() (6 input parameters) Param[4]: p4 (type: Vector2) Param[5]: thick (type: float) Param[6]: color (type: Color) -Function 258: DrawSplineSegmentBezierQuadratic() (5 input parameters) +Function 260: DrawSplineSegmentBezierQuadratic() (5 input parameters) Name: DrawSplineSegmentBezierQuadratic Return type: void Description: Draw spline segment: Quadratic Bezier, 2 points, 1 control point @@ -2518,7 +2534,7 @@ Function 258: DrawSplineSegmentBezierQuadratic() (5 input parameters) Param[3]: p3 (type: Vector2) Param[4]: thick (type: float) Param[5]: color (type: Color) -Function 259: DrawSplineSegmentBezierCubic() (6 input parameters) +Function 261: DrawSplineSegmentBezierCubic() (6 input parameters) Name: DrawSplineSegmentBezierCubic Return type: void Description: Draw spline segment: Cubic Bezier, 2 points, 2 control points @@ -2528,14 +2544,14 @@ Function 259: DrawSplineSegmentBezierCubic() (6 input parameters) Param[4]: p4 (type: Vector2) Param[5]: thick (type: float) Param[6]: color (type: Color) -Function 260: GetSplinePointLinear() (3 input parameters) +Function 262: GetSplinePointLinear() (3 input parameters) Name: GetSplinePointLinear Return type: Vector2 Description: Get (evaluate) spline point: Linear Param[1]: startPos (type: Vector2) Param[2]: endPos (type: Vector2) Param[3]: t (type: float) -Function 261: GetSplinePointBasis() (5 input parameters) +Function 263: GetSplinePointBasis() (5 input parameters) Name: GetSplinePointBasis Return type: Vector2 Description: Get (evaluate) spline point: B-Spline @@ -2544,7 +2560,7 @@ Function 261: GetSplinePointBasis() (5 input parameters) Param[3]: p3 (type: Vector2) Param[4]: p4 (type: Vector2) Param[5]: t (type: float) -Function 262: GetSplinePointCatmullRom() (5 input parameters) +Function 264: GetSplinePointCatmullRom() (5 input parameters) Name: GetSplinePointCatmullRom Return type: Vector2 Description: Get (evaluate) spline point: Catmull-Rom @@ -2553,7 +2569,7 @@ Function 262: GetSplinePointCatmullRom() (5 input parameters) Param[3]: p3 (type: Vector2) Param[4]: p4 (type: Vector2) Param[5]: t (type: float) -Function 263: GetSplinePointBezierQuad() (4 input parameters) +Function 265: GetSplinePointBezierQuad() (4 input parameters) Name: GetSplinePointBezierQuad Return type: Vector2 Description: Get (evaluate) spline point: Quadratic Bezier @@ -2561,7 +2577,7 @@ Function 263: GetSplinePointBezierQuad() (4 input parameters) Param[2]: c2 (type: Vector2) Param[3]: p3 (type: Vector2) Param[4]: t (type: float) -Function 264: GetSplinePointBezierCubic() (5 input parameters) +Function 266: GetSplinePointBezierCubic() (5 input parameters) Name: GetSplinePointBezierCubic Return type: Vector2 Description: Get (evaluate) spline point: Cubic Bezier @@ -2570,13 +2586,13 @@ Function 264: GetSplinePointBezierCubic() (5 input parameters) Param[3]: c3 (type: Vector2) Param[4]: p4 (type: Vector2) Param[5]: t (type: float) -Function 265: CheckCollisionRecs() (2 input parameters) +Function 267: CheckCollisionRecs() (2 input parameters) Name: CheckCollisionRecs Return type: bool Description: Check collision between two rectangles Param[1]: rec1 (type: Rectangle) Param[2]: rec2 (type: Rectangle) -Function 266: CheckCollisionCircles() (4 input parameters) +Function 268: CheckCollisionCircles() (4 input parameters) Name: CheckCollisionCircles Return type: bool Description: Check collision between two circles @@ -2584,14 +2600,14 @@ Function 266: CheckCollisionCircles() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector2) Param[4]: radius2 (type: float) -Function 267: CheckCollisionCircleRec() (3 input parameters) +Function 269: CheckCollisionCircleRec() (3 input parameters) Name: CheckCollisionCircleRec Return type: bool Description: Check collision between circle and rectangle Param[1]: center (type: Vector2) Param[2]: radius (type: float) Param[3]: rec (type: Rectangle) -Function 268: CheckCollisionCircleLine() (4 input parameters) +Function 270: CheckCollisionCircleLine() (4 input parameters) Name: CheckCollisionCircleLine Return type: bool Description: Check if circle collides with a line created betweeen two points [p1] and [p2] @@ -2599,20 +2615,20 @@ Function 268: CheckCollisionCircleLine() (4 input parameters) Param[2]: radius (type: float) Param[3]: p1 (type: Vector2) Param[4]: p2 (type: Vector2) -Function 269: CheckCollisionPointRec() (2 input parameters) +Function 271: CheckCollisionPointRec() (2 input parameters) Name: CheckCollisionPointRec Return type: bool Description: Check if point is inside rectangle Param[1]: point (type: Vector2) Param[2]: rec (type: Rectangle) -Function 270: CheckCollisionPointCircle() (3 input parameters) +Function 272: CheckCollisionPointCircle() (3 input parameters) Name: CheckCollisionPointCircle Return type: bool Description: Check if point is inside circle Param[1]: point (type: Vector2) Param[2]: center (type: Vector2) Param[3]: radius (type: float) -Function 271: CheckCollisionPointTriangle() (4 input parameters) +Function 273: CheckCollisionPointTriangle() (4 input parameters) Name: CheckCollisionPointTriangle Return type: bool Description: Check if point is inside a triangle @@ -2620,7 +2636,7 @@ Function 271: CheckCollisionPointTriangle() (4 input parameters) Param[2]: p1 (type: Vector2) Param[3]: p2 (type: Vector2) Param[4]: p3 (type: Vector2) -Function 272: CheckCollisionPointLine() (4 input parameters) +Function 274: CheckCollisionPointLine() (4 input parameters) Name: CheckCollisionPointLine Return type: bool Description: Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold] @@ -2628,14 +2644,14 @@ Function 272: CheckCollisionPointLine() (4 input parameters) Param[2]: p1 (type: Vector2) Param[3]: p2 (type: Vector2) Param[4]: threshold (type: int) -Function 273: CheckCollisionPointPoly() (3 input parameters) +Function 275: CheckCollisionPointPoly() (3 input parameters) Name: CheckCollisionPointPoly Return type: bool Description: Check if point is within a polygon described by array of vertices Param[1]: point (type: Vector2) Param[2]: points (type: const Vector2 *) Param[3]: pointCount (type: int) -Function 274: CheckCollisionLines() (5 input parameters) +Function 276: CheckCollisionLines() (5 input parameters) Name: CheckCollisionLines Return type: bool Description: Check the collision between two lines defined by two points each, returns collision point by reference @@ -2644,18 +2660,18 @@ Function 274: CheckCollisionLines() (5 input parameters) Param[3]: startPos2 (type: Vector2) Param[4]: endPos2 (type: Vector2) Param[5]: collisionPoint (type: Vector2 *) -Function 275: GetCollisionRec() (2 input parameters) +Function 277: GetCollisionRec() (2 input parameters) Name: GetCollisionRec Return type: Rectangle Description: Get collision rectangle for two rectangles collision Param[1]: rec1 (type: Rectangle) Param[2]: rec2 (type: Rectangle) -Function 276: LoadImage() (1 input parameters) +Function 278: LoadImage() (1 input parameters) Name: LoadImage Return type: Image Description: Load image from file into CPU memory (RAM) Param[1]: fileName (type: const char *) -Function 277: LoadImageRaw() (5 input parameters) +Function 279: LoadImageRaw() (5 input parameters) Name: LoadImageRaw Return type: Image Description: Load image from RAW file data @@ -2664,13 +2680,13 @@ Function 277: LoadImageRaw() (5 input parameters) Param[3]: height (type: int) Param[4]: format (type: int) Param[5]: headerSize (type: int) -Function 278: LoadImageAnim() (2 input parameters) +Function 280: LoadImageAnim() (2 input parameters) Name: LoadImageAnim Return type: Image Description: Load image sequence from file (frames appended to image.data) Param[1]: fileName (type: const char *) Param[2]: frames (type: int *) -Function 279: LoadImageAnimFromMemory() (4 input parameters) +Function 281: LoadImageAnimFromMemory() (4 input parameters) Name: LoadImageAnimFromMemory Return type: Image Description: Load image sequence from memory buffer @@ -2678,60 +2694,60 @@ Function 279: LoadImageAnimFromMemory() (4 input parameters) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) Param[4]: frames (type: int *) -Function 280: LoadImageFromMemory() (3 input parameters) +Function 282: LoadImageFromMemory() (3 input parameters) Name: LoadImageFromMemory Return type: Image Description: Load image from memory buffer, fileType refers to extension: i.e. '.png' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 281: LoadImageFromTexture() (1 input parameters) +Function 283: LoadImageFromTexture() (1 input parameters) Name: LoadImageFromTexture Return type: Image Description: Load image from GPU texture data Param[1]: texture (type: Texture2D) -Function 282: LoadImageFromScreen() (0 input parameters) +Function 284: LoadImageFromScreen() (0 input parameters) Name: LoadImageFromScreen Return type: Image Description: Load image from screen buffer and (screenshot) No input parameters -Function 283: IsImageValid() (1 input parameters) +Function 285: IsImageValid() (1 input parameters) Name: IsImageValid Return type: bool Description: Check if an image is valid (data and parameters) Param[1]: image (type: Image) -Function 284: UnloadImage() (1 input parameters) +Function 286: UnloadImage() (1 input parameters) Name: UnloadImage Return type: void Description: Unload image from CPU memory (RAM) Param[1]: image (type: Image) -Function 285: ExportImage() (2 input parameters) +Function 287: ExportImage() (2 input parameters) Name: ExportImage Return type: bool Description: Export image data to file, returns true on success Param[1]: image (type: Image) Param[2]: fileName (type: const char *) -Function 286: ExportImageToMemory() (3 input parameters) +Function 288: ExportImageToMemory() (3 input parameters) Name: ExportImageToMemory Return type: unsigned char * Description: Export image to memory buffer Param[1]: image (type: Image) Param[2]: fileType (type: const char *) Param[3]: fileSize (type: int *) -Function 287: ExportImageAsCode() (2 input parameters) +Function 289: ExportImageAsCode() (2 input parameters) Name: ExportImageAsCode Return type: bool Description: Export image as code file defining an array of bytes, returns true on success Param[1]: image (type: Image) Param[2]: fileName (type: const char *) -Function 288: GenImageColor() (3 input parameters) +Function 290: GenImageColor() (3 input parameters) Name: GenImageColor Return type: Image Description: Generate image: plain color Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: color (type: Color) -Function 289: GenImageGradientLinear() (5 input parameters) +Function 291: GenImageGradientLinear() (5 input parameters) Name: GenImageGradientLinear Return type: Image Description: Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient @@ -2740,7 +2756,7 @@ Function 289: GenImageGradientLinear() (5 input parameters) Param[3]: direction (type: int) Param[4]: start (type: Color) Param[5]: end (type: Color) -Function 290: GenImageGradientRadial() (5 input parameters) +Function 292: GenImageGradientRadial() (5 input parameters) Name: GenImageGradientRadial Return type: Image Description: Generate image: radial gradient @@ -2749,7 +2765,7 @@ Function 290: GenImageGradientRadial() (5 input parameters) Param[3]: density (type: float) Param[4]: inner (type: Color) Param[5]: outer (type: Color) -Function 291: GenImageGradientSquare() (5 input parameters) +Function 293: GenImageGradientSquare() (5 input parameters) Name: GenImageGradientSquare Return type: Image Description: Generate image: square gradient @@ -2758,7 +2774,7 @@ Function 291: GenImageGradientSquare() (5 input parameters) Param[3]: density (type: float) Param[4]: inner (type: Color) Param[5]: outer (type: Color) -Function 292: GenImageChecked() (6 input parameters) +Function 294: GenImageChecked() (6 input parameters) Name: GenImageChecked Return type: Image Description: Generate image: checked @@ -2768,14 +2784,14 @@ Function 292: GenImageChecked() (6 input parameters) Param[4]: checksY (type: int) Param[5]: col1 (type: Color) Param[6]: col2 (type: Color) -Function 293: GenImageWhiteNoise() (3 input parameters) +Function 295: GenImageWhiteNoise() (3 input parameters) Name: GenImageWhiteNoise Return type: Image Description: Generate image: white noise Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: factor (type: float) -Function 294: GenImagePerlinNoise() (5 input parameters) +Function 296: GenImagePerlinNoise() (5 input parameters) Name: GenImagePerlinNoise Return type: Image Description: Generate image: perlin noise @@ -2784,45 +2800,45 @@ Function 294: GenImagePerlinNoise() (5 input parameters) Param[3]: offsetX (type: int) Param[4]: offsetY (type: int) Param[5]: scale (type: float) -Function 295: GenImageCellular() (3 input parameters) +Function 297: GenImageCellular() (3 input parameters) Name: GenImageCellular Return type: Image Description: Generate image: cellular algorithm, bigger tileSize means bigger cells Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: tileSize (type: int) -Function 296: GenImageText() (3 input parameters) +Function 298: GenImageText() (3 input parameters) Name: GenImageText Return type: Image Description: Generate image: grayscale image from text data Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: text (type: const char *) -Function 297: ImageCopy() (1 input parameters) +Function 299: ImageCopy() (1 input parameters) Name: ImageCopy Return type: Image Description: Create an image duplicate (useful for transformations) Param[1]: image (type: Image) -Function 298: ImageFromImage() (2 input parameters) +Function 300: ImageFromImage() (2 input parameters) Name: ImageFromImage Return type: Image Description: Create an image from another image piece Param[1]: image (type: Image) Param[2]: rec (type: Rectangle) -Function 299: ImageFromChannel() (2 input parameters) +Function 301: ImageFromChannel() (2 input parameters) Name: ImageFromChannel Return type: Image Description: Create an image from a selected channel of another image (GRAYSCALE) Param[1]: image (type: Image) Param[2]: selectedChannel (type: int) -Function 300: ImageText() (3 input parameters) +Function 302: ImageText() (3 input parameters) Name: ImageText Return type: Image Description: Create an image from text (default font) Param[1]: text (type: const char *) Param[2]: fontSize (type: int) Param[3]: color (type: Color) -Function 301: ImageTextEx() (5 input parameters) +Function 303: ImageTextEx() (5 input parameters) Name: ImageTextEx Return type: Image Description: Create an image from text (custom sprite font) @@ -2831,76 +2847,76 @@ Function 301: ImageTextEx() (5 input parameters) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) Param[5]: tint (type: Color) -Function 302: ImageFormat() (2 input parameters) +Function 304: ImageFormat() (2 input parameters) Name: ImageFormat Return type: void Description: Convert image data to desired format Param[1]: image (type: Image *) Param[2]: newFormat (type: int) -Function 303: ImageToPOT() (2 input parameters) +Function 305: ImageToPOT() (2 input parameters) Name: ImageToPOT Return type: void Description: Convert image to POT (power-of-two) Param[1]: image (type: Image *) Param[2]: fill (type: Color) -Function 304: ImageCrop() (2 input parameters) +Function 306: ImageCrop() (2 input parameters) Name: ImageCrop Return type: void Description: Crop an image to a defined rectangle Param[1]: image (type: Image *) Param[2]: crop (type: Rectangle) -Function 305: ImageAlphaCrop() (2 input parameters) +Function 307: ImageAlphaCrop() (2 input parameters) Name: ImageAlphaCrop Return type: void Description: Crop image depending on alpha value Param[1]: image (type: Image *) Param[2]: threshold (type: float) -Function 306: ImageAlphaClear() (3 input parameters) +Function 308: ImageAlphaClear() (3 input parameters) Name: ImageAlphaClear Return type: void Description: Clear alpha channel to desired color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: threshold (type: float) -Function 307: ImageAlphaMask() (2 input parameters) +Function 309: ImageAlphaMask() (2 input parameters) Name: ImageAlphaMask Return type: void Description: Apply alpha mask to image Param[1]: image (type: Image *) Param[2]: alphaMask (type: Image) -Function 308: ImageAlphaPremultiply() (1 input parameters) +Function 310: ImageAlphaPremultiply() (1 input parameters) Name: ImageAlphaPremultiply Return type: void Description: Premultiply alpha channel Param[1]: image (type: Image *) -Function 309: ImageBlurGaussian() (2 input parameters) +Function 311: ImageBlurGaussian() (2 input parameters) Name: ImageBlurGaussian Return type: void Description: Apply Gaussian blur using a box blur approximation Param[1]: image (type: Image *) Param[2]: blurSize (type: int) -Function 310: ImageKernelConvolution() (3 input parameters) +Function 312: ImageKernelConvolution() (3 input parameters) Name: ImageKernelConvolution Return type: void Description: Apply custom square convolution kernel to image Param[1]: image (type: Image *) Param[2]: kernel (type: const float *) Param[3]: kernelSize (type: int) -Function 311: ImageResize() (3 input parameters) +Function 313: ImageResize() (3 input parameters) Name: ImageResize Return type: void Description: Resize image (Bicubic scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 312: ImageResizeNN() (3 input parameters) +Function 314: ImageResizeNN() (3 input parameters) Name: ImageResizeNN Return type: void Description: Resize image (Nearest-Neighbor scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 313: ImageResizeCanvas() (6 input parameters) +Function 315: ImageResizeCanvas() (6 input parameters) Name: ImageResizeCanvas Return type: void Description: Resize canvas and fill with color @@ -2910,12 +2926,12 @@ Function 313: ImageResizeCanvas() (6 input parameters) Param[4]: offsetX (type: int) Param[5]: offsetY (type: int) Param[6]: fill (type: Color) -Function 314: ImageMipmaps() (1 input parameters) +Function 316: ImageMipmaps() (1 input parameters) Name: ImageMipmaps Return type: void Description: Compute all mipmap levels for a provided image Param[1]: image (type: Image *) -Function 315: ImageDither() (5 input parameters) +Function 317: ImageDither() (5 input parameters) Name: ImageDither Return type: void Description: Dither image data to 16bpp or lower (Floyd-Steinberg dithering) @@ -2924,109 +2940,109 @@ Function 315: ImageDither() (5 input parameters) Param[3]: gBpp (type: int) Param[4]: bBpp (type: int) Param[5]: aBpp (type: int) -Function 316: ImageFlipVertical() (1 input parameters) +Function 318: ImageFlipVertical() (1 input parameters) Name: ImageFlipVertical Return type: void Description: Flip image vertically Param[1]: image (type: Image *) -Function 317: ImageFlipHorizontal() (1 input parameters) +Function 319: ImageFlipHorizontal() (1 input parameters) Name: ImageFlipHorizontal Return type: void Description: Flip image horizontally Param[1]: image (type: Image *) -Function 318: ImageRotate() (2 input parameters) +Function 320: ImageRotate() (2 input parameters) Name: ImageRotate Return type: void Description: Rotate image by input angle in degrees (-359 to 359) Param[1]: image (type: Image *) Param[2]: degrees (type: int) -Function 319: ImageRotateCW() (1 input parameters) +Function 321: ImageRotateCW() (1 input parameters) Name: ImageRotateCW Return type: void Description: Rotate image clockwise 90deg Param[1]: image (type: Image *) -Function 320: ImageRotateCCW() (1 input parameters) +Function 322: ImageRotateCCW() (1 input parameters) Name: ImageRotateCCW Return type: void Description: Rotate image counter-clockwise 90deg Param[1]: image (type: Image *) -Function 321: ImageColorTint() (2 input parameters) +Function 323: ImageColorTint() (2 input parameters) Name: ImageColorTint Return type: void Description: Modify image color: tint Param[1]: image (type: Image *) Param[2]: color (type: Color) -Function 322: ImageColorInvert() (1 input parameters) +Function 324: ImageColorInvert() (1 input parameters) Name: ImageColorInvert Return type: void Description: Modify image color: invert Param[1]: image (type: Image *) -Function 323: ImageColorGrayscale() (1 input parameters) +Function 325: ImageColorGrayscale() (1 input parameters) Name: ImageColorGrayscale Return type: void Description: Modify image color: grayscale Param[1]: image (type: Image *) -Function 324: ImageColorContrast() (2 input parameters) +Function 326: ImageColorContrast() (2 input parameters) Name: ImageColorContrast Return type: void Description: Modify image color: contrast (-100 to 100) Param[1]: image (type: Image *) Param[2]: contrast (type: float) -Function 325: ImageColorBrightness() (2 input parameters) +Function 327: ImageColorBrightness() (2 input parameters) Name: ImageColorBrightness Return type: void Description: Modify image color: brightness (-255 to 255) Param[1]: image (type: Image *) Param[2]: brightness (type: int) -Function 326: ImageColorReplace() (3 input parameters) +Function 328: ImageColorReplace() (3 input parameters) Name: ImageColorReplace Return type: void Description: Modify image color: replace color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: replace (type: Color) -Function 327: LoadImageColors() (1 input parameters) +Function 329: LoadImageColors() (1 input parameters) Name: LoadImageColors Return type: Color * Description: Load color data from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) -Function 328: LoadImagePalette() (3 input parameters) +Function 330: LoadImagePalette() (3 input parameters) Name: LoadImagePalette Return type: Color * Description: Load colors palette from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) Param[2]: maxPaletteSize (type: int) Param[3]: colorCount (type: int *) -Function 329: UnloadImageColors() (1 input parameters) +Function 331: UnloadImageColors() (1 input parameters) Name: UnloadImageColors Return type: void Description: Unload color data loaded with LoadImageColors() Param[1]: colors (type: Color *) -Function 330: UnloadImagePalette() (1 input parameters) +Function 332: UnloadImagePalette() (1 input parameters) Name: UnloadImagePalette Return type: void Description: Unload colors palette loaded with LoadImagePalette() Param[1]: colors (type: Color *) -Function 331: GetImageAlphaBorder() (2 input parameters) +Function 333: GetImageAlphaBorder() (2 input parameters) Name: GetImageAlphaBorder Return type: Rectangle Description: Get image alpha border rectangle Param[1]: image (type: Image) Param[2]: threshold (type: float) -Function 332: GetImageColor() (3 input parameters) +Function 334: GetImageColor() (3 input parameters) Name: GetImageColor Return type: Color Description: Get image pixel color at (x, y) position Param[1]: image (type: Image) Param[2]: x (type: int) Param[3]: y (type: int) -Function 333: ImageClearBackground() (2 input parameters) +Function 335: ImageClearBackground() (2 input parameters) Name: ImageClearBackground Return type: void Description: Clear image background with given color Param[1]: dst (type: Image *) Param[2]: color (type: Color) -Function 334: ImageDrawPixel() (4 input parameters) +Function 336: ImageDrawPixel() (4 input parameters) Name: ImageDrawPixel Return type: void Description: Draw pixel within an image @@ -3034,14 +3050,14 @@ Function 334: ImageDrawPixel() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: color (type: Color) -Function 335: ImageDrawPixelV() (3 input parameters) +Function 337: ImageDrawPixelV() (3 input parameters) Name: ImageDrawPixelV Return type: void Description: Draw pixel within an image (Vector version) Param[1]: dst (type: Image *) Param[2]: position (type: Vector2) Param[3]: color (type: Color) -Function 336: ImageDrawLine() (6 input parameters) +Function 338: ImageDrawLine() (6 input parameters) Name: ImageDrawLine Return type: void Description: Draw line within an image @@ -3051,7 +3067,7 @@ Function 336: ImageDrawLine() (6 input parameters) Param[4]: endPosX (type: int) Param[5]: endPosY (type: int) Param[6]: color (type: Color) -Function 337: ImageDrawLineV() (4 input parameters) +Function 339: ImageDrawLineV() (4 input parameters) Name: ImageDrawLineV Return type: void Description: Draw line within an image (Vector version) @@ -3059,7 +3075,7 @@ Function 337: ImageDrawLineV() (4 input parameters) Param[2]: start (type: Vector2) Param[3]: end (type: Vector2) Param[4]: color (type: Color) -Function 338: ImageDrawLineEx() (5 input parameters) +Function 340: ImageDrawLineEx() (5 input parameters) Name: ImageDrawLineEx Return type: void Description: Draw a line defining thickness within an image @@ -3068,7 +3084,7 @@ Function 338: ImageDrawLineEx() (5 input parameters) Param[3]: end (type: Vector2) Param[4]: thick (type: int) Param[5]: color (type: Color) -Function 339: ImageDrawCircle() (5 input parameters) +Function 341: ImageDrawCircle() (5 input parameters) Name: ImageDrawCircle Return type: void Description: Draw a filled circle within an image @@ -3077,7 +3093,7 @@ Function 339: ImageDrawCircle() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 340: ImageDrawCircleV() (4 input parameters) +Function 342: ImageDrawCircleV() (4 input parameters) Name: ImageDrawCircleV Return type: void Description: Draw a filled circle within an image (Vector version) @@ -3085,7 +3101,7 @@ Function 340: ImageDrawCircleV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 341: ImageDrawCircleLines() (5 input parameters) +Function 343: ImageDrawCircleLines() (5 input parameters) Name: ImageDrawCircleLines Return type: void Description: Draw circle outline within an image @@ -3094,7 +3110,7 @@ Function 341: ImageDrawCircleLines() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 342: ImageDrawCircleLinesV() (4 input parameters) +Function 344: ImageDrawCircleLinesV() (4 input parameters) Name: ImageDrawCircleLinesV Return type: void Description: Draw circle outline within an image (Vector version) @@ -3102,7 +3118,7 @@ Function 342: ImageDrawCircleLinesV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 343: ImageDrawRectangle() (6 input parameters) +Function 345: ImageDrawRectangle() (6 input parameters) Name: ImageDrawRectangle Return type: void Description: Draw rectangle within an image @@ -3112,7 +3128,7 @@ Function 343: ImageDrawRectangle() (6 input parameters) Param[4]: width (type: int) Param[5]: height (type: int) Param[6]: color (type: Color) -Function 344: ImageDrawRectangleV() (4 input parameters) +Function 346: ImageDrawRectangleV() (4 input parameters) Name: ImageDrawRectangleV Return type: void Description: Draw rectangle within an image (Vector version) @@ -3120,14 +3136,14 @@ Function 344: ImageDrawRectangleV() (4 input parameters) Param[2]: position (type: Vector2) Param[3]: size (type: Vector2) Param[4]: color (type: Color) -Function 345: ImageDrawRectangleRec() (3 input parameters) +Function 347: ImageDrawRectangleRec() (3 input parameters) Name: ImageDrawRectangleRec Return type: void Description: Draw rectangle within an image Param[1]: dst (type: Image *) Param[2]: rec (type: Rectangle) Param[3]: color (type: Color) -Function 346: ImageDrawRectangleLines() (4 input parameters) +Function 348: ImageDrawRectangleLines() (4 input parameters) Name: ImageDrawRectangleLines Return type: void Description: Draw rectangle lines within an image @@ -3135,7 +3151,7 @@ Function 346: ImageDrawRectangleLines() (4 input parameters) Param[2]: rec (type: Rectangle) Param[3]: thick (type: int) Param[4]: color (type: Color) -Function 347: ImageDrawTriangle() (5 input parameters) +Function 349: ImageDrawTriangle() (5 input parameters) Name: ImageDrawTriangle Return type: void Description: Draw triangle within an image @@ -3144,7 +3160,7 @@ Function 347: ImageDrawTriangle() (5 input parameters) Param[3]: v2 (type: Vector2) Param[4]: v3 (type: Vector2) Param[5]: color (type: Color) -Function 348: ImageDrawTriangleEx() (7 input parameters) +Function 350: ImageDrawTriangleEx() (7 input parameters) Name: ImageDrawTriangleEx Return type: void Description: Draw triangle with interpolated colors within an image @@ -3155,7 +3171,7 @@ Function 348: ImageDrawTriangleEx() (7 input parameters) Param[5]: c1 (type: Color) Param[6]: c2 (type: Color) Param[7]: c3 (type: Color) -Function 349: ImageDrawTriangleLines() (5 input parameters) +Function 351: ImageDrawTriangleLines() (5 input parameters) Name: ImageDrawTriangleLines Return type: void Description: Draw triangle outline within an image @@ -3164,7 +3180,7 @@ Function 349: ImageDrawTriangleLines() (5 input parameters) Param[3]: v2 (type: Vector2) Param[4]: v3 (type: Vector2) Param[5]: color (type: Color) -Function 350: ImageDrawTriangleFan() (4 input parameters) +Function 352: ImageDrawTriangleFan() (4 input parameters) Name: ImageDrawTriangleFan Return type: void Description: Draw a triangle fan defined by points within an image (first vertex is the center) @@ -3172,7 +3188,7 @@ Function 350: ImageDrawTriangleFan() (4 input parameters) Param[2]: points (type: Vector2 *) Param[3]: pointCount (type: int) Param[4]: color (type: Color) -Function 351: ImageDrawTriangleStrip() (4 input parameters) +Function 353: ImageDrawTriangleStrip() (4 input parameters) Name: ImageDrawTriangleStrip Return type: void Description: Draw a triangle strip defined by points within an image @@ -3180,7 +3196,7 @@ Function 351: ImageDrawTriangleStrip() (4 input parameters) Param[2]: points (type: Vector2 *) Param[3]: pointCount (type: int) Param[4]: color (type: Color) -Function 352: ImageDraw() (5 input parameters) +Function 354: ImageDraw() (5 input parameters) Name: ImageDraw Return type: void Description: Draw a source image within a destination image (tint applied to source) @@ -3189,7 +3205,7 @@ Function 352: ImageDraw() (5 input parameters) Param[3]: srcRec (type: Rectangle) Param[4]: dstRec (type: Rectangle) Param[5]: tint (type: Color) -Function 353: ImageDrawText() (6 input parameters) +Function 355: ImageDrawText() (6 input parameters) Name: ImageDrawText Return type: void Description: Draw text (using default font) within an image (destination) @@ -3199,7 +3215,7 @@ Function 353: ImageDrawText() (6 input parameters) Param[4]: posY (type: int) Param[5]: fontSize (type: int) Param[6]: color (type: Color) -Function 354: ImageDrawTextEx() (7 input parameters) +Function 356: ImageDrawTextEx() (7 input parameters) Name: ImageDrawTextEx Return type: void Description: Draw text (custom sprite font) within an image (destination) @@ -3210,79 +3226,79 @@ Function 354: ImageDrawTextEx() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 355: LoadTexture() (1 input parameters) +Function 357: LoadTexture() (1 input parameters) Name: LoadTexture Return type: Texture2D Description: Load texture from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 356: LoadTextureFromImage() (1 input parameters) +Function 358: LoadTextureFromImage() (1 input parameters) Name: LoadTextureFromImage Return type: Texture2D Description: Load texture from image data Param[1]: image (type: Image) -Function 357: LoadTextureCubemap() (2 input parameters) +Function 359: LoadTextureCubemap() (2 input parameters) Name: LoadTextureCubemap Return type: TextureCubemap Description: Load cubemap from image, multiple image cubemap layouts supported Param[1]: image (type: Image) Param[2]: layout (type: int) -Function 358: LoadRenderTexture() (2 input parameters) +Function 360: LoadRenderTexture() (2 input parameters) Name: LoadRenderTexture Return type: RenderTexture2D Description: Load texture for rendering (framebuffer) Param[1]: width (type: int) Param[2]: height (type: int) -Function 359: IsTextureValid() (1 input parameters) +Function 361: IsTextureValid() (1 input parameters) Name: IsTextureValid Return type: bool Description: Check if a texture is valid (loaded in GPU) Param[1]: texture (type: Texture2D) -Function 360: UnloadTexture() (1 input parameters) +Function 362: UnloadTexture() (1 input parameters) Name: UnloadTexture Return type: void Description: Unload texture from GPU memory (VRAM) Param[1]: texture (type: Texture2D) -Function 361: IsRenderTextureValid() (1 input parameters) +Function 363: IsRenderTextureValid() (1 input parameters) Name: IsRenderTextureValid Return type: bool Description: Check if a render texture is valid (loaded in GPU) Param[1]: target (type: RenderTexture2D) -Function 362: UnloadRenderTexture() (1 input parameters) +Function 364: UnloadRenderTexture() (1 input parameters) Name: UnloadRenderTexture Return type: void Description: Unload render texture from GPU memory (VRAM) Param[1]: target (type: RenderTexture2D) -Function 363: UpdateTexture() (2 input parameters) +Function 365: UpdateTexture() (2 input parameters) Name: UpdateTexture Return type: void Description: Update GPU texture with new data Param[1]: texture (type: Texture2D) Param[2]: pixels (type: const void *) -Function 364: UpdateTextureRec() (3 input parameters) +Function 366: UpdateTextureRec() (3 input parameters) Name: UpdateTextureRec Return type: void Description: Update GPU texture rectangle with new data Param[1]: texture (type: Texture2D) Param[2]: rec (type: Rectangle) Param[3]: pixels (type: const void *) -Function 365: GenTextureMipmaps() (1 input parameters) +Function 367: GenTextureMipmaps() (1 input parameters) Name: GenTextureMipmaps Return type: void Description: Generate GPU mipmaps for a texture Param[1]: texture (type: Texture2D *) -Function 366: SetTextureFilter() (2 input parameters) +Function 368: SetTextureFilter() (2 input parameters) Name: SetTextureFilter Return type: void Description: Set texture scaling filter mode Param[1]: texture (type: Texture2D) Param[2]: filter (type: int) -Function 367: SetTextureWrap() (2 input parameters) +Function 369: SetTextureWrap() (2 input parameters) Name: SetTextureWrap Return type: void Description: Set texture wrapping mode Param[1]: texture (type: Texture2D) Param[2]: wrap (type: int) -Function 368: DrawTexture() (4 input parameters) +Function 370: DrawTexture() (4 input parameters) Name: DrawTexture Return type: void Description: Draw a Texture2D @@ -3290,14 +3306,14 @@ Function 368: DrawTexture() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: tint (type: Color) -Function 369: DrawTextureV() (3 input parameters) +Function 371: DrawTextureV() (3 input parameters) Name: DrawTextureV Return type: void Description: Draw a Texture2D with position defined as Vector2 Param[1]: texture (type: Texture2D) Param[2]: position (type: Vector2) Param[3]: tint (type: Color) -Function 370: DrawTextureEx() (5 input parameters) +Function 372: DrawTextureEx() (5 input parameters) Name: DrawTextureEx Return type: void Description: Draw a Texture2D with extended parameters @@ -3306,7 +3322,7 @@ Function 370: DrawTextureEx() (5 input parameters) Param[3]: rotation (type: float) Param[4]: scale (type: float) Param[5]: tint (type: Color) -Function 371: DrawTextureRec() (4 input parameters) +Function 373: DrawTextureRec() (4 input parameters) Name: DrawTextureRec Return type: void Description: Draw a part of a texture defined by a rectangle @@ -3314,7 +3330,7 @@ Function 371: DrawTextureRec() (4 input parameters) Param[2]: source (type: Rectangle) Param[3]: position (type: Vector2) Param[4]: tint (type: Color) -Function 372: DrawTexturePro() (6 input parameters) +Function 374: DrawTexturePro() (6 input parameters) Name: DrawTexturePro Return type: void Description: Draw a part of a texture defined by a rectangle with 'pro' parameters @@ -3324,7 +3340,7 @@ Function 372: DrawTexturePro() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 373: DrawTextureNPatch() (6 input parameters) +Function 375: DrawTextureNPatch() (6 input parameters) Name: DrawTextureNPatch Return type: void Description: Draws a texture (or part of it) that stretches or shrinks nicely @@ -3334,119 +3350,119 @@ Function 373: DrawTextureNPatch() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 374: ColorIsEqual() (2 input parameters) +Function 376: ColorIsEqual() (2 input parameters) Name: ColorIsEqual Return type: bool Description: Check if two colors are equal Param[1]: col1 (type: Color) Param[2]: col2 (type: Color) -Function 375: Fade() (2 input parameters) +Function 377: Fade() (2 input parameters) Name: Fade Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 376: ColorToInt() (1 input parameters) +Function 378: ColorToInt() (1 input parameters) Name: ColorToInt Return type: int Description: Get hexadecimal value for a Color (0xRRGGBBAA) Param[1]: color (type: Color) -Function 377: ColorNormalize() (1 input parameters) +Function 379: ColorNormalize() (1 input parameters) Name: ColorNormalize Return type: Vector4 Description: Get Color normalized as float [0..1] Param[1]: color (type: Color) -Function 378: ColorFromNormalized() (1 input parameters) +Function 380: ColorFromNormalized() (1 input parameters) Name: ColorFromNormalized Return type: Color Description: Get Color from normalized values [0..1] Param[1]: normalized (type: Vector4) -Function 379: ColorToHSV() (1 input parameters) +Function 381: ColorToHSV() (1 input parameters) Name: ColorToHSV Return type: Vector3 Description: Get HSV values for a Color, hue [0..360], saturation/value [0..1] Param[1]: color (type: Color) -Function 380: ColorFromHSV() (3 input parameters) +Function 382: ColorFromHSV() (3 input parameters) Name: ColorFromHSV Return type: Color Description: Get a Color from HSV values, hue [0..360], saturation/value [0..1] Param[1]: hue (type: float) Param[2]: saturation (type: float) Param[3]: value (type: float) -Function 381: ColorTint() (2 input parameters) +Function 383: ColorTint() (2 input parameters) Name: ColorTint Return type: Color Description: Get color multiplied with another color Param[1]: color (type: Color) Param[2]: tint (type: Color) -Function 382: ColorBrightness() (2 input parameters) +Function 384: ColorBrightness() (2 input parameters) Name: ColorBrightness Return type: Color Description: Get color with brightness correction, brightness factor goes from -1.0f to 1.0f Param[1]: color (type: Color) Param[2]: factor (type: float) -Function 383: ColorContrast() (2 input parameters) +Function 385: ColorContrast() (2 input parameters) Name: ColorContrast Return type: Color Description: Get color with contrast correction, contrast values between -1.0f and 1.0f Param[1]: color (type: Color) Param[2]: contrast (type: float) -Function 384: ColorAlpha() (2 input parameters) +Function 386: ColorAlpha() (2 input parameters) Name: ColorAlpha Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 385: ColorAlphaBlend() (3 input parameters) +Function 387: ColorAlphaBlend() (3 input parameters) Name: ColorAlphaBlend Return type: Color Description: Get src alpha-blended into dst color with tint Param[1]: dst (type: Color) Param[2]: src (type: Color) Param[3]: tint (type: Color) -Function 386: ColorLerp() (3 input parameters) +Function 388: ColorLerp() (3 input parameters) Name: ColorLerp Return type: Color Description: Get color lerp interpolation between two colors, factor [0.0f..1.0f] Param[1]: color1 (type: Color) Param[2]: color2 (type: Color) Param[3]: factor (type: float) -Function 387: GetColor() (1 input parameters) +Function 389: GetColor() (1 input parameters) Name: GetColor Return type: Color Description: Get Color structure from hexadecimal value Param[1]: hexValue (type: unsigned int) -Function 388: GetPixelColor() (2 input parameters) +Function 390: GetPixelColor() (2 input parameters) Name: GetPixelColor Return type: Color Description: Get Color from a source pixel pointer of certain format Param[1]: srcPtr (type: void *) Param[2]: format (type: int) -Function 389: SetPixelColor() (3 input parameters) +Function 391: SetPixelColor() (3 input parameters) Name: SetPixelColor Return type: void Description: Set color formatted into destination pixel pointer Param[1]: dstPtr (type: void *) Param[2]: color (type: Color) Param[3]: format (type: int) -Function 390: GetPixelDataSize() (3 input parameters) +Function 392: GetPixelDataSize() (3 input parameters) Name: GetPixelDataSize Return type: int Description: Get pixel data size in bytes for certain format Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: format (type: int) -Function 391: GetFontDefault() (0 input parameters) +Function 393: GetFontDefault() (0 input parameters) Name: GetFontDefault Return type: Font Description: Get the default Font No input parameters -Function 392: LoadFont() (1 input parameters) +Function 394: LoadFont() (1 input parameters) Name: LoadFont Return type: Font Description: Load font from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 393: LoadFontEx() (4 input parameters) +Function 395: LoadFontEx() (4 input parameters) Name: LoadFontEx Return type: Font Description: Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character set, font size is provided in pixels height @@ -3454,14 +3470,14 @@ Function 393: LoadFontEx() (4 input parameters) Param[2]: fontSize (type: int) Param[3]: codepoints (type: int *) Param[4]: codepointCount (type: int) -Function 394: LoadFontFromImage() (3 input parameters) +Function 396: LoadFontFromImage() (3 input parameters) Name: LoadFontFromImage Return type: Font Description: Load font from Image (XNA style) Param[1]: image (type: Image) Param[2]: key (type: Color) Param[3]: firstChar (type: int) -Function 395: LoadFontFromMemory() (6 input parameters) +Function 397: LoadFontFromMemory() (6 input parameters) Name: LoadFontFromMemory Return type: Font Description: Load font from memory buffer, fileType refers to extension: i.e. '.ttf' @@ -3471,12 +3487,12 @@ Function 395: LoadFontFromMemory() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: codepoints (type: int *) Param[6]: codepointCount (type: int) -Function 396: IsFontValid() (1 input parameters) +Function 398: IsFontValid() (1 input parameters) Name: IsFontValid Return type: bool Description: Check if a font is valid (font data loaded, WARNING: GPU texture not checked) Param[1]: font (type: Font) -Function 397: LoadFontData() (6 input parameters) +Function 399: LoadFontData() (6 input parameters) Name: LoadFontData Return type: GlyphInfo * Description: Load font data for further use @@ -3486,7 +3502,7 @@ Function 397: LoadFontData() (6 input parameters) Param[4]: codepoints (type: int *) Param[5]: codepointCount (type: int) Param[6]: type (type: int) -Function 398: GenImageFontAtlas() (6 input parameters) +Function 400: GenImageFontAtlas() (6 input parameters) Name: GenImageFontAtlas Return type: Image Description: Generate image font atlas using chars info @@ -3496,30 +3512,30 @@ Function 398: GenImageFontAtlas() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: padding (type: int) Param[6]: packMethod (type: int) -Function 399: UnloadFontData() (2 input parameters) +Function 401: UnloadFontData() (2 input parameters) Name: UnloadFontData Return type: void Description: Unload font chars info data (RAM) Param[1]: glyphs (type: GlyphInfo *) Param[2]: glyphCount (type: int) -Function 400: UnloadFont() (1 input parameters) +Function 402: UnloadFont() (1 input parameters) Name: UnloadFont Return type: void Description: Unload font from GPU memory (VRAM) Param[1]: font (type: Font) -Function 401: ExportFontAsCode() (2 input parameters) +Function 403: ExportFontAsCode() (2 input parameters) Name: ExportFontAsCode Return type: bool Description: Export font as code file, returns true on success Param[1]: font (type: Font) Param[2]: fileName (type: const char *) -Function 402: DrawFPS() (2 input parameters) +Function 404: DrawFPS() (2 input parameters) Name: DrawFPS Return type: void Description: Draw current FPS Param[1]: posX (type: int) Param[2]: posY (type: int) -Function 403: DrawText() (5 input parameters) +Function 405: DrawText() (5 input parameters) Name: DrawText Return type: void Description: Draw text (using default font) @@ -3528,7 +3544,7 @@ Function 403: DrawText() (5 input parameters) Param[3]: posY (type: int) Param[4]: fontSize (type: int) Param[5]: color (type: Color) -Function 404: DrawTextEx() (6 input parameters) +Function 406: DrawTextEx() (6 input parameters) Name: DrawTextEx Return type: void Description: Draw text using font and additional parameters @@ -3538,7 +3554,7 @@ Function 404: DrawTextEx() (6 input parameters) Param[4]: fontSize (type: float) Param[5]: spacing (type: float) Param[6]: tint (type: Color) -Function 405: DrawTextPro() (8 input parameters) +Function 407: DrawTextPro() (8 input parameters) Name: DrawTextPro Return type: void Description: Draw text using Font and pro parameters (rotation) @@ -3550,7 +3566,7 @@ Function 405: DrawTextPro() (8 input parameters) Param[6]: fontSize (type: float) Param[7]: spacing (type: float) Param[8]: tint (type: Color) -Function 406: DrawTextCodepoint() (5 input parameters) +Function 408: DrawTextCodepoint() (5 input parameters) Name: DrawTextCodepoint Return type: void Description: Draw one character (codepoint) @@ -3559,7 +3575,7 @@ Function 406: DrawTextCodepoint() (5 input parameters) Param[3]: position (type: Vector2) Param[4]: fontSize (type: float) Param[5]: tint (type: Color) -Function 407: DrawTextCodepoints() (7 input parameters) +Function 409: DrawTextCodepoints() (7 input parameters) Name: DrawTextCodepoints Return type: void Description: Draw multiple character (codepoint) @@ -3570,18 +3586,18 @@ Function 407: DrawTextCodepoints() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 408: SetTextLineSpacing() (1 input parameters) +Function 410: SetTextLineSpacing() (1 input parameters) Name: SetTextLineSpacing Return type: void Description: Set vertical line spacing when drawing with line-breaks Param[1]: spacing (type: int) -Function 409: MeasureText() (2 input parameters) +Function 411: MeasureText() (2 input parameters) Name: MeasureText Return type: int Description: Measure string width for default font Param[1]: text (type: const char *) Param[2]: fontSize (type: int) -Function 410: MeasureTextEx() (4 input parameters) +Function 412: MeasureTextEx() (4 input parameters) Name: MeasureTextEx Return type: Vector2 Description: Measure string size for Font @@ -3589,195 +3605,195 @@ Function 410: MeasureTextEx() (4 input parameters) Param[2]: text (type: const char *) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) -Function 411: GetGlyphIndex() (2 input parameters) +Function 413: GetGlyphIndex() (2 input parameters) Name: GetGlyphIndex Return type: int Description: Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 412: GetGlyphInfo() (2 input parameters) +Function 414: GetGlyphInfo() (2 input parameters) Name: GetGlyphInfo Return type: GlyphInfo Description: Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 413: GetGlyphAtlasRec() (2 input parameters) +Function 415: GetGlyphAtlasRec() (2 input parameters) Name: GetGlyphAtlasRec Return type: Rectangle Description: Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 414: LoadUTF8() (2 input parameters) +Function 416: LoadUTF8() (2 input parameters) Name: LoadUTF8 Return type: char * Description: Load UTF-8 text encoded from codepoints array Param[1]: codepoints (type: const int *) Param[2]: length (type: int) -Function 415: UnloadUTF8() (1 input parameters) +Function 417: UnloadUTF8() (1 input parameters) Name: UnloadUTF8 Return type: void Description: Unload UTF-8 text encoded from codepoints array Param[1]: text (type: char *) -Function 416: LoadCodepoints() (2 input parameters) +Function 418: LoadCodepoints() (2 input parameters) Name: LoadCodepoints Return type: int * Description: Load all codepoints from a UTF-8 text string, codepoints count returned by parameter Param[1]: text (type: const char *) Param[2]: count (type: int *) -Function 417: UnloadCodepoints() (1 input parameters) +Function 419: UnloadCodepoints() (1 input parameters) Name: UnloadCodepoints Return type: void Description: Unload codepoints data from memory Param[1]: codepoints (type: int *) -Function 418: GetCodepointCount() (1 input parameters) +Function 420: GetCodepointCount() (1 input parameters) Name: GetCodepointCount Return type: int Description: Get total number of codepoints in a UTF-8 encoded string Param[1]: text (type: const char *) -Function 419: GetCodepoint() (2 input parameters) +Function 421: GetCodepoint() (2 input parameters) Name: GetCodepoint Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 420: GetCodepointNext() (2 input parameters) +Function 422: GetCodepointNext() (2 input parameters) Name: GetCodepointNext Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 421: GetCodepointPrevious() (2 input parameters) +Function 423: GetCodepointPrevious() (2 input parameters) Name: GetCodepointPrevious Return type: int Description: Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 422: CodepointToUTF8() (2 input parameters) +Function 424: CodepointToUTF8() (2 input parameters) Name: CodepointToUTF8 Return type: const char * Description: Encode one codepoint into UTF-8 byte array (array length returned as parameter) Param[1]: codepoint (type: int) Param[2]: utf8Size (type: int *) -Function 423: TextCopy() (2 input parameters) +Function 425: TextCopy() (2 input parameters) Name: TextCopy Return type: int Description: Copy one string to another, returns bytes copied Param[1]: dst (type: char *) Param[2]: src (type: const char *) -Function 424: TextIsEqual() (2 input parameters) +Function 426: TextIsEqual() (2 input parameters) Name: TextIsEqual Return type: bool Description: Check if two text string are equal Param[1]: text1 (type: const char *) Param[2]: text2 (type: const char *) -Function 425: TextLength() (1 input parameters) +Function 427: TextLength() (1 input parameters) Name: TextLength Return type: unsigned int Description: Get text length, checks for '\0' ending Param[1]: text (type: const char *) -Function 426: TextFormat() (2 input parameters) +Function 428: TextFormat() (2 input parameters) Name: TextFormat Return type: const char * Description: Text formatting with variables (sprintf() style) Param[1]: text (type: const char *) Param[2]: args (type: ...) -Function 427: TextSubtext() (3 input parameters) +Function 429: TextSubtext() (3 input parameters) Name: TextSubtext Return type: const char * Description: Get a piece of a text string Param[1]: text (type: const char *) Param[2]: position (type: int) Param[3]: length (type: int) -Function 428: TextReplace() (3 input parameters) +Function 430: TextReplace() (3 input parameters) Name: TextReplace Return type: char * Description: Replace text string (WARNING: memory must be freed!) Param[1]: text (type: const char *) Param[2]: replace (type: const char *) Param[3]: by (type: const char *) -Function 429: TextInsert() (3 input parameters) +Function 431: TextInsert() (3 input parameters) Name: TextInsert Return type: char * Description: Insert text in a position (WARNING: memory must be freed!) Param[1]: text (type: const char *) Param[2]: insert (type: const char *) Param[3]: position (type: int) -Function 430: TextJoin() (3 input parameters) +Function 432: TextJoin() (3 input parameters) Name: TextJoin Return type: char * Description: Join text strings with delimiter Param[1]: textList (type: char **) Param[2]: count (type: int) Param[3]: delimiter (type: const char *) -Function 431: TextSplit() (3 input parameters) +Function 433: TextSplit() (3 input parameters) Name: TextSplit Return type: char ** Description: Split text into multiple strings Param[1]: text (type: const char *) Param[2]: delimiter (type: char) Param[3]: count (type: int *) -Function 432: TextAppend() (3 input parameters) +Function 434: TextAppend() (3 input parameters) Name: TextAppend Return type: void Description: Append text at specific position and move cursor! Param[1]: text (type: char *) Param[2]: append (type: const char *) Param[3]: position (type: int *) -Function 433: TextFindIndex() (2 input parameters) +Function 435: TextFindIndex() (2 input parameters) Name: TextFindIndex Return type: int Description: Find first text occurrence within a string Param[1]: text (type: const char *) Param[2]: find (type: const char *) -Function 434: TextToUpper() (1 input parameters) +Function 436: TextToUpper() (1 input parameters) Name: TextToUpper Return type: char * Description: Get upper case version of provided string Param[1]: text (type: const char *) -Function 435: TextToLower() (1 input parameters) +Function 437: TextToLower() (1 input parameters) Name: TextToLower Return type: char * Description: Get lower case version of provided string Param[1]: text (type: const char *) -Function 436: TextToPascal() (1 input parameters) +Function 438: TextToPascal() (1 input parameters) Name: TextToPascal Return type: char * Description: Get Pascal case notation version of provided string Param[1]: text (type: const char *) -Function 437: TextToSnake() (1 input parameters) +Function 439: TextToSnake() (1 input parameters) Name: TextToSnake Return type: char * Description: Get Snake case notation version of provided string Param[1]: text (type: const char *) -Function 438: TextToCamel() (1 input parameters) +Function 440: TextToCamel() (1 input parameters) Name: TextToCamel Return type: char * Description: Get Camel case notation version of provided string Param[1]: text (type: const char *) -Function 439: TextToInteger() (1 input parameters) +Function 441: TextToInteger() (1 input parameters) Name: TextToInteger Return type: int Description: Get integer value from text Param[1]: text (type: const char *) -Function 440: TextToFloat() (1 input parameters) +Function 442: TextToFloat() (1 input parameters) Name: TextToFloat Return type: float Description: Get float value from text Param[1]: text (type: const char *) -Function 441: DrawLine3D() (3 input parameters) +Function 443: DrawLine3D() (3 input parameters) Name: DrawLine3D Return type: void Description: Draw a line in 3D world space Param[1]: startPos (type: Vector3) Param[2]: endPos (type: Vector3) Param[3]: color (type: Color) -Function 442: DrawPoint3D() (2 input parameters) +Function 444: DrawPoint3D() (2 input parameters) Name: DrawPoint3D Return type: void Description: Draw a point in 3D space, actually a small line Param[1]: position (type: Vector3) Param[2]: color (type: Color) -Function 443: DrawCircle3D() (5 input parameters) +Function 445: DrawCircle3D() (5 input parameters) Name: DrawCircle3D Return type: void Description: Draw a circle in 3D world space @@ -3786,7 +3802,7 @@ Function 443: DrawCircle3D() (5 input parameters) Param[3]: rotationAxis (type: Vector3) Param[4]: rotationAngle (type: float) Param[5]: color (type: Color) -Function 444: DrawTriangle3D() (4 input parameters) +Function 446: DrawTriangle3D() (4 input parameters) Name: DrawTriangle3D Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -3794,14 +3810,14 @@ Function 444: DrawTriangle3D() (4 input parameters) Param[2]: v2 (type: Vector3) Param[3]: v3 (type: Vector3) Param[4]: color (type: Color) -Function 445: DrawTriangleStrip3D() (3 input parameters) +Function 447: DrawTriangleStrip3D() (3 input parameters) Name: DrawTriangleStrip3D Return type: void Description: Draw a triangle strip defined by points Param[1]: points (type: const Vector3 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 446: DrawCube() (5 input parameters) +Function 448: DrawCube() (5 input parameters) Name: DrawCube Return type: void Description: Draw cube @@ -3810,14 +3826,14 @@ Function 446: DrawCube() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 447: DrawCubeV() (3 input parameters) +Function 449: DrawCubeV() (3 input parameters) Name: DrawCubeV Return type: void Description: Draw cube (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 448: DrawCubeWires() (5 input parameters) +Function 450: DrawCubeWires() (5 input parameters) Name: DrawCubeWires Return type: void Description: Draw cube wires @@ -3826,21 +3842,21 @@ Function 448: DrawCubeWires() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 449: DrawCubeWiresV() (3 input parameters) +Function 451: DrawCubeWiresV() (3 input parameters) Name: DrawCubeWiresV Return type: void Description: Draw cube wires (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 450: DrawSphere() (3 input parameters) +Function 452: DrawSphere() (3 input parameters) Name: DrawSphere Return type: void Description: Draw sphere Param[1]: centerPos (type: Vector3) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 451: DrawSphereEx() (5 input parameters) +Function 453: DrawSphereEx() (5 input parameters) Name: DrawSphereEx Return type: void Description: Draw sphere with extended parameters @@ -3849,7 +3865,7 @@ Function 451: DrawSphereEx() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 452: DrawSphereWires() (5 input parameters) +Function 454: DrawSphereWires() (5 input parameters) Name: DrawSphereWires Return type: void Description: Draw sphere wires @@ -3858,7 +3874,7 @@ Function 452: DrawSphereWires() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 453: DrawCylinder() (6 input parameters) +Function 455: DrawCylinder() (6 input parameters) Name: DrawCylinder Return type: void Description: Draw a cylinder/cone @@ -3868,7 +3884,7 @@ Function 453: DrawCylinder() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 454: DrawCylinderEx() (6 input parameters) +Function 456: DrawCylinderEx() (6 input parameters) Name: DrawCylinderEx Return type: void Description: Draw a cylinder with base at startPos and top at endPos @@ -3878,7 +3894,7 @@ Function 454: DrawCylinderEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 455: DrawCylinderWires() (6 input parameters) +Function 457: DrawCylinderWires() (6 input parameters) Name: DrawCylinderWires Return type: void Description: Draw a cylinder/cone wires @@ -3888,7 +3904,7 @@ Function 455: DrawCylinderWires() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 456: DrawCylinderWiresEx() (6 input parameters) +Function 458: DrawCylinderWiresEx() (6 input parameters) Name: DrawCylinderWiresEx Return type: void Description: Draw a cylinder wires with base at startPos and top at endPos @@ -3898,7 +3914,7 @@ Function 456: DrawCylinderWiresEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 457: DrawCapsule() (6 input parameters) +Function 459: DrawCapsule() (6 input parameters) Name: DrawCapsule Return type: void Description: Draw a capsule with the center of its sphere caps at startPos and endPos @@ -3908,7 +3924,7 @@ Function 457: DrawCapsule() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 458: DrawCapsuleWires() (6 input parameters) +Function 460: DrawCapsuleWires() (6 input parameters) Name: DrawCapsuleWires Return type: void Description: Draw capsule wireframe with the center of its sphere caps at startPos and endPos @@ -3918,51 +3934,51 @@ Function 458: DrawCapsuleWires() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 459: DrawPlane() (3 input parameters) +Function 461: DrawPlane() (3 input parameters) Name: DrawPlane Return type: void Description: Draw a plane XZ Param[1]: centerPos (type: Vector3) Param[2]: size (type: Vector2) Param[3]: color (type: Color) -Function 460: DrawRay() (2 input parameters) +Function 462: DrawRay() (2 input parameters) Name: DrawRay Return type: void Description: Draw a ray line Param[1]: ray (type: Ray) Param[2]: color (type: Color) -Function 461: DrawGrid() (2 input parameters) +Function 463: DrawGrid() (2 input parameters) Name: DrawGrid Return type: void Description: Draw a grid (centered at (0, 0, 0)) Param[1]: slices (type: int) Param[2]: spacing (type: float) -Function 462: LoadModel() (1 input parameters) +Function 464: LoadModel() (1 input parameters) Name: LoadModel Return type: Model Description: Load model from files (meshes and materials) Param[1]: fileName (type: const char *) -Function 463: LoadModelFromMesh() (1 input parameters) +Function 465: LoadModelFromMesh() (1 input parameters) Name: LoadModelFromMesh Return type: Model Description: Load model from generated mesh (default material) Param[1]: mesh (type: Mesh) -Function 464: IsModelValid() (1 input parameters) +Function 466: IsModelValid() (1 input parameters) Name: IsModelValid Return type: bool Description: Check if a model is valid (loaded in GPU, VAO/VBOs) Param[1]: model (type: Model) -Function 465: UnloadModel() (1 input parameters) +Function 467: UnloadModel() (1 input parameters) Name: UnloadModel Return type: void Description: Unload model (including meshes) from memory (RAM and/or VRAM) Param[1]: model (type: Model) -Function 466: GetModelBoundingBox() (1 input parameters) +Function 468: GetModelBoundingBox() (1 input parameters) Name: GetModelBoundingBox Return type: BoundingBox Description: Compute model bounding box limits (considers all meshes) Param[1]: model (type: Model) -Function 467: DrawModel() (4 input parameters) +Function 469: DrawModel() (4 input parameters) Name: DrawModel Return type: void Description: Draw a model (with texture if set) @@ -3970,7 +3986,7 @@ Function 467: DrawModel() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 468: DrawModelEx() (6 input parameters) +Function 470: DrawModelEx() (6 input parameters) Name: DrawModelEx Return type: void Description: Draw a model with extended parameters @@ -3980,7 +3996,7 @@ Function 468: DrawModelEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 469: DrawModelWires() (4 input parameters) +Function 471: DrawModelWires() (4 input parameters) Name: DrawModelWires Return type: void Description: Draw a model wires (with texture if set) @@ -3988,7 +4004,7 @@ Function 469: DrawModelWires() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 470: DrawModelWiresEx() (6 input parameters) +Function 472: DrawModelWiresEx() (6 input parameters) Name: DrawModelWiresEx Return type: void Description: Draw a model wires (with texture if set) with extended parameters @@ -3998,7 +4014,7 @@ Function 470: DrawModelWiresEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 471: DrawModelPoints() (4 input parameters) +Function 473: DrawModelPoints() (4 input parameters) Name: DrawModelPoints Return type: void Description: Draw a model as points @@ -4006,7 +4022,7 @@ Function 471: DrawModelPoints() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 472: DrawModelPointsEx() (6 input parameters) +Function 474: DrawModelPointsEx() (6 input parameters) Name: DrawModelPointsEx Return type: void Description: Draw a model as points with extended parameters @@ -4016,13 +4032,13 @@ Function 472: DrawModelPointsEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 473: DrawBoundingBox() (2 input parameters) +Function 475: DrawBoundingBox() (2 input parameters) Name: DrawBoundingBox Return type: void Description: Draw bounding box (wires) Param[1]: box (type: BoundingBox) Param[2]: color (type: Color) -Function 474: DrawBillboard() (5 input parameters) +Function 476: DrawBillboard() (5 input parameters) Name: DrawBillboard Return type: void Description: Draw a billboard texture @@ -4031,7 +4047,7 @@ Function 474: DrawBillboard() (5 input parameters) Param[3]: position (type: Vector3) Param[4]: scale (type: float) Param[5]: tint (type: Color) -Function 475: DrawBillboardRec() (6 input parameters) +Function 477: DrawBillboardRec() (6 input parameters) Name: DrawBillboardRec Return type: void Description: Draw a billboard texture defined by source @@ -4041,7 +4057,7 @@ Function 475: DrawBillboardRec() (6 input parameters) Param[4]: position (type: Vector3) Param[5]: size (type: Vector2) Param[6]: tint (type: Color) -Function 476: DrawBillboardPro() (9 input parameters) +Function 478: DrawBillboardPro() (9 input parameters) Name: DrawBillboardPro Return type: void Description: Draw a billboard texture defined by source and rotation @@ -4054,13 +4070,13 @@ Function 476: DrawBillboardPro() (9 input parameters) Param[7]: origin (type: Vector2) Param[8]: rotation (type: float) Param[9]: tint (type: Color) -Function 477: UploadMesh() (2 input parameters) +Function 479: UploadMesh() (2 input parameters) Name: UploadMesh Return type: void Description: Upload mesh vertex data in GPU and provide VAO/VBO ids Param[1]: mesh (type: Mesh *) Param[2]: dynamic (type: bool) -Function 478: UpdateMeshBuffer() (5 input parameters) +Function 480: UpdateMeshBuffer() (5 input parameters) Name: UpdateMeshBuffer Return type: void Description: Update mesh vertex data in GPU for a specific buffer index @@ -4069,19 +4085,19 @@ Function 478: UpdateMeshBuffer() (5 input parameters) Param[3]: data (type: const void *) Param[4]: dataSize (type: int) Param[5]: offset (type: int) -Function 479: UnloadMesh() (1 input parameters) +Function 481: UnloadMesh() (1 input parameters) Name: UnloadMesh Return type: void Description: Unload mesh data from CPU and GPU Param[1]: mesh (type: Mesh) -Function 480: DrawMesh() (3 input parameters) +Function 482: DrawMesh() (3 input parameters) Name: DrawMesh Return type: void Description: Draw a 3d mesh with material and transform Param[1]: mesh (type: Mesh) Param[2]: material (type: Material) Param[3]: transform (type: Matrix) -Function 481: DrawMeshInstanced() (4 input parameters) +Function 483: DrawMeshInstanced() (4 input parameters) Name: DrawMeshInstanced Return type: void Description: Draw multiple mesh instances with material and different transforms @@ -4089,35 +4105,35 @@ Function 481: DrawMeshInstanced() (4 input parameters) Param[2]: material (type: Material) Param[3]: transforms (type: const Matrix *) Param[4]: instances (type: int) -Function 482: GetMeshBoundingBox() (1 input parameters) +Function 484: GetMeshBoundingBox() (1 input parameters) Name: GetMeshBoundingBox Return type: BoundingBox Description: Compute mesh bounding box limits Param[1]: mesh (type: Mesh) -Function 483: GenMeshTangents() (1 input parameters) +Function 485: GenMeshTangents() (1 input parameters) Name: GenMeshTangents Return type: void Description: Compute mesh tangents Param[1]: mesh (type: Mesh *) -Function 484: ExportMesh() (2 input parameters) +Function 486: ExportMesh() (2 input parameters) Name: ExportMesh Return type: bool Description: Export mesh data to file, returns true on success Param[1]: mesh (type: Mesh) Param[2]: fileName (type: const char *) -Function 485: ExportMeshAsCode() (2 input parameters) +Function 487: ExportMeshAsCode() (2 input parameters) Name: ExportMeshAsCode Return type: bool Description: Export mesh as code file (.h) defining multiple arrays of vertex attributes Param[1]: mesh (type: Mesh) Param[2]: fileName (type: const char *) -Function 486: GenMeshPoly() (2 input parameters) +Function 488: GenMeshPoly() (2 input parameters) Name: GenMeshPoly Return type: Mesh Description: Generate polygonal mesh Param[1]: sides (type: int) Param[2]: radius (type: float) -Function 487: GenMeshPlane() (4 input parameters) +Function 489: GenMeshPlane() (4 input parameters) Name: GenMeshPlane Return type: Mesh Description: Generate plane mesh (with subdivisions) @@ -4125,42 +4141,42 @@ Function 487: GenMeshPlane() (4 input parameters) Param[2]: length (type: float) Param[3]: resX (type: int) Param[4]: resZ (type: int) -Function 488: GenMeshCube() (3 input parameters) +Function 490: GenMeshCube() (3 input parameters) Name: GenMeshCube Return type: Mesh Description: Generate cuboid mesh Param[1]: width (type: float) Param[2]: height (type: float) Param[3]: length (type: float) -Function 489: GenMeshSphere() (3 input parameters) +Function 491: GenMeshSphere() (3 input parameters) Name: GenMeshSphere Return type: Mesh Description: Generate sphere mesh (standard sphere) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 490: GenMeshHemiSphere() (3 input parameters) +Function 492: GenMeshHemiSphere() (3 input parameters) Name: GenMeshHemiSphere Return type: Mesh Description: Generate half-sphere mesh (no bottom cap) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 491: GenMeshCylinder() (3 input parameters) +Function 493: GenMeshCylinder() (3 input parameters) Name: GenMeshCylinder Return type: Mesh Description: Generate cylinder mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 492: GenMeshCone() (3 input parameters) +Function 494: GenMeshCone() (3 input parameters) Name: GenMeshCone Return type: Mesh Description: Generate cone/pyramid mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 493: GenMeshTorus() (4 input parameters) +Function 495: GenMeshTorus() (4 input parameters) Name: GenMeshTorus Return type: Mesh Description: Generate torus mesh @@ -4168,7 +4184,7 @@ Function 493: GenMeshTorus() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 494: GenMeshKnot() (4 input parameters) +Function 496: GenMeshKnot() (4 input parameters) Name: GenMeshKnot Return type: Mesh Description: Generate trefoil knot mesh @@ -4176,91 +4192,91 @@ Function 494: GenMeshKnot() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 495: GenMeshHeightmap() (2 input parameters) +Function 497: GenMeshHeightmap() (2 input parameters) Name: GenMeshHeightmap Return type: Mesh Description: Generate heightmap mesh from image data Param[1]: heightmap (type: Image) Param[2]: size (type: Vector3) -Function 496: GenMeshCubicmap() (2 input parameters) +Function 498: GenMeshCubicmap() (2 input parameters) Name: GenMeshCubicmap Return type: Mesh Description: Generate cubes-based map mesh from image data Param[1]: cubicmap (type: Image) Param[2]: cubeSize (type: Vector3) -Function 497: LoadMaterials() (2 input parameters) +Function 499: LoadMaterials() (2 input parameters) Name: LoadMaterials Return type: Material * Description: Load materials from model file Param[1]: fileName (type: const char *) Param[2]: materialCount (type: int *) -Function 498: LoadMaterialDefault() (0 input parameters) +Function 500: LoadMaterialDefault() (0 input parameters) Name: LoadMaterialDefault Return type: Material Description: Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) No input parameters -Function 499: IsMaterialValid() (1 input parameters) +Function 501: IsMaterialValid() (1 input parameters) Name: IsMaterialValid Return type: bool Description: Check if a material is valid (shader assigned, map textures loaded in GPU) Param[1]: material (type: Material) -Function 500: UnloadMaterial() (1 input parameters) +Function 502: UnloadMaterial() (1 input parameters) Name: UnloadMaterial Return type: void Description: Unload material from GPU memory (VRAM) Param[1]: material (type: Material) -Function 501: SetMaterialTexture() (3 input parameters) +Function 503: SetMaterialTexture() (3 input parameters) Name: SetMaterialTexture Return type: void Description: Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) Param[1]: material (type: Material *) Param[2]: mapType (type: int) Param[3]: texture (type: Texture2D) -Function 502: SetModelMeshMaterial() (3 input parameters) +Function 504: SetModelMeshMaterial() (3 input parameters) Name: SetModelMeshMaterial Return type: void Description: Set material for a mesh Param[1]: model (type: Model *) Param[2]: meshId (type: int) Param[3]: materialId (type: int) -Function 503: LoadModelAnimations() (2 input parameters) +Function 505: LoadModelAnimations() (2 input parameters) Name: LoadModelAnimations Return type: ModelAnimation * Description: Load model animations from file Param[1]: fileName (type: const char *) Param[2]: animCount (type: int *) -Function 504: UpdateModelAnimation() (3 input parameters) +Function 506: UpdateModelAnimation() (3 input parameters) Name: UpdateModelAnimation Return type: void Description: Update model animation pose (CPU) Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: int) -Function 505: UpdateModelAnimationBones() (3 input parameters) +Function 507: UpdateModelAnimationBones() (3 input parameters) Name: UpdateModelAnimationBones Return type: void Description: Update model animation mesh bone matrices (GPU skinning) Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: int) -Function 506: UnloadModelAnimation() (1 input parameters) +Function 508: UnloadModelAnimation() (1 input parameters) Name: UnloadModelAnimation Return type: void Description: Unload animation data Param[1]: anim (type: ModelAnimation) -Function 507: UnloadModelAnimations() (2 input parameters) +Function 509: UnloadModelAnimations() (2 input parameters) Name: UnloadModelAnimations Return type: void Description: Unload animation array data Param[1]: animations (type: ModelAnimation *) Param[2]: animCount (type: int) -Function 508: IsModelAnimationValid() (2 input parameters) +Function 510: IsModelAnimationValid() (2 input parameters) Name: IsModelAnimationValid Return type: bool Description: Check model animation skeleton match Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) -Function 509: CheckCollisionSpheres() (4 input parameters) +Function 511: CheckCollisionSpheres() (4 input parameters) Name: CheckCollisionSpheres Return type: bool Description: Check collision between two spheres @@ -4268,40 +4284,40 @@ Function 509: CheckCollisionSpheres() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector3) Param[4]: radius2 (type: float) -Function 510: CheckCollisionBoxes() (2 input parameters) +Function 512: CheckCollisionBoxes() (2 input parameters) Name: CheckCollisionBoxes Return type: bool Description: Check collision between two bounding boxes Param[1]: box1 (type: BoundingBox) Param[2]: box2 (type: BoundingBox) -Function 511: CheckCollisionBoxSphere() (3 input parameters) +Function 513: CheckCollisionBoxSphere() (3 input parameters) Name: CheckCollisionBoxSphere Return type: bool Description: Check collision between box and sphere Param[1]: box (type: BoundingBox) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 512: GetRayCollisionSphere() (3 input parameters) +Function 514: GetRayCollisionSphere() (3 input parameters) Name: GetRayCollisionSphere Return type: RayCollision Description: Get collision info between ray and sphere Param[1]: ray (type: Ray) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 513: GetRayCollisionBox() (2 input parameters) +Function 515: GetRayCollisionBox() (2 input parameters) Name: GetRayCollisionBox Return type: RayCollision Description: Get collision info between ray and box Param[1]: ray (type: Ray) Param[2]: box (type: BoundingBox) -Function 514: GetRayCollisionMesh() (3 input parameters) +Function 516: GetRayCollisionMesh() (3 input parameters) Name: GetRayCollisionMesh Return type: RayCollision Description: Get collision info between ray and mesh Param[1]: ray (type: Ray) Param[2]: mesh (type: Mesh) Param[3]: transform (type: Matrix) -Function 515: GetRayCollisionTriangle() (4 input parameters) +Function 517: GetRayCollisionTriangle() (4 input parameters) Name: GetRayCollisionTriangle Return type: RayCollision Description: Get collision info between ray and triangle @@ -4309,7 +4325,7 @@ Function 515: GetRayCollisionTriangle() (4 input parameters) Param[2]: p1 (type: Vector3) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) -Function 516: GetRayCollisionQuad() (5 input parameters) +Function 518: GetRayCollisionQuad() (5 input parameters) Name: GetRayCollisionQuad Return type: RayCollision Description: Get collision info between ray and quad @@ -4318,158 +4334,158 @@ Function 516: GetRayCollisionQuad() (5 input parameters) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) Param[5]: p4 (type: Vector3) -Function 517: InitAudioDevice() (0 input parameters) +Function 519: InitAudioDevice() (0 input parameters) Name: InitAudioDevice Return type: void Description: Initialize audio device and context No input parameters -Function 518: CloseAudioDevice() (0 input parameters) +Function 520: CloseAudioDevice() (0 input parameters) Name: CloseAudioDevice Return type: void Description: Close the audio device and context No input parameters -Function 519: IsAudioDeviceReady() (0 input parameters) +Function 521: IsAudioDeviceReady() (0 input parameters) Name: IsAudioDeviceReady Return type: bool Description: Check if audio device has been initialized successfully No input parameters -Function 520: SetMasterVolume() (1 input parameters) +Function 522: SetMasterVolume() (1 input parameters) Name: SetMasterVolume Return type: void Description: Set master volume (listener) Param[1]: volume (type: float) -Function 521: GetMasterVolume() (0 input parameters) +Function 523: GetMasterVolume() (0 input parameters) Name: GetMasterVolume Return type: float Description: Get master volume (listener) No input parameters -Function 522: LoadWave() (1 input parameters) +Function 524: LoadWave() (1 input parameters) Name: LoadWave Return type: Wave Description: Load wave data from file Param[1]: fileName (type: const char *) -Function 523: LoadWaveFromMemory() (3 input parameters) +Function 525: LoadWaveFromMemory() (3 input parameters) Name: LoadWaveFromMemory Return type: Wave Description: Load wave from memory buffer, fileType refers to extension: i.e. '.wav' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 524: IsWaveValid() (1 input parameters) +Function 526: IsWaveValid() (1 input parameters) Name: IsWaveValid Return type: bool Description: Checks if wave data is valid (data loaded and parameters) Param[1]: wave (type: Wave) -Function 525: LoadSound() (1 input parameters) +Function 527: LoadSound() (1 input parameters) Name: LoadSound Return type: Sound Description: Load sound from file Param[1]: fileName (type: const char *) -Function 526: LoadSoundFromWave() (1 input parameters) +Function 528: LoadSoundFromWave() (1 input parameters) Name: LoadSoundFromWave Return type: Sound Description: Load sound from wave data Param[1]: wave (type: Wave) -Function 527: LoadSoundAlias() (1 input parameters) +Function 529: LoadSoundAlias() (1 input parameters) Name: LoadSoundAlias Return type: Sound Description: Create a new sound that shares the same sample data as the source sound, does not own the sound data Param[1]: source (type: Sound) -Function 528: IsSoundValid() (1 input parameters) +Function 530: IsSoundValid() (1 input parameters) Name: IsSoundValid Return type: bool Description: Checks if a sound is valid (data loaded and buffers initialized) Param[1]: sound (type: Sound) -Function 529: UpdateSound() (3 input parameters) +Function 531: UpdateSound() (3 input parameters) Name: UpdateSound Return type: void Description: Update sound buffer with new data Param[1]: sound (type: Sound) Param[2]: data (type: const void *) Param[3]: sampleCount (type: int) -Function 530: UnloadWave() (1 input parameters) +Function 532: UnloadWave() (1 input parameters) Name: UnloadWave Return type: void Description: Unload wave data Param[1]: wave (type: Wave) -Function 531: UnloadSound() (1 input parameters) +Function 533: UnloadSound() (1 input parameters) Name: UnloadSound Return type: void Description: Unload sound Param[1]: sound (type: Sound) -Function 532: UnloadSoundAlias() (1 input parameters) +Function 534: UnloadSoundAlias() (1 input parameters) Name: UnloadSoundAlias Return type: void Description: Unload a sound alias (does not deallocate sample data) Param[1]: alias (type: Sound) -Function 533: ExportWave() (2 input parameters) +Function 535: ExportWave() (2 input parameters) Name: ExportWave Return type: bool Description: Export wave data to file, returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 534: ExportWaveAsCode() (2 input parameters) +Function 536: ExportWaveAsCode() (2 input parameters) Name: ExportWaveAsCode Return type: bool Description: Export wave sample data to code (.h), returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 535: PlaySound() (1 input parameters) +Function 537: PlaySound() (1 input parameters) Name: PlaySound Return type: void Description: Play a sound Param[1]: sound (type: Sound) -Function 536: StopSound() (1 input parameters) +Function 538: StopSound() (1 input parameters) Name: StopSound Return type: void Description: Stop playing a sound Param[1]: sound (type: Sound) -Function 537: PauseSound() (1 input parameters) +Function 539: PauseSound() (1 input parameters) Name: PauseSound Return type: void Description: Pause a sound Param[1]: sound (type: Sound) -Function 538: ResumeSound() (1 input parameters) +Function 540: ResumeSound() (1 input parameters) Name: ResumeSound Return type: void Description: Resume a paused sound Param[1]: sound (type: Sound) -Function 539: IsSoundPlaying() (1 input parameters) +Function 541: IsSoundPlaying() (1 input parameters) Name: IsSoundPlaying Return type: bool Description: Check if a sound is currently playing Param[1]: sound (type: Sound) -Function 540: SetSoundVolume() (2 input parameters) +Function 542: SetSoundVolume() (2 input parameters) Name: SetSoundVolume Return type: void Description: Set volume for a sound (1.0 is max level) Param[1]: sound (type: Sound) Param[2]: volume (type: float) -Function 541: SetSoundPitch() (2 input parameters) +Function 543: SetSoundPitch() (2 input parameters) Name: SetSoundPitch Return type: void Description: Set pitch for a sound (1.0 is base level) Param[1]: sound (type: Sound) Param[2]: pitch (type: float) -Function 542: SetSoundPan() (2 input parameters) +Function 544: SetSoundPan() (2 input parameters) Name: SetSoundPan Return type: void Description: Set pan for a sound (0.5 is center) Param[1]: sound (type: Sound) Param[2]: pan (type: float) -Function 543: WaveCopy() (1 input parameters) +Function 545: WaveCopy() (1 input parameters) Name: WaveCopy Return type: Wave Description: Copy a wave to a new wave Param[1]: wave (type: Wave) -Function 544: WaveCrop() (3 input parameters) +Function 546: WaveCrop() (3 input parameters) Name: WaveCrop Return type: void Description: Crop a wave to defined frames range Param[1]: wave (type: Wave *) Param[2]: initFrame (type: int) Param[3]: finalFrame (type: int) -Function 545: WaveFormat() (4 input parameters) +Function 547: WaveFormat() (4 input parameters) Name: WaveFormat Return type: void Description: Convert wave data to desired format @@ -4477,203 +4493,203 @@ Function 545: WaveFormat() (4 input parameters) Param[2]: sampleRate (type: int) Param[3]: sampleSize (type: int) Param[4]: channels (type: int) -Function 546: LoadWaveSamples() (1 input parameters) +Function 548: LoadWaveSamples() (1 input parameters) Name: LoadWaveSamples Return type: float * Description: Load samples data from wave as a 32bit float data array Param[1]: wave (type: Wave) -Function 547: UnloadWaveSamples() (1 input parameters) +Function 549: UnloadWaveSamples() (1 input parameters) Name: UnloadWaveSamples Return type: void Description: Unload samples data loaded with LoadWaveSamples() Param[1]: samples (type: float *) -Function 548: LoadMusicStream() (1 input parameters) +Function 550: LoadMusicStream() (1 input parameters) Name: LoadMusicStream Return type: Music Description: Load music stream from file Param[1]: fileName (type: const char *) -Function 549: LoadMusicStreamFromMemory() (3 input parameters) +Function 551: LoadMusicStreamFromMemory() (3 input parameters) Name: LoadMusicStreamFromMemory Return type: Music Description: Load music stream from data Param[1]: fileType (type: const char *) Param[2]: data (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 550: IsMusicValid() (1 input parameters) +Function 552: IsMusicValid() (1 input parameters) Name: IsMusicValid Return type: bool Description: Checks if a music stream is valid (context and buffers initialized) Param[1]: music (type: Music) -Function 551: UnloadMusicStream() (1 input parameters) +Function 553: UnloadMusicStream() (1 input parameters) Name: UnloadMusicStream Return type: void Description: Unload music stream Param[1]: music (type: Music) -Function 552: PlayMusicStream() (1 input parameters) +Function 554: PlayMusicStream() (1 input parameters) Name: PlayMusicStream Return type: void Description: Start music playing Param[1]: music (type: Music) -Function 553: IsMusicStreamPlaying() (1 input parameters) +Function 555: IsMusicStreamPlaying() (1 input parameters) Name: IsMusicStreamPlaying Return type: bool Description: Check if music is playing Param[1]: music (type: Music) -Function 554: UpdateMusicStream() (1 input parameters) +Function 556: UpdateMusicStream() (1 input parameters) Name: UpdateMusicStream Return type: void Description: Updates buffers for music streaming Param[1]: music (type: Music) -Function 555: StopMusicStream() (1 input parameters) +Function 557: StopMusicStream() (1 input parameters) Name: StopMusicStream Return type: void Description: Stop music playing Param[1]: music (type: Music) -Function 556: PauseMusicStream() (1 input parameters) +Function 558: PauseMusicStream() (1 input parameters) Name: PauseMusicStream Return type: void Description: Pause music playing Param[1]: music (type: Music) -Function 557: ResumeMusicStream() (1 input parameters) +Function 559: ResumeMusicStream() (1 input parameters) Name: ResumeMusicStream Return type: void Description: Resume playing paused music Param[1]: music (type: Music) -Function 558: SeekMusicStream() (2 input parameters) +Function 560: SeekMusicStream() (2 input parameters) Name: SeekMusicStream Return type: void Description: Seek music to a position (in seconds) Param[1]: music (type: Music) Param[2]: position (type: float) -Function 559: SetMusicVolume() (2 input parameters) +Function 561: SetMusicVolume() (2 input parameters) Name: SetMusicVolume Return type: void Description: Set volume for music (1.0 is max level) Param[1]: music (type: Music) Param[2]: volume (type: float) -Function 560: SetMusicPitch() (2 input parameters) +Function 562: SetMusicPitch() (2 input parameters) Name: SetMusicPitch Return type: void Description: Set pitch for a music (1.0 is base level) Param[1]: music (type: Music) Param[2]: pitch (type: float) -Function 561: SetMusicPan() (2 input parameters) +Function 563: SetMusicPan() (2 input parameters) Name: SetMusicPan Return type: void Description: Set pan for a music (0.5 is center) Param[1]: music (type: Music) Param[2]: pan (type: float) -Function 562: GetMusicTimeLength() (1 input parameters) +Function 564: GetMusicTimeLength() (1 input parameters) Name: GetMusicTimeLength Return type: float Description: Get music time length (in seconds) Param[1]: music (type: Music) -Function 563: GetMusicTimePlayed() (1 input parameters) +Function 565: GetMusicTimePlayed() (1 input parameters) Name: GetMusicTimePlayed Return type: float Description: Get current music time played (in seconds) Param[1]: music (type: Music) -Function 564: LoadAudioStream() (3 input parameters) +Function 566: LoadAudioStream() (3 input parameters) Name: LoadAudioStream Return type: AudioStream Description: Load audio stream (to stream raw audio pcm data) Param[1]: sampleRate (type: unsigned int) Param[2]: sampleSize (type: unsigned int) Param[3]: channels (type: unsigned int) -Function 565: IsAudioStreamValid() (1 input parameters) +Function 567: IsAudioStreamValid() (1 input parameters) Name: IsAudioStreamValid Return type: bool Description: Checks if an audio stream is valid (buffers initialized) Param[1]: stream (type: AudioStream) -Function 566: UnloadAudioStream() (1 input parameters) +Function 568: UnloadAudioStream() (1 input parameters) Name: UnloadAudioStream Return type: void Description: Unload audio stream and free memory Param[1]: stream (type: AudioStream) -Function 567: UpdateAudioStream() (3 input parameters) +Function 569: UpdateAudioStream() (3 input parameters) Name: UpdateAudioStream Return type: void Description: Update audio stream buffers with data Param[1]: stream (type: AudioStream) Param[2]: data (type: const void *) Param[3]: frameCount (type: int) -Function 568: IsAudioStreamProcessed() (1 input parameters) +Function 570: IsAudioStreamProcessed() (1 input parameters) Name: IsAudioStreamProcessed Return type: bool Description: Check if any audio stream buffers requires refill Param[1]: stream (type: AudioStream) -Function 569: PlayAudioStream() (1 input parameters) +Function 571: PlayAudioStream() (1 input parameters) Name: PlayAudioStream Return type: void Description: Play audio stream Param[1]: stream (type: AudioStream) -Function 570: PauseAudioStream() (1 input parameters) +Function 572: PauseAudioStream() (1 input parameters) Name: PauseAudioStream Return type: void Description: Pause audio stream Param[1]: stream (type: AudioStream) -Function 571: ResumeAudioStream() (1 input parameters) +Function 573: ResumeAudioStream() (1 input parameters) Name: ResumeAudioStream Return type: void Description: Resume audio stream Param[1]: stream (type: AudioStream) -Function 572: IsAudioStreamPlaying() (1 input parameters) +Function 574: IsAudioStreamPlaying() (1 input parameters) Name: IsAudioStreamPlaying Return type: bool Description: Check if audio stream is playing Param[1]: stream (type: AudioStream) -Function 573: StopAudioStream() (1 input parameters) +Function 575: StopAudioStream() (1 input parameters) Name: StopAudioStream Return type: void Description: Stop audio stream Param[1]: stream (type: AudioStream) -Function 574: SetAudioStreamVolume() (2 input parameters) +Function 576: SetAudioStreamVolume() (2 input parameters) Name: SetAudioStreamVolume Return type: void Description: Set volume for audio stream (1.0 is max level) Param[1]: stream (type: AudioStream) Param[2]: volume (type: float) -Function 575: SetAudioStreamPitch() (2 input parameters) +Function 577: SetAudioStreamPitch() (2 input parameters) Name: SetAudioStreamPitch Return type: void Description: Set pitch for audio stream (1.0 is base level) Param[1]: stream (type: AudioStream) Param[2]: pitch (type: float) -Function 576: SetAudioStreamPan() (2 input parameters) +Function 578: SetAudioStreamPan() (2 input parameters) Name: SetAudioStreamPan Return type: void Description: Set pan for audio stream (0.5 is centered) Param[1]: stream (type: AudioStream) Param[2]: pan (type: float) -Function 577: SetAudioStreamBufferSizeDefault() (1 input parameters) +Function 579: SetAudioStreamBufferSizeDefault() (1 input parameters) Name: SetAudioStreamBufferSizeDefault Return type: void Description: Default size for new audio streams Param[1]: size (type: int) -Function 578: SetAudioStreamCallback() (2 input parameters) +Function 580: SetAudioStreamCallback() (2 input parameters) Name: SetAudioStreamCallback Return type: void Description: Audio thread callback to request new data Param[1]: stream (type: AudioStream) Param[2]: callback (type: AudioCallback) -Function 579: AttachAudioStreamProcessor() (2 input parameters) +Function 581: AttachAudioStreamProcessor() (2 input parameters) Name: AttachAudioStreamProcessor Return type: void Description: Attach audio stream processor to stream, receives frames x 2 samples as 'float' (stereo) Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 580: DetachAudioStreamProcessor() (2 input parameters) +Function 582: DetachAudioStreamProcessor() (2 input parameters) Name: DetachAudioStreamProcessor Return type: void Description: Detach audio stream processor from stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 581: AttachAudioMixedProcessor() (1 input parameters) +Function 583: AttachAudioMixedProcessor() (1 input parameters) Name: AttachAudioMixedProcessor Return type: void Description: Attach audio stream processor to the entire audio pipeline, receives frames x 2 samples as 'float' (stereo) Param[1]: processor (type: AudioCallback) -Function 582: DetachAudioMixedProcessor() (1 input parameters) +Function 584: DetachAudioMixedProcessor() (1 input parameters) Name: DetachAudioMixedProcessor Return type: void Description: Detach audio stream processor from the entire audio pipeline diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index b761f8b56..52f7f5b1d 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -679,7 +679,7 @@ - + @@ -1409,6 +1409,12 @@ + + + + + + @@ -1416,6 +1422,12 @@ + + + + + + From 6eeaf1dd5b22778a704d41c402cc8c0fc22731c3 Mon Sep 17 00:00:00 2001 From: M374LX Date: Sat, 31 May 2025 16:43:25 -0300 Subject: [PATCH 024/242] Update RGFW to 1.7.5-dev --- src/external/RGFW.h | 902 ++++++++++++++++++----------- src/platforms/rcore_desktop_rgfw.c | 1 + 2 files changed, 562 insertions(+), 341 deletions(-) diff --git a/src/external/RGFW.h b/src/external/RGFW.h index f121ef630..1582fbfad 100644 --- a/src/external/RGFW.h +++ b/src/external/RGFW.h @@ -1,7 +1,7 @@ /* * -* RGFW 1.7 -* +* RGFW 1.7.5-dev + * Copyright (C) 2022-25 ColleagueRiley * * libpng license @@ -160,7 +160,7 @@ int main() { @Easymode -> support, testing/debugging, bug fixes and reviews Joshua Rowe (omnisci3nce) - bug fix, review (macOS) @lesleyrs -> bug fix, review (OpenGL) - Nick Porcino (meshula) - testing, organization, review (MacOS, examples) + Nick Porcino (meshula) - testing, organization, review (MacOS, examples) @DarekParodia -> code review (X11) (C++) */ @@ -202,8 +202,12 @@ int main() { #define RGFW_ASSERT assert #endif -#if !defined(RGFW_MEMCPY) || !defined(RGFW_STRNCMP) || !defined(RGFW_STRNCPY) - #include +#if !defined(RGFW_MEMCPY) || !defined(RGFW_STRNCMP) || !defined(RGFW_STRNCPY) || !defined(RGFW_MEMSET) + #include +#endif + +#ifndef RGFW_MEMSET + #define RGFW_MEMSET(ptr, value, num) memset(ptr, value, num) #endif #ifndef RGFW_MEMCPY @@ -225,18 +229,10 @@ int main() { #ifndef RGFW_STRTOL /* required for X11 XDnD and X11 Monitor DPI */ #include - #define RGFW_STRTOL(str, endptr, base) strtol(str, endptr, base) + #define RGFW_STRTOL(str, endptr, base) strtol(str, endptr, base) #define RGFW_ATOF(num) atof(num) #endif -#if !_MSC_VER - #ifndef inline - #ifndef __APPLE__ - #define inline __inline - #endif - #endif -#endif - #ifdef RGFW_WIN95 /* for windows 95 testing (not that it really works) */ #define RGFW_NO_MONITOR #define RGFW_NO_PASSTHROUGH @@ -258,14 +254,17 @@ int main() { #define RGFWDEF __attribute__((visibility("default"))) #endif #endif + #ifndef RGFWDEF + #define RGFWDEF + #endif #endif #ifndef RGFWDEF - #ifdef RGFW_C89 - #define RGFWDEF __inline - #else - #define RGFWDEF inline - #endif + #ifdef RGFW_C89 + #define RGFWDEF __inline + #else + #define RGFWDEF inline + #endif #endif #ifndef RGFW_ENUM @@ -285,15 +284,15 @@ int main() { #include #ifndef RGFW_INT_DEFINED #ifdef RGFW_USE_INT /* optional for any system that might not have stdint.h */ - typedef unsigned char u8; - typedef signed char i8; - typedef unsigned short u16; - typedef signed short i16; - typedef unsigned long int u32; - typedef signed long int i32; - typedef unsigned long long u64; - typedef signed long long i64; - #else /* use stdint standard types instead of c ""standard"" types */ + typedef unsigned char u8; + typedef signed char i8; + typedef unsigned short u16; + typedef signed short i16; + typedef unsigned long int u32; + typedef signed long int i32; + typedef unsigned long long u64; + typedef signed long long i64; + #else /* use stdint standard types instead of c "standard" types */ #include typedef uint8_t u8; @@ -305,7 +304,7 @@ int main() { typedef uint64_t u64; typedef int64_t i64; #endif - #define RGFW_INT_DEFINED + #define RGFW_INT_DEFINED #endif #ifndef RGFW_BOOL_DEFINED @@ -566,17 +565,17 @@ typedef RGFW_ENUM(u8, RGFW_gamepadCodes) { /*! basic vector type, if there's not already a point/vector type of choice */ #ifndef RGFW_point - typedef struct { i32 x, y; } RGFW_point; + typedef struct RGFW_point { i32 x, y; } RGFW_point; #endif /*! 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; + typedef struct RGFW_rect { i32 x, y, w, h; } RGFW_rect; #endif /*! basic area type, if there's not already a area type of choice */ #ifndef RGFW_area - typedef struct { u32 w, h; } RGFW_area; + typedef struct RGFW_area { u32 w, h; } RGFW_area; #endif #if defined(__cplusplus) && !defined(__APPLE__) @@ -714,6 +713,7 @@ typedef struct RGFW_window_src { i64 counter_value; XID counter; #endif + RGFW_rect r; #endif /* RGFW_X11 */ #if defined(RGFW_WAYLAND) struct wl_display* wl_display; @@ -811,9 +811,11 @@ typedef struct RGFW_window { RGFW_event event; /*!< current event */ RGFW_rect r; /*!< the x, y, w and h of the struct */ - + + /*! which key RGFW_window_shouldClose checks. Settting this to RGFW_keyNULL disables the feature. */ + RGFW_key exitKey; RGFW_point _lastMousePoint; /*!< last cusor point (for raw mouse data) */ - + u32 _flags; /*!< windows flags (for RGFW to check) */ RGFW_rect _oldRect; /*!< rect before fullscreen */ } RGFW_window; /*!< window structure for managing the window */ @@ -990,6 +992,8 @@ RGFWDEF RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win); /*!< sets the m this is useful for a 3D camera */ RGFWDEF void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area); +/*! if the mouse is held by RGFW */ +RGFWDEF RGFW_bool RGFW_window_mouseHeld(RGFW_window* win); /*! stop holding the mouse and let it move freely */ RGFWDEF void RGFW_window_mouseUnhold(RGFW_window* win); @@ -1101,14 +1105,14 @@ typedef RGFW_ENUM(u8, RGFW_errorCode) { RGFW_warningWayland, RGFW_warningOpenGL }; -typedef struct RGFW_debugContext { RGFW_window* win; RGFW_monitor monitor; u32 srcError; } RGFW_debugContext; +typedef struct RGFW_debugContext { RGFW_window* win; RGFW_monitor* monitor; u32 srcError; } RGFW_debugContext; #if defined(__cplusplus) && !defined(__APPLE__) -#define RGFW_DEBUG_CTX(win, err) {win, { 0 }, err} -#define RGFW_DEBUG_CTX_MON(monitor) {_RGFW.root, monitor, 0} +#define RGFW_DEBUG_CTX(win, err) {win, NULL, err} +#define RGFW_DEBUG_CTX_MON(monitor) {_RGFW.root, &monitor, 0} #else -#define RGFW_DEBUG_CTX(win, err) (RGFW_debugContext){win, (RGFW_monitor){ 0 }, err} -#define RGFW_DEBUG_CTX_MON(monitor) (RGFW_debugContext){_RGFW.root, monitor, 0} +#define RGFW_DEBUG_CTX(win, err) (RGFW_debugContext){win, NULL, err} +#define RGFW_DEBUG_CTX_MON(monitor) (RGFW_debugContext){_RGFW.root, &monitor, 0} #endif typedef void (* RGFW_debugfunc)(RGFW_debugType type, RGFW_errorCode err, RGFW_debugContext ctx, const char* msg); @@ -1300,10 +1304,13 @@ typedef RGFW_ENUM(u8, RGFW_glHints) { RGFW_glCore = 0, RGFW_glCompatibility /*!< RGFW_glProfile options */ }; RGFWDEF void RGFW_setGLHint(RGFW_glHints hint, i32 value); +RGFWDEF RGFW_bool RGFW_extensionSupported(const char* extension, size_t len); /*!< check if whether the specified API extension is supported by the current OpenGL or OpenGL ES context */ RGFWDEF RGFW_proc 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 */ RGFWDEF void RGFW_window_swapBuffers_OpenGL(RGFW_window* win); /*!< swap opengl buffer (only) called by RGFW_window_swapInterval */ void* RGFW_getCurrent_OpenGL(void); /*!< get the current context (OpenGL backend (GLX) (WGL) (EGL) (cocoa) (webgl))*/ + +RGFWDEF RGFW_bool RGFW_extensionSupportedPlatform(const char* extension, size_t len); /*!< check if whether the specified platform-specific API extension is supported by the current OpenGL or OpenGL ES context */ #endif #ifdef RGFW_VULKAN #if defined(RGFW_WAYLAND) && defined(RGFW_X11) @@ -1378,6 +1385,9 @@ RGFWDEF RGFW_window* RGFW_getRootWindow(void); void RGFW_eventQueuePush(RGFW_event event); RGFW_event* RGFW_eventQueuePop(RGFW_window* win); +/* for C++ / C89 */ +#define RGFW_eventQueuePushEx(eventInit) { RGFW_event e; eventInit; RGFW_eventQueuePush(e); } + /*! key codes and mouse icon enums */ @@ -1468,7 +1478,6 @@ typedef RGFW_ENUM(u8, RGFW_key) { RGFW_down, RGFW_left, RGFW_right, - RGFW_insert, RGFW_end, RGFW_home, @@ -1511,7 +1520,6 @@ typedef RGFW_ENUM(u8, RGFW_mouseIcons) { RGFW_mouseNotAllowed, RGFW_mouseIconFinal = 16 /* padding for alignment */ }; - /** @} */ #endif /* RGFW_HEADER */ @@ -1593,7 +1601,7 @@ void RGFW_sendDebugInfo(RGFW_debugType type, RGFW_errorCode err, RGFW_debugConte #ifdef RGFW_BUFFER case RGFW_errBuffer: case RGFW_infoBuffer: printf(" buffer size: %i %i\n", ctx.win->bufferSize.w, ctx.win->bufferSize.h); break; #endif - case RGFW_infoMonitor: printf(": scale (%s):\n rect: {%i, %i, %i, %i}\n physical size:%f %f\n scale: %f %f\n pixelRatio: %f\n refreshRate: %i\n depth: %i\n", ctx.monitor.name, ctx.monitor.x, ctx.monitor.y, ctx.monitor.mode.area.w, ctx.monitor.mode.area.h, ctx.monitor.physW, ctx.monitor.physH, ctx.monitor.scaleX, ctx.monitor.scaleY, ctx.monitor.pixelRatio, ctx.monitor.mode.refreshRate, ctx.monitor.mode.red + ctx.monitor.mode.green + ctx.monitor.mode.blue); break; + case RGFW_infoMonitor: printf(": scale (%s):\n rect: {%i, %i, %i, %i}\n physical size:%f %f\n scale: %f %f\n pixelRatio: %f\n refreshRate: %i\n depth: %i\n", ctx.monitor->name, ctx.monitor->x, ctx.monitor->y, ctx.monitor->mode.area.w, ctx.monitor->mode.area.h, ctx.monitor->physW, ctx.monitor->physH, ctx.monitor->scaleX, ctx.monitor->scaleY, ctx.monitor->pixelRatio, ctx.monitor->mode.refreshRate, ctx.monitor->mode.red + ctx.monitor->mode.green + ctx.monitor->mode.blue); break; case RGFW_infoWindow: printf(" with rect of {%i, %i, %i, %i} \n", ctx.win->r.x, ctx.win->r.y,ctx. win->r.w, ctx.win->r.h); break; case RGFW_errDirectXContext: printf(" srcError %i\n", ctx.srcError); break; default: printf("\n"); @@ -1725,7 +1733,7 @@ void RGFW_init_keys(void) { RGFW_MAP [RGFW_OS_BASED_VALUE(133, 0x15B, 55, DOM_VK_WIN)] = RGFW_superL, #if !defined(RGFW_MACOS) && !defined(RGFW_WASM) RGFW_MAP [RGFW_OS_BASED_VALUE(105, 0x11D, 59, 0)] = RGFW_controlR RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(135, 0x15C, 55, 0)] = RGFW_superR, + RGFW_MAP [RGFW_OS_BASED_VALUE(134, 0x15C, 55, 0)] = RGFW_superR, RGFW_MAP [RGFW_OS_BASED_VALUE(62, 0x036, 56, 0)] = RGFW_shiftR RGFW_NEXT RGFW_MAP [RGFW_OS_BASED_VALUE(108, 0x138, 58, 0)] = RGFW_altR, #endif @@ -1783,15 +1791,13 @@ typedef struct { RGFW_keyState RGFW_keyboard[RGFW_keyLast] = { {0, 0} }; -RGFWDEF void RGFW_resetKey(void); -void RGFW_resetKey(void) { - size_t len = RGFW_keyLast; /*!< last_key == length */ - +RGFWDEF void RGFW_resetKeyPrev(void); +void RGFW_resetKeyPrev(void) { size_t i; /*!< reset each previous state */ - for (i = 0; i < len; i++) - RGFW_keyboard[i].prev = 0; + for (i = 0; i < RGFW_keyLast; i++) RGFW_keyboard[i].prev = 0; } - +RGFWDEF void RGFW_resetKey(void); +void RGFW_resetKey(void) { RGFW_MEMSET(RGFW_keyboard, 0, sizeof(RGFW_keyboard)); } /* this is the end of keycode data */ @@ -1879,17 +1885,16 @@ void RGFW_window_checkMode(RGFW_window* win); void RGFW_window_checkMode(RGFW_window* win) { if (RGFW_window_isMinimized(win)) { win->_flags |= RGFW_windowMinimize; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowMinimized, ._win = win}); RGFW_windowMinimizedCallback(win, win->r); } else if (RGFW_window_isMaximized(win)) { win->_flags |= RGFW_windowMaximize; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowMaximized, ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_windowMaximized; e._win = win); RGFW_windowMaximizedCallback(win, win->r); } else if (((win->_flags & RGFW_windowMinimize) && !RGFW_window_isMaximized(win)) || (win->_flags & RGFW_windowMaximize && !RGFW_window_isMaximized(win))) { win->_flags &= ~(u32)RGFW_windowMinimize; if (RGFW_window_isMaximized(win) == RGFW_FALSE) win->_flags &= ~(u32)RGFW_windowMaximize; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowRestored, ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_windowRestored; e._win = win); RGFW_windowRestoredCallback(win, win->r); } } @@ -1948,10 +1953,12 @@ typedef struct RGFW_globalStruct { RGFW_event events[RGFW_MAX_EVENTS]; } RGFW_globalStruct; -#ifndef RGFW_C89 +#if !defined(RGFW_C89) && !defined(__cplusplus) RGFW_globalStruct _RGFW = {.root = NULL, .current = NULL, .windowCount = -1, .eventLen = 0, .eventIndex = 0}; +#define _RGFW_init RGFW_TRUE #else -RGFW_globalStruct _RGFW = {NULL, NULL, -1, 0, 0}; +RGFW_bool _RGFW_init = RGFW_FALSE; +RGFW_globalStruct _RGFW; #endif void RGFW_eventQueuePush(RGFW_event event) { @@ -1961,9 +1968,10 @@ void RGFW_eventQueuePush(RGFW_event event) { } RGFW_event* RGFW_eventQueuePop(RGFW_window* win) { - if (_RGFW.eventLen == 0) return NULL; + RGFW_event* ev; + if (_RGFW.eventLen == 0) return NULL; - RGFW_event* ev = (RGFW_event*)&_RGFW.events[_RGFW.eventIndex]; + ev = (RGFW_event*)&_RGFW.events[_RGFW.eventIndex]; _RGFW.eventLen--; if (_RGFW.eventLen && _RGFW.eventIndex < (_RGFW.eventLen - 1)) @@ -1976,27 +1984,29 @@ RGFW_event* RGFW_eventQueuePop(RGFW_window* win) { return NULL; } + ev->droppedFilesCount = win->event.droppedFilesCount; ev->droppedFiles = win->event.droppedFiles; return ev; } RGFW_event* RGFW_window_checkEventCore(RGFW_window* win); RGFW_event* RGFW_window_checkEventCore(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - if (win->event.type == 0 && _RGFW.eventLen == 0) - RGFW_resetKey(); + RGFW_event* ev; + RGFW_ASSERT(win != NULL); + if (win->event.type == 0 && _RGFW.eventLen == 0) + RGFW_resetKeyPrev(); if (win->event.type == RGFW_quit && win->_flags & RGFW_windowFreeOnClose) { - static RGFW_event ev; - ev = win->event; + static RGFW_event event; + event = win->event; RGFW_window_close(win); - return &ev; + return &event; } if (win->event.type != RGFW_DNDInit) win->event.type = 0; /* check queued events */ - RGFW_event* ev = RGFW_eventQueuePop(win); + ev = RGFW_eventQueuePop(win); if (ev != NULL) { if (ev->type == RGFW_quit) RGFW_window_setShouldClose(win, RGFW_TRUE); win->event = *ev; @@ -2014,7 +2024,7 @@ RGFW_window* RGFW_getRootWindow(void) { return _RGFW.root; } /* do a basic initialization for RGFW_window, this is to standard it for each OS */ void RGFW_window_basic_init(RGFW_window* win, RGFW_rect rect, RGFW_windowFlags flags) { RGFW_UNUSED(flags); - if (_RGFW.windowCount == -1) RGFW_init(); + if (_RGFW.windowCount == -1 || _RGFW_init == RGFW_FALSE) RGFW_init(); _RGFW.windowCount++; /* rect based the requested flags */ @@ -2027,19 +2037,23 @@ void RGFW_window_basic_init(RGFW_window* win, RGFW_rect rect, RGFW_windowFlags f /* set and init the new window's data */ win->r = rect; + win->exitKey = RGFW_escape; win->event.droppedFilesCount = 0; win->_flags = 0 | (win->_flags & RGFW_WINDOW_ALLOC); win->_flags |= flags; win->event.keyMod = 0; - win->_lastMousePoint = RGFW_POINT(0, 0); + win->_lastMousePoint.x = 0; + win->_lastMousePoint.y = 0; win->event.droppedFiles = (char**)RGFW_ALLOC(RGFW_MAX_PATH * RGFW_MAX_DROPS); - RGFW_ASSERT(win->event.droppedFiles != NULL); - - u32 i; - for (i = 0; i < RGFW_MAX_DROPS; i++) - win->event.droppedFiles[i] = (char*)(win->event.droppedFiles + RGFW_MAX_DROPS + (i * RGFW_MAX_PATH)); + RGFW_ASSERT(win->event.droppedFiles != NULL); + + { + u32 i; + for (i = 0; i < RGFW_MAX_DROPS; i++) + win->event.droppedFiles[i] = (char*)(win->event.droppedFiles + RGFW_MAX_DROPS + (i * RGFW_MAX_PATH)); + } } void RGFW_window_setFlags(RGFW_window* win, RGFW_windowFlags flags) { @@ -2199,10 +2213,11 @@ void RGFW_window_center(RGFW_window* win) { } RGFW_bool RGFW_monitor_scaleToWindow(RGFW_monitor mon, RGFW_window* win) { - RGFW_ASSERT(win != NULL); - RGFW_monitorMode mode; - mode.area = RGFW_AREA(win->r.w, win->r.h); + RGFW_ASSERT(win != NULL); + + mode.area.w = (u32)win->r.w; + mode.area.h = (u32)win->r.h; return RGFW_monitor_requestMode(mon, mode, RGFW_monitorScale); } @@ -2223,7 +2238,7 @@ RGFW_bool RGFW_monitorModeCompare(RGFW_monitorMode mon, RGFW_monitorMode mon2, R } RGFW_bool RGFW_window_shouldClose(RGFW_window* win) { - return (win == NULL || (win->_flags & RGFW_EVENT_QUIT)|| RGFW_isPressed(win, RGFW_escape)); + return (win == NULL || (win->_flags & RGFW_EVENT_QUIT)|| (win->exitKey && RGFW_isPressed(win, win->exitKey))); } void RGFW_window_setShouldClose(RGFW_window* win, RGFW_bool shouldClose) { @@ -2256,15 +2271,15 @@ RGFW_bool RGFW_window_setIcon(RGFW_window* win, u8* icon, RGFW_area a, i32 chann 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->_flags & RGFW_HOLD_MOUSE)) - return; +RGFW_bool RGFW_window_mouseHeld(RGFW_window* win) { return RGFW_BOOL(win->_flags & RGFW_HOLD_MOUSE); } + +void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area) { if (!area.w && !area.h) area = RGFW_AREA(win->r.w / 2, win->r.h / 2); win->_flags |= RGFW_HOLD_MOUSE; - RGFW_captureCursor(win, win->r); + RGFW_captureCursor(win, win->r); RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2))); } @@ -2464,6 +2479,58 @@ void RGFW_setGLHint(RGFW_glHints hint, i32 value) { if (hint < RGFW_glFinalHint && hint) RGFW_GL_HINTS[hint] = value; } +RGFW_bool RGFW_extensionSupportedStr(const char* extensions, const char* ext, size_t len) { + const char *start = extensions; + const char *where; + const char* terminator; + + if (extensions == NULL || ext == NULL) + return RGFW_FALSE; + + where = strstr(extensions, ext); + while (where) { + terminator = where + len; + if ((where == start || *(where - 1) == ' ') && + (*terminator == ' ' || *terminator == '\0')) { + return RGFW_TRUE; + } + where = RGFW_STRSTR(terminator, ext); + } + + return RGFW_FALSE; +} + +RGFW_bool RGFW_extensionSupported(const char* extension, size_t len) { + #ifdef GL_NUM_EXTENSIONS + if (RGFW_GL_HINTS[RGFW_glMajor] >= 3) { + i32 i; + GLint count = 0; + + RGFW_proc RGFW_glGetStringi = RGFW_getProcAddress("glGetStringi"); + RGFW_proc RGFW_glGetIntegerv = RGFW_getProcAddress("RGFW_glGetIntegerv"); + if (RGFW_glGetIntegerv) + ((void(*)(GLenum, GLint*))RGFW_glGetIntegerv)(GL_NUM_EXTENSIONS, &count); + + for (i = 0; RGFW_glGetStringi && i < count; i++) { + const char* en = ((const char* (*)(u32, u32))RGFW_glGetStringi)(GL_EXTENSIONS, (u32)i); + if (en && RGFW_STRNCMP(en, extension, len) == 0) + return RGFW_TRUE; + } + } else +#endif + { + RGFW_proc RGFW_glGetString = RGFW_getProcAddress("glGetString"); + + if (RGFW_glGetString) { + const char* extensions = ((const char*(*)(u32))RGFW_glGetString)(GL_EXTENSIONS); + if ((extensions != NULL) && RGFW_extensionSupportedStr(extensions, extension, len)) + return RGFW_TRUE; + } + } + + return RGFW_extensionSupportedPlatform(extension, len); +} + /* OPENGL normal only (no EGL / OSMesa) */ #if defined(RGFW_OPENGL) && !defined(RGFW_EGL) && !defined(RGFW_CUSTOM_BACKEND) && !defined(RGFW_WASM) @@ -2824,7 +2891,7 @@ void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software) { if (RGFW_GL_HINTS[RGFW_glReleaseBehavior] == RGFW_releaseFlush) { RGFW_GL_ADD_ATTRIB(0x2097, 0x2098); } else { - RGFW_GL_ADD_ATTRIB(0x2097, 0x0000); + RGFW_GL_ADD_ATTRIB(0x2096, 0x0000); } RGFW_GL_ADD_ATTRIB(EGL_NONE, EGL_NONE); @@ -2886,6 +2953,11 @@ RGFW_proc RGFW_getProcAddress(const char* procname) { return (RGFW_proc) eglGetProcAddress(procname); } +RGFW_bool RGFW_extensionSupportedPlatform(const char* extension, size_t len) { + const char* extensions = eglQueryString(_RGFW.root->src.EGL_display, EGL_EXTENSIONS); + return extensions != NULL && RGFW_extensionSupportedStr(extensions, extension, len); +} + void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { RGFW_ASSERT(win != NULL); @@ -2946,7 +3018,7 @@ wayland: RGFW_bool RGFW_getVKPresentationSupport(VkInstance instance, VkPhysicalDevice physicalDevice, u32 queueFamilyIndex) { RGFW_ASSERT(instance); - if (_RGFW.windowCount == -1) RGFW_init(); + if (_RGFW.windowCount == -1 || _RGFW_init == RGFW_FALSE) RGFW_init(); #ifdef RGFW_X11 RGFW_GOTO_WAYLAND(0); Visual* visual = DefaultVisual(_RGFW.display, DefaultScreen(_RGFW.display)); @@ -3031,8 +3103,10 @@ This is where OS specific stuff starts RGFW_gamepads_name[index][sizeof(RGFW_gamepads_name[index]) - 1] = 0; u8 j; - for (j = 0; j < 16; j++) - RGFW_gamepadPressed[index][j] = (RGFW_keyState){0, 0}; + for (j = 0; j < 16; j++) { + RGFW_gamepadPressed[index][j].prev = 0; + RGFW_gamepadPressed[index][j].current = 0; + } win->event.type = RGFW_gamepadConnected; @@ -3230,7 +3304,7 @@ void xdg_toplevel_close_handler(void *data, if (win == NULL) win = RGFW_key_win; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_quit, ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_quit; e._win = win); RGFW_windowQuitCallback(win); } @@ -3256,9 +3330,9 @@ void pointer_enter(void *data, struct wl_pointer *pointer, uint32_t serial, stru RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); RGFW_mouse_win = win; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_mouseEnter, - .point = RGFW_POINT(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)), - ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_mouseEnter; + e.point = RGFW_POINT(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)); + e._win = win); RGFW_mouseNotifyCallback(win, win->event.point, RGFW_TRUE); } @@ -3268,9 +3342,9 @@ void pointer_leave(void *data, struct wl_pointer *pointer, uint32_t serial, stru if (RGFW_mouse_win == win) RGFW_mouse_win = NULL; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_mouseLeave, - .point = win->event.point, - ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_mouseLeave; + e.point = win->event.point; + e._win = win); RGFW_mouseNotifyCallback(win, win->event.point, RGFW_FALSE); } @@ -3278,9 +3352,9 @@ void pointer_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fi RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(x); RGFW_UNUSED(y); RGFW_ASSERT(RGFW_mouse_win != NULL); - RGFW_eventQueuePush((RGFW_event){.type = RGFW_mousePosChanged, - .point = RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y)), - ._win = RGFW_mouse_win}); + RGFW_eventQueuePushEx(e.type = RGFW_mousePosChanged; + e.point = RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y)); + e._win = RGFW_mouse_win); RGFW_mousePosCallback(RGFW_mouse_win, RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y)), RGFW_mouse_win->event.vector); } @@ -3297,9 +3371,9 @@ void pointer_button(void *data, struct wl_pointer *pointer, uint32_t serial, uin RGFW_mouseButtons[b].prev = RGFW_mouseButtons[b].current; RGFW_mouseButtons[b].current = RGFW_BOOL(state); - RGFW_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonPressed + RGFW_BOOL(state), - .button = (u8)b, - ._win = RGFW_mouse_win}); + RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonPressed + RGFW_BOOL(state); + e.button = (u8)b; + e._win = RGFW_mouse_win); RGFW_mouseButtonCallback(RGFW_mouse_win, (u8)b, 0, RGFW_BOOL(state)); } void pointer_axis(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { @@ -3308,10 +3382,10 @@ void pointer_axis(void *data, struct wl_pointer *pointer, uint32_t time, uint32_ double scroll = wl_fixed_to_double(value); - RGFW_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonPressed, - .button = RGFW_mouseScrollUp + (scroll < 0), - .scroll = scroll, - ._win = RGFW_mouse_win}); + RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonPressed; + e.button = RGFW_mouseScrollUp + (scroll < 0); + e.scroll = scroll; + e._win = RGFW_mouse_win); RGFW_mouseButtonCallback(RGFW_mouse_win, RGFW_mouseScrollUp + (scroll < 0), scroll, 1); } @@ -3336,8 +3410,11 @@ void keyboard_enter (void *data, struct wl_keyboard *keyboard, uint32_t serial, RGFW_key_win = (RGFW_window*)wl_surface_get_user_data(surface); RGFW_key_win->_flags |= RGFW_windowFocus; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_focusIn, ._win = RGFW_key_win}); + RGFW_eventQueuePushEx(e.type = RGFW_focusIn, e._win = RGFW_key_win); RGFW_focusCallback(RGFW_key_win, RGFW_TRUE); + + RGFW_resetKey(); + if ((win->_flags & RGFW_HOLD_MOUSE)) RGFW_window_mouseHold(win, RGFW_AREA(win->r.w, win->r.h)); } 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); @@ -3346,7 +3423,7 @@ void keyboard_leave (void *data, struct wl_keyboard *keyboard, uint32_t serial, if (RGFW_key_win == win) RGFW_key_win = NULL; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_focusOut, ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_focusOut; e._win = win); win->_flags &= ~(u32)RGFW_windowFocus; RGFW_focusCallback(win, RGFW_FALSE); } @@ -3361,11 +3438,11 @@ void keyboard_key (void *data, struct wl_keyboard *keyboard, uint32_t serial, ui RGFW_keyboard[RGFWkey].prev = RGFW_keyboard[RGFWkey].current; RGFW_keyboard[RGFWkey].current = RGFW_BOOL(state); - RGFW_eventQueuePush((RGFW_event){.type = (u8)(RGFW_keyPressed + state), - .key = (u8)RGFWkey, - .keyChar = (u8)keysym, - .repeat = RGFW_isHeld(RGFW_key_win, (u8)RGFWkey), - ._win = RGFW_key_win}); + RGFW_eventQueuePushEx(e.type = (u8)(RGFW_keyPressed + state); + e.key = (u8)RGFWkey; + e.keyChar = (u8)keysym; + e.repeat = RGFW_isHeld(RGFW_key_win, (u8)RGFWkey); + e._win = RGFW_key_win); RGFW_updateKeyMods(RGFW_key_win, RGFW_BOOL(xkb_keymap_mod_get_index(keymap, "Lock")), RGFW_BOOL(xkb_keymap_mod_get_index(keymap, "Mod2")), RGFW_BOOL(xkb_keymap_mod_get_index(keymap, "ScrollLock"))); RGFW_keyCallback(RGFW_key_win, (u8)RGFWkey, (u8)keysym, RGFW_key_win->event.keyMod, RGFW_BOOL(state)); @@ -3601,6 +3678,10 @@ void RGFW_setXInstName(const char* name) { } #if defined(RGFW_OPENGL) && !defined(RGFW_EGL) +RGFW_bool RGFW_extensionSupportedPlatform(const char * extension, size_t len) { + const char* extensions = glXQueryExtensionsString(_RGFW.display, XDefaultScreen(_RGFW.display)); + return (extensions != NULL) && RGFW_extensionSupportedStr(extensions, extension, len); +} RGFW_proc RGFW_getProcAddress(const char* procname) { return (RGFW_proc) glXGetProcAddress((GLubyte*) procname); } #endif @@ -3802,7 +3883,7 @@ void RGFW_window_getVisual(RGFW_window* win, RGFW_bool software) { if (best_fbc == -1) { RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to get a valid GLX visual"); return; - } + } win->src.bestFbc = fbc[best_fbc]; XVisualInfo* vi = glXGetVisualFromFBConfig(win->src.display, win->src.bestFbc); @@ -3814,6 +3895,7 @@ void RGFW_window_getVisual(RGFW_window* win, RGFW_bool software) { XFree(fbc); win->src.visual = *vi; + XFree(vi); #else RGFW_UNUSED(software); win->src.visual.visual = DefaultVisual(win->src.display, DefaultScreen(win->src.display)); @@ -3888,7 +3970,13 @@ RGFW_UNUSED(win); i32 RGFW_init(void) { - RGFW_GOTO_WAYLAND(1); + RGFW_GOTO_WAYLAND(1); +#if defined(RGFW_C89) || defined(__cplusplus) + if (_RGFW_init) return 0; + _RGFW_init = RGFW_TRUE; + _RGFW.root = NULL; _RGFW.current = NULL; _RGFW.windowCount = -1; _RGFW.eventLen = 0; _RGFW.eventIndex = 0; +#endif + #ifdef RGFW_X11 if (_RGFW.windowCount != -1) return 0; #ifdef RGFW_USE_XDL @@ -3937,6 +4025,7 @@ i32 RGFW_init(void) { XInitThreads(); /*!< init X11 threading */ _RGFW.display = XOpenDisplay(0); XSetWindowAttributes wa; + RGFW_MEMSET(&wa, 0, sizeof(wa)); wa.event_mask = PropertyChangeMask; _RGFW.helperWindow = XCreateWindow(_RGFW.display, XDefaultRootWindow(_RGFW.display), 0, 0, 1, 1, 0, 0, InputOnly, DefaultVisual(_RGFW.display, DefaultScreen(_RGFW.display)), CWEventMask, &wa); @@ -3953,7 +4042,7 @@ i32 RGFW_init(void) { XkbGetNames(_RGFW.display, XkbKeyNamesMask, desc); - memset(&rec, 0, sizeof(rec)); + RGFW_MEMSET(&rec, 0, sizeof(rec)); rec.keycodes = (char*)"evdev"; evdesc = XkbGetKeyboardByName(_RGFW.display, XkbUseCoreKbd, &rec, XkbGBN_KeyNamesMask, XkbGBN_KeyNamesMask, False); /* memo: RGFW_keycodes[x11 keycode] = rgfw keycode */ @@ -3979,7 +4068,7 @@ wayland: _RGFW.wl_display = wl_display_connect(NULL); #endif _RGFW.windowCount = 0; - RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, (RGFW_debugContext){0}, "global context initialized"); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context initialized"); return 0; } @@ -3999,21 +4088,18 @@ RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowF /* make X window attrubutes */ XSetWindowAttributes swa; + RGFW_MEMSET(&swa, 0, sizeof(swa)); + Colormap cmap; swa.colormap = cmap = XCreateColormap(win->src.display, DefaultRootWindow(win->src.display), win->src.visual.visual, AllocNone); - - swa.background_pixmap = None; - swa.border_pixel = 0; swa.event_mask = event_mask; - swa.background_pixel = 0; - /* create the window */ win->src.window = XCreateWindow(win->src.display, DefaultRootWindow(win->src.display), win->r.x, win->r.y, (u32)win->r.w, (u32)win->r.h, 0, win->src.visual.depth, InputOutput, win->src.visual.visual, - CWColormap | CWBorderPixel | CWBackPixel | CWEventMask, &swa); + CWColormap | CWBorderPixel | CWEventMask, &swa); XFreeColors(win->src.display, cmap, NULL, 0, 0); @@ -4085,9 +4171,11 @@ RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowF RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new window was created"); RGFW_window_setMouseDefault(win); RGFW_window_setFlags(win, flags); + + win->src.r = win->r; RGFW_window_show(win); - return win; /*return newly created window */ + return win; /*return newly created window */ #endif #ifdef RGFW_WAYLAND wayland: @@ -4625,7 +4713,7 @@ RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { if (version > 5) break; - size_t i; + size_t i; for (i = 0; i < win->event.droppedFilesCount; i++) win->event.droppedFiles[i][0] = '\0'; @@ -4742,7 +4830,11 @@ RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { win->_flags |= RGFW_windowFocus; win->event.type = RGFW_focusIn; RGFW_focusCallback(win, 1); - break; + + + RGFW_resetKey(); + if ((win->_flags & RGFW_HOLD_MOUSE)) RGFW_window_mouseHold(win, RGFW_AREA(win->r.w, win->r.h)); + break; case FocusOut: if ((win->_flags & RGFW_windowFullscreen)) RGFW_window_minimize(win); @@ -4769,17 +4861,17 @@ RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { case ConfigureNotify: { /* detect resize */ RGFW_window_checkMode(win); - if (E.xconfigure.width != win->r.w || E.xconfigure.height != win->r.h) { + if (E.xconfigure.width != win->src.r.w || E.xconfigure.height != win->src.r.h) { win->event.type = RGFW_windowResized; - win->r = RGFW_RECT(win->r.x, win->r.y, E.xconfigure.width, E.xconfigure.height); + win->src.r = win->r = RGFW_RECT(win->src.r.x, win->src.r.y, E.xconfigure.width, E.xconfigure.height); RGFW_windowResizedCallback(win, win->r); break; } /* detect move */ - if (E.xconfigure.x != win->r.x || E.xconfigure.y != win->r.y) { + if (E.xconfigure.x != win->src.r.x || E.xconfigure.y != win->src.r.y) { win->event.type = RGFW_windowMoved; - win->r = RGFW_RECT(E.xconfigure.x, E.xconfigure.y, win->r.w, win->r.h); + win->src.r = win->r = RGFW_RECT(E.xconfigure.x, E.xconfigure.y, win->src.r.w, win->src.r.h); RGFW_windowMovedCallback(win, win->r); break; } @@ -4876,11 +4968,9 @@ void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a) { void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { RGFW_ASSERT(win != NULL); - if (a.w == 0 && a.h == 0) - return; - + long flags; XSizeHints hints; - long flags; + RGFW_MEMSET(&hints, 0, sizeof(XSizeHints)); XGetWMNormalHints(win->src.display, win->src.window, &hints, &flags); @@ -4895,11 +4985,9 @@ void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { RGFW_ASSERT(win != NULL); - if (a.w == 0 && a.h == 0) - a = RGFW_getScreenSize(); - + long flags; XSizeHints hints; - long flags; + RGFW_MEMSET(&hints, 0, sizeof(XSizeHints)); XGetWMNormalHints(win->src.display, win->src.window, &hints, &flags); @@ -5058,9 +5146,14 @@ void RGFW_window_setName(RGFW_window* win, const char* name) { XStoreName(win->src.display, win->src.window, name); RGFW_LOAD_ATOM(_NET_WM_NAME); - XChangeProperty( + + char buf[256]; + RGFW_MEMSET(buf, 0, sizeof(buf)); + RGFW_STRNCPY(buf, name, sizeof(buf) - 1); + + XChangeProperty( win->src.display, win->src.window, _NET_WM_NAME, RGFW_XUTF8_STRING, - 8, PropModeReplace, (u8*)name, 256 + 8, PropModeReplace, (u8*)buf, sizeof(buf) ); #endif #ifdef RGFW_WAYLAND @@ -5310,12 +5403,13 @@ void RGFW_window_show(RGFW_window* win) { RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { RGFW_GOTO_WAYLAND(1); - #ifdef RGFW_X11 +#ifdef RGFW_X11 RGFW_init(); - if (XGetSelectionOwner(_RGFW.display, RGFW_XCLIPBOARD) == _RGFW.helperWindow) { - if (str != NULL) - RGFW_STRNCPY(str, _RGFW.clipboard, _RGFW.clipboard_len); - return (RGFW_ssize_t)_RGFW.clipboard_len; + if (XGetSelectionOwner(_RGFW.display, RGFW_XCLIPBOARD) == _RGFW.helperWindow) { + if (str != NULL) + RGFW_STRNCPY(str, _RGFW.clipboard, _RGFW.clipboard_len - 1); + _RGFW.clipboard[_RGFW.clipboard_len - 1] = '\0'; + return (RGFW_ssize_t)_RGFW.clipboard_len - 1; } XEvent event; @@ -5328,25 +5422,25 @@ RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { XConvertSelection(_RGFW.display, RGFW_XCLIPBOARD, RGFW_XUTF8_STRING, XSEL_DATA, _RGFW.helperWindow, CurrentTime); XSync(_RGFW.display, 0); - while (1) { - XNextEvent(_RGFW.display, &event); - if (event.type != SelectionNotify) continue; + while (1) { + XNextEvent(_RGFW.display, &event); + if (event.type != SelectionNotify) continue; - if (event.xselection.selection != RGFW_XCLIPBOARD || event.xselection.property == 0) - return -1; - break; + if (event.xselection.selection != RGFW_XCLIPBOARD || event.xselection.property == 0) + return -1; + break; } XGetWindowProperty(event.xselection.display, event.xselection.requestor, - event.xselection.property, 0L, (~0L), 0, AnyPropertyType, &target, - &format, &sizeN, &N, (u8**) &data); + event.xselection.property, 0L, (~0L), 0, AnyPropertyType, &target, + &format, &sizeN, &N, (u8**) &data); RGFW_ssize_t size; if (sizeN > strCapacity && str != NULL) size = -1; if ((target == RGFW_XUTF8_STRING || target == XA_STRING) && str != NULL) { - RGFW_MEMCPY(str, data, sizeN); + RGFW_MEMCPY(str, data, sizeN); str[sizeN] = '\0'; XFree(data); } else if (str != NULL) size = -1; @@ -5354,11 +5448,11 @@ RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property); size = (RGFW_ssize_t)sizeN; - return size; - #endif - #if defined(RGFW_WAYLAND) - wayland: return 0; - #endif + return size; +#endif +#if defined(RGFW_WAYLAND) +wayland: return 0; +#endif } i32 RGFW_XHandleClipboardSelectionHelper(void) { @@ -5407,9 +5501,10 @@ void RGFW_writeClipboard(const char* text, u32 textLen) { RGFW_FREE(_RGFW.clipboard); _RGFW.clipboard = (char*)RGFW_ALLOC(textLen); - RGFW_ASSERT(_RGFW.clipboard != NULL); + RGFW_ASSERT(_RGFW.clipboard != NULL); - RGFW_STRNCPY(_RGFW.clipboard, text, textLen); + RGFW_STRNCPY(_RGFW.clipboard, text, textLen - 1); + _RGFW.clipboard[textLen - 1] = '\0'; _RGFW.clipboard_len = textLen; #endif #ifdef RGFW_WAYLAND @@ -5543,7 +5638,8 @@ RGFW_monitor RGFW_XCreateMonitor(i32 screen) { RGFW_splitBPP((u32)DefaultDepth(display, DefaultScreen(display)), &monitor.mode); char* name = XDisplayName((const char*)display); - RGFW_MEMCPY(monitor.name, name, 128); + RGFW_STRNCPY(monitor.name, name, sizeof(monitor.name) - 1); + monitor.name[sizeof(monitor.name) - 1] = '\0'; float dpi = XGetSystemContentDPI(display, screen); monitor.pixelRatio = dpi >= 192.0f ? 2 : 1; @@ -5575,7 +5671,8 @@ RGFW_monitor RGFW_XCreateMonitor(i32 screen) { float physW = (float)info->mm_width / 25.4f; float physH = (float)info->mm_height / 25.4f; - RGFW_MEMCPY(monitor.name, info->name, 128); + RGFW_STRNCPY(monitor.name, info->name, sizeof(monitor.name) - 1); + monitor.name[sizeof(monitor.name) - 1] = '\0'; if ((u8)physW && (u8)physH) { monitor.physW = physW; @@ -5690,12 +5787,15 @@ wayland: } RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { - RGFW_ASSERT(win != NULL); + RGFW_monitor mon; + RGFW_MEMSET(&mon, 0, sizeof(mon)); + + RGFW_ASSERT(win != NULL); RGFW_GOTO_WAYLAND(1); #ifdef RGFW_X11 XWindowAttributes attrs; if (!XGetWindowAttributes(win->src.display, win->src.window, &attrs)) { - return (RGFW_monitor){0}; + return mon; } i32 i; @@ -5709,7 +5809,7 @@ RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { #ifdef RGFW_WAYLAND wayland: #endif - return (RGFW_monitor){0}; + return mon; } @@ -5796,7 +5896,7 @@ void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { #endif void RGFW_deinit(void) { - if (_RGFW.windowCount == -1) return; + if (_RGFW.windowCount == -1 || _RGFW_init == RGFW_FALSE) return; #define RGFW_FREE_LIBRARY(x) if (x != NULL) dlclose(x); x = NULL; #ifdef RGFW_X11 /* to save the clipboard on the x server after the window is closed */ @@ -5847,7 +5947,7 @@ void RGFW_deinit(void) { _RGFW.root = NULL; _RGFW.windowCount = -1; - RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, (RGFW_debugContext){0}, "global context deinitialized"); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context deinitialized"); } void RGFW_window_close(RGFW_window* win) { @@ -6120,7 +6220,20 @@ PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL; #define wglShareLists wglShareListsSRC #endif -#ifdef RGFW_OPENGL +#if defined(RGFW_OPENGL) && !defined(RGFW_EGL) +RGFW_bool RGFW_extensionSupportedPlatform(const char * extension, size_t len) { + const char* extensions = NULL; + + RGFW_proc proc = RGFW_getProcAddress("wglGetExtensionsStringARB"); + RGFW_proc proc2 = RGFW_getProcAddress("wglGetExtensionsStringEXT"); + + if (proc) + extensions = ((const char* (*)(HDC))proc)(wglGetCurrentDC()); + else if (proc2) + extensions = ((const char*(*)(void))proc2)(); + + return extensions != NULL && RGFW_extensionSupportedStr(extensions, extension, len); +} RGFW_proc RGFW_getProcAddress(const char* procname) { RGFW_proc proc = (RGFW_proc)wglGetProcAddress(procname); @@ -6174,14 +6287,14 @@ LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) switch (message) { case WM_CLOSE: case WM_QUIT: - RGFW_eventQueuePush((RGFW_event){.type = RGFW_quit, ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_quit; e._win = win); RGFW_windowQuitCallback(win); return 0; case WM_ACTIVATE: { RGFW_bool inFocus = RGFW_BOOL(LOWORD(wParam) != WA_INACTIVE); if (inFocus) win->_flags |= RGFW_windowFocus; else win->_flags &= ~ (u32)RGFW_windowFocus; - RGFW_eventQueuePush((RGFW_event){.type = (RGFW_eventType)((u8)RGFW_focusOut - inFocus), ._win = win}); + RGFW_eventQueuePushEx(e.type = (RGFW_eventType)((u8)RGFW_focusOut - inFocus); e._win = win); RGFW_focusCallback(win, inFocus); @@ -6196,7 +6309,7 @@ LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_MOVE: win->r.x = windowRect.left; win->r.y = windowRect.top; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowMoved, ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_windowMoved; e._win = win); RGFW_windowMovedCallback(win, win->r); return DefWindowProcW(hWnd, message, wParam, lParam); case WM_SIZE: { @@ -6224,7 +6337,7 @@ LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) win->r.w = windowRect.right - windowRect.left; win->r.h = (windowRect.bottom - windowRect.top) - (i32)win->src.hOffset; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowResized, ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_windowResized; e._win = win); RGFW_windowResizedCallback(win, win->r); RGFW_window_checkMode(win); return DefWindowProcW(hWnd, message, wParam, lParam); @@ -6236,7 +6349,7 @@ LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) const float scaleX = HIWORD(wParam) / (float) 96; const float scaleY = LOWORD(wParam) / (float) 96; RGFW_scaleUpdatedCallback(win, scaleX, scaleY); - RGFW_eventQueuePush((RGFW_event){.type = RGFW_scaleUpdated, .scaleX = scaleX, .scaleY = scaleY , ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_scaleUpdated; e.scaleX = scaleX; e.scaleY = scaleY; e._win = win); return DefWindowProcW(hWnd, message, wParam, lParam); } #endif @@ -6254,7 +6367,7 @@ LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_PAINT: { PAINTSTRUCT ps; BeginPaint(hWnd, &ps); - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowRefresh, ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_windowRefresh; e._win = win); RGFW_windowRefreshCallback(win); EndPaint(hWnd, &ps); @@ -6307,7 +6420,10 @@ LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) __declspec(dllimport) u32 __stdcall timeBeginPeriod(u32 uPeriod); __declspec(dllimport) u32 __stdcall timeEndPeriod(u32 uPeriod); #endif -#define RGFW_PROC_DEF(proc, name) if (name##SRC == NULL && proc != NULL) name##SRC = (PFN_##name)(RGFW_proc)GetProcAddress((proc), (#name)); +#define RGFW_PROC_DEF(proc, name) if (name##SRC == NULL && proc != NULL) { \ + name##SRC = (PFN_##name)(RGFW_proc)GetProcAddress((proc), (#name)); \ + RGFW_ASSERT(name##SRC != NULL); \ + } #ifndef RGFW_NO_XINPUT void RGFW_loadXInput(void); @@ -6322,9 +6438,9 @@ void RGFW_loadXInput(void) { } if (XInputGetStateSRC == NULL) - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errFailedFuncLoad, RGFW_DEBUG_CTX(.win = _RGFW.root, .srcError = 0), "Failed to load XInputGetState"); + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errFailedFuncLoad, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to load XInputGetState"); if (XInputGetKeystrokeSRC == NULL) - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errFailedFuncLoad, RGFW_DEBUG_CTX(.win = _RGFW.root, .srcError = 0), "Failed to load XInputGetKeystroke"); + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errFailedFuncLoad, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to load XInputGetKeystroke"); } #endif @@ -6333,7 +6449,7 @@ void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area){ win->buffer = buffer; win->bufferSize = area; - BITMAPV5HEADER bi = { 0 }; + BITMAPV5HEADER bi; ZeroMemory(&bi, sizeof(bi)); bi.bV5Size = sizeof(bi); bi.bV5Width = (i32)area.w; @@ -6383,7 +6499,7 @@ void RGFW_captureCursor(RGFW_window* win, RGFW_rect rect) { RegisterRawInputDevices(&id, 1, sizeof(id)); } -#define RGFW_LOAD_LIBRARY(x, lib) if (x == NULL) x = LoadLibraryA(lib) +#define RGFW_LOAD_LIBRARY(x, lib) if (x == NULL) { x = LoadLibraryA(lib); RGFW_ASSERT(x != NULL); } #ifdef RGFW_DIRECTX int RGFW_window_createDXSwapChain(RGFW_window* win, IDXGIFactory* pFactory, IUnknown* pDevice, IDXGISwapChain** swapchain) { @@ -6403,7 +6519,7 @@ int RGFW_window_createDXSwapChain(RGFW_window* win, IDXGIFactory* pFactory, IUnk HRESULT hr = pFactory->lpVtbl->CreateSwapChain(pFactory, (IUnknown*)pDevice, &swapChainDesc, swapchain); if (FAILED(hr)) { - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errDirectXContext, RGFW_DEBUG_CTX(.win = win, .srcError = hr), "Failed to create DirectX swap chain!"); + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errDirectXContext, RGFW_DEBUG_CTX(win, hr), "Failed to create DirectX swap chain!"); return -2; } @@ -6533,7 +6649,13 @@ void RGFW_window_freeOpenGL(RGFW_window* win) { i32 RGFW_init(void) { - #ifndef RGFW_NO_XINPUT +#if defined(RGFW_C89) || defined(__cplusplus) + if (_RGFW_init) return 0; + _RGFW_init = RGFW_TRUE; + _RGFW.root = NULL; _RGFW.current = NULL; _RGFW.windowCount = -1; _RGFW.eventLen = 0; _RGFW.eventIndex = 0; +#endif + + #ifndef RGFW_NO_XINPUT if (RGFW_XInput_dll == NULL) RGFW_loadXInput(); #endif @@ -6562,7 +6684,6 @@ i32 RGFW_init(void) { #ifndef RGFW_NO_LOAD_WGL RGFW_PROC_DEF(RGFW_wgl_dll, wglCreateContext); RGFW_PROC_DEF(RGFW_wgl_dll, wglDeleteContext); - RGFW_PROC_DEF(RGFW_wgl_dll, wglDeleteContext); RGFW_PROC_DEF(RGFW_wgl_dll, wglGetProcAddress); RGFW_PROC_DEF(RGFW_wgl_dll, wglMakeCurrent); RGFW_PROC_DEF(RGFW_wgl_dll, wglGetCurrentDC); @@ -6574,7 +6695,7 @@ i32 RGFW_init(void) { _RGFW.hiddenMouse = RGFW_loadMouse(RGFW_blk, RGFW_AREA(1, 1), 4); _RGFW.windowCount = 0; - RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, (RGFW_debugContext){0}, "global context initialized"); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context initialized"); return 1; } @@ -7082,7 +7203,7 @@ RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { break; unsigned size = sizeof(RAWINPUT); - static RAWINPUT raw = {0}; + static RAWINPUT raw; GetRawInputData((HRAWINPUT)msg.lParam, RID_INPUT, &raw, &size, sizeof(RAWINPUTHEADER)); @@ -7233,7 +7354,7 @@ RGFW_monitor win32CreateMonitor(HMONITOR src) { if (!(dd.StateFlags & DISPLAY_DEVICE_ACTIVE)) continue; - DEVMODE dm; + DEVMODEA dm; ZeroMemory(&dm, sizeof(dm)); dm.dmSize = sizeof(dm); @@ -7246,7 +7367,8 @@ RGFW_monitor win32CreateMonitor(HMONITOR src) { mdd.cb = sizeof(mdd); if (EnumDisplayDevicesA(dd.DeviceName, (DWORD)deviceNum, &mdd, 0)) { - RGFW_MEMCPY(monitor.name, mdd.DeviceString, 128); + RGFW_STRNCPY(monitor.name, mdd.DeviceString, sizeof(monitor.name) - 1); + monitor.name[sizeof(monitor.name) - 1] = '\0'; break; } } @@ -7334,13 +7456,14 @@ RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { } RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) { - HMONITOR src = MonitorFromPoint((POINT) { mon.x, mon.y }, MONITOR_DEFAULTTOPRIMARY); + POINT p = { mon.x, mon.y }; + HMONITOR src = MonitorFromPoint(p, MONITOR_DEFAULTTOPRIMARY); MONITORINFOEX monitorInfo; monitorInfo.cbSize = sizeof(MONITORINFOEX); GetMonitorInfoA(src, (LPMONITORINFO)&monitorInfo); - DISPLAY_DEVICE dd; + DISPLAY_DEVICEA dd; dd.cb = sizeof(dd); /* Enumerate display devices */ @@ -7349,10 +7472,10 @@ RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW if (!(dd.StateFlags & DISPLAY_DEVICE_ACTIVE)) continue; - if (strcmp(dd.DeviceName, monitorInfo.szDevice) != 0) + if (strcmp(dd.DeviceName, (const char*)monitorInfo.szDevice) != 0) continue; - DEVMODE dm; + DEVMODEA dm; ZeroMemory(&dm, sizeof(dm)); dm.dmSize = sizeof(dm); @@ -7500,7 +7623,7 @@ void RGFW_deinit(void) { RGFW_freeMouse(_RGFW.hiddenMouse); _RGFW.windowCount = -1; - RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, (RGFW_debugContext){0}, "global context deinitialized"); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context deinitialized"); } @@ -7885,10 +8008,10 @@ typedef RGFW_ENUM(NSUInteger, NSBitmapFormat) { NSBitmapFormatAlphaNonpremultiplied = 1 << 1, /* 0 means is premultiplied */ NSBitmapFormatFloatingpointSamples = 1 << 2, /* 0 is integer */ - NSBitmapFormatSixteenBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 8), - NSBitmapFormatThirtyTwoBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 9), - NSBitmapFormatSixteenBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 10), - NSBitmapFormatThirtyTwoBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 11) + NSBitmapFormatSixteenBitLittleEndian = (1 << 8), + NSBitmapFormatThirtyTwoBitLittleEndian = (1 << 9), + NSBitmapFormatSixteenBitBigEndian = (1 << 10), + NSBitmapFormatThirtyTwoBitBigEndian = (1 << 11) }; id NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits); @@ -7907,18 +8030,17 @@ id NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha ((id)nsclass, func, red, green, blue, alpha); } -#define NS_OPENGL_ENUM_DEPRECATED(minVers, maxVers) API_AVAILABLE(macos(minVers)) typedef RGFW_ENUM(NSInteger, NSOpenGLContextParameter) { - NSOpenGLContextParameterSwapInterval NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 222, /* 1 param. 0 -> Don't sync, 1 -> Sync to vertical retrace */ - NSOpenGLContextParametectxaceOrder NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 235, /* 1 param. 1 -> Above Window (default), -1 -> Below Window */ - NSOpenGLContextParametectxaceOpacity NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 236, /* 1 param. 1-> Surface is opaque (default), 0 -> non-opaque */ - NSOpenGLContextParametectxaceBackingSize NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 304, /* 2 params. Width/height of surface backing size */ - NSOpenGLContextParameterReclaimResources NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 308, /* 0 params. */ - NSOpenGLContextParameterCurrentRendererID NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 309, /* 1 param. Retrieves the current renderer ID */ - NSOpenGLContextParameterGPUVertexProcessing NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 310, /* 1 param. Currently processing vertices with GPU (get) */ - NSOpenGLContextParameterGPUFragmentProcessing NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 311, /* 1 param. Currently processing fragments with GPU (get) */ - NSOpenGLContextParameterHasDrawable NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 314, /* 1 param. Boolean returned if drawable is attached */ - NSOpenGLContextParameterMPSwapsInFlight NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 315, /* 1 param. Max number of swaps queued by the MP GL engine */ + NSOpenGLContextParameterSwapInterval = 222, /* 1 param. 0 -> Don't sync, 1 -> Sync to vertical retrace */ + NSOpenGLContextParametectxaceOrder = 235, /* 1 param. 1 -> Above Window (default), -1 -> Below Window */ + NSOpenGLContextParametectxaceOpacity = 236, /* 1 param. 1-> Surface is opaque (default), 0 -> non-opaque */ + NSOpenGLContextParametectxaceBackingSize = 304, /* 2 params. Width/height of surface backing size */ + NSOpenGLContextParameterReclaimResources = 308, /* 0 params. */ + NSOpenGLContextParameterCurrentRendererID = 309, /* 1 param. Retrieves the current renderer ID */ + NSOpenGLContextParameterGPUVertexProcessing = 310, /* 1 param. Currently processing vertices with GPU (get) */ + NSOpenGLContextParameterGPUFragmentProcessing = 311, /* 1 param. Currently processing fragments with GPU (get) */ + NSOpenGLContextParameterHasDrawable = 314, /* 1 param. Boolean returned if drawable is attached */ + NSOpenGLContextParameterMPSwapsInFlight = 315, /* 1 param. Max number of swaps queued by the MP GL engine */ NSOpenGLContextParameterSwapRectangle API_DEPRECATED("", macos(10.0, 10.14)) = 200, /* 4 params. Set or get the swap rectangle {x, y, w, h} */ NSOpenGLContextParameterSwapRectangleEnable API_DEPRECATED("", macos(10.0, 10.14)) = 201, /* Enable or disable the swap rectangle */ @@ -8065,6 +8187,8 @@ id NSWindow_contentView(id window) { */ #ifdef RGFW_OPENGL +/* MacOS opengl API spares us yet again (there are no extensions) */ +RGFW_bool RGFW_extensionSupportedPlatform(const char * extension, size_t len) { RGFW_UNUSED(extension); RGFW_UNUSED(len); return RGFW_FALSE; } CFBundleRef RGFWnsglFramework = NULL; RGFW_proc RGFW_getProcAddress(const char* procname) { @@ -8091,7 +8215,7 @@ u32 RGFW_OnClose(id self) { if (win == NULL) return true; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_quit, ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_quit; e._win = win); RGFW_windowQuitCallback(win); return false; @@ -8115,9 +8239,9 @@ NSDragOperation draggingUpdated(id self, SEL sel, id sender) { return 0; NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation")); - RGFW_eventQueuePush((RGFW_event){.type = RGFW_DNDInit, - .point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y)), - ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_DNDInit; + e.point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y)); + e._win = win); RGFW_dndInitCallback(win, win->event.point); return NSDragOperationCopy; @@ -8174,15 +8298,17 @@ bool performDragOperation(id self, SEL sel, id sender) { for (i = 0; i < count; i++) { id fileURL = objc_msgSend_arr(fileURLs, sel_registerName("objectAtIndex:"), i); const char *filePath = ((const char* (*)(id, SEL))objc_msgSend)(fileURL, sel_registerName("UTF8String")); - RGFW_MEMCPY(win->event.droppedFiles[i], filePath, RGFW_MAX_PATH); + RGFW_STRNCPY(win->event.droppedFiles[i], filePath, RGFW_MAX_PATH - 1); win->event.droppedFiles[i][RGFW_MAX_PATH - 1] = '\0'; } NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation")); - RGFW_eventQueuePush((RGFW_event){.type = RGFW_DND, - .point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y)), - .droppedFilesCount = (size_t)count, - ._win = win}); - + + win->event.droppedFilesCount = (size_t)count; + RGFW_eventQueuePushEx(e.type = RGFW_DND; + e.point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y)); + e.droppedFilesCount = (size_t)count; + e._win = win); + RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount); return false; @@ -8192,6 +8318,51 @@ bool performDragOperation(id self, SEL sel, id sender) { #include #include +u32 RGFW_osx_getFallbackRefreshRate(CGDirectDisplayID displayID) { + u32 refreshRate = 0; + io_iterator_t it; + io_service_t service; + CFNumberRef indexRef, clockRef, countRef; + uint32_t clock, count; + +#ifdef kIOMainPortDefault + if (IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceMatching("IOFramebuffer"), &it) != 0) +#elif defined(kIOMasterPortDefault) + if (IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceMatching("IOFramebuffer"), &it) != 0) +#endif + return RGFW_FALSE; + + while ((service = IOIteratorNext(it)) != 0) { + uint32_t index; + indexRef = (CFNumberRef)IORegistryEntryCreateCFProperty(service, CFSTR("IOFramebufferOpenGLIndex"), kCFAllocatorDefault, kNilOptions); + if (indexRef == 0) continue; + + if (CFNumberGetValue(indexRef, kCFNumberIntType, &index) && CGOpenGLDisplayMaskToDisplayID(1 << index) == displayID) { + CFRelease(indexRef); + break; + } + + CFRelease(indexRef); + } + + if (service) { + clockRef = (CFNumberRef)IORegistryEntryCreateCFProperty(service, CFSTR("IOFBCurrentPixelClock"), kCFAllocatorDefault, kNilOptions); + if (clockRef) { + if (CFNumberGetValue(clockRef, kCFNumberIntType, &clock) && clock) { + countRef = (CFNumberRef)IORegistryEntryCreateCFProperty(service, CFSTR("IOFBCurrentPixelCount"), kCFAllocatorDefault, kNilOptions); + if (countRef && CFNumberGetValue(countRef, kCFNumberIntType, &count) && count) { + refreshRate = (u32)RGFW_ROUND(clock / (double) count); + CFRelease(countRef); + } + } + CFRelease(clockRef); + } + } + + IOObjectRelease(it); + return refreshRate; +} + IOHIDDeviceRef RGFW_osxControllers[4] = {NULL}; size_t findControllerIndex(IOHIDDeviceRef device) { @@ -8239,10 +8410,10 @@ void RGFW__osxInputValueChangedCallback(void *context, IOReturn result, void *se RGFW_gamepadButtonCallback(_RGFW.root, (u16)index, button, (u8)intValue); RGFW_gamepadPressed[index][button].prev = RGFW_gamepadPressed[index][button].current; RGFW_gamepadPressed[index][button].current = RGFW_BOOL(intValue); - RGFW_eventQueuePush((RGFW_event){.type = intValue ? RGFW_gamepadButtonPressed: RGFW_gamepadButtonReleased, - .button = button, - .gamepad = (u16)index, - ._win = _RGFW.root}); + RGFW_eventQueuePushEx(e.type = intValue ? RGFW_gamepadButtonPressed: RGFW_gamepadButtonReleased; + e.button = button; + e.gamepad = (u16)index; + e._win = _RGFW.root); break; } case kHIDPage_GenericDesktop: { @@ -8264,12 +8435,15 @@ void RGFW__osxInputValueChangedCallback(void *context, IOReturn result, void *se default: return; } - RGFW_eventQueuePush((RGFW_event){.type = RGFW_gamepadAxisMove, - .gamepad = (u16)index, - .axis = {RGFW_gamepadAxes[index][0], RGFW_gamepadAxes[index][1], - RGFW_gamepadAxes[index][2], RGFW_gamepadAxes[index][3]}, - .whichAxis = whichAxis, - ._win = _RGFW.root}); + RGFW_event e; + e.type = RGFW_gamepadAxisMove; + e.gamepad = (u16)index; + e.whichAxis = whichAxis; + e._win = _RGFW.root; + for (size_t i = 0; i < 4; i++) + e.axis[i] = RGFW_gamepadAxes[index][i]; + + RGFW_eventQueuePush(e); RGFW_gamepadAxisCallback(_RGFW.root, (u16)index, RGFW_gamepadAxes[index], 2, whichAxis); } @@ -8313,9 +8487,9 @@ void RGFW__osxDeviceAddedCallback(void* context, IOReturn result, void *sender, RGFW_gamepads[i] = (u16)i; RGFW_gamepadCount++; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_gamepadConnected, - .gamepad = (u16)i, - ._win = _RGFW.root}); + RGFW_eventQueuePushEx(e.type = RGFW_gamepadConnected; + e.gamepad = (u16)i; + e._win = _RGFW.root); RGFW_gamepadCallback(_RGFW.root, (u16)i, 1); break; @@ -8337,9 +8511,9 @@ void RGFW__osxDeviceRemovedCallback(void *context, IOReturn result, void *sender if (index != (size_t)-1) RGFW_osxControllers[index] = NULL; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_gamepadDisconnected, - .gamepad = (u16)index, - ._win = _RGFW.root}); + RGFW_eventQueuePushEx(e.type = RGFW_gamepadDisconnected; + e.gamepad = (u16)index; + e._win = _RGFW.root); RGFW_gamepadCallback(_RGFW.root, (u16)index, 0); RGFW_gamepadCount--; @@ -8418,7 +8592,7 @@ void RGFW__osxWindowDeminiaturize(id self, SEL sel) { if (win == NULL) return; win->_flags |= RGFW_windowMinimize; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowRestored, ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_windowRestored; e._win = win); RGFW_windowRestoredCallback(win, win->r); } @@ -8429,7 +8603,7 @@ void RGFW__osxWindowMiniaturize(id self, SEL sel) { if (win == NULL) return; win->_flags &= ~(u32)RGFW_windowMinimize; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowMinimized, ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_windowMinimized; e._win = win); RGFW_windowMinimizedCallback(win, win->r); } @@ -8441,9 +8615,12 @@ void RGFW__osxWindowBecameKey(id self, SEL sel) { if (win == NULL) return; win->_flags |= RGFW_windowFocus; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_focusIn, ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_focusIn; e._win = win); RGFW_focusCallback(win, RGFW_TRUE); + + RGFW_resetKey(); + if ((win->_flags & RGFW_HOLD_MOUSE)) RGFW_window_mouseHold(win, RGFW_AREA(win->r.w, win->r.h)); } void RGFW__osxWindowResignKey(id self, SEL sel) { @@ -8453,7 +8630,7 @@ void RGFW__osxWindowResignKey(id self, SEL sel) { if (win == NULL) return; win->_flags &= ~(u32)RGFW_windowFocus; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_focusOut, ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_focusOut; e._win = win); RGFW_focusCallback(win, RGFW_FALSE); } @@ -8470,17 +8647,17 @@ NSSize RGFW__osxWindowResize(id self, SEL sel, NSSize frameSize) { RGFW_monitor mon = RGFW_window_getMonitor(win); if ((i32)mon.mode.area.w == win->r.w && (i32)mon.mode.area.h - 102 <= win->r.h) { win->_flags |= RGFW_windowMaximize; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowMaximized, ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_windowMaximized; e._win = win); RGFW_windowMaximizedCallback(win, win->r); } else if (win->_flags & RGFW_windowMaximize) { win->_flags &= ~(u32)RGFW_windowMaximize; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowRestored, ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_windowRestored; e._win = win); RGFW_windowRestoredCallback(win, win->r); } - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowResized, ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_windowResized; e._win = win); RGFW_windowResizedCallback(win, win->r); return frameSize; } @@ -8496,7 +8673,7 @@ void RGFW__osxWindowMove(id self, SEL sel) { win->r.x = (i32) frame.origin.x; win->r.y = (i32) frame.origin.y; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowMoved, ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_windowMoved; e._win = win); RGFW_windowMovedCallback(win, win->r); } @@ -8508,7 +8685,7 @@ void RGFW__osxViewDidChangeBackingProperties(id self, SEL _cmd) { RGFW_monitor mon = RGFW_window_getMonitor(win); RGFW_scaleUpdatedCallback(win, mon.scaleX, mon.scaleY); - RGFW_eventQueuePush((RGFW_event){.type = RGFW_scaleUpdated, .scaleX = mon.scaleX, .scaleY = mon.scaleY , ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_scaleUpdated; e.scaleX = mon.scaleX; e.scaleY = mon.scaleY ; e._win = win); } void RGFW__osxDrawRect(id self, SEL _cmd, CGRect rect) { @@ -8517,7 +8694,7 @@ void RGFW__osxDrawRect(id self, SEL _cmd, CGRect rect) { object_getInstanceVariable(self, "RGFW_window", (void**)&win); if (win == NULL) return; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowRefresh, ._win = win}); + RGFW_eventQueuePushEx(e.type = RGFW_windowRefresh; e._win = win); RGFW_windowRefreshCallback(win); } @@ -8614,6 +8791,12 @@ void RGFW_window_freeOpenGL(RGFW_window* win) { i32 RGFW_init(void) { +#if defined(RGFW_C89) || defined(__cplusplus) + if (_RGFW_init) return 0; + _RGFW_init = RGFW_TRUE; + _RGFW.root = NULL; _RGFW.current = NULL; _RGFW.windowCount = -1; _RGFW.eventLen = 0; _RGFW.eventIndex = 0; +#endif + /* NOTE(EimaMei): Why does Apple hate good code? Like wtf, who thought of methods being a great idea??? Imagine a universe, where MacOS had a proper system API (we would probably have like 20% better performance). */ @@ -8635,7 +8818,7 @@ i32 RGFW_init(void) { } _RGFW.windowCount = 0; - RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, (RGFW_debugContext){0}, "global context initialized"); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context initialized"); return 0; } @@ -8829,20 +9012,20 @@ typedef RGFW_ENUM(u32, NSEventType) { /* various types of events */ NSEventTypeOtherMouseUp = 26, NSEventTypeOtherMouseDragged = 27, /* The following event types are available on some hardware on 10.5.2 and later */ - NSEventTypeGesture API_AVAILABLE(macos(10.5)) = 29, - NSEventTypeMagnify API_AVAILABLE(macos(10.5)) = 30, - NSEventTypeSwipe API_AVAILABLE(macos(10.5)) = 31, - NSEventTypeRotate API_AVAILABLE(macos(10.5)) = 18, - NSEventTypeBeginGesture API_AVAILABLE(macos(10.5)) = 19, - NSEventTypeEndGesture API_AVAILABLE(macos(10.5)) = 20, + NSEventTypeGesture = 29, + NSEventTypeMagnify = 30, + NSEventTypeSwipe = 31, + NSEventTypeRotate = 18, + NSEventTypeBeginGesture = 19, + NSEventTypeEndGesture = 20, - NSEventTypeSmartMagnify API_AVAILABLE(macos(10.8)) = 32, - NSEventTypeQuickLook API_AVAILABLE(macos(10.8)) = 33, + NSEventTypeSmartMagnify = 32, + NSEventTypeQuickLook = 33, - NSEventTypePressure API_AVAILABLE(macos(10.10.3)) = 34, - NSEventTypeDirectTouch API_AVAILABLE(macos(10.10)) = 37, + NSEventTypePressure = 34, + NSEventTypeDirectTouch = 37, - NSEventTypeChangeMode API_AVAILABLE(macos(10.15)) = 38, + NSEventTypeChangeMode = 38, }; typedef unsigned long long NSEventMask; @@ -9424,6 +9607,7 @@ id RGFW_getNSScreenForDisplayID(CGDirectDisplayID display) { return NULL; } +u32 RGFW_osx_getFallbackRefreshRate(CGDirectDisplayID displayID); u32 RGFW_osx_getRefreshRate(CGDirectDisplayID display, CGDisplayModeRef mode) { if (mode) { @@ -9431,13 +9615,13 @@ u32 RGFW_osx_getRefreshRate(CGDirectDisplayID display, CGDisplayModeRef mode) { if (refreshRate != 0) return refreshRate; } - CVDisplayLinkRef link; - CVDisplayLinkCreateWithCGDisplay(display, &link); - const CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link); - if (!(time.flags & kCVTimeIsIndefinite)) - return (u32) (time.timeScale / (double) time.timeValue); - - return 0; +#ifndef RGFW_NO_IOKIT + u32 res = RGFW_osx_getFallbackRefreshRate(display); + if (res != 0) return res; +#else + RGFW_UNUSED(display); +#endif + return 60; } RGFW_monitor RGFW_NSCreateMonitor(CGDirectDisplayID display, id screen) { @@ -9633,7 +9817,7 @@ void RGFW_window_swapBuffers_software(RGFW_window* win) { void RGFW_deinit(void) { _RGFW.windowCount = -1; - RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, (RGFW_debugContext){0}, "global context deinitialized"); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context deinitialized"); } void RGFW_window_close(RGFW_window* win) { @@ -9646,7 +9830,7 @@ void RGFW_window_close(RGFW_window* win) { RGFW_FREE(win->buffer); #endif - RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, (RGFW_debugContext){0}, "global context deinitialized"); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context deinitialized"); _RGFW.windowCount--; if (_RGFW.windowCount == 0) RGFW_deinit(); @@ -9682,15 +9866,15 @@ u64 RGFW_getTimerValue(void) { return (u64)mach_absolute_time(); } */ #ifdef RGFW_WASM -EM_BOOL Emscripten_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_eventQueuePush((RGFW_event){.type = RGFW_windowResized, ._win = _RGFW.root}); - RGFW_windowResizedCallback(_RGFW.root, RGFW_RECT(0, 0, e->windowInnerWidth, e->windowInnerHeight)); + RGFW_eventQueuePushEx(e.type = RGFW_windowResized; e._win = _RGFW.root); + RGFW_windowResizedCallback(_RGFW.root, RGFW_RECT(0, 0, E->windowInnerWidth, E->windowInnerHeight)); return EM_TRUE; } -EM_BOOL Emscripten_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); static u8 fullscreen = RGFW_FALSE; static RGFW_rect ogRect; @@ -9700,8 +9884,8 @@ EM_BOOL Emscripten_on_fullscreenchange(int eventType, const EmscriptenFullscreen } fullscreen = !fullscreen; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_windowResized, ._win = _RGFW.root}); - _RGFW.root->r = RGFW_RECT(0, 0, e->screenWidth, e->screenHeight); + RGFW_eventQueuePushEx(e.type = RGFW_windowResized; e._win = _RGFW.root); + _RGFW.root->r = RGFW_RECT(0, 0, E->screenWidth, E->screenHeight); EM_ASM("Module.canvas.focus();"); @@ -9728,49 +9912,52 @@ EM_BOOL Emscripten_on_fullscreenchange(int eventType, const EmscriptenFullscreen -EM_BOOL Emscripten_on_focusin(int eventType, const EmscriptenFocusEvent* e, void* userData) { - RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(e); +EM_BOOL Emscripten_on_focusin(int eventType, const EmscriptenFocusEvent* E, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(E); - RGFW_eventQueuePush((RGFW_event){.type = RGFW_focusIn, ._win = _RGFW.root}); + RGFW_eventQueuePushEx(e.type = RGFW_focusIn; e._win = _RGFW.root); _RGFW.root->_flags |= RGFW_windowFocus; RGFW_focusCallback(_RGFW.root, 1); + + RGFW_resetKey(); + if ((_RGFW.root->_flags & RGFW_HOLD_MOUSE)) RGFW_window_mouseHold(_RGFW.root, RGFW_AREA(_RGFW.root->r.w, _RGFW.root->r.h)); return EM_TRUE; } -EM_BOOL Emscripten_on_focusout(int eventType, const EmscriptenFocusEvent* e, void* userData) { - RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(e); +EM_BOOL Emscripten_on_focusout(int eventType, const EmscriptenFocusEvent* E, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(E); - RGFW_eventQueuePush((RGFW_event){.type = RGFW_focusOut, ._win = _RGFW.root}); + RGFW_eventQueuePushEx(e.type = RGFW_focusOut; e._win = _RGFW.root); _RGFW.root->_flags &= ~(u32)RGFW_windowFocus; RGFW_focusCallback(_RGFW.root, 0); return EM_TRUE; } -EM_BOOL Emscripten_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_eventQueuePush((RGFW_event){.type = RGFW_mousePosChanged, - .point = RGFW_POINT(e->targetX, e->targetY), - .vector = RGFW_POINT(e->movementX, e->movementY), - ._win = _RGFW.root}); + RGFW_eventQueuePushEx(e.type = RGFW_mousePosChanged; + e.point = RGFW_POINT(E->targetX, E->targetY); + e.vector = RGFW_POINT(E->movementX, E->movementY); + e._win = _RGFW.root); - _RGFW.root->_lastMousePoint = RGFW_POINT(e->targetX, e->targetY); - RGFW_mousePosCallback(_RGFW.root, RGFW_POINT(e->targetX, e->targetY), RGFW_POINT(e->movementX, e->movementY)); + _RGFW.root->_lastMousePoint = RGFW_POINT(E->targetX, E->targetY); + RGFW_mousePosCallback(_RGFW.root, RGFW_POINT(E->targetX, E->targetY), RGFW_POINT(E->movementX, E->movementY)); return EM_TRUE; } -EM_BOOL Emscripten_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); - int button = e->button; + int button = E->button; if (button > 2) button += 2; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonPressed, - .point = RGFW_POINT(e->targetX, e->targetY), - .vector = RGFW_POINT(e->movementX, e->movementY), - .button = (u8)button, - .scroll = 0, - ._win = _RGFW.root}); + RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonPressed; + e.point = RGFW_POINT(E->targetX, E->targetY); + e.vector = RGFW_POINT(E->movementX, E->movementY); + e.button = (u8)button; + e.scroll = 0; + e._win = _RGFW.root); RGFW_mouseButtons[button].prev = RGFW_mouseButtons[button].current; RGFW_mouseButtons[button].current = 1; @@ -9778,19 +9965,19 @@ EM_BOOL Emscripten_on_mousedown(int eventType, const EmscriptenMouseEvent* e, vo return EM_TRUE; } -EM_BOOL Emscripten_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); - int button = e->button; + int button = E->button; if (button > 2) button += 2; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonReleased, - .point = RGFW_POINT(e->targetX, e->targetY), - .vector = RGFW_POINT(e->movementX, e->movementY), - .button = (u8)button, - .scroll = 0, - ._win = _RGFW.root}); + RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonReleased; + e.point = RGFW_POINT(E->targetX, E->targetY); + e.vector = RGFW_POINT(E->movementX, E->movementY); + e.button = (u8)button; + e.scroll = 0; + e._win = _RGFW.root); RGFW_mouseButtons[button].prev = RGFW_mouseButtons[button].current; RGFW_mouseButtons[button].current = 0; @@ -9798,78 +9985,78 @@ EM_BOOL Emscripten_on_mouseup(int eventType, const EmscriptenMouseEvent* e, void return EM_TRUE; } -EM_BOOL Emscripten_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); - int button = RGFW_mouseScrollUp + (e->deltaY < 0); - RGFW_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonPressed, - .button = (u8)button, - .scroll = (double)(e->deltaY < 0 ? 1 : -1), - ._win = _RGFW.root}); + int button = RGFW_mouseScrollUp + (E->deltaY < 0); + RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonPressed; + e.button = (u8)button; + e.scroll = (double)(E->deltaY < 0 ? 1 : -1); + e._win = _RGFW.root); RGFW_mouseButtons[button].prev = RGFW_mouseButtons[button].current; RGFW_mouseButtons[button].current = 1; - RGFW_mouseButtonCallback(_RGFW.root, button, e->deltaY < 0 ? 1 : -1, 1); + RGFW_mouseButtonCallback(_RGFW.root, button, E->deltaY < 0 ? 1 : -1, 1); return EM_TRUE; } -EM_BOOL Emscripten_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); size_t i; - for (i = 0; i < (size_t)e->numTouches; i++) { - RGFW_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonPressed, - .point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY), - .button = RGFW_mouseLeft, - ._win = _RGFW.root}); + for (i = 0; i < (size_t)E->numTouches; i++) { + RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonPressed; + e.point = RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY); + e.button = RGFW_mouseLeft; + e._win = _RGFW.root); RGFW_mouseButtons[RGFW_mouseLeft].prev = RGFW_mouseButtons[RGFW_mouseLeft].current; RGFW_mouseButtons[RGFW_mouseLeft].current = 1; - _RGFW.root->_lastMousePoint = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY); - RGFW_mousePosCallback(_RGFW.root, RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY), _RGFW.root->event.vector); + _RGFW.root->_lastMousePoint = RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY); + RGFW_mousePosCallback(_RGFW.root, RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY), _RGFW.root->event.vector); RGFW_mouseButtonCallback(_RGFW.root, RGFW_mouseLeft, 0, 1); } return EM_TRUE; } -EM_BOOL Emscripten_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_eventQueuePush((RGFW_event){.type = RGFW_mousePosChanged, - .point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY), - .button = RGFW_mouseLeft, - ._win = _RGFW.root}); + for (i = 0; i < (size_t)E->numTouches; i++) { + RGFW_eventQueuePushEx(e.type = RGFW_mousePosChanged; + e.point = RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY); + e.button = RGFW_mouseLeft; + e._win = _RGFW.root); - _RGFW.root->_lastMousePoint = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY); - RGFW_mousePosCallback(_RGFW.root, RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY), _RGFW.root->event.vector); + _RGFW.root->_lastMousePoint = RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY); + RGFW_mousePosCallback(_RGFW.root, RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY), _RGFW.root->event.vector); } return EM_TRUE; } -EM_BOOL Emscripten_on_touchend(int eventType, const EmscriptenTouchEvent* e, void* userData) { +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_eventQueuePush((RGFW_event){.type = RGFW_mouseButtonReleased, - .point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY), - .button = RGFW_mouseLeft, - ._win = _RGFW.root}); + for (i = 0; i < (size_t)E->numTouches; i++) { + RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonReleased; + e.point = RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY); + e.button = RGFW_mouseLeft; + e._win = _RGFW.root); RGFW_mouseButtons[RGFW_mouseLeft].prev = RGFW_mouseButtons[RGFW_mouseLeft].current; RGFW_mouseButtons[RGFW_mouseLeft].current = 0; - _RGFW.root->_lastMousePoint = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY); - RGFW_mousePosCallback(_RGFW.root, RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY), _RGFW.root->event.vector); + _RGFW.root->_lastMousePoint = RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY); + RGFW_mousePosCallback(_RGFW.root, RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY), _RGFW.root->event.vector); RGFW_mouseButtonCallback(_RGFW.root, RGFW_mouseLeft, 0, 0); } return EM_TRUE; } -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_touchcancel(int eventType, const EmscriptenTouchEvent* E, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); return EM_TRUE; } EM_BOOL Emscripten_on_gamepad(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); @@ -9879,7 +10066,8 @@ EM_BOOL Emscripten_on_gamepad(int eventType, const EmscriptenGamepadEvent *gamep size_t i = gamepadEvent->index; if (gamepadEvent->connected) { - RGFW_MEMCPY(RGFW_gamepads_name[gamepadEvent->index], gamepadEvent->id, sizeof(RGFW_gamepads_name[gamepadEvent->index])); + RGFW_STRNCPY(RGFW_gamepads_name[gamepadEvent->index], gamepadEvent->id, sizeof(RGFW_gamepads_name[gamepadEvent->index]) - 1); + RGFW_gamepads_name[gamepadEvent->index][sizeof(RGFW_gamepads_name[gamepadEvent->index]) - 1] = '\0'; RGFW_gamepads_type[i] = RGFW_gamepadUnknown; if (RGFW_STRSTR(RGFW_gamepads_name[i], "Microsoft") || RGFW_STRSTR(RGFW_gamepads_name[i], "X-Box")) RGFW_gamepads_type[i] = RGFW_gamepadMicrosoft; @@ -9894,9 +10082,9 @@ EM_BOOL Emscripten_on_gamepad(int eventType, const EmscriptenGamepadEvent *gamep RGFW_gamepadCount--; } - RGFW_eventQueuePush((RGFW_event){.type = (RGFW_eventType)(gamepadEvent->connected ? RGFW_gamepadConnected : RGFW_gamepadConnected), - .gamepad = (u16)gamepadEvent->index, - ._win = _RGFW.root}); + RGFW_eventQueuePushEx(e.type = (RGFW_eventType)(gamepadEvent->connected ? RGFW_gamepadConnected : RGFW_gamepadConnected); + e.gamepad = (u16)gamepadEvent->index; + e._win = _RGFW.root); RGFW_gamepadCallback(_RGFW.root, gamepadEvent->index, gamepadEvent->connected); RGFW_gamepads[gamepadEvent->index] = gamepadEvent->connected; @@ -10022,11 +10210,11 @@ void EMSCRIPTEN_KEEPALIVE RGFW_handleKeyEvent(char* key, char* code, RGFW_bool p if (*((u32*)key) == *((u32*)"Tab")) mappedKey = RGFW_tab; } - RGFW_eventQueuePush((RGFW_event){.type = (RGFW_eventType)(press ? RGFW_keyPressed : RGFW_keyReleased), - .key = (u8)physicalKey, - .keyChar = (u8)mappedKey, - .keyMod = _RGFW.root->event.keyMod, - ._win = _RGFW.root}); + RGFW_eventQueuePushEx(e.type = (RGFW_eventType)(press ? RGFW_keyPressed : RGFW_keyReleased); + e.key = (u8)physicalKey; + e.keyChar = (u8)mappedKey; + e.keyMod = _RGFW.root->event.keyMod; + e._win = _RGFW.root); RGFW_keyboard[physicalKey].prev = RGFW_keyboard[physicalKey].current; RGFW_keyboard[physicalKey].current = press; @@ -10042,9 +10230,10 @@ void EMSCRIPTEN_KEEPALIVE Emscripten_onDrop(size_t count) { if (!(_RGFW.root->_flags & RGFW_windowAllowDND)) return; - RGFW_eventQueuePush((RGFW_event){.type = RGFW_DND, - .droppedFilesCount = count, - ._win = _RGFW.root}); + _RGFW.root->event.droppedFilesCount = count; + RGFW_eventQueuePushEx(e.type = RGFW_DND; + e.droppedFilesCount = count; + e._win = _RGFW.root); RGFW_dndCallback(_RGFW.root, _RGFW.root->event.droppedFiles, count); } @@ -10083,7 +10272,8 @@ 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 */ - RGFW_MEMCPY((char*)_RGFW.root->event.droppedFiles[index], file, RGFW_MAX_PATH); + RGFW_STRNCPY((char*)_RGFW.root->event.droppedFiles[index], file, RGFW_MAX_PATH - 1); + _RGFW.root->event.droppedFiles[index][RGFW_MAX_PATH - 1] = '\0'; } #include @@ -10151,7 +10341,17 @@ void RGFW_window_freeOpenGL(RGFW_window* win) { #endif } -i32 RGFW_init(void) { _RGFW.windowCount = 0; RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, (RGFW_debugContext){0}, "global context initialized"); return 0; } +i32 RGFW_init(void) { +#if defined(RGFW_C89) || defined(__cplusplus) + if (_RGFW_init) return 0; + _RGFW_init = RGFW_TRUE; + _RGFW.root = NULL; _RGFW.current = NULL; _RGFW.windowCount = -2; _RGFW.eventLen = 0; _RGFW.eventIndex = 0; +#endif + + _RGFW.windowCount = 0; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context initialized"); + return 0; +} RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) { RGFW_window_basic_init(win, rect, flags); @@ -10462,7 +10662,7 @@ void* RGFW_getCurrent_OpenGL(void) { return (void*)emscripten_webgl_get_current_ void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { RGFW_UNUSED(win); RGFW_UNUSED(swapInterval); } #endif -void RGFW_deinit(void) { _RGFW.windowCount = -1; RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, (RGFW_debugContext){0}, "global context deinitialized"); } +void RGFW_deinit(void) { _RGFW.windowCount = -1; RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context deinitialized"); } void RGFW_window_close(RGFW_window* win) { if ((win->_flags & RGFW_windowNoInitAPI) == 0) RGFW_window_freeOpenGL(win); @@ -10491,8 +10691,28 @@ RGFW_area RGFW_getScreenSize(void) { return RGFW_AREA(RGFW_innerWidth(), RGFW_innerHeight()); } +RGFW_bool RGFW_extensionSupportedPlatform(const char* extension, size_t len) { +#ifdef RGFW_OPENGL + return EM_ASM_INT({ + var ext = UTF8ToString($0, $1); + var canvas = document.querySelector('canvas'); + var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); + if (!gl) return 0; + + var supported = gl.getSupportedExtensions(); + return supported && supported.includes(ext) ? 1 : 0; + }, extension, len); +#else + return RGFW_FALSE; +#endif +} + RGFW_proc RGFW_getProcAddress(const char* procname) { - return (RGFW_proc)emscripten_webgl_get_proc_address(procname); +#ifdef RGFW_OPENGL + return (RGFW_proc)emscripten_webgl_get_proc_address(procname); +#else + return NULL +#endif } void RGFW_sleep(u64 milisecond) { diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 8498ddf17..c57ead1d6 100644 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -177,6 +177,7 @@ static const unsigned short keyMappingRGFW[] = { [RGFW_shiftR] = KEY_RIGHT_SHIFT, [RGFW_controlR] = KEY_RIGHT_CONTROL, [RGFW_altR] = KEY_RIGHT_ALT, + [RGFW_superR] = KEY_RIGHT_SUPER, #endif [RGFW_space] = KEY_SPACE, From 296e3af470b5afbc8e3eb30c925166d761974769 Mon Sep 17 00:00:00 2001 From: jestarray <34615798+jestarray@users.noreply.github.com> Date: Sat, 31 May 2025 14:11:00 -0700 Subject: [PATCH 025/242] add const qualifier to ImageDrawTriangleFan and ImageDrawTriangleStrip arguments --- projects/Notepad++/raylib_npp_parser/raylib_to_parse.h | 4 ++-- src/raylib.h | 4 ++-- src/rtextures.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h index 89cce66f9..27e9b79a2 100644 --- a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h +++ b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h @@ -440,8 +440,8 @@ RLAPI void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color c RLAPI void ImageDrawTriangle(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle within an image RLAPI void ImageDrawTriangleEx(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color c1, Color c2, Color c3); // Draw triangle with interpolated colors within an image RLAPI void ImageDrawTriangleLines(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline within an image -RLAPI void ImageDrawTriangleFan(Image *dst, Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points within an image (first vertex is the center) -RLAPI void ImageDrawTriangleStrip(Image *dst, Vector2 *points, int pointCount, Color color); // Draw a triangle strip defined by points within an image +RLAPI void ImageDrawTriangleFan(Image *dst, const Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points within an image (first vertex is the center) +RLAPI void ImageDrawTriangleStrip(Image *dst, const Vector2 *points, int pointCount, Color color); // Draw a triangle strip defined by points within an image RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color tint); // Draw a source image within a destination image (tint applied to source) RLAPI void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) within an image (destination) RLAPI void ImageDrawTextEx(Image *dst, Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text (custom sprite font) within an image (destination) diff --git a/src/raylib.h b/src/raylib.h index 3601faa25..71d346ea7 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1408,8 +1408,8 @@ RLAPI void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color c RLAPI void ImageDrawTriangle(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle within an image RLAPI void ImageDrawTriangleEx(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color c1, Color c2, Color c3); // Draw triangle with interpolated colors within an image RLAPI void ImageDrawTriangleLines(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline within an image -RLAPI void ImageDrawTriangleFan(Image *dst, Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points within an image (first vertex is the center) -RLAPI void ImageDrawTriangleStrip(Image *dst, Vector2 *points, int pointCount, Color color); // Draw a triangle strip defined by points within an image +RLAPI void ImageDrawTriangleFan(Image *dst, const Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points within an image (first vertex is the center) +RLAPI void ImageDrawTriangleStrip(Image *dst, const Vector2 *points, int pointCount, Color color); // Draw a triangle strip defined by points within an image RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color tint); // Draw a source image within a destination image (tint applied to source) RLAPI void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) within an image (destination) RLAPI void ImageDrawTextEx(Image *dst, Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text (custom sprite font) within an image (destination) diff --git a/src/rtextures.c b/src/rtextures.c index 7a404a6de..7a5a5e57e 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3891,7 +3891,7 @@ void ImageDrawTriangleLines(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Colo } // Draw a triangle fan defined by points within an image (first vertex is the center) -void ImageDrawTriangleFan(Image *dst, Vector2 *points, int pointCount, Color color) +void ImageDrawTriangleFan(Image *dst, const Vector2 *points, int pointCount, Color color) { if (pointCount >= 3) { @@ -3903,7 +3903,7 @@ void ImageDrawTriangleFan(Image *dst, Vector2 *points, int pointCount, Color col } // Draw a triangle strip defined by points within an image -void ImageDrawTriangleStrip(Image *dst, Vector2 *points, int pointCount, Color color) +void ImageDrawTriangleStrip(Image *dst, const Vector2 *points, int pointCount, Color color) { if (pointCount >= 3) { From 58a6846c8f1c11b2e7007109a8455604453c9b61 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 31 May 2025 21:26:50 +0000 Subject: [PATCH 026/242] Update raylib_api.* by CI --- parser/output/raylib_api.json | 4 ++-- parser/output/raylib_api.lua | 4 ++-- parser/output/raylib_api.txt | 4 ++-- parser/output/raylib_api.xml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index efbac71b0..e796478e4 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -8302,7 +8302,7 @@ "name": "dst" }, { - "type": "Vector2 *", + "type": "const Vector2 *", "name": "points" }, { @@ -8325,7 +8325,7 @@ "name": "dst" }, { - "type": "Vector2 *", + "type": "const Vector2 *", "name": "points" }, { diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index 08615aa22..a2897a226 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -6141,7 +6141,7 @@ return { returnType = "void", params = { {type = "Image *", name = "dst"}, - {type = "Vector2 *", name = "points"}, + {type = "const Vector2 *", name = "points"}, {type = "int", name = "pointCount"}, {type = "Color", name = "color"} } @@ -6152,7 +6152,7 @@ return { returnType = "void", params = { {type = "Image *", name = "dst"}, - {type = "Vector2 *", name = "points"}, + {type = "const Vector2 *", name = "points"}, {type = "int", name = "pointCount"}, {type = "Color", name = "color"} } diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index 2f44d94cc..4de3e267a 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -3185,7 +3185,7 @@ Function 352: ImageDrawTriangleFan() (4 input parameters) Return type: void Description: Draw a triangle fan defined by points within an image (first vertex is the center) Param[1]: dst (type: Image *) - Param[2]: points (type: Vector2 *) + Param[2]: points (type: const Vector2 *) Param[3]: pointCount (type: int) Param[4]: color (type: Color) Function 353: ImageDrawTriangleStrip() (4 input parameters) @@ -3193,7 +3193,7 @@ Function 353: ImageDrawTriangleStrip() (4 input parameters) Return type: void Description: Draw a triangle strip defined by points within an image Param[1]: dst (type: Image *) - Param[2]: points (type: Vector2 *) + Param[2]: points (type: const Vector2 *) Param[3]: pointCount (type: int) Param[4]: color (type: Color) Function 354: ImageDraw() (5 input parameters) diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index 52f7f5b1d..e79e55f4f 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -2090,13 +2090,13 @@ - + - + From 19ae6f2c2d5b7789490fcee549337f3fd804359b Mon Sep 17 00:00:00 2001 From: Vincent Lee Date: Sat, 31 May 2025 22:58:18 -0700 Subject: [PATCH 027/242] [rshapes] Fix incorrect parameter names in DrawRectangleGradientEx Examining the code shows that the rectangle is drawn winding counterclockwise, starting with the top left. Therefore the colors used should be in the order: topLeft, bottomLeft, bottomRight, topRight. However, the variables actually being used are topLeft, bottomLeft, topRight, bottomRight. I was confused by this as I was getting striping where I didn't expect any. Put another way, the last two parameters are misnamed. This diff swaps the parameter names and their usages. The result is that no runtime behaviour changes: the same parameter order yields the same visual result both before and after this change, but the parameter names now correctly reflect what they are actually used for. You can actually see this in the implementation of DrawRectangleGradientV, which (correctly) passes top, bottom, bottom, top to DrawRectangleGradientEx. --- src/raylib.h | 2 +- src/rshapes.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index fc949a02d..7d24b1410 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1273,7 +1273,7 @@ RLAPI void DrawRectangleRec(Rectangle rec, Color color); 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 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 DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color bottomRight, Color topRight); // 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 c739f4162..704aede39 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -774,7 +774,7 @@ void DrawRectangleGradientH(int posX, int posY, int width, int height, Color lef } // Draw a gradient-filled rectangle -void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color topRight, Color bottomRight) +void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color bottomRight, Color topRight) { rlSetTexture(GetShapesTexture().id); Rectangle shapeRect = GetShapesTextureRectangle(); @@ -791,11 +791,11 @@ void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Col rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); rlVertex2f(rec.x, rec.y + rec.height); - rlColor4ub(topRight.r, topRight.g, topRight.b, topRight.a); + rlColor4ub(bottomRight.r, bottomRight.g, bottomRight.b, bottomRight.a); rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); rlVertex2f(rec.x + rec.width, rec.y + rec.height); - rlColor4ub(bottomRight.r, bottomRight.g, bottomRight.b, bottomRight.a); + rlColor4ub(topRight.r, topRight.g, topRight.b, topRight.a); rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); rlVertex2f(rec.x + rec.width, rec.y); rlEnd(); From b52a9f8a0493d4f70842d235a0cb452125109c93 Mon Sep 17 00:00:00 2001 From: garrisonhh Date: Sun, 1 Jun 2025 10:11:42 -0400 Subject: [PATCH 028/242] Add LICENSE to build.zig.zon --- build.zig.zon | 1 + 1 file changed, 1 insertion(+) diff --git a/build.zig.zon b/build.zig.zon index f18d9db84..3bf82afe5 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -23,5 +23,6 @@ "build.zig.zon", "src", "examples", + "LICENSE", }, } From 51958d6e2cb9ac3167b81187f36d6b2149d24a94 Mon Sep 17 00:00:00 2001 From: alqeeu Date: Sun, 1 Jun 2025 17:37:31 +0300 Subject: [PATCH 029/242] changed `RGFW_window_eventWait` timeout to -1 --- src/platforms/rcore_desktop_rgfw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index c57ead1d6..a1694af4a 100644 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -989,7 +989,7 @@ void PollInputEvents(void) if ((CORE.Window.eventWaiting) || (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN))) { - RGFW_window_eventWait(platform.window, 0); // Wait for input events: keyboard/mouse/window events (callbacks) -> Update keys state + RGFW_window_eventWait(platform.window, -1); // Wait for input events: keyboard/mouse/window events (callbacks) -> Update keys state CORE.Time.previous = GetTime(); } From bb5b5434a75b6be87582b7e160415475ae52cb6b Mon Sep 17 00:00:00 2001 From: M374LX Date: Sun, 1 Jun 2025 14:37:01 -0300 Subject: [PATCH 030/242] Update miniaudio to v0.11.22 --- src/external/miniaudio.h | 1813 ++++++++++++++++++++++++++++---------- 1 file changed, 1324 insertions(+), 489 deletions(-) diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index ad113337e..c74bebeb3 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.21 - 2023-11-15 +miniaudio - v0.11.22 - 2025-02-24 David Reid - mackron@gmail.com @@ -12,15 +12,18 @@ GitHub: https://github.com/mackron/miniaudio /* 1. Introduction =============== -miniaudio is a single file library for audio playback and capture. To use it, do the following in -one .c file: +To use miniaudio, include "miniaudio.h": ```c - #define MINIAUDIO_IMPLEMENTATION #include "miniaudio.h" ``` -You can do `#include "miniaudio.h"` in other parts of the program just like any other header. +The implementation is contained in "miniaudio.c". Just compile this like any other source file. You +can include miniaudio.c if you want to compile your project as a single translation unit: + + ```c + #include "miniaudio.c" + ``` miniaudio includes both low level and high level APIs. The low level API is good for those who want to do all of their mixing themselves and only require a light weight interface to the underlying @@ -293,7 +296,7 @@ avoids the same sound being loaded multiple times. The node graph is used for mixing and effect processing. The idea is that you connect a number of nodes into the graph by connecting each node's outputs to another node's inputs. Each node can -implement it's own effect. By chaining nodes together, advanced mixing and effect processing can +implement its own effect. By chaining nodes together, advanced mixing and effect processing can be achieved. The engine encapsulates both the resource manager and the node graph to create a simple, easy to @@ -398,7 +401,7 @@ the be started and/or stopped at a specific time. This can be done with the foll ``` The start/stop time needs to be specified based on the absolute timer which is controlled by the -engine. The current global time time in PCM frames can be retrieved with +engine. The current global time in PCM frames can be retrieved with `ma_engine_get_time_in_pcm_frames()`. The engine's global time can be changed with `ma_engine_set_time_in_pcm_frames()` for synchronization purposes if required. Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before anything will play: @@ -430,11 +433,11 @@ Sounds and sound groups are nodes in the engine's node graph and can be plugged API. This makes it possible to connect sounds and sound groups to effect nodes to produce complex effect chains. -A sound can have it's volume changed with `ma_sound_set_volume()`. If you prefer decibel volume +A sound can have its volume changed with `ma_sound_set_volume()`. If you prefer decibel volume control you can use `ma_volume_db_to_linear()` to convert from decibel representation to linear. Panning and pitching is supported with `ma_sound_set_pan()` and `ma_sound_set_pitch()`. If you know -a sound will never have it's pitch changed with `ma_sound_set_pitch()` or via the doppler effect, +a sound will never have its pitch changed with `ma_sound_set_pitch()` or via the doppler effect, you can specify the `MA_SOUND_FLAG_NO_PITCH` flag when initializing the sound for an optimization. By default, sounds and sound groups have spatialization enabled. If you don't ever want to @@ -483,21 +486,12 @@ link the relevant frameworks but should compile cleanly out of the box with Xcod through the command line requires linking to `-lpthread` and `-lm`. Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's -notarization process. To fix this there are two options. The first is to use the -`MA_NO_RUNTIME_LINKING` option, like so: - - ```c - #ifdef __APPLE__ - #define MA_NO_RUNTIME_LINKING - #endif - #define MINIAUDIO_IMPLEMENTATION - #include "miniaudio.h" - ``` - -This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioToolbox`. -If you get errors about AudioToolbox, try with `-framework AudioUnit` instead. You may get this when -using older versions of iOS. Alternatively, if you would rather keep using runtime linking you can -add the following to your entitlements.xcent file: +notarization process. To fix this there are two options. The first is to compile with +`-DMA_NO_RUNTIME_LINKING` which in turn will require linking with +`-framework CoreFoundation -framework CoreAudio -framework AudioToolbox`. If you get errors about +AudioToolbox, try with `-framework AudioUnit` instead. You may get this when using older versions +of iOS. Alternatively, if you would rather keep using runtime linking you can add the following to +your entitlements.xcent file: ``` com.apple.security.cs.allow-dyld-environment-variables @@ -555,7 +549,7 @@ To run locally, you'll need to use emrun: 2.7. Build Options ------------------ -`#define` these options before including miniaudio.h. +`#define` these options before including miniaudio.c, or pass them as compiler flags: +----------------------------------+--------------------------------------------------------------------+ | Option | Description | @@ -586,6 +580,8 @@ To run locally, you'll need to use emrun: +----------------------------------+--------------------------------------------------------------------+ | MA_NO_WEBAUDIO | Disables the Web Audio backend. | +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_CUSTOM | Disables support for custom backends. | + +----------------------------------+--------------------------------------------------------------------+ | MA_NO_NULL | Disables the null backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_ENABLE_ONLY_SPECIFIC_BACKENDS | Disables all backends by default and requires `MA_ENABLE_*` to | @@ -630,6 +626,9 @@ To run locally, you'll need to use emrun: | MA_ENABLE_WEBAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | | | enable the Web Audio backend. | +----------------------------------+--------------------------------------------------------------------+ + | MA_ENABLE_CUSTOM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | + | | enable custom backends. | + +----------------------------------+--------------------------------------------------------------------+ | MA_ENABLE_NULL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | | | enable the null backend. | +----------------------------------+--------------------------------------------------------------------+ @@ -693,11 +692,30 @@ To run locally, you'll need to use emrun: | | You may need to enable this if your target platform does not allow | | | runtime linking via `dlopen()`. | +----------------------------------+--------------------------------------------------------------------+ + | MA_USE_STDINT | (Pass this in as a compiler flag. Do not `#define` this before | + | | miniaudio.c) Forces the use of stdint.h for sized types. | + +----------------------------------+--------------------------------------------------------------------+ | MA_DEBUG_OUTPUT | Enable `printf()` output of debug logs (`MA_LOG_LEVEL_DEBUG`). | +----------------------------------+--------------------------------------------------------------------+ | MA_COINIT_VALUE | Windows only. The value to pass to internal calls to | | | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. | +----------------------------------+--------------------------------------------------------------------+ + | MA_FORCE_UWP | Windows only. Affects only the WASAPI backend. Will force the | + | | WASAPI backend to use the UWP code path instead of the regular | + | | desktop path. This is normally auto-detected and should rarely be | + | | needed to be used explicitly, but can be useful for debugging. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_ON_THREAD_ENTRY | Defines some code that will be executed as soon as an internal | + | | miniaudio-managed thread is created. This will be the first thing | + | | to be executed by the thread entry point. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_ON_THREAD_EXIT | Defines some code that will be executed from the entry point of an | + | | internal miniaudio-managed thread upon exit. This will be the last | + | | thing to be executed before the thread's entry point exits. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_THREAD_DEFAULT_STACK_SIZE | If set, specifies the default stack size used by miniaudio-managed | + | | threads. | + +----------------------------------+--------------------------------------------------------------------+ | MA_API | Controls how public APIs should be decorated. Default is `extern`. | +----------------------------------+--------------------------------------------------------------------+ @@ -1309,7 +1327,7 @@ only works for sounds that were initialized with `ma_sound_init_from_file()` and When you initialize a sound, if you specify a sound group the sound will be attached to that group automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint. -If you would instead rather leave the sound unattached by default, you can can specify the +If you would instead rather leave the sound unattached by default, you can specify the `MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT` flag. This is useful if you want to set up a complex node graph. @@ -1686,6 +1704,7 @@ combination of the following flags: MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING ``` When no flags are specified (set to 0), the sound will be fully loaded into memory, but not @@ -1706,6 +1725,14 @@ can instead stream audio data which you can do by specifying the second pages. When a new page needs to be decoded, a job will be posted to the job queue and then subsequently processed in a job thread. +The `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flag can be used so that the sound will loop +when it reaches the end by default. It's recommended you use this flag when you want to have a +looping streaming sound. If you try loading a very short sound as a stream, you will get a glitch. +This is because the resource manager needs to pre-fill the initial buffer at initialization time, +and if you don't specify the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flag, the resource +manager will assume the sound is not looping and will stop filling the buffer when it reaches the +end, therefore resulting in a discontinuous buffer. + For in-memory sounds, reference counting is used to ensure the data is loaded only once. This means multiple calls to `ma_resource_manager_data_source_init()` with the same file path will result in the file data only being loaded once. Each call to `ma_resource_manager_data_source_init()` must be @@ -1720,7 +1747,7 @@ actual file paths. When `ma_resource_manager_data_source_init()` is called (with `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag), the resource manager will look for these explicitly registered data buffers and, if found, will use it as the backing data for the data source. Note that the resource manager does *not* make a copy of this data so it is up to the -caller to ensure the pointer stays valid for it's lifetime. Use +caller to ensure the pointer stays valid for its lifetime. Use `ma_resource_manager_unregister_data()` to unregister the self-managed data. You can also use `ma_resource_manager_register_file()` and `ma_resource_manager_unregister_file()` to register and unregister a file. It does not make sense to use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` @@ -2031,7 +2058,7 @@ In the above graph, it starts with two data sources whose outputs are attached t splitter node. It's at this point that the two data sources are mixed. After mixing, the splitter performs it's processing routine and produces two outputs which is simply a duplication of the input stream. One output is attached to a low pass filter, whereas the other output is attached to -a echo/delay. The outputs of the the low pass filter and the echo are attached to the endpoint, and +a echo/delay. The outputs of the low pass filter and the echo are attached to the endpoint, and since they're both connected to the same input bus, they'll be mixed. Each input bus must be configured to accept the same number of channels, but the number of channels @@ -2072,7 +2099,7 @@ data from the graph: ``` When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in -data from it's input attachments, which in turn recursively pull in data from their inputs, and so +data from its input attachments, which in turn recursively pull in data from their inputs, and so on. At the start of the graph there will be some kind of data source node which will have zero inputs and will instead read directly from a data source. The base nodes don't literally need to read from a `ma_data_source` object, but they will always have some kind of underlying object that @@ -2318,7 +2345,7 @@ You can start and stop a node with the following: By default the node is in a started state, but since it won't be connected to anything won't actually be invoked by the node graph until it's connected. When you stop a node, data will not be -read from any of it's input connections. You can use this property to stop a group of sounds +read from any of its input connections. You can use this property to stop a group of sounds atomically. You can configure the initial state of a node in it's config: @@ -2411,29 +2438,29 @@ audio thread is finished so that control is not handed back to the caller thereb chance to free the node's memory. When the audio thread is processing a node, it does so by reading from each of the output buses of -the node. In order for a node to process data for one of it's output buses, it needs to read from -each of it's input buses, and so on an so forth. It follows that once all output buses of a node +the node. In order for a node to process data for one of its output buses, it needs to read from +each of its input buses, and so on an so forth. It follows that once all output buses of a node are detached, the node as a whole will be disconnected and no further processing will occur unless it's output buses are reattached, which won't be happening when the node is being uninitialized. By having `ma_node_detach_output_bus()` wait until the audio thread is finished with it, we can simplify a few things, at the expense of making `ma_node_detach_output_bus()` a bit slower. By doing this, the implementation of `ma_node_uninit()` becomes trivial - just detach all output -nodes, followed by each of the attachments to each of it's input nodes, and then do any final clean +nodes, followed by each of the attachments to each of its input nodes, and then do any final clean up. With the above design, the worst-case scenario is `ma_node_detach_output_bus()` taking as long as it takes to process the output bus being detached. This will happen if it's called at just the wrong moment where the audio thread has just iterated it and has just started processing. The caller of `ma_node_detach_output_bus()` will stall until the audio thread is finished, which -includes the cost of recursively processing it's inputs. This is the biggest compromise made with -the approach taken by miniaudio for it's lock-free processing system. The cost of detaching nodes +includes the cost of recursively processing its inputs. This is the biggest compromise made with +the approach taken by miniaudio for its lock-free processing system. The cost of detaching nodes earlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass detachments, detach starting from the lowest level nodes and work your way towards the final endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not running, detachment will be fast and detachment in any order will be the same. The reason nodes need to wait for their input attachments to complete is due to the potential for desyncs between -data sources. If the node was to terminate processing mid way through processing it's inputs, +data sources. If the node was to terminate processing mid way through processing its inputs, there's a chance that some of the underlying data sources will have been read, but then others not. That will then result in a potential desynchronization when detaching and reattaching higher-level nodes. A possible solution to this is to have an option when detaching to terminate processing @@ -2804,7 +2831,7 @@ weights. Custom weights can be passed in as the last parameter of `ma_channel_converter_config_init()`. Predefined channel maps can be retrieved with `ma_channel_map_init_standard()`. This takes a -`ma_standard_channel_map` enum as it's first parameter, which can be one of the following: +`ma_standard_channel_map` enum as its first parameter, which can be one of the following: +-----------------------------------+-----------------------------------------------------------+ | Name | Description | @@ -2890,7 +2917,7 @@ like the following: ma_resample_algorithm_linear); ma_resampler resampler; - ma_result result = ma_resampler_init(&config, &resampler); + ma_result result = ma_resampler_init(&config, NULL, &resampler); if (result != MA_SUCCESS) { // An error occurred... } @@ -3132,7 +3159,7 @@ Biquad filtering is achieved with the `ma_biquad` API. Example: ```c ma_biquad_config config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2); - ma_result result = ma_biquad_init(&config, &biquad); + ma_result result = ma_biquad_init(&config, NULL, &biquad); if (result != MA_SUCCESS) { // Error. } @@ -3723,7 +3750,7 @@ extern "C" { #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 21 +#define MA_VERSION_REVISION 22 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #if defined(_MSC_VER) && !defined(__clang__) @@ -3740,8 +3767,7 @@ extern "C" { #endif - -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) || defined(__ppc64__) #define MA_SIZEOF_PTR 8 #else #define MA_SIZEOF_PTR 4 @@ -3805,7 +3831,7 @@ typedef void* ma_handle; typedef void* ma_ptr; /* -ma_proc is annoying because when compiling with GCC we get pendantic warnings about converting +ma_proc is annoying because when compiling with GCC we get pedantic warnings about converting between `void*` and `void (*)()`. We can't use `void (*)()` with MSVC however, because we'll get warning C4191 about "type cast between incompatible function types". To work around this I'm going to use a different data type depending on the compiler. @@ -3999,7 +4025,7 @@ Special wchar_t type to ensure any structures in the public sections that refere consistent size across all platforms. On Windows, wchar_t is 2 bytes, whereas everywhere else it's 4 bytes. Since Windows likes to use -wchar_t for it's IDs, we need a special explicitly sized wchar type that is always 2 bytes on all +wchar_t for its IDs, we need a special explicitly sized wchar type that is always 2 bytes on all platforms. */ #if !defined(MA_POSIX) && defined(MA_WIN32) @@ -4025,7 +4051,7 @@ MA_LOG_LEVEL_INFO callback. MA_LOG_LEVEL_WARNING - Warnings. You should enable this in you development builds and action them when encounted. These + Warnings. You should enable this in you development builds and action them when encountered. These logs usually indicate a potential problem or misconfiguration, but still allow you to keep running. This will never be called from within the data callback. @@ -5457,7 +5483,7 @@ input frames. MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); /* -Resets the resampler's timer and clears it's internal cache. +Resets the resampler's timer and clears its internal cache. */ MA_API ma_result ma_resampler_reset(ma_resampler* pResampler); @@ -5678,7 +5704,7 @@ MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannel /* Copies a channel map. -Both input and output channel map buffers must have a capacity of at at least `channels`. +Both input and output channel map buffers must have a capacity of at least `channels`. */ MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels); @@ -5817,6 +5843,8 @@ MA_API void ma_data_source_uninit(ma_data_source* pDataSource); MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */ MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex); +MA_API ma_result ma_data_source_seek_seconds(ma_data_source* pDataSource, float secondCount, float* pSecondsSeeked); /* Can only seek forward. Abstraction to ma_data_source_seek_pcm_frames() */ +MA_API ma_result ma_data_source_seek_to_second(ma_data_source* pDataSource, float seekPointInSeconds); /* Abstraction to ma_data_source_seek_to_pcm_frame() */ MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor); MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ @@ -6182,6 +6210,12 @@ MA_API ma_result ma_event_wait(ma_event* pEvent); Signals the specified auto-reset event. */ MA_API ma_result ma_event_signal(ma_event* pEvent); + + +MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore); +MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore); +MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore); +MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore); #endif /* MA_NO_THREADING */ @@ -6273,7 +6307,7 @@ Job Queue /* Slot Allocator -------------- -The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocator an index that can be used +The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocate an index that can be used as the insertion point for an object. Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs. @@ -7006,6 +7040,8 @@ typedef union int nullbackend; /* The null backend uses an integer for device IDs. */ } ma_device_id; +MA_API ma_bool32 ma_device_id_equal(const ma_device_id* pA, const ma_device_id* pB); + typedef struct ma_context_config ma_context_config; typedef struct ma_device_config ma_device_config; @@ -7093,6 +7129,7 @@ struct ma_device_config { const char* pStreamNamePlayback; const char* pStreamNameCapture; + int channelMap; } pulse; struct { @@ -7112,6 +7149,7 @@ struct ma_device_config ma_aaudio_allowed_capture_policy allowedCapturePolicy; ma_bool32 noAutoStartAfterReroute; ma_bool32 enableCompatibilityWorkarounds; + ma_bool32 allowSetBufferCapacity; } aaudio; }; @@ -7184,7 +7222,7 @@ and on output returns detailed information about the device in `ma_device_info`. case when the device ID is NULL, in which case information about the default device needs to be retrieved. Once the context has been created and the device ID retrieved (if using anything other than the default device), the device can be created. -This is a little bit more complicated than initialization of the context due to it's more complicated configuration. When initializing a +This is a little bit more complicated than initialization of the context due to its more complicated configuration. When initializing a device, a duplex device may be requested. This means a separate data format needs to be specified for both playback and capture. On input, the data format is set to what the application wants. On output it's set to the native format which should match as closely as possible to the requested format. The conversion between the format requested by the application and the device's native format will be handled @@ -7205,10 +7243,10 @@ asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should The handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit easier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and `onDeviceWrite()` callbacks can optionally be implemented. These are blocking and work just like reading and writing from a file. If the -backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within it's callback. +backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within its callback. This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback. -If the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceDataLoop()` callback +If the backend requires absolute flexibility with its data delivery, it can optionally implement the `onDeviceDataLoop()` callback which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional. The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been @@ -7248,6 +7286,10 @@ struct ma_context_config void* pUserData; ma_allocation_callbacks allocationCallbacks; struct + { + ma_handle hWnd; /* HWND. Optional window handle to pass into SetCooperativeLevel(). Will default to the foreground window, and if that fails, the desktop window. */ + } dsound; + struct { ma_bool32 useVerboseDeviceEnumeration; } alsa; @@ -7336,6 +7378,7 @@ struct ma_context #ifdef MA_SUPPORT_DSOUND struct { + ma_handle hWnd; /* Can be null. */ ma_handle hDSoundDLL; ma_proc DirectSoundCreate; ma_proc DirectSoundEnumerateA; @@ -7942,6 +7985,7 @@ struct ma_device { /*AAudioStream**/ ma_ptr pStreamPlayback; /*AAudioStream**/ ma_ptr pStreamCapture; + ma_mutex rerouteLock; ma_aaudio_usage usage; ma_aaudio_content_type contentType; ma_aaudio_input_preset inputPreset; @@ -8365,6 +8409,10 @@ Retrieves basic information about every active playback and/or capture device. This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos` parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback. +Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require +opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this, +but don't call it from within the enumeration callback. + Parameters ---------- @@ -8406,7 +8454,7 @@ The returned pointers will become invalid upon the next call this this function, See Also -------- -ma_context_get_devices() +ma_context_enumerate_devices() */ MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount); @@ -8545,7 +8593,7 @@ from a microphone. Whether or not you should send or receive data from the devic playback, capture, full-duplex or loopback. (Note that loopback mode is only supported on select backends.) Sending and receiving audio data to and from the device is done via a callback which is fired by miniaudio at periodic time intervals. -The frequency at which data is delivered to and from a device depends on the size of it's period. The size of the period can be defined in terms of PCM frames +The frequency at which data is delivered to and from a device depends on the size of its period. The size of the period can be defined in terms of PCM frames or milliseconds, whichever is more convenient. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and increased risk of glitching due to the more frequent and granular data deliver intervals. The size of a period will depend on your requirements, but miniaudio's defaults should work fine for most scenarios. If you're building a game you should leave this fairly small, whereas if you're building a simple @@ -8619,7 +8667,7 @@ then be set directly on the structure. Below are the members of the `ma_device_c performanceProfile A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or - `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at it's default value. + `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at its default value. noPreSilencedOutputBuffer When set to true, the contents of the output buffer passed into the data callback will be left undefined. When set to false (default), the contents of @@ -8659,7 +8707,7 @@ then be set directly on the structure. Below are the members of the `ma_device_c A pointer that will passed to callbacks in pBackendVTable. resampling.linear.lpfOrder - The linear resampler applies a low-pass filter as part of it's processing for anti-aliasing. This setting controls the order of the filter. The higher + The linear resampler applies a low-pass filter as part of its processing for anti-aliasing. This setting controls the order of the filter. The higher the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`. @@ -8736,6 +8784,9 @@ then be set directly on the structure. Below are the members of the `ma_device_c pulse.pStreamNameCapture PulseAudio only. Sets the stream name for capture. + pulse.channelMap + PulseAudio only. Sets the channel map that is requested from PulseAudio. See MA_PA_CHANNEL_MAP_* constants. Defaults to MA_PA_CHANNEL_MAP_AIFF. + coreaudio.allowNominalSampleRateChange Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This is disabled by default in order to prevent intrusive changes to the user's system. This is useful if you want to use a sample rate @@ -8909,7 +8960,7 @@ Unsafe. It is not safe to call this inside any callback. Remarks ------- -You only need to use this function if you want to configure the context differently to it's defaults. You should never use this function if you want to manage +You only need to use this function if you want to configure the context differently to its defaults. You should never use this function if you want to manage your own context. See the documentation for `ma_context_init()` for information on the different context configuration options. @@ -9674,7 +9725,7 @@ Utilities ************************************************************************************************************************************************************/ /* -Calculates a buffer size in milliseconds from the specified number of frames and sample rate. +Calculates a buffer size in milliseconds (rounded up) from the specified number of frames and sample rate. */ MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate); @@ -9931,7 +9982,7 @@ struct ma_decoder void* pInputCache; /* In input format. Can be null if it's not needed. */ ma_uint64 inputCacheCap; /* The capacity of the input cache. */ ma_uint64 inputCacheConsumed; /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */ - ma_uint64 inputCacheRemaining; /* The number of valid frames remaining in the cahce. */ + ma_uint64 inputCacheRemaining; /* The number of valid frames remaining in the cache. */ ma_allocation_callbacks allocationCallbacks; union { @@ -9972,7 +10023,7 @@ This is not thread safe without your own synchronization. MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* -Seeks to a PCM frame based on it's absolute index. +Seeks to a PCM frame based on its absolute index. This is not thread safe without your own synchronization. */ @@ -10235,7 +10286,8 @@ typedef enum MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE = 0x00000002, /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage. */ MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC = 0x00000004, /* When set, the resource manager will load the data source asynchronously. */ MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT = 0x00000008, /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010 /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010, /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING = 0x00000020 /* When set, configures the data source to loop by default. */ } ma_resource_manager_data_source_flags; @@ -10303,8 +10355,8 @@ typedef struct ma_uint64 rangeEndInPCMFrames; ma_uint64 loopPointBegInPCMFrames; ma_uint64 loopPointEndInPCMFrames; - ma_bool32 isLooping; ma_uint32 flags; + ma_bool32 isLooping; /* Deprecated. Use the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING flag in `flags` instead. */ } ma_resource_manager_data_source_config; MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void); @@ -10547,6 +10599,16 @@ Node Graph /* Use this when the bus count is determined by the node instance rather than the vtable. */ #define MA_NODE_BUS_COUNT_UNKNOWN 255 + +/* For some internal memory management of ma_node_graph. */ +typedef struct +{ + size_t offset; + size_t sizeInBytes; + unsigned char _data[1]; +} ma_stack; + + typedef struct ma_node_graph ma_node_graph; typedef void ma_node; @@ -10586,7 +10648,7 @@ typedef struct void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut); /* - A callback for retrieving the number of a input frames that are required to output the + A callback for retrieving the number of input frames that are required to output the specified number of output frames. You would only want to implement this when the node performs resampling. This is optional, even for nodes that perform resampling, but it does offer a small reduction in latency as it allows miniaudio to calculate the exact number of input frames @@ -10671,10 +10733,14 @@ typedef struct ma_node_base ma_node_base; struct ma_node_base { /* These variables are set once at startup. */ - ma_node_graph* pNodeGraph; /* The graph this node belongs to. */ + ma_node_graph* pNodeGraph; /* The graph this node belongs to. */ const ma_node_vtable* vtable; - float* pCachedData; /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */ - ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */ + ma_uint32 inputBusCount; + ma_uint32 outputBusCount; + ma_node_input_bus* pInputBuses; + ma_node_output_bus* pOutputBuses; + float* pCachedData; /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */ + ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */ /* These variables are read and written only from the audio thread. */ ma_uint16 cachedFrameCountOut; @@ -10682,13 +10748,9 @@ struct ma_node_base ma_uint16 consumedFrameCountIn; /* These variables are read and written between different threads. */ - MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */ - MA_ATOMIC(8, ma_uint64) stateTimes[2]; /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */ - MA_ATOMIC(8, ma_uint64) localTime; /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */ - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - ma_node_input_bus* pInputBuses; - ma_node_output_bus* pOutputBuses; + MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */ + MA_ATOMIC(8, ma_uint64) stateTimes[2]; /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */ + MA_ATOMIC(8, ma_uint64) localTime; /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */ /* Memory management. */ ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; @@ -10724,7 +10786,8 @@ MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime); typedef struct { ma_uint32 channels; - ma_uint16 nodeCacheCapInFrames; + ma_uint32 processingSizeInFrames; /* This is the preferred processing size for node processing callbacks unless overridden by a node itself. Can be 0 in which case it will be based on the frame count passed into ma_node_graph_read_pcm_frames(), but will not be well defined. */ + size_t preMixStackSizeInBytes; /* Defaults to 512KB per channel. Reducing this will save memory, but the depth of your node graph will be more restricted. */ } ma_node_graph_config; MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels); @@ -10735,10 +10798,15 @@ struct ma_node_graph /* Immutable. */ ma_node_base base; /* The node graph itself is a node so it can be connected as an input to different node graph. This has zero inputs and calls ma_node_graph_read_pcm_frames() to generate it's output. */ ma_node_base endpoint; /* Special node that all nodes eventually connect to. Data is read from this node in ma_node_graph_read_pcm_frames(). */ - ma_uint16 nodeCacheCapInFrames; + float* pProcessingCache; /* This will be allocated when processingSizeInFrames is non-zero. This is needed because ma_node_graph_read_pcm_frames() can be called with a variable number of frames, and we may need to do some buffering in situations where the caller requests a frame count that's not a multiple of processingSizeInFrames. */ + ma_uint32 processingCacheFramesRemaining; + ma_uint32 processingSizeInFrames; /* Read and written by multiple threads. */ MA_ATOMIC(4, ma_bool32) isReading; + + /* Modified only by the audio thread. */ + ma_stack* pPreMixStack; }; MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph); @@ -11023,6 +11091,7 @@ typedef enum MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */ MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */ MA_SOUND_FLAG_UNKNOWN_LENGTH = 0x00000010, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH */ + MA_SOUND_FLAG_LOOPING = 0x00000020, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING */ /* ma_sound specific flags. */ MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00001000, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ @@ -11062,7 +11131,7 @@ MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_e /* Base node object for both ma_sound and ma_sound_group. */ typedef struct { - ma_node_base baseNode; /* Must be the first member for compatiblity with the ma_node API. */ + ma_node_base baseNode; /* Must be the first member for compatibility with the ma_node API. */ ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */ ma_uint32 sampleRate; /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */ ma_uint32 volumeSmoothTimeInPCMFrames; @@ -11122,13 +11191,13 @@ typedef struct ma_uint64 rangeEndInPCMFrames; ma_uint64 loopPointBegInPCMFrames; ma_uint64 loopPointEndInPCMFrames; - ma_bool32 isLooping; ma_sound_end_proc endCallback; /* Fired when the sound reaches the end. Will be fired from the audio thread. Do not restart, uninitialize or otherwise change the state of the sound from here. Instead fire an event or set a variable to indicate to a different thread to change the start of the sound. Will not be fired in response to a scheduled stop with ma_sound_set_stop_time_*(). */ void* pEndCallbackUserData; #ifndef MA_NO_RESOURCE_MANAGER ma_resource_manager_pipeline_notifications initNotifications; #endif ma_fence* pDoneFence; /* Deprecated. Use initNotifications instead. Released when the resource manager has finished decoding the entire sound. Not used with streams. */ + ma_bool32 isLooping; /* Deprecated. Use the MA_SOUND_FLAG_LOOPING flag in `flags` instead. */ } ma_sound_config; MA_API ma_sound_config ma_sound_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ @@ -11192,6 +11261,7 @@ typedef struct ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */ ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */ ma_uint32 defaultVolumeSmoothTimeInPCMFrames; /* Defaults to 0. Controls the default amount of smoothing to apply to volume changes to sounds. High values means more smoothing at the expense of high latency (will take longer to reach the new volume). */ + ma_uint32 preMixStackSizeInBytes; /* A stack is used for internal processing in the node graph. This allows you to configure the size of this stack. Smaller values will reduce the maximum depth of your node graph. You should rarely need to modify this. */ ma_allocation_callbacks allocationCallbacks; ma_bool32 noAutoStart; /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */ ma_bool32 noDevice; /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */ @@ -11206,12 +11276,12 @@ MA_API ma_engine_config ma_engine_config_init(void); struct ma_engine { - ma_node_graph nodeGraph; /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */ + ma_node_graph nodeGraph; /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */ #if !defined(MA_NO_RESOURCE_MANAGER) ma_resource_manager* pResourceManager; #endif #if !defined(MA_NO_DEVICE_IO) - ma_device* pDevice; /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */ + ma_device* pDevice; /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */ #endif ma_log* pLog; ma_uint32 sampleRate; @@ -11220,10 +11290,10 @@ struct ma_engine ma_allocation_callbacks allocationCallbacks; ma_bool8 ownsResourceManager; ma_bool8 ownsDevice; - ma_spinlock inlinedSoundLock; /* For synchronizing access so the inlined sound list. */ - ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */ - MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */ - ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */ + ma_spinlock inlinedSoundLock; /* For synchronizing access to the inlined sound list. */ + ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */ + MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */ + ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */ ma_uint32 defaultVolumeSmoothTimeInPCMFrames; ma_mono_expansion_mode monoExpansionMode; ma_engine_process_proc onProcess; @@ -11348,6 +11418,7 @@ MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping); MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound); MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound); MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex); /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */ +MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds); /* Abstraction to ma_sound_seek_to_pcm_frame() */ MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor); MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength); @@ -13861,7 +13932,7 @@ static ma_uint32 ma_ffs_32(ma_uint32 x) /* Just a naive implementation just to get things working for now. Will optimize this later. */ for (i = 0; i < 32; i += 1) { - if ((x & (1 << i)) != 0) { + if ((x & (1U << i)) != 0) { return i; } } @@ -14024,7 +14095,7 @@ static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 dith Atomics **************************************************************************************************************************************************************/ -/* ma_atomic.h begin */ +/* c89atomic.h begin */ #ifndef ma_atomic_h #if defined(__cplusplus) extern "C" { @@ -14750,12 +14821,12 @@ typedef int ma_atomic_memory_order; typedef ma_uint8 ma_atomic_flag; #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) - #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) + #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) #else typedef ma_uint32 ma_atomic_flag; #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_32(ptr, order) #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_32(ptr, order) - #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_32(ptr, order) + #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_32(ptr, order) #endif #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) #define MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE @@ -14836,15 +14907,24 @@ typedef int ma_atomic_memory_order; __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } + #if defined(__clang__) + #pragma clang diagnostic push + #if __clang_major__ >= 8 + #pragma clang diagnostic ignored "-Watomic-alignment" + #endif + #endif static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) { __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } + #if defined(__clang__) + #pragma clang diagnostic pop + #endif typedef ma_uint8 ma_atomic_flag; #define ma_atomic_flag_test_and_set_explicit(dst, order) (ma_bool32)__atomic_test_and_set(dst, order) #define ma_atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order) - #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) + #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) #else #define ma_atomic_memory_order_relaxed 1 #define ma_atomic_memory_order_consume 2 @@ -15358,7 +15438,7 @@ typedef int ma_atomic_memory_order; typedef ma_uint8 ma_atomic_flag; #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) - #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) + #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) #endif #if !defined(MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) #if defined(MA_ATOMIC_HAS_8) @@ -15883,7 +15963,7 @@ static MA_INLINE void ma_atomic_spinlock_lock(volatile ma_atomic_spinlock* pSpin if (ma_atomic_flag_test_and_set_explicit(pSpinlock, ma_atomic_memory_order_acquire) == 0) { break; } - while (c89atoimc_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { + while (ma_atomic_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { } } } @@ -15898,7 +15978,7 @@ static MA_INLINE void ma_atomic_spinlock_unlock(volatile ma_atomic_spinlock* pSp } #endif #endif -/* ma_atomic.h end */ +/* c89atomic.h end */ #define MA_ATOMIC_SAFE_TYPE_IMPL(c89TypeExtension, type) \ static MA_INLINE ma_##type ma_atomic_##type##_get(ma_atomic_##type* x) \ @@ -16096,7 +16176,7 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority int result; pthread_attr_t* pAttr = NULL; -#if !defined(__EMSCRIPTEN__) +#if !defined(__EMSCRIPTEN__) && !defined(__3DS__) /* Try setting the thread priority. It's not critical if anything fails here. */ pthread_attr_t attr; if (pthread_attr_init(&attr) == 0) { @@ -16142,19 +16222,34 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority if (priority == ma_thread_priority_idle) { sched.sched_priority = priorityMin; } else if (priority == ma_thread_priority_realtime) { - sched.sched_priority = priorityMax; - } else { - sched.sched_priority += ((int)priority + 5) * priorityStep; /* +5 because the lowest priority is -5. */ - if (sched.sched_priority < priorityMin) { - sched.sched_priority = priorityMin; + #if defined(MA_PTHREAD_REALTIME_THREAD_PRIORITY) + { + sched.sched_priority = MA_PTHREAD_REALTIME_THREAD_PRIORITY; } - if (sched.sched_priority > priorityMax) { + #else + { sched.sched_priority = priorityMax; } + #endif + } else { + sched.sched_priority += ((int)priority + 5) * priorityStep; /* +5 because the lowest priority is -5. */ } - /* I'm not treating a failure of setting the priority as a critical error so not checking the return value here. */ - pthread_attr_setschedparam(&attr, &sched); + if (sched.sched_priority < priorityMin) { + sched.sched_priority = priorityMin; + } + if (sched.sched_priority > priorityMax) { + sched.sched_priority = priorityMax; + } + + /* I'm not treating a failure of setting the priority as a critical error so not aborting on failure here. */ + if (pthread_attr_setschedparam(&attr, &sched) == 0) { + #if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) + { + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + } + #endif + } } } } @@ -16187,7 +16282,7 @@ static void ma_thread_wait__posix(ma_thread* pThread) static ma_result ma_mutex_init__posix(ma_mutex* pMutex) { int result; - + if (pMutex == NULL) { return MA_INVALID_ARGS; } @@ -17406,7 +17501,7 @@ static ma_job_proc g_jobVTable[MA_JOB_TYPE_COUNT] = /* Device. */ #if !defined(MA_NO_DEVICE_IO) - ma_job_process__device__aaudio_reroute /*MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE*/ + ma_job_process__device__aaudio_reroute /* MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE */ #endif }; @@ -17751,7 +17846,7 @@ MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob) is stored. One thread can fall through to the freeing of this item while another is still using "head" for the retrieval of the "next" variable. - The slot allocator might need to make use of some reference counting to ensure it's only truely freed when + The slot allocator might need to make use of some reference counting to ensure it's only truly freed when there are no more references to the item. This must be fixed before removing these locks. */ @@ -17859,7 +17954,16 @@ MA_API void ma_dlclose(ma_log* pLog, ma_handle handle) #ifdef MA_WIN32 FreeLibrary((HMODULE)handle); #else - dlclose((void*)handle); + /* Hack for Android bug (see https://github.com/android/ndk/issues/360). Calling dlclose() pre-API 28 may segfault. */ + #if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) + { + dlclose((void*)handle); + } + #else + { + (void)handle; + } + #endif #endif (void)pLog; @@ -17880,12 +17984,12 @@ MA_API ma_proc ma_dlsym(ma_log* pLog, ma_handle handle, const char* symbol) #ifdef _WIN32 proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); #else -#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) +#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" #endif proc = (ma_proc)dlsym((void*)handle, symbol); -#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) +#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__) #pragma GCC diagnostic pop #endif #endif @@ -17923,9 +18027,13 @@ DEVICE I/O #endif #endif +#ifdef MA_APPLE + #include +#endif + #ifndef MA_NO_DEVICE_IO -#if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200) +#if defined(MA_APPLE) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101200) #include /* For mach_absolute_time() */ #endif @@ -17939,6 +18047,10 @@ DEVICE I/O #endif #endif +/* This must be set to at least 26. */ +#ifndef MA_AAUDIO_MIN_ANDROID_SDK_VERSION +#define MA_AAUDIO_MIN_ANDROID_SDK_VERSION 27 +#endif MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags) @@ -18085,7 +18197,7 @@ MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend) #if defined(MA_HAS_AAUDIO) #if defined(MA_ANDROID) { - return ma_android_sdk_version() >= 26; + return ma_android_sdk_version() >= MA_AAUDIO_MIN_ANDROID_SDK_VERSION; } #else return MA_FALSE; @@ -18402,7 +18514,6 @@ typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey); typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, const char* lpValueName, DWORD* lpReserved, DWORD* lpType, BYTE* lpData, DWORD* lpcbData); #endif /* MA_WIN32_DESKTOP */ - MA_API size_t ma_strlen_WCHAR(const WCHAR* str) { size_t len = 0; @@ -18487,7 +18598,7 @@ Timing return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart; } -#elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200) +#elif defined(MA_APPLE) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101200) static ma_uint64 g_ma_TimerFrequency = 0; static void ma_timer_init(ma_timer* pTimer) { @@ -18670,11 +18781,16 @@ static void ma_device__on_notification_rerouted(ma_device* pDevice) #endif #if defined(MA_EMSCRIPTEN) -EMSCRIPTEN_KEEPALIVE -void ma_device__on_notification_unlocked(ma_device* pDevice) +#ifdef __cplusplus +extern "C" { +#endif +void EMSCRIPTEN_KEEPALIVE ma_device__on_notification_unlocked(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_unlocked)); } +#ifdef __cplusplus +} +#endif #endif @@ -18802,7 +18918,7 @@ static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut unsigned int prevDenormalState = ma_device_disable_denormals(pDevice); { /* Volume control of input makes things a bit awkward because the input buffer is read-only. We'll need to use a temp buffer and loop in this case. */ - if (pFramesIn != NULL && masterVolumeFactor < 1) { + if (pFramesIn != NULL && masterVolumeFactor != 1) { ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); @@ -18825,7 +18941,7 @@ static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut /* Volume control and clipping for playback devices. */ if (pFramesOut != NULL) { - if (masterVolumeFactor < 1) { + if (masterVolumeFactor != 1) { if (pFramesIn == NULL) { /* <-- In full-duplex situations, the volume will have been applied to the input samples before the data callback. Applying it again post-callback will incorrectly compound it. */ ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor); } @@ -18837,6 +18953,11 @@ static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut } } ma_device_restore_denormals(pDevice, prevDenormalState); + } else { + /* No data callback. Just silence the output. */ + if (pFramesOut != NULL) { + ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels); + } } } @@ -18922,9 +19043,7 @@ static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 fra framesToReadThisIterationIn = requiredInputFrameCount; } - if (framesToReadThisIterationIn > 0) { - ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn); - } + ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn); /* At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any @@ -18965,7 +19084,7 @@ static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frame ma_uint64 totalClientFramesProcessed = 0; const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat; - /* We just keep going until we've exhaused all of our input frames and cannot generate any more output frames. */ + /* We just keep going until we've exhausted all of our input frames and cannot generate any more output frames. */ for (;;) { ma_uint64 deviceFramesProcessedThisIteration; ma_uint64 clientFramesProcessedThisIteration; @@ -19248,7 +19367,7 @@ static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice) } /* - If we weren't able to generate any output frames it must mean we've exhaused all of our input. The only time this would not be the case is if capturedClientData was too small + If we weren't able to generate any output frames it must mean we've exhausted all of our input. The only time this would not be the case is if capturedClientData was too small which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE. */ if (capturedClientFramesToProcessThisIteration == 0) { @@ -19451,7 +19570,7 @@ static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 oper /* The first thing to do is wait for an operation slot to become available. We only have a single slot for this, but we could extend this later - to support queing of operations. + to support queuing of operations. */ result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore); if (result != MA_SUCCESS) { @@ -21268,7 +21387,7 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context } /* - Exlcusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on + Exclusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on UWP. Failure to retrieve the exclusive mode format is not considered an error, so from here on out, MA_SUCCESS is guaranteed to be returned. */ @@ -21473,10 +21592,23 @@ static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device MA_ASSERT(pContext != NULL); MA_ASSERT(ppMMDevice != NULL); + /* + This weird COM init/uninit here is a hack to work around a crash when changing devices. What is happening is + WASAPI fires a callback from another thread when the device is changed. It's from that thread where this + function is getting called. What I'm suspecting is that the other thread is not initializing COM which in turn + results in CoCreateInstance() failing. + + The community has reported that this seems to fix the crash. There are future plans to move all WASAPI operation + over to a single thread to make everything safer, but in the meantime while we wait for that to come online I'm + happy enough to use this hack instead. + */ ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); - hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + { + hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + } ma_CoUninitialize(pContext); - if (FAILED(hr)) { + + if (FAILED(hr)) { /* <-- This is checking the call above to ma_CoCreateInstance(). */ ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n"); return ma_result_from_HRESULT(hr); } @@ -21508,7 +21640,7 @@ static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pCon size_t idlen = ma_strlen_WCHAR(pDeviceIDString); if (idlen+1 > ma_countof(pDeviceID->wasapi)) { ma_CoTaskMemFree(pContext, pDeviceIDString); - MA_ASSERT(MA_FALSE); /* NOTE: If this is triggered, please report it. It means the format of the ID must haved change and is too long to fit in our fixed sized buffer. */ + MA_ASSERT(MA_FALSE); /* NOTE: If this is triggered, please report it. It means the format of the ID must have changed and is too long to fit in our fixed sized buffer. */ return MA_ERROR; } @@ -21952,12 +22084,16 @@ static ma_result ma_device_uninit__wasapi(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - if (pDevice->wasapi.pDeviceEnumerator) { - ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient); - ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator); + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + { + if (pDevice->wasapi.pDeviceEnumerator) { + ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient); + ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator); + } + + ma_mutex_uninit(&pDevice->wasapi.rerouteLock); } -#endif + #endif if (pDevice->wasapi.pRenderClient) { if (pDevice->wasapi.pMappedBufferPlayback != NULL) { @@ -22258,7 +22394,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* - If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing + If the periodicity is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing it and trying it again. */ hr = E_FAIL; @@ -22268,7 +22404,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device if (bufferDuration > 500*10000) { break; } else { - if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinit loop. Should never happen, but it makes me feel better. */ + if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinite loop. Should never happen, but it makes me feel better. */ break; } @@ -23007,6 +23143,14 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) } if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { + /* If we have a mapped buffer we need to release it. */ + if (pDevice->wasapi.pMappedBufferCapture != NULL) { + ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); + pDevice->wasapi.pMappedBufferCapture = NULL; + pDevice->wasapi.mappedBufferCaptureCap = 0; + pDevice->wasapi.mappedBufferCaptureLen = 0; + } + hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device."); @@ -23020,31 +23164,34 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) return ma_result_from_HRESULT(hr); } - /* If we have a mapped buffer we need to release it. */ - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } - ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + if (pDevice->wasapi.pMappedBufferPlayback != NULL) { + ma_silence_pcm_frames( + ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels), + pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen, + pDevice->playback.internalFormat, pDevice->playback.internalChannels + ); + ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); + pDevice->wasapi.pMappedBufferPlayback = NULL; + pDevice->wasapi.mappedBufferPlaybackCap = 0; + pDevice->wasapi.mappedBufferPlaybackLen = 0; + } + /* The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played. */ if (ma_atomic_bool32_get(&pDevice->wasapi.isStartedPlayback)) { /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */ - DWORD waitTime = pDevice->wasapi.actualBufferSizeInFramesPlayback / pDevice->playback.internalSampleRate; + DWORD waitTime = (pDevice->wasapi.actualBufferSizeInFramesPlayback * 1000) / pDevice->playback.internalSampleRate; if (pDevice->playback.shareMode == ma_share_mode_exclusive) { WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime); - } - else { - ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1; + } else { + ma_uint32 prevFramesAvailablePlayback = (ma_uint32)-1; ma_uint32 framesAvailablePlayback; for (;;) { result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback); @@ -23060,13 +23207,13 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames has not changed, get out of the loop. I don't think this should ever happen, but I think it's nice to have just in case. */ - if (framesAvailablePlayback == prevFramesAvaialablePlayback) { + if (framesAvailablePlayback == prevFramesAvailablePlayback) { break; } - prevFramesAvaialablePlayback = framesAvailablePlayback; + prevFramesAvailablePlayback = framesAvailablePlayback; - WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime * 1000); ResetEvent((HANDLE)pDevice->wasapi.hEventPlayback); /* Manual reset. */ + WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime); } } } @@ -23078,19 +23225,20 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) } /* The audio client needs to be reset otherwise restarting will fail. */ - hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); + { + ma_int32 retries = 5; + + while ((hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback)) == MA_AUDCLNT_E_BUFFER_OPERATION_PENDING && retries > 0) { + ma_sleep(10); + retries -= 1; + } + } + if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device."); return ma_result_from_HRESULT(hr); } - if (pDevice->wasapi.pMappedBufferPlayback != NULL) { - ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; - pDevice->wasapi.mappedBufferPlaybackCap = 0; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - } - ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE); } @@ -23657,6 +23805,13 @@ DirectSound Backend #define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010 #define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020 +#define MA_DSBSTATUS_PLAYING 0x00000001 +#define MA_DSBSTATUS_BUFFERLOST 0x00000002 +#define MA_DSBSTATUS_LOOPING 0x00000004 +#define MA_DSBSTATUS_LOCHARDWARE 0x00000008 +#define MA_DSBSTATUS_LOCSOFTWARE 0x00000010 +#define MA_DSBSTATUS_TERMINATED 0x00000020 + #define MA_DSCBSTART_LOOPING 0x00000001 typedef struct @@ -24026,9 +24181,12 @@ static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma } /* The cooperative level must be set before doing anything else. */ - hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); + hWnd = (HWND)pContext->dsound.hWnd; if (hWnd == 0) { - hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)(); + hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); + if (hWnd == 0) { + hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)(); + } } hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY); @@ -24532,8 +24690,8 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf } /* - Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices. We need to initialize - the capture device first because we'll want to match it's buffer size and period count on the playback side if we're using + Unfortunately DirectSound uses different APIs and data structures for playback and capture devices. We need to initialize + the capture device first because we'll want to match its buffer size and period count on the playback side if we're using full-duplex mode. */ if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { @@ -24816,6 +24974,7 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) ma_bool32 isPlaybackDeviceStarted = MA_FALSE; ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */ ma_uint32 waitTimeInMilliseconds = 1; + DWORD playbackBufferStatus = 0; MA_ASSERT(pDevice != NULL); @@ -25144,6 +25303,20 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) break; } + hr = ma_IDirectSoundBuffer_GetStatus((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &playbackBufferStatus); + if (SUCCEEDED(hr) && (playbackBufferStatus & MA_DSBSTATUS_PLAYING) == 0 && isPlaybackDeviceStarted) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[DirectSound] Attempting to resume audio due to state: %d.", (int)playbackBufferStatus); + hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); + if (FAILED(hr)) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed after attempting to resume from state %d.", (int)playbackBufferStatus); + return ma_result_from_HRESULT(hr); + } + + isPlaybackDeviceStarted = MA_TRUE; + ma_sleep(waitTimeInMilliseconds); + continue; + } + if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; } @@ -25345,6 +25518,8 @@ static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_ return MA_API_NOT_FOUND; } + pContext->dsound.hWnd = pConfig->dsound.hWnd; + pCallbacks->onContextInit = ma_context_init__dsound; pCallbacks->onContextUninit = ma_context_uninit__dsound; pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound; @@ -25667,7 +25842,7 @@ static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters. - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The problem, however is that WASAPI and DirectSound use " ()" format (such as "Speakers (High Definition Audio)"), - but WinMM does not specificy the component name. From my admittedly limited testing, I've notice the component name seems to + but WinMM does not specify the component name. From my admittedly limited testing, I've notice the component name seems to usually fit within the 31 characters of the fixed sized buffer, so what I'm going to do is parse that string for the component name, and then concatenate the name from the registry. */ @@ -25935,7 +26110,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi return MA_DEVICE_TYPE_NOT_SUPPORTED; } - /* No exlusive mode with WinMM. */ + /* No exclusive mode with WinMM. */ if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { return MA_SHARE_MODE_NOT_SUPPORTED; @@ -25957,7 +26132,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi /* We use an event to know when a new fragment needs to be enqueued. */ pDevice->winmm.hEventCapture = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); if (pDevice->winmm.hEventCapture == NULL) { - errorMsg = "[WinMM] Failed to create event for fragment enqueing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError()); + errorMsg = "[WinMM] Failed to create event for fragment enqueuing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError()); goto on_error; } @@ -25995,7 +26170,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi /* We use an event to know when a new fragment needs to be enqueued. */ pDevice->winmm.hEventPlayback = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); if (pDevice->winmm.hEventPlayback == NULL) { - errorMsg = "[WinMM] Failed to create event for fragment enqueing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError()); + errorMsg = "[WinMM] Failed to create event for fragment enqueuing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError()); goto on_error; } @@ -27117,7 +27292,7 @@ static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode s /* We're trying to open a specific device. There's a few things to consider here: - miniaudio recongnizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When + miniaudio recognizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it finds an appropriate one that works. This comes in very handy when trying to open a device in shared mode ("dmix"), vs exclusive mode ("hw"). */ @@ -27216,7 +27391,7 @@ static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enu /* At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device - initialization time and is used as an indicator to try and use the most appropriate plugin depending on the + initialization time and is used as an indicator to try to use the most appropriate plugin depending on the device type and sharing mode. */ char* dst = hwid; @@ -27395,7 +27570,7 @@ static void ma_context_iterate_rates_and_add_native_data_format__alsa(ma_context ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir); ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir); - /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculus. */ + /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculous. */ minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); maxSampleRate = ma_clamp(maxSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); @@ -27471,10 +27646,10 @@ static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_devic /* Some ALSA devices can support many permutations of formats, channels and rates. We only support a fixed number of permutations which means we need to employ some strategies to ensure the best - combinations are returned. An example is the "pulse" device which can do it's own data conversion + combinations are returned. An example is the "pulse" device which can do its own data conversion in software and as a result can support any combination of format, channels and rate. - We want to ensure the the first data formats are the best. We have a list of favored sample + We want to ensure that the first data formats are the best. We have a list of favored sample formats and sample rates, so these will be the basis of our iteration. */ @@ -28052,7 +28227,21 @@ static ma_result ma_device_start__alsa(ma_device* pDevice) } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* Don't need to do anything for playback because it'll be started automatically when enough data has been written. */ + /* + When data is written to the device we wait for the device to get ready to receive data with poll(). In my testing + I have observed that poll() can sometimes block forever unless the device is started explicitly with snd_pcm_start() + or some data is written with snd_pcm_writei(). + + To resolve this I've decided to do an explicit start with snd_pcm_start(). The problem with this is that the device + is started without any data in the internal buffer which will result in an immediate underrun. If instead we were + to call into snd_pcm_writei() in an attempt to prevent the underrun, we would run the risk of a weird deadlock + issue as documented inside ma_device_write__alsa(). + */ + resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); + if (resultALSA < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start playback device."); + return ma_result_from_errno(-resultALSA); + } } return MA_SUCCESS; @@ -28065,6 +28254,7 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice) a small chance that our wakeupfd has not been cleared. We'll clear that out now if applicable. */ int resultPoll; + int resultRead; if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n"); @@ -28079,12 +28269,15 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice) ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n"); } - /* Clear the wakeupfd. */ - resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0); - if (resultPoll > 0) { - ma_uint64 t; - read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t)); - } + /* Clear the wakeupfd. */ + resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0); + if (resultPoll > 0) { + ma_uint64 t; + resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t)); + if (resultRead != sizeof(t)) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from capture wakeupfd. read() = %d\n", resultRead); + } + } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { @@ -28101,12 +28294,14 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice) } /* Clear the wakeupfd. */ - resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0); - if (resultPoll > 0) { - ma_uint64 t; - read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t)); - } - + resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0); + if (resultPoll > 0) { + ma_uint64 t; + resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t)); + if (resultRead != sizeof(t)) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from playback wakeupfd. read() = %d\n", resultRead); + } + } } return MA_SUCCESS; @@ -28119,13 +28314,20 @@ static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, st int resultALSA; int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1); if (resultPoll < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] poll() failed.\n"); - return ma_result_from_errno(errno); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] poll() failed.\n"); + + /* + There have been reports that poll() is returning an error randomly and that instead of + returning an error, simply trying again will work. I'm experimenting with adopting this + advice. + */ + continue; + /*return ma_result_from_errno(errno);*/ } /* Before checking the ALSA poll descriptor flag we need to check if the wakeup descriptor - has had it's POLLIN flag set. If so, we need to actually read the data and then exit + has had it's POLLIN flag set. If so, we need to actually read the data and then exit the function. The wakeup descriptor will be the first item in the descriptors buffer. */ if ((pPollDescriptors[0].revents & POLLIN) != 0) { @@ -28154,7 +28356,7 @@ static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, st ma_snd_pcm_state_t state = ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM); if (state == MA_SND_PCM_STATE_XRUN) { /* The PCM is in a xrun state. This will be recovered from at a higher level. We can disregard this. */ - } else { + } else { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] POLLERR detected. status = %d\n", ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM)); } } @@ -28587,7 +28789,7 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co return MA_SUCCESS; } -#endif /* ALSA */ +#endif /* MA_HAS_ALSA */ @@ -28598,7 +28800,7 @@ PulseAudio Backend ******************************************************************************/ #ifdef MA_HAS_PULSEAUDIO /* -The PulseAudio API, along with Apple's Core Audio, is the worst of the maintream audio APIs. This is a brief description of what's going on +The PulseAudio API, along with Apple's Core Audio, is the worst of the mainstream audio APIs. This is a brief description of what's going on in the PulseAudio backend. I apologize if this gets a bit ranty for your liking - you might want to skip this discussion. PulseAudio has something they call the "Simple API", which unfortunately isn't suitable for miniaudio. I've not seen anywhere where it @@ -28613,7 +28815,7 @@ get fun, and I don't mean that in a good way... The problems start with the very name of the API - "asynchronous". Yes, this is an asynchronous oriented API which means your commands don't immediately take effect. You instead need to issue your commands, and then wait for them to complete. The waiting mechanism is -enabled through the use of a "main loop". In the asychronous API you cannot get away from the main loop, and the main loop is where almost +enabled through the use of a "main loop". In the asynchronous API you cannot get away from the main loop, and the main loop is where almost all of PulseAudio's problems stem from. When you first initialize PulseAudio you need an object referred to as "main loop". You can implement this yourself by defining your own @@ -28663,7 +28865,7 @@ because PulseAudio takes it literally, specifically the "can be". You would thin writing and reading data to and from the stream, and that would be right, except when it's not. When you initialize the stream, you can set a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices straight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified, -PulseAudio will immediately fire it's write or read callback. This is *technically* correct (based on the wording in the documentation) +PulseAudio will immediately fire its write or read callback. This is *technically* correct (based on the wording in the documentation) because indeed, data *can* be written at this point. The problem is that it's not *practical*. It makes sense that the write/read callback would be where a program will want to write or read data to or from the stream, but when it's called before the application has even requested that the stream be started, it's just not practical because the program probably isn't ready for any kind of data delivery at @@ -30041,16 +30243,18 @@ static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFra static ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap) { - static int g_StreamCounter = 0; + static ma_atomic_uint32 g_StreamCounter = { 0 }; char actualStreamName[256]; if (pStreamName != NULL) { ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1); } else { - ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:"); - ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10); /* 8 = strlen("miniaudio:") */ + const char* pBaseName = "miniaudio:"; + size_t baseNameLen = strlen(pBaseName); + ma_strcpy_s(actualStreamName, sizeof(actualStreamName), pBaseName); + ma_itoa_s((int)ma_atomic_uint32_get(&g_StreamCounter), actualStreamName + baseNameLen, sizeof(actualStreamName)-baseNameLen, 10); } - g_StreamCounter += 1; + ma_atomic_uint32_fetch_add(&g_StreamCounter, 1); return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap); } @@ -30304,6 +30508,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ma_pa_buffer_attr attr; const ma_pa_sample_spec* pActualSS = NULL; const ma_pa_buffer_attr* pActualAttr = NULL; + const ma_pa_channel_map* pActualChannelMap = NULL; ma_uint32 iChannel; ma_pa_stream_flags_t streamFlags; @@ -30364,7 +30569,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi } /* Use a default channel map. */ - ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT); + ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, pConfig->pulse.channelMap); /* Use the requested sample rate if one was specified. */ if (pDescriptorCapture->sampleRate != 0) { @@ -30453,7 +30658,12 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi goto on_error4; } + /* Internal channel map. */ + pActualChannelMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + if (pActualChannelMap == NULL) { + pActualChannelMap = &cmap; /* Fallback just in case. */ + } /* Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting @@ -30463,8 +30673,8 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi fixed sooner than later. I might remove this hack later. */ if (pDescriptorCapture->channels > 2) { - for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) { - pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); + for (iChannel = 0; iChannel < pDescriptorCapture->channels; iChannel += 1) { + pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(pActualChannelMap->map[iChannel]); } } else { /* Hack for mono and stereo. */ @@ -30511,7 +30721,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi } /* Use a default channel map. */ - ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT); + ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, pConfig->pulse.channelMap); /* Use the requested sample rate if one was specified. */ @@ -30605,7 +30815,12 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi goto on_error4; } + /* Internal channel map. */ + pActualChannelMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + if (pActualChannelMap == NULL) { + pActualChannelMap = &cmap; /* Fallback just in case. */ + } /* Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting @@ -30615,8 +30830,8 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi fixed sooner than later. I might remove this hack later. */ if (pDescriptorPlayback->channels > 2) { - for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) { - pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); + for (iChannel = 0; iChannel < pDescriptorPlayback->channels; iChannel += 1) { + pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(pActualChannelMap->map[iChannel]); } } else { /* Hack for mono and stereo. */ @@ -31769,7 +31984,7 @@ static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_co return MA_SUCCESS; } -#endif /* JACK */ +#endif /* MA_HAS_JACK */ @@ -31860,7 +32075,7 @@ that supports this level of detail. There was some public domain sample code I s and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API. -Most (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When +Most (all?) functions in the AudioObject API take a AudioObjectID as its input. This is the device identifier. When retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be @@ -32195,6 +32410,12 @@ static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* #define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMaster #endif +/* kAudioDevicePropertyScope* were renamed to kAudioObjectPropertyScope* in 10.8. */ +#if !defined(MAC_OS_X_VERSION_10_8) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8) +#define kAudioObjectPropertyScopeInput kAudioDevicePropertyScopeInput +#define kAudioObjectPropertyScopeOutput kAudioDevicePropertyScopeOutput +#endif + static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */ { AudioObjectPropertyAddress propAddressDevices; @@ -32784,7 +33005,7 @@ static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjec desiredSampleRate = sampleRate; if (desiredSampleRate == 0) { - desiredSampleRate = pOrigFormat->mSampleRate; + desiredSampleRate = (ma_uint32)pOrigFormat->mSampleRate; } desiredChannelCount = channels; @@ -33427,7 +33648,7 @@ static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFl } } else { /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */ - MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should heve been validated at initialization time. */ + MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */ /* For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something @@ -33518,11 +33739,12 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla */ for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels; + /*printf("DEBUG: nDataByteSize = %d\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ } status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList); if (status != noErr) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " ERROR: AudioUnitRender() failed with %d.\n", (int)status); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "ERROR: AudioUnitRender() failed with %d.\n", (int)status); return status; } @@ -33758,7 +33980,7 @@ static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContex ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); { - /* Don't do anything if we've already initializd device tracking. */ + /* Don't do anything if we've already initialized device tracking. */ if (g_DeviceTrackingInitCounter_CoreAudio == 0) { AudioObjectPropertyAddress propAddress; propAddress.mScope = kAudioObjectPropertyScopeGlobal; @@ -34070,11 +34292,11 @@ typedef struct static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference) /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */ { - ma_result result; + ma_result result = MA_SUCCESS; OSStatus status; UInt32 enableIOFlag; AudioStreamBasicDescription bestFormat; - UInt32 actualPeriodSizeInFrames; + ma_uint32 actualPeriodSizeInFrames; AURenderCallbackStruct callbackInfo; #if defined(MA_APPLE_DESKTOP) AudioObjectID deviceObjectID; @@ -34226,7 +34448,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format. - Something that does seem to work, however, has been setting the nominal sample rate on the deivce object. The problem with + Something that does seem to work, however, has been setting the nominal sample rate on the device object. The problem with this, however, is that it actually changes the sample rate at the operating system level and not just the application. This could be intrusive to the user, however, so I don't think it's wise to make this the default. Instead I'm making this a configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample @@ -34277,15 +34499,28 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev /* I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead. + + UPDATE 20/02/2025: + When testing on the simulator with an iPhone 15 and iOS 17 I get an error when initializing the audio + unit if set the input channels to pAudioSession.inputNumberOfChannels. What is happening is the channel + count returned from AudioUnitGetProperty() above is set to 2, but pAudioSession is reporting a channel + count of 1. When this happens, the call to AudioUnitSetProprty() below just down below will succeed, but + AudioUnitInitialize() further down will fail. The only solution I have come up with is to not set the + channel count to pAudioSession.inputNumberOfChannels. */ if (deviceType == ma_device_type_playback) { bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels; } + + #if 0 if (deviceType == ma_device_type_capture) { + /*printf("DEBUG: bestFormat.mChannelsPerFrame = %d; pAudioSession.inputNumberOfChannels = %d\n", (int)bestFormat.mChannelsPerFrame, (int)pAudioSession.inputNumberOfChannels);*/ bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels; } + #endif } + status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); if (status != noErr) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); @@ -34305,7 +34540,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev } pData->channelsOut = bestFormat.mChannelsPerFrame; - pData->sampleRateOut = bestFormat.mSampleRate; + pData->sampleRateOut = (ma_uint32)bestFormat.mSampleRate; } /* Clamp the channel count for safety. */ @@ -34612,7 +34847,7 @@ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_c ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); /* - If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly + If we are using the default device we'll need to listen for changes to the system's default device so we can seamlessly switch the device in the background. */ if (pConfig->capture.pDeviceID == NULL) { @@ -34676,7 +34911,7 @@ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_c ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); /* - If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly + If we are using the default device we'll need to listen for changes to the system's default device so we can seamlessly switch the device in the background. */ if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) { @@ -34994,7 +35229,7 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte return MA_SUCCESS; } -#endif /* Core Audio */ +#endif /* MA_HAS_COREAUDIO */ @@ -35486,7 +35721,7 @@ static ma_result ma_device_uninit__sndio(ma_device* pDevice) ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); } - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); } @@ -35841,7 +36076,7 @@ static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_c (void)pConfig; return MA_SUCCESS; } -#endif /* sndio */ +#endif /* MA_HAS_SNDIO */ @@ -35859,6 +36094,10 @@ audio(4) Backend #include #include +#ifdef __NetBSD__ +#include +#endif + #if defined(__OpenBSD__) #include #if defined(OpenBSD) && OpenBSD >= 201709 @@ -36078,7 +36317,7 @@ static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext ma_uint32 channels; ma_uint32 sampleRate; -#ifdef __NetBSD__ +#if defined(__NetBSD__) && (__NetBSD_Version__ >= 900000000) if (ioctl(fd, AUDIO_GETFORMAT, &fdInfo) < 0) { return MA_ERROR; } @@ -36364,7 +36603,7 @@ 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__ +#if defined(__NetBSD__) && (__NetBSD_Version__ >= 900000000) fdInfoResult = ioctl(fdctl, AUDIO_GETFORMAT, &fdInfo); #else fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo); @@ -36735,7 +36974,7 @@ static ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_ return MA_SUCCESS; } -#endif /* audio4 */ +#endif /* MA_HAS_AUDIO4 */ /****************************************************************************** @@ -37098,7 +37337,7 @@ static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_conf } /* - The OSS documantation is very clear about the order we should be initializing the device's properties: + The OSS documentation is very clear about the order we should be initializing the device's properties: 1) Format 2) Channels 3) Sample rate. @@ -37366,7 +37605,7 @@ static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_con return MA_SUCCESS; } -#endif /* OSS */ +#endif /* MA_HAS_OSS */ @@ -37379,7 +37618,9 @@ AAudio Backend ******************************************************************************/ #ifdef MA_HAS_AAUDIO -/*#include */ +#ifdef MA_NO_RUNTIME_LINKING + #include +#endif typedef int32_t ma_aaudio_result_t; typedef int32_t ma_aaudio_direction_t; @@ -37592,9 +37833,7 @@ static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUs MA_ASSERT(pDevice != NULL); (void)error; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream)); - /* When we get an error, we'll assume that the stream is in an erroneous state and needs to be restarted. From the documentation, we cannot do this from the error callback. Therefore we are going to use an event thread for the AAudio backend to do this @@ -37622,7 +37861,9 @@ static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio( ma_device* pDevice = (ma_device*)pUserData; MA_ASSERT(pDevice != NULL); - ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, frameCount); + if (frameCount > 0) { + ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, (ma_uint32)frameCount); + } (void)pStream; return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; @@ -37633,7 +37874,14 @@ static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio ma_device* pDevice = (ma_device*)pUserData; MA_ASSERT(pDevice != NULL); - ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, frameCount); + /* + I've had a report that AAudio can sometimes post a frame count of 0. We need to check for that here + so we don't get any errors at a deeper level. I'm doing the same with the capture side for safety, + though I've not yet had any reports about that one. + */ + if (frameCount > 0) { + ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, (ma_uint32)frameCount); + } (void)pStream; return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; @@ -37668,32 +37916,25 @@ static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate); } - if (deviceType == ma_device_type_capture) { - if (pDescriptor->channels != 0) { - ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); - } - if (pDescriptor->format != ma_format_unknown) { - ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); - } - } else { - if (pDescriptor->channels != 0) { - ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); - } - if (pDescriptor->format != ma_format_unknown) { - ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); - } + if (pDescriptor->channels != 0) { + ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); + } + + if (pDescriptor->format != ma_format_unknown) { + ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); } /* - There have been reports where setting the frames per data callback results in an error - later on from Android. To address this, I'm experimenting with simply not setting it on - anything from Android 11 and earlier. Suggestions welcome on how we might be able to make - this more targetted. + There have been reports where setting the frames per data callback results in an error. + In particular, re-routing may inadvertently switch from low-latency mode, resulting in a less stable + stream from the legacy path (AudioStreamLegacy). To address this, we simply don't set the value. It + can still be set if it's explicitly requested via the aaudio.allowSetBufferCapacity variable in the + device config. */ - if (!pConfig->aaudio.enableCompatibilityWorkarounds || ma_android_sdk_version() > 30) { + if ((!pConfig->aaudio.enableCompatibilityWorkarounds || ma_android_sdk_version() > 30) && pConfig->aaudio.allowSetBufferCapacity) { /* - AAudio is annoying when it comes to it's buffer calculation stuff because it doesn't let you + AAudio is annoying when it comes to its buffer calculation stuff because it doesn't let you retrieve the actual sample rate until after you've opened the stream. But you need to configure the buffer capacity before you open the stream... :/ @@ -37727,7 +37968,11 @@ static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice); } - /* Not sure how this affects things, but since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let go ahead and set it. */ + /* + If we set AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, we allow for MMAP (non-legacy path). + Since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let's use it. + Beware though, with a conservative performance profile, AAudio will indeed take the legacy path. + */ ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MA_AAUDIO_PERFORMANCE_MODE_NONE); /* We need to set an error callback to detect device changes. */ @@ -37763,6 +38008,9 @@ static ma_result ma_open_stream_basic__aaudio(ma_context* pContext, const ma_dev return result; } + /* Let's give AAudio a hint to avoid the legacy path (AudioStreamLegacy). */ + ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); + return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream); } @@ -37787,6 +38035,10 @@ static ma_result ma_open_stream__aaudio(ma_device* pDevice, const ma_device_conf static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream) { + if (pStream == NULL) { + return MA_INVALID_ARGS; + } + return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream)); } @@ -37913,20 +38165,36 @@ static ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_dev return MA_SUCCESS; } +static ma_result ma_close_streams__aaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + /* When re-routing, streams may have been closed and never re-opened. Hence the extra checks below. */ + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); + pDevice->aaudio.pStreamCapture = NULL; + } + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); + pDevice->aaudio.pStreamPlayback = NULL; + } + + return MA_SUCCESS; +} static ma_result ma_device_uninit__aaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - pDevice->aaudio.pStreamCapture = NULL; + /* Wait for any rerouting to finish before attempting to close the streams. */ + ma_mutex_lock(&pDevice->aaudio.rerouteLock); + { + ma_close_streams__aaudio(pDevice); } + ma_mutex_unlock(&pDevice->aaudio.rerouteLock); - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); - pDevice->aaudio.pStreamPlayback = NULL; - } + /* Destroy re-routing lock. */ + ma_mutex_uninit(&pDevice->aaudio.rerouteLock); return MA_SUCCESS; } @@ -37978,7 +38246,7 @@ static ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_dev return MA_SUCCESS; } -static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +static ma_result ma_device_init_streams__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { ma_result result; @@ -38011,6 +38279,25 @@ static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_conf return MA_SUCCESS; } +static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + + result = ma_device_init_streams__aaudio(pDevice, pConfig, pDescriptorPlayback, pDescriptorCapture); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_mutex_init(&pDevice->aaudio.rerouteLock); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) { ma_aaudio_result_t resultAA; @@ -38018,12 +38305,16 @@ static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStr MA_ASSERT(pDevice != NULL); + if (pStream == NULL) { + return MA_INVALID_ARGS; + } + resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream); if (resultAA != MA_AAUDIO_OK) { return ma_result_from_aaudio(resultAA); } - /* Do we actually need to wait for the device to transition into it's started state? */ + /* Do we actually need to wait for the device to transition into its started state? */ /* The device should be in either a starting or started state. If it's not set to started we need to wait for it to transition. It should go from starting to started. */ currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); @@ -38050,6 +38341,10 @@ static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStre MA_ASSERT(pDevice != NULL); + if (pStream == NULL) { + return MA_INVALID_ARGS; + } + /* From the AAudio documentation: @@ -38135,22 +38430,20 @@ static ma_result ma_device_stop__aaudio(ma_device* pDevice) static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType) { ma_result result; + int32_t retries = 0; MA_ASSERT(pDevice != NULL); - /* The first thing to do is close the streams. */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { - ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - pDevice->aaudio.pStreamCapture = NULL; - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); - pDevice->aaudio.pStreamPlayback = NULL; - } - - /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */ + /* + TODO: Stop retrying if main thread is about to uninit device. + */ + ma_mutex_lock(&pDevice->aaudio.rerouteLock); { +error_disconnected: + /* The first thing to do is close the streams. */ + ma_close_streams__aaudio(pDevice); + + /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */ ma_device_config deviceConfig; ma_device_descriptor descriptorPlayback; ma_device_descriptor descriptorCapture; @@ -38199,15 +38492,17 @@ static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type dev descriptorPlayback.periodCount = deviceConfig.periods; } - result = ma_device_init__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture); + result = ma_device_init_streams__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture); if (result != MA_SUCCESS) { - return result; + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to create stream after route change."); + goto done; } result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture); if (result != MA_SUCCESS) { - ma_device_uninit__aaudio(pDevice); - return result; + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to initialize device after route change."); + ma_close_streams__aaudio(pDevice); + goto done; } /* We'll only ever do this in response to a reroute. */ @@ -38216,14 +38511,29 @@ static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type dev /* If the device is started, start the streams. Maybe make this configurable? */ if (ma_device_get_state(pDevice) == ma_device_state_started) { if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) { - ma_device_start__aaudio(pDevice); + result = ma_device_start__aaudio(pDevice); + if (result != MA_SUCCESS) { + /* We got disconnected! Retry a few times, until we find a connected device! */ + retries += 1; + if (retries <= 3) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change, retrying(%d)", retries); + goto error_disconnected; + } + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change."); + goto done; + } } else { ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */ } } - - return MA_SUCCESS; + + result = MA_SUCCESS; } +done: + /* Re-routing done */ + ma_mutex_unlock(&pDevice->aaudio.rerouteLock); + + return result; } static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) @@ -38234,12 +38544,12 @@ static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type t MA_ASSERT(type != ma_device_type_duplex); MA_ASSERT(pDeviceInfo != NULL); - if (type == ma_device_type_playback) { + if (type == ma_device_type_capture) { pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture; pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio; ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ } - if (type == ma_device_type_capture) { + if (type == ma_device_type_playback) { pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback; pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio; ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ @@ -38272,6 +38582,7 @@ static ma_result ma_context_uninit__aaudio(ma_context* pContext) static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) { +#if !defined(MA_NO_RUNTIME_LINKING) size_t i; const char* libNames[] = { "libaaudio.so" @@ -38317,7 +38628,39 @@ static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_ pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst"); pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStart"); pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStop"); - +#else + pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)AAudio_createStreamBuilder; + pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)AAudioStreamBuilder_delete; + pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)AAudioStreamBuilder_setDeviceId; + pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)AAudioStreamBuilder_setDirection; + pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)AAudioStreamBuilder_setSharingMode; + pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)AAudioStreamBuilder_setFormat; + pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)AAudioStreamBuilder_setChannelCount; + pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)AAudioStreamBuilder_setSampleRate; + pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)AAudioStreamBuilder_setBufferCapacityInFrames; + pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)AAudioStreamBuilder_setFramesPerDataCallback; + pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)AAudioStreamBuilder_setDataCallback; + pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)AAudioStreamBuilder_setErrorCallback; + pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)AAudioStreamBuilder_setPerformanceMode; + pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)AAudioStreamBuilder_setUsage; + pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)AAudioStreamBuilder_setContentType; + pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)AAudioStreamBuilder_setInputPreset; + #if defined(__ANDROID_API__) && __ANDROID_API__ >= 29 + pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)AAudioStreamBuilder_setAllowedCapturePolicy; + #endif + pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)AAudioStreamBuilder_openStream; + pContext->aaudio.AAudioStream_close = (ma_proc)AAudioStream_close; + pContext->aaudio.AAudioStream_getState = (ma_proc)AAudioStream_getState; + pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)AAudioStream_waitForStateChange; + pContext->aaudio.AAudioStream_getFormat = (ma_proc)AAudioStream_getFormat; + pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)AAudioStream_getChannelCount; + pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)AAudioStream_getSampleRate; + pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)AAudioStream_getBufferCapacityInFrames; + pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)AAudioStream_getFramesPerDataCallback; + pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)AAudioStream_getFramesPerBurst; + pContext->aaudio.AAudioStream_requestStart = (ma_proc)AAudioStream_requestStart; + pContext->aaudio.AAudioStream_requestStop = (ma_proc)AAudioStream_requestStop; +#endif pCallbacks->onContextInit = ma_context_init__aaudio; pCallbacks->onContextUninit = ma_context_uninit__aaudio; @@ -38355,6 +38698,7 @@ static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_ static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) { + ma_result result; ma_device* pDevice; MA_ASSERT(pJob != NULL); @@ -38363,7 +38707,18 @@ static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) MA_ASSERT(pDevice != NULL); /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */ - return ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType); + result = ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType); + if (result != MA_SUCCESS) { + /* + Getting here means we failed to reroute the device. The best thing I can think of here is to + just stop the device. + */ + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[AAudio] Stopping device due to reroute failure."); + ma_device_stop(pDevice); + return result; + } + + return MA_SUCCESS; } #else /* Getting here means there is no AAudio backend so we need a no-op job implementation. */ @@ -39649,6 +40004,10 @@ Web Audio Backend #if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 32))) #include #define MA_SUPPORT_AUDIO_WORKLETS + + #if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 70))) + #define MA_SUPPORT_AUDIO_WORKLETS_VARIABLE_BUFFER_SIZE + #endif #endif /* @@ -39660,7 +40019,7 @@ TODO: Version 0.12: Swap this logic around so that AudioWorklets are used by def /* The thread stack size must be a multiple of 16. */ #ifndef MA_AUDIO_WORKLETS_THREAD_STACK_SIZE -#define MA_AUDIO_WORKLETS_THREAD_STACK_SIZE 16384 +#define MA_AUDIO_WORKLETS_THREAD_STACK_SIZE 131072 #endif #if defined(MA_USE_AUDIO_WORKLETS) @@ -39786,12 +40145,14 @@ static ma_result ma_device_uninit__webaudio(ma_device* pDevice) #if defined(MA_USE_AUDIO_WORKLETS) { EM_ASM({ - var device = miniaudio.get_device_by_index($0); + var device = window.miniaudio.get_device_by_index($0); if (device.streamNode !== undefined) { device.streamNode.disconnect(); device.streamNode = undefined; } + + device.pDevice = undefined; }, pDevice->webaudio.deviceIndex); emscripten_destroy_web_audio_node(pDevice->webaudio.audioWorklet); @@ -39801,7 +40162,7 @@ static ma_result ma_device_uninit__webaudio(ma_device* pDevice) #else { EM_ASM({ - var device = miniaudio.get_device_by_index($0); + var device = window.miniaudio.get_device_by_index($0); /* Make sure all nodes are disconnected and marked for collection. */ if (device.scriptNode !== undefined) { @@ -39828,7 +40189,7 @@ static ma_result ma_device_uninit__webaudio(ma_device* pDevice) /* Clean up the device on the JS side. */ EM_ASM({ - miniaudio.untrack_device_by_index($0); + window.miniaudio.untrack_device_by_index($0); }, pDevice->webaudio.deviceIndex); ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); @@ -39894,10 +40255,6 @@ static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const (void)paramCount; (void)pParams; - if (ma_device_get_state(pDevice) != ma_device_state_started) { - return EM_TRUE; - } - /* The Emscripten documentation says that it'll always be 128 frames being passed in. Hard coding it like that feels like a very bad idea to me. Even if it's hard coded in the backend, the API and documentation should always refer @@ -39906,7 +40263,20 @@ static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const Unfortunately the audio data is not interleaved so we'll need to convert it before we give the data to miniaudio for further processing. */ - frameCount = 128; + if (pDevice->type == ma_device_type_playback) { + frameCount = pDevice->playback.internalPeriodSizeInFrames; + } else { + frameCount = pDevice->capture.internalPeriodSizeInFrames; + } + + if (ma_device_get_state(pDevice) != ma_device_state_started) { + /* Fill the output buffer with zero to avoid a noise sound */ + for (int i = 0; i < outputCount; i += 1) { + MA_ZERO_MEMORY(pOutputs[i].data, pOutputs[i].numberOfChannels * frameCount * sizeof(float)); + } + + return EM_TRUE; + } if (inputCount > 0) { /* Input data needs to be interleaved before we hand it to the client. */ @@ -39961,7 +40331,7 @@ static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T a count from MediaStreamAudioSourceNode (what we use for capture)? The only way to have control is to configure an output channel count on the capture side. This is slightly confusing for capture mode because intuitively you wouldn't actually connect an output to an input-only node, but this is what we'll have to do in order to have - proper control over the channel count. In the capture case, we'll have to output silence to it's output node. + proper control over the channel count. In the capture case, we'll have to output silence to its output node. */ if (pParameters->pConfig->deviceType == ma_device_type_capture) { channels = (int)((pParameters->pDescriptorCapture->channels > 0) ? pParameters->pDescriptorCapture->channels : MA_DEFAULT_CHANNELS); @@ -39984,7 +40354,15 @@ static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T a Now that we know the channel count to use we can allocate the intermediary buffer. The intermediary buffer is used for interleaving and deinterleaving. */ - intermediaryBufferSizeInFrames = 128; + #if defined(MA_SUPPORT_AUDIO_WORKLETS_VARIABLE_BUFFER_SIZE) + { + intermediaryBufferSizeInFrames = (size_t)emscripten_audio_context_quantum_size(audioContext); + } + #else + { + intermediaryBufferSizeInFrames = 128; + } + #endif pParameters->pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(intermediaryBufferSizeInFrames * (ma_uint32)channels * sizeof(float), &pParameters->pDevice->pContext->allocationCallbacks); if (pParameters->pDevice->webaudio.pIntermediaryBuffer == NULL) { @@ -39993,7 +40371,6 @@ static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T a return; } - pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice); /* With the audio worklet initialized we can now attach it to the graph. */ @@ -40133,7 +40510,6 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co /* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */ pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes); - /* With the context created we can now create the worklet. We can only have a single worklet per audio context which means we'll need to craft this appropriately to handle duplex devices correctly. @@ -40182,11 +40558,12 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co /* We need to add an entry to the miniaudio.devices list on the JS side so we can do some JS/C interop. */ pDevice->webaudio.deviceIndex = EM_ASM_INT({ - return miniaudio.track_device({ + return window.miniaudio.track_device({ webaudio: emscriptenGetAudioObject($0), - state: 1 /* 1 = ma_device_state_stopped */ + state: 1, /* 1 = ma_device_state_stopped */ + pDevice: $1 }); - }, pDevice->webaudio.audioContext); + }, pDevice->webaudio.audioContext, pDevice); return MA_SUCCESS; } @@ -40198,7 +40575,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co ma_uint32 sampleRate; ma_uint32 periodSizeInFrames; - /* The channel count will depend on the device type. If it's a capture, use it's, otherwise use the playback side. */ + /* The channel count will depend on the device type. If it's a capture, use its, otherwise use the playback side. */ if (pConfig->deviceType == ma_device_type_capture) { channels = (pDescriptorCapture->channels > 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; } else { @@ -40267,11 +40644,11 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co /* The node processing callback. */ device.scriptNode.onaudioprocess = function(e) { if (device.intermediaryBufferView == null || device.intermediaryBufferView.length == 0) { - device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels); + device.intermediaryBufferView = new Float32Array(HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels); } /* Do the capture side first. */ - if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) { + if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) { /* The data must be interleaved before being processed miniaudio. */ for (var iChannel = 0; iChannel < channels; iChannel += 1) { var inputBuffer = e.inputBuffer.getChannelData(iChannel); @@ -40285,7 +40662,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co _ma_device_process_pcm_frames_capture__webaudio(pDevice, bufferSize, pIntermediaryBuffer); } - if (deviceType == miniaudio.device_type.playback || deviceType == miniaudio.device_type.duplex) { + if (deviceType == window.miniaudio.device_type.playback || deviceType == window.miniaudio.device_type.duplex) { _ma_device_process_pcm_frames_playback__webaudio(pDevice, bufferSize, pIntermediaryBuffer); for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { @@ -40305,7 +40682,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co }; /* Now we need to connect our node to the graph. */ - if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) { + if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) { navigator.mediaDevices.getUserMedia({audio:true, video:false}) .then(function(stream) { device.streamNode = device.webaudio.createMediaStreamSource(stream); @@ -40317,13 +40694,13 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co }); } - if (deviceType == miniaudio.device_type.playback) { + if (deviceType == window.miniaudio.device_type.playback) { device.scriptNode.connect(device.webaudio.destination); } device.pDevice = pDevice; - return miniaudio.track_device(device); + return window.miniaudio.track_device(device); }, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice); if (deviceIndex < 0) { @@ -40333,7 +40710,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co pDevice->webaudio.deviceIndex = deviceIndex; /* Grab the sample rate from the audio context directly. */ - sampleRate = (ma_uint32)EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); + sampleRate = (ma_uint32)EM_ASM_INT({ return window.miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); if (pDescriptorCapture != NULL) { pDescriptorCapture->format = ma_format_f32; @@ -40363,9 +40740,9 @@ static ma_result ma_device_start__webaudio(ma_device* pDevice) MA_ASSERT(pDevice != NULL); EM_ASM({ - var device = miniaudio.get_device_by_index($0); + var device = window.miniaudio.get_device_by_index($0); device.webaudio.resume(); - device.state = miniaudio.device_state.started; + device.state = window.miniaudio.device_state.started; }, pDevice->webaudio.deviceIndex); return MA_SUCCESS; @@ -40385,9 +40762,9 @@ static ma_result ma_device_stop__webaudio(ma_device* pDevice) do any kind of explicit draining. */ EM_ASM({ - var device = miniaudio.get_device_by_index($0); + var device = window.miniaudio.get_device_by_index($0); device.webaudio.suspend(); - device.state = miniaudio.device_state.stopped; + device.state = window.miniaudio.device_state.stopped; }, pDevice->webaudio.deviceIndex); ma_device__on_notification_stopped(pDevice); @@ -40405,6 +40782,10 @@ static ma_result ma_context_uninit__webaudio(ma_context* pContext) /* Remove the global miniaudio object from window if there are no more references to it. */ EM_ASM({ if (typeof(window.miniaudio) !== 'undefined') { + miniaudio.unlock_event_types.map(function(event_type) { + document.removeEventListener(event_type, miniaudio.unlock, true); + }); + window.miniaudio.referenceCount -= 1; if (window.miniaudio.referenceCount === 0) { delete window.miniaudio; @@ -40446,6 +40827,7 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex window.miniaudio.device_state.started = $4; /* Device cache for mapping devices to indexes for JavaScript/C interop. */ + let miniaudio = window.miniaudio; miniaudio.devices = []; miniaudio.track_device = function(device) { @@ -40497,13 +40879,13 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex var device = miniaudio.devices[i]; if (device != null && device.webaudio != null && - device.state === window.miniaudio.device_state.started) { + device.state === miniaudio.device_state.started) { device.webaudio.resume().then(() => { - Module._ma_device__on_notification_unlocked(device.pDevice); - }, - (error) => {console.error("Failed to resume audiocontext", error); - }); + _ma_device__on_notification_unlocked(device.pDevice); + }, + (error) => {console.error("Failed to resume audiocontext", error); + }); } } miniaudio.unlock_event_types.map(function(event_type) { @@ -40539,7 +40921,7 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex return MA_SUCCESS; } -#endif /* Web Audio */ +#endif /* MA_HAS_WEBAUDIO */ @@ -40818,7 +41200,7 @@ MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceTy ma_device_info deviceInfo; if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - result = ma_device_get_info(pDevice, (deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo); + result = ma_device_get_info(pDevice, ma_device_type_capture, &deviceInfo); if (result == MA_SUCCESS) { ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); } else { @@ -40865,7 +41247,7 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) #endif /* - When the device is being initialized it's initial state is set to ma_device_state_uninitialized. Before returning from + When the device is being initialized its initial state is set to ma_device_state_uninitialized. Before returning from ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker thread to signal an event to know when the worker thread is ready for action. @@ -41210,6 +41592,24 @@ MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_ } +MA_API ma_bool32 ma_device_id_equal(const ma_device_id* pA, const ma_device_id* pB) +{ + size_t i; + + if (pA == NULL || pB == NULL) { + return MA_FALSE; + } + + for (i = 0; i < sizeof(ma_device_id); i += 1) { + if (((const char*)pA)[i] != ((const char*)pB)[i]) { + return MA_FALSE; + } + } + + return MA_TRUE; +} + + MA_API ma_context_config ma_context_config_init(void) { @@ -41983,7 +42383,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC return result; } - /* Wait for the worker thread to put the device into it's stopped state for real. */ + /* Wait for the worker thread to put the device into its stopped state for real. */ ma_event_wait(&pDevice->stopEvent); MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); } else { @@ -42009,7 +42409,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend)); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; - ma_device_get_name(pDevice, (pDevice->type == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, name, sizeof(name), NULL); + ma_device_get_name(pDevice, ma_device_type_capture, name, sizeof(name), NULL); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Capture"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format)); @@ -42262,6 +42662,17 @@ MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_ if (type == ma_device_type_playback) { return ma_context_get_device_info(pDevice->pContext, type, pDevice->playback.pID, pDeviceInfo); } else { + /* + Here we're getting the capture side, which is the branch we'll be entering for a loopback + device, since loopback is capturing. However, if the device is using the default device ID, + it won't get the correct information because it'll think we're asking for the default + capture device, where in fact for loopback we want the default *playback* device. We'll do + a bit of a hack here to make sure we get the correct info. + */ + if (pDevice->type == ma_device_type_loopback && pDevice->capture.pID == NULL) { + type = ma_device_type_playback; + } + return ma_context_get_device_info(pDevice->pContext, type, pDevice->capture.pID, pDeviceInfo); } } @@ -42323,6 +42734,15 @@ MA_API ma_result ma_device_start(ma_device* pDevice) ma_mutex_lock(&pDevice->startStopLock); { + /* + We need to check again if the device is in a started state because it's possible for one thread to have started the device + while another was waiting on the mutex. + */ + if (ma_device_get_state(pDevice) == ma_device_state_started) { + ma_mutex_unlock(&pDevice->startStopLock); + return MA_SUCCESS; /* Already started. */ + } + /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */ MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); @@ -42383,6 +42803,15 @@ MA_API ma_result ma_device_stop(ma_device* pDevice) ma_mutex_lock(&pDevice->startStopLock); { + /* + We need to check again if the device is in a stopped state because it's possible for one thread to have stopped the device + while another was waiting on the mutex. + */ + if (ma_device_get_state(pDevice) == ma_device_state_stopped) { + ma_mutex_unlock(&pDevice->startStopLock); + return MA_SUCCESS; /* Already stopped. */ + } + /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */ MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started); @@ -42401,7 +42830,7 @@ MA_API ma_result ma_device_stop(ma_device* pDevice) } else { /* Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If - the backend is implementing it's own audio thread loop we'll need to wake it up if required. Note that we need to make + the backend is implementing its own audio thread loop we'll need to wake it up if required. Note that we need to make sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super important though, so I'm asserting it here as well for extra safety in case we accidentally change something later. */ @@ -42518,6 +42947,15 @@ MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void return MA_INVALID_ARGS; } + /* + There is an assert deeper in the code that checks that frameCount > 0. Since this is a public facing + API we'll need to check for that here. I've had reports that AAudio can sometimes post a frame count + of 0. + */ + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + if (pDevice->type == ma_device_type_duplex) { if (pInput != NULL) { ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb); @@ -42592,7 +43030,7 @@ MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 return 0; } - return bufferSizeInFrames*1000 / sampleRate; + return (bufferSizeInFrames*1000 + (sampleRate - 1)) / sampleRate; } MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate) @@ -47420,7 +47858,7 @@ static ma_result ma_bpf_get_heap_layout(const ma_bpf_config* pConfig, ma_bpf_hea return MA_INVALID_ARGS; } - bpf2Count = pConfig->channels / 2; + bpf2Count = pConfig->order / 2; pHeapLayout->sizeInBytes = 0; @@ -49478,7 +49916,7 @@ MA_API float ma_fader_get_current_volume(const ma_fader* pFader) } else if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { /* Safe case because the < 0 case was checked above. */ return pFader->volumeEnd; } else { - /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpoluation between volumeBeg and volumeEnd based on our cursor position. */ + /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpolation between volumeBeg and volumeEnd based on our cursor position. */ return ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, (ma_uint32)pFader->cursorInFrames / (float)((ma_uint32)pFader->lengthInFrames)); /* Safe cast to uint32 because we clamp it in ma_fader_process_pcm_frames(). */ } } @@ -49701,9 +50139,9 @@ static float ma_attenuation_exponential(float distance, float minDistance, float /* -Dopper Effect calculation taken from the OpenAL spec, with two main differences: +Doppler Effect calculation taken from the OpenAL spec, with two main differences: - 1) The source to listener vector will have already been calcualted at an earlier step so we can + 1) The source to listener vector will have already been calculated at an earlier step so we can just use that directly. We need only the position of the source relative to the origin. 2) We don't scale by a frequency because we actually just want the ratio which we'll plug straight @@ -49742,7 +50180,7 @@ static void ma_get_default_channel_map_for_spatializer(ma_channel* pChannelMap, Special case for stereo. Want to default the left and right speakers to side left and side right so that they're facing directly down the X axis rather than slightly forward. Not doing this will result in sounds being quieter when behind the listener. This might - actually be good for some scenerios, but I don't think it's an appropriate default because + actually be good for some scenarios, but I don't think it's an appropriate default because it can be a bit unexpected. */ if (channelCount == 2) { @@ -50076,7 +50514,7 @@ MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma config.maxDistance = MA_FLT_MAX; config.rolloff = 1; config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterAngleInRadians = 6.283185f; /* 360 degress. */ + config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */ config.coneOuterGain = 0.0f; config.dopplerFactor = 1; config.directionalAttenuationFactor = 1; @@ -50310,7 +50748,7 @@ static float ma_calculate_angular_gain(ma_vec3f dirA, ma_vec3f dirB, float coneI To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We just need to get the direction from the source to the listener and then do a dot product against that and the direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer - angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than + angles. If the dot product is greater than the outer angle, we just use coneOuterGain. If it's less than the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. */ if (coneInnerAngleInRadians < 6.283185f) { @@ -50380,7 +50818,7 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_vec3f relativePosNormalized; ma_vec3f relativePos; /* The position relative to the listener. */ ma_vec3f relativeDir; /* The direction of the sound, relative to the listener. */ - ma_vec3f listenerVel; /* The volocity of the listener. For doppler pitch calculation. */ + ma_vec3f listenerVel; /* The velocity of the listener. For doppler pitch calculation. */ float speedOfSound; float distance = 0; float gain = 1; @@ -50461,11 +50899,11 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We just need to get the direction from the source to the listener and then do a dot product against that and the direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer - angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than + angles. If the dot product is greater than the outer angle, we just use coneOuterGain. If it's less than the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. */ if (distance > 0) { - /* Source anglular gain. */ + /* Source angular gain. */ float spatializerConeInnerAngle; float spatializerConeOuterAngle; float spatializerConeOuterGain; @@ -50977,7 +51415,7 @@ MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatiali listenerDirection = ma_spatializer_listener_get_direction(pListener); /* - We need to calcualte the right vector from our forward and up vectors. This is done with + We need to calculate the right vector from our forward and up vectors. This is done with a cross product. */ axisZ = ma_vec3f_normalize(listenerDirection); /* Normalization required here because we can't trust the caller. */ @@ -51123,7 +51561,7 @@ static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pRes lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder); /* - If the resampler is alreay initialized we don't want to do a fresh initialization of the low-pass filter because it will result in the cached frames + If the resampler is already initialized we don't want to do a fresh initialization of the low-pass filter because it will result in the cached frames getting cleared. Instead we re-initialize the filter which will maintain any cached frames. */ if (isResamplerAlreadyInitialized) { @@ -51818,7 +52256,7 @@ MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_li preliminaryInputFrameCount = (pResampler->inTimeInt + outputFrameCount*pResampler->inAdvanceInt ) + preliminaryInputFrameCountFromFrac; /* - If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greather than + If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greater than the amount of whole input frames we have available as input we need to *not* add an extra output frame as there won't be enough data to actually process. Otherwise we need to add the extra output frame. */ @@ -51856,7 +52294,7 @@ MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler) } } - /* The low pass filter needs to have it's cache reset. */ + /* The low pass filter needs to have its cache reset. */ ma_lpf_clear_cache(&pResampler->lpf); return MA_SUCCESS; @@ -52373,19 +52811,19 @@ static float ma_calculate_channel_position_rectangular_weight(ma_channel channel of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated. Imagine the front/left speaker as emitting audio from two planes - the front plane and the left plane. You can think of the front/left - speaker emitting half of it's total volume from the front, and the other half from the left. Since part of it's volume is being emitted + speaker emitting half of its total volume from the front, and the other half from the left. Since part of its volume is being emitted from the left side, and the side/left and back/left channels also emit audio from the left plane, one would expect that they would receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between the two speakers. Note that in the examples below I've added a top/front/left speaker as an example just to show how the math works across 3 spatial dimensions. The first thing to do is figure out how each speaker's volume is spread over each of plane: - - front/left: 2 planes (front and left) = 1/2 = half it's total volume on each plane + - front/left: 2 planes (front and left) = 1/2 = half its total volume on each plane - side/left: 1 plane (left only) = 1/1 = entire volume from left plane - - back/left: 2 planes (back and left) = 1/2 = half it's total volume on each plane - - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane + - back/left: 2 planes (back and left) = 1/2 = half its total volume on each plane + - top/front/left: 3 planes (top, front and left) = 1/3 = one third its total volume on each plane - The amount of volume each channel contributes to each of it's planes is what controls how much it is willing to given and take to other + The amount of volume each channel contributes to each of its planes is what controls how much it is willing to given and take to other channels on the same plane. The volume that is willing to the given by one channel is multiplied by the volume that is willing to be taken by the other to produce the final contribution. */ @@ -52496,12 +52934,7 @@ static ma_channel_conversion_path ma_channel_map_get_conversion_path(const ma_ch ma_uint32 iChannelIn; ma_bool32 areAllChannelPositionsPresent = MA_TRUE; for (iChannelIn = 0; iChannelIn < channelsIn; ++iChannelIn) { - ma_bool32 isInputChannelPositionInOutput = MA_FALSE; - if (ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn))) { - isInputChannelPositionInOutput = MA_TRUE; - break; - } - + ma_bool32 isInputChannelPositionInOutput = ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn)); if (!isInputChannelPositionInOutput) { areAllChannelPositionsPresent = MA_FALSE; break; @@ -52528,8 +52961,8 @@ static ma_result ma_channel_map_build_shuffle_table(const ma_channel* pChannelMa } /* - When building the shuffle table we just do a 1:1 mapping based on the first occurance of a channel. If the - input channel has more than one occurance of a channel position, the second one will be ignored. + When building the shuffle table we just do a 1:1 mapping based on the first occurrence of a channel. If the + input channel has more than one occurrence of a channel position, the second one will be ignored. */ for (iChannelOut = 0; iChannelOut < channelCountOut; iChannelOut += 1) { ma_channel channelOut; @@ -54824,7 +55257,7 @@ static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_co Before doing any processing we need to determine how many frames we should try processing this iteration, for both input and output. The resampler requires us to perform format and channel conversion before passing any data into it. If we get our input count wrong, we'll - end up peforming redundant pre-processing. This isn't the end of the world, but it does + end up performing redundant pre-processing. This isn't the end of the world, but it does result in some inefficiencies proportionate to how far our estimates are off. If the resampler has a means to calculate exactly how much we'll need, we'll use that. @@ -55994,7 +56427,7 @@ MA_API const char* ma_channel_position_to_string(ma_channel channel) case MA_CHANNEL_LFE : return "CHANNEL_LFE"; case MA_CHANNEL_BACK_LEFT : return "CHANNEL_BACK_LEFT"; case MA_CHANNEL_BACK_RIGHT : return "CHANNEL_BACK_RIGHT"; - case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER "; + case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER"; case MA_CHANNEL_FRONT_RIGHT_CENTER: return "CHANNEL_FRONT_RIGHT_CENTER"; case MA_CHANNEL_BACK_CENTER : return "CHANNEL_BACK_CENTER"; case MA_CHANNEL_SIDE_LEFT : return "CHANNEL_SIDE_LEFT"; @@ -56299,13 +56732,9 @@ MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes) newReadOffsetLoopFlag ^= 0x80000000; } - ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes)); + ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); - if (ma_rb_pointer_distance(pRB) == 0) { - return MA_AT_END; - } else { - return MA_SUCCESS; - } + return MA_SUCCESS; } MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) @@ -56385,13 +56814,9 @@ MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes) newWriteOffsetLoopFlag ^= 0x80000000; } - ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes)); + ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); - if (ma_rb_pointer_distance(pRB) == 0) { - return MA_AT_END; - } else { - return MA_SUCCESS; - } + return MA_SUCCESS; } MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes) @@ -56614,6 +57039,16 @@ static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, voi totalFramesRead += mappedFrameCount; } + /* + There is no notion of an "end" in a ring buffer. If we didn't have enough data to fill the requested frame + count we'll need to pad with silence. If we don't do this, totalFramesRead might equal 0 which will result + in the data source layer at a higher level translating this to MA_AT_END which is incorrect for a ring buffer. + */ + if (totalFramesRead < frameCount) { + ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), (frameCount - totalFramesRead), pRB->format, pRB->channels); + totalFramesRead = frameCount; + } + *pFramesRead = totalFramesRead; return MA_SUCCESS; } @@ -57162,6 +57597,10 @@ MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_da return MA_INVALID_ARGS; } + if (pConfig->vtable == NULL) { + return MA_INVALID_ARGS; + } + pDataSourceBase->vtable = pConfig->vtable; pDataSourceBase->rangeBegInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG; pDataSourceBase->rangeEndInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END; @@ -57212,6 +57651,58 @@ static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_ return MA_SUCCESS; } +static ma_result ma_data_source_read_pcm_frames_from_backend(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + + MA_ASSERT(pDataSourceBase != NULL); + MA_ASSERT(pDataSourceBase->vtable != NULL); + MA_ASSERT(pDataSourceBase->vtable->onRead != NULL); + MA_ASSERT(pFramesRead != NULL); + + if (pFramesOut != NULL) { + return pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, pFramesRead); + } else { + /* + No output buffer. Probably seeking forward. Read and discard. Can probably optimize this in terms of + onSeek and onGetCursor, but need to keep in mind that the data source may not implement these functions. + */ + ma_result result; + ma_uint64 framesRead; + ma_format format; + ma_uint32 channels; + ma_uint64 discardBufferCapInFrames; + ma_uint8 pDiscardBuffer[4096]; + + result = ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0); + if (result != MA_SUCCESS) { + return result; + } + + discardBufferCapInFrames = sizeof(pDiscardBuffer) / ma_get_bytes_per_frame(format, channels); + + framesRead = 0; + while (framesRead < frameCount) { + ma_uint64 framesReadThisIteration = 0; + ma_uint64 framesToRead = frameCount - framesRead; + if (framesToRead > discardBufferCapInFrames) { + framesToRead = discardBufferCapInFrames; + } + + result = pDataSourceBase->vtable->onRead(pDataSourceBase, pDiscardBuffer, framesToRead, &framesReadThisIteration); + if (result != MA_SUCCESS) { + return result; + } + + framesRead += framesReadThisIteration; + } + + *pFramesRead = framesRead; + + return MA_SUCCESS; + } +} + static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; @@ -57227,9 +57718,11 @@ static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDa return MA_INVALID_ARGS; } + MA_ASSERT(pDataSourceBase->vtable != NULL); + if ((pDataSourceBase->vtable->flags & MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT) != 0 || (pDataSourceBase->rangeEndInFrames == ~((ma_uint64)0) && (pDataSourceBase->loopEndInFrames == ~((ma_uint64)0) || loop == MA_FALSE))) { /* Either the data source is self-managing the range, or no range is set - just read like normal. The data source itself will tell us when the end is reached. */ - result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); + result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead); } else { /* Need to clamp to within the range. */ ma_uint64 relativeCursor; @@ -57238,7 +57731,7 @@ static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDa result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &relativeCursor); if (result != MA_SUCCESS) { /* Failed to retrieve the cursor. Cannot read within a range or loop points. Just read like normal - this may happen for things like noise data sources where it doesn't really matter. */ - result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); + result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead); } else { ma_uint64 rangeBeg; ma_uint64 rangeEnd; @@ -57266,7 +57759,7 @@ static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDa MA_AT_END so the higher level function can know about it. */ if (frameCount > 0) { - result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); + result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead); } else { result = MA_AT_END; /* The cursor is sitting on the end of the range which means we're at the end. */ } @@ -57348,7 +57841,7 @@ MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, voi totalFramesProcessed += framesProcessed; /* - If we encounted an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is + If we encountered an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is not necessarily considered an error. */ if (result != MA_SUCCESS && result != MA_AT_END) { @@ -57439,7 +57932,7 @@ MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, m ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; if (pDataSourceBase == NULL) { - return MA_SUCCESS; + return MA_INVALID_ARGS; } if (pDataSourceBase->vtable->onSeek == NULL) { @@ -57447,12 +57940,61 @@ MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, m } if (frameIndex > pDataSourceBase->rangeEndInFrames) { - return MA_INVALID_OPERATION; /* Trying to seek to far forward. */ + return MA_INVALID_OPERATION; /* Trying to seek too far forward. */ } + MA_ASSERT(pDataSourceBase->vtable != NULL); + return pDataSourceBase->vtable->onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex); } +MA_API ma_result ma_data_source_seek_seconds(ma_data_source* pDataSource, float secondCount, float* pSecondsSeeked) +{ + ma_uint64 frameCount; + ma_uint64 framesSeeked = 0; + ma_uint32 sampleRate; + ma_result result; + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; + } + + /* We need PCM frames instead of seconds */ + frameCount = (ma_uint64)(secondCount * sampleRate); + + result = ma_data_source_seek_pcm_frames(pDataSource, frameCount, &framesSeeked); + + /* VC6 doesn't support division between unsigned 64-bit integer and floating point number. Signed integer needed. This shouldn't affect anything in practice */ + *pSecondsSeeked = (ma_int64)framesSeeked / (float)sampleRate; + return result; +} + +MA_API ma_result ma_data_source_seek_to_second(ma_data_source* pDataSource, float seekPointInSeconds) +{ + ma_uint64 frameIndex; + ma_uint32 sampleRate; + ma_result result; + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; + } + + /* We need PCM frames instead of seconds */ + frameIndex = (ma_uint64)(seekPointInSeconds * sampleRate); + + return ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex); +} + MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; @@ -57479,6 +58021,8 @@ MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_ return MA_INVALID_ARGS; } + MA_ASSERT(pDataSourceBase->vtable != NULL); + if (pDataSourceBase->vtable->onGetDataFormat == NULL) { return MA_NOT_IMPLEMENTED; } @@ -57519,6 +58063,8 @@ MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSo return MA_SUCCESS; } + MA_ASSERT(pDataSourceBase->vtable != NULL); + if (pDataSourceBase->vtable->onGetCursor == NULL) { return MA_NOT_IMPLEMENTED; } @@ -57552,6 +58098,8 @@ MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSo return MA_INVALID_ARGS; } + MA_ASSERT(pDataSourceBase->vtable != NULL); + /* If we have a range defined we'll use that to determine the length. This is one of rare times where we'll actually trust the caller. If they've set the range, I think it's mostly safe to @@ -57639,6 +58187,8 @@ MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool ma_atomic_exchange_32(&pDataSourceBase->isLooping, isLooping); + MA_ASSERT(pDataSourceBase->vtable != NULL); + /* If there's no callback for this just treat it as a successful no-op. */ if (pDataSourceBase->vtable->onSetLooping == NULL) { return MA_SUCCESS; @@ -57676,7 +58226,7 @@ MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSou /* We may need to adjust the position of the cursor to ensure it's clamped to the range. Grab it now - so we can calculate it's absolute position before we change the range. + so we can calculate its absolute position before we change the range. */ result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &relativeCursor); if (result == MA_SUCCESS) { @@ -57710,7 +58260,7 @@ MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSou /* Seek to within range. Note that our seek positions here are relative to the new range. We don't want - do do this if we failed to retrieve the cursor earlier on because it probably means the data source + to do this if we failed to retrieve the cursor earlier on because it probably means the data source has no notion of a cursor. In practice the seek would probably fail (which we silently ignore), but I'm just not even going to attempt it. */ @@ -57729,6 +58279,13 @@ MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSo { const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; + if (pRangeBegInFrames != NULL) { + *pRangeBegInFrames = 0; + } + if (pRangeEndInFrames != NULL) { + *pRangeEndInFrames = 0; + } + if (pDataSource == NULL) { return; } @@ -57773,6 +58330,13 @@ MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pD { const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; + if (pLoopBegInFrames != NULL) { + *pLoopBegInFrames = 0; + } + if (pLoopEndInFrames != NULL) { + *pLoopEndInFrames = 0; + } + if (pDataSource == NULL) { return; } @@ -59167,7 +59731,7 @@ static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_i result = ma_SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod); } else if (ma_SetFilePointer != NULL) { /* No SetFilePointerEx() so restrict to 31 bits. */ - if (origin > 0x7FFFFFFF) { + if (offset > 0x7FFFFFFF) { return MA_OUT_OF_RANGE; } @@ -59377,7 +59941,7 @@ static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_i result = _fseeki64((FILE*)file, offset, whence); #else /* No _fseeki64() so restrict to 31 bits. */ - if (origin > 0x7FFFFFFF) { + if (offset > 0x7FFFFFFF) { return MA_OUT_OF_RANGE; } @@ -59770,7 +60334,7 @@ extern "C" { #define MA_DR_WAV_XSTRINGIFY(x) MA_DR_WAV_STRINGIFY(x) #define MA_DR_WAV_VERSION_MAJOR 0 #define MA_DR_WAV_VERSION_MINOR 13 -#define MA_DR_WAV_VERSION_REVISION 13 +#define MA_DR_WAV_VERSION_REVISION 18 #define MA_DR_WAV_VERSION_STRING MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MAJOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MINOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_REVISION) #include #define MA_DR_WAVE_FORMAT_PCM 0x1 @@ -60190,7 +60754,7 @@ extern "C" { #define MA_DR_FLAC_XSTRINGIFY(x) MA_DR_FLAC_STRINGIFY(x) #define MA_DR_FLAC_VERSION_MAJOR 0 #define MA_DR_FLAC_VERSION_MINOR 12 -#define MA_DR_FLAC_VERSION_REVISION 42 +#define MA_DR_FLAC_VERSION_REVISION 43 #define MA_DR_FLAC_VERSION_STRING MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MAJOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MINOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_REVISION) #include #if defined(_MSC_VER) && _MSC_VER >= 1700 @@ -60477,7 +61041,7 @@ extern "C" { #define MA_DR_MP3_XSTRINGIFY(x) MA_DR_MP3_STRINGIFY(x) #define MA_DR_MP3_VERSION_MAJOR 0 #define MA_DR_MP3_VERSION_MINOR 6 -#define MA_DR_MP3_VERSION_REVISION 38 +#define MA_DR_MP3_VERSION_REVISION 40 #define MA_DR_MP3_VERSION_STRING MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MAJOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MINOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_REVISION) #include #define MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 @@ -60639,7 +61203,7 @@ MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint3 return config; } -MA_API ma_decoder_config ma_decoder_config_init_default() +MA_API ma_decoder_config ma_decoder_config_init_default(void) { return ma_decoder_config_init(ma_format_unknown, 0, 0); } @@ -63232,7 +63796,7 @@ MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_ #if !defined(MA_NO_VORBIS) { /* - stb_vorbis lacks a callback based API for it's pulling API which means we're stuck with the + stb_vorbis lacks a callback based API for its pulling API which means we're stuck with the pushing API. In order for us to be able to successfully initialize the decoder we need to supply it with enough data. We need to keep loading data until we have enough. */ @@ -63313,7 +63877,7 @@ MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, co { (void)pAllocationCallbacks; - /* stb_vorbis uses an int as it's size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */ + /* stb_vorbis uses an int as its size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */ if (dataSize > INT_MAX) { return MA_TOO_BIG; } @@ -63403,7 +63967,7 @@ MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFram /* The first thing to do is read from any already-cached frames. */ ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->push.framesRemaining, (frameCount - totalFramesRead)); /* Safe cast because pVorbis->framesRemaining is 32-bit. */ - /* The output pointer can be null in which case we just treate it as a seek. */ + /* The output pointer can be null in which case we just treat it as a seek. */ if (pFramesOut != NULL) { ma_uint64 iFrame; for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) { @@ -63477,7 +64041,7 @@ MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFram } } - /* If we don't have a success code at this point it means we've encounted an error or the end of the file has been reached (probably the latter). */ + /* If we don't have a success code at this point it means we've encountered an error or the end of the file has been reached (probably the latter). */ if (result != MA_SUCCESS) { break; } @@ -64291,8 +64855,7 @@ MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, cons #if defined(MA_HAS_WAV) || \ defined(MA_HAS_MP3) || \ defined(MA_HAS_FLAC) || \ - defined(MA_HAS_VORBIS) || \ - defined(MA_HAS_OPUS) + defined(MA_HAS_VORBIS) #define MA_HAS_PATH_API #endif @@ -65107,7 +65670,7 @@ MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesO } else { /* Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we - need to run through each sample because we need to ensure it's internal cache is updated. + need to run through each sample because we need to ensure its internal cache is updated. */ if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) { result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut); @@ -65197,8 +65760,17 @@ MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesO if (requiredInputFrameCount > 0) { result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn); + + /* + Note here that even if we've reached the end, we don't want to abort because there might be more output frames needing to be + generated from cached input data, which might happen if resampling is being performed. + */ + if (result != MA_SUCCESS && result != MA_AT_END) { + break; + } } else { framesReadThisIterationIn = 0; + pIntermediaryBuffer[0] = 0; /* <-- This is just to silence a static analysis warning. */ } /* @@ -66679,7 +67251,7 @@ MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type) /* This function should never have been implemented in the first place. Changing the type dynamically is not - supported. Instead you need to uninitialize and reinitiailize a fresh `ma_noise` object. This function + supported. Instead you need to uninitialize and reinitialize a fresh `ma_noise` object. This function will be removed in version 0.12. */ MA_ASSERT(MA_FALSE); @@ -67725,7 +68297,7 @@ MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pCon pResourceManager->config.pVFS = &pResourceManager->defaultVFS; } - /* If threading has been disabled at compile time, enfore it at run time as well. */ + /* If threading has been disabled at compile time, enforce it at run time as well. */ #ifdef MA_NO_THREADING { pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; @@ -67762,15 +68334,17 @@ MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pCon /* Custom decoding backends. */ if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) { size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount; + ma_decoding_backend_vtable** ppCustomDecodingBackendVTables; - pResourceManager->config.ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks); + ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks); if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) { ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); return MA_OUT_OF_MEMORY; } - MA_COPY_MEMORY(pResourceManager->config.ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes); + MA_COPY_MEMORY(ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes); + pResourceManager->config.ppCustomDecodingBackendVTables = ppCustomDecodingBackendVTables; pResourceManager->config.customDecodingBackendCount = pConfig->customDecodingBackendCount; pResourceManager->config.pCustomDecodingBackendUserData = pConfig->pCustomDecodingBackendUserData; } @@ -67821,7 +68395,7 @@ static void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode; ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - /* The data buffer has been removed from the BST, so now we need to free it's data. */ + /* The data buffer has been removed from the BST, so now we need to free its data. */ ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); } } @@ -67834,7 +68408,7 @@ MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager) /* Job threads need to be killed first. To do this we need to post a quit message to the message queue and then wait for the thread. The quit message will never be removed from the - queue which means it will never not be returned after being encounted for the first time which means all threads will eventually receive it. + queue which means it will never not be returned after being encountered for the first time which means all threads will eventually receive it. */ ma_resource_manager_post_job_quit(pResourceManager); @@ -67874,7 +68448,7 @@ MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager) #endif } - ma_free(pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks); + ma_free((ma_decoding_backend_vtable**)pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks); /* <-- Naughty const-cast, but this is safe. */ if (pResourceManager->config.pLog == &pResourceManager->log) { ma_log_uninit(&pResourceManager->log); @@ -68292,7 +68866,7 @@ static ma_result ma_resource_manager_data_buffer_node_decode_next_page(ma_resour } result = ma_decoder_read_pcm_frames(pDecoder, pPage->pAudioData, framesToTryReading, &framesRead); - if (framesRead > 0) { + if (result == MA_SUCCESS && framesRead > 0) { pPage->sizeInFrames = framesRead; result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage); @@ -68445,7 +69019,7 @@ static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(m if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { ma_resource_manager_inline_notification_uninit(pInitNotification); } else { - /* These will have been freed by the job thread, but with WAIT_INIT they will already have happend sinced the job has already been handled. */ + /* These will have been freed by the job thread, but with WAIT_INIT they will already have happened since the job has already been handled. */ ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); } @@ -68810,6 +69384,10 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; } + if (pConfig->isLooping) { + flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; + } + async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0; /* @@ -68822,7 +69400,7 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma These fences are always released at the "done" tag at the end of this function. They'll be acquired a second if loading asynchronously. This double acquisition system is just done to - simplify code maintanence. + simplify code maintenance. */ ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); { @@ -68867,7 +69445,7 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma /* The status of the data buffer needs to be set to MA_BUSY before posting the job so that the - worker thread is aware of it's busy state. If the LOAD_DATA_BUFFER job sees a status other + worker thread is aware of its busy state. If the LOAD_DATA_BUFFER job sees a status other than MA_BUSY, it'll assume an error and fall through to an early exit. */ ma_atomic_exchange_i32(&pDataBuffer->result, MA_BUSY); @@ -68886,7 +69464,7 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma job.data.resourceManager.loadDataBuffer.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; job.data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; - job.data.resourceManager.loadDataBuffer.isLooping = pConfig->isLooping; + job.data.resourceManager.loadDataBuffer.isLooping = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0; /* If we need to wait for initialization to complete we can just process the job in place. */ if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { @@ -69107,22 +69685,29 @@ MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_man isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY); if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) { - /* Don't try reading more than the available frame count. */ - if (frameCount > availableFrames) { - frameCount = availableFrames; + /* Don't try reading more than the available frame count if the data buffer node is still loading. */ + if (isDecodedBufferBusy) { + if (frameCount > availableFrames) { + frameCount = availableFrames; - /* - If there's no frames available we want to set the status to MA_AT_END. The logic below - will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this - is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count - is 0 because that'll result in a situation where it's possible MA_AT_END won't get - returned. - */ - if (frameCount == 0) { - result = MA_AT_END; + /* + If there's no frames available we want to set the status to MA_AT_END. The logic below + will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this + is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count + is 0 because that'll result in a situation where it's possible MA_AT_END won't get + returned. + */ + if (frameCount == 0) { + result = MA_AT_END; + } + } else { + isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */ } } else { - isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */ + /* + Getting here means the buffer has been fully loaded. We can just pass the frame count straight + into ma_data_source_read_pcm_frames() below and let ma_data_source handle it. + */ } } } @@ -69522,6 +70107,7 @@ MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pR ma_bool32 waitBeforeReturning = MA_FALSE; ma_resource_manager_inline_notification waitNotification; ma_resource_manager_pipeline_notifications notifications; + ma_uint32 flags; if (pDataStream == NULL) { if (pConfig != NULL && pConfig->pNotifications != NULL) { @@ -69552,13 +70138,18 @@ MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pR return result; } + flags = pConfig->flags; + if (pConfig->isLooping) { + flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; + } + pDataStream->pResourceManager = pResourceManager; pDataStream->flags = pConfig->flags; pDataStream->result = MA_BUSY; ma_data_source_set_range_in_pcm_frames(pDataStream, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); ma_data_source_set_loop_point_in_pcm_frames(pDataStream, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); - ma_data_source_set_looping(pDataStream, pConfig->isLooping); + ma_data_source_set_looping(pDataStream, (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0); if (pResourceManager == NULL || (pConfig->pFilePath == NULL && pConfig->pFilePathW == NULL)) { ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); @@ -70180,6 +70771,9 @@ static ma_result ma_resource_manager_data_source_preinit(ma_resource_manager* pR } pDataSource->flags = pConfig->flags; + if (pConfig->isLooping) { + pDataSource->flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; + } return MA_SUCCESS; } @@ -70738,9 +71332,10 @@ static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob */ result = ma_resource_manager_data_buffer_result(pDataBuffer); if (result != MA_BUSY) { - goto done; /* <-- This will ensure the exucution pointer is incremented. */ + goto done; /* <-- This will ensure the execution pointer is incremented. */ } else { result = MA_SUCCESS; /* <-- Make sure this is reset. */ + (void)result; /* <-- This is to suppress a static analysis diagnostic about "result" not being used. But for safety when I do future maintenance I don't want to delete that assignment. */ } /* Try initializing the connector if we haven't already. */ @@ -71087,11 +71682,74 @@ static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob #ifndef MA_NO_NODE_GRAPH + +static ma_stack* ma_stack_init(size_t sizeInBytes, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_stack* pStack; + + if (sizeInBytes == 0) { + return NULL; + } + + pStack = (ma_stack*)ma_malloc(sizeof(*pStack) - sizeof(pStack->_data) + sizeInBytes, pAllocationCallbacks); + if (pStack == NULL) { + return NULL; + } + + pStack->offset = 0; + pStack->sizeInBytes = sizeInBytes; + + return pStack; +} + +static void ma_stack_uninit(ma_stack* pStack, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pStack == NULL) { + return; + } + + ma_free(pStack, pAllocationCallbacks); +} + +static void* ma_stack_alloc(ma_stack* pStack, size_t sz) +{ + /* The size of the allocation is stored in the memory directly before the pointer. This needs to include padding to keep it aligned to ma_uintptr */ + void* p = (void*)((char*)pStack->_data + pStack->offset); + size_t* pSize = (size_t*)p; + + sz = (sz + (sizeof(ma_uintptr) - 1)) & ~(sizeof(ma_uintptr) - 1); /* Padding. */ + if (pStack->offset + sz + sizeof(size_t) > pStack->sizeInBytes) { + return NULL; /* Out of memory. */ + } + + pStack->offset += sz + sizeof(size_t); + + *pSize = sz; + return (void*)((char*)p + sizeof(size_t)); +} + +static void ma_stack_free(ma_stack* pStack, void* p) +{ + size_t* pSize; + + if (p == NULL) { + return; + } + + pSize = (size_t*)p - 1; + pStack->offset -= *pSize + sizeof(size_t); +} + + + /* 10ms @ 48K = 480. Must never exceed 65535. */ #ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS #define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480 #endif +#ifndef MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL +#define MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL 524288 +#endif static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime); @@ -71131,8 +71789,8 @@ MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels) ma_node_graph_config config; MA_ZERO_OBJECT(&config); - config.channels = channels; - config.nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; + config.channels = channels; + config.processingSizeInFrames = 0; return config; } @@ -71219,11 +71877,7 @@ MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const m } MA_ZERO_OBJECT(pNodeGraph); - pNodeGraph->nodeCacheCapInFrames = pConfig->nodeCacheCapInFrames; - if (pNodeGraph->nodeCacheCapInFrames == 0) { - pNodeGraph->nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; - } - + pNodeGraph->processingSizeInFrames = pConfig->processingSizeInFrames; /* Base node so we can use the node graph as a node into another graph. */ baseConfig = ma_node_config_init(); @@ -71248,6 +71902,40 @@ MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const m return result; } + + /* Processing cache. */ + if (pConfig->processingSizeInFrames > 0) { + pNodeGraph->pProcessingCache = (float*)ma_malloc(pConfig->processingSizeInFrames * pConfig->channels * sizeof(float), pAllocationCallbacks); + if (pNodeGraph->pProcessingCache == NULL) { + ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); + ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); + return MA_OUT_OF_MEMORY; + } + } + + + /* + We need a pre-mix stack. The size of this stack is configurable via the config. The default value depends on the channel count. + */ + { + size_t preMixStackSizeInBytes = pConfig->preMixStackSizeInBytes; + if (preMixStackSizeInBytes == 0) { + preMixStackSizeInBytes = pConfig->channels * MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL; + } + + pNodeGraph->pPreMixStack = ma_stack_init(preMixStackSizeInBytes, pAllocationCallbacks); + if (pNodeGraph->pPreMixStack == NULL) { + ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); + ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); + if (pNodeGraph->pProcessingCache != NULL) { + ma_free(pNodeGraph->pProcessingCache, pAllocationCallbacks); + } + + return MA_OUT_OF_MEMORY; + } + } + + return MA_SUCCESS; } @@ -71258,6 +71946,17 @@ MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_ } ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); + ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); + + if (pNodeGraph->pProcessingCache != NULL) { + ma_free(pNodeGraph->pProcessingCache, pAllocationCallbacks); + pNodeGraph->pProcessingCache = NULL; + } + + if (pNodeGraph->pPreMixStack != NULL) { + ma_stack_uninit(pNodeGraph->pPreMixStack, pAllocationCallbacks); + pNodeGraph->pPreMixStack = NULL; + } } MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph) @@ -71290,27 +71989,72 @@ MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* totalFramesRead = 0; while (totalFramesRead < frameCount) { ma_uint32 framesJustRead; - ma_uint64 framesToRead = frameCount - totalFramesRead; + ma_uint64 framesToRead; + float* pRunningFramesOut; + framesToRead = frameCount - totalFramesRead; if (framesToRead > 0xFFFFFFFF) { framesToRead = 0xFFFFFFFF; } - ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE); - { - result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint)); - } - ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE); + pRunningFramesOut = (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels); - totalFramesRead += framesJustRead; + /* If there's anything in the cache, consume that first. */ + if (pNodeGraph->processingCacheFramesRemaining > 0) { + ma_uint32 framesToReadFromCache; - if (result != MA_SUCCESS) { - break; - } + framesToReadFromCache = (ma_uint32)framesToRead; + if (framesToReadFromCache > pNodeGraph->processingCacheFramesRemaining) { + framesToReadFromCache = pNodeGraph->processingCacheFramesRemaining; + } - /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */ - if (framesJustRead == 0) { - break; + MA_COPY_MEMORY(pRunningFramesOut, pNodeGraph->pProcessingCache, framesToReadFromCache * channels * sizeof(float)); + MA_MOVE_MEMORY(pNodeGraph->pProcessingCache, pNodeGraph->pProcessingCache + (framesToReadFromCache * channels), (pNodeGraph->processingCacheFramesRemaining - framesToReadFromCache) * channels * sizeof(float)); + pNodeGraph->processingCacheFramesRemaining -= framesToReadFromCache; + + totalFramesRead += framesToReadFromCache; + continue; + } else { + /* + If processingSizeInFrames is non-zero, we need to make sure we always read in chunks of that size. If the frame count is less than + that, we need to read into the cache and then continue on. + */ + float* pReadDst = pRunningFramesOut; + + if (pNodeGraph->processingSizeInFrames > 0) { + if (framesToRead < pNodeGraph->processingSizeInFrames) { + pReadDst = pNodeGraph->pProcessingCache; /* We need to read into the cache because otherwise we'll overflow the output buffer. */ + } + + framesToRead = pNodeGraph->processingSizeInFrames; + } + + ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE); + { + result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, pReadDst, (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint)); + } + ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE); + + /* + Do not increment the total frames read counter if we read into the cache. We use this to determine how many frames have + been written to the final output buffer. + */ + if (pReadDst == pNodeGraph->pProcessingCache) { + /* We read into the cache. */ + pNodeGraph->processingCacheFramesRemaining = framesJustRead; + } else { + /* We read straight into the output buffer. */ + totalFramesRead += framesJustRead; + } + + if (result != MA_SUCCESS) { + break; + } + + /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */ + if (framesJustRead == 0) { + break; + } } } @@ -71511,7 +72255,7 @@ static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInp *not* using a lock when iterating over the list in the audio thread. We therefore need to craft this in a way such that the iteration on the audio thread doesn't break. - The the first thing to do is swap out the "next" pointer of the previous output bus with the + The first thing to do is swap out the "next" pointer of the previous output bus with the new "next" output bus. This is the operation that matters for iteration on the audio thread. After that, the previous pointer on the new "next" pointer needs to be updated, after which point the linked list will be in a good state. @@ -71604,7 +72348,7 @@ static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_outpu /* Now we need to attach the output bus to the linked list. This involves updating two pointers on two different output buses so I'm going to go ahead and keep this simple and just use a lock. - There are ways to do this without a lock, but it's just too hard to maintain for it's value. + There are ways to do this without a lock, but it's just too hard to maintain for its value. Although we're locking here, it's important to remember that we're *not* locking when iterating and reading audio data since that'll be running on the audio thread. As a result we need to be @@ -71697,11 +72441,9 @@ static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_ ma_uint32 inputChannels; ma_bool32 doesOutputBufferHaveContent = MA_FALSE; - (void)pInputNode; /* Not currently used. */ - /* This will be called from the audio thread which means we can't be doing any locking. Basically, - this function will not perfom any locking, whereas attaching and detaching will, but crafted in + this function will not perform any locking, whereas attaching and detaching will, but crafted in such a way that we don't need to perform any locking here. The important thing to remember is to always iterate in a forward direction. @@ -71747,19 +72489,12 @@ static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_ if (pFramesOut != NULL) { /* Read. */ - float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)]; - ma_uint32 tempCapInFrames = ma_countof(temp) / inputChannels; - while (framesProcessed < frameCount) { float* pRunningFramesOut; ma_uint32 framesToRead; - ma_uint32 framesJustRead; + ma_uint32 framesJustRead = 0; framesToRead = frameCount - framesProcessed; - if (framesToRead > tempCapInFrames) { - framesToRead = tempCapInFrames; - } - pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels); if (doesOutputBufferHaveContent == MA_FALSE) { @@ -71767,11 +72502,32 @@ static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_ result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed); } else { /* Slow path. Not the first attachment. Mixing required. */ - result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, temp, framesToRead, &framesJustRead, globalTime + framesProcessed); - if (result == MA_SUCCESS || result == MA_AT_END) { - if (isSilentOutput == MA_FALSE) { /* Don't mix if the node outputs silence. */ - ma_mix_pcm_frames_f32(pRunningFramesOut, temp, framesJustRead, inputChannels, /*volume*/1); + ma_uint32 preMixBufferCapInFrames = ((ma_node_base*)pInputNode)->cachedDataCapInFramesPerBus; + float* pPreMixBuffer = (float*)ma_stack_alloc(((ma_node_base*)pInputNode)->pNodeGraph->pPreMixStack, preMixBufferCapInFrames * inputChannels * sizeof(float)); + + if (pPreMixBuffer == NULL) { + /* + If you're hitting this assert it means you've got an unusually deep chain of nodes, you've got an excessively large processing + size, or you have a combination of both, and as a result have run out of stack space. You can increase this using the + preMixStackSizeInBytes variable in ma_node_graph_config. If you're using ma_engine, you can do it via the preMixStackSizeInBytes + variable in ma_engine_config. It defaults to 512KB per output channel. + */ + MA_ASSERT(MA_FALSE); + } else { + if (framesToRead > preMixBufferCapInFrames) { + framesToRead = preMixBufferCapInFrames; } + + result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pPreMixBuffer, framesToRead, &framesJustRead, globalTime + framesProcessed); + if (result == MA_SUCCESS || result == MA_AT_END) { + if (isSilentOutput == MA_FALSE) { /* Don't mix if the node outputs silence. */ + ma_mix_pcm_frames_f32(pRunningFramesOut, pPreMixBuffer, framesJustRead, inputChannels, /*volume*/1); + } + } + + /* The pre-mix buffer is no longer required. */ + ma_stack_free(((ma_node_base*)pInputNode)->pNodeGraph->pPreMixStack, pPreMixBuffer); + pPreMixBuffer = NULL; } } @@ -71826,6 +72582,25 @@ MA_API ma_node_config ma_node_config_init(void) return config; } +static ma_uint16 ma_node_config_get_cache_size_in_frames(const ma_node_config* pConfig, const ma_node_graph* pNodeGraph) +{ + ma_uint32 cacheSizeInFrames; + + (void)pConfig; + + if (pNodeGraph->processingSizeInFrames > 0) { + cacheSizeInFrames = pNodeGraph->processingSizeInFrames; + } else { + cacheSizeInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; + } + + if (cacheSizeInFrames > 0xFFFF) { + cacheSizeInFrames = 0xFFFF; + } + + return (ma_uint16)cacheSizeInFrames; +} + static ma_result ma_node_detach_full(ma_node* pNode); @@ -71980,7 +72755,7 @@ static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_nod /* Cached audio data. - We need to allocate memory for a caching both input and output data. We have an optimization + We need to allocate memory for caching both input and output data. We have an optimization where no caching is necessary for specific conditions: - The node has 0 inputs and 1 output. @@ -71999,14 +72774,18 @@ static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_nod } else { /* Slow path. Cache needed. */ size_t cachedDataSizeInBytes = 0; + ma_uint32 cacheCapInFrames; ma_uint32 iBus; + /* The capacity of the cache is based on our callback processing size. */ + cacheCapInFrames = ma_node_config_get_cache_size_in_frames(pConfig, pNodeGraph); + for (iBus = 0; iBus < inputBusCount; iBus += 1) { - cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]); + cachedDataSizeInBytes += cacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]); } for (iBus = 0; iBus < outputBusCount; iBus += 1) { - cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]); + cachedDataSizeInBytes += cacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]); } pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes; @@ -72092,13 +72871,12 @@ MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_n if (heapLayout.cachedDataOffset != MA_SIZE_MAX) { pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset); - pNodeBase->cachedDataCapInFramesPerBus = pNodeGraph->nodeCacheCapInFrames; + pNodeBase->cachedDataCapInFramesPerBus = ma_node_config_get_cache_size_in_frames(pConfig, pNodeGraph); } else { pNodeBase->pCachedData = NULL; } - /* We need to run an initialization step for each input and output bus. */ for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]); @@ -72272,7 +73050,7 @@ static ma_result ma_node_detach_full(ma_node* pNode) /* At this point all output buses will have been detached from the graph and we can be guaranteed - that none of it's input nodes will be getting processed by the graph. We can detach these + that none of its input nodes will be getting processed by the graph. We can detach these without needing to worry about the audio thread touching them. */ for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) { @@ -72287,7 +73065,7 @@ static ma_result ma_node_detach_full(ma_node* pNode) linked list logic. We don't need to worry about the audio thread referencing these because the step above severed the connection to the graph. */ - for (pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext)) { + for (pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext)) { ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex); /* This won't do any waiting in practice and should be efficient. */ } } @@ -72309,7 +73087,7 @@ MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIn return MA_INVALID_ARGS; /* Invalid output bus index. */ } - /* We need to lock the output bus because we need to inspect the input node and grab it's input bus. */ + /* We need to lock the output bus because we need to inspect the input node and grab its input bus. */ ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]); { pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode; @@ -72475,7 +73253,7 @@ MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_ui /* Getting here means the node is marked as started, but it may still not be truly started due to - it's start time not having been reached yet. Also, the stop time may have also been reached in + its start time not having been reached yet. Also, the stop time may have also been reached in which case it'll be considered stopped. */ if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeBeg) { @@ -72486,7 +73264,7 @@ MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_ui return ma_node_state_stopped; /* Stop time has been reached. */ } - /* Getting here means the node is marked as started and is within it's start/stop times. */ + /* Getting here means the node is marked as started and is within its start/stop times. */ return ma_node_state_started; } @@ -72648,12 +73426,12 @@ static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusInde frameCountOut = totalFramesRead; if (totalFramesRead > 0) { - ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */ + ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Explicit cast to silence the warning. */ } /* A passthrough should never have modified the input and output frame counts. If you're - triggering these assers you need to fix your processing callback. + triggering these asserts you need to fix your processing callback. */ MA_ASSERT(frameCountIn == totalFramesRead); MA_ASSERT(frameCountOut == totalFramesRead); @@ -72831,7 +73609,7 @@ static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusInde frames available right now. */ if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) { - ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */ + ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Explicit cast to silence the warning. */ } else { frameCountOut = 0; /* No data was processed. */ } @@ -74068,7 +74846,7 @@ static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngin { MA_ASSERT(pEngineNode != NULL); - /* Don't try to be clever by skiping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */ + /* Don't try to be clever by skipping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */ return !ma_atomic_load_explicit_32(&pEngineNode->isPitchDisabled, ma_atomic_memory_order_acquire); } @@ -74105,7 +74883,7 @@ static ma_result ma_engine_node_set_volume(ma_engine_node* pEngineNode, float vo /* If we're not smoothing we should bypass the volume gainer entirely. */ if (pEngineNode->volumeSmoothTimeInPCMFrames == 0) { - /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for hodling our volume. */ + /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for holding our volume. */ ma_spatializer_set_master_volume(&pEngineNode->spatializer, volume); } else { /* We're using volume smoothing, so apply the master volume to the gainer. */ @@ -74420,7 +75198,7 @@ static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float ma_sound_set_at_end(pSound, MA_TRUE); /* This will be set to false in ma_sound_start(). */ } - pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_engine_get_channels(ma_sound_get_engine(pSound))); + pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_node_get_output_channels(pNode, 0)); frameCountIn = (ma_uint32)framesJustRead; frameCountOut = framesRemaining; @@ -74751,7 +75529,7 @@ MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* p /* - Spatialization comes next. We spatialize based ont he node's output channel count. It's up the caller to + Spatialization comes next. We spatialize based on the node's output channel count. It's up the caller to ensure channels counts link up correctly in the node graph. */ spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); @@ -74941,6 +75719,21 @@ static void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOu ma_engine_read_pcm_frames(pEngine, pFramesOut, frameCount, NULL); } + +static ma_uint32 ma_device__get_processing_size_in_frames(ma_device* pDevice) +{ + /* + The processing size is the period size. The device can have a fixed sized processing size, or + it can be decided by the backend in which case it can be variable. + */ + if (pDevice->playback.intermediaryBufferCap > 0) { + /* Using a fixed sized processing callback. */ + return pDevice->playback.intermediaryBufferCap; + } else { + /* Not using a fixed sized processing callback. Need to estimate the processing size based on the backend. */ + return pDevice->playback.internalPeriodSizeInFrames; + } +} #endif MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine) @@ -75034,6 +75827,14 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng if (pEngine->pDevice != NULL) { engineConfig.channels = pEngine->pDevice->playback.channels; engineConfig.sampleRate = pEngine->pDevice->sampleRate; + + /* + The processing size used by the engine is determined by engineConfig.periodSizeInFrames. We want + to make this equal to what the device is using for it's period size. If we don't do that, it's + possible that the node graph will split it's processing into multiple passes which can introduce + glitching. + */ + engineConfig.periodSizeInFrames = ma_device__get_processing_size_in_frames(pEngine->pDevice); } } #endif @@ -75060,9 +75861,10 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng } - /* The engine is a node graph. This needs to be initialized after we have the device so we can can determine the channel count. */ + /* The engine is a node graph. This needs to be initialized after we have the device so we can determine the channel count. */ nodeGraphConfig = ma_node_graph_config_init(engineConfig.channels); - nodeGraphConfig.nodeCacheCapInFrames = (engineConfig.periodSizeInFrames > 0xFFFF) ? 0xFFFF : (ma_uint16)engineConfig.periodSizeInFrames; + nodeGraphConfig.processingSizeInFrames = engineConfig.periodSizeInFrames; + nodeGraphConfig.preMixStackSizeInBytes = engineConfig.preMixStackSizeInBytes; result = ma_node_graph_init(&nodeGraphConfig, &pEngine->allocationCallbacks, &pEngine->nodeGraph); if (result != MA_SUCCESS) { @@ -75142,8 +75944,8 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks); resourceManagerConfig.pVFS = engineConfig.pResourceManagerVFS; - /* The Emscripten build cannot use threads. */ - #if defined(MA_EMSCRIPTEN) + /* The Emscripten build cannot use threads unless it's targeting pthreads. */ + #if defined(MA_EMSCRIPTEN) && !defined(__EMSCRIPTEN_PTHREADS__) { resourceManagerConfig.jobThreadCount = 0; resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; @@ -75658,7 +76460,7 @@ MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePa return MA_INVALID_ARGS; } - /* Attach to the endpoint node if nothing is specicied. */ + /* Attach to the endpoint node if nothing is specified. */ if (pNode == NULL) { pNode = ma_node_graph_get_endpoint(&pEngine->nodeGraph); nodeInputBusIndex = 0; @@ -75875,7 +76677,7 @@ static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, con ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); } - ma_sound_set_looping(pSound, pConfig->isLooping); + ma_sound_set_looping(pSound, pConfig->isLooping || ((pConfig->flags & MA_SOUND_FLAG_LOOPING) != 0)); return MA_SUCCESS; } @@ -75899,6 +76701,9 @@ MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_s it and can avoid accessing the sound from within the notification. */ flags = pConfig->flags | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT; + if (pConfig->isLooping) { + flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; + } pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); if (pSound->pResourceManagerDataSource == NULL) { @@ -75927,7 +76732,7 @@ MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_s resourceManagerDataSourceConfig.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; resourceManagerDataSourceConfig.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; resourceManagerDataSourceConfig.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; - resourceManagerDataSourceConfig.isLooping = pConfig->isLooping; + resourceManagerDataSourceConfig.isLooping = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0; result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource); if (result != MA_SUCCESS) { @@ -76079,7 +76884,7 @@ MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pCo { /* Getting here means we're not loading from a file. We may be loading from an already-initialized - data source, or none at all. If we aren't specifying any data source, we'll be initializing the + data source, or none at all. If we aren't specifying any data source, we'll be initializing the equivalent to a group. ma_data_source_init_from_data_source_internal() will deal with this for us, so no special treatment required here. */ @@ -76799,6 +77604,27 @@ MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameInd return MA_SUCCESS; } +MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds) +{ + ma_uint64 frameIndex; + ma_uint32 sampleRate; + ma_result result; + + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; + } + + /* We need PCM frames. We need to convert first */ + frameIndex = (ma_uint64)(seekPointInSeconds * sampleRate); + + return ma_sound_seek_to_pcm_frame(pSound, frameIndex); +} + MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { if (pSound == NULL) { @@ -77245,7 +78071,7 @@ code below please report the bug to the respective repository for the relevant p *************************************************************************************************************************************************************** **************************************************************************************************************************************************************/ #if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) -#if !defined(MA_DR_WAV_IMPLEMENTATION) && !defined(MA_DR_WAV_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ +#if !defined(MA_DR_WAV_IMPLEMENTATION) /* dr_wav_c begin */ #ifndef ma_dr_wav_c #define ma_dr_wav_c @@ -78567,7 +79393,6 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_p } if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { if (ma_dr_wav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) { - return MA_FALSE; } } else if (pWav->container == ma_dr_wav_container_rf64) { if (ma_dr_wav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) { @@ -78836,7 +79661,9 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_p compressionFormat = MA_DR_WAVE_FORMAT_MULAW; } else if (ma_dr_wav_fourcc_equal(type, "ima4")) { compressionFormat = MA_DR_WAVE_FORMAT_DVI_ADPCM; - sampleSizeInBits = 4; + sampleSizeInBits = 4; + (void)compressionFormat; + (void)sampleSizeInBits; return MA_FALSE; } else { return MA_FALSE; @@ -78894,9 +79721,7 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_p } } if (isProcessingMetadata) { - ma_uint64 metadataBytesRead; - metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); - MA_DR_WAV_ASSERT(metadataBytesRead <= header.sizeInBytes); + ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { break; } @@ -80344,6 +81169,12 @@ MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToW MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { ma_uint64 totalFramesRead = 0; + static ma_int32 adaptationTable[] = { + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 + }; + static ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; + static ma_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; MA_DR_WAV_ASSERT(pWav != NULL); MA_DR_WAV_ASSERT(framesToRead > 0); while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { @@ -80362,6 +81193,9 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_ pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0]; pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1]; pWav->msadpcm.cachedFrameCount = 2; + if (pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff1Table)) { + return totalFramesRead; + } } else { ma_uint8 header[14]; if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { @@ -80381,6 +81215,9 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_ pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1]; pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1]; pWav->msadpcm.cachedFrameCount = 2; + if (pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= ma_dr_wav_countof(coeff2Table)) { + return totalFramesRead; + } } } while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { @@ -80403,12 +81240,6 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_ if (pWav->msadpcm.bytesRemainingInBlock == 0) { continue; } else { - static ma_int32 adaptationTable[] = { - 230, 230, 230, 230, 307, 409, 512, 614, - 768, 614, 512, 409, 307, 230, 230, 230 - }; - static ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; - static ma_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; ma_uint8 nibbles; ma_int32 nibble0; ma_int32 nibble1; @@ -81659,7 +82490,7 @@ MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sample return; } for (i = 0; i < sampleCount; ++i) { - *pOut++ = (ma_int32)(2147483648.0 * pIn[i]); + *pOut++ = (ma_int32)(2147483648.0f * pIn[i]); } } MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount) @@ -82073,7 +82904,7 @@ MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b) #endif /* MA_NO_WAV */ #if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) -#if !defined(MA_DR_FLAC_IMPLEMENTATION) && !defined(MA_DR_FLAC_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ +#if !defined(MA_DR_FLAC_IMPLEMENTATION) /* dr_flac_c begin */ #ifndef ma_dr_flac_c #define ma_dr_flac_c @@ -85105,6 +85936,7 @@ static ma_bool32 ma_dr_flac__read_subframe_header(ma_dr_flac_bs* bs, ma_dr_flac_ if ((header & 0x80) != 0) { return MA_FALSE; } + pSubframe->lpcOrder = 0; type = (header & 0x7E) >> 1; if (type == 0) { pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_CONSTANT; @@ -85162,6 +85994,9 @@ static ma_bool32 ma_dr_flac__decode_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame } subframeBitsPerSample -= pSubframe->wastedBitsPerSample; pSubframe->pSamplesS32 = pDecodedSamplesOut; + if (frame->header.blockSizeInPCMFrames < pSubframe->lpcOrder) { + return MA_FALSE; + } switch (pSubframe->subframeType) { case MA_DR_FLAC_SUBFRAME_CONSTANT: @@ -89818,7 +90653,7 @@ MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterat #endif /* MA_NO_FLAC */ #if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) -#if !defined(MA_DR_MP3_IMPLEMENTATION) && !defined(MA_DR_MP3_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ +#if !defined(MA_DR_MP3_IMPLEMENTATION) /* dr_mp3_c begin */ #ifndef ma_dr_mp3_c #define ma_dr_mp3_c @@ -89879,7 +90714,7 @@ MA_API const char* ma_dr_mp3_version_string(void) #define MA_DR_MP3_MIN(a, b) ((a) > (b) ? (b) : (a)) #define MA_DR_MP3_MAX(a, b) ((a) < (b) ? (b) : (a)) #if !defined(MA_DR_MP3_NO_SIMD) -#if !defined(MA_DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64)) +#if !defined(MA_DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) #define MA_DR_MP3_ONLY_SIMD #endif #if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))) @@ -89952,7 +90787,7 @@ end: return g_have_simd - 1; #endif } -#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) +#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) #include #define MA_DR_MP3_HAVE_SSE 0 #define MA_DR_MP3_HAVE_SIMD 1 @@ -89981,7 +90816,7 @@ static int ma_dr_mp3_have_simd(void) #else #define MA_DR_MP3_HAVE_SIMD 0 #endif -#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(__ARM_ARCH_6M__) +#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(_M_ARM64EC) && !defined(__ARM_ARCH_6M__) #define MA_DR_MP3_HAVE_ARMV6 1 static __inline__ __attribute__((always_inline)) ma_int32 ma_dr_mp3_clip_int16_arm(ma_int32 a) { @@ -91147,8 +91982,8 @@ static ma_int16 ma_dr_mp3d_scale_pcm(float sample) s32 -= (s32 < 0); s = (ma_int16)ma_dr_mp3_clip_int16_arm(s32); #else - if (sample >= 32766.5) return (ma_int16) 32767; - if (sample <= -32767.5) return (ma_int16)-32768; + if (sample >= 32766.5f) return (ma_int16) 32767; + if (sample <= -32767.5f) return (ma_int16)-32768; s = (ma_int16)(sample + .5f); s -= (s < 0); #endif @@ -91534,9 +92369,9 @@ MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_s for(; i < num_samples; i++) { float sample = in[i] * 32768.0f; - if (sample >= 32766.5) + if (sample >= 32766.5f) out[i] = (ma_int16) 32767; - else if (sample <= -32767.5) + else if (sample <= -32767.5f) out[i] = (ma_int16)-32768; else { @@ -92614,7 +93449,7 @@ For more information, please refer to =============================================================================== ALTERNATIVE 2 - MIT No Attribution =============================================================================== -Copyright 2023 David Reid +Copyright 2025 David Reid 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 From 59338c2c29cdb4ebd977c39a203bb7613e9f56e8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 2 Jun 2025 16:37:46 +0000 Subject: [PATCH 031/242] Update raylib_api.* by CI --- parser/output/raylib_api.json | 4 ++-- parser/output/raylib_api.lua | 4 ++-- parser/output/raylib_api.txt | 4 ++-- parser/output/raylib_api.xml | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index e796478e4..e95a14eac 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -6010,11 +6010,11 @@ }, { "type": "Color", - "name": "topRight" + "name": "bottomRight" }, { "type": "Color", - "name": "bottomRight" + "name": "topRight" } ] }, diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index a2897a226..a7a2ff688 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -4976,8 +4976,8 @@ return { {type = "Rectangle", name = "rec"}, {type = "Color", name = "topLeft"}, {type = "Color", name = "bottomLeft"}, - {type = "Color", name = "topRight"}, - {type = "Color", name = "bottomRight"} + {type = "Color", name = "bottomRight"}, + {type = "Color", name = "topRight"} } }, { diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index 4de3e267a..e9a8a1315 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -2356,8 +2356,8 @@ Function 239: DrawRectangleGradientEx() (5 input parameters) Param[1]: rec (type: Rectangle) Param[2]: topLeft (type: Color) Param[3]: bottomLeft (type: Color) - Param[4]: topRight (type: Color) - Param[5]: bottomRight (type: Color) + Param[4]: bottomRight (type: Color) + Param[5]: topRight (type: Color) Function 240: DrawRectangleLines() (5 input parameters) Name: DrawRectangleLines Return type: void diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index e79e55f4f..9f4cfaa86 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -1488,8 +1488,8 @@ - + From 3f228f4594fac91958aed645bb5b1dec67e54910 Mon Sep 17 00:00:00 2001 From: Hamza RAHAL Date: Mon, 2 Jun 2025 22:52:24 +0200 Subject: [PATCH 032/242] [examples] : adding new fancy clock --- examples/Makefile | 3 +- examples/Makefile.Web | 3 +- examples/README.md | 210 +++++++++++------------ examples/shapes/shapes_digital_clock.c | 191 +++++++++++++++++++++ examples/shapes/shapes_digital_clock.png | Bin 0 -> 3651 bytes 5 files changed, 300 insertions(+), 107 deletions(-) create mode 100644 examples/shapes/shapes_digital_clock.c create mode 100644 examples/shapes/shapes_digital_clock.png diff --git a/examples/Makefile b/examples/Makefile index 548e869c2..32a3a75ab 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -547,7 +547,8 @@ SHAPES = \ shapes/shapes_rectangle_advanced \ shapes/shapes_rectangle_scaling \ shapes/shapes_splines_drawing \ - shapes/shapes_top_down_lights + shapes/shapes_top_down_lights \ + shapes/shapes_digital_clock TEXTURES = \ textures/textures_background_scrolling \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 04fbe927a..35ae70a18 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -429,7 +429,8 @@ SHAPES = \ shapes/shapes_rectangle_advanced \ shapes/shapes_rectangle_scaling \ shapes/shapes_splines_drawing \ - shapes/shapes_top_down_lights + shapes/shapes_top_down_lights \ + shapes/shapes_digital_clock TEXTURES = \ textures/textures_background_scrolling \ diff --git a/examples/README.md b/examples/README.md index 9982c2001..7553ad113 100644 --- a/examples/README.md +++ b/examples/README.md @@ -85,39 +85,39 @@ Examples using raylib shapes drawing functionality, provided by raylib [shapes]( | 51 | [shapes_top_down_lights](shapes/shapes_top_down_lights.c) | shapes_top_down_lights | ⭐️⭐️⭐️⭐️ | 4.2 | 4.2 | [Jeffery Myers](https://github.com/JeffM2501) | | 52 | [shapes_rectangle_advanced](shapes/shapes_rectangle_advanced.c) | shapes_rectangle_advanced | ⭐️⭐️⭐️⭐️ | 5.5 | 5.5 | [Everton Jr.](https://github.com/evertonse) | | 53 | [shapes_splines_drawing](shapes/shapes_splines_drawing.c) | shapes_splines_drawing | ⭐️⭐️⭐️☆ | 5.0 | 5.0 | [Ray](https://github.com/raysan5) | - +| 54 | [shapes_digital_clock](shapes/shapes_digital_clock.c) | shapes_digital_clock | ⭐️⭐️☆☆ | 5.5 | 5.5 | [Hamza RAHAL](https://github.com/rhmz-rhl) | ### category: textures Examples using raylib textures functionality, including image/textures loading/generation and drawing, provided by raylib [textures](../src/textures.c) modul | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 54 | [textures_logo_raylib](textures/textures_logo_raylib.c) | textures_logo_raylib | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 55 | [textures_srcrec_dstrec](textures/textures_srcrec_dstrec.c) | textures_srcrec_dstrec | ⭐️⭐️⭐️☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 56 | [textures_image_drawing](textures/textures_image_drawing.c) | textures_image_drawing | ⭐️⭐️☆☆ | 1.4 | 1.4 | [Ray](https://github.com/raysan5) | -| 57 | [textures_image_generation](textures/textures_image_generation.c) | textures_image_generation | ⭐️⭐️☆☆ | 1.8 | 1.8 | [Wilhem Barbier](https://github.com/nounoursheureux) | -| 58 | [textures_image_loading](textures/textures_image_loading.c) | textures_image_loading | ⭐️☆☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 59 | [textures_image_processing](textures/textures_image_processing.c) | textures_image_processing | ⭐️⭐️⭐️☆ | 1.4 | 3.5 | [Ray](https://github.com/raysan5) | -| 60 | [textures_image_text](textures/textures_image_text.c) | textures_image_text | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) | -| 61 | [textures_to_image](textures/textures_to_image.c) | textures_to_image | ⭐️☆☆☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | -| 62 | [textures_raw_data](textures/textures_raw_data.c) | textures_raw_data | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | -| 63 | [textures_particles_blending](textures/textures_particles_blending.c) | textures_particles_blending | ⭐️☆☆☆ | 1.7 | 3.5 | [Ray](https://github.com/raysan5) | -| 64 | [textures_npatch_drawing](textures/textures_npatch_drawing.c) | textures_npatch_drawing | ⭐️⭐️⭐️☆ | 2.0 | 2.5 | [Jorge A. Gomes](https://github.com/overdev) | -| 65 | [textures_background_scrolling](textures/textures_background_scrolling.c) | textures_background_scrolling | ⭐️☆☆☆ | 2.0 | 2.5 | [Ray](https://github.com/raysan5) | -| 66 | [textures_sprite_anim](textures/textures_sprite_anim.c) | textures_sprite_anim | ⭐️⭐️☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 67 | [textures_sprite_button](textures/textures_sprite_button.c) | textures_sprite_button | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | -| 68 | [textures_sprite_explosion](textures/textures_sprite_explosion.c) | textures_sprite_explosion | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 69 | [textures_bunnymark](textures/textures_bunnymark.c) | textures_bunnymark | ⭐️⭐️⭐️☆ | 1.6 | 2.5 | [Ray](https://github.com/raysan5) | -| 70 | [textures_mouse_painting](textures/textures_mouse_painting.c) | textures_mouse_painting | ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Chris Dill](https://github.com/MysteriousSpace) | -| 71 | [textures_blend_modes](textures/textures_blend_modes.c) | textures_blend_modes | ⭐️☆☆☆ | 3.5 | 3.5 | [Karlo Licudine](https://github.com/accidentalrebel) | -| 72 | [textures_draw_tiled](textures/textures_draw_tiled.c) | textures_draw_tiled | ⭐️⭐️⭐️☆ | 3.0 | 4.2 | [Vlad Adrian](https://github.com/demizdor) | -| 73 | [textures_polygon](textures/textures_polygon.c) | textures_polygon | ⭐️☆☆☆ | 3.7 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | -| 74 | [textures_fog_of_war](textures/textures_fog_of_war.c) | textures_fog_of_war | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) | -| 75 | [textures_gif_player](textures/textures_gif_player.c) | textures_gif_player | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) | -| 76 | [textures_image_kernel](textures/textures_image_kernel.c) | textures_image_kernel | ⭐️⭐️⭐️⭐️ | 1.3 | 1.3 | [Karim Salem](https://github.com/kimo-s) | -| 77 | [textures_image_channel](textures/textures_image_channel.c) | textures_image_channel | ⭐️⭐️☆☆ | 5.1-dev | 5.1-dev | [Bruno Cabral](https://github.com/brccabral) | -| 78 | [textures_image_rotate](textures/textures_image_rotate.c) | textures_image_rotate | ⭐️⭐️☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 79 | [textures_textured_curve](textures/textures_textured_curve.c) | textures_textured_curve | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Jeffery Myers](https://github.com/JeffM2501) | +| 55 | [textures_logo_raylib](textures/textures_logo_raylib.c) | textures_logo_raylib | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | +| 56 | [textures_srcrec_dstrec](textures/textures_srcrec_dstrec.c) | textures_srcrec_dstrec | ⭐️⭐️⭐️☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | +| 57 | [textures_image_drawing](textures/textures_image_drawing.c) | textures_image_drawing | ⭐️⭐️☆☆ | 1.4 | 1.4 | [Ray](https://github.com/raysan5) | +| 58 | [textures_image_generation](textures/textures_image_generation.c) | textures_image_generation | ⭐️⭐️☆☆ | 1.8 | 1.8 | [Wilhem Barbier](https://github.com/nounoursheureux) | +| 59 | [textures_image_loading](textures/textures_image_loading.c) | textures_image_loading | ⭐️☆☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | +| 60 | [textures_image_processing](textures/textures_image_processing.c) | textures_image_processing | ⭐️⭐️⭐️☆ | 1.4 | 3.5 | [Ray](https://github.com/raysan5) | +| 61 | [textures_image_text](textures/textures_image_text.c) | textures_image_text | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) | +| 62 | [textures_to_image](textures/textures_to_image.c) | textures_to_image | ⭐️☆☆☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | +| 63 | [textures_raw_data](textures/textures_raw_data.c) | textures_raw_data | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | +| 64 | [textures_particles_blending](textures/textures_particles_blending.c) | textures_particles_blending | ⭐️☆☆☆ | 1.7 | 3.5 | [Ray](https://github.com/raysan5) | +| 65 | [textures_npatch_drawing](textures/textures_npatch_drawing.c) | textures_npatch_drawing | ⭐️⭐️⭐️☆ | 2.0 | 2.5 | [Jorge A. Gomes](https://github.com/overdev) | +| 66 | [textures_background_scrolling](textures/textures_background_scrolling.c) | textures_background_scrolling | ⭐️☆☆☆ | 2.0 | 2.5 | [Ray](https://github.com/raysan5) | +| 67 | [textures_sprite_anim](textures/textures_sprite_anim.c) | textures_sprite_anim | ⭐️⭐️☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | +| 68 | [textures_sprite_button](textures/textures_sprite_button.c) | textures_sprite_button | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | +| 69 | [textures_sprite_explosion](textures/textures_sprite_explosion.c) | textures_sprite_explosion | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 70 | [textures_bunnymark](textures/textures_bunnymark.c) | textures_bunnymark | ⭐️⭐️⭐️☆ | 1.6 | 2.5 | [Ray](https://github.com/raysan5) | +| 71 | [textures_mouse_painting](textures/textures_mouse_painting.c) | textures_mouse_painting | ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Chris Dill](https://github.com/MysteriousSpace) | +| 72 | [textures_blend_modes](textures/textures_blend_modes.c) | textures_blend_modes | ⭐️☆☆☆ | 3.5 | 3.5 | [Karlo Licudine](https://github.com/accidentalrebel) | +| 73 | [textures_draw_tiled](textures/textures_draw_tiled.c) | textures_draw_tiled | ⭐️⭐️⭐️☆ | 3.0 | 4.2 | [Vlad Adrian](https://github.com/demizdor) | +| 74 | [textures_polygon](textures/textures_polygon.c) | textures_polygon | ⭐️☆☆☆ | 3.7 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | +| 75 | [textures_fog_of_war](textures/textures_fog_of_war.c) | textures_fog_of_war | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) | +| 76 | [textures_gif_player](textures/textures_gif_player.c) | textures_gif_player | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) | +| 77 | [textures_image_kernel](textures/textures_image_kernel.c) | textures_image_kernel | ⭐️⭐️⭐️⭐️ | 1.3 | 1.3 | [Karim Salem](https://github.com/kimo-s) | +| 78 | [textures_image_channel](textures/textures_image_channel.c) | textures_image_channel | ⭐️⭐️☆☆ | 5.1-dev | 5.1-dev | [Bruno Cabral](https://github.com/brccabral) | +| 79 | [textures_image_rotate](textures/textures_image_rotate.c) | textures_image_rotate | ⭐️⭐️☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | +| 80 | [textures_textured_curve](textures/textures_textured_curve.c) | textures_textured_curve | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Jeffery Myers](https://github.com/JeffM2501) | ### category: text @@ -125,18 +125,18 @@ Examples using raylib text functionality, including sprite fonts loading/generat | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 80 | [text_raylib_fonts](text/text_raylib_fonts.c) | text_raylib_fonts | ⭐️☆☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) | -| 81 | [text_font_spritefont](text/text_font_spritefont.c) | text_font_spritefont | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 82 | [text_font_filters](text/text_font_filters.c) | text_font_filters | ⭐️⭐️☆☆ | 1.3 | 4.2 | [Ray](https://github.com/raysan5) | -| 83 | [text_font_loading](text/text_font_loading.c) | text_font_loading | ⭐️☆☆☆ | 1.4 | 3.0 | [Ray](https://github.com/raysan5) | -| 84 | [text_font_sdf](text/text_font_sdf.c) | text_font_sdf | ⭐️⭐️⭐️☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | -| 85 | [text_format_text](text/text_format_text.c) | text_format_text | ⭐️☆☆☆ | 1.1 | 3.0 | [Ray](https://github.com/raysan5) | -| 86 | [text_input_box](text/text_input_box.c) | text_input_box | ⭐️⭐️☆☆ | 1.7 | 3.5 | [Ray](https://github.com/raysan5) | -| 87 | [text_writing_anim](text/text_writing_anim.c) | text_writing_anim | ⭐️⭐️☆☆ | 1.4 | 1.4 | [Ray](https://github.com/raysan5) | -| 88 | [text_rectangle_bounds](text/text_rectangle_bounds.c) | text_rectangle_bounds | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) | -| 89 | [text_unicode](text/text_unicode.c) | text_unicode | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) | -| 90 | [text_draw_3d](text/text_draw_3d.c) | text_draw_3d | ⭐️⭐️⭐️⭐️ | 3.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) | -| 91 | [text_codepoints_loading](text/text_codepoints_loading.c) | text_codepoints_loading | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) | +| 81 | [text_raylib_fonts](text/text_raylib_fonts.c) | text_raylib_fonts | ⭐️☆☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) | +| 82 | [text_font_spritefont](text/text_font_spritefont.c) | text_font_spritefont | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | +| 83 | [text_font_filters](text/text_font_filters.c) | text_font_filters | ⭐️⭐️☆☆ | 1.3 | 4.2 | [Ray](https://github.com/raysan5) | +| 84 | [text_font_loading](text/text_font_loading.c) | text_font_loading | ⭐️☆☆☆ | 1.4 | 3.0 | [Ray](https://github.com/raysan5) | +| 85 | [text_font_sdf](text/text_font_sdf.c) | text_font_sdf | ⭐️⭐️⭐️☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | +| 86 | [text_format_text](text/text_format_text.c) | text_format_text | ⭐️☆☆☆ | 1.1 | 3.0 | [Ray](https://github.com/raysan5) | +| 87 | [text_input_box](text/text_input_box.c) | text_input_box | ⭐️⭐️☆☆ | 1.7 | 3.5 | [Ray](https://github.com/raysan5) | +| 88 | [text_writing_anim](text/text_writing_anim.c) | text_writing_anim | ⭐️⭐️☆☆ | 1.4 | 1.4 | [Ray](https://github.com/raysan5) | +| 89 | [text_rectangle_bounds](text/text_rectangle_bounds.c) | text_rectangle_bounds | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) | +| 90 | [text_unicode](text/text_unicode.c) | text_unicode | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) | +| 91 | [text_draw_3d](text/text_draw_3d.c) | text_draw_3d | ⭐️⭐️⭐️⭐️ | 3.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) | +| 92 | [text_codepoints_loading](text/text_codepoints_loading.c) | text_codepoints_loading | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) | ### category: models @@ -144,29 +144,29 @@ Examples using raylib models functionality, including models loading/generation | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 92 | [models_animation](models/models_animation.c) | models_animation | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Culacant](https://github.com/culacant) | -| 93 | [models_billboard](models/models_billboard.c) | models_billboard | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | -| 94 | [models_box_collisions](models/models_box_collisions.c) | models_box_collisions | ⭐️☆☆☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | -| 95 | [models_cubicmap](models/models_cubicmap.c) | models_cubicmap | ⭐️⭐️☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) | -| 96 | [models_first_person_maze](models/models_first_person_maze.c) | models_first_person_maze | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 97 | [models_geometric_shapes](models/models_geometric_shapes.c) | models_geometric_shapes | ⭐️☆☆☆ | 1.0 | 3.5 | [Ray](https://github.com/raysan5) | -| 98 | [models_mesh_generation](models/models_mesh_generation.c) | models_mesh_generation | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) | -| 99 | [models_mesh_picking](models/models_mesh_picking.c) | models_mesh_picking | ⭐️⭐️⭐️☆ | 1.7 | 4.0 | [Joel Davis](https://github.com/joeld42) | -| 100 | [models_loading](models/models_loading.c) | models_loading | ⭐️☆☆☆ | 2.0 | 4.2 | [Ray](https://github.com/raysan5) | -| 101 | [models_loading_gltf](models/models_loading_gltf.c) | models_loading_gltf | ⭐️☆☆☆ | 3.7 | 4.2 | [Ray](https://github.com/raysan5) | -| 102 | [models_loading_vox](models/models_loading_vox.c) | models_loading_vox | ⭐️☆☆☆ | 4.0 | 4.0 | [Johann Nadalutti](https://github.com/procfxgen) | -| 103 | [models_loading_m3d](models/models_loading_m3d.c) | models_loading_m3d | ⭐️⭐️☆☆ | 4.5 | 4.5 | [bzt](https://bztsrc.gitlab.io/model3d) | -| 104 | [models_orthographic_projection](models/models_orthographic_projection.c) | models_orthographic_projection | ⭐️☆☆☆ | 2.0 | 3.7 | [Max Danielsson](https://github.com/autious) | -| 105 | [models_point_rendering](models/models_point_rendering.c) | models_point_rendering | ⭐️⭐️⭐️☆ | 5.0 | 5.0 | [Reese Gallagher](https://github.com/satchelfrost) | -| 106 | [models_rlgl_solar_system](models/models_rlgl_solar_system.c) | models_rlgl_solar_system | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Ray](https://github.com/raysan5) | -| 107 | [models_yaw_pitch_roll](models/models_yaw_pitch_roll.c) | models_yaw_pitch_roll | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Berni](https://github.com/Berni8k) | -| 108 | [models_waving_cubes](models/models_waving_cubes.c) | models_waving_cubes | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Codecat](https://github.com/codecat) | -| 109 | [models_heightmap](models/models_heightmap.c) | models_heightmap | ⭐️☆☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) | -| 110 | [models_skybox](models/models_skybox.c) | models_skybox | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) | -| 111 | [models_draw_cube_texture](models/models_draw_cube_texture.c) | models_draw_cube_texture | ⭐️⭐️☆☆ | 4.5 | 4.5 | [Ray](https://github.com/raysan5) | -| 112 | [models_gpu_skinning](models/models_gpu_skinning.c) | models_gpu_skinning | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Daniel Holden](https://github.com/orangeduck) | -| 113 | [models_bone_socket](models/models_bone_socket.c) | models_bone_socket | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [iP](https://github.com/ipzaur) | -| 114 | [models_tesseract_view](models/models_tesseract_view.c) | models_tesseract_view | ⭐️⭐️☆☆ | 5.6-dev | 5.6-dev | [Timothy van der Valk](https://github.com/arceryz) | +| 93 | [models_animation](models/models_animation.c) | models_animation | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Culacant](https://github.com/culacant) | +| 94 | [models_billboard](models/models_billboard.c) | models_billboard | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | +| 95 | [models_box_collisions](models/models_box_collisions.c) | models_box_collisions | ⭐️☆☆☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | +| 96 | [models_cubicmap](models/models_cubicmap.c) | models_cubicmap | ⭐️⭐️☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) | +| 97 | [models_first_person_maze](models/models_first_person_maze.c) | models_first_person_maze | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 98 | [models_geometric_shapes](models/models_geometric_shapes.c) | models_geometric_shapes | ⭐️☆☆☆ | 1.0 | 3.5 | [Ray](https://github.com/raysan5) | +| 99 | [models_mesh_generation](models/models_mesh_generation.c) | models_mesh_generation | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) | +| 100 | [models_mesh_picking](models/models_mesh_picking.c) | models_mesh_picking | ⭐️⭐️⭐️☆ | 1.7 | 4.0 | [Joel Davis](https://github.com/joeld42) | +| 101 | [models_loading](models/models_loading.c) | models_loading | ⭐️☆☆☆ | 2.0 | 4.2 | [Ray](https://github.com/raysan5) | +| 102 | [models_loading_gltf](models/models_loading_gltf.c) | models_loading_gltf | ⭐️☆☆☆ | 3.7 | 4.2 | [Ray](https://github.com/raysan5) | +| 103 | [models_loading_vox](models/models_loading_vox.c) | models_loading_vox | ⭐️☆☆☆ | 4.0 | 4.0 | [Johann Nadalutti](https://github.com/procfxgen) | +| 104 | [models_loading_m3d](models/models_loading_m3d.c) | models_loading_m3d | ⭐️⭐️☆☆ | 4.5 | 4.5 | [bzt](https://bztsrc.gitlab.io/model3d) | +| 105 | [models_orthographic_projection](models/models_orthographic_projection.c) | models_orthographic_projection | ⭐️☆☆☆ | 2.0 | 3.7 | [Max Danielsson](https://github.com/autious) | +| 106 | [models_point_rendering](models/models_point_rendering.c) | models_point_rendering | ⭐️⭐️⭐️☆ | 5.0 | 5.0 | [Reese Gallagher](https://github.com/satchelfrost) | +| 107 | [models_rlgl_solar_system](models/models_rlgl_solar_system.c) | models_rlgl_solar_system | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Ray](https://github.com/raysan5) | +| 108 | [models_yaw_pitch_roll](models/models_yaw_pitch_roll.c) | models_yaw_pitch_roll | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Berni](https://github.com/Berni8k) | +| 109 | [models_waving_cubes](models/models_waving_cubes.c) | models_waving_cubes | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Codecat](https://github.com/codecat) | +| 110 | [models_heightmap](models/models_heightmap.c) | models_heightmap | ⭐️☆☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) | +| 111 | [models_skybox](models/models_skybox.c) | models_skybox | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) | +| 112 | [models_draw_cube_texture](models/models_draw_cube_texture.c) | models_draw_cube_texture | ⭐️⭐️☆☆ | 4.5 | 4.5 | [Ray](https://github.com/raysan5) | +| 113 | [models_gpu_skinning](models/models_gpu_skinning.c) | models_gpu_skinning | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Daniel Holden](https://github.com/orangeduck) | +| 114 | [models_bone_socket](models/models_bone_socket.c) | models_bone_socket | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [iP](https://github.com/ipzaur) | +| 115 | [models_tesseract_view](models/models_tesseract_view.c) | models_tesseract_view | ⭐️⭐️☆☆ | 5.6-dev | 5.6-dev | [Timothy van der Valk](https://github.com/arceryz) | ### category: shaders @@ -174,34 +174,34 @@ Examples using raylib shaders functionality, including shaders loading, paramete | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 115 | [shaders_basic_lighting](shaders/shaders_basic_lighting.c) | shaders_basic_lighting | ⭐️⭐️⭐️⭐️ | 3.0 | 4.2 | [Chris Camacho](https://github.com/chriscamacho) | -| 116 | [shaders_model_shader](shaders/shaders_model_shader.c) | shaders_model_shader | ⭐️⭐️☆☆ | 1.3 | 3.7 | [Ray](https://github.com/raysan5) | -| 117 | [shaders_shapes_textures](shaders/shaders_shapes_textures.c) | shaders_shapes_textures | ⭐️⭐️☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) | -| 118 | [shaders_custom_uniform](shaders/shaders_custom_uniform.c) | shaders_custom_uniform | ⭐️⭐️☆☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | -| 119 | [shaders_postprocessing](shaders/shaders_postprocessing.c) | shaders_postprocessing | ⭐️⭐️⭐️☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | -| 120 | [shaders_palette_switch](shaders/shaders_palette_switch.c) | shaders_palette_switch | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Marco Lizza](https://github.com/MarcoLizza) | -| 121 | [shaders_raymarching](shaders/shaders_raymarching.c) | shaders_raymarching | ⭐️⭐️⭐️⭐️ | 2.0 | 4.2 | [Ray](https://github.com/raysan5) | -| 122 | [shaders_texture_drawing](shaders/shaders_texture_drawing.c) | shaders_texture_drawing | ⭐️⭐️☆☆ | 2.0 | 3.7 | [Michał Ciesielski](https://github.com/ciessielski) | -| 123 | [shaders_texture_outline](shaders/shaders_texture_outline.c) | shaders_texture_outline | ⭐️⭐️⭐️☆ | 4.0 | 4.0 | [Samuel Skiff](https://github.com/GoldenThumbs) | -| 124 | [shaders_texture_waves](shaders/shaders_texture_waves.c) | shaders_texture_waves | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Anata](https://github.com/anatagawa) | -| 125 | [shaders_julia_set](shaders/shaders_julia_set.c) | shaders_julia_set | ⭐️⭐️⭐️☆ | 2.5 | 4.0 | [Josh Colclough](https://github.com/joshcol9232) | -| 126 | [shaders_eratosthenes](shaders/shaders_eratosthenes.c) | shaders_eratosthenes | ⭐️⭐️⭐️☆ | 2.5 | 4.0 | [ProfJski](https://github.com/ProfJski) | -| 127 | [shaders_fog](shaders/shaders_fog.c) | shaders_fog | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | -| 128 | [shaders_simple_mask](shaders/shaders_simple_mask.c) | shaders_simple_mask | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | -| 129 | [shaders_hot_reloading](shaders/shaders_hot_reloading.c) | shaders_hot_reloading | ⭐️⭐️⭐️☆ | 3.0 | 3.5 | [Ray](https://github.com/raysan5) | -| 130 | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | shaders_mesh_instancing | ⭐️⭐️⭐️⭐️ | 3.7 | 4.2 | [seanpringle](https://github.com/seanpringle) | -| 131 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | shaders_multi_sample2d | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 132 | [shaders_spotlight](shaders/shaders_spotlight.c) | shaders_spotlight | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | -| 133 | [shaders_deferred_render](shaders/shaders_deferred_render.c) | shaders_deferred_render | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [Justin Andreas Lacoste](https://github.com/27justin) | -| 134 | [shaders_hybrid_render](shaders/shaders_hybrid_render.c) | shaders_hybrid_render | ⭐️⭐️⭐️⭐️ | 4.2 | 4.2 | [Buğra Alptekin Sarı](https://github.com/BugraAlptekinSari) | -| 135 | [shaders_texture_tiling](shaders/shaders_texture_tiling.c) | shaders_texture_tiling | ⭐️⭐️☆☆ | 4.5 | 4.5 | [Luis Almeida](https://github.com/luis605) | -| 136 | [shaders_shadowmap](shaders/shaders_shadowmap.c) | shaders_shadowmap | ⭐️⭐️⭐️⭐️ | 5.0 | 5.0 | [TheManTheMythTheGameDev](https://github.com/TheManTheMythTheGameDev) | -| 137 | [shaders_vertex_displacement](shaders/shaders_vertex_displacement.c) | shaders_vertex_displacement | ⭐️⭐️⭐️☆ | 5.0 | 4.5 | [Alex ZH](https://github.com/ZzzhHe) | -| 138 | [shaders_write_depth](shaders/shaders_write_depth.c) | shaders_write_depth | ⭐️⭐️☆☆ | 4.2 | 4.2 | [Buğra Alptekin Sarı](https://github.com/BugraAlptekinSari) | -| 139 | [shaders_basic_pbr](shaders/shaders_basic_pbr.c) | shaders_basic_pbr | ⭐️⭐️⭐️⭐️ | 5.0 | 5.1-dev | [Afan OLOVCIC](https://github.com/_DevDad) | -| 140 | [shaders_lightmap](shaders/shaders_lightmap.c) | shaders_lightmap | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Jussi Viitala](https://github.com/nullstare) | -| 141 | [shaders_rounded_rectangle](shaders/shaders_rounded_rectangle.c) | shaders_rounded_rectangle | ⭐️⭐️⭐️☆ | 5.5 | 5.5 | [Anstro Pleuton](https://github.com/anstropleuton) | -| 142 | [shaders_view_depth](shaders/shaders_view_depth.c) | shaders_view_depth | ⭐️⭐️⭐️☆ | 5.6-dev | 5.6-dev | [Luís Almeida](https://github.com/luis605) | +| 116 | [shaders_basic_lighting](shaders/shaders_basic_lighting.c) | shaders_basic_lighting | ⭐️⭐️⭐️⭐️ | 3.0 | 4.2 | [Chris Camacho](https://github.com/chriscamacho) | +| 117 | [shaders_model_shader](shaders/shaders_model_shader.c) | shaders_model_shader | ⭐️⭐️☆☆ | 1.3 | 3.7 | [Ray](https://github.com/raysan5) | +| 118 | [shaders_shapes_textures](shaders/shaders_shapes_textures.c) | shaders_shapes_textures | ⭐️⭐️☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) | +| 119 | [shaders_custom_uniform](shaders/shaders_custom_uniform.c) | shaders_custom_uniform | ⭐️⭐️☆☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | +| 120 | [shaders_postprocessing](shaders/shaders_postprocessing.c) | shaders_postprocessing | ⭐️⭐️⭐️☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | +| 121 | [shaders_palette_switch](shaders/shaders_palette_switch.c) | shaders_palette_switch | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Marco Lizza](https://github.com/MarcoLizza) | +| 122 | [shaders_raymarching](shaders/shaders_raymarching.c) | shaders_raymarching | ⭐️⭐️⭐️⭐️ | 2.0 | 4.2 | [Ray](https://github.com/raysan5) | +| 123 | [shaders_texture_drawing](shaders/shaders_texture_drawing.c) | shaders_texture_drawing | ⭐️⭐️☆☆ | 2.0 | 3.7 | [Michał Ciesielski](https://github.com/ciessielski) | +| 124 | [shaders_texture_outline](shaders/shaders_texture_outline.c) | shaders_texture_outline | ⭐️⭐️⭐️☆ | 4.0 | 4.0 | [Samuel Skiff](https://github.com/GoldenThumbs) | +| 125 | [shaders_texture_waves](shaders/shaders_texture_waves.c) | shaders_texture_waves | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Anata](https://github.com/anatagawa) | +| 126 | [shaders_julia_set](shaders/shaders_julia_set.c) | shaders_julia_set | ⭐️⭐️⭐️☆ | 2.5 | 4.0 | [Josh Colclough](https://github.com/joshcol9232) | +| 127 | [shaders_eratosthenes](shaders/shaders_eratosthenes.c) | shaders_eratosthenes | ⭐️⭐️⭐️☆ | 2.5 | 4.0 | [ProfJski](https://github.com/ProfJski) | +| 128 | [shaders_fog](shaders/shaders_fog.c) | shaders_fog | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | +| 129 | [shaders_simple_mask](shaders/shaders_simple_mask.c) | shaders_simple_mask | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | +| 130 | [shaders_hot_reloading](shaders/shaders_hot_reloading.c) | shaders_hot_reloading | ⭐️⭐️⭐️☆ | 3.0 | 3.5 | [Ray](https://github.com/raysan5) | +| 131 | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | shaders_mesh_instancing | ⭐️⭐️⭐️⭐️ | 3.7 | 4.2 | [seanpringle](https://github.com/seanpringle) | +| 132 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | shaders_multi_sample2d | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 133 | [shaders_spotlight](shaders/shaders_spotlight.c) | shaders_spotlight | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | +| 134 | [shaders_deferred_render](shaders/shaders_deferred_render.c) | shaders_deferred_render | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [Justin Andreas Lacoste](https://github.com/27justin) | +| 135 | [shaders_hybrid_render](shaders/shaders_hybrid_render.c) | shaders_hybrid_render | ⭐️⭐️⭐️⭐️ | 4.2 | 4.2 | [Buğra Alptekin Sarı](https://github.com/BugraAlptekinSari) | +| 136 | [shaders_texture_tiling](shaders/shaders_texture_tiling.c) | shaders_texture_tiling | ⭐️⭐️☆☆ | 4.5 | 4.5 | [Luis Almeida](https://github.com/luis605) | +| 137 | [shaders_shadowmap](shaders/shaders_shadowmap.c) | shaders_shadowmap | ⭐️⭐️⭐️⭐️ | 5.0 | 5.0 | [TheManTheMythTheGameDev](https://github.com/TheManTheMythTheGameDev) | +| 138 | [shaders_vertex_displacement](shaders/shaders_vertex_displacement.c) | shaders_vertex_displacement | ⭐️⭐️⭐️☆ | 5.0 | 4.5 | [Alex ZH](https://github.com/ZzzhHe) | +| 139 | [shaders_write_depth](shaders/shaders_write_depth.c) | shaders_write_depth | ⭐️⭐️☆☆ | 4.2 | 4.2 | [Buğra Alptekin Sarı](https://github.com/BugraAlptekinSari) | +| 140 | [shaders_basic_pbr](shaders/shaders_basic_pbr.c) | shaders_basic_pbr | ⭐️⭐️⭐️⭐️ | 5.0 | 5.1-dev | [Afan OLOVCIC](https://github.com/_DevDad) | +| 141 | [shaders_lightmap](shaders/shaders_lightmap.c) | shaders_lightmap | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Jussi Viitala](https://github.com/nullstare) | +| 142 | [shaders_rounded_rectangle](shaders/shaders_rounded_rectangle.c) | shaders_rounded_rectangle | ⭐️⭐️⭐️☆ | 5.5 | 5.5 | [Anstro Pleuton](https://github.com/anstropleuton) | +| 143 | [shaders_view_depth](shaders/shaders_view_depth.c) | shaders_view_depth | ⭐️⭐️⭐️☆ | 5.6-dev | 5.6-dev | [Luís Almeida](https://github.com/luis605) | ### category: audio @@ -209,26 +209,26 @@ Examples using raylib audio functionality, including sound/music loading and pla | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 142 | [audio_module_playing](audio/audio_module_playing.c) | audio_module_playing | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 143 | [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐️☆☆☆ | 1.3 | 4.2 | [Ray](https://github.com/raysan5) | -| 144 | [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐️⭐️⭐️☆ | 1.6 | 4.2 | [Ray](https://github.com/raysan5) | -| 145 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | -| 146 | [audio_mixed_processor](audio/audio_mixed_processor.c) | audio_mixed_processor | ⭐️⭐️⭐️⭐️ | 4.2 | 4.2 | [hkc](https://github.com/hatkidchan) | -| 147 | [audio_stream_effects](audio/audio_stream_effects.c) | audio_stream_effects | ⭐️⭐️⭐️⭐️ | 4.2 | 5.0 | [Ray](https://github.com/raysan5) | -| 148 | [audio_sound_multi](audio/audio_sound_multi.c) | audio_sound_multi | ⭐️⭐️☆☆ | 4.6 | 4.6 | [Jeffery Myers](https://github.com/JeffM2501) | -| 149 | [audio_sound_positioning](audio/audio_sound_positioning.c) | audio_sound_positioning | ⭐️⭐️☆☆ | 5.5 | 5.5 | [Le Juez Victor](https://github.com/Bigfoot71) | +| 144 | [audio_module_playing](audio/audio_module_playing.c) | audio_module_playing | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 145 | [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐️☆☆☆ | 1.3 | 4.2 | [Ray](https://github.com/raysan5) | +| 146 | [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐️⭐️⭐️☆ | 1.6 | 4.2 | [Ray](https://github.com/raysan5) | +| 147 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | +| 148 | [audio_mixed_processor](audio/audio_mixed_processor.c) | audio_mixed_processor | ⭐️⭐️⭐️⭐️ | 4.2 | 4.2 | [hkc](https://github.com/hatkidchan) | +| 149 | [audio_stream_effects](audio/audio_stream_effects.c) | audio_stream_effects | ⭐️⭐️⭐️⭐️ | 4.2 | 5.0 | [Ray](https://github.com/raysan5) | +| 150 [audio_sound_multi](audio/audio_sound_multi.c) | audio_sound_multi | ⭐️⭐️☆☆ | 4.6 | 4.6 | [Jeffery Myers](https://github.com/JeffM2501) | +| 151 | [audio_sound_positioning](audio/audio_sound_positioning.c) | audio_sound_positioning | ⭐️⭐️☆☆ | 5.5 | 5.5 | [Le Juez Victor](https://github.com/Bigfoot71) | ### category: others -Examples showing raylib misc functionality that does not fit in other categories, like standalone modules usage or examples integrating external libraries. +Ex150amples showing raylib misc functionality that does not fit in other categories, like standalone modules usage or examples integrating external libraries. | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 150 | [rlgl_standalone](others/rlgl_standalone.c) | rlgl_standalone | ⭐️⭐️⭐️⭐️ | 1.6 | 4.0 | [Ray](https://github.com/raysan5) | -| 151 | [rlgl_compute_shader](others/rlgl_compute_shader.c) | rlgl_compute_shader | ⭐️⭐️⭐️⭐️ | 4.0 | 4.0 | [Teddy Astie](https://github.com/tsnake41) | -| 152 | [easings_testbed](others/easings_testbed.c) | easings_testbed | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) | -| 153 | [raylib_opengl_interop](others/raylib_opengl_interop.c) | raylib_opengl_interop | ⭐️⭐️⭐️⭐️ | 3.8 | 4.0 | [Stephan Soller](https://github.com/arkanis) | -| 154 | [embedded_files_loading](others/embedded_files_loading.c) | embedded_files_loading | ⭐️⭐️☆☆ | 3.0 | 3.5 | [Kristian Holmgren](https://github.com/defutura) | -| 155 | [raymath_vector_angle](others/raymath_vector_angle.c) | raymath_vector_angle | ⭐️⭐️☆☆ | 1.0 | 4.6 | [Ray](https://github.com/raysan5) | +| 152 | [rlgl_standalone](others/rlgl_standalone.c) | rlgl_standalone | ⭐️⭐️⭐️⭐️ | 1.6 | 4.0 | [Ray](https://github.com/raysan5) | +| 153 | [rlgl_compute_shader](others/rlgl_compute_shader.c) | rlgl_compute_shader | ⭐️⭐️⭐️⭐️ | 4.0 | 4.0 | [Teddy Astie](https://github.com/tsnake41) | +| 154 | [easings_testbed](others/easings_testbed.c) | easings_testbed | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) | +| 155 | [raylib_opengl_interop](others/raylib_opengl_interop.c) | raylib_opengl_interop | ⭐️⭐️⭐️⭐️ | 3.8 | 4.0 | [Stephan Soller](https://github.com/arkanis) | +| 156 | [embedded_files_loading](others/embedded_files_loading.c) | embedded_files_loading | ⭐️⭐️☆☆ | 3.0 | 3.5 | [Kristian Holmgren](https://github.com/defutura) | +| 157 | [raymath_vector_angle](others/raymath_vector_angle.c) | raymath_vector_angle | ⭐️⭐️☆☆ | 1.0 | 4.6 | [Ray](https://github.com/raysan5) | 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/shapes/shapes_digital_clock.c b/examples/shapes/shapes_digital_clock.c new file mode 100644 index 000000000..12a8c8dfe --- /dev/null +++ b/examples/shapes/shapes_digital_clock.c @@ -0,0 +1,191 @@ +/******************************************************************************************* +* +* raylib [shapes] example - fancy clock using basic shapes +* +* Example complexity rating: [★★☆☆] 2/4 +* +* Example originally created with raylib 5.5, last time updated with raylib 5.5 +* +* Example contributed by Hamza RAHAL (@hmz-rhl) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025-2025 Hamza RAHAL (@hmz-rhl) +* +********************************************************************************************/ + +#include "raylib.h" +#include // needed for cos & sin functions +#include // needed to get machine time + +#define DIGIT_SIZE 30 + +typedef enum +{ + NORMAL_MODE = 0, + HANDS_FREE_MODE, +} ClockMode; + +typedef struct +{ + int value; + Vector2 origin; + float angle; + int length; + int thickness; + Color colour; +} Hand; + +typedef struct +{ + Hand second; + Hand minute; + Hand hour; + ClockMode cm; +} Clock; + + +void UpdateClock(Clock *clock) +{ + time_t rawtime; + struct tm * timeinfo; + + time(&rawtime); + timeinfo = localtime(&rawtime); + + // updating datas + clock->second.value = timeinfo->tm_sec; + clock->minute.value = timeinfo->tm_min; + clock->hour.value = timeinfo->tm_hour; + + clock->hour.angle = (timeinfo->tm_hour % 12)*180.0/6.0f; + clock->hour.angle += (timeinfo->tm_min % 60)*30/60.0f; + clock->hour.angle -= 90 ; + + clock->minute.angle = (timeinfo->tm_min % 60)*6.0f; + clock->minute.angle += (timeinfo->tm_sec % 60)*6/60.0f; + clock->minute.angle -= 90 ; + + clock->second.angle = (timeinfo->tm_sec % 60)*6.0f; + clock->second.angle -= 90 ; + +} + +void drawClock(Clock clock) +{ + if (clock.cm == HANDS_FREE_MODE) + { + DrawText(TextFormat("%i", clock.second.value), clock.second.origin.x + (clock.second.length - 10)*cos(clock.second.angle*(float)(M_PI/180)) - DIGIT_SIZE/2, clock.second.origin.y + clock.second.length*sin(clock.second.angle*(float)(M_PI/180)) - DIGIT_SIZE/2, DIGIT_SIZE, GRAY); + + DrawText(TextFormat("%i", clock.minute.value), clock.minute.origin.x + clock.minute.length*cos(clock.minute.angle*(float)(M_PI/180)) - DIGIT_SIZE/2, clock.minute.origin.y + clock.minute.length*sin(clock.minute.angle*(float)(M_PI/180)) - DIGIT_SIZE/2, DIGIT_SIZE, RED); + + DrawText(TextFormat("%i", clock.hour.value), clock.hour.origin.x + clock.hour.length*cos(clock.hour.angle*(float)(M_PI/180)) - DIGIT_SIZE/2, clock.hour.origin.y + clock.hour.length*sin(clock.hour.angle*(float)(M_PI/180)) - DIGIT_SIZE/2, DIGIT_SIZE, GOLD); + } + else + { + DrawRectanglePro( + (Rectangle){ clock.second.origin.x, + clock.second.origin.y, + clock.second.length, + clock.second.thickness, + } + , (Vector2){0, clock.second.thickness/2}, + clock.second.angle, + clock.second.colour + ); + + DrawRectanglePro( + (Rectangle){ clock.minute.origin.x, + clock.minute.origin.y, + clock.minute.length, + clock.minute.thickness, + } + , (Vector2){0, clock.minute.thickness/2}, + clock.minute.angle, + clock.minute.colour + ); + + DrawRectanglePro( + (Rectangle){ clock.hour.origin.x, + clock.hour.origin.y, + clock.hour.length, + clock.hour.thickness, + } + , (Vector2){0, clock.hour.thickness/2}, + clock.hour.angle, + clock.hour.colour + ); + } +} + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + Clock myClock = { + .cm = NORMAL_MODE, + + .second.origin = (Vector2){400, 225}, + .second.angle = 45, + .second.length = 140, + .second.thickness = 3, + .second.colour = BEIGE, + + .minute.origin = (Vector2){400, 225}, + .minute.angle = 10, + .minute.length = 130, + .minute.thickness = 7, + .minute.colour = DARKGRAY, + + .hour.origin = (Vector2){400, 225}, + .hour.angle = 0, + .hour.length = 100, + .hour.thickness = 7, + .hour.colour = BLACK, + }; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - digital clock"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + if (IsKeyPressed(KEY_SPACE)) + { + myClock.cm = (myClock.cm == HANDS_FREE_MODE) ? NORMAL_MODE : HANDS_FREE_MODE; + } + + UpdateClock(&myClock); + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawCircle(400, 225, 5, BLACK); // center dot + drawClock(myClock); + + DrawText("press [SPACE] to switch clock mode", 350, 400, 10, GRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/shapes/shapes_digital_clock.png b/examples/shapes/shapes_digital_clock.png new file mode 100644 index 0000000000000000000000000000000000000000..22939253176be52b334d781d99a2e7c23a750ddd GIT binary patch literal 3651 zcmeHKYfzI{8h$^b35r3~ibA*qC|gB9z(5fMQ$(&SQe<|iS`8o&7KBIu5k)=)(bkGc zYu5`4$yP9ojJ8%jMG*ucD~^Jf0#(t<#eLGw{@9=FuboaandHqm@8x;k z%Q;^@iHryqPPLp$M8eRJz$hY~B@xqL%*RUUWpcsSBxOKov@u@Wj5qATZ;Q<3F`28< z6Eio*W+YHjTKdKW7iD}#LPDBSo}M|tYxIZGJcH7*jD*n34gl)Vzz?Ex%AVb;Io#<*MX^5neK#KPk3Y6~z$GIq zDDv-v?{qk>(PV$xWa|Mzl5#7PrUQU{KZnoz zg&<<~o`i_?Rx?CRQ-nm~2sySk1webxEYOvf{igbVlz~@6PZ?5_HI9$=j#T82zpR@T zY$SbhO-`8`Ea$ZL5i6NcB{}!q!;EH*n&A%j_v^_ZO`s1rb-y|2k{GoDbFTiq4>Pa|;2%^Sg-{|n``ONxKq zyKZAdlI3!`H)3uP$D@owK3A)&Hxp5borG+<(%!$wkZ6ifwMOY~=S4C z+W}$6no}hI3xrSFNtF=5(+7I$idru+?E!Kst9l5mwS|1?WMGu0Kv-@N7C=bo3Y#o7 zfv^if{)oX zDH8A%AA-Ir5j*#>;|bV8@AzOR%E7rRFU%6rtw1?R0-;29m08J0?#_u#O`5!utu zgpty*X6U=hp2oKw>OA_w%#xhTI}}=5QWo;*l3V@0sfG^JZa(fCmG|aF@?5@2Gn2DI zPJ;!GXqA?2b9F;cItobT-;%hXyt(<$3)dIVHKjT6K|I#JI?>HPe|+0u2-O?G$Ew!O z%7f3K2r0k?%Cw3g8ZHl3JV=p1^&)TVPH%I!&IG$>{ zc>zx`RWQPw|Nb*RRcgjuq@Bo6C0g_u*pwl$=dW4{>Fa;Vma^H0Zjj0n1(w5H-#&*)lnCOAvEOiiG-#3dX3|P{OqK15@?hFh2wH zc;x1SgBJ}aA-5kZ+xS|4RE+b)Kt45? zlClR=^GybiaKs9m_*y00dZD$Jo(HY31A)}_F~)ch+{ZCR{stv zfpI+fK~yr5L1&zHhvV7c$XVjdJ~(fmaFL~UHiDnLvNmwfG`$l_#fJHH+dF!&3kzh9T*w&?;3b@Zud~ZYTaaQu5F7AkuG=ZR#8!G&X(A> z!^7FH;|Kh* z!*Inh<$0^$K3+BY;7&@(TwPtyKtso&r0VD0Bc5a9NsBDP*F7Jqag3jDJE%gV{6}}*KTlD zE=~F2AIiG-S1fycyXD@GS!^)wh^XTgP78mE&ii5Lrb%b%%KPE#mFX$H51nk1))nXa zv3NRAMLAZSY`k!#sy}Y<+O8(g#)b1+xh41W(p+w_W4QGToExs>4Tp}FZhh3+8#>xm z`|9=5qsj9v@~_5(b+^`@3sUxOF`p8iboNC4`dOmzw<~M?mrZ)`pW8+Lu}f!xP2SNJ zr+&^R&IzBWC%d+t?lqG6VTMWC^(**OQs2n57yR{ZR>lqw{C%R( Date: Tue, 3 Jun 2025 20:42:27 +0200 Subject: [PATCH 033/242] REVIEWED: `shapes_digital_clock` example --- examples/shapes/shapes_digital_clock.c | 202 ++++++++++++------------- 1 file changed, 98 insertions(+), 104 deletions(-) diff --git a/examples/shapes/shapes_digital_clock.c b/examples/shapes/shapes_digital_clock.c index 12a8c8dfe..8aa887c3c 100644 --- a/examples/shapes/shapes_digital_clock.c +++ b/examples/shapes/shapes_digital_clock.c @@ -11,113 +11,46 @@ * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software * -* Copyright (c) 2025-2025 Hamza RAHAL (@hmz-rhl) +* Copyright (c) 2025 Hamza RAHAL (@hmz-rhl) * ********************************************************************************************/ #include "raylib.h" -#include // needed for cos & sin functions -#include // needed to get machine time + +#include // Required for: cosf(), sinf() +#include // Required for: time(), localtime() #define DIGIT_SIZE 30 -typedef enum -{ - NORMAL_MODE = 0, - HANDS_FREE_MODE, +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef enum { + MODE_NORMAL = 0, + MODE_HANDS_FREE, } ClockMode; -typedef struct -{ +typedef struct { int value; Vector2 origin; float angle; int length; int thickness; - Color colour; -} Hand; + Color color; +} ClockHand; -typedef struct -{ - Hand second; - Hand minute; - Hand hour; - ClockMode cm; +typedef struct { + ClockMode mode; + ClockHand second; + ClockHand minute; + ClockHand hour; } Clock; - -void UpdateClock(Clock *clock) -{ - time_t rawtime; - struct tm * timeinfo; - - time(&rawtime); - timeinfo = localtime(&rawtime); - - // updating datas - clock->second.value = timeinfo->tm_sec; - clock->minute.value = timeinfo->tm_min; - clock->hour.value = timeinfo->tm_hour; - - clock->hour.angle = (timeinfo->tm_hour % 12)*180.0/6.0f; - clock->hour.angle += (timeinfo->tm_min % 60)*30/60.0f; - clock->hour.angle -= 90 ; - - clock->minute.angle = (timeinfo->tm_min % 60)*6.0f; - clock->minute.angle += (timeinfo->tm_sec % 60)*6/60.0f; - clock->minute.angle -= 90 ; - - clock->second.angle = (timeinfo->tm_sec % 60)*6.0f; - clock->second.angle -= 90 ; - -} - -void drawClock(Clock clock) -{ - if (clock.cm == HANDS_FREE_MODE) - { - DrawText(TextFormat("%i", clock.second.value), clock.second.origin.x + (clock.second.length - 10)*cos(clock.second.angle*(float)(M_PI/180)) - DIGIT_SIZE/2, clock.second.origin.y + clock.second.length*sin(clock.second.angle*(float)(M_PI/180)) - DIGIT_SIZE/2, DIGIT_SIZE, GRAY); - - DrawText(TextFormat("%i", clock.minute.value), clock.minute.origin.x + clock.minute.length*cos(clock.minute.angle*(float)(M_PI/180)) - DIGIT_SIZE/2, clock.minute.origin.y + clock.minute.length*sin(clock.minute.angle*(float)(M_PI/180)) - DIGIT_SIZE/2, DIGIT_SIZE, RED); - - DrawText(TextFormat("%i", clock.hour.value), clock.hour.origin.x + clock.hour.length*cos(clock.hour.angle*(float)(M_PI/180)) - DIGIT_SIZE/2, clock.hour.origin.y + clock.hour.length*sin(clock.hour.angle*(float)(M_PI/180)) - DIGIT_SIZE/2, DIGIT_SIZE, GOLD); - } - else - { - DrawRectanglePro( - (Rectangle){ clock.second.origin.x, - clock.second.origin.y, - clock.second.length, - clock.second.thickness, - } - , (Vector2){0, clock.second.thickness/2}, - clock.second.angle, - clock.second.colour - ); - - DrawRectanglePro( - (Rectangle){ clock.minute.origin.x, - clock.minute.origin.y, - clock.minute.length, - clock.minute.thickness, - } - , (Vector2){0, clock.minute.thickness/2}, - clock.minute.angle, - clock.minute.colour - ); - - DrawRectanglePro( - (Rectangle){ clock.hour.origin.x, - clock.hour.origin.y, - clock.hour.length, - clock.hour.thickness, - } - , (Vector2){0, clock.hour.thickness/2}, - clock.hour.angle, - clock.hour.colour - ); - } -} +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +static void UpdateClock(Clock *clock); // Update clock time +static void DrawClock(Clock clock, Vector2 centerPos); // Draw clock at desired position //------------------------------------------------------------------------------------ // Program main entry point @@ -128,31 +61,29 @@ int main(void) //-------------------------------------------------------------------------------------- const int screenWidth = 800; const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - digital clock"); + // Initialize clock Clock myClock = { - .cm = NORMAL_MODE, + .mode = MODE_NORMAL, - .second.origin = (Vector2){400, 225}, .second.angle = 45, .second.length = 140, .second.thickness = 3, - .second.colour = BEIGE, + .second.color = BEIGE, - .minute.origin = (Vector2){400, 225}, .minute.angle = 10, .minute.length = 130, .minute.thickness = 7, - .minute.colour = DARKGRAY, + .minute.color = DARKGRAY, - .hour.origin = (Vector2){400, 225}, .hour.angle = 0, .hour.length = 100, .hour.thickness = 7, - .hour.colour = BLACK, + .hour.color = BLACK, }; - InitWindow(screenWidth, screenHeight, "raylib [shapes] example - digital clock"); - SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -160,23 +91,27 @@ int main(void) while (!WindowShouldClose()) // Detect window close button or ESC key { // Update + //---------------------------------------------------------------------------------- if (IsKeyPressed(KEY_SPACE)) { - myClock.cm = (myClock.cm == HANDS_FREE_MODE) ? NORMAL_MODE : HANDS_FREE_MODE; + if (myClock.mode == MODE_HANDS_FREE) myClock.mode = MODE_NORMAL; + else if (myClock.mode == MODE_NORMAL) myClock.mode = MODE_HANDS_FREE; } UpdateClock(&myClock); + //---------------------------------------------------------------------------------- // Draw //---------------------------------------------------------------------------------- BeginDrawing(); - ClearBackground(RAYWHITE); + ClearBackground(RAYWHITE); - DrawCircle(400, 225, 5, BLACK); // center dot - drawClock(myClock); + DrawCircle(400, 225, 5, BLACK); // Clock center dot + + DrawClock(myClock, (Vector2){ 400, 225 }); // Clock in selected mode - DrawText("press [SPACE] to switch clock mode", 350, 400, 10, GRAY); + DrawText("Press [SPACE] to switch clock mode", 10, 10, 20, DARKGRAY); EndDrawing(); //---------------------------------------------------------------------------------- @@ -188,4 +123,63 @@ int main(void) //-------------------------------------------------------------------------------------- return 0; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Update clock time +static void UpdateClock(Clock *clock) +{ + time_t rawtime; + struct tm * timeinfo; + + time(&rawtime); + timeinfo = localtime(&rawtime); + + // Updating time data + clock->second.value = timeinfo->tm_sec; + clock->minute.value = timeinfo->tm_min; + clock->hour.value = timeinfo->tm_hour; + + clock->hour.angle = (timeinfo->tm_hour%12)*180.0/6.0f; + clock->hour.angle += (timeinfo->tm_min%60)*30/60.0f; + clock->hour.angle -= 90; + + clock->minute.angle = (timeinfo->tm_min%60)*6.0f; + clock->minute.angle += (timeinfo->tm_sec%60)*6/60.0f; + clock->minute.angle -= 90; + + clock->second.angle = (timeinfo->tm_sec%60)*6.0f; + clock->second.angle -= 90; +} + +// Draw clock +static void DrawClock(Clock clock, Vector2 centerPosition) +{ + if (clock.mode == MODE_HANDS_FREE) + { + DrawCircleLinesV(centerPosition, clock.minute.length, LIGHTGRAY); + + DrawText(TextFormat("%i", clock.second.value), centerPosition.x + (clock.second.length - 10)*cosf(clock.second.angle*(float)(PI/180)) - DIGIT_SIZE/2, centerPosition.y + clock.second.length*sinf(clock.second.angle*(float)(PI/180)) - DIGIT_SIZE/2, DIGIT_SIZE, GRAY); + + DrawText(TextFormat("%i", clock.minute.value), clock.minute.origin.x + clock.minute.length*cosf(clock.minute.angle*(float)(PI/180)) - DIGIT_SIZE/2, centerPosition.y + clock.minute.length*sinf(clock.minute.angle*(float)(PI/180)) - DIGIT_SIZE/2, DIGIT_SIZE, RED); + + DrawText(TextFormat("%i", clock.hour.value), centerPosition.x + clock.hour.length*cosf(clock.hour.angle*(float)(PI/180)) - DIGIT_SIZE/2, centerPosition.y + clock.hour.length*sinf(clock.hour.angle*(float)(PI/180)) - DIGIT_SIZE/2, DIGIT_SIZE, GOLD); + } + else if (clock.mode == MODE_NORMAL) + { + // Draw hand seconds + DrawRectanglePro((Rectangle){ centerPosition.x, centerPosition.y, clock.second.length, clock.second.thickness }, + (Vector2){ 0.0f, clock.second.thickness/2.0f }, clock.second.angle, clock.second.color); + + // Draw hand minutes + DrawRectanglePro((Rectangle){ centerPosition.x, centerPosition.y, clock.minute.length, clock.minute.thickness }, + (Vector2){ 0.0f, clock.minute.thickness/2.0f }, clock.minute.angle, clock.minute.color); + + // Draw hand hours + DrawRectanglePro((Rectangle){ centerPosition.x, centerPosition.y, clock.hour.length, clock.hour.thickness }, + (Vector2){ 0.0f, clock.hour.thickness/2.0f }, clock.hour.angle, clock.hour.color); + } } \ No newline at end of file From 060bd787b1051e1974b8efd7ca048a9c320f8c63 Mon Sep 17 00:00:00 2001 From: katonar Date: Thu, 5 Jun 2025 15:50:07 +0200 Subject: [PATCH 034/242] Refactor: Replace DRM swap buffer implementation with asynchronous page-flipping and framebuffer caching The original implementation created/destroyed framebuffers (FBs) per-frame, leading to kernel overhead and screen tearing. This commit replaces it with a different approach using: - Asynchronous `drmModePageFlip()` with vblank sync - Framebuffer caching to reduce repeated FB creation/removal operations - Proper resource management through BO callbacks and buffer release synchronization - Added error handling for busy displays, cache overflows, and flip failures - Event-driven cleanup via page_flip_handler to prevent GPU/scanout conflicts Co-authored-by: rob-bits --- src/platforms/rcore_drm.c | 245 ++++++++++++++++++++++++++++++++++---- 1 file changed, 220 insertions(+), 25 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 3ec3135e1..9aebe6cf0 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -71,6 +71,21 @@ #include "EGL/egl.h" // Native platform windowing system interface #include "EGL/eglext.h" // EGL extensions +#include // for drmHandleEvent poll +#include //for EBUSY, EAGAIN + +#define MAX_CACHED_BOS 3 + +typedef struct { + struct gbm_bo *bo; + uint32_t fb_id; // DRM framebuffer ID +} FramebufferCache; + +static FramebufferCache fbCache[MAX_CACHED_BOS]; +static volatile int fbCacheCount = 0; +static volatile bool pendingFlip = false; +static bool crtcSet = false; + #ifndef EGL_OPENGL_ES3_BIT #define EGL_OPENGL_ES3_BIT 0x40 #endif @@ -217,6 +232,7 @@ static const short linuxToRaylibMap[KEYMAP_SIZE] = { //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- +int InitSwapScreenBuffer(void); int InitPlatform(void); // Initialize platform (graphics, inputs and more) void ClosePlatform(void); // Close platform @@ -551,34 +567,207 @@ void DisableCursor(void) CORE.Input.Mouse.cursorHidden = true; } -// Swap back buffer with front buffer (screen drawing) -void SwapScreenBuffer(void) -{ - eglSwapBuffers(platform.device, platform.surface); +static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data) { + uint32_t fb_id = (uintptr_t)data; + // Remove from cache + for (int i = 0; i < fbCacheCount; i++) { + if (fbCache[i].bo == bo) { + TRACELOG(LOG_INFO, "DRM: fb removed %u", (uintptr_t)fb_id); + drmModeRmFB(platform.fd, fbCache[i].fb_id); // Release DRM FB + // Shift remaining entries + for (int j = i; j < fbCacheCount - 1; j++) { + fbCache[j] = fbCache[j + 1]; + } + fbCacheCount--; + break; + } + } +} - if (!platform.gbmSurface || (-1 == platform.fd) || !platform.connector || !platform.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); - - struct gbm_bo *bo = gbm_surface_lock_front_buffer(platform.gbmSurface); - if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer"); - - uint32_t fb = 0; - int result = drmModeAddFB(platform.fd, platform.connector->modes[platform.modeIndex].hdisplay, platform.connector->modes[platform.modeIndex].vdisplay, 24, 32, gbm_bo_get_stride(bo), gbm_bo_get_handle(bo).u32, &fb); - if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d", result); - - result = drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fb, 0, 0, &platform.connector->connector_id, 1, &platform.connector->modes[platform.modeIndex]); - if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result); - - if (platform.prevFB) - { - result = drmModeRmFB(platform.fd, platform.prevFB); - if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result); +// Create or retrieve cached DRM FB for BO +static uint32_t get_or_create_fb_for_bo(struct gbm_bo *bo) { + // Try to find existing cache entry + for (int i = 0; i < fbCacheCount; i++) { + if (fbCache[i].bo == bo) { + return fbCache[i].fb_id; + } } - platform.prevFB = fb; + // Create new entry if cache not full + if (fbCacheCount >= MAX_CACHED_BOS) { + //FB cache full! + return 0; + } - if (platform.prevBO) gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); + uint32_t handle = gbm_bo_get_handle(bo).u32; + uint32_t stride = gbm_bo_get_stride(bo); + uint32_t width = gbm_bo_get_width(bo); + uint32_t height = gbm_bo_get_height(bo); + uint32_t fb_id; + if (drmModeAddFB(platform.fd, width, height, 24, 32, stride, handle, &fb_id)) { + //rmModeAddFB failed + return 0; + } + + // Store in cache + fbCache[fbCacheCount] = (FramebufferCache){ .bo = bo, .fb_id = fb_id }; + fbCacheCount++; + + // Set destroy callback to auto-cleanup + gbm_bo_set_user_data(bo, (void*)(uintptr_t)fb_id, drm_fb_destroy_callback); + + TRACELOG(LOG_INFO, "DRM: added new bo %u" , (uintptr_t)fb_id); + return fb_id; +} + +// Renders a blank frame to allocate initial buffers +void RenderBlankFrame() { + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(platform.device, platform.surface); + + // Ensure the buffer is processed + glFinish(); +} + +// Initialize with first buffer only +int InitSwapScreenBuffer() { + if (!platform.gbmSurface || platform.fd < 0) { + TRACELOG(LOG_ERROR, "DRM not initialized"); + return -1; + } + + // Render a blank frame to allocate buffers + RenderBlankFrame(); + + // Get first buffer + struct gbm_bo *bo = gbm_surface_lock_front_buffer(platform.gbmSurface); + if (!bo) { + TRACELOG(LOG_ERROR, "Failed to lock initial buffer"); + return -1; + } + + // Create FB for first buffer + uint32_t fb_id = get_or_create_fb_for_bo(bo); + if (!fb_id) { + gbm_surface_release_buffer(platform.gbmSurface, bo); + return -1; + } + + // Initial CRTC setup + if (drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fb_id, + 0, 0, &platform.connector->connector_id, 1, + &platform.connector->modes[platform.modeIndex])) { + TRACELOG(LOG_ERROR, "Initial CRTC setup failed: %s", strerror(errno)); + gbm_surface_release_buffer(platform.gbmSurface, bo); + return -1; + } + + // Keep first buffer locked until flipped platform.prevBO = bo; + crtcSet = true; + return 0; +} + +// Static page flip handler +// this will be called once the drmModePageFlip() finished from the drmHandleEvent(platform.fd, &evctx); context +static void page_flip_handler(int fd, unsigned int frame, + unsigned int sec, unsigned int usec, + void *data) { + (void)fd; (void)frame; (void)sec; (void)usec; // Unused + pendingFlip = false; + struct gbm_bo *bo_to_release = (struct gbm_bo *)data; + //Buffers are released after the flip completes (via page_flip_handler), ensuring they're no longer in use. + // Prevents the GPU from writing to a buffer being scanned out + if (bo_to_release) { + gbm_surface_release_buffer(platform.gbmSurface, bo_to_release); + } +} + +// Swap implementation with proper caching +void SwapScreenBuffer() { + static int loopCnt = 0; + loopCnt++; + static int errCnt[5] = {0}; + if (!crtcSet || !platform.gbmSurface) return; + + //call this only, if pendingFlip is not set + eglSwapBuffers(platform.device, platform.surface); + + // Process pending events non-blocking + drmEventContext evctx = { + .version = DRM_EVENT_CONTEXT_VERSION, + .page_flip_handler = page_flip_handler + }; + + struct pollfd pfd = { .fd = platform.fd, .events = POLLIN }; + //polling for event for 0ms + while (poll(&pfd, 1, 0) > 0) { + drmHandleEvent(platform.fd, &evctx); + } + + // Skip if previous flip pending + if (pendingFlip) { + //Skip frame: flip pending + errCnt[0]++; + return; + } + + // Get new front buffer + struct gbm_bo *next_bo = gbm_surface_lock_front_buffer(platform.gbmSurface); + if (!next_bo) { + //Failed to lock front buffer + errCnt[1]++; + return; + } + + // Get FB ID (creates new one if needed) + uint32_t fb_id = get_or_create_fb_for_bo(next_bo); + if (!fb_id) { + gbm_surface_release_buffer(platform.gbmSurface, next_bo); + errCnt[2]++; + return; + } + + // Attempt page flip + /* rmModePageFlip() schedules a buffer-flip for the next vblank and then + * notifies us about it. It takes a CRTC-id, fb-id and an arbitrary + * data-pointer and then schedules the page-flip. This is fully asynchronous and + * When the page-flip happens, the DRM-fd will become readable and we can call + * drmHandleEvent(). This will read all vblank/page-flip events and call our + * modeset_page_flip_event() callback with the data-pointer that we passed to + * drmModePageFlip(). We simply call modeset_draw_dev() then so the next frame + * is rendered.. + * returns immediately. + */ + if (drmModePageFlip(platform.fd, platform.crtc->crtc_id, fb_id, + DRM_MODE_PAGE_FLIP_EVENT, platform.prevBO)) { + if (errno == EBUSY) { + //Display busy - skip flip + errCnt[3]++; + } else { + //Page flip failed + errCnt[4]++; + } + gbm_surface_release_buffer(platform.gbmSurface, next_bo); + return; + } + + // Success: update state + pendingFlip = true; + + platform.prevBO = next_bo; + //successful usage, do benchmarking + //in every 10 sec, at 60FPS 60*10 -> 600 + if(loopCnt >= 600) { + TRACELOG(LOG_INFO, "DRM err counters: %d, %d, %d, %d, %d, %d",errCnt[0],errCnt[1],errCnt[2],errCnt[3],errCnt[4], loopCnt); + //reinit the errors + for(int i=0;i<5;i++) { + errCnt[i] = 0; + } + loopCnt = 0; + } } //---------------------------------------------------------------------------------- @@ -910,7 +1099,8 @@ int InitPlatform(void) EGL_BLUE_SIZE, 8, // BLUE color bit depth (alternative: 5) EGL_ALPHA_SIZE, 8, // ALPHA bit depth (required for transparent framebuffer) //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI) - EGL_DEPTH_SIZE, 24, // Depth buffer size (Required to use Depth testing!) + //ToDo: verify this. In 5.5 it is 16, in master it was 24 + EGL_DEPTH_SIZE, 16, // Depth buffer size (Required to use Depth testing!) //EGL_STENCIL_SIZE, 8, // Stencil buffer size EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs) @@ -1083,9 +1273,14 @@ int InitPlatform(void) CORE.Storage.basePath = GetWorkingDirectory(); //---------------------------------------------------------------------------- - TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully"); + if(InitSwapScreenBuffer() == 0) { + TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully"); + return 0; + } else { + TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized failed"); + return -1; + } - return 0; } // Close platform From 533c12c386e7f663f8c4da47bf50dc2e44a64af4 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 7 Jun 2025 15:33:35 +0200 Subject: [PATCH 035/242] Small security tweaks --- src/platforms/rcore_drm.c | 2 +- src/platforms/rcore_web.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 3ec3135e1..619d05dcd 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -1374,7 +1374,7 @@ static void InitEvdevInput(void) if ((strncmp("event", entity->d_name, strlen("event")) == 0) || // Search for devices named "event*" (strncmp("mouse", entity->d_name, strlen("mouse")) == 0)) // Search for devices named "mouse*" { - sprintf(path, "%s%s", DEFAULT_EVDEV_PATH, entity->d_name); + snprintf(path, MAX_FILEPATH_LENGTH, "%s%s", DEFAULT_EVDEV_PATH, entity->d_name); ConfigureEvdevDevice(path); // Configure the device if appropriate } } diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index f83e3159e..62e4e94cc 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -1746,7 +1746,7 @@ static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadE if (gamepadEvent->connected && (gamepadEvent->index < MAX_GAMEPADS)) { CORE.Input.Gamepad.ready[gamepadEvent->index] = true; - sprintf(CORE.Input.Gamepad.name[gamepadEvent->index], "%s", gamepadEvent->id); + snprintf(CORE.Input.Gamepad.name[gamepadEvent->index], MAX_GAMEPAD_NAME_LENGTH, "%s", gamepadEvent->id); } else CORE.Input.Gamepad.ready[gamepadEvent->index] = false; From 8a3a8ee8e3d9f295a70df75b9c84f5cf86dd345d Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 7 Jun 2025 20:14:10 +0200 Subject: [PATCH 036/242] Update shapes_digital_clock.c --- examples/shapes/shapes_digital_clock.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/shapes/shapes_digital_clock.c b/examples/shapes/shapes_digital_clock.c index 8aa887c3c..ffa1ff5dc 100644 --- a/examples/shapes/shapes_digital_clock.c +++ b/examples/shapes/shapes_digital_clock.c @@ -61,7 +61,7 @@ int main(void) //-------------------------------------------------------------------------------------- const int screenWidth = 800; const int screenHeight = 450; - + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - digital clock"); // Initialize clock @@ -69,7 +69,7 @@ int main(void) .mode = MODE_NORMAL, .second.angle = 45, - .second.length = 140, + .second.length = 140, .second.thickness = 3, .second.color = BEIGE, @@ -104,11 +104,11 @@ int main(void) // Draw //---------------------------------------------------------------------------------- BeginDrawing(); - + ClearBackground(RAYWHITE); DrawCircle(400, 225, 5, BLACK); // Clock center dot - + DrawClock(myClock, (Vector2){ 400, 225 }); // Clock in selected mode DrawText("Press [SPACE] to switch clock mode", 10, 10, 20, DARKGRAY); @@ -138,7 +138,7 @@ static void UpdateClock(Clock *clock) time(&rawtime); timeinfo = localtime(&rawtime); - // Updating time data + // Updating time data clock->second.value = timeinfo->tm_sec; clock->minute.value = timeinfo->tm_min; clock->hour.value = timeinfo->tm_hour; @@ -150,7 +150,7 @@ static void UpdateClock(Clock *clock) clock->minute.angle = (timeinfo->tm_min%60)*6.0f; clock->minute.angle += (timeinfo->tm_sec%60)*6/60.0f; clock->minute.angle -= 90; - + clock->second.angle = (timeinfo->tm_sec%60)*6.0f; clock->second.angle -= 90; } @@ -161,7 +161,7 @@ static void DrawClock(Clock clock, Vector2 centerPosition) if (clock.mode == MODE_HANDS_FREE) { DrawCircleLinesV(centerPosition, clock.minute.length, LIGHTGRAY); - + DrawText(TextFormat("%i", clock.second.value), centerPosition.x + (clock.second.length - 10)*cosf(clock.second.angle*(float)(PI/180)) - DIGIT_SIZE/2, centerPosition.y + clock.second.length*sinf(clock.second.angle*(float)(PI/180)) - DIGIT_SIZE/2, DIGIT_SIZE, GRAY); DrawText(TextFormat("%i", clock.minute.value), clock.minute.origin.x + clock.minute.length*cosf(clock.minute.angle*(float)(PI/180)) - DIGIT_SIZE/2, centerPosition.y + clock.minute.length*sinf(clock.minute.angle*(float)(PI/180)) - DIGIT_SIZE/2, DIGIT_SIZE, RED); @@ -171,11 +171,11 @@ static void DrawClock(Clock clock, Vector2 centerPosition) else if (clock.mode == MODE_NORMAL) { // Draw hand seconds - DrawRectanglePro((Rectangle){ centerPosition.x, centerPosition.y, clock.second.length, clock.second.thickness }, + DrawRectanglePro((Rectangle){ centerPosition.x, centerPosition.y, clock.second.length, clock.second.thickness }, (Vector2){ 0.0f, clock.second.thickness/2.0f }, clock.second.angle, clock.second.color); // Draw hand minutes - DrawRectanglePro((Rectangle){ centerPosition.x, centerPosition.y, clock.minute.length, clock.minute.thickness }, + DrawRectanglePro((Rectangle){ centerPosition.x, centerPosition.y, clock.minute.length, clock.minute.thickness }, (Vector2){ 0.0f, clock.minute.thickness/2.0f }, clock.minute.angle, clock.minute.color); // Draw hand hours From 59bcf680aafb6f054e150f70c8c71deb44ad04b1 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 7 Jun 2025 20:14:24 +0200 Subject: [PATCH 037/242] Code gardening... --- src/platforms/rcore_desktop_glfw.c | 22 ++-- src/platforms/rcore_desktop_sdl.c | 4 +- src/platforms/rcore_drm.c | 2 +- src/platforms/rcore_web.c | 4 +- src/raudio.c | 10 +- src/rcore.c | 14 +-- src/rlgl.h | 2 +- src/rmodels.c | 187 ++++++++++++++--------------- src/rtext.c | 6 +- src/rtextures.c | 8 +- 10 files changed, 128 insertions(+), 131 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 83c13d34e..9bbd6559a 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -130,9 +130,9 @@ static void CursorEnterCallback(GLFWwindow *window, int enter); static void JoystickCallback(int jid, int event); // GLFW3 Joystick Connected/Disconnected Callback // Wrappers used by glfwInitAllocator -static void* AllocateWrapper(size_t size, void* user); // GLFW3 GLFWallocatefun, wrapps around RL_MALLOC macro -static void* ReallocateWrapper(void* block, size_t size, void* user); // GLFW3 GLFWreallocatefun, wrapps around RL_MALLOC macro -static void DeallocateWrapper(void* block, void* user); // GLFW3 GLFWdeallocatefun, wraps around RL_FREE macro +static void *AllocateWrapper(size_t size, void *user); // GLFW3 GLFWallocatefun, wrapps around RL_MALLOC macro +static void *ReallocateWrapper(void *block, size_t size, void *user); // GLFW3 GLFWreallocatefun, wrapps around RL_MALLOC macro +static void DeallocateWrapper(void *block, void *user); // GLFW3 GLFWdeallocatefun, wraps around RL_FREE macro //---------------------------------------------------------------------------------- // Module Functions Declaration @@ -576,7 +576,7 @@ void SetWindowIcons(Image *images, int count) else { int valid = 0; - GLFWimage *icons = RL_CALLOC(count, sizeof(GLFWimage)); + GLFWimage *icons = (GLFWimage *)RL_CALLOC(count, sizeof(GLFWimage)); for (int i = 0; i < count; i++) { @@ -1300,17 +1300,17 @@ static void SetDimensionsFromMonitor(GLFWmonitor *monitor) // We need to provide these because GLFWallocator expects function pointers with specific signatures. // Similar wrappers exist in utils.c but we cannot reuse them here due to declaration mismatch. // https://www.glfw.org/docs/latest/intro_guide.html#init_allocator -static void* AllocateWrapper(size_t size, void* user) +static void *AllocateWrapper(size_t size, void *user) { (void)user; return RL_MALLOC(size); } -static void* ReallocateWrapper(void* block, size_t size, void* user) +static void *ReallocateWrapper(void *block, size_t size, void *user) { (void)user; return RL_REALLOC(block, size); } -static void DeallocateWrapper(void* block, void* user) +static void DeallocateWrapper(void *block, void *user) { (void)user; RL_FREE(block); @@ -1327,6 +1327,7 @@ int InitPlatform(void) .reallocate = ReallocateWrapper, .user = NULL, // RL_*ALLOC macros are not capable of handling user-provided data }; + glfwInitAllocator(&allocator); #if defined(__APPLE__) @@ -1696,6 +1697,8 @@ int InitPlatform(void) // Retrieve gamepad names for (int i = 0; i < MAX_GAMEPADS; i++) { + // WARNING: If glfwGetJoystickName() is longer than MAX_GAMEPAD_NAME_LENGTH, + // we can get a not-NULL terminated string, so, we only copy up to (MAX_GAMEPAD_NAME_LENGTH - 1) if (glfwJoystickPresent(i)) strncpy(CORE.Input.Gamepad.name[i], glfwGetJoystickName(i), MAX_GAMEPAD_NAME_LENGTH - 1); } //---------------------------------------------------------------------------- @@ -1753,7 +1756,7 @@ static void ErrorCallback(int error, const char *description) static void WindowSizeCallback(GLFWwindow *window, int width, int height) { // WARNING: On window minimization, callback is called, - // but we don't want to change internal screen values, it breaks things + // but we don't want to change internal screen values, it breaks things if ((width == 0) || (height == 0)) return; // Reset viewport and projection matrix for new size @@ -1973,6 +1976,9 @@ static void JoystickCallback(int jid, int event) { if (event == GLFW_CONNECTED) { + // WARNING: If glfwGetJoystickName() is longer than MAX_GAMEPAD_NAME_LENGTH, + // we can get a not-NULL terminated string, so, we clean destination and only copy up to -1 + memset(CORE.Input.Gamepad.name[jid], 0, MAX_GAMEPAD_NAME_LENGTH); strncpy(CORE.Input.Gamepad.name[jid], glfwGetJoystickName(jid), MAX_GAMEPAD_NAME_LENGTH - 1); } else if (event == GLFW_DISCONNECTED) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index aacb2f800..6af1cb6c7 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -721,7 +721,7 @@ void SetWindowIcon(Image image) bmask = 0x001F, amask = 0; depth = 16, pitch = image.width*2; } break; - case PIXELFORMAT_UNCOMPRESSED_R8G8B8: + case PIXELFORMAT_UNCOMPRESSED_R8G8B8: { // WARNING: SDL2 could be using BGR but SDL3 RGB rmask = 0xFF0000, gmask = 0x00FF00; @@ -1697,8 +1697,8 @@ void PollInputEvents(void) CORE.Input.Gamepad.axisCount[jid] = SDL_JoystickNumAxes(SDL_GameControllerGetJoystick(platform.gamepad[jid])); CORE.Input.Gamepad.axisState[jid][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f; CORE.Input.Gamepad.axisState[jid][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f; + memset(CORE.Input.Gamepad.name[jid], 0, MAX_GAMEPAD_NAME_LENGTH); strncpy(CORE.Input.Gamepad.name[jid], SDL_GameControllerNameForIndex(jid), MAX_GAMEPAD_NAME_LENGTH - 1); - CORE.Input.Gamepad.name[jid][MAX_GAMEPAD_NAME_LENGTH - 1] = '\0'; } else { diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 619d05dcd..06d3b5d5a 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -948,7 +948,7 @@ int InitPlatform(void) TRACELOG(LOG_TRACE, "DISPLAY: EGL configs available: %d", numConfigs); - EGLConfig *configs = RL_CALLOC(numConfigs, sizeof(*configs)); + EGLConfig *configs = (EGLConfig *)RL_CALLOC(numConfigs, sizeof(*configs)); if (!configs) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get memory for EGL configs"); diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 62e4e94cc..046b79187 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -163,13 +163,13 @@ bool WindowShouldClose(void) // REF: https://emscripten.org/docs/porting/asyncify.html // WindowShouldClose() is not called on a web-ready raylib application if using emscripten_set_main_loop() - // and encapsulating one frame execution on a UpdateDrawFrame() function, + // and encapsulating one frame execution on a UpdateDrawFrame() function, // allowing the browser to manage execution asynchronously // Optionally we can manage the time we give-control-back-to-browser if required, // but it seems below line could generate stuttering on some browsers emscripten_sleep(12); - + return false; } diff --git a/src/raudio.c b/src/raudio.c index dd8f4a255..a9e47b0ec 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1337,7 +1337,7 @@ Music LoadMusicStream(const char *fileName) #if defined(SUPPORT_FILEFORMAT_WAV) else if (IsFileExtension(fileName, ".wav")) { - drwav *ctxWav = RL_CALLOC(1, sizeof(drwav)); + drwav *ctxWav = (drwav *)RL_CALLOC(1, sizeof(drwav)); bool success = drwav_init_file(ctxWav, fileName, NULL); if (success) @@ -1387,7 +1387,7 @@ Music LoadMusicStream(const char *fileName) #if defined(SUPPORT_FILEFORMAT_MP3) else if (IsFileExtension(fileName, ".mp3")) { - drmp3 *ctxMp3 = RL_CALLOC(1, sizeof(drmp3)); + drmp3 *ctxMp3 = (drmp3 *)RL_CALLOC(1, sizeof(drmp3)); int result = drmp3_init_file(ctxMp3, fileName, NULL); if (result > 0) @@ -1478,7 +1478,7 @@ Music LoadMusicStream(const char *fileName) #if defined(SUPPORT_FILEFORMAT_MOD) else if (IsFileExtension(fileName, ".mod")) { - jar_mod_context_t *ctxMod = RL_CALLOC(1, sizeof(jar_mod_context_t)); + jar_mod_context_t *ctxMod = (jar_mod_context_t *)RL_CALLOC(1, sizeof(jar_mod_context_t)); jar_mod_init(ctxMod); int result = jar_mod_load_file(ctxMod, fileName); @@ -1529,7 +1529,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, #if defined(SUPPORT_FILEFORMAT_WAV) else if ((strcmp(fileType, ".wav") == 0) || (strcmp(fileType, ".WAV") == 0)) { - drwav *ctxWav = RL_CALLOC(1, sizeof(drwav)); + drwav *ctxWav = (drwav *)RL_CALLOC(1, sizeof(drwav)); bool success = drwav_init_memory(ctxWav, (const void *)data, dataSize, NULL); @@ -1580,7 +1580,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, #if defined(SUPPORT_FILEFORMAT_MP3) else if ((strcmp(fileType, ".mp3") == 0) || (strcmp(fileType, ".MP3") == 0)) { - drmp3 *ctxMp3 = RL_CALLOC(1, sizeof(drmp3)); + drmp3 *ctxMp3 = (drmp3 *)RL_CALLOC(1, sizeof(drmp3)); int success = drmp3_init_memory(ctxMp3, (const void*)data, dataSize, NULL); if (success) diff --git a/src/rcore.c b/src/rcore.c index f216a197a..f8b061a50 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2500,7 +2500,7 @@ unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDa #if defined(SUPPORT_COMPRESSION_API) // Compress data and generate a valid DEFLATE stream - struct sdefl *sdefl = RL_CALLOC(1, sizeof(struct sdefl)); // WARNING: Possible stack overflow, struct sdefl is almost 1MB + struct sdefl *sdefl = (struct sdefl *)RL_CALLOC(1, sizeof(struct sdefl)); // WARNING: Possible stack overflow, struct sdefl is almost 1MB int bounds = sdefl_bound(dataSize); compData = (unsigned char *)RL_CALLOC(bounds, 1); @@ -2580,7 +2580,7 @@ char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize) outputCount += 4; i += 3; } - + // Add required padding bytes for (int p = 0; p < padding; p++) encodedData[outputCount - p - 1] = '='; @@ -2602,10 +2602,10 @@ unsigned char *DecodeDataBase64(const char *text, int *outputSize) // every character in the corresponding ASCII position static const unsigned char base64DecodeTable[256] = { ['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, ['E'] = 4, ['F'] = 5, ['G'] = 6, ['H'] = 7, - ['I'] = 8, ['J'] = 9, ['K'] = 10, ['L'] = 11, ['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15, + ['I'] = 8, ['J'] = 9, ['K'] = 10, ['L'] = 11, ['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15, ['Q'] = 16, ['R'] = 17, ['S'] = 18, ['T'] = 19, ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23, ['Y'] = 24, ['Z'] = 25, - ['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29, ['e'] = 30, ['f'] = 31, ['g'] = 32, ['h'] = 33, - ['i'] = 34, ['j'] = 35, ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39, ['o'] = 40, ['p'] = 41, + ['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29, ['e'] = 30, ['f'] = 31, ['g'] = 32, ['h'] = 33, + ['i'] = 34, ['j'] = 35, ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39, ['o'] = 40, ['p'] = 41, ['q'] = 42, ['r'] = 43, ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47, ['w'] = 48, ['x'] = 49, ['y'] = 50, ['z'] = 51, ['0'] = 52, ['1'] = 53, ['2'] = 54, ['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59, ['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63 @@ -2747,7 +2747,7 @@ unsigned int *ComputeMD5(unsigned char *data, int dataSize) int newDataSize = ((((dataSize + 8)/64) + 1)*64) - 8; - unsigned char *msg = RL_CALLOC(newDataSize + 64, 1); // Initialize with '0' bits, allocating 64 extra bytes + unsigned char *msg = (unsigned char *)RL_CALLOC(newDataSize + 64, 1); // Initialize with '0' bits, allocating 64 extra bytes memcpy(msg, data, dataSize); msg[dataSize] = 128; // Write the '1' bit @@ -2837,7 +2837,7 @@ unsigned int *ComputeSHA1(unsigned char *data, int dataSize) int newDataSize = ((((dataSize + 8)/64) + 1)*64); - unsigned char *msg = RL_CALLOC(newDataSize, 1); // Initialize with '0' bits + unsigned char *msg = (unsigned char *)RL_CALLOC(newDataSize, 1); // Initialize with '0' bits memcpy(msg, data, dataSize); msg[dataSize] = 128; // Write the '1' bit diff --git a/src/rlgl.h b/src/rlgl.h index d595f4606..686260d3b 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2425,7 +2425,7 @@ void rlLoadExtensions(void *loader) // Get supported extensions list GLint numExt = 0; - const char **extList = RL_MALLOC(512*sizeof(const char *)); // Allocate 512 strings pointers (2 KB) + const char **extList = (char **)RL_MALLOC(512*sizeof(const char *)); // Allocate 512 strings pointers (2 KB) const char *extensions = (const char *)glGetString(GL_EXTENSIONS); // One big const string // NOTE: We have to duplicate string because glGetString() returns a const string diff --git a/src/rmodels.c b/src/rmodels.c index 5f3fd8993..84daabc15 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2174,7 +2174,7 @@ Material *LoadMaterials(const char *fileName, int *materialCount) int result = tinyobj_parse_mtl_file(&mats, &count, fileName); if (result != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to parse materials file", fileName); - materials = RL_MALLOC(count*sizeof(Material)); + materials = (Material *)RL_MALLOC(count*sizeof(Material)); ProcessMaterialsOBJ(materials, mats, count); tinyobj_materials_free(mats, count); @@ -3713,7 +3713,7 @@ void GenMeshTangents(Mesh *mesh) // Create a tangent perpendicular to the normal if (fabsf(normal.z) > 0.707f) tangent = (Vector3){ 1.0f, 0.0f, 0.0f }; else tangent = Vector3Normalize((Vector3){ -normal.y, normal.x, 0.0f }); - + mesh->tangents[i*4 + 0] = tangent.x; mesh->tangents[i*4 + 1] = tangent.y; mesh->tangents[i*4 + 2] = tangent.z; @@ -3724,7 +3724,7 @@ void GenMeshTangents(Mesh *mesh) // Gram-Schmidt orthogonalization to make tangent orthogonal to normal // T_prime = T - N * dot(N, T) Vector3 orthogonalized = Vector3Subtract(tangent, Vector3Scale(normal, Vector3DotProduct(normal, tangent))); - + // Handle cases where orthogonalized vector is too small if (Vector3Length(orthogonalized) < 0.0001f) { @@ -3742,7 +3742,7 @@ void GenMeshTangents(Mesh *mesh) mesh->tangents[i*4 + 0] = orthogonalized.x; mesh->tangents[i*4 + 1] = orthogonalized.y; mesh->tangents[i*4 + 2] = orthogonalized.z; - + // Calculate the handedness (w component) mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, orthogonalized), tan2[i]) < 0.0f)? -1.0f : 1.0f; } @@ -4642,13 +4642,13 @@ static Model LoadIQM(const char *fileName) //fileDataPtr += sizeof(IQMHeader); // Move file data pointer // Meshes data processing - imesh = RL_MALLOC(iqmHeader->num_meshes*sizeof(IQMMesh)); + imesh = (IQMMesh *)RL_MALLOC(iqmHeader->num_meshes*sizeof(IQMMesh)); //fseek(iqmFile, iqmHeader->ofs_meshes, SEEK_SET); //fread(imesh, sizeof(IQMMesh)*iqmHeader->num_meshes, 1, iqmFile); memcpy(imesh, fileDataPtr + iqmHeader->ofs_meshes, iqmHeader->num_meshes*sizeof(IQMMesh)); model.meshCount = iqmHeader->num_meshes; - model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh)); + model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); model.materialCount = model.meshCount; model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); @@ -4676,24 +4676,24 @@ static Model LoadIQM(const char *fileName) model.meshes[i].vertexCount = imesh[i].num_vertexes; - model.meshes[i].vertices = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); // Default vertex positions - model.meshes[i].normals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); // Default vertex normals - model.meshes[i].texcoords = RL_CALLOC(model.meshes[i].vertexCount*2, sizeof(float)); // Default vertex texcoords + model.meshes[i].vertices = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); // Default vertex positions + model.meshes[i].normals = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); // Default vertex normals + model.meshes[i].texcoords = (float *)RL_CALLOC(model.meshes[i].vertexCount*2, sizeof(float)); // Default vertex texcoords - model.meshes[i].boneIds = RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(unsigned char)); // Up-to 4 bones supported! - model.meshes[i].boneWeights = RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(float)); // Up-to 4 bones supported! + model.meshes[i].boneIds = (unsigned char *)RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(unsigned char)); // Up-to 4 bones supported! + model.meshes[i].boneWeights = (float *)RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(float)); // Up-to 4 bones supported! model.meshes[i].triangleCount = imesh[i].num_triangles; - model.meshes[i].indices = RL_CALLOC(model.meshes[i].triangleCount*3, sizeof(unsigned short)); + model.meshes[i].indices = (unsigned short *)RL_CALLOC(model.meshes[i].triangleCount*3, sizeof(unsigned short)); // Animated vertex data, what we actually process for rendering // NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning) - model.meshes[i].animVertices = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); - model.meshes[i].animNormals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); + model.meshes[i].animVertices = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); + model.meshes[i].animNormals = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); } // Triangles data processing - tri = RL_MALLOC(iqmHeader->num_triangles*sizeof(IQMTriangle)); + tri = (IQMTriangle *)RL_MALLOC(iqmHeader->num_triangles*sizeof(IQMTriangle)); //fseek(iqmFile, iqmHeader->ofs_triangles, SEEK_SET); //fread(tri, sizeof(IQMTriangle), iqmHeader->num_triangles, iqmFile); memcpy(tri, fileDataPtr + iqmHeader->ofs_triangles, iqmHeader->num_triangles*sizeof(IQMTriangle)); @@ -4715,7 +4715,7 @@ static Model LoadIQM(const char *fileName) } // Vertex arrays data processing - va = RL_MALLOC(iqmHeader->num_vertexarrays*sizeof(IQMVertexArray)); + va = (IQMVertexArray *)RL_MALLOC(iqmHeader->num_vertexarrays*sizeof(IQMVertexArray)); //fseek(iqmFile, iqmHeader->ofs_vertexarrays, SEEK_SET); //fread(va, sizeof(IQMVertexArray), iqmHeader->num_vertexarrays, iqmFile); memcpy(va, fileDataPtr + iqmHeader->ofs_vertexarrays, iqmHeader->num_vertexarrays*sizeof(IQMVertexArray)); @@ -4726,7 +4726,7 @@ static Model LoadIQM(const char *fileName) { case IQM_POSITION: { - vertex = RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float)); + vertex = (float *)RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float)); //fseek(iqmFile, va[i].offset, SEEK_SET); //fread(vertex, iqmHeader->num_vertexes*3*sizeof(float), 1, iqmFile); memcpy(vertex, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*3*sizeof(float)); @@ -4744,7 +4744,7 @@ static Model LoadIQM(const char *fileName) } break; case IQM_NORMAL: { - normal = RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float)); + normal = (float *)RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float)); //fseek(iqmFile, va[i].offset, SEEK_SET); //fread(normal, iqmHeader->num_vertexes*3*sizeof(float), 1, iqmFile); memcpy(normal, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*3*sizeof(float)); @@ -4762,7 +4762,7 @@ static Model LoadIQM(const char *fileName) } break; case IQM_TEXCOORD: { - text = RL_MALLOC(iqmHeader->num_vertexes*2*sizeof(float)); + text = (float *)RL_MALLOC(iqmHeader->num_vertexes*2*sizeof(float)); //fseek(iqmFile, va[i].offset, SEEK_SET); //fread(text, iqmHeader->num_vertexes*2*sizeof(float), 1, iqmFile); memcpy(text, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*2*sizeof(float)); @@ -4779,7 +4779,7 @@ static Model LoadIQM(const char *fileName) } break; case IQM_BLENDINDEXES: { - blendi = RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(char)); + blendi = (char *)RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(char)); //fseek(iqmFile, va[i].offset, SEEK_SET); //fread(blendi, iqmHeader->num_vertexes*4*sizeof(char), 1, iqmFile); memcpy(blendi, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(char)); @@ -4796,7 +4796,7 @@ static Model LoadIQM(const char *fileName) } break; case IQM_BLENDWEIGHTS: { - blendw = RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char)); + blendw = (unsigned char *)RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char)); //fseek(iqmFile, va[i].offset, SEEK_SET); //fread(blendw, iqmHeader->num_vertexes*4*sizeof(unsigned char), 1, iqmFile); memcpy(blendw, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(unsigned char)); @@ -4813,14 +4813,14 @@ static Model LoadIQM(const char *fileName) } break; case IQM_COLOR: { - color = RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char)); + color = (unsigned char *)RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char)); //fseek(iqmFile, va[i].offset, SEEK_SET); //fread(blendw, iqmHeader->num_vertexes*4*sizeof(unsigned char), 1, iqmFile); memcpy(color, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(unsigned char)); for (unsigned int m = 0; m < iqmHeader->num_meshes; m++) { - model.meshes[m].colors = RL_CALLOC(model.meshes[m].vertexCount*4, sizeof(unsigned char)); + model.meshes[m].colors = (unsigned char *)RL_CALLOC(model.meshes[m].vertexCount*4, sizeof(unsigned char)); int vCounter = 0; for (unsigned int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++) @@ -4834,14 +4834,14 @@ static Model LoadIQM(const char *fileName) } // Bones (joints) data processing - ijoint = RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint)); + ijoint = (IQMJoint *)RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint)); //fseek(iqmFile, iqmHeader->ofs_joints, SEEK_SET); //fread(ijoint, sizeof(IQMJoint), iqmHeader->num_joints, iqmFile); memcpy(ijoint, fileDataPtr + iqmHeader->ofs_joints, iqmHeader->num_joints*sizeof(IQMJoint)); model.boneCount = iqmHeader->num_joints; - model.bones = RL_MALLOC(iqmHeader->num_joints*sizeof(BoneInfo)); - model.bindPose = RL_MALLOC(iqmHeader->num_joints*sizeof(Transform)); + model.bones = (BoneInfo *)RL_MALLOC(iqmHeader->num_joints*sizeof(BoneInfo)); + model.bindPose = (Transform *)RL_MALLOC(iqmHeader->num_joints*sizeof(Transform)); for (unsigned int i = 0; i < iqmHeader->num_joints; i++) { @@ -4871,7 +4871,7 @@ static Model LoadIQM(const char *fileName) for (int i = 0; i < model.meshCount; i++) { model.meshes[i].boneCount = model.boneCount; - model.meshes[i].boneMatrices = RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix)); + model.meshes[i].boneMatrices = (Matrix *)RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix)); for (int j = 0; j < model.meshes[i].boneCount; j++) { @@ -4963,36 +4963,36 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou } // Get bones data - IQMPose *poses = RL_MALLOC(iqmHeader->num_poses*sizeof(IQMPose)); + IQMPose *poses = (IQMPose *)RL_MALLOC(iqmHeader->num_poses*sizeof(IQMPose)); //fseek(iqmFile, iqmHeader->ofs_poses, SEEK_SET); //fread(poses, sizeof(IQMPose), iqmHeader->num_poses, iqmFile); memcpy(poses, fileDataPtr + iqmHeader->ofs_poses, iqmHeader->num_poses*sizeof(IQMPose)); // Get animations data *animCount = iqmHeader->num_anims; - IQMAnim *anim = RL_MALLOC(iqmHeader->num_anims*sizeof(IQMAnim)); + IQMAnim *anim = (IQMAnim *)RL_MALLOC(iqmHeader->num_anims*sizeof(IQMAnim)); //fseek(iqmFile, iqmHeader->ofs_anims, SEEK_SET); //fread(anim, sizeof(IQMAnim), iqmHeader->num_anims, iqmFile); memcpy(anim, fileDataPtr + iqmHeader->ofs_anims, iqmHeader->num_anims*sizeof(IQMAnim)); - ModelAnimation *animations = RL_MALLOC(iqmHeader->num_anims*sizeof(ModelAnimation)); + ModelAnimation *animations = (ModelAnimation *)RL_MALLOC(iqmHeader->num_anims*sizeof(ModelAnimation)); // frameposes - unsigned short *framedata = RL_MALLOC(iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short)); + unsigned short *framedata = (unsigned short *)RL_MALLOC(iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short)); //fseek(iqmFile, iqmHeader->ofs_frames, SEEK_SET); //fread(framedata, sizeof(unsigned short), iqmHeader->num_frames*iqmHeader->num_framechannels, iqmFile); memcpy(framedata, fileDataPtr + iqmHeader->ofs_frames, iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short)); // joints - IQMJoint *joints = RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint)); + IQMJoint *joints = (IQMJoint *)RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint)); memcpy(joints, fileDataPtr + iqmHeader->ofs_joints, iqmHeader->num_joints*sizeof(IQMJoint)); for (unsigned int a = 0; a < iqmHeader->num_anims; a++) { animations[a].frameCount = anim[a].num_frames; animations[a].boneCount = iqmHeader->num_poses; - animations[a].bones = RL_MALLOC(iqmHeader->num_poses*sizeof(BoneInfo)); - animations[a].framePoses = RL_MALLOC(anim[a].num_frames*sizeof(Transform *)); + animations[a].bones = (BoneInfo *)RL_MALLOC(iqmHeader->num_poses*sizeof(BoneInfo)); + animations[a].framePoses = (Transform **)RL_MALLOC(anim[a].num_frames*sizeof(Transform *)); memcpy(animations[a].name, fileDataPtr + iqmHeader->ofs_text + anim[a].name, 32); // I don't like this 32 here TraceLog(LOG_INFO, "IQM Anim %s", animations[a].name); // animations[a].framerate = anim.framerate; // TODO: Use animation framerate data? @@ -5007,7 +5007,7 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou animations[a].bones[j].parent = poses[j].parent; } - for (unsigned int j = 0; j < anim[a].num_frames; j++) animations[a].framePoses[j] = RL_MALLOC(iqmHeader->num_poses*sizeof(Transform)); + for (unsigned int j = 0; j < anim[a].num_frames; j++) animations[a].framePoses[j] = (Transform *)RL_MALLOC(iqmHeader->num_poses*sizeof(Transform)); int dcounter = anim[a].first_frame*iqmHeader->num_framechannels; @@ -5198,7 +5198,7 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat } else if ((cgltfImage->buffer_view != NULL) && (cgltfImage->buffer_view->buffer->data != NULL)) // Check if image is provided as data buffer { - unsigned char *data = RL_MALLOC(cgltfImage->buffer_view->size); + unsigned char *data = (unsigned char *)RL_MALLOC(cgltfImage->buffer_view->size); int offset = (int)cgltfImage->buffer_view->offset; int stride = (int)cgltfImage->buffer_view->stride? (int)cgltfImage->buffer_view->stride : 1; @@ -5231,16 +5231,12 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat static BoneInfo *LoadBoneInfoGLTF(cgltf_skin skin, int *boneCount) { *boneCount = (int)skin.joints_count; - BoneInfo *bones = RL_MALLOC(skin.joints_count*sizeof(BoneInfo)); + BoneInfo *bones = (BoneInfo *)RL_CALLOC(skin.joints_count, sizeof(BoneInfo)); for (unsigned int i = 0; i < skin.joints_count; i++) { cgltf_node node = *skin.joints[i]; - if (node.name != NULL) - { - strncpy(bones[i].name, node.name, sizeof(bones[i].name)); - bones[i].name[sizeof(bones[i].name) - 1] = '\0'; - } + if (node.name != NULL) strncpy(bones[i].name, node.name, sizeof(bones[i].name) - 1); // Find parent bone index int parentIndex = -1; @@ -5362,15 +5358,15 @@ static Model LoadGLTF(const char *fileName) // Load our model data: meshes and materials model.meshCount = primitivesCount; - model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh)); + model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); // NOTE: We keep an extra slot for default material, in case some mesh requires it model.materialCount = (int)data->materials_count + 1; - model.materials = RL_CALLOC(model.materialCount, sizeof(Material)); + model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); model.materials[0] = LoadMaterialDefault(); // Load default material (index: 0) // Load mesh-material indices, by default all meshes are mapped to material index: 0 - model.meshMaterial = RL_CALLOC(model.meshCount, sizeof(int)); + model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); // Load materials data //---------------------------------------------------------------------------------------------------- @@ -5540,7 +5536,7 @@ static Model LoadGLTF(const char *fileName) { // Init raylib mesh vertices to copy glTF attribute data model.meshes[meshIndex].vertexCount = (int)attribute->count; - model.meshes[meshIndex].vertices = RL_MALLOC(attribute->count*3*sizeof(float)); + model.meshes[meshIndex].vertices = (float *)RL_MALLOC(attribute->count*3*sizeof(float)); // Load 3 components of float data type into mesh.vertices LOAD_ATTRIBUTE(attribute, 3, float, model.meshes[meshIndex].vertices) @@ -5564,7 +5560,7 @@ static Model LoadGLTF(const char *fileName) if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_32f)) { // Init raylib mesh normals to copy glTF attribute data - model.meshes[meshIndex].normals = RL_MALLOC(attribute->count*3*sizeof(float)); + model.meshes[meshIndex].normals = (float *)RL_MALLOC(attribute->count*3*sizeof(float)); // Load 3 components of float data type into mesh.normals LOAD_ATTRIBUTE(attribute, 3, float, model.meshes[meshIndex].normals) @@ -5588,7 +5584,7 @@ static Model LoadGLTF(const char *fileName) if ((attribute->type == cgltf_type_vec4) && (attribute->component_type == cgltf_component_type_r_32f)) { // Init raylib mesh tangent to copy glTF attribute data - model.meshes[meshIndex].tangents = RL_MALLOC(attribute->count*4*sizeof(float)); + model.meshes[meshIndex].tangents = (float *)RL_MALLOC(attribute->count*4*sizeof(float)); // Load 4 components of float data type into mesh.tangents LOAD_ATTRIBUTE(attribute, 4, float, model.meshes[meshIndex].tangents) @@ -5674,10 +5670,10 @@ static Model LoadGLTF(const char *fileName) if (attribute->component_type == cgltf_component_type_r_8u) { // Init raylib mesh color to copy glTF attribute data - model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); + model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char)); // Load data into a temp buffer to be converted to raylib data type - unsigned char *temp = RL_MALLOC(attribute->count*3*sizeof(unsigned char)); + unsigned char *temp = (unsigned char *)RL_MALLOC(attribute->count*3*sizeof(unsigned char)); LOAD_ATTRIBUTE(attribute, 3, unsigned char, temp); // Convert data to raylib color data type (4 bytes) @@ -5694,10 +5690,10 @@ static Model LoadGLTF(const char *fileName) else if (attribute->component_type == cgltf_component_type_r_16u) { // Init raylib mesh color to copy glTF attribute data - model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); + model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char)); // Load data into a temp buffer to be converted to raylib data type - unsigned short *temp = RL_MALLOC(attribute->count*3*sizeof(unsigned short)); + unsigned short *temp = (unsigned short *)RL_MALLOC(attribute->count*3*sizeof(unsigned short)); LOAD_ATTRIBUTE(attribute, 3, unsigned short, temp); // Convert data to raylib color data type (4 bytes) @@ -5714,10 +5710,10 @@ static Model LoadGLTF(const char *fileName) else if (attribute->component_type == cgltf_component_type_r_32f) { // Init raylib mesh color to copy glTF attribute data - model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); + model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char)); // Load data into a temp buffer to be converted to raylib data type - float *temp = RL_MALLOC(attribute->count*3*sizeof(float)); + float *temp = (float *)RL_MALLOC(attribute->count*3*sizeof(float)); LOAD_ATTRIBUTE(attribute, 3, float, temp); // Convert data to raylib color data type (4 bytes) @@ -5738,7 +5734,7 @@ static Model LoadGLTF(const char *fileName) if (attribute->component_type == cgltf_component_type_r_8u) { // Init raylib mesh color to copy glTF attribute data - model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); + model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char)); // Load 4 components of unsigned char data type into mesh.colors LOAD_ATTRIBUTE(attribute, 4, unsigned char, model.meshes[meshIndex].colors) @@ -5746,10 +5742,10 @@ static Model LoadGLTF(const char *fileName) else if (attribute->component_type == cgltf_component_type_r_16u) { // Init raylib mesh color to copy glTF attribute data - model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); + model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char)); // Load data into a temp buffer to be converted to raylib data type - unsigned short *temp = RL_MALLOC(attribute->count*4*sizeof(unsigned short)); + unsigned short *temp = (unsigned short *)RL_MALLOC(attribute->count*4*sizeof(unsigned short)); LOAD_ATTRIBUTE(attribute, 4, unsigned short, temp); // Convert data to raylib color data type (4 bytes) @@ -5760,10 +5756,10 @@ static Model LoadGLTF(const char *fileName) else if (attribute->component_type == cgltf_component_type_r_32f) { // Init raylib mesh color to copy glTF attribute data - model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); + model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char)); // Load data into a temp buffer to be converted to raylib data type - float *temp = RL_MALLOC(attribute->count*4*sizeof(float)); + float *temp = (float *)RL_MALLOC(attribute->count*4*sizeof(float)); LOAD_ATTRIBUTE(attribute, 4, float, temp); // Convert data to raylib color data type (4 bytes), we expect the color data normalized @@ -5789,7 +5785,7 @@ static Model LoadGLTF(const char *fileName) if (attribute->component_type == cgltf_component_type_r_16u) { // Init raylib mesh indices to copy glTF attribute data - model.meshes[meshIndex].indices = RL_MALLOC(attribute->count*sizeof(unsigned short)); + model.meshes[meshIndex].indices = (unsigned short *)RL_MALLOC(attribute->count*sizeof(unsigned short)); // Load unsigned short data type into mesh.indices LOAD_ATTRIBUTE(attribute, 1, unsigned short, model.meshes[meshIndex].indices) @@ -5797,14 +5793,14 @@ static Model LoadGLTF(const char *fileName) else if (attribute->component_type == cgltf_component_type_r_8u) { // Init raylib mesh indices to copy glTF attribute data - model.meshes[meshIndex].indices = RL_MALLOC(attribute->count*sizeof(unsigned short)); + model.meshes[meshIndex].indices = (unsigned short *)RL_MALLOC(attribute->count*sizeof(unsigned short)); LOAD_ATTRIBUTE_CAST(attribute, 1, unsigned char, model.meshes[meshIndex].indices, unsigned short) } else if (attribute->component_type == cgltf_component_type_r_32u) { // Init raylib mesh indices to copy glTF attribute data - model.meshes[meshIndex].indices = RL_MALLOC(attribute->count*sizeof(unsigned short)); + model.meshes[meshIndex].indices = (unsigned short *)RL_MALLOC(attribute->count*sizeof(unsigned short)); LOAD_ATTRIBUTE_CAST(attribute, 1, unsigned int, model.meshes[meshIndex].indices, unsigned short); TRACELOG(LOG_WARNING, "MODEL: [%s] Indices data converted from u32 to u16, possible loss of data", fileName); @@ -5848,7 +5844,7 @@ static Model LoadGLTF(const char *fileName) { cgltf_skin skin = data->skins[0]; model.bones = LoadBoneInfoGLTF(skin, &model.boneCount); - model.bindPose = RL_MALLOC(model.boneCount*sizeof(Transform)); + model.bindPose = (Transform *)RL_MALLOC(model.boneCount*sizeof(Transform)); for (int i = 0; i < model.boneCount; i++) { @@ -5905,7 +5901,7 @@ static Model LoadGLTF(const char *fileName) if (attribute->component_type == cgltf_component_type_r_8u) { // Init raylib mesh boneIds to copy glTF attribute data - model.meshes[meshIndex].boneIds = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char)); + model.meshes[meshIndex].boneIds = (unsigned char *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char)); // Load attribute: vec4, u8 (unsigned char) LOAD_ATTRIBUTE(attribute, 4, unsigned char, model.meshes[meshIndex].boneIds) @@ -5913,10 +5909,10 @@ static Model LoadGLTF(const char *fileName) else if (attribute->component_type == cgltf_component_type_r_16u) { // Init raylib mesh boneIds to copy glTF attribute data - model.meshes[meshIndex].boneIds = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char)); + model.meshes[meshIndex].boneIds = (unsigned char *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char)); // Load data into a temp buffer to be converted to raylib data type - unsigned short *temp = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned short)); + unsigned short *temp = (unsigned short *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned short)); LOAD_ATTRIBUTE(attribute, 4, unsigned short, temp); // Convert data to raylib color data type (4 bytes) @@ -5948,10 +5944,10 @@ static Model LoadGLTF(const char *fileName) if (attribute->component_type == cgltf_component_type_r_8u) { // Init raylib mesh bone weight to copy glTF attribute data - model.meshes[meshIndex].boneWeights = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float)); + model.meshes[meshIndex].boneWeights = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float)); // Load data into a temp buffer to be converted to raylib data type - unsigned char *temp = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); + unsigned char *temp = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char)); LOAD_ATTRIBUTE(attribute, 4, unsigned char, temp); // Convert data to raylib bone weight data type (4 bytes) @@ -5962,10 +5958,10 @@ static Model LoadGLTF(const char *fileName) else if (attribute->component_type == cgltf_component_type_r_16u) { // Init raylib mesh bone weight to copy glTF attribute data - model.meshes[meshIndex].boneWeights = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float)); + model.meshes[meshIndex].boneWeights = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float)); // Load data into a temp buffer to be converted to raylib data type - unsigned short *temp = RL_MALLOC(attribute->count*4*sizeof(unsigned short)); + unsigned short *temp = (unsigned short *)RL_MALLOC(attribute->count*4*sizeof(unsigned short)); LOAD_ATTRIBUTE(attribute, 4, unsigned short, temp); // Convert data to raylib bone weight data type @@ -5976,7 +5972,7 @@ static Model LoadGLTF(const char *fileName) else if (attribute->component_type == cgltf_component_type_r_32f) { // Init raylib mesh bone weight to copy glTF attribute data - model.meshes[meshIndex].boneWeights = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float)); + model.meshes[meshIndex].boneWeights = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float)); // Load 4 components of float data type into mesh.boneWeights // for cgltf_attribute_type_weights we have: @@ -6007,8 +6003,8 @@ static Model LoadGLTF(const char *fileName) if (parentBoneId >= 0) { - model.meshes[meshIndex].boneIds = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char)); - model.meshes[meshIndex].boneWeights = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float)); + model.meshes[meshIndex].boneIds = (unsigned char *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char)); + model.meshes[meshIndex].boneWeights = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float)); for (int vertexIndex = 0; vertexIndex < model.meshes[meshIndex].vertexCount*4; vertexIndex += 4) { @@ -6019,9 +6015,9 @@ static Model LoadGLTF(const char *fileName) } // Animated vertex data - model.meshes[meshIndex].animVertices = RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float)); + model.meshes[meshIndex].animVertices = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float)); memcpy(model.meshes[meshIndex].animVertices, model.meshes[meshIndex].vertices, model.meshes[meshIndex].vertexCount*3*sizeof(float)); - model.meshes[meshIndex].animNormals = RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float)); + model.meshes[meshIndex].animNormals = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float)); if (model.meshes[meshIndex].normals != NULL) { memcpy(model.meshes[meshIndex].animNormals, model.meshes[meshIndex].normals, model.meshes[meshIndex].vertexCount*3*sizeof(float)); @@ -6029,7 +6025,7 @@ static Model LoadGLTF(const char *fileName) // Bone Transform Matrices model.meshes[meshIndex].boneCount = model.boneCount; - model.meshes[meshIndex].boneMatrices = RL_CALLOC(model.meshes[meshIndex].boneCount, sizeof(Matrix)); + model.meshes[meshIndex].boneMatrices = (Matrix *)RL_CALLOC(model.meshes[meshIndex].boneCount, sizeof(Matrix)); for (int j = 0; j < model.meshes[meshIndex].boneCount; j++) { @@ -6219,7 +6215,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo { cgltf_skin skin = data->skins[0]; *animCount = (int)data->animations_count; - animations = RL_MALLOC(data->animations_count*sizeof(ModelAnimation)); + animations = (ModelAnimation *)RL_CALLOC(data->animations_count, sizeof(ModelAnimation)); for (unsigned int i = 0; i < data->animations_count; i++) { @@ -6234,7 +6230,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo cgltf_interpolation_type interpolationType; }; - struct Channels *boneChannels = RL_CALLOC(animations[i].boneCount, sizeof(struct Channels)); + struct Channels *boneChannels = (struct Channels *)RL_CALLOC(animations[i].boneCount, sizeof(struct Channels)); float animDuration = 0.0f; for (unsigned int j = 0; j < animData.channels_count; j++) @@ -6292,18 +6288,14 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo animDuration = (t > animDuration)? t : animDuration; } - if (animData.name != NULL) - { - strncpy(animations[i].name, animData.name, sizeof(animations[i].name)); - animations[i].name[sizeof(animations[i].name) - 1] = '\0'; - } + if (animData.name != NULL) strncpy(animations[i].name, animData.name, sizeof(animations[i].name) - 1); animations[i].frameCount = (int)(animDuration*1000.0f/GLTF_ANIMDELAY) + 1; - animations[i].framePoses = RL_MALLOC(animations[i].frameCount*sizeof(Transform *)); + animations[i].framePoses = (Transform **)RL_MALLOC(animations[i].frameCount*sizeof(Transform *)); for (int j = 0; j < animations[i].frameCount; j++) { - animations[i].framePoses[j] = RL_MALLOC(animations[i].boneCount*sizeof(Transform)); + animations[i].framePoses[j] = (Transform *)RL_MALLOC(animations[i].boneCount*sizeof(Transform)); float time = ((float) j*GLTF_ANIMDELAY)/1000.0f; for (int k = 0; k < animations[i].boneCount; k++) @@ -6453,7 +6445,7 @@ static Model LoadVOX(const char *fileName) // Copy colors size = pmesh->vertexCount*sizeof(Color); - pmesh->colors = RL_MALLOC(size); + pmesh->colors = (unsigned char *)RL_MALLOC(size); memcpy(pmesh->colors, pcolors, size); // First material index @@ -6589,7 +6581,7 @@ static Model LoadM3D(const char *fileName) // If no map is provided, or we have colors defined, we allocate storage for vertex colors // M3D specs only consider vertex colors if no material is provided, however raylib uses both and mixes the colors - if ((mi == M3D_UNDEF) || vcolor) model.meshes[k].colors = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); + if ((mi == M3D_UNDEF) || vcolor) model.meshes[k].colors = (unsigned char *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); // If no map is provided and we allocated vertex colors, set them to white if ((mi == M3D_UNDEF) && (model.meshes[k].colors != NULL)) @@ -6756,13 +6748,13 @@ static Model LoadM3D(const char *fileName) if (m3d->numbone) { model.boneCount = m3d->numbone + 1; - model.bones = RL_CALLOC(model.boneCount, sizeof(BoneInfo)); - model.bindPose = RL_CALLOC(model.boneCount, sizeof(Transform)); + model.bones = (BoneInfo *)RL_CALLOC(model.boneCount, sizeof(BoneInfo)); + model.bindPose = (Transform *)RL_CALLOC(model.boneCount, sizeof(Transform)); for (i = 0; i < (int)m3d->numbone; i++) { model.bones[i].parent = m3d->bone[i].parent; - strncpy(model.bones[i].name, m3d->bone[i].name, sizeof(model.bones[i].name)); + strncpy(model.bones[i].name, m3d->bone[i].name, sizeof(model.bones[i].name) - 1); model.bindPose[i].translation.x = m3d->vertex[m3d->bone[i].pos].x*m3d->scale; model.bindPose[i].translation.y = m3d->vertex[m3d->bone[i].pos].y*m3d->scale; model.bindPose[i].translation.z = m3d->vertex[m3d->bone[i].pos].z*m3d->scale; @@ -6808,7 +6800,7 @@ static Model LoadM3D(const char *fileName) memcpy(model.meshes[i].animNormals, model.meshes[i].normals, model.meshes[i].vertexCount*3*sizeof(float)); model.meshes[i].boneCount = model.boneCount; - model.meshes[i].boneMatrices = RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix)); + model.meshes[i].boneMatrices = (Matrix *)RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix)); for (j = 0; j < model.meshes[i].boneCount; j++) { model.meshes[i].boneMatrices[j] = MatrixIdentity(); @@ -6858,24 +6850,23 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou return NULL; } - animations = RL_MALLOC(m3d->numaction*sizeof(ModelAnimation)); + animations = (ModelAnimation *)RL_CALLOC(m3d->numaction, sizeof(ModelAnimation)); *animCount = m3d->numaction; for (unsigned int a = 0; a < m3d->numaction; a++) { animations[a].frameCount = m3d->action[a].durationmsec/M3D_ANIMDELAY; animations[a].boneCount = m3d->numbone + 1; - animations[a].bones = RL_MALLOC((m3d->numbone + 1)*sizeof(BoneInfo)); - animations[a].framePoses = RL_MALLOC(animations[a].frameCount*sizeof(Transform *)); - strncpy(animations[a].name, m3d->action[a].name, sizeof(animations[a].name)); - animations[a].name[sizeof(animations[a].name) - 1] = '\0'; + animations[a].bones = (BoneInfo *)RL_MALLOC((m3d->numbone + 1)*sizeof(BoneInfo)); + animations[a].framePoses = (Transform **)RL_MALLOC(animations[a].frameCount*sizeof(Transform *)); + strncpy(animations[a].name, m3d->action[a].name, sizeof(animations[a].name) - 1); TRACELOG(LOG_INFO, "MODEL: [%s] animation #%i: %i msec, %i frames", fileName, a, m3d->action[a].durationmsec, animations[a].frameCount); for (i = 0; i < (int)m3d->numbone; i++) { animations[a].bones[i].parent = m3d->bone[i].parent; - strncpy(animations[a].bones[i].name, m3d->bone[i].name, sizeof(animations[a].bones[i].name)); + strncpy(animations[a].bones[i].name, m3d->bone[i].name, sizeof(animations[a].bones[i].name) - 1); } // A special, never transformed "no bone" bone, used for boneless vertices @@ -6886,7 +6877,7 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou // regular intervals, so let the M3D SDK do the heavy lifting and calculate interpolated bones for (i = 0; i < animations[a].frameCount; i++) { - animations[a].framePoses[i] = RL_MALLOC((m3d->numbone + 1)*sizeof(Transform)); + animations[a].framePoses[i] = (Transform *)RL_MALLOC((m3d->numbone + 1)*sizeof(Transform)); m3db_t *pose = m3d_pose(m3d, a, i*M3D_ANIMDELAY); diff --git a/src/rtext.c b/src/rtext.c index ee498b119..d7818d2db 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -162,7 +162,7 @@ extern void LoadFontDefault(void) #define BIT_CHECK(a,b) ((a) & (1u << (b))) // check to see if we have allready allocated the font for an image, and if we don't need to upload, then just return - if (defaultFont.glyphs != NULL && !isGpuReady) + if (defaultFont.glyphs != NULL && !isGpuReady) return; // NOTE: Using UTF-8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement @@ -260,11 +260,11 @@ extern void LoadFontDefault(void) counter++; } - + if (isGpuReady) { defaultFont.texture = LoadTextureFromImage(imFont); - + // we have already loaded the font glyph data an image, and the GPU is ready, we are done // if we don't do this, we will leak memory by reallocating the glyphs and rects if (defaultFont.glyphs != NULL) diff --git a/src/rtextures.c b/src/rtextures.c index 7a5a5e57e..ac06d6d80 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -2102,8 +2102,8 @@ void ImageBlurGaussian(Image *image, int blurSize) Color *pixels = LoadImageColors(*image); // Loop switches between pixelsCopy1 and pixelsCopy2 - Vector4 *pixelsCopy1 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4)); - Vector4 *pixelsCopy2 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4)); + Vector4 *pixelsCopy1 = (Vector4 *)RL_MALLOC((image->height)*(image->width)*sizeof(Vector4)); + Vector4 *pixelsCopy2 = (Vector4 *)RL_MALLOC((image->height)*(image->width)*sizeof(Vector4)); for (int i = 0; i < (image->height*image->width); i++) { @@ -2251,8 +2251,8 @@ void ImageKernelConvolution(Image *image, const float *kernel, int kernelSize) Color *pixels = LoadImageColors(*image); - Vector4 *imageCopy2 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4)); - Vector4 *temp = RL_MALLOC(kernelSize*sizeof(Vector4)); + Vector4 *imageCopy2 = (Vector4 *)RL_MALLOC((image->height)*(image->width)*sizeof(Vector4)); + Vector4 *temp = (Vector4 *)RL_MALLOC(kernelSize*sizeof(Vector4)); for (int i = 0; i < kernelSize; i++) { From 3e336e4470f7975af67f716d4d809441883d7eef Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Jun 2025 19:52:35 +0200 Subject: [PATCH 038/242] Reviewed warning --- src/rlgl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rlgl.h b/src/rlgl.h index 686260d3b..c39d096fb 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2425,7 +2425,7 @@ void rlLoadExtensions(void *loader) // Get supported extensions list GLint numExt = 0; - const char **extList = (char **)RL_MALLOC(512*sizeof(const char *)); // Allocate 512 strings pointers (2 KB) + const char **extList = (const char **)RL_MALLOC(512*sizeof(const char *)); // Allocate 512 strings pointers (2 KB) const char *extensions = (const char *)glGetString(GL_EXTENSIONS); // One big const string // NOTE: We have to duplicate string because glGetString() returns a const string From 96c898852cfefe23a9f8e9658700ee9e0c2bb2eb Mon Sep 17 00:00:00 2001 From: M374LX Date: Thu, 12 Jun 2025 19:23:12 -0300 Subject: [PATCH 039/242] Update RGFW --- src/external/RGFW.h | 609 ++++++++++++++++++++++++++++++-------------- 1 file changed, 422 insertions(+), 187 deletions(-) diff --git a/src/external/RGFW.h b/src/external/RGFW.h index 1582fbfad..8bd9b5709 100644 --- a/src/external/RGFW.h +++ b/src/external/RGFW.h @@ -101,7 +101,7 @@ int main() { RGFW_window_swapBuffers(win); - glClearColor(1, 1, 1, 1); + glClearColor(1.0f, 1.0f, 1.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); } @@ -370,13 +370,13 @@ int main() { #if !defined(RGFW_NO_API) && (!defined(RGFW_BUFFER) || defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) #define RGFW_EGL #define RGFW_OPENGL - #define RGFW_UNIX #include #endif + #define RGFW_UNIX #include #endif -#if !defined(RGFW_NO_X11) && !defined(RGFW_NO_X11) && (defined(__unix__) || defined(RGFW_MACOS_X11) || defined(RGFW_X11)) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND) +#if !defined(RGFW_NO_X11) && (defined(__unix__) || defined(RGFW_MACOS_X11) || defined(RGFW_X11)) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND) #define RGFW_MACOS_X11 #define RGFW_X11 #define RGFW_UNIX @@ -945,6 +945,8 @@ RGFWDEF void RGFW_window_restore(RGFW_window* win); /*!< restore the window from RGFWDEF void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating); /*!< make the window a floating window */ RGFWDEF void RGFW_window_setOpacity(RGFW_window* win, u8 opacity); /*!< sets the opacity of a window */ +RGFWDEF RGFW_bool RGFW_window_opengl_isSoftware(RGFW_window* win); + /*! if the window should have a border or not (borderless) based on bool value of `border` */ RGFWDEF void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border); RGFWDEF RGFW_bool RGFW_window_borderless(RGFW_window* win); @@ -1278,7 +1280,7 @@ typedef void (*RGFW_proc)(void); /* function pointer equivalent of void* */ /*! native API functions */ #if defined(RGFW_OPENGL) || defined(RGFW_EGL) /*!< create an opengl context for the RGFW window, run by createWindow by default (unless the RGFW_windowNoInitAPI is included) */ -RGFWDEF void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software); +RGFWDEF void RGFW_window_initOpenGL(RGFW_window* win); /*!< called by `RGFW_window_close` by default (unless the RGFW_windowNoInitAPI is set) */ RGFWDEF void RGFW_window_freeOpenGL(RGFW_window* win); @@ -1443,12 +1445,13 @@ typedef RGFW_ENUM(u8, RGFW_key) { RGFW_period = '.', RGFW_comma = ',', RGFW_slash = '/', - RGFW_bracket = '{', - RGFW_closeBracket = '}', - RGFW_semicolon = ';', + RGFW_bracket = '[', + RGFW_closeBracket = ']', + RGFW_semicolon = ';', RGFW_apostrophe = '\'', RGFW_backSlash = '\\', RGFW_return = '\n', + RGFW_enter = RGFW_return, RGFW_delete = '\177', /* 127 */ @@ -1501,10 +1504,18 @@ typedef RGFW_ENUM(u8, RGFW_key) { RGFW_KP_Period, RGFW_KP_Return, RGFW_scrollLock, - RGFW_keyLast = 256 /* padding for alignment ~(175 by default) */ + RGFW_printScreen, + RGFW_pause, + RGFW_keyLast = 256 /* padding for alignment ~(175 by default) */ }; + +/*! converts api keycode to the RGFW unmapped/physical key */ RGFWDEF u32 RGFW_apiKeyToRGFW(u32 keycode); +/*! converts RGFW keycode to the unmapped/physical api key */ +RGFWDEF u32 RGFW_rgfwToApiKey(u32 keycode); +/*! converts RGFW keycode to the mapped keychar */ +RGFWDEF u8 RGFW_rgfwToKeyChar(u32 keycode); typedef RGFW_ENUM(u8, RGFW_mouseIcons) { RGFW_mouseNormal = 0, @@ -1541,8 +1552,10 @@ RGFW_bool RGFW_usingWayland(void) { return RGFW_useWaylandBool; } #if !defined(RGFW_NO_X11) && defined(RGFW_WAYLAND) #define RGFW_GOTO_WAYLAND(fallback) if (RGFW_useWaylandBool && fallback == 0) goto wayland +#define RGFW_WAYLAND_LABEL wayland:; #else #define RGFW_GOTO_WAYLAND(fallback) +#define RGFW_WAYLAND_LABEL #endif char* RGFW_clipboard_data; @@ -1626,11 +1639,6 @@ u64 RGFW_getTimeNS(void) { RGFW_IMPLEMENTATION starts with generic RGFW defines This is the start of keycode data - - Why not use macros instead of the numbers itself? - Windows -> Not all scancodes keys are macros - Linux -> Only symcodes are values, (XK_0 - XK_1, XK_a - XK_z) are larger than 0xFF00, I can't find any way to work with them without making the array an unreasonable size - MacOS -> windows and linux already don't have keycodes as macros, so there's no point */ @@ -1650,6 +1658,8 @@ This is the start of keycode data #define RGFW_MAP RGFW_keycodes #endif +u32 RGFW_apiKeycodes[RGFW_keyLast] = { 0 }; + u8 RGFW_keycodes [RGFW_OS_BASED_VALUE(256, 512, 128, 256)] = { #if defined(__cplusplus) || defined(RGFW_C89) 0 @@ -1748,7 +1758,7 @@ void RGFW_init_keys(void) { RGFW_MAP [RGFW_OS_BASED_VALUE(75, 0x043, 102, DOM_VK_F9)] = RGFW_F9 RGFW_NEXT RGFW_MAP [RGFW_OS_BASED_VALUE(76, 0x044, 110, DOM_VK_F10)] = RGFW_F10 RGFW_NEXT RGFW_MAP [RGFW_OS_BASED_VALUE(95, 0x057, 104, DOM_VK_F11)] = RGFW_F11 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(96, 0x058, 112, DOM_VK_F12)] = RGFW_F12 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(96, 0x058, 111, DOM_VK_F12)] = RGFW_F12 RGFW_NEXT RGFW_MAP [RGFW_OS_BASED_VALUE(111, 0x148, 126, DOM_VK_UP)] = RGFW_up RGFW_NEXT RGFW_MAP [RGFW_OS_BASED_VALUE(116, 0x150, 125, DOM_VK_DOWN)] = RGFW_down RGFW_NEXT RGFW_MAP [RGFW_OS_BASED_VALUE(113, 0x14B, 123, DOM_VK_LEFT)] = RGFW_left RGFW_NEXT @@ -1760,6 +1770,8 @@ void RGFW_init_keys(void) { RGFW_MAP [RGFW_OS_BASED_VALUE(9, 0x001, 53, DOM_VK_ESCAPE)] = RGFW_escape RGFW_NEXT RGFW_MAP [RGFW_OS_BASED_VALUE(110, 0x147, 116, DOM_VK_HOME)] = RGFW_home RGFW_NEXT RGFW_MAP [RGFW_OS_BASED_VALUE(78, 0x046, 107, DOM_VK_SCROLL_LOCK)] = RGFW_scrollLock RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(107, 0x137, 105, DOM_VK_PRINTSCREEN)] = RGFW_printScreen RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(128, 0x045, 113, DOM_VK_PAUSE)] = RGFW_pause RGFW_NEXT #if defined(__cplusplus) || defined(RGFW_C89) } #else @@ -1782,6 +1794,25 @@ u32 RGFW_apiKeyToRGFW(u32 keycode) { return RGFW_keycodes[keycode]; } + +u32 RGFW_rgfwToApiKey(u32 keycode) { + if (RGFW_apiKeycodes[RGFW_backtick] != RGFW_OS_BASED_VALUE(49, 0x029, 50, DOM_VK_BACK_QUOTE)) { + for (u32 i = 0; i < RGFW_keyLast; i++) { + for (u32 y = 0; y < sizeof(RGFW_keycodes); y++) { + if (RGFW_keycodes[y] == i) { + RGFW_apiKeycodes[i] = y; + break; + } + } + } + } + + /* make sure the key isn't out of bounds */ + if (keycode > sizeof(RGFW_apiKeycodes) / sizeof(u32)) + return 0; + + return RGFW_apiKeycodes[keycode]; +} #endif /* RGFW_CUSTOM_BACKEND */ typedef struct { @@ -1947,7 +1978,7 @@ typedef struct RGFW_globalStruct { #ifdef RGFW_WAYLAND struct wl_display* wl_display; #endif - #if defined(RGFW_X11) || defined(RGFW_WINDOWS) + #if defined(RGFW_X11) || defined(RGFW_WINDOWS) || defined(RGFW_WAYLAND) RGFW_mouse* hiddenMouse; #endif RGFW_event events[RGFW_MAX_EVENTS]; @@ -1964,7 +1995,7 @@ RGFW_globalStruct _RGFW; void RGFW_eventQueuePush(RGFW_event event) { if (_RGFW.eventLen >= RGFW_MAX_EVENTS) return; _RGFW.events[_RGFW.eventLen] = event; - _RGFW.eventLen++; + _RGFW.eventLen++; } RGFW_event* RGFW_eventQueuePop(RGFW_window* win) { @@ -1973,18 +2004,19 @@ RGFW_event* RGFW_eventQueuePop(RGFW_window* win) { ev = (RGFW_event*)&_RGFW.events[_RGFW.eventIndex]; - _RGFW.eventLen--; - if (_RGFW.eventLen && _RGFW.eventIndex < (_RGFW.eventLen - 1)) + _RGFW.eventLen--; + if (_RGFW.eventLen >= 0 && _RGFW.eventIndex < _RGFW.eventLen) { _RGFW.eventIndex++; - else if (_RGFW.eventLen == 0) - _RGFW.eventIndex = 0; + } else if (_RGFW.eventLen == 0) { + _RGFW.eventIndex = 0; + } if (ev->_win != win && ev->_win != NULL) { - RGFW_eventQueuePush(*ev); - return NULL; + RGFW_eventQueuePush(*ev); + return NULL; } - ev->droppedFilesCount = win->event.droppedFilesCount; + ev->droppedFilesCount = win->event.droppedFilesCount; ev->droppedFiles = win->event.droppedFiles; return ev; } @@ -2010,7 +2042,7 @@ RGFW_event* RGFW_window_checkEventCore(RGFW_window* win) { if (ev != NULL) { if (ev->type == RGFW_quit) RGFW_window_setShouldClose(win, RGFW_TRUE); win->event = *ev; - } + } else return NULL; return &win->event; @@ -2095,6 +2127,10 @@ void RGFW_window_setFlags(RGFW_window* win, RGFW_windowFlags flags) { win->_flags = flags | (win->_flags & RGFW_INTERNAL_FLAGS); } +RGFW_bool RGFW_window_opengl_isSoftware(RGFW_window* win) { + return RGFW_BOOL(win->_flags |= RGFW_windowOpenglSoftware); +} + RGFW_bool RGFW_window_isInFocus(RGFW_window* win) { #ifdef RGFW_WASM return RGFW_TRUE; @@ -2411,6 +2447,28 @@ RGFW_bool RGFW_window_borderless(RGFW_window* win) { RGFW_bool RGFW_window_isFullscreen(RGFW_window* win){ return RGFW_BOOL(win->_flags & RGFW_windowFullscreen); } RGFW_bool RGFW_window_allowsDND(RGFW_window* win) { return RGFW_BOOL(win->_flags & RGFW_windowAllowDND); } +void RGFW_window_focusLost(RGFW_window* win) { + /* standard routines for when a window looses focus */ + _RGFW.root->_flags &= ~(u32)RGFW_windowFocus; + if ((win->_flags & RGFW_windowFullscreen)) + RGFW_window_minimize(win); + + for (size_t key = 0; key < RGFW_keyLast; key++) { + if (RGFW_isPressed(NULL, (u8)key) == RGFW_FALSE) continue; + RGFW_keyboard[key].current = RGFW_FALSE; + u8 keyChar = RGFW_rgfwToKeyChar((u32)key); + RGFW_keyCallback(win, (u8)key, keyChar, win->event.keyMod, RGFW_FALSE); + RGFW_eventQueuePushEx(e.type = RGFW_keyReleased; + e.key = (u8)key; + e.keyChar = keyChar; + e.repeat = RGFW_FALSE; + e.keyMod = win->event.keyMod; + e._win = win); + } + + RGFW_resetKey(); +} + #ifndef RGFW_WINDOWS void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow) { RGFW_setBit(&win->_flags, RGFW_windowAllowDND, allow); @@ -2425,7 +2483,7 @@ void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow) { struct timespec; #endif -#if defined(RGFW_X11) || defined(RGFW_WINDOWS) +#if defined(RGFW_WAYLAND) || defined(RGFW_X11) || defined(RGFW_WINDOWS) void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) { RGFW_window_showMouseFlags(win, show); if (show == 0) @@ -2587,9 +2645,8 @@ RGFW_bool RGFW_extensionSupported(const char* extension, size_t len) { 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 */ -i32* RGFW_initFormatAttribs(u32 useSoftware); -i32* RGFW_initFormatAttribs(u32 useSoftware) { - RGFW_UNUSED(useSoftware); +i32* RGFW_initFormatAttribs(void); +i32* RGFW_initFormatAttribs(void) { static i32 attribs[] = { #if defined(RGFW_X11) || defined(RGFW_WINDOWS) RGFW_GL_RENDER_TYPE, @@ -2670,7 +2727,7 @@ i32* RGFW_initFormatAttribs(u32 useSoftware) { #endif #ifdef RGFW_MACOS - if (useSoftware) { + if (_RGFW.root->_flags & RGFW_windowOpenglSoftware) { RGFW_GL_ADD_ATTRIB(70, kCGLRendererGenericFloatID); } else { attribs[index] = RGFW_GL_RENDER_TYPE; @@ -2747,8 +2804,7 @@ i32* RGFW_initFormatAttribs(u32 useSoftware) { #endif -void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software) { - RGFW_UNUSED(software); +void RGFW_window_initOpenGL(RGFW_window* win) { #if defined(RGFW_LINK_EGL) eglInitializeSource = (PFNEGLINITIALIZEPROC) eglGetProcAddress("eglInitialize"); eglGetConfigsSource = (PFNEGLGETCONFIGSPROC) eglGetProcAddress("eglGetConfigs"); @@ -2791,10 +2847,15 @@ void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software) { #elif defined(RGFW_WAYLAND) if (RGFW_useWaylandBool) win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.wl_display); - else + else + #endif + #ifdef RGFW_X11 win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.display); - #else - win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.display); + #else + {} + #endif + #if !defined(RGFW_WAYLAND) && !defined(RGFW_WINDOWS) && !defined(RGFW_X11) + win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.display); #endif EGLint major, minor; @@ -2852,8 +2913,13 @@ void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software) { if (RGFW_useWaylandBool) win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.eglWindow, NULL); else - win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL); - #else + #endif + #ifdef RGFW_X11 + win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL); + #else + {} + #endif + #if !defined(RGFW_X11) && !defined(RGFW_WAYLAND) && !defined(RGFW_MACOS) win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL); #endif @@ -3000,7 +3066,7 @@ VkResult RGFW_window_createVKSurface(RGFW_window* win, VkInstance instance, VkSu return vkCreateXlibSurfaceKHR(instance, &x11, NULL, surface); #endif #if defined(RGFW_WAYLAND) -wayland: +RGFW_WAYLAND_LABEL VkWaylandSurfaceCreateInfoKHR wayland = { VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, 0, 0, (struct wl_display*) win->src.wl_display, (struct wl_surface*) win->src.surface }; return vkCreateWaylandSurfaceKHR(instance, &wayland, NULL, surface); #elif defined(RGFW_WINDOWS) @@ -3029,7 +3095,7 @@ RGFW_bool RGFW_getVKPresentationSupport(VkInstance instance, VkPhysicalDevice ph return out; #endif #if defined(RGFW_WAYLAND) -wayland: +RGFW_WAYLAND_LABEL RGFW_bool wlout = vkGetPhysicalDeviceWaylandPresentationSupportKHR(physicalDevice, queueFamilyIndex, _RGFW.wl_display); return wlout; #elif defined(RGFW_WINDOWS) @@ -3362,16 +3428,17 @@ void pointer_button(void *data, struct wl_pointer *pointer, uint32_t serial, uin RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(serial); RGFW_ASSERT(RGFW_mouse_win != NULL); - u32 b = (button - 0x110) + 1; + u32 b = (button - 0x110); /* flip right and middle button codes */ - if (b == 2) b = 3; - else if (b == 3) b = 2; + if (b == 1) b = 2; + else if (b == 2) b = 1; RGFW_mouseButtons[b].prev = RGFW_mouseButtons[b].current; RGFW_mouseButtons[b].current = RGFW_BOOL(state); - RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonPressed + RGFW_BOOL(state); + RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonReleased - RGFW_BOOL(state); + e.point = RGFW_mouse_win->event.point; e.button = (u8)b; e._win = RGFW_mouse_win); RGFW_mouseButtonCallback(RGFW_mouse_win, (u8)b, 0, RGFW_BOOL(state)); @@ -3380,9 +3447,10 @@ void pointer_axis(void *data, struct wl_pointer *pointer, uint32_t time, uint32_ RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(axis); RGFW_ASSERT(RGFW_mouse_win != NULL); - double scroll = wl_fixed_to_double(value); + double scroll = - wl_fixed_to_double(value); RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonPressed; + e.point = RGFW_mouse_win->event.point; e.button = RGFW_mouseScrollUp + (scroll < 0); e.scroll = scroll; e._win = RGFW_mouse_win); @@ -3410,11 +3478,10 @@ void keyboard_enter (void *data, struct wl_keyboard *keyboard, uint32_t serial, RGFW_key_win = (RGFW_window*)wl_surface_get_user_data(surface); RGFW_key_win->_flags |= RGFW_windowFocus; - RGFW_eventQueuePushEx(e.type = RGFW_focusIn, e._win = RGFW_key_win); + RGFW_eventQueuePushEx(e.type = RGFW_focusIn; e._win = RGFW_key_win); RGFW_focusCallback(RGFW_key_win, RGFW_TRUE); - RGFW_resetKey(); - if ((win->_flags & RGFW_HOLD_MOUSE)) RGFW_window_mouseHold(win, RGFW_AREA(win->r.w, win->r.h)); + if ((RGFW_key_win->_flags & RGFW_HOLD_MOUSE)) RGFW_window_mouseHold(RGFW_key_win, RGFW_AREA(RGFW_key_win->r.w, RGFW_key_win->r.h)); } 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); @@ -3424,8 +3491,8 @@ void keyboard_leave (void *data, struct wl_keyboard *keyboard, uint32_t serial, RGFW_key_win = NULL; RGFW_eventQueuePushEx(e.type = RGFW_focusOut; e._win = win); - win->_flags &= ~(u32)RGFW_windowFocus; RGFW_focusCallback(win, RGFW_FALSE); + RGFW_window_focusLost(win); } 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); @@ -3456,9 +3523,9 @@ int, int))&RGFW_doNothing}; void seat_capabilities (void *data, struct wl_seat *seat, uint32_t capabilities) { RGFW_UNUSED(data); - struct wl_pointer_listener pointer_listener = (struct wl_pointer_listener){&pointer_enter, &pointer_leave, &pointer_motion, &pointer_button, &pointer_axis, (void (*)(void *, struct wl_pointer *))&RGFW_doNothing, (void (*)(void *, struct wl_pointer *, uint32_t))&RGFW_doNothing, (void (*)(void *, struct wl_pointer *, uint32_t, uint32_t))&RGFW_doNothing, (void (*)(void *, struct wl_pointer *, uint32_t, int32_t))&RGFW_doNothing, (void (*)(void *, struct wl_pointer *, uint32_t, int32_t))&RGFW_doNothing}; + static struct wl_pointer_listener pointer_listener = {&pointer_enter, &pointer_leave, &pointer_motion, &pointer_button, &pointer_axis, (void (*)(void *, struct wl_pointer *))&RGFW_doNothing, (void (*)(void *, struct wl_pointer *, uint32_t))&RGFW_doNothing, (void (*)(void *, struct wl_pointer *, uint32_t, uint32_t))&RGFW_doNothing, (void (*)(void *, struct wl_pointer *, uint32_t, int32_t))&RGFW_doNothing, (void (*)(void *, struct wl_pointer *, uint32_t, int32_t))&RGFW_doNothing, (void (*)(void*, struct wl_pointer*, uint32_t, uint32_t))&RGFW_doNothing}; - if (capabilities & WL_SEAT_CAPABILITY_POINTER) { + if (capabilities & WL_SEAT_CAPABILITY_POINTER) { struct wl_pointer *pointer = wl_seat_get_pointer (seat); wl_pointer_add_listener (pointer, &pointer_listener, NULL); } @@ -3620,16 +3687,16 @@ Atom RGFW_XUTF8_STRING = 0; Atom wm_delete_window = 0, RGFW_XCLIPBOARD = 0; -#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) +#if defined(RGFW_X11) && !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) typedef XcursorImage* (*PFN_XcursorImageCreate)(int, int); typedef void (*PFN_XcursorImageDestroy)(XcursorImage*); typedef Cursor(*PFN_XcursorImageLoadCursor)(Display*, const XcursorImage*); #endif -#ifdef RGFW_OPENGL +#if defined(RGFW_OPENGL) && defined(RGFW_X11) typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); #endif -#if !defined(RGFW_NO_X11_XI_PRELOAD) +#if !defined(RGFW_NO_X11_XI_PRELOAD) && defined(RGFW_X11) typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int); PFN_XISelectEvents XISelectEventsSRC = NULL; #define XISelectEvents XISelectEventsSRC @@ -3637,7 +3704,7 @@ Atom wm_delete_window = 0, RGFW_XCLIPBOARD = 0; void* X11Xihandle = NULL; #endif -#if !defined(RGFW_NO_X11_EXT_PRELOAD) +#if !defined(RGFW_NO_X11_EXT_PRELOAD) && defined(RGFW_X11) typedef void (* PFN_XSyncIntToValue)(XSyncValue*, int); PFN_XSyncIntToValue XSyncIntToValueSRC = NULL; #define XSyncIntToValue XSyncIntToValueSRC @@ -3660,7 +3727,7 @@ Atom wm_delete_window = 0, RGFW_XCLIPBOARD = 0; void* X11XEXThandle = NULL; #endif -#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) +#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) && defined(RGFW_X11) PFN_XcursorImageLoadCursor XcursorImageLoadCursorSRC = NULL; PFN_XcursorImageCreate XcursorImageCreateSRC = NULL; PFN_XcursorImageDestroy XcursorImageDestroySRC = NULL; @@ -3672,10 +3739,10 @@ Atom wm_delete_window = 0, RGFW_XCLIPBOARD = 0; void* X11Cursorhandle = NULL; #endif +#ifdef RGFW_X11 const char* RGFW_instName = NULL; -void RGFW_setXInstName(const char* name) { - RGFW_instName = name; -} +void RGFW_setXInstName(const char* name) { RGFW_instName = name; } +#endif #if defined(RGFW_OPENGL) && !defined(RGFW_EGL) RGFW_bool RGFW_extensionSupportedPlatform(const char * extension, size_t len) { @@ -3706,7 +3773,7 @@ void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area) { ); #endif #ifdef RGFW_WAYLAND - wayland: {} + RGFW_WAYLAND_LABEL {} u32 size = (u32)(win->r.w * win->r.h * 4); int fd = create_shm_file(size); if (fd < 0) { @@ -3750,7 +3817,7 @@ void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area) { #endif #else #ifdef RGFW_WAYLAND - wayland:{} + RGFW_WAYLAND_LABEL{} #endif RGFW_UNUSED(win); RGFW_UNUSED(buffer); RGFW_UNUSED(area); @@ -3786,7 +3853,7 @@ void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { #endif #ifdef RGFW_WAYLAND - wayland: + RGFW_WAYLAND_LABEL RGFW_UNUSED(win); RGFW_UNUSED(border); #endif } @@ -3806,7 +3873,7 @@ RGFW_GOTO_WAYLAND(0); XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1); #endif #ifdef RGFW_WAYLAND - wayland: + RGFW_WAYLAND_LABEL RGFW_UNUSED(win); #endif } @@ -3829,7 +3896,7 @@ RGFW_GOTO_WAYLAND(0); RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (i32)(r.w / 2), win->r.y + (i32)(r.h / 2))); #endif #ifdef RGFW_WAYLAND - wayland: + RGFW_WAYLAND_LABEL RGFW_UNUSED(win); RGFW_UNUSED(r); #endif } @@ -3840,11 +3907,10 @@ RGFW_GOTO_WAYLAND(0); if (ptr != NULL) memcpy(&name##SRC, &ptr, sizeof(PFN_##name)); \ } - - -void RGFW_window_getVisual(RGFW_window* win, RGFW_bool software) { +#ifdef RGFW_X11 +void RGFW_window_getVisual(RGFW_window* win) { #if defined(RGFW_OPENGL) && !defined(RGFW_EGL) - i32* visual_attribs = RGFW_initFormatAttribs(software); + i32* visual_attribs = RGFW_initFormatAttribs(); i32 fbcount; GLXFBConfig* fbc = glXChooseFBConfig(win->src.display, DefaultScreen(win->src.display), visual_attribs, &fbcount); @@ -3893,11 +3959,16 @@ void RGFW_window_getVisual(RGFW_window* win, RGFW_bool software) { if (best_samples < RGFW_GL_HINTS[RGFW_glSamples]) RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, RGFW_DEBUG_CTX(win, 0), "Failed to load matching sampiling"); + int configCaveat; + if (glXGetFBConfigAttrib(win->src.display, win->src.bestFbc, GLX_CONFIG_CAVEAT, &configCaveat) == Success && + configCaveat == GLX_SLOW_CONFIG) { + win->_flags |= RGFW_windowOpenglSoftware; + } + XFree(fbc); win->src.visual = *vi; XFree(vi); #else - RGFW_UNUSED(software); win->src.visual.visual = DefaultVisual(win->src.display, DefaultScreen(win->src.display)); win->src.visual.depth = DefaultDepth(win->src.display, DefaultScreen(win->src.display)); if (win->_flags & RGFW_windowTransparent) { @@ -3907,10 +3978,9 @@ void RGFW_window_getVisual(RGFW_window* win, RGFW_bool software) { } #endif } - +#endif #ifndef RGFW_EGL -void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software) { - RGFW_UNUSED(software); +void RGFW_window_initOpenGL(RGFW_window* win) { #ifdef RGFW_OPENGL i32 context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 }; context_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB; @@ -3948,11 +4018,11 @@ void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software) { win->src.ctx = glXCreateContext(win->src.display, &win->src.visual, ctx, True); } } - - glXMakeCurrent(win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx); - RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context initalized"); + + glXMakeCurrent(win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context initalized"); #else - RGFW_UNUSED(win); RGFW_UNUSED(software); + RGFW_UNUSED(win); #endif } @@ -4064,7 +4134,7 @@ i32 RGFW_init(void) { } #endif #ifdef RGFW_WAYLAND -wayland: +RGFW_WAYLAND_LABEL _RGFW.wl_display = wl_display_connect(NULL); #endif _RGFW.windowCount = 0; @@ -4084,7 +4154,7 @@ RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowF i64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask | LeaveWindowMask | EnterWindowMask | ExposureMask; /*!< X11 events accepted */ win->src.display = XOpenDisplay(NULL); - RGFW_window_getVisual(win, RGFW_BOOL(flags & RGFW_windowOpenglSoftware)); + RGFW_window_getVisual(win); /* make X window attrubutes */ XSetWindowAttributes swa; @@ -4164,7 +4234,7 @@ RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowF #endif if ((flags & RGFW_windowNoInitAPI) == 0) { - RGFW_window_initOpenGL(win, RGFW_BOOL(flags & RGFW_windowOpenglSoftware)); + RGFW_window_initOpenGL(win); RGFW_window_initBuffer(win); } @@ -4178,7 +4248,7 @@ RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowF return win; /*return newly created window */ #endif #ifdef RGFW_WAYLAND - wayland: + RGFW_WAYLAND_LABEL RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningWayland, RGFW_DEBUG_CTX(win, 0), "RGFW Wayland support is experimental"); win->src.wl_display = _RGFW.wl_display; @@ -4194,8 +4264,9 @@ RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowF #ifdef RGFW_X11 + win->src.display = _RGFW.display; win->src.window = _RGFW.helperWindow; - XMapWindow(win->src.display, win->src.window); + XMapWindow(_RGFW.display, win->src.window); XFlush(win->src.display); if (wm_delete_window == 0) { wm_delete_window = XInternAtom(win->src.display, "WM_DELETE_WINDOW", False); @@ -4259,7 +4330,7 @@ RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowF while (wl_display_dispatch(win->src.wl_display) != -1 && !RGFW_wl_configured) { } if ((flags & RGFW_windowNoInitAPI) == 0) { - RGFW_window_initOpenGL(win, RGFW_BOOL(flags & RGFW_windowOpenglSoftware)); + RGFW_window_initOpenGL(win); RGFW_window_initBuffer(win); } struct wl_callback* callback = wl_surface_frame(win->src.surface); @@ -4271,7 +4342,8 @@ RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowF if (flags & RGFW_windowScaleToMonitor) RGFW_window_scaleToMonitor(win); #endif - + + RGFW_window_setName(win, name); RGFW_window_setMouseDefault(win); RGFW_window_setFlags(win, flags); return win; /* return newly created window */ @@ -4287,25 +4359,31 @@ RGFW_area RGFW_getScreenSize(void) { return RGFW_AREA(scrn->width, scrn->height); #endif #ifdef RGFW_WAYLAND - wayland: return RGFW_AREA(_RGFW.root->r.w, _RGFW.root->r.h); /* TODO */ + RGFW_WAYLAND_LABEL return RGFW_AREA(_RGFW.root->r.w, _RGFW.root->r.h); /* TODO */ #endif } RGFW_point RGFW_getGlobalMousePoint(void) { RGFW_init(); - RGFW_point RGFWMouse; - + RGFW_point RGFWMouse = RGFW_POINT(0, 0); + RGFW_GOTO_WAYLAND(1); +#ifdef RGFW_X11 i32 x, y; u32 z; Window window1, window2; XQueryPointer(_RGFW.display, XDefaultRootWindow(_RGFW.display), &window1, &window2, &RGFWMouse.x, &RGFWMouse.y, &x, &y, &z); - return RGFWMouse; +#endif +#ifdef RGFW_WAYLAND + RGFW_WAYLAND_LABEL + return RGFWMouse; +#endif } RGFWDEF void RGFW_XHandleClipboardSelection(XEvent* event); -void RGFW_XHandleClipboardSelection(XEvent* event) { - RGFW_LOAD_ATOM(ATOM_PAIR); +void RGFW_XHandleClipboardSelection(XEvent* event) { RGFW_UNUSED(event); +#ifdef RGFW_X11 + RGFW_LOAD_ATOM(ATOM_PAIR); RGFW_LOAD_ATOM(MULTIPLE); RGFW_LOAD_ATOM(TARGETS); RGFW_LOAD_ATOM(SAVE_TARGETS); @@ -4365,6 +4443,7 @@ void RGFW_XHandleClipboardSelection(XEvent* event) { reply.xselection.time = request->time; XSendEvent(_RGFW.display, request->requestor, False, 0, &reply); +#endif } char* RGFW_strtok(char* str, const char* delimStr); @@ -4419,6 +4498,31 @@ char* RGFW_strtok(char* str, const char* delimStr) { i32 RGFW_XHandleClipboardSelectionHelper(void); + +u8 RGFW_rgfwToKeyChar(u32 key) { + u32 keycode = RGFW_rgfwToApiKey(key); + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 + Window root = DefaultRootWindow(_RGFW.display); + Window ret_root, ret_child; + int root_x, root_y, win_x, win_y; + unsigned int mask; + XQueryPointer(_RGFW.display, root, &ret_root, &ret_child, &root_x, &root_y, &win_x, &win_y, &mask); + KeySym sym = (KeySym)XkbKeycodeToKeysym(_RGFW.display, (KeyCode)keycode, 0, (KeyCode)mask & ShiftMask ? 1 : 0); + + if ((mask & LockMask) && sym >= XK_a && sym <= XK_z) + sym = (mask & ShiftMask) ? sym + 32 : sym - 32; + if ((u8)sym != (u32)sym) + sym = 0; + + return (u8)sym; +#endif +#ifdef RGFW_WAYLAND + RGFW_WAYLAND_LABEL RGFW_UNUSED(keycode); + return (u8)key; +#endif +} + RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { RGFW_XHandleClipboardSelectionHelper(); @@ -4479,21 +4583,14 @@ RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { /* set event key data */ win->event.key = (u8)RGFW_apiKeyToRGFW(E.xkey.keycode); - KeySym sym = (KeySym)XkbKeycodeToKeysym(win->src.display, (KeyCode)E.xkey.keycode, 0, (KeyCode)E.xkey.state & ShiftMask ? 1 : 0); - - if ((E.xkey.state & LockMask) && sym >= XK_a && sym <= XK_z) - sym = (E.xkey.state & ShiftMask) ? sym + 32 : sym - 32; - if ((u8)sym != (u32)sym) - sym = 0; - - win->event.keyChar = (u8)sym; + win->event.keyChar = (u8)RGFW_rgfwToKeyChar(win->event.key); RGFW_keyboard[win->event.key].prev = RGFW_keyboard[win->event.key].current; /* get keystate data */ win->event.type = (E.type == KeyPress) ? RGFW_keyPressed : RGFW_keyReleased; - XKeyboardState keystate; + XKeyboardState keystate; XGetKeyboardControl(win->src.display, &keystate); RGFW_keyboard[win->event.key].current = (E.type == KeyPress); @@ -4813,12 +4910,13 @@ RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { XFree(data); if (version >= 2) { - XEvent new_reply = { ClientMessage }; - new_reply.xclient.format = 32; - new_reply.xclient.message_type = XdndFinished; + XEvent new_reply = { ClientMessage }; + new_reply.xclient.window = source; + new_reply.xclient.message_type = XdndFinished; + new_reply.xclient.format = 32; new_reply.xclient.data.l[1] = (long int)result; new_reply.xclient.data.l[2] = (long int)XdndActionCopy; - XSendEvent(win->src.display, source, False, NoEventMask, &new_reply); + XSendEvent(win->src.display, source, False, NoEventMask, &new_reply); XFlush(win->src.display); } break; @@ -4832,16 +4930,12 @@ RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { RGFW_focusCallback(win, 1); - RGFW_resetKey(); if ((win->_flags & RGFW_HOLD_MOUSE)) RGFW_window_mouseHold(win, RGFW_AREA(win->r.w, win->r.h)); break; case FocusOut: - if ((win->_flags & RGFW_windowFullscreen)) - RGFW_window_minimize(win); - - win->_flags &= ~(u32)RGFW_windowFocus; - win->event.type = RGFW_focusOut; + win->event.type = RGFW_focusOut; RGFW_focusCallback(win, 0); + RGFW_window_focusLost(win); break; case PropertyNotify: RGFW_window_checkMode(win); break; case EnterNotify: { @@ -4887,7 +4981,7 @@ RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { else return NULL; #endif #ifdef RGFW_WAYLAND - wayland: + RGFW_WAYLAND_LABEL if ((win->_flags & RGFW_windowHide) == 0) wl_display_roundtrip(win->src.wl_display); return NULL; @@ -4903,7 +4997,7 @@ void RGFW_window_move(RGFW_window* win, RGFW_point v) { XMoveWindow(win->src.display, win->src.window, v.x, v.y); #endif #ifdef RGFW_WAYLAND - wayland: + RGFW_WAYLAND_LABEL RGFW_ASSERT(win != NULL); if (win->src.compositor) { @@ -4936,7 +5030,7 @@ void RGFW_window_resize(RGFW_window* win, RGFW_area a) { } #endif #ifdef RGFW_WAYLAND - wayland: + RGFW_WAYLAND_LABEL if (win->src.compositor) { xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->r.w, win->r.h); #ifdef RGFW_OPENGL @@ -4948,10 +5042,11 @@ void RGFW_window_resize(RGFW_window* win, RGFW_area a) { void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a) { RGFW_ASSERT(win != NULL); + RGFW_GOTO_WAYLAND(0); if (a.w == 0 && a.h == 0) return; - +#ifdef RGFW_X11 XSizeHints hints; long flags; @@ -4963,11 +5058,17 @@ void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a) { hints.min_aspect.y = hints.max_aspect.y = (i32)a.h; XSetWMNormalHints(win->src.display, win->src.window, &hints); + return; +#endif +#ifdef RGFW_WAYLAND + RGFW_WAYLAND_LABEL +#endif } void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { RGFW_ASSERT(win != NULL); - + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 long flags; XSizeHints hints; RGFW_MEMSET(&hints, 0, sizeof(XSizeHints)); @@ -4980,11 +5081,17 @@ void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { hints.min_height = (i32)a.h; XSetWMNormalHints(win->src.display, win->src.window, &hints); + return; +#endif +#ifdef RGFW_WAYLAND +RGFW_WAYLAND_LABEL RGFW_UNUSED(a); +#endif } void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { RGFW_ASSERT(win != NULL); - + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 long flags; XSizeHints hints; RGFW_MEMSET(&hints, 0, sizeof(XSizeHints)); @@ -4997,8 +5104,13 @@ void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { hints.max_height = (i32)a.h; XSetWMNormalHints(win->src.display, win->src.window, &hints); +#endif +#ifdef RGFW_WAYLAND + RGFW_WAYLAND_LABEL RGFW_UNUSED(a); +#endif } +#ifdef RGFW_X11 void RGFW_toggleXMaximized(RGFW_window* win, RGFW_bool maximized); void RGFW_toggleXMaximized(RGFW_window* win, RGFW_bool maximized) { RGFW_ASSERT(win != NULL); @@ -5019,33 +5131,54 @@ void RGFW_toggleXMaximized(RGFW_window* win, RGFW_bool maximized) { XSendEvent(win->src.display, DefaultRootWindow(win->src.display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); } +#endif void RGFW_window_maximize(RGFW_window* win) { win->_oldRect = win->r; - RGFW_toggleXMaximized(win, 1); + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 + RGFW_toggleXMaximized(win, 1); + return; +#endif +#ifdef RGFW_WAYLAND + RGFW_WAYLAND_LABEL + return; +#endif } void RGFW_window_focus(RGFW_window* win) { RGFW_ASSERT(win); - + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 XWindowAttributes attr; XGetWindowAttributes(win->src.display, win->src.window, &attr); if (attr.map_state != IsViewable) return; XSetInputFocus(win->src.display, win->src.window, RevertToPointerRoot, CurrentTime); XFlush(win->src.display); +#endif +#ifdef RGFW_WAYLAND +RGFW_WAYLAND_LABEL; +#endif } void RGFW_window_raise(RGFW_window* win) { RGFW_ASSERT(win); - XRaiseWindow(win->src.display, win->src.window); + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 + XRaiseWindow(win->src.display, win->src.window); XMapRaised(win->src.display, win->src.window); +#endif +#ifdef RGFW_WAYLAND +RGFW_WAYLAND_LABEL; +#endif } +#ifdef RGFW_X11 void RGFW_window_setXAtom(RGFW_window* win, Atom netAtom, RGFW_bool fullscreen); void RGFW_window_setXAtom(RGFW_window* win, Atom netAtom, RGFW_bool fullscreen) { RGFW_ASSERT(win != NULL); - RGFW_LOAD_ATOM(_NET_WM_STATE); + RGFW_LOAD_ATOM(_NET_WM_STATE); XEvent xev = {0}; xev.xclient.type = ClientMessage; @@ -5060,61 +5193,93 @@ void RGFW_window_setXAtom(RGFW_window* win, Atom netAtom, RGFW_bool fullscreen) XSendEvent(win->src.display, DefaultRootWindow(win->src.display), False, SubstructureNotifyMask | SubstructureRedirectMask, &xev); } +#endif void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) { RGFW_ASSERT(win != NULL); - if (fullscreen) { + RGFW_GOTO_WAYLAND(0); + if (fullscreen) { win->_flags |= RGFW_windowFullscreen; win->_oldRect = win->r; } else win->_flags &= ~(u32)RGFW_windowFullscreen; - +#ifdef RGFW_X11 RGFW_LOAD_ATOM(_NET_WM_STATE_FULLSCREEN); RGFW_window_setXAtom(win, _NET_WM_STATE_FULLSCREEN, fullscreen); XRaiseWindow(win->src.display, win->src.window); XMapRaised(win->src.display, win->src.window); +#endif +#ifdef RGFW_WAYLAND + RGFW_WAYLAND_LABEL; +#endif } void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) { RGFW_ASSERT(win != NULL); - + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 RGFW_LOAD_ATOM(_NET_WM_STATE_ABOVE); RGFW_window_setXAtom(win, _NET_WM_STATE_ABOVE, floating); +#endif +#ifdef RGFW_WAYLAND +RGFW_WAYLAND_LABEL RGFW_UNUSED(floating); +#endif } void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) { RGFW_ASSERT(win != NULL); + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 const u32 value = (u32) (0xffffffffu * (double) opacity); RGFW_LOAD_ATOM(NET_WM_WINDOW_OPACITY); XChangeProperty(win->src.display, win->src.window, NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &value, 1); +#endif +#ifdef RGFW_WAYLAND +RGFW_WAYLAND_LABEL RGFW_UNUSED(opacity); +#endif } void RGFW_window_minimize(RGFW_window* win) { RGFW_ASSERT(win != NULL); + RGFW_GOTO_WAYLAND(0); if (RGFW_window_isMaximized(win)) return; win->_oldRect = win->r; - XIconifyWindow(win->src.display, win->src.window, DefaultScreen(win->src.display)); +#ifdef RGFW_X11 + XIconifyWindow(win->src.display, win->src.window, DefaultScreen(win->src.display)); XFlush(win->src.display); +#endif +#ifdef RGFW_WAYLAND + RGFW_WAYLAND_LABEL; +#endif } void RGFW_window_restore(RGFW_window* win) { RGFW_ASSERT(win != NULL); - RGFW_toggleXMaximized(win, 0); - + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 + RGFW_toggleXMaximized(win, 0); +#endif +#ifdef RGFW_WAYLAND + RGFW_WAYLAND_LABEL +#endif win->r = win->_oldRect; RGFW_window_move(win, RGFW_POINT(win->r.x, win->r.y)); RGFW_window_resize(win, RGFW_AREA(win->r.w, win->r.h)); - - RGFW_window_show(win); - XFlush(win->src.display); + + RGFW_window_show(win); +#ifdef RGFW_X11 + XFlush(win->src.display); +#endif } RGFW_bool RGFW_window_isFloating(RGFW_window* win) { - RGFW_LOAD_ATOM(_NET_WM_STATE); + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 + RGFW_LOAD_ATOM(_NET_WM_STATE); RGFW_LOAD_ATOM(_NET_WM_STATE_ABOVE); Atom actual_type; @@ -5135,7 +5300,10 @@ RGFW_bool RGFW_window_isFloating(RGFW_window* win) { if (prop_return) XFree(prop_return); - +#endif +#ifdef RGFW_WAYLAND + RGFW_WAYLAND_LABEL RGFW_UNUSED(win); +#endif return RGFW_FALSE; } @@ -5157,7 +5325,7 @@ void RGFW_window_setName(RGFW_window* win, const char* name) { ); #endif #ifdef RGFW_WAYLAND - wayland: + RGFW_WAYLAND_LABEL if (win->src.compositor) xdg_toplevel_set_title(win->src.xdg_toplevel, name); #endif @@ -5166,7 +5334,9 @@ void RGFW_window_setName(RGFW_window* win, const char* name) { #ifndef RGFW_NO_PASSTHROUGH void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) { RGFW_ASSERT(win != NULL); - if (passthrough) { + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 + if (passthrough) { Region region = XCreateRegion(); XShapeCombineRegion(win->src.display, win->src.window, ShapeInput, 0, 0, region, ShapeSet); XDestroyRegion(region); @@ -5175,6 +5345,10 @@ void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) { } XShapeCombineMask(win->src.display, win->src.window, ShapeInput, 0, 0, None, ShapeSet); +#endif +#ifdef RGFW_WAYLAND + RGFW_WAYLAND_LABEL RGFW_UNUSED(passthrough); +#endif } #endif /* RGFW_NO_PASSTHROUGH */ @@ -5243,7 +5417,7 @@ RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* icon, RGFW_area a, i32 cha return RGFW_BOOL(res); #endif #ifdef RGFW_WAYLAND - wayland: + RGFW_WAYLAND_LABEL RGFW_UNUSED(icon); RGFW_UNUSED(a); RGFW_UNUSED(channels); RGFW_UNUSED(type); return RGFW_FALSE; #endif } @@ -5284,7 +5458,7 @@ RGFW_mouse* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels) { #endif #endif #ifdef RGFW_WAYLAND - wayland: + RGFW_WAYLAND_LABEL RGFW_UNUSED(icon); RGFW_UNUSED(a); RGFW_UNUSED(channels); return NULL; /* TODO */ #endif @@ -5297,7 +5471,7 @@ RGFW_GOTO_WAYLAND(0); XDefineCursor(win->src.display, win->src.window, (Cursor)mouse); #endif #ifdef RGFW_WAYLAND - wayland: + RGFW_WAYLAND_LABEL RGFW_UNUSED(win); RGFW_UNUSED(mouse); #endif } @@ -5309,7 +5483,7 @@ RGFW_GOTO_WAYLAND(0); XFreeCursor(_RGFW.display, (Cursor)mouse); #endif #ifdef RGFW_WAYLAND - wayland: + RGFW_WAYLAND_LABEL RGFW_UNUSED(mouse); #endif } @@ -5333,7 +5507,7 @@ RGFW_GOTO_WAYLAND(1); XWarpPointer(win->src.display, None, win->src.window, 0, 0, 0, 0, (int) p.x - win->r.x, (int) p.y - win->r.y); #endif #ifdef RGFW_WAYLAND - wayland: + RGFW_WAYLAND_LABEL RGFW_UNUSED(win); RGFW_UNUSED(p); #endif } @@ -5360,7 +5534,7 @@ RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { return RGFW_TRUE; #endif #ifdef RGFW_WAYLAND - wayland: { } + RGFW_WAYLAND_LABEL { } static const char* iconStrings[16] = { "left_ptr", "left_ptr", "text", "cross", "pointer", "e-resize", "n-resize", "nw-resize", "ne-resize", "all-resize", "not-allowed" }; struct wl_cursor* wlcursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, iconStrings[mouse]); @@ -5380,7 +5554,7 @@ void RGFW_window_hide(RGFW_window* win) { XUnmapWindow(win->src.display, win->src.window); #endif #ifdef RGFW_WAYLAND - wayland: + RGFW_WAYLAND_LABEL wl_surface_attach(win->src.surface, NULL, 0, 0); wl_surface_commit(win->src.surface); win->_flags |= RGFW_windowHide; @@ -5395,7 +5569,7 @@ void RGFW_window_show(RGFW_window* win) { XMapWindow(win->src.display, win->src.window); #endif #ifdef RGFW_WAYLAND - wayland: + RGFW_WAYLAND_LABEL /* wl_surface_attach(win->src.surface, win->rc., 0, 0); */ wl_surface_commit(win->src.surface); #endif @@ -5448,11 +5622,12 @@ RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property); size = (RGFW_ssize_t)sizeN; - return size; -#endif -#if defined(RGFW_WAYLAND) -wayland: return 0; -#endif + return size; + #endif + #if defined(RGFW_WAYLAND) + RGFW_WAYLAND_LABEL RGFW_UNUSED(str); RGFW_UNUSED(strCapacity); + return 0; + #endif } i32 RGFW_XHandleClipboardSelectionHelper(void) { @@ -5508,23 +5683,32 @@ void RGFW_writeClipboard(const char* text, u32 textLen) { _RGFW.clipboard_len = textLen; #endif #ifdef RGFW_WAYLAND - wayland: + RGFW_WAYLAND_LABEL RGFW_UNUSED(text); RGFW_UNUSED(textLen); #endif } RGFW_bool RGFW_window_isHidden(RGFW_window* win) { RGFW_ASSERT(win != NULL); + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 XWindowAttributes windowAttributes; XGetWindowAttributes(win->src.display, win->src.window, &windowAttributes); return (windowAttributes.map_state == IsUnmapped && !RGFW_window_isMinimized(win)); +#endif +#ifdef RGFW_WAYLAND + RGFW_WAYLAND_LABEL + return RGFW_FALSE; +#endif } RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { RGFW_ASSERT(win != NULL); - RGFW_LOAD_ATOM(WM_STATE); + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 + RGFW_LOAD_ATOM(WM_STATE); Atom actual_type; i32 actual_format; @@ -5545,12 +5729,19 @@ RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { XWindowAttributes windowAttributes; XGetWindowAttributes(win->src.display, win->src.window, &windowAttributes); - return windowAttributes.map_state != IsViewable; + return windowAttributes.map_state != IsViewable; +#endif +#ifdef RGFW_WAYLAND + RGFW_WAYLAND_LABEL + return RGFW_FALSE; +#endif } RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { RGFW_ASSERT(win != NULL); - RGFW_LOAD_ATOM(_NET_WM_STATE); + RGFW_GOTO_WAYLAND(0); +#ifdef RGFW_X11 + RGFW_LOAD_ATOM(_NET_WM_STATE); RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_VERT); RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ); @@ -5581,7 +5772,10 @@ RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { if (prop_data != NULL) XFree(prop_data); - +#endif +#ifdef RGFW_WAYLAND +RGFW_WAYLAND_LABEL; +#endif return RGFW_FALSE; } @@ -5594,6 +5788,7 @@ u32 RGFW_XCalculateRefreshRate(XRRModeInfo mi) { #endif +#ifdef RGFW_X11 static float XGetSystemContentDPI(Display* display, i32 screen) { float dpi = 96.0f; @@ -5617,11 +5812,15 @@ static float XGetSystemContentDPI(Display* display, i32 screen) { return dpi; } +#endif RGFW_monitor RGFW_XCreateMonitor(i32 screen); RGFW_monitor RGFW_XCreateMonitor(i32 screen) { RGFW_monitor monitor; RGFW_init(); + + RGFW_GOTO_WAYLAND(1); +#ifdef RGFW_X11 Display* display = _RGFW.display; if (screen == -1) screen = DefaultScreen(display); @@ -5694,7 +5893,13 @@ RGFW_monitor RGFW_XCreateMonitor(i32 screen) { #endif RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, RGFW_DEBUG_CTX_MON(monitor), "monitor found"); - return monitor; + return monitor; +#endif +#ifdef RGFW_WAYLAND +RGFW_WAYLAND_LABEL RGFW_UNUSED(screen); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, RGFW_DEBUG_CTX_MON(monitor), "monitor found"); + return monitor; +#endif } RGFW_monitor* RGFW_getMonitors(size_t* len) { @@ -5716,7 +5921,8 @@ RGFW_monitor* RGFW_getMonitors(size_t* len) { return monitors; #endif #ifdef RGFW_WAYLAND - wayland: return monitors; /* TODO WAYLAND */ + RGFW_WAYLAND_LABEL RGFW_UNUSED(len); + return monitors; /* TODO WAYLAND */ #endif } @@ -5726,7 +5932,7 @@ RGFW_monitor RGFW_getPrimaryMonitor(void) { return RGFW_XCreateMonitor(-1); #endif #ifdef RGFW_WAYLAND - wayland: return (RGFW_monitor){ 0 }; /* TODO WAYLAND */ + RGFW_WAYLAND_LABEL return (RGFW_monitor){ 0 }; /* TODO WAYLAND */ #endif } @@ -5781,7 +5987,7 @@ RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW #endif #endif #ifdef RGFW_WAYLAND -wayland: +RGFW_WAYLAND_LABEL RGFW_UNUSED(mon); RGFW_UNUSED(mode); RGFW_UNUSED(request); #endif return RGFW_FALSE; } @@ -5807,7 +6013,7 @@ RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { } #endif #ifdef RGFW_WAYLAND -wayland: +RGFW_WAYLAND_LABEL #endif return mon; @@ -5836,7 +6042,7 @@ void RGFW_window_swapBuffers_software(RGFW_window* win) { return; #endif #ifdef RGFW_WAYLAND - wayland: + RGFW_WAYLAND_LABEL #if !defined(RGFW_BUFFER_BGR) && !defined(RGFW_OSMESA) RGFW_RGB_to_BGR(win, win->src.buffer); #else @@ -5853,7 +6059,7 @@ void RGFW_window_swapBuffers_software(RGFW_window* win) { #endif #else #ifdef RGFW_WAYLAND - wayland: + RGFW_WAYLAND_LABEL #endif RGFW_UNUSED(win); #endif @@ -5912,6 +6118,8 @@ void RGFW_deinit(void) { } RGFW_freeMouse(_RGFW.hiddenMouse); + + XDestroyWindow(_RGFW.display, (Drawable) _RGFW.helperWindow); /*!< close the window */ XCloseDisplay(_RGFW.display); /*!< kill connection to the x server */ #if !defined(RGFW_NO_X11_CURSOR_PRELOAD) && !defined(RGFW_NO_X11_CURSOR) @@ -5954,9 +6162,8 @@ void RGFW_window_close(RGFW_window* win) { RGFW_ASSERT(win != NULL); if ((win->_flags & RGFW_windowNoInitAPI) == 0) RGFW_window_freeOpenGL(win); - #ifdef RGFW_X11 RGFW_GOTO_WAYLAND(0); - + #ifdef RGFW_X11 /* ungrab pointer if it was grabbed */ if (win->_flags & RGFW_HOLD_MOUSE) XUngrabPointer(win->src.display, CurrentTime); @@ -5988,19 +6195,17 @@ void RGFW_window_close(RGFW_window* win) { #endif #ifdef RGFW_WAYLAND - wayland: + RGFW_WAYLAND_LABEL - #ifdef RGFW_X11 - XDestroyWindow(win->src.display, (Drawable) win->src.window); - #endif RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a window was freed"); - _RGFW.windowCount--; - if (_RGFW.windowCount == 0) RGFW_deinit(); - xdg_toplevel_destroy(win->src.xdg_toplevel); - xdg_surface_destroy(win->src.xdg_surface); + xdg_toplevel_destroy(win->src.xdg_toplevel); + xdg_surface_destroy(win->src.xdg_surface); wl_surface_destroy(win->src.surface); + _RGFW.windowCount--; + if (_RGFW.windowCount == 0) RGFW_deinit(); + #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) wl_buffer_destroy(win->src.wl_buffer); if ((win->_flags & RGFW_BUFFER_ALLOC)) @@ -6297,6 +6502,7 @@ LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) RGFW_eventQueuePushEx(e.type = (RGFW_eventType)((u8)RGFW_focusOut - inFocus); e._win = win); RGFW_focusCallback(win, inFocus); + RGFW_window_focusLost(win); if ((win->_flags & RGFW_windowFullscreen) == 0) return DefWindowProcW(hWnd, message, wParam, lParam); @@ -6561,7 +6767,7 @@ void RGFW_win32_loadOpenGLFuncs(HWND dummyWin) { } #ifndef RGFW_EGL -void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software) { +void RGFW_window_initOpenGL(RGFW_window* win) { #ifdef RGFW_OPENGL PIXELFORMATDESCRIPTOR pfd; pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); @@ -6577,13 +6783,13 @@ void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software) { if (RGFW_GL_HINTS[RGFW_glStereo]) pfd.dwFlags |= PFD_STEREO; /* try to create the pixel format we want for opengl and then try to create an opengl context for the specified version */ - if (software) + if (win->_flags & RGFW_windowOpenglSoftware) pfd.dwFlags |= PFD_GENERIC_FORMAT | PFD_GENERIC_ACCELERATED; /* get pixel format, default to a basic pixel format */ int pixel_format = ChoosePixelFormat(win->src.hdc, &pfd); if (wglChoosePixelFormatARB != NULL) { - i32* pixel_format_attribs = (i32*)RGFW_initFormatAttribs(software); + i32* pixel_format_attribs = (i32*)RGFW_initFormatAttribs(); int new_pixel_format; UINT num_formats; @@ -6597,6 +6803,10 @@ void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software) { if (!DescribePixelFormat(win->src.hdc, pixel_format, sizeof(suggested), &suggested) || !SetPixelFormat(win->src.hdc, pixel_format, &pfd)) RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to set the WGL pixel format"); + + if (!(pfd.dwFlags & PFD_GENERIC_ACCELERATED)) { + win->_flags |= RGFW_windowOpenglSoftware; + } if (wglCreateContextAttribsARB != NULL) { /* create opengl/WGL context for the specified version */ @@ -6631,7 +6841,7 @@ void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software) { wglShareLists(_RGFW.root->src.ctx, win->src.ctx); RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context initalized"); #else - RGFW_UNUSED(win); RGFW_UNUSED(software); + RGFW_UNUSED(win); #endif } @@ -6769,7 +6979,7 @@ RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowF win->src.hdc = GetDC(win->src.window); if ((flags & RGFW_windowNoInitAPI) == 0) { - RGFW_window_initOpenGL(win, RGFW_BOOL(flags & RGFW_windowOpenglSoftware)); + RGFW_window_initOpenGL(win); RGFW_window_initBuffer(win); } @@ -7043,12 +7253,33 @@ void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) { MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD)waitMS, QS_ALLINPUT); } +u8 RGFW_rgfwToKeyChar(u32 rgfw_keycode) { + UINT vsc = RGFW_rgfwToApiKey(rgfw_keycode); // Should return a Windows VK_* code + BYTE keyboardState[256] = {0}; + + if (!GetKeyboardState(keyboardState)) + return (u8)rgfw_keycode; + + UINT vk = MapVirtualKeyW(vsc, MAPVK_VSC_TO_VK); + HKL layout = GetKeyboardLayout(0); + + wchar_t charBuffer[2] = {0}; + int result = ToUnicodeEx(vk, vsc, keyboardState, charBuffer, 1, 0, layout); + + if (result <= 0) + return (u8)rgfw_keycode; + + return (u8)charBuffer[0]; +} + RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { if (win == NULL || ((win->_flags & RGFW_windowFreeOnClose) && (win->_flags & RGFW_EVENT_QUIT))) return NULL; RGFW_event* ev = RGFW_window_checkEventCore(win); - if (ev) return ev; + if (ev) { + return ev; + } - static HDROP drop; + static HDROP drop; if (win->event.type == RGFW_DNDInit) { if (win->event.droppedFilesCount) { u32 i; @@ -8619,7 +8850,6 @@ void RGFW__osxWindowBecameKey(id self, SEL sel) { RGFW_focusCallback(win, RGFW_TRUE); - RGFW_resetKey(); if ((win->_flags & RGFW_HOLD_MOUSE)) RGFW_window_mouseHold(win, RGFW_AREA(win->r.w, win->r.h)); } @@ -8629,8 +8859,8 @@ void RGFW__osxWindowResignKey(id self, SEL sel) { object_getInstanceVariable(self, "RGFW_window", (void**)&win); if (win == NULL) return; - win->_flags &= ~(u32)RGFW_windowFocus; - RGFW_eventQueuePushEx(e.type = RGFW_focusOut; e._win = win); + RGFW_window_focusLost(win); + RGFW_eventQueuePushEx(e.type = RGFW_focusOut; e._win = win); RGFW_focusCallback(win, RGFW_FALSE); } @@ -8739,14 +8969,15 @@ id RGFW__osx_generateViewClass(const char* subclass, RGFW_window* win) { } #ifndef RGFW_EGL -void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software) { +void RGFW_window_initOpenGL(RGFW_window* win) { #ifdef RGFW_OPENGL - void* attrs = RGFW_initFormatAttribs(software); + void* attrs = RGFW_initFormatAttribs(); void* format = NSOpenGLPixelFormat_initWithAttributes((uint32_t*)attrs); if (format == NULL) { RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to load pixel format for OpenGL"); - void* subAttrs = RGFW_initFormatAttribs(1); + win->_flags |= RGFW_windowOpenglSoftware; + void* subAttrs = RGFW_initFormatAttribs(); format = NSOpenGLPixelFormat_initWithAttributes((uint32_t*)subAttrs); if (format == NULL) @@ -8773,7 +9004,7 @@ void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software) { objc_msgSend_void(win->src.ctx, sel_registerName("makeCurrentContext")); RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context initalized"); #else - RGFW_UNUSED(win); RGFW_UNUSED(software); + RGFW_UNUSED(win); #endif } @@ -8856,7 +9087,7 @@ RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowF objc_msgSend_void_id((id)win->src.window, sel_registerName("setTitle:"), str); if ((flags & RGFW_windowNoInitAPI) == 0) { - RGFW_window_initOpenGL(win, RGFW_BOOL(flags & RGFW_windowOpenglSoftware)); + RGFW_window_initOpenGL(win); RGFW_window_initBuffer(win); } @@ -9067,7 +9298,6 @@ void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) { (NSApp, eventFunc, ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true); - if (e) { ((void (*)(id, SEL, id, bool))objc_msgSend) (NSApp, sel_registerName("postEvent:atStart:"), e, 1); @@ -9076,6 +9306,10 @@ void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) { objc_msgSend_bool_void(eventPool, sel_registerName("drain")); } +u8 RGFW_rgfwToKeyChar(u32 rgfw_keycode) { + return (u8)rgfw_keycode; /* TODO */ +} + RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { if (win == NULL || ((win->_flags & RGFW_windowFreeOnClose) && (win->_flags & RGFW_EVENT_QUIT))) return NULL; @@ -9160,8 +9394,6 @@ RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { case NSEventTypeKeyUp: { u32 key = (u16) objc_msgSend_uint(e, sel_registerName("keyCode")); - - u32 mappedKey = (u32)*(((char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("charactersIgnoringModifiers"))))); if (((u8)mappedKey) == 239) mappedKey = 0; @@ -9919,7 +10151,6 @@ EM_BOOL Emscripten_on_focusin(int eventType, const EmscriptenFocusEvent* E, void _RGFW.root->_flags |= RGFW_windowFocus; RGFW_focusCallback(_RGFW.root, 1); - RGFW_resetKey(); if ((_RGFW.root->_flags & RGFW_HOLD_MOUSE)) RGFW_window_mouseHold(_RGFW.root, RGFW_AREA(_RGFW.root->r.w, _RGFW.root->r.h)); return EM_TRUE; } @@ -9928,8 +10159,8 @@ EM_BOOL Emscripten_on_focusout(int eventType, const EmscriptenFocusEvent* E, voi RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(E); RGFW_eventQueuePushEx(e.type = RGFW_focusOut; e._win = _RGFW.root); - _RGFW.root->_flags &= ~(u32)RGFW_windowFocus; - RGFW_focusCallback(_RGFW.root, 0); + RGFW_window_focusLost(_RGFW.root); + RGFW_focusCallback(_RGFW.root, 0); return EM_TRUE; } @@ -10292,7 +10523,7 @@ void EMSCRIPTEN_KEEPALIVE RGFW_writeFile(const char *path, const char *data, siz fclose(file); } -void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software) { +void RGFW_window_initOpenGL(RGFW_window* win) { #if defined(RGFW_OPENGL) && !defined(RGFW_WEBGPU) && !defined(RGFW_OSMESA) && !defined(RGFW_BUFFER) EmscriptenWebGLContextAttributes attrs; attrs.alpha = RGFW_GL_HINTS[RGFW_glDepth]; @@ -10355,7 +10586,7 @@ i32 RGFW_init(void) { RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) { RGFW_window_basic_init(win, rect, flags); - RGFW_window_initOpenGL(win, 0); + RGFW_window_initOpenGL(win); #if defined(RGFW_WEBGPU) win->src.ctx = wgpuCreateInstance(NULL); @@ -10465,6 +10696,10 @@ RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowF return win; } +u8 RGFW_rgfwToKeyChar(u32 rgfw_keycode) { + return (u8)rgfw_keycode; /* TODO */ +} + RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { if (win == NULL || ((win->_flags & RGFW_windowFreeOnClose) && (win->_flags & RGFW_EVENT_QUIT))) return NULL; RGFW_event* ev = RGFW_window_checkEventCore(win); From 106bcf460ae65242d680f29dd1196e71c5308498 Mon Sep 17 00:00:00 2001 From: Marcos Grzesiak Date: Thu, 12 Jun 2025 23:41:57 -0400 Subject: [PATCH 040/242] add uiua bindings to the list --- BINDINGS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BINDINGS.md b/BINDINGS.md index 3468b7e89..4088a13ce 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -93,6 +93,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers | [raylib-apl](https://github.com/Brian-ED/raylib-apl) | **5.0** | [Dyalog APL](https://www.dyalog.com/) | MIT | | [raylib-jai](https://github.com/ahmedqarmout2/raylib-jai) | **5.5** | [Jai](https://github.com/BSVino/JaiPrimer/blob/master/JaiPrimer.md) | MIT | | [fnl-raylib](https://github.com/0riginaln0/fnl-raylib) | **5.5** | [Fennel](https://fennel-lang.org/) | MIT | +| [Rayua](https://github.com/uiua-lang/rayua) | **5.5** | [Uiua](https://www.uiua.org/) | **???** | ### Utility Wrapers @@ -103,6 +104,7 @@ These are utility wrappers for specific languages, they are not required to use | [claylib](https://github.com/defun-games/claylib) | 4.5 | [Common Lisp](https://common-lisp.net) | Zlib | | [rayed-bqn](https://github.com/Brian-ED/rayed-bqn) | **5.0** | [BQN](https://mlochbaum.github.io/BQN) | MIT | | [DOOR](https://github.com/RealDoigt/DOOR) | 4.0 | [D](https://dlang.org) | MIT | +| [Iris](https://github.com/Marcos-cat/iris) | **5.5** | [Uiua](https://www.uiua.org/) | MIT | ### Older or Unmaintained Language Bindings From 43bad2612bccdf6e74b0d45f0d3de0c5255a673e Mon Sep 17 00:00:00 2001 From: danil <61111955+danilwhale@users.noreply.github.com> Date: Tue, 17 Jun 2025 17:15:09 +0300 Subject: [PATCH 041/242] docs: add Raylib-cs.BleedingEdge to the bindings --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index 4088a13ce..986c27abc 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -14,6 +14,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers | [Raylib-CsLo](https://github.com/NotNotTech/Raylib-CsLo) | 4.2 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | | [Raylib-CSharp-Vinculum](https://github.com/ZeroElectric/Raylib-CSharp-Vinculum) | **5.0** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | | [Raylib-CSharp](https://github.com/MrScautHD/Raylib-CSharp) | **5.5** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MIT | +| [Raylib-cs.BleedingEdge](https://github.com/danilwhale/Raylib-cs.BleedingEdge) | **5.6-dev** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | | [cl-raylib](https://github.com/longlene/cl-raylib) | 4.0 | [Common Lisp](https://common-lisp.net) | MIT | | [claylib/wrap](https://github.com/defun-games/claylib) | 4.5 | [Common Lisp](https://common-lisp.net) | Zlib | | [claw-raylib](https://github.com/bohonghuang/claw-raylib) | **auto** | [Common Lisp](https://common-lisp.net) | Apache-2.0 | From 518ad8b018ab29879bbad65a010fcac6796bff99 Mon Sep 17 00:00:00 2001 From: mlorenc Date: Fri, 20 Jun 2025 01:15:14 +0200 Subject: [PATCH 042/242] Fix ScanDirectoryFilesRecursively Fixes a regression since 5.5, where `ScanDirectoryFilesRecursively` no longer does the "recursively" part due to `path` being `static`. The issue was once already fixed in https://github.com/raysan5/raylib/commit/5530a3ceb88962066affa5db8e13b00b64444b37 but recently made it back it in. --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index f8b061a50..a15e117b3 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3757,7 +3757,7 @@ static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const // Scan all files and directories recursively from a base path static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *files, const char *filter) { - static char path[MAX_FILEPATH_LENGTH] = { 0 }; + char path[MAX_FILEPATH_LENGTH] = { 0 }; memset(path, 0, MAX_FILEPATH_LENGTH); struct dirent *dp = NULL; From 1abac023bdc433370b8db00d770eaf152a6cdeae Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 20 Jun 2025 23:34:41 +0200 Subject: [PATCH 043/242] Update rcore.c --- src/rcore.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rcore.c b/src/rcore.c index a15e117b3..e97ef357c 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3757,6 +3757,7 @@ static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const // Scan all files and directories recursively from a base path static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *files, const char *filter) { + // WARNING: Path can not be static or it will be reused between recursive function calls! char path[MAX_FILEPATH_LENGTH] = { 0 }; memset(path, 0, MAX_FILEPATH_LENGTH); From b6773760887ed63f8856e3a29a5fe26391a33fc2 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Jun 2025 23:52:34 +0200 Subject: [PATCH 044/242] Delete shader in case compilation fails --- src/rlgl.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rlgl.h b/src/rlgl.h index c39d096fb..2562ad45d 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -4178,6 +4178,9 @@ unsigned int rlCompileShader(const char *shaderCode, int type) RL_FREE(log); } + // Unload object allocated by glCreateShader(), + // despite failing in the compilation process + glDeleteShader(shader); shader = 0; } else From eee9dd8c944331dd804752383692076ab4d2790e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agnis=20Aldi=C5=86=C5=A1=20=22NeZv=C4=93rs?= Date: Mon, 23 Jun 2025 13:03:35 +0300 Subject: [PATCH 045/242] Example: core_3d_fps_controller Quake like camera animations and strafe jump movement --- examples/Makefile | 1 + examples/Makefile.Web | 4 + examples/core/core_3d_fps_controller.c | 360 +++++++++++++++++++++++++ examples/core/resources/huh_jump.wav | Bin 0 -> 19050 bytes 4 files changed, 365 insertions(+) create mode 100644 examples/core/core_3d_fps_controller.c create mode 100644 examples/core/resources/huh_jump.wav diff --git a/examples/Makefile b/examples/Makefile index 32a3a75ab..edd2eb399 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -500,6 +500,7 @@ CORE = \ core/core_3d_camera_free \ core/core_3d_camera_mode \ core/core_3d_camera_split_screen \ + core/core_3d_fps_controller \ core/core_3d_picking \ core/core_automation_events \ core/core_basic_screen_manager \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 35ae70a18..5ba550eb0 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -382,6 +382,7 @@ CORE = \ core/core_3d_camera_free \ core/core_3d_camera_mode \ core/core_3d_camera_split_screen \ + core/core_3d_fps_controller \ core/core_3d_picking \ core/core_automation_events \ core/core_basic_screen_manager \ @@ -587,6 +588,9 @@ core/core_3d_camera_mode: core/core_3d_camera_mode.c core/core_3d_camera_split_screen: core/core_3d_camera_split_screen.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +core/core_3d_fps_controller: core/core_3d_fps_controller.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + core/core_3d_picking: core/core_3d_picking.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) diff --git a/examples/core/core_3d_fps_controller.c b/examples/core/core_3d_fps_controller.c new file mode 100644 index 000000000..56d8c4543 --- /dev/null +++ b/examples/core/core_3d_fps_controller.c @@ -0,0 +1,360 @@ +/******************************************************************************************* +* +* raylib [core] example - Input Gestures for Web +* +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 5.5 +* +* Example contributed by Agnis Aldins (@nezvers) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025-2025 Agnis Aldins (@nezvers) +* +********************************************************************************************/ + +#include "raylib.h" +#include "raymath.h" +#include "rcamera.h" + +//#define PLATFORM_WEB + +#if defined(PLATFORM_WEB) +#include +#endif + +#if defined(PLATFORM_DESKTOP) +#define GLSL_VERSION 330 +#else // PLATFORM_ANDROID, PLATFORM_WEB +#define GLSL_VERSION 100 +#endif + + +/* Movement constants */ +#define GRAVITY 32.f +#define MAX_SPEED 20.f +#define CROUCH_SPEED 5.f +#define JUMP_FORCE 12.f +#define MAX_ACCEL 150.f +/* Grounded drag */ +#define FRICTION 0.86f +/* Increasing air drag, increases strafing speed */ +#define AIR_DRAG 0.98f +/* Responsiveness for turning movement direction to looked direction */ +#define CONTROL 15.f +#define CROUCH_HEIGHT 0.f +#define STAND_HEIGHT 1.f +#define BOTTOM_HEIGHT 0.5f + +#define NORMALIZE_INPUT 0 + +typedef struct { + Vector3 position; + Vector3 velocity; + Vector3 dir; + bool isGrounded; + Sound soundJump; +}Body; + +const int screenWidth = 1280; +const int screenHeight = 720; +Vector2 sensitivity = { 0.001f, 0.001f }; + +Body player; +Camera camera; +Vector2 lookRotation = { 0 }; +float headTimer; +float walkLerp; +float headLerp; +Vector2 lean; + +void UpdateDrawFrame(void); // Update and Draw one frame + +void DrawLevel(); + +void UpdateCameraAngle(Camera* camera, Vector2* rot, float headTimer, float walkLerp, Vector2 lean); + +void UpdateBody(Body* body, float rot, char side, char forward, bool jumpPressed, bool crouchHold); + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "Raylib Quake-like controller"); + InitAudioDevice(); + + player = (Body){ Vector3Zero(), Vector3Zero(), Vector3Zero(), false, LoadSound("resources/huh_jump.wav")}; + camera = (Camera){ 0 }; + camera.fovy = 60.f; // Camera field-of-view Y + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type + + lookRotation = Vector2Zero(); + headTimer = 0.f; + walkLerp = 0.f; + headLerp = STAND_HEIGHT; + lean = Vector2Zero(); + + camera.position = (Vector3){ + player.position.x, + player.position.y + (BOTTOM_HEIGHT + headLerp), + player.position.z, + }; + UpdateCameraAngle(&camera, &lookRotation, headTimer, walkLerp, lean); + + DisableCursor(); // Limit cursor to relative movement inside the window + +#if defined(PLATFORM_WEB) + emscripten_set_main_loop(UpdateDrawFrame, 0, 1); +#else + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + UpdateDrawFrame(); + } +#endif + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadSound(player.soundJump); + CloseAudioDevice(); + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + +void UpdateDrawFrame(void) +{ + // Update + //---------------------------------------------------------------------------------- + + Vector2 mouse_delta = GetMouseDelta(); + lookRotation.x -= mouse_delta.x * sensitivity.x; + lookRotation.y += mouse_delta.y * sensitivity.y; + + char sideway = (IsKeyDown(KEY_D) - IsKeyDown(KEY_A)); + char forward = (IsKeyDown(KEY_W) - IsKeyDown(KEY_S)); + bool crouching = IsKeyDown(KEY_LEFT_CONTROL); + UpdateBody(&player, lookRotation.x, sideway, forward, IsKeyPressed(KEY_SPACE), crouching); + + float delta = GetFrameTime(); + headLerp = Lerp(headLerp, (crouching ? CROUCH_HEIGHT : STAND_HEIGHT), 20.f * delta); + camera.position = (Vector3){ + player.position.x, + player.position.y + (BOTTOM_HEIGHT + headLerp), + player.position.z, + }; + + if (player.isGrounded && (forward != 0 || sideway != 0)) { + headTimer += delta * 3.f; + walkLerp = Lerp(walkLerp, 1.f, 10.f * delta); + camera.fovy = Lerp(camera.fovy, 55.f, 5.f * delta); + } + else { + walkLerp = Lerp(walkLerp, 0.f, 10.f * delta); + camera.fovy = Lerp(camera.fovy, 60.f, 5.f * delta); + } + + lean.x = Lerp(lean.x, sideway * 0.02f, 10.f * delta); + lean.y = Lerp(lean.y, forward * 0.015f, 10.f * delta); + + UpdateCameraAngle(&camera, &lookRotation, headTimer, walkLerp, lean); + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + DrawLevel(); + + EndMode3D(); + + // Draw info box + DrawRectangle(5, 5, 330, 100, Fade(SKYBLUE, 0.5f)); + DrawRectangleLines(5, 5, 330, 100, BLUE); + + DrawText("Camera controls:", 15, 15, 10, BLACK); + DrawText("- Move keys: W, A, S, D, Space, Left-Ctrl", 15, 30, 10, BLACK); + DrawText("- Look around: arrow keys or mouse", 15, 45, 10, BLACK); + DrawText(TextFormat("- Velocity Len: (%06.3f)", Vector2Length((Vector2) { player.velocity.x, player.velocity.z })), 15, 60, 10, BLACK); + + + EndDrawing(); + //---------------------------------------------------------------------------------- +} + +void UpdateBody(Body* body, float rot, char side, char forward, bool jumpPressed, bool crouchHold) +{ + Vector2 input = (Vector2){ (float)side, (float)-forward }; +#if defined(NORMALIZE_INPUT) + // Slow down diagonal movement + if (side != 0 & forward != 0) + { + input = Vector2Normalize(input); + } +#endif + + float delta = GetFrameTime(); + + if (!body->isGrounded) + { + body->velocity.y -= GRAVITY * delta; + } + if (body->isGrounded && jumpPressed) + { + body->velocity.y = JUMP_FORCE; + body->isGrounded = false; + SetSoundPitch(body->soundJump, 1.f + (GetRandomValue(-100, 100) * 0.001)); + PlaySound(body->soundJump); + } + + Vector3 front_vec = (Vector3){ sin(rot), 0.f, cos(rot) }; + Vector3 right_vec = (Vector3){ cos(-rot), 0.f, sin(-rot) }; + + Vector3 desired_dir = (Vector3){ + input.x * right_vec.x + input.y * front_vec.x, + 0.f, + input.x * right_vec.z + input.y * front_vec.z, + }; + + body->dir = Vector3Lerp(body->dir, desired_dir, CONTROL * delta); + + float decel = body->isGrounded ? FRICTION : AIR_DRAG; + Vector3 hvel = (Vector3){ + body->velocity.x * decel, + 0.f, + body->velocity.z * decel + }; + + float hvel_length = Vector3Length(hvel); // a.k.a. magnitude + if (hvel_length < MAX_SPEED * 0.01f) { + hvel = (Vector3){ 0 }; + } + + /* This is what creates strafing */ + float speed = Vector3DotProduct(hvel, body->dir); + + /* + Whenever the amount of acceleration to add is clamped by the maximum acceleration constant, + a Player can make the speed faster by bringing the direction closer to horizontal velocity angle + More info here: https://youtu.be/v3zT3Z5apaM?t=165 + */ + float max_speed = crouchHold ? CROUCH_SPEED : MAX_SPEED; + float accel = Clamp(max_speed - speed, 0.f, MAX_ACCEL * delta); + hvel.x += body->dir.x * accel; + hvel.z += body->dir.z * accel; + + body->velocity.x = hvel.x; + body->velocity.z = hvel.z; + + body->position.x += body->velocity.x * delta; + body->position.y += body->velocity.y * delta; + body->position.z += body->velocity.z * delta; + + /* Fancy collision system against "THE FLOOR" */ + if (body->position.y <= 0.f) + { + body->position.y = 0.f; + body->velocity.y = 0.f; + body->isGrounded = true; // <= enables jumping + } +} + +void UpdateCameraAngle(Camera* camera, Vector2* rot, float headTimer, float walkLerp, Vector2 lean) +{ + const Vector3 up = (Vector3){ 0.f, 1.f, 0.f }; + const Vector3 targetOffset = (Vector3){ 0.f, 0.f, -1.f }; + + /* Left & Right */ + Vector3 yaw = Vector3RotateByAxisAngle(targetOffset, up, rot->x); + + // Clamp view up + float maxAngleUp = Vector3Angle(up, yaw); + maxAngleUp -= 0.001f; // avoid numerical errors + if ( -(rot->y) > maxAngleUp) { rot->y = -maxAngleUp; } + + // Clamp view down + float maxAngleDown = Vector3Angle(Vector3Negate(up), yaw); + maxAngleDown *= -1.0f; // downwards angle is negative + maxAngleDown += 0.001f; // avoid numerical errors + if ( -(rot->y) < maxAngleDown) { rot->y = -maxAngleDown; } + + /* Up & Down */ + Vector3 right = Vector3Normalize(Vector3CrossProduct(yaw, up)); + + // Rotate view vector around right axis + Vector3 pitch = Vector3RotateByAxisAngle(yaw, right, -rot->y - lean.y); + + // Head animation + // Rotate up direction around forward axis + float _sin = sin(headTimer * PI); + float _cos = cos(headTimer * PI); + const float stepRotation = 0.01f; + camera->up = Vector3RotateByAxisAngle(up, pitch, _sin * stepRotation + lean.x); + + /* BOB */ + const float bobSide = 0.1f; + const float bobUp = 0.15f; + Vector3 bobbing = Vector3Scale(right, _sin * bobSide); + bobbing.y = fabsf(_cos * bobUp); + camera->position = Vector3Add(camera->position, Vector3Scale(bobbing, walkLerp)); + + camera->target = Vector3Add(camera->position, pitch); +} + + +void DrawLevel() +{ + const int floorExtent = 25; + const float tileSize = 5.f; + const Color tileColor1 = (Color){ 150, 200, 200, 255 }; + // Floor tiles + for (int y = -floorExtent; y < floorExtent; y++) + { + for (int x = -floorExtent; x < floorExtent; x++) + { + if ((y & 1) && (x & 1)) + { + DrawPlane((Vector3) { x * tileSize, 0.f, y * tileSize}, + (Vector2) {tileSize, tileSize}, tileColor1); + } + else if(!(y & 1) && !(x & 1)) + { + DrawPlane((Vector3) { x * tileSize, 0.f, y * tileSize}, + (Vector2) {tileSize, tileSize}, LIGHTGRAY); + } + } + } + + const Vector3 towerSize = (Vector3){ 16.f, 32.f, 16.f }; + const Color towerColor = (Color){ 150, 200, 200, 255 }; + + Vector3 towerPos = (Vector3){ 16.f, 16.f, 16.f }; + DrawCubeV(towerPos, towerSize, towerColor); + DrawCubeWiresV(towerPos, towerSize, DARKBLUE); + + towerPos.x *= -1; + DrawCubeV(towerPos, towerSize, towerColor); + DrawCubeWiresV(towerPos, towerSize, DARKBLUE); + + towerPos.z *= -1; + DrawCubeV(towerPos, towerSize, towerColor); + DrawCubeWiresV(towerPos, towerSize, DARKBLUE); + + towerPos.x *= -1; + DrawCubeV(towerPos, towerSize, towerColor); + DrawCubeWiresV(towerPos, towerSize, DARKBLUE); +} + diff --git a/examples/core/resources/huh_jump.wav b/examples/core/resources/huh_jump.wav new file mode 100644 index 0000000000000000000000000000000000000000..9ca8a2e683ef87d19f594be54d04a40dd0bade10 GIT binary patch literal 19050 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 Gd;ABC^mEGq literal 0 HcmV?d00001 From 4d5a56a5f89d37f96ca72d245e39679c2ffc4b46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agnis=20Aldi=C5=86=C5=A1=20=22NeZv=C4=93rs?= Date: Mon, 23 Jun 2025 13:15:14 +0300 Subject: [PATCH 046/242] change game's resolution --- examples/core/core_3d_fps_controller.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/core/core_3d_fps_controller.c b/examples/core/core_3d_fps_controller.c index 56d8c4543..ff348daef 100644 --- a/examples/core/core_3d_fps_controller.c +++ b/examples/core/core_3d_fps_controller.c @@ -58,8 +58,8 @@ typedef struct { Sound soundJump; }Body; -const int screenWidth = 1280; -const int screenHeight = 720; +const int screenWidth = 800; +const int screenHeight = 450; Vector2 sensitivity = { 0.001f, 0.001f }; Body player; From 4233544670c9a2ef9a30e4dcbe747395efea7c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agnis=20Aldi=C5=86=C5=A1=20=22NeZv=C4=93rs?= Date: Mon, 23 Jun 2025 13:32:08 +0300 Subject: [PATCH 047/242] Update core_3d_fps_controller.c --- examples/core/core_3d_fps_controller.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/core/core_3d_fps_controller.c b/examples/core/core_3d_fps_controller.c index ff348daef..d4936388b 100644 --- a/examples/core/core_3d_fps_controller.c +++ b/examples/core/core_3d_fps_controller.c @@ -356,5 +356,7 @@ void DrawLevel() towerPos.x *= -1; DrawCubeV(towerPos, towerSize, towerColor); DrawCubeWiresV(towerPos, towerSize, DARKBLUE); -} + // Red sun + DrawSphere((Vector3) { 300.f, 300.f, 0.f }, 100.f, (Color) { 255, 0, 0, 255 }); +} From a2cf878190569f2d37c90ff475594d90205eafb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agnis=20Aldi=C5=86=C5=A1=20=22NeZv=C4=93rs?= Date: Mon, 23 Jun 2025 13:43:38 +0300 Subject: [PATCH 048/242] cleanup variable shadowing --- examples/core/core_3d_fps_controller.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/core/core_3d_fps_controller.c b/examples/core/core_3d_fps_controller.c index d4936388b..3a21ac2dd 100644 --- a/examples/core/core_3d_fps_controller.c +++ b/examples/core/core_3d_fps_controller.c @@ -74,7 +74,7 @@ void UpdateDrawFrame(void); // Update and Draw one frame void DrawLevel(); -void UpdateCameraAngle(Camera* camera, Vector2* rot, float headTimer, float walkLerp, Vector2 lean); +void UpdateCameraAngle(); void UpdateBody(Body* body, float rot, char side, char forward, bool jumpPressed, bool crouchHold); @@ -167,7 +167,7 @@ void UpdateDrawFrame(void) lean.x = Lerp(lean.x, sideway * 0.02f, 10.f * delta); lean.y = Lerp(lean.y, forward * 0.015f, 10.f * delta); - UpdateCameraAngle(&camera, &lookRotation, headTimer, walkLerp, lean); + UpdateCameraAngle(); // Draw //---------------------------------------------------------------------------------- @@ -272,46 +272,46 @@ void UpdateBody(Body* body, float rot, char side, char forward, bool jumpPressed } } -void UpdateCameraAngle(Camera* camera, Vector2* rot, float headTimer, float walkLerp, Vector2 lean) +void UpdateCameraAngle() { const Vector3 up = (Vector3){ 0.f, 1.f, 0.f }; const Vector3 targetOffset = (Vector3){ 0.f, 0.f, -1.f }; /* Left & Right */ - Vector3 yaw = Vector3RotateByAxisAngle(targetOffset, up, rot->x); + Vector3 yaw = Vector3RotateByAxisAngle(targetOffset, up, lookRotation.x); // Clamp view up float maxAngleUp = Vector3Angle(up, yaw); maxAngleUp -= 0.001f; // avoid numerical errors - if ( -(rot->y) > maxAngleUp) { rot->y = -maxAngleUp; } + if ( -(lookRotation.y) > maxAngleUp) { lookRotation.y = -maxAngleUp; } // Clamp view down float maxAngleDown = Vector3Angle(Vector3Negate(up), yaw); maxAngleDown *= -1.0f; // downwards angle is negative maxAngleDown += 0.001f; // avoid numerical errors - if ( -(rot->y) < maxAngleDown) { rot->y = -maxAngleDown; } + if ( -(lookRotation.y) < maxAngleDown) { lookRotation.y = -maxAngleDown; } /* Up & Down */ Vector3 right = Vector3Normalize(Vector3CrossProduct(yaw, up)); // Rotate view vector around right axis - Vector3 pitch = Vector3RotateByAxisAngle(yaw, right, -rot->y - lean.y); + Vector3 pitch = Vector3RotateByAxisAngle(yaw, right, -lookRotation.y - lean.y); // Head animation // Rotate up direction around forward axis float _sin = sin(headTimer * PI); float _cos = cos(headTimer * PI); const float stepRotation = 0.01f; - camera->up = Vector3RotateByAxisAngle(up, pitch, _sin * stepRotation + lean.x); + camera.up = Vector3RotateByAxisAngle(up, pitch, _sin * stepRotation + lean.x); /* BOB */ const float bobSide = 0.1f; const float bobUp = 0.15f; Vector3 bobbing = Vector3Scale(right, _sin * bobSide); bobbing.y = fabsf(_cos * bobUp); - camera->position = Vector3Add(camera->position, Vector3Scale(bobbing, walkLerp)); + camera.position = Vector3Add(camera.position, Vector3Scale(bobbing, walkLerp)); - camera->target = Vector3Add(camera->position, pitch); + camera.target = Vector3Add(camera.position, pitch); } From fca2317640dc32e231316e33a0967cee70382c02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agnis=20Aldi=C5=86=C5=A1=20=22NeZv=C4=93rs?= Date: Mon, 23 Jun 2025 13:44:50 +0300 Subject: [PATCH 049/242] remove //#define PLATFORM_WEB --- examples/core/core_3d_fps_controller.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/core/core_3d_fps_controller.c b/examples/core/core_3d_fps_controller.c index 3a21ac2dd..ddb25ba8a 100644 --- a/examples/core/core_3d_fps_controller.c +++ b/examples/core/core_3d_fps_controller.c @@ -19,8 +19,6 @@ #include "raymath.h" #include "rcamera.h" -//#define PLATFORM_WEB - #if defined(PLATFORM_WEB) #include #endif From fa9653d179b56a1c663f0e34a7167c2f6f3c299d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agnis=20Aldi=C5=86=C5=A1=20=22NeZv=C4=93rs?= Date: Mon, 23 Jun 2025 13:50:24 +0300 Subject: [PATCH 050/242] Fix function call --- examples/core/core_3d_fps_controller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/core/core_3d_fps_controller.c b/examples/core/core_3d_fps_controller.c index ddb25ba8a..c4ef367e9 100644 --- a/examples/core/core_3d_fps_controller.c +++ b/examples/core/core_3d_fps_controller.c @@ -102,7 +102,7 @@ int main(void) player.position.y + (BOTTOM_HEIGHT + headLerp), player.position.z, }; - UpdateCameraAngle(&camera, &lookRotation, headTimer, walkLerp, lean); + UpdateCameraAngle(); DisableCursor(); // Limit cursor to relative movement inside the window From f6b7168ed4c2c70d15c01ed9f3710e8c976b7452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agnis=20Aldi=C5=86=C5=A1=20=22NeZv=C4=93rs?= Date: Mon, 23 Jun 2025 16:06:46 +0300 Subject: [PATCH 051/242] Add preview image --- examples/core/core_3d_fps_controller.png | Bin 0 -> 16483 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/core/core_3d_fps_controller.png diff --git a/examples/core/core_3d_fps_controller.png b/examples/core/core_3d_fps_controller.png new file mode 100644 index 0000000000000000000000000000000000000000..65a908d529e5eeb82682af2230caf35d1877b4b2 GIT binary patch literal 16483 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 a_X6v}toVg7jH&UmNmWtnezm-1`2Pd$cN=8@ literal 0 HcmV?d00001 From ed022e89e5aabd91c80288df0c671efb4c7e9492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agnis=20Aldi=C5=86=C5=A1=20=22NeZv=C4=93rs?= Date: Mon, 23 Jun 2025 16:22:14 +0300 Subject: [PATCH 052/242] fix preview crop --- examples/core/core_3d_fps_controller.png | Bin 16483 -> 9083 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/examples/core/core_3d_fps_controller.png b/examples/core/core_3d_fps_controller.png index 65a908d529e5eeb82682af2230caf35d1877b4b2..d4f83482ffbc77da8353983100d6590071719d23 100644 GIT binary patch literal 9083 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 literal 16483 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 a_X6v}toVg7jH&UmNmWtnezm-1`2Pd$cN=8@ From 6266d0f419cc4bfbc1912ead8a691a2855440d56 Mon Sep 17 00:00:00 2001 From: Diego Sanz <35377545+ElDigoXD@users.noreply.github.com> Date: Tue, 24 Jun 2025 13:48:20 +0200 Subject: [PATCH 053/242] Fix typo on config.h --- src/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.h b/src/config.h index dc6cc5893..ef01013b3 100644 --- a/src/config.h +++ b/src/config.h @@ -189,7 +189,7 @@ //------------------------------------------------------------------------------------ // Module: rtextures - Configuration Flags //------------------------------------------------------------------------------------ -// Selecte desired fileformats to be supported for image data loading +// Selected desired fileformats to be supported for image data loading #define SUPPORT_FILEFORMAT_PNG 1 //#define SUPPORT_FILEFORMAT_BMP 1 //#define SUPPORT_FILEFORMAT_TGA 1 From 44f670899ced285793166616cac9af4a1137d2d4 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 24 Jun 2025 20:11:35 +0200 Subject: [PATCH 054/242] REVIEWED: Avoid `rtext` dependency on `rcore_desktop_sdl` #4959 --- src/platforms/rcore_desktop_sdl.c | 53 ++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 6af1cb6c7..60753caf4 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -424,6 +424,8 @@ void ClosePlatform(void); // Close platform static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode); // Help convert SDL scancodes to raylib key +static int GetCodepointNextSDL(const char *text, int *codepointSize); // Get next codepoint in a byte sequence and bytes processed + //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- @@ -1601,13 +1603,18 @@ void PollInputEvents(void) { // NOTE: event.text.text data comes an UTF-8 text sequence but we register codepoints (int) - int codepointSize = 0; - // Check if there is space available in the queue if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) { // Add character (codepoint) to the queue - CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = GetCodepointNext(event.text.text, &codepointSize); + #if defined(PLATFORM_DESKTOP_SDL3) + unsigned int textLen = strlen(event.text.text); + unsigned int codepoint = (unsigned int)SDL_StepUTF8(&event.text.text, textLen); + #else + int codepointSize = 0; + codepoint = GetCodepointNextSDL(event.text.text, &codepointSize); + #endif + CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = codepoint; CORE.Input.Keyboard.charPressedQueueCount++; } } break; @@ -2093,4 +2100,42 @@ static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode) return KEY_NULL; // No equivalent key in Raylib } -// EOF + +// Get next codepoint in a byte sequence and bytes processed +static int GetCodepointNextSDL(const char *text, int *codepointSize) +{ + const char *ptr = text; + int codepoint = 0x3f; // Codepoint (defaults to '?') + *codepointSize = 1; + + // Get current codepoint and bytes processed + 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 + 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 + 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 + codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]); + *codepointSize = 2; + } + else if (0x00 == (0x80 & ptr[0])) + { + // 1 byte UTF-8 codepoint + codepoint = ptr[0]; + *codepointSize = 1; + } + + return codepoint; +} From d659037fbe73c262d45add495a446d9e226b3563 Mon Sep 17 00:00:00 2001 From: Maicon Date: Fri, 27 Jun 2025 08:35:55 +0100 Subject: [PATCH 055/242] Update emsdk version for zig build to fix the issue with the EM_BOOL --- build.zig.zon | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 3bf82afe5..571550860 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -12,8 +12,8 @@ .lazy = true, }, .emsdk = .{ - .url = "git+https://github.com/emscripten-core/emsdk#3.1.50", - .hash = "N-V-__8AALRTBQDo_pUJ8IQ-XiIyYwDKQVwnr7-7o5kvPDGE", + .url = "git+https://github.com/emscripten-core/emsdk#4.0.9", + .hash = "N-V-__8AAJl1DwBezhYo_VE6f53mPVm00R-Fk28NPW7P14EQ", .lazy = true, }, }, @@ -23,6 +23,6 @@ "build.zig.zon", "src", "examples", - "LICENSE", + "LICENSE", }, } From 6e9c3acaa4e321d70940783dc9bbfb65974e6d20 Mon Sep 17 00:00:00 2001 From: Maicon Date: Sun, 29 Jun 2025 09:04:58 +0100 Subject: [PATCH 056/242] Add run examples using zig and emscripten for web --- build.zig | 244 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 178 insertions(+), 66 deletions(-) diff --git a/build.zig b/build.zig index e445bf576..754078534 100644 --- a/build.zig +++ b/build.zig @@ -4,6 +4,9 @@ const builtin = @import("builtin"); /// Minimum supported version of Zig const min_ver = "0.13.0"; +const emccOutputDir = "zig-out" ++ std.fs.path.sep_str ++ "htmlout" ++ std.fs.path.sep_str; +const emccOutputFile = "index.html"; + comptime { const order = std.SemanticVersion.order; const parse = std.SemanticVersion.parse; @@ -45,6 +48,26 @@ fn emSdkSetupStep(b: *std.Build, emsdk: *std.Build.Dependency) !?*std.Build.Step } } +// Adaoted from Not-Nik/raylib-zig +fn emscriptenRunStep(b: *std.Build, emsdk: *std.Build.Dependency, examplePath: []const u8) !*std.Build.Step.Run { + const dot_emsc_path = emsdk.path("upstream/emscripten/cache/sysroot/include").getPath(b); + // If compiling on windows , use emrun.bat. + const emrunExe = switch (builtin.os.tag) { + .windows => "emrun.bat", + else => "emrun", + }; + var emrun_run_arg = try b.allocator.alloc(u8, dot_emsc_path.len + emrunExe.len + 1); + defer b.allocator.free(emrun_run_arg); + + if (b.sysroot == null) { + emrun_run_arg = try std.fmt.bufPrint(emrun_run_arg, "{s}", .{emrunExe}); + } else { + emrun_run_arg = try std.fmt.bufPrint(emrun_run_arg, "{s}" ++ std.fs.path.sep_str ++ "{s}", .{ dot_emsc_path, emrunExe }); + } + const run_cmd = b.addSystemCommand(&.{ emrun_run_arg, examplePath }); + return run_cmd; +} + /// A list of all flags from `src/config.h` that one may override const config_h_flags = outer: { // Set this value higher if compile errors happen as `src/config.h` gets larger @@ -85,6 +108,9 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. "-D_GNU_SOURCE", "-DGL_SILENCE_DEPRECATION=199309L", "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/3674 + // This is off by default but some linux distributions have it on by default + // No Stack Protector is set to prevent the issues when running the examples for emscripten + "-fno-stack-protector", }); if (options.shared) { @@ -511,12 +537,9 @@ fn addExamples( optimize: std.builtin.OptimizeMode, raylib: *std.Build.Step.Compile, ) !*std.Build.Step { - if (target.result.os.tag == .emscripten) { - return &b.addFail("Emscripten building via Zig unsupported").step; - } - const all = b.step(module, "All " ++ module ++ " examples"); const module_subpath = b.pathJoin(&.{ "examples", module }); + const module_resources = b.pathJoin(&.{ module_subpath, "resources@resources" }); var dir = try std.fs.cwd().openDir(b.pathFromRoot(module_subpath), .{ .iterate = true }); defer if (comptime builtin.zig_version.minor >= 12) dir.close(); @@ -530,71 +553,160 @@ fn addExamples( // zig's mingw headers do not include pthread.h if (std.mem.eql(u8, "core_loading_thread", name) and target.result.os.tag == .windows) continue; - const exe = b.addExecutable(.{ - .name = name, - .target = target, - .optimize = optimize, - }); - exe.addCSourceFile(.{ .file = b.path(path), .flags = &.{} }); - exe.linkLibC(); + if (target.result.os.tag == .emscripten) { + const exe_lib = b.addStaticLibrary(.{ + .name = name, + .target = target, + .optimize = optimize, + }); + exe_lib.addCSourceFile(.{ + .file = b.path(path), + .flags = &.{ + "-fno-stack-protector", + }, + }); + exe_lib.linkLibC(); + exe_lib.rdynamic = true; - // special examples that test using these external dependencies directly - // alongside raylib - if (std.mem.eql(u8, name, "rlgl_standalone")) { - exe.addIncludePath(b.path("src")); - exe.addIncludePath(b.path("src/external/glfw/include")); - if (!hasCSource(raylib.root_module, "rglfw.c")) { - exe.addCSourceFile(.{ .file = b.path("src/rglfw.c"), .flags = &.{} }); + exe_lib.root_module.addCMacro("PLATFORM_WEB", ""); + exe_lib.shared_memory = false; + exe_lib.root_module.single_threaded = false; + + if (std.mem.eql(u8, name, "raylib_opengl_interop")) { + exe_lib.addIncludePath(b.path("src/external")); } + + exe_lib.linkLibrary(raylib); + + // Include emscripten for cross compilation + if (b.lazyDependency("emsdk", .{})) |emsdk_dep| { + if (try emSdkSetupStep(b, emsdk_dep)) |emSdkStep| { + exe_lib.step.dependOn(&emSdkStep.step); + } + + exe_lib.addIncludePath(emsdk_dep.path("upstream/emscripten/cache/sysroot/include")); + // addAssets(b, exe_lib); + // Create the output directory because emcc can't do it. + + const emccOutputDirExample = b.pathJoin(&.{ emccOutputDir, name, std.fs.path.sep_str }); + const mkdir_command = switch (builtin.os.tag) { + .windows => b.addSystemCommand(&.{ "cmd.exe", "/c", "if", "not", "exist", emccOutputDirExample, "mkdir", emccOutputDirExample }), + else => b.addSystemCommand(&.{ "mkdir", "-p", emccOutputDirExample }), + }; + + const emcc_exe = switch (builtin.os.tag) { // TODO bundle emcc as a build dependency + .windows => "emcc.bat", + else => "emcc", + }; + + const emcc_exe_path = b.pathJoin(&.{ emsdk_dep.path("upstream/emscripten").getPath(b), emcc_exe }); + const emcc_command = b.addSystemCommand(&[_][]const u8{emcc_exe_path}); + emcc_command.step.dependOn(&mkdir_command.step); + const emccOutputDirExampleWithFile = b.pathJoin(&.{ emccOutputDir, name, std.fs.path.sep_str, emccOutputFile }); + emcc_command.addArgs(&[_][]const u8{ + "-o", + emccOutputDirExampleWithFile, + "-sFULL-ES3=1", + "-sUSE_GLFW=3", + "-sSTACK_OVERFLOW_CHECK=1", + "-sASYNCIFY", + "-O0", + "--emrun", + "--preload-file", + module_resources, + "--shell-file", + b.path("src/shell.html").getPath(b), + }); + + const link_items: []const *std.Build.Step.Compile = &.{ + raylib, + exe_lib, + }; + for (link_items) |item| { + emcc_command.addFileArg(item.getEmittedBin()); + emcc_command.step.dependOn(&item.step); + } + + const run_step = emscriptenRunStep(b, emsdk_dep, emccOutputDirExampleWithFile) catch |err| { + // do some stuff, maybe log an error + std.debug.print("EmscriptenRunStep error: {}\n", .{err}); + continue; + }; + run_step.step.dependOn(&emcc_command.step); + run_step.addArg("--no_browser"); + const run_option = b.step(name, name); + + run_option.dependOn(&run_step.step); + + all.dependOn(&emcc_command.step); + } + } else { + const exe = b.addExecutable(.{ + .name = name, + .target = target, + .optimize = optimize, + }); + exe.addCSourceFile(.{ .file = b.path(path), .flags = &.{} }); + exe.linkLibC(); + + // special examples that test using these external dependencies directly + // alongside raylib + if (std.mem.eql(u8, name, "rlgl_standalone")) { + exe.addIncludePath(b.path("src")); + exe.addIncludePath(b.path("src/external/glfw/include")); + if (!hasCSource(raylib.root_module, "rglfw.c")) { + exe.addCSourceFile(.{ .file = b.path("src/rglfw.c"), .flags = &.{} }); + } + } + if (std.mem.eql(u8, name, "raylib_opengl_interop")) { + exe.addIncludePath(b.path("src/external")); + } + + exe.linkLibrary(raylib); + + switch (target.result.os.tag) { + .windows => { + exe.linkSystemLibrary("winmm"); + exe.linkSystemLibrary("gdi32"); + exe.linkSystemLibrary("opengl32"); + + exe.root_module.addCMacro("PLATFORM_DESKTOP", ""); + }, + .linux => { + exe.linkSystemLibrary("GL"); + exe.linkSystemLibrary("rt"); + exe.linkSystemLibrary("dl"); + exe.linkSystemLibrary("m"); + exe.linkSystemLibrary("X11"); + + exe.root_module.addCMacro("PLATFORM_DESKTOP", ""); + }, + .macos => { + exe.linkFramework("Foundation"); + exe.linkFramework("Cocoa"); + exe.linkFramework("OpenGL"); + exe.linkFramework("CoreAudio"); + exe.linkFramework("CoreVideo"); + exe.linkFramework("IOKit"); + + exe.root_module.addCMacro("PLATFORM_DESKTOP", ""); + }, + else => { + @panic("Unsupported OS"); + }, + } + + const install_cmd = b.addInstallArtifact(exe, .{}); + + const run_cmd = b.addRunArtifact(exe); + run_cmd.cwd = b.path(module_subpath); + run_cmd.step.dependOn(&install_cmd.step); + + const run_step = b.step(name, name); + run_step.dependOn(&run_cmd.step); + + all.dependOn(&install_cmd.step); } - if (std.mem.eql(u8, name, "raylib_opengl_interop")) { - exe.addIncludePath(b.path("src/external")); - } - - exe.linkLibrary(raylib); - - switch (target.result.os.tag) { - .windows => { - exe.linkSystemLibrary("winmm"); - exe.linkSystemLibrary("gdi32"); - exe.linkSystemLibrary("opengl32"); - - exe.root_module.addCMacro("PLATFORM_DESKTOP", ""); - }, - .linux => { - exe.linkSystemLibrary("GL"); - exe.linkSystemLibrary("rt"); - exe.linkSystemLibrary("dl"); - exe.linkSystemLibrary("m"); - exe.linkSystemLibrary("X11"); - - exe.root_module.addCMacro("PLATFORM_DESKTOP", ""); - }, - .macos => { - exe.linkFramework("Foundation"); - exe.linkFramework("Cocoa"); - exe.linkFramework("OpenGL"); - exe.linkFramework("CoreAudio"); - exe.linkFramework("CoreVideo"); - exe.linkFramework("IOKit"); - - exe.root_module.addCMacro("PLATFORM_DESKTOP", ""); - }, - else => { - @panic("Unsupported OS"); - }, - } - - const install_cmd = b.addInstallArtifact(exe, .{}); - - const run_cmd = b.addRunArtifact(exe); - run_cmd.cwd = b.path(module_subpath); - run_cmd.step.dependOn(&install_cmd.step); - - const run_step = b.step(name, name); - run_step.dependOn(&run_cmd.step); - - all.dependOn(&install_cmd.step); } return all; } From 8f50436dc924cd8696d0fea1bf21ec8de521dfa3 Mon Sep 17 00:00:00 2001 From: Maicon Date: Sun, 29 Jun 2025 09:11:41 +0100 Subject: [PATCH 057/242] Fix comments --- build.zig | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/build.zig b/build.zig index 754078534..214de2970 100644 --- a/build.zig +++ b/build.zig @@ -585,9 +585,8 @@ fn addExamples( } exe_lib.addIncludePath(emsdk_dep.path("upstream/emscripten/cache/sysroot/include")); - // addAssets(b, exe_lib); - // Create the output directory because emcc can't do it. + // Create the output directory because emcc can't do it. const emccOutputDirExample = b.pathJoin(&.{ emccOutputDir, name, std.fs.path.sep_str }); const mkdir_command = switch (builtin.os.tag) { .windows => b.addSystemCommand(&.{ "cmd.exe", "/c", "if", "not", "exist", emccOutputDirExample, "mkdir", emccOutputDirExample }), @@ -628,7 +627,6 @@ fn addExamples( } const run_step = emscriptenRunStep(b, emsdk_dep, emccOutputDirExampleWithFile) catch |err| { - // do some stuff, maybe log an error std.debug.print("EmscriptenRunStep error: {}\n", .{err}); continue; }; From 1db006b0825f7e0bb2db59f2aa683211857f8388 Mon Sep 17 00:00:00 2001 From: Colin Woodbury Date: Mon, 30 Jun 2025 05:40:17 +0900 Subject: [PATCH 058/242] docs: mention another Common Lisp binding --- BINDINGS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 986c27abc..719a902a0 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -18,6 +18,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers | [cl-raylib](https://github.com/longlene/cl-raylib) | 4.0 | [Common Lisp](https://common-lisp.net) | MIT | | [claylib/wrap](https://github.com/defun-games/claylib) | 4.5 | [Common Lisp](https://common-lisp.net) | Zlib | | [claw-raylib](https://github.com/bohonghuang/claw-raylib) | **auto** | [Common Lisp](https://common-lisp.net) | Apache-2.0 | +| [raylib](https://github.com/fosskers/raylib) | 5.5 | [Common Lisp](https://common-lisp.net) | MPL-2.0 | | [chez-raylib](https://github.com/Yunoinsky/chez-raylib) | **auto** | [Chez Scheme](https://cisco.github.io/ChezScheme) | GPLv3 | | [CLIPSraylib](https://github.com/mrryanjohnston/CLIPSraylib) | **auto** | [CLIPS](https://www.clipsrules.net/) | MIT | | [raylib-cr](https://github.com/sol-vin/raylib-cr) | 4.6-dev (5e1a81) | [Crystal](https://crystal-lang.org) | Apache-2.0 | @@ -50,7 +51,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers | [raylib-luajit-generated](https://github.com/james2doyle/raylib-luajit-generated) | 5.5 | [Lua](http://www.lua.org) | MIT | | [raylib-matte](https://github.com/jcorks/raylib-matte) | 4.6-dev | [Matte](https://github.com/jcorks/matte) | **???** | | [Raylib.nelua](https://github.com/AuzFox/Raylib.nelua) | **5.5** | [nelua](https://nelua.io) | Zlib | -| [raylib-bindings](https://github.com/vaiorabbit/raylib-bindings) | 5.6-dev | [Ruby](https://www.ruby-lang.org/en) | Zlib | +| [raylib-bindings](https://github.com/vaiorabbit/raylib-bindings) | 5.6-dev | [Ruby](https://www.ruby-lang.org/en) | Zlib | | [naylib](https://github.com/planetis-m/naylib) | **5.6-dev** | [Nim](https://nim-lang.org) | MIT | | [node-raylib](https://github.com/RobLoach/node-raylib) | 4.5 | [Node.js](https://nodejs.org/en) | Zlib | | [raylib-odin](https://github.com/odin-lang/Odin/tree/master/vendor/raylib) | **5.5** | [Odin](https://odin-lang.org) | BSD-3Clause | From 0cae8890b85395285df3767de4baf1925148b8ea Mon Sep 17 00:00:00 2001 From: Maicon Date: Mon, 30 Jun 2025 09:54:20 +0100 Subject: [PATCH 059/242] Remove -fno-stack-protector as it is not needed and add requestFullscreen on exported methods --- build.zig | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/build.zig b/build.zig index 214de2970..4026a23eb 100644 --- a/build.zig +++ b/build.zig @@ -108,9 +108,6 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. "-D_GNU_SOURCE", "-DGL_SILENCE_DEPRECATION=199309L", "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/3674 - // This is off by default but some linux distributions have it on by default - // No Stack Protector is set to prevent the issues when running the examples for emscripten - "-fno-stack-protector", }); if (options.shared) { @@ -365,7 +362,6 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. setDesktopPlatform(raylib, options.platform); }, .emscripten => { - // Include emscripten for cross compilation if (b.lazyDependency("emsdk", .{})) |dep| { if (try emSdkSetupStep(b, dep)) |emSdkStep| { raylib.step.dependOn(&emSdkStep.step); @@ -561,19 +557,17 @@ fn addExamples( }); exe_lib.addCSourceFile(.{ .file = b.path(path), - .flags = &.{ - "-fno-stack-protector", - }, + .flags = &.{}, }); exe_lib.linkLibC(); - exe_lib.rdynamic = true; - - exe_lib.root_module.addCMacro("PLATFORM_WEB", ""); - exe_lib.shared_memory = false; - exe_lib.root_module.single_threaded = false; + if (std.mem.eql(u8, name, "rlgl_standalone")) { + //TODO: Make rlgl_standalone example work + continue; + } if (std.mem.eql(u8, name, "raylib_opengl_interop")) { - exe_lib.addIncludePath(b.path("src/external")); + //TODO: Make raylib_opengl_interop example work + continue; } exe_lib.linkLibrary(raylib); @@ -593,7 +587,7 @@ fn addExamples( else => b.addSystemCommand(&.{ "mkdir", "-p", emccOutputDirExample }), }; - const emcc_exe = switch (builtin.os.tag) { // TODO bundle emcc as a build dependency + const emcc_exe = switch (builtin.os.tag) { .windows => "emcc.bat", else => "emcc", }; @@ -608,6 +602,7 @@ fn addExamples( "-sFULL-ES3=1", "-sUSE_GLFW=3", "-sSTACK_OVERFLOW_CHECK=1", + "-sEXPORTED_RUNTIME_METHODS=['requestFullscreen']", "-sASYNCIFY", "-O0", "--emrun", @@ -626,10 +621,7 @@ fn addExamples( emcc_command.step.dependOn(&item.step); } - const run_step = emscriptenRunStep(b, emsdk_dep, emccOutputDirExampleWithFile) catch |err| { - std.debug.print("EmscriptenRunStep error: {}\n", .{err}); - continue; - }; + const run_step = try emscriptenRunStep(b, emsdk_dep, emccOutputDirExampleWithFile); run_step.step.dependOn(&emcc_command.step); run_step.addArg("--no_browser"); const run_option = b.step(name, name); From eef1bac3e249212e2d83da48c71a4ca73cf535b3 Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Mon, 30 Jun 2025 19:38:34 +0100 Subject: [PATCH 060/242] fix misspelling --- build.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.zig b/build.zig index 4026a23eb..14e9af21e 100644 --- a/build.zig +++ b/build.zig @@ -48,7 +48,7 @@ fn emSdkSetupStep(b: *std.Build, emsdk: *std.Build.Dependency) !?*std.Build.Step } } -// Adaoted from Not-Nik/raylib-zig +// Adapted from Not-Nik/raylib-zig fn emscriptenRunStep(b: *std.Build, emsdk: *std.Build.Dependency, examplePath: []const u8) !*std.Build.Step.Run { const dot_emsc_path = emsdk.path("upstream/emscripten/cache/sysroot/include").getPath(b); // If compiling on windows , use emrun.bat. From bee524e5e6443a8abe6ae76a943db6109a26171f Mon Sep 17 00:00:00 2001 From: sir-irk Date: Tue, 1 Jul 2025 13:23:05 -0500 Subject: [PATCH 061/242] fixing offset for processing tangents for gltf loading --- src/rmodels.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 84daabc15..ac6f4c594 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5577,7 +5577,7 @@ static Model LoadGLTF(const char *fileName) } else TRACELOG(LOG_WARNING, "MODEL: [%s] Normal attribute data format not supported, use vec3 float", fileName); } - else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_tangent) // TANGENT, vec3, float + else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_tangent) // TANGENT, vec4, float, w is tangent basis sign { cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data; @@ -5593,10 +5593,10 @@ static Model LoadGLTF(const char *fileName) float *tangents = model.meshes[meshIndex].tangents; for (unsigned int k = 0; k < attribute->count; k++) { - Vector3 tt = Vector3Transform((Vector3){ tangents[3*k], tangents[3*k+1], tangents[3*k+2] }, worldMatrix); - tangents[3*k] = tt.x; - tangents[3*k+1] = tt.y; - tangents[3*k+2] = tt.z; + Vector3 tt = Vector3Transform((Vector3){ tangents[4*k], tangents[4*k+1], tangents[4*k+2] }, worldMatrix); + tangents[4*k] = tt.x; + tangents[4*k+1] = tt.y; + tangents[4*k+2] = tt.z; } } else TRACELOG(LOG_WARNING, "MODEL: [%s] Tangent attribute data format not supported, use vec4 float", fileName); From f86295732a5a15156917d2b92696826bae72ac6d Mon Sep 17 00:00:00 2001 From: sir-irk Date: Tue, 1 Jul 2025 15:18:11 -0500 Subject: [PATCH 062/242] fixing shader tangents to be vec4 --- examples/shaders/resources/shaders/glsl100/pbr.vs | 6 +++--- examples/shaders/resources/shaders/glsl120/pbr.vs | 8 ++++---- examples/shaders/resources/shaders/glsl330/pbr.vs | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/shaders/resources/shaders/glsl100/pbr.vs b/examples/shaders/resources/shaders/glsl100/pbr.vs index a55c0ea4b..079ccdc70 100644 --- a/examples/shaders/resources/shaders/glsl100/pbr.vs +++ b/examples/shaders/resources/shaders/glsl100/pbr.vs @@ -4,7 +4,7 @@ attribute vec3 vertexPosition; attribute vec2 vertexTexCoord; attribute vec3 vertexNormal; -attribute vec3 vertexTangent; +attribute vec4 vertexTangent; attribute vec4 vertexColor; // Input uniform values @@ -52,7 +52,7 @@ mat3 transpose(mat3 m) void main() { // Compute binormal from vertex normal and tangent - vec3 vertexBinormal = cross(vertexNormal, vertexTangent); + vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz) * vertexTangent.w; // Compute fragment normal based on normal transformations mat3 normalMatrix = transpose(inverse(mat3(matModel))); @@ -62,7 +62,7 @@ void main() fragTexCoord = vertexTexCoord*2.0; fragNormal = normalize(normalMatrix*vertexNormal); - vec3 fragTangent = normalize(normalMatrix*vertexTangent); + vec3 fragTangent = normalize(normalMatrix*vertexTangent.xyz) * vertexTangent.w; fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal); vec3 fragBinormal = normalize(normalMatrix*vertexBinormal); fragBinormal = cross(fragNormal, fragTangent); diff --git a/examples/shaders/resources/shaders/glsl120/pbr.vs b/examples/shaders/resources/shaders/glsl120/pbr.vs index d3cc66488..4b68ef673 100644 --- a/examples/shaders/resources/shaders/glsl120/pbr.vs +++ b/examples/shaders/resources/shaders/glsl120/pbr.vs @@ -4,7 +4,7 @@ attribute vec3 vertexPosition; attribute vec2 vertexTexCoord; attribute vec3 vertexNormal; -attribute vec3 vertexTangent; +attribute vec4 vertexTangent; attribute vec4 vertexColor; // Input uniform values @@ -52,7 +52,7 @@ mat3 transpose(mat3 m) void main() { // Compute binormal from vertex normal and tangent - vec3 vertexBinormal = cross(vertexNormal, vertexTangent); + vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz) * vertexTangent.w; // Compute fragment normal based on normal transformations mat3 normalMatrix = transpose(inverse(mat3(matModel))); @@ -62,7 +62,7 @@ void main() fragTexCoord = vertexTexCoord*2.0; fragNormal = normalize(normalMatrix*vertexNormal); - vec3 fragTangent = normalize(normalMatrix*vertexTangent); + vec3 fragTangent = normalize(normalMatrix*vertexTangent.xyz) * vertexTangent.w; fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal); vec3 fragBinormal = normalize(normalMatrix*vertexBinormal); fragBinormal = cross(fragNormal, fragTangent); @@ -71,4 +71,4 @@ void main() // Calculate final vertex position gl_Position = mvp*vec4(vertexPosition, 1.0); -} \ No newline at end of file +} diff --git a/examples/shaders/resources/shaders/glsl330/pbr.vs b/examples/shaders/resources/shaders/glsl330/pbr.vs index 6f262313c..a9e65e96c 100644 --- a/examples/shaders/resources/shaders/glsl330/pbr.vs +++ b/examples/shaders/resources/shaders/glsl330/pbr.vs @@ -4,7 +4,7 @@ in vec3 vertexPosition; in vec2 vertexTexCoord; in vec3 vertexNormal; -in vec3 vertexTangent; +in vec4 vertexTangent; in vec4 vertexColor; // Input uniform values @@ -26,7 +26,7 @@ const float normalOffset = 0.1; void main() { // Compute binormal from vertex normal and tangent - vec3 vertexBinormal = cross(vertexNormal, vertexTangent); + vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz) * vertexTangent.w; // Compute fragment normal based on normal transformations mat3 normalMatrix = transpose(inverse(mat3(matModel))); @@ -36,7 +36,7 @@ void main() fragTexCoord = vertexTexCoord*2.0; fragNormal = normalize(normalMatrix*vertexNormal); - vec3 fragTangent = normalize(normalMatrix*vertexTangent); + vec3 fragTangent = normalize(normalMatrix*vertexTangent.xyz) * vertexTangent.w; fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal); vec3 fragBinormal = normalize(normalMatrix*vertexBinormal); fragBinormal = cross(fragNormal, fragTangent); @@ -45,4 +45,4 @@ void main() // Calculate final vertex position gl_Position = mvp*vec4(vertexPosition, 1.0); -} \ No newline at end of file +} From ed509193d9d311648f5b6e4010588a1186118f19 Mon Sep 17 00:00:00 2001 From: sir-irk Date: Tue, 1 Jul 2025 15:30:50 -0500 Subject: [PATCH 063/242] remving w multiply on the tangent itself --- examples/shaders/resources/shaders/glsl100/pbr.vs | 2 +- examples/shaders/resources/shaders/glsl120/pbr.vs | 2 +- examples/shaders/resources/shaders/glsl330/pbr.vs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/shaders/resources/shaders/glsl100/pbr.vs b/examples/shaders/resources/shaders/glsl100/pbr.vs index 079ccdc70..baf003842 100644 --- a/examples/shaders/resources/shaders/glsl100/pbr.vs +++ b/examples/shaders/resources/shaders/glsl100/pbr.vs @@ -62,7 +62,7 @@ void main() fragTexCoord = vertexTexCoord*2.0; fragNormal = normalize(normalMatrix*vertexNormal); - vec3 fragTangent = normalize(normalMatrix*vertexTangent.xyz) * vertexTangent.w; + vec3 fragTangent = normalize(normalMatrix*vertexTangent.xyz); fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal); vec3 fragBinormal = normalize(normalMatrix*vertexBinormal); fragBinormal = cross(fragNormal, fragTangent); diff --git a/examples/shaders/resources/shaders/glsl120/pbr.vs b/examples/shaders/resources/shaders/glsl120/pbr.vs index 4b68ef673..e9750a60a 100644 --- a/examples/shaders/resources/shaders/glsl120/pbr.vs +++ b/examples/shaders/resources/shaders/glsl120/pbr.vs @@ -62,7 +62,7 @@ void main() fragTexCoord = vertexTexCoord*2.0; fragNormal = normalize(normalMatrix*vertexNormal); - vec3 fragTangent = normalize(normalMatrix*vertexTangent.xyz) * vertexTangent.w; + vec3 fragTangent = normalize(normalMatrix*vertexTangent.xyz); fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal); vec3 fragBinormal = normalize(normalMatrix*vertexBinormal); fragBinormal = cross(fragNormal, fragTangent); diff --git a/examples/shaders/resources/shaders/glsl330/pbr.vs b/examples/shaders/resources/shaders/glsl330/pbr.vs index a9e65e96c..8aabb6baf 100644 --- a/examples/shaders/resources/shaders/glsl330/pbr.vs +++ b/examples/shaders/resources/shaders/glsl330/pbr.vs @@ -36,7 +36,7 @@ void main() fragTexCoord = vertexTexCoord*2.0; fragNormal = normalize(normalMatrix*vertexNormal); - vec3 fragTangent = normalize(normalMatrix*vertexTangent.xyz) * vertexTangent.w; + vec3 fragTangent = normalize(normalMatrix*vertexTangent.xyz); fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal); vec3 fragBinormal = normalize(normalMatrix*vertexBinormal); fragBinormal = cross(fragNormal, fragTangent); From f1600a0c7e981c04aad18371d757cf799f28dd0e Mon Sep 17 00:00:00 2001 From: Maicon Date: Fri, 4 Jul 2025 10:24:32 +0100 Subject: [PATCH 064/242] Fix issue on zig build emscripten run if the user has not installed emsdk --- build.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.zig b/build.zig index 14e9af21e..501d7a229 100644 --- a/build.zig +++ b/build.zig @@ -60,7 +60,7 @@ fn emscriptenRunStep(b: *std.Build, emsdk: *std.Build.Dependency, examplePath: [ defer b.allocator.free(emrun_run_arg); if (b.sysroot == null) { - emrun_run_arg = try std.fmt.bufPrint(emrun_run_arg, "{s}", .{emrunExe}); + emrun_run_arg = try std.fmt.bufPrint(emrun_run_arg, "{s}" ++ std.fs.path.sep_str ++ "{s}", .{ emsdk.path("upstream/emscripten").getPath(b), emrunExe }); } else { emrun_run_arg = try std.fmt.bufPrint(emrun_run_arg, "{s}" ++ std.fs.path.sep_str ++ "{s}", .{ dot_emsc_path, emrunExe }); } From 910f4083e180df6d3d49584131665f0e9e61049b Mon Sep 17 00:00:00 2001 From: EmilSylveon Date: Sat, 5 Jul 2025 00:53:01 +0300 Subject: [PATCH 065/242] update dr_libs --- src/external/dr_flac.h | 462 +++++++++++---------- src/external/dr_mp3.h | 899 ++++++++++++++++++++++++++++++++--------- src/external/dr_wav.h | 492 +++++++++++++++------- 3 files changed, 1282 insertions(+), 571 deletions(-) diff --git a/src/external/dr_flac.h b/src/external/dr_flac.h index 14324cf37..497fcddd5 100644 --- a/src/external/dr_flac.h +++ b/src/external/dr_flac.h @@ -1,121 +1,12 @@ /* FLAC audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. -dr_flac - v0.12.42 - 2023-11-02 +dr_flac - v0.13.0 - TBD David Reid - mackron@gmail.com GitHub: https://github.com/mackron/dr_libs */ -/* -RELEASE NOTES - v0.12.0 -======================= -Version 0.12.0 has breaking API changes including changes to the existing API and the removal of deprecated APIs. - - -Improved Client-Defined Memory Allocation ------------------------------------------ -The main change with this release is the addition of a more flexible way of implementing custom memory allocation routines. The -existing system of DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE are still in place and will be used by default when no custom -allocation callbacks are specified. - -To use the new system, you pass in a pointer to a drflac_allocation_callbacks object to drflac_open() and family, like this: - - void* my_malloc(size_t sz, void* pUserData) - { - return malloc(sz); - } - void* my_realloc(void* p, size_t sz, void* pUserData) - { - return realloc(p, sz); - } - void my_free(void* p, void* pUserData) - { - free(p); - } - - ... - - drflac_allocation_callbacks allocationCallbacks; - allocationCallbacks.pUserData = &myData; - allocationCallbacks.onMalloc = my_malloc; - allocationCallbacks.onRealloc = my_realloc; - allocationCallbacks.onFree = my_free; - drflac* pFlac = drflac_open_file("my_file.flac", &allocationCallbacks); - -The advantage of this new system is that it allows you to specify user data which will be passed in to the allocation routines. - -Passing in null for the allocation callbacks object will cause dr_flac to use defaults which is the same as DRFLAC_MALLOC, -DRFLAC_REALLOC and DRFLAC_FREE and the equivalent of how it worked in previous versions. - -Every API that opens a drflac object now takes this extra parameter. These include the following: - - drflac_open() - drflac_open_relaxed() - drflac_open_with_metadata() - drflac_open_with_metadata_relaxed() - drflac_open_file() - drflac_open_file_with_metadata() - drflac_open_memory() - drflac_open_memory_with_metadata() - drflac_open_and_read_pcm_frames_s32() - drflac_open_and_read_pcm_frames_s16() - drflac_open_and_read_pcm_frames_f32() - drflac_open_file_and_read_pcm_frames_s32() - drflac_open_file_and_read_pcm_frames_s16() - drflac_open_file_and_read_pcm_frames_f32() - drflac_open_memory_and_read_pcm_frames_s32() - drflac_open_memory_and_read_pcm_frames_s16() - drflac_open_memory_and_read_pcm_frames_f32() - - - -Optimizations -------------- -Seeking performance has been greatly improved. A new binary search based seeking algorithm has been introduced which significantly -improves performance over the brute force method which was used when no seek table was present. Seek table based seeking also takes -advantage of the new binary search seeking system to further improve performance there as well. Note that this depends on CRC which -means it will be disabled when DR_FLAC_NO_CRC is used. - -The SSE4.1 pipeline has been cleaned up and optimized. You should see some improvements with decoding speed of 24-bit files in -particular. 16-bit streams should also see some improvement. - -drflac_read_pcm_frames_s16() has been optimized. Previously this sat on top of drflac_read_pcm_frames_s32() and performed it's s32 -to s16 conversion in a second pass. This is now all done in a single pass. This includes SSE2 and ARM NEON optimized paths. - -A minor optimization has been implemented for drflac_read_pcm_frames_s32(). This will now use an SSE2 optimized pipeline for stereo -channel reconstruction which is the last part of the decoding process. - -The ARM build has seen a few improvements. The CLZ (count leading zeroes) and REV (byte swap) instructions are now used when -compiling with GCC and Clang which is achieved using inline assembly. The CLZ instruction requires ARM architecture version 5 at -compile time and the REV instruction requires ARM architecture version 6. - -An ARM NEON optimized pipeline has been implemented. To enable this you'll need to add -mfpu=neon to the command line when compiling. - - -Removed APIs ------------- -The following APIs were deprecated in version 0.11.0 and have been completely removed in version 0.12.0: - - drflac_read_s32() -> drflac_read_pcm_frames_s32() - drflac_read_s16() -> drflac_read_pcm_frames_s16() - drflac_read_f32() -> drflac_read_pcm_frames_f32() - drflac_seek_to_sample() -> drflac_seek_to_pcm_frame() - drflac_open_and_decode_s32() -> drflac_open_and_read_pcm_frames_s32() - drflac_open_and_decode_s16() -> drflac_open_and_read_pcm_frames_s16() - drflac_open_and_decode_f32() -> drflac_open_and_read_pcm_frames_f32() - drflac_open_and_decode_file_s32() -> drflac_open_file_and_read_pcm_frames_s32() - drflac_open_and_decode_file_s16() -> drflac_open_file_and_read_pcm_frames_s16() - drflac_open_and_decode_file_f32() -> drflac_open_file_and_read_pcm_frames_f32() - drflac_open_and_decode_memory_s32() -> drflac_open_memory_and_read_pcm_frames_s32() - drflac_open_and_decode_memory_s16() -> drflac_open_memory_and_read_pcm_frames_s16() - drflac_open_and_decode_memory_f32() -> drflac_open_memroy_and_read_pcm_frames_f32() - -Prior versions of dr_flac operated on a per-sample basis whereas now it operates on PCM frames. The removed APIs all relate -to the old per-sample APIs. You now need to use the "pcm_frame" versions. -*/ - - /* Introduction ============ @@ -179,7 +70,7 @@ reports metadata to the application through the use of a callback, and every met The main opening APIs (`drflac_open()`, etc.) will fail if the header is not present. The presents a problem in certain scenarios such as broadcast style streams or internet radio where the header may not be present because the user has started playback mid-stream. To handle this, use the relaxed APIs: - + `drflac_open_relaxed()` `drflac_open_with_metadata_relaxed()` @@ -234,8 +125,8 @@ extern "C" { #define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x) #define DRFLAC_VERSION_MAJOR 0 -#define DRFLAC_VERSION_MINOR 12 -#define DRFLAC_VERSION_REVISION 42 +#define DRFLAC_VERSION_MINOR 13 +#define DRFLAC_VERSION_REVISION 0 #define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) #include /* For size_t. */ @@ -348,11 +239,11 @@ but also more memory. In my testing there is diminishing returns after about 4KB #define DRFLAC_64BIT #endif -#if defined(__x86_64__) || defined(_M_X64) +#if defined(__x86_64__) || (defined(_M_X64) && !defined(_M_ARM64EC)) #define DRFLAC_X64 #elif defined(__i386) || defined(_M_IX86) #define DRFLAC_X86 -#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) +#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) #define DRFLAC_ARM #endif /* End Architecture Detection */ @@ -406,8 +297,9 @@ typedef enum typedef enum { - drflac_seek_origin_start, - drflac_seek_origin_current + DRFLAC_SEEK_SET, + DRFLAC_SEEK_CUR, + DRFLAC_SEEK_END } drflac_seek_origin; /* The order of members in this structure is important because we map this directly to the raw data within the SEEKTABLE metadata block. */ @@ -547,7 +439,7 @@ offset (in) The number of bytes to move, relative to the origin. Will never be negative. origin (in) - The origin of the seek - the current position or the start of the stream. + The origin of the seek - the current position, the start of the stream, or the end of the stream. Return Value @@ -557,14 +449,32 @@ Whether or not the seek was successful. Remarks ------- -The offset will never be negative. Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which will be -either drflac_seek_origin_start or drflac_seek_origin_current. +Seeking relative to the start and the current position must always be supported. If seeking from the end of the stream is not supported, return DRFLAC_FALSE. When seeking to a PCM frame using drflac_seek_to_pcm_frame(), dr_flac may call this with an offset beyond the end of the FLAC stream. This needs to be detected and handled by returning DRFLAC_FALSE. */ typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin); +/* +Callback for when the current position in the stream needs to be retrieved. + + +Parameters +---------- +pUserData (in) + The user data that was passed to drflac_open() and family. + +pCursor (out) + A pointer to a variable to receive the current position in the stream. + + +Return Value +------------ +Whether or not the operation was successful. +*/ +typedef drflac_bool32 (* drflac_tell_proc)(void* pUserData, drflac_int64* pCursor); + /* Callback for when a metadata block is read. @@ -603,6 +513,9 @@ typedef struct /* The function to call when the current read position needs to be moved. */ drflac_seek_proc onSeek; + /* The function to call when the current read position needs to be retrieved. */ + drflac_tell_proc onTell; + /* The user data to pass around to onRead and onSeek. */ void* pUserData; @@ -828,7 +741,7 @@ drflac_open_memory() drflac_open_with_metadata() drflac_close() */ -DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); /* Opens a FLAC stream with relaxed validation of the header block. @@ -869,7 +782,7 @@ force your `onRead` callback to return 0, which dr_flac will use as an indicator Use `drflac_open_with_metadata_relaxed()` if you need access to metadata. */ -DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); /* Opens a FLAC decoder and notifies the caller of the metadata chunks (album art, etc.). @@ -926,7 +839,7 @@ drflac_open_memory_with_metadata() drflac_open() drflac_close() */ -DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); /* The same as drflac_open_with_metadata(), except attempts to open the stream even when a header block is not present. @@ -936,7 +849,7 @@ See Also drflac_open_with_metadata() drflac_open_relaxed() */ -DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); /* Closes the given FLAC decoder. @@ -1234,13 +1147,13 @@ read samples into a dynamically sized buffer on the heap until no samples are le Do not call this function on a broadcast type of stream (like internet radio streams and whatnot). */ -DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); /* Same as drflac_open_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */ -DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); /* Same as drflac_open_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */ -DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); #ifndef DR_FLAC_NO_STDIO /* Same as drflac_open_and_read_pcm_frames_s32() except opens the decoder from a file. */ @@ -2960,25 +2873,25 @@ static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFro */ if (offsetFromStart > 0x7FFFFFFF) { drflac_uint64 bytesRemaining = offsetFromStart; - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, DRFLAC_SEEK_SET)) { return DRFLAC_FALSE; } bytesRemaining -= 0x7FFFFFFF; while (bytesRemaining > 0x7FFFFFFF) { - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, DRFLAC_SEEK_CUR)) { return DRFLAC_FALSE; } bytesRemaining -= 0x7FFFFFFF; } if (bytesRemaining > 0) { - if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) { + if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, DRFLAC_SEEK_CUR)) { return DRFLAC_FALSE; } } } else { - if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) { + if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, DRFLAC_SEEK_SET)) { return DRFLAC_FALSE; } } @@ -5393,6 +5306,12 @@ static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe return DRFLAC_FALSE; } + /* + Default to 0 for the LPC order. It's important that we always set this to 0 for non LPC + and FIXED subframes because we'll be using it in a generic validation check later. + */ + pSubframe->lpcOrder = 0; + type = (header & 0x7E) >> 1; if (type == 0) { pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT; @@ -5465,6 +5384,18 @@ static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, pSubframe->pSamplesS32 = pDecodedSamplesOut; + /* + pDecodedSamplesOut will be pointing to a buffer that was allocated with enough memory to store + maxBlockSizeInPCMFrames samples (as specified in the FLAC header). We need to guard against an + overflow here. At a higher level we are checking maxBlockSizeInPCMFrames from the header, but + here we need to do an additional check to ensure this frame's block size fully encompasses any + warmup samples which is determined by the LPC order. For non LPC and FIXED subframes, the LPC + order will be have been set to 0 in drflac__read_subframe_header(). + */ + if (frame->header.blockSizeInPCMFrames < pSubframe->lpcOrder) { + return DRFLAC_FALSE; + } + switch (pSubframe->subframeType) { case DRFLAC_SUBFRAME_CONSTANT: @@ -6312,6 +6243,7 @@ typedef struct { drflac_read_proc onRead; drflac_seek_proc onSeek; + drflac_tell_proc onTell; drflac_meta_proc onMeta; drflac_container container; void* pUserData; @@ -6479,7 +6411,7 @@ static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbac } -static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeekpointCount, drflac_allocation_callbacks* pAllocationCallbacks) +static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeekpointCount, drflac_allocation_callbacks* pAllocationCallbacks) { /* We want to keep track of the byte position in the stream of the seektable. At the time of calling this function we know that @@ -6489,6 +6421,8 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d drflac_uint64 seektablePos = 0; drflac_uint32 seektableSize = 0; + (void)onTell; + for (;;) { drflac_metadata metadata; drflac_uint8 isLastBlock = 0; @@ -6702,10 +6636,10 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d /* Skip to the index point count */ pRunningData += 35; - + indexCount = pRunningData[0]; pRunningData += 1; - + bufferSize += indexCount * sizeof(drflac_cuesheet_track_index); /* Quick validation check. */ @@ -6840,7 +6774,7 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d metadata.data.padding.unused = 0; /* Padding doesn't have anything meaningful in it, so just skip over it, but make sure the caller is aware of it by firing the callback. */ - if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { + if (!onSeek(pUserData, blockSize, DRFLAC_SEEK_CUR)) { isLastBlock = DRFLAC_TRUE; /* An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. */ } else { onMeta(pUserDataMD, &metadata); @@ -6852,7 +6786,7 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d { /* Invalid chunk. Just skip over this one. */ if (onMeta) { - if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { + if (!onSeek(pUserData, blockSize, DRFLAC_SEEK_CUR)) { isLastBlock = DRFLAC_TRUE; /* An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. */ } } @@ -6886,7 +6820,7 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d /* If we're not handling metadata, just skip over the block. If we are, it will have been handled earlier in the switch statement above. */ if (onMeta == NULL && blockSize > 0) { - if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { + if (!onSeek(pUserData, blockSize, DRFLAC_SEEK_CUR)) { isLastBlock = DRFLAC_TRUE; } } @@ -7220,6 +7154,7 @@ typedef struct { drflac_read_proc onRead; /* The original onRead callback from drflac_open() and family. */ drflac_seek_proc onSeek; /* The original onSeek callback from drflac_open() and family. */ + drflac_tell_proc onTell; /* The original onTell callback from drflac_open() and family. */ void* pUserData; /* The user data passed on onRead and onSeek. This is the user data that was passed on drflac_open() and family. */ drflac_uint64 currentBytePos; /* The position of the byte we are sitting on in the physical byte stream. Used for efficient seeking. */ drflac_uint64 firstBytePos; /* The position of the first byte in the physical bitstream. Points to the start of the "OggS" identifier of the FLAC bos page. */ @@ -7241,32 +7176,32 @@ static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin) { - if (origin == drflac_seek_origin_start) { + if (origin == DRFLAC_SEEK_SET) { if (offset <= 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) { + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, DRFLAC_SEEK_SET)) { return DRFLAC_FALSE; } oggbs->currentBytePos = offset; return DRFLAC_TRUE; } else { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, DRFLAC_SEEK_SET)) { return DRFLAC_FALSE; } oggbs->currentBytePos = offset; - return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current); + return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, DRFLAC_SEEK_CUR); } } else { while (offset > 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, DRFLAC_SEEK_CUR)) { return DRFLAC_FALSE; } oggbs->currentBytePos += 0x7FFFFFFF; offset -= 0x7FFFFFFF; } - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) { /* <-- Safe cast thanks to the loop above. */ + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, DRFLAC_SEEK_CUR)) { /* <-- Safe cast thanks to the loop above. */ return DRFLAC_FALSE; } oggbs->currentBytePos += offset; @@ -7298,7 +7233,7 @@ static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_og if (header.serialNumber != oggbs->serialNumber) { /* It's not a FLAC page. Skip it. */ - if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) { + if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, DRFLAC_SEEK_CUR)) { return DRFLAC_FALSE; } continue; @@ -7384,7 +7319,7 @@ static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) At this point we will have found either the packet or the end of the page. If were at the end of the page we'll want to load the next page and keep searching for the end of the packet. */ - drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current); + drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, DRFLAC_SEEK_CUR); oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; if (atEndOfPage) { @@ -7462,8 +7397,8 @@ static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_see DRFLAC_ASSERT(offset >= 0); /* <-- Never seek backwards. */ /* Seeking is always forward which makes things a lot simpler. */ - if (origin == drflac_seek_origin_start) { - if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) { + if (origin == DRFLAC_SEEK_SET) { + if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, DRFLAC_SEEK_SET)) { return DRFLAC_FALSE; } @@ -7471,38 +7406,50 @@ static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_see return DRFLAC_FALSE; } - return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current); - } + return drflac__on_seek_ogg(pUserData, offset, DRFLAC_SEEK_CUR); + } else if (origin == DRFLAC_SEEK_CUR) { + while (bytesSeeked < offset) { + int bytesRemainingToSeek = offset - bytesSeeked; + DRFLAC_ASSERT(bytesRemainingToSeek >= 0); - DRFLAC_ASSERT(origin == drflac_seek_origin_current); + if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { + bytesSeeked += bytesRemainingToSeek; + (void)bytesSeeked; /* <-- Silence a dead store warning emitted by Clang Static Analyzer. */ + oggbs->bytesRemainingInPage -= bytesRemainingToSeek; + break; + } - while (bytesSeeked < offset) { - int bytesRemainingToSeek = offset - bytesSeeked; - DRFLAC_ASSERT(bytesRemainingToSeek >= 0); + /* If we get here it means some of the requested data is contained in the next pages. */ + if (oggbs->bytesRemainingInPage > 0) { + bytesSeeked += (int)oggbs->bytesRemainingInPage; + oggbs->bytesRemainingInPage = 0; + } - if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { - bytesSeeked += bytesRemainingToSeek; - (void)bytesSeeked; /* <-- Silence a dead store warning emitted by Clang Static Analyzer. */ - oggbs->bytesRemainingInPage -= bytesRemainingToSeek; - break; - } - - /* If we get here it means some of the requested data is contained in the next pages. */ - if (oggbs->bytesRemainingInPage > 0) { - bytesSeeked += (int)oggbs->bytesRemainingInPage; - oggbs->bytesRemainingInPage = 0; - } - - DRFLAC_ASSERT(bytesRemainingToSeek > 0); - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { - /* Failed to go to the next page. We either hit the end of the stream or had a CRC mismatch. */ - return DRFLAC_FALSE; + DRFLAC_ASSERT(bytesRemainingToSeek > 0); + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { + /* Failed to go to the next page. We either hit the end of the stream or had a CRC mismatch. */ + return DRFLAC_FALSE; + } } + } else if (origin == DRFLAC_SEEK_END) { + /* Seeking to the end is not supported. */ + return DRFLAC_FALSE; } return DRFLAC_TRUE; } +static drflac_bool32 drflac__on_tell_ogg(void* pUserData, drflac_int64* pCursor) +{ + /* + Not implemented for Ogg containers because we don't currently track the byte position of the logical bitstream. To support this, we'll need + to track the position in drflac__on_read_ogg and drflac__on_seek_ogg. + */ + (void)pUserData; + (void)pCursor; + return DRFLAC_FALSE; +} + static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) { @@ -7525,7 +7472,7 @@ static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 runningGranulePosition = 0; for (;;) { if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { - drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); + drflac_oggbs__seek_physical(oggbs, originalBytePos, DRFLAC_SEEK_SET); return DRFLAC_FALSE; /* Never did find that sample... */ } @@ -7559,7 +7506,7 @@ static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 a new frame. This property means that after we've seeked to the page we can immediately start looping over frames until we find the one containing the target sample. */ - if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) { + if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, DRFLAC_SEEK_SET)) { return DRFLAC_FALSE; } if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { @@ -7726,7 +7673,7 @@ static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_r The next 2 bytes are the non-audio packets, not including this one. We don't care about this because we're going to be handling it in a generic way based on the serial number and packet types. */ - if (!onSeek(pUserData, 2, drflac_seek_origin_current)) { + if (!onSeek(pUserData, 2, DRFLAC_SEEK_CUR)) { return DRFLAC_FALSE; } @@ -7783,18 +7730,18 @@ static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_r } } else { /* Not a FLAC header. Skip it. */ - if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { + if (!onSeek(pUserData, bytesRemainingInPage, DRFLAC_SEEK_CUR)) { return DRFLAC_FALSE; } } } else { /* Not a FLAC header. Seek past the entire page and move on to the next. */ - if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { + if (!onSeek(pUserData, bytesRemainingInPage, DRFLAC_SEEK_CUR)) { return DRFLAC_FALSE; } } } else { - if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) { + if (!onSeek(pUserData, pageBodySize, DRFLAC_SEEK_CUR)) { return DRFLAC_FALSE; } } @@ -7819,18 +7766,19 @@ static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_r } #endif -static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) +static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) { drflac_bool32 relaxed; drflac_uint8 id[4]; - if (pInit == NULL || onRead == NULL || onSeek == NULL) { + if (pInit == NULL || onRead == NULL || onSeek == NULL) { /* <-- onTell is optional. */ return DRFLAC_FALSE; } DRFLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); pInit->onRead = onRead; pInit->onSeek = onSeek; + pInit->onTell = onTell; pInit->onMeta = onMeta; pInit->container = container; pInit->pUserData = pUserData; @@ -7838,6 +7786,7 @@ static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_p pInit->bs.onRead = onRead; pInit->bs.onSeek = onSeek; + pInit->bs.onTell = onTell; pInit->bs.pUserData = pUserData; drflac__reset_cache(&pInit->bs); @@ -7870,7 +7819,7 @@ static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_p headerSize += 10; } - if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) { + if (!onSeek(pUserData, headerSize, DRFLAC_SEEK_CUR)) { return DRFLAC_FALSE; /* Failed to seek past the tag. */ } pInit->runningFilePos += headerSize; @@ -7922,7 +7871,7 @@ static void drflac__init_from_info(drflac* pFlac, const drflac_init_info* pInit) } -static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD, const drflac_allocation_callbacks* pAllocationCallbacks) +static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac_init_info init; drflac_uint32 allocationSize; @@ -7940,7 +7889,7 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac /* CPU support first. */ drflac__init_cpu_caps(); - if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { + if (!drflac__init_private(&init, onRead, onSeek, onTell, onMeta, container, pUserData, pUserDataMD)) { return NULL; } @@ -7996,6 +7945,7 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac DRFLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); pOggbs->onRead = onRead; pOggbs->onSeek = onSeek; + pOggbs->onTell = onTell; pOggbs->pUserData = pUserData; pOggbs->currentBytePos = init.oggFirstBytePos; pOggbs->firstBytePos = init.oggFirstBytePos; @@ -8016,17 +7966,19 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac if (init.hasMetadataBlocks) { drflac_read_proc onReadOverride = onRead; drflac_seek_proc onSeekOverride = onSeek; + drflac_tell_proc onTellOverride = onTell; void* pUserDataOverride = pUserData; #ifndef DR_FLAC_NO_OGG if (init.container == drflac_container_ogg) { onReadOverride = drflac__on_read_ogg; onSeekOverride = drflac__on_seek_ogg; + onTellOverride = drflac__on_tell_ogg; pUserDataOverride = (void*)pOggbs; } #endif - if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { + if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onTellOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { #ifndef DR_FLAC_NO_OGG drflac__free_from_callbacks(pOggbs, &allocationCallbacks); #endif @@ -8061,6 +8013,7 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac /* The Ogg bistream needs to be layered on top of the original bitstream. */ pFlac->bs.onRead = drflac__on_read_ogg; pFlac->bs.onSeek = drflac__on_seek_ogg; + pFlac->bs.onTell = drflac__on_tell_ogg; pFlac->bs.pUserData = (void*)pInternalOggbs; pFlac->_oggbs = (void*)pInternalOggbs; } @@ -8087,7 +8040,7 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac DRFLAC_ASSERT(pFlac->bs.onRead != NULL); /* Seek to the seektable, then just read directly into our seektable buffer. */ - if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) { + if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, DRFLAC_SEEK_SET)) { drflac_uint32 iSeekpoint; for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { @@ -8105,7 +8058,7 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac } /* We need to seek back to where we were. If this fails it's a critical error. */ - if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, drflac_seek_origin_start)) { + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, DRFLAC_SEEK_SET)) { drflac__free_from_callbacks(pFlac, &allocationCallbacks); return NULL; } @@ -8276,7 +8229,7 @@ static drflac_result drflac_result_from_errno(int e) #ifdef ENOSYS case ENOSYS: return DRFLAC_NOT_IMPLEMENTED; #endif - #ifdef ENOTEMPTY + #if defined(ENOTEMPTY) && ENOTEMPTY != EEXIST /* In AIX, ENOTEMPTY and EEXIST use the same value. */ case ENOTEMPTY: return DRFLAC_DIRECTORY_NOT_EMPTY; #endif #ifdef ELOOP @@ -8727,11 +8680,41 @@ static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t byt static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) { - DRFLAC_ASSERT(offset >= 0); /* <-- Never seek backwards. */ + int whence = SEEK_SET; + if (origin == DRFLAC_SEEK_CUR) { + whence = SEEK_CUR; + } else if (origin == DRFLAC_SEEK_END) { + whence = SEEK_END; + } - return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; + return fseek((FILE*)pUserData, offset, whence) == 0; } +static drflac_bool32 drflac__on_tell_stdio(void* pUserData, drflac_int64* pCursor) +{ + FILE* pFileStdio = (FILE*)pUserData; + drflac_int64 result; + + /* These were all validated at a higher level. */ + DRFLAC_ASSERT(pFileStdio != NULL); + DRFLAC_ASSERT(pCursor != NULL); + +#if defined(_WIN32) + #if defined(_MSC_VER) && _MSC_VER > 1200 + result = _ftelli64(pFileStdio); + #else + result = ftell(pFileStdio); + #endif +#else + result = ftell(pFileStdio); +#endif + + *pCursor = result; + + return DRFLAC_TRUE; +} + + DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) { @@ -8742,7 +8725,7 @@ DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocati return NULL; } - pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, drflac__on_tell_stdio, (void*)pFile, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return NULL; @@ -8761,7 +8744,7 @@ DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_all return NULL; } - pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, drflac__on_tell_stdio, (void*)pFile, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return NULL; @@ -8780,7 +8763,7 @@ DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_ return NULL; } - pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); + pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, drflac__on_tell_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return pFlac; @@ -8799,7 +8782,7 @@ DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, dr return NULL; } - pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); + pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, drflac__on_tell_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return pFlac; @@ -8834,28 +8817,45 @@ static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t by static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin) { drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; + drflac_int64 newCursor; DRFLAC_ASSERT(memoryStream != NULL); - DRFLAC_ASSERT(offset >= 0); /* <-- Never seek backwards. */ - if (offset > (drflac_int64)memoryStream->dataSize) { + newCursor = memoryStream->currentReadPos; + + if (origin == DRFLAC_SEEK_SET) { + newCursor = 0; + } else if (origin == DRFLAC_SEEK_CUR) { + newCursor = (drflac_int64)memoryStream->currentReadPos; + } else if (origin == DRFLAC_SEEK_END) { + newCursor = (drflac_int64)memoryStream->dataSize; + } else { + DRFLAC_ASSERT(!"Invalid seek origin"); return DRFLAC_FALSE; } - if (origin == drflac_seek_origin_current) { - if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { - memoryStream->currentReadPos += offset; - } else { - return DRFLAC_FALSE; /* Trying to seek too far forward. */ - } - } else { - if ((drflac_uint32)offset <= memoryStream->dataSize) { - memoryStream->currentReadPos = offset; - } else { - return DRFLAC_FALSE; /* Trying to seek too far forward. */ - } + newCursor += offset; + + if (newCursor < 0) { + return DRFLAC_FALSE; /* Trying to seek prior to the start of the buffer. */ + } + if ((size_t)newCursor > memoryStream->dataSize) { + return DRFLAC_FALSE; /* Trying to seek beyond the end of the buffer. */ } + memoryStream->currentReadPos = (size_t)newCursor; + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__on_tell_memory(void* pUserData, drflac_int64* pCursor) +{ + drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; + + DRFLAC_ASSERT(memoryStream != NULL); + DRFLAC_ASSERT(pCursor != NULL); + + *pCursor = (drflac_int64)memoryStream->currentReadPos; return DRFLAC_TRUE; } @@ -8867,7 +8867,7 @@ DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const memoryStream.data = (const drflac_uint8*)pData; memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; - pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream, pAllocationCallbacks); + pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, drflac__on_tell_memory, &memoryStream, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } @@ -8898,7 +8898,7 @@ DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t da memoryStream.data = (const drflac_uint8*)pData; memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; - pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); + pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, drflac__on_tell_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } @@ -8923,22 +8923,22 @@ DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t da -DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { - return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); + return drflac_open_with_metadata_private(onRead, onSeek, onTell, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); } -DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { - return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); + return drflac_open_with_metadata_private(onRead, onSeek, onTell, NULL, container, pUserData, pUserData, pAllocationCallbacks); } -DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { - return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); + return drflac_open_with_metadata_private(onRead, onSeek, onTell, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); } -DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { - return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); + return drflac_open_with_metadata_private(onRead, onSeek, onTell, onMeta, container, pUserData, pUserData, pAllocationCallbacks); } DRFLAC_API void drflac_close(drflac* pFlac) @@ -11770,7 +11770,7 @@ DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s32, drflac_int32) DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s16, drflac_int16) DRFLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) -DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; @@ -11784,7 +11784,7 @@ DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc on *totalPCMFrameCountOut = 0; } - pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + pFlac = drflac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } @@ -11792,7 +11792,7 @@ DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc on return drflac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } -DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; @@ -11806,7 +11806,7 @@ DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc on *totalPCMFrameCountOut = 0; } - pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + pFlac = drflac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } @@ -11814,7 +11814,7 @@ DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc on return drflac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } -DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; @@ -11828,7 +11828,7 @@ DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, d *totalPCMFrameCountOut = 0; } - pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + pFlac = drflac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } @@ -12077,6 +12077,26 @@ DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterat /* REVISION HISTORY ================ +v0.13.0 - TBD + - API CHANGE: Seek origin enums have been renamed to match the naming convention used by other dr_libs libraries: + - drflac_seek_origin_start -> DRFLAC_SEEK_SET + - drflac_seek_origin_current -> DRFLAC_SEEK_CUR + - DRFLAC_SEEK_END (new) + - API CHANGE: A new seek origin has been added to allow seeking from the end of the file. If you implement your own `onSeek` callback, you should now detect and handle `DRFLAC_SEEK_END`. If seeking to the end is not supported, return `DRFLAC_FALSE`. If you only use `*_open_file()` or `*_open_memory()`, you need not change anything. + - API CHANGE: An `onTell` callback has been added to the following functions: + - drflac_open() + - drflac_open_relaxed() + - drflac_open_with_metadata() + - drflac_open_with_metadata_relaxed() + - drflac_open_and_read_pcm_frames_s32() + - drflac_open_and_read_pcm_frames_s16() + - drflac_open_and_read_pcm_frames_f32() + - Fix compilation for AIX OS. + +v0.12.43 - 2024-12-17 + - Fix a possible buffer overflow during decoding. + - Improve detection of ARM64EC + v0.12.42 - 2023-11-02 - Fix build for ARMv6-M. - Fix a compilation warning with GCC. diff --git a/src/external/dr_mp3.h b/src/external/dr_mp3.h index e1a66d99c..cc033ce87 100644 --- a/src/external/dr_mp3.h +++ b/src/external/dr_mp3.h @@ -1,6 +1,6 @@ /* MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. -dr_mp3 - v0.6.39 - 2024-02-27 +dr_mp3 - v0.7.0 - TBD David Reid - mackron@gmail.com @@ -10,30 +10,7 @@ Based on minimp3 (https://github.com/lieff/minimp3) which is where the real work */ /* -RELEASE NOTES - VERSION 0.6 -=========================== -Version 0.6 includes breaking changes with the configuration of decoders. The ability to customize the number of output channels and the sample rate has been -removed. You must now use the channel count and sample rate reported by the MP3 stream itself, and all channel and sample rate conversion must be done -yourself. - - -Changes to Initialization -------------------------- -Previously, `drmp3_init()`, etc. took a pointer to a `drmp3_config` object that allowed you to customize the output channels and sample rate. This has been -removed. If you need the old behaviour you will need to convert the data yourself or just not upgrade. The following APIs have changed. - - `drmp3_init()` - `drmp3_init_memory()` - `drmp3_init_file()` - - -Miscellaneous Changes ---------------------- -Support for loading a file from a `wchar_t` string has been added via the `drmp3_init_file_w()` API. -*/ - -/* -Introducation +Introduction ============= dr_mp3 is a single file library. To use it, do something like the following in one .c file. @@ -94,8 +71,8 @@ extern "C" { #define DRMP3_XSTRINGIFY(x) DRMP3_STRINGIFY(x) #define DRMP3_VERSION_MAJOR 0 -#define DRMP3_VERSION_MINOR 6 -#define DRMP3_VERSION_REVISION 39 +#define DRMP3_VERSION_MINOR 7 +#define DRMP3_VERSION_REVISION 0 #define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) #include /* For size_t. */ @@ -124,7 +101,7 @@ typedef unsigned int drmp3_uint32; #pragma GCC diagnostic pop #endif #endif -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) || defined(__powerpc64__) typedef drmp3_uint64 drmp3_uintptr; #else typedef drmp3_uint32 drmp3_uintptr; @@ -133,6 +110,9 @@ typedef drmp3_uint8 drmp3_bool8; typedef drmp3_uint32 drmp3_bool32; #define DRMP3_TRUE 1 #define DRMP3_FALSE 0 + +/* Weird shifting syntax is for VC6 compatibility. */ +#define DRMP3_UINT64_MAX (((drmp3_uint64)0xFFFFFFFF << 32) | (drmp3_uint64)0xFFFFFFFF) /* End Sized Types */ /* Decorations */ @@ -154,7 +134,7 @@ typedef drmp3_uint32 drmp3_bool32; #endif #endif - #if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION) + #if defined(DR_MP3_IMPLEMENTATION) #define DRMP3_API DRMP3_DLL_EXPORT #else #define DRMP3_API DRMP3_DLL_IMPORT @@ -279,7 +259,7 @@ Low Level Push API */ typedef struct { - int frame_bytes, channels, hz, layer, bitrate_kbps; + int frame_bytes, channels, sample_rate, layer, bitrate_kbps; } drmp3dec_frame_info; typedef struct @@ -306,8 +286,9 @@ Main API (Pull API) */ typedef enum { - drmp3_seek_origin_start, - drmp3_seek_origin_current + DRMP3_SEEK_SET, + DRMP3_SEEK_CUR, + DRMP3_SEEK_END } drmp3_seek_origin; typedef struct @@ -318,10 +299,27 @@ typedef struct drmp3_uint16 pcmFramesToDiscard; /* The number of leading samples to read and discard. These are discarded after mp3FramesToDiscard. */ } drmp3_seek_point; +typedef enum +{ + DRMP3_METADATA_TYPE_ID3V1, + DRMP3_METADATA_TYPE_ID3V2, + DRMP3_METADATA_TYPE_APE, + DRMP3_METADATA_TYPE_XING, + DRMP3_METADATA_TYPE_VBRI +} drmp3_metadata_type; + +typedef struct +{ + drmp3_metadata_type type; + const void* pRawData; /* A pointer to the raw data. */ + size_t rawDataSize; +} drmp3_metadata; + + /* Callback for when data is read. Return value is the number of bytes actually read. -pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family. +pUserData [in] The user data that was passed to drmp3_init(), and family. pBufferOut [out] The output buffer. bytesToRead [in] The number of bytes to read. @@ -335,17 +333,33 @@ typedef size_t (* drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t byt /* Callback for when data needs to be seeked. -pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family. -offset [in] The number of bytes to move, relative to the origin. Will never be negative. -origin [in] The origin of the seek - the current position or the start of the stream. +pUserData [in] The user data that was passed to drmp3_init(), and family. +offset [in] The number of bytes to move, relative to the origin. Can be negative. +origin [in] The origin of the seek. Returns whether or not the seek was successful. - -Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which -will be either drmp3_seek_origin_start or drmp3_seek_origin_current. */ typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin); +/* +Callback for retrieving the current cursor position. + +pUserData [in] The user data that was passed to drmp3_init(), and family. +pCursor [out] The cursor position in bytes from the start of the stream. + +Returns whether or not the cursor position was successfully retrieved. +*/ +typedef drmp3_bool32 (* drmp3_tell_proc)(void* pUserData, drmp3_int64* pCursor); + + +/* +Callback for when metadata is read. + +Only the raw data is provided. The client is responsible for parsing the contents of the data themsevles. +*/ +typedef void (* drmp3_meta_proc)(void* pUserData, const drmp3_metadata* pMetadata); + + typedef struct { drmp3_uint32 channels; @@ -359,22 +373,31 @@ typedef struct drmp3_uint32 sampleRate; drmp3_read_proc onRead; drmp3_seek_proc onSeek; + drmp3_meta_proc onMeta; void* pUserData; + void* pUserDataMeta; drmp3_allocation_callbacks allocationCallbacks; drmp3_uint32 mp3FrameChannels; /* The number of channels in the currently loaded MP3 frame. Internal use only. */ drmp3_uint32 mp3FrameSampleRate; /* The sample rate of the currently loaded MP3 frame. Internal use only. */ drmp3_uint32 pcmFramesConsumedInMP3Frame; drmp3_uint32 pcmFramesRemainingInMP3Frame; drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME]; /* <-- Multipled by sizeof(float) to ensure there's enough room for DR_MP3_FLOAT_OUTPUT. */ - drmp3_uint64 currentPCMFrame; /* The current PCM frame, globally, based on the output sample rate. Mainly used for seeking. */ + drmp3_uint64 currentPCMFrame; /* The current PCM frame, globally. */ drmp3_uint64 streamCursor; /* The current byte the decoder is sitting on in the raw stream. */ + drmp3_uint64 streamLength; /* The length of the stream in bytes. dr_mp3 will not read beyond this. If a ID3v1 or APE tag is present, this will be set to the first byte of the tag. */ + drmp3_uint64 streamStartOffset; /* The offset of the start of the MP3 data. This is used for skipping ID3v2 and VBR tags. */ drmp3_seek_point* pSeekPoints; /* NULL by default. Set with drmp3_bind_seek_table(). Memory is owned by the client. dr_mp3 will never attempt to free this pointer. */ drmp3_uint32 seekPointCount; /* The number of items in pSeekPoints. When set to 0 assumes to no seek table. Defaults to zero. */ + drmp3_uint32 delayInPCMFrames; + drmp3_uint32 paddingInPCMFrames; + drmp3_uint64 totalPCMFrameCount; /* Set to DRMP3_UINT64_MAX if the length is unknown. Includes delay and padding. */ + drmp3_bool32 isVBR; + drmp3_bool32 isCBR; size_t dataSize; size_t dataCapacity; size_t dataConsumed; drmp3_uint8* pData; - drmp3_bool32 atEnd : 1; + drmp3_bool32 atEnd; struct { const drmp3_uint8* pData; @@ -388,6 +411,7 @@ Initializes an MP3 decoder. onRead [in] The function to call when data needs to be read from the client. onSeek [in] The function to call when the read position of the client data needs to move. +onTell [in] The function to call when the read position of the client data needs to be retrieved. pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek. Returns true if successful; false otherwise. @@ -396,7 +420,7 @@ Close the loader with drmp3_uninit(). See also: drmp3_init_file(), drmp3_init_memory(), drmp3_uninit() */ -DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks); +DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, drmp3_meta_proc onMeta, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks); /* Initializes an MP3 decoder from a block of memory. @@ -406,6 +430,7 @@ the lifetime of the drmp3 object. The buffer should contain the contents of the entire MP3 file. */ +DRMP3_API drmp3_bool32 drmp3_init_memory_with_metadata(drmp3* pMP3, const void* pData, size_t dataSize, drmp3_meta_proc onMeta, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks); DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks); #ifndef DR_MP3_NO_STDIO @@ -416,6 +441,9 @@ This holds the internal FILE object until drmp3_uninit() is called. Keep this in objects because the operating system may restrict the number of file handles an application can have open at any given time. */ +DRMP3_API drmp3_bool32 drmp3_init_file_with_metadata(drmp3* pMP3, const char* pFilePath, drmp3_meta_proc onMeta, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks); +DRMP3_API drmp3_bool32 drmp3_init_file_with_metadata_w(drmp3* pMP3, const wchar_t* pFilePath, drmp3_meta_proc onMeta, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks); + DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks); DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks); #endif @@ -495,8 +523,8 @@ On output pConfig will receive the channel count and sample rate of the stream. Free the returned pointer with drmp3_free(). */ -DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); -DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); +DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); +DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); @@ -529,7 +557,7 @@ DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocation ************************************************************************************************************************************************************ ************************************************************************************************************************************************************/ -#if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION) +#if defined(DR_MP3_IMPLEMENTATION) #ifndef dr_mp3_c #define dr_mp3_c @@ -604,7 +632,7 @@ DRMP3_API const char* drmp3_version_string(void) #if !defined(DR_MP3_NO_SIMD) -#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64)) +#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) /* x64 always have SSE2, arm64 always have neon, no need for generic code */ #define DR_MP3_ONLY_SIMD #endif @@ -680,7 +708,7 @@ end: return g_have_simd - 1; #endif } -#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) +#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) #include #define DRMP3_HAVE_SSE 0 #define DRMP3_HAVE_SIMD 1 @@ -713,7 +741,7 @@ static int drmp3_have_simd(void) #endif -#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(__ARM_ARCH_6M__) +#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(_M_ARM64EC) && !defined(__ARM_ARCH_6M__) #define DRMP3_HAVE_ARMV6 1 static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_arm(drmp3_int32 a) { @@ -2296,7 +2324,7 @@ DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int m DRMP3_COPY_MEMORY(dec->header, hdr, DRMP3_HDR_SIZE); info->frame_bytes = i + frame_size; info->channels = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; - info->hz = drmp3_hdr_sample_rate_hz(hdr); + info->sample_rate = drmp3_hdr_sample_rate_hz(hdr); info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr); info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr); @@ -2589,22 +2617,56 @@ static drmp3_allocation_callbacks drmp3_copy_allocation_callbacks_or_defaults(co static size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead) { - size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); + size_t bytesRead; + + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->onRead != NULL); + + /* + Don't try reading 0 bytes from the callback. This can happen when the stream is clamped against + ID3v1 or APE tags at the end of the stream. + */ + if (bytesToRead == 0) { + return 0; + } + + bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); pMP3->streamCursor += bytesRead; + return bytesRead; } +static size_t drmp3__on_read_clamped(drmp3* pMP3, void* pBufferOut, size_t bytesToRead) +{ + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->onRead != NULL); + + if (pMP3->streamLength == DRMP3_UINT64_MAX) { + return drmp3__on_read(pMP3, pBufferOut, bytesToRead); + } else { + drmp3_uint64 bytesRemaining; + + bytesRemaining = (pMP3->streamLength - pMP3->streamCursor); + if (bytesToRead > bytesRemaining) { + bytesToRead = (size_t)bytesRemaining; + } + + return drmp3__on_read(pMP3, pBufferOut, bytesToRead); + } +} + static drmp3_bool32 drmp3__on_seek(drmp3* pMP3, int offset, drmp3_seek_origin origin) { DRMP3_ASSERT(offset >= 0); + DRMP3_ASSERT(origin == DRMP3_SEEK_SET || origin == DRMP3_SEEK_CUR); if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) { return DRMP3_FALSE; } - if (origin == drmp3_seek_origin_start) { + if (origin == DRMP3_SEEK_SET) { pMP3->streamCursor = (drmp3_uint64)offset; - } else { + } else{ pMP3->streamCursor += offset; } @@ -2617,21 +2679,20 @@ static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_se return drmp3__on_seek(pMP3, (int)offset, origin); } - /* Getting here "offset" is too large for a 32-bit integer. We just keep seeking forward until we hit the offset. */ - if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_start)) { + if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, DRMP3_SEEK_SET)) { return DRMP3_FALSE; } offset -= 0x7FFFFFFF; while (offset > 0) { if (offset <= 0x7FFFFFFF) { - if (!drmp3__on_seek(pMP3, (int)offset, drmp3_seek_origin_current)) { + if (!drmp3__on_seek(pMP3, (int)offset, DRMP3_SEEK_CUR)) { return DRMP3_FALSE; } offset = 0; } else { - if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_current)) { + if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, DRMP3_SEEK_CUR)) { return DRMP3_FALSE; } offset -= 0x7FFFFFFF; @@ -2641,8 +2702,22 @@ static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_se return DRMP3_TRUE; } +static void drmp3__on_meta(drmp3* pMP3, drmp3_metadata_type type, const void* pRawData, size_t rawDataSize) +{ + if (pMP3->onMeta) { + drmp3_metadata metadata; -static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) + DRMP3_ZERO_OBJECT(&metadata); + metadata.type = type; + metadata.pRawData = pRawData; + metadata.rawDataSize = rawDataSize; + + pMP3->onMeta(pMP3->pUserDataMeta, &metadata); + } +} + + +static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sample_t* pPCMFrames, drmp3dec_frame_info* pMP3FrameInfo, const drmp3_uint8** ppMP3FrameData) { drmp3_uint32 pcmFramesRead = 0; @@ -2682,7 +2757,7 @@ static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sa pMP3->dataCapacity = newDataCap; } - bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + bytesRead = drmp3__on_read_clamped(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); if (bytesRead == 0) { if (pMP3->dataSize == 0) { pMP3->atEnd = DRMP3_TRUE; @@ -2709,10 +2784,8 @@ static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sa pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); /* <-- Safe size_t -> int conversion thanks to the check above. */ /* Consume the data. */ - if (info.frame_bytes > 0) { - pMP3->dataConsumed += (size_t)info.frame_bytes; - pMP3->dataSize -= (size_t)info.frame_bytes; - } + pMP3->dataConsumed += (size_t)info.frame_bytes; + pMP3->dataSize -= (size_t)info.frame_bytes; /* pcmFramesRead will be equal to 0 if decoding failed. If it is zero and info.frame_bytes > 0 then we have successfully decoded the frame. */ if (pcmFramesRead > 0) { @@ -2720,7 +2793,16 @@ static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sa pMP3->pcmFramesConsumedInMP3Frame = 0; pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; pMP3->mp3FrameChannels = info.channels; - pMP3->mp3FrameSampleRate = info.hz; + pMP3->mp3FrameSampleRate = info.sample_rate; + + if (pMP3FrameInfo != NULL) { + *pMP3FrameInfo = info; + } + + if (ppMP3FrameData != NULL) { + *ppMP3FrameData = pMP3->pData + pMP3->dataConsumed - (size_t)info.frame_bytes; + } + break; } else if (info.frame_bytes == 0) { /* Need more data. minimp3 recommends doing data submission in 16K chunks. */ @@ -2747,7 +2829,7 @@ static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sa } /* Fill in a chunk. */ - bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + bytesRead = drmp3__on_read_clamped(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); if (bytesRead == 0) { pMP3->atEnd = DRMP3_TRUE; return 0; /* Error reading more data. */ @@ -2760,7 +2842,7 @@ static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sa return pcmFramesRead; } -static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) +static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sample_t* pPCMFrames, drmp3dec_frame_info* pMP3FrameInfo, const drmp3_uint8** ppMP3FrameData) { drmp3_uint32 pcmFramesRead = 0; drmp3dec_frame_info info; @@ -2779,11 +2861,21 @@ static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sampl pMP3->pcmFramesConsumedInMP3Frame = 0; pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; pMP3->mp3FrameChannels = info.channels; - pMP3->mp3FrameSampleRate = info.hz; + pMP3->mp3FrameSampleRate = info.sample_rate; + + if (pMP3FrameInfo != NULL) { + *pMP3FrameInfo = info; + } + + if (ppMP3FrameData != NULL) { + *ppMP3FrameData = pMP3->memory.pData + pMP3->memory.currentReadPos; + } + break; } else if (info.frame_bytes > 0) { /* No frames were read, but it looks like we skipped past one. Read the next MP3 frame. */ pMP3->memory.currentReadPos += (size_t)info.frame_bytes; + pMP3->streamCursor += (size_t)info.frame_bytes; } else { /* Nothing at all was read. Abort. */ break; @@ -2792,23 +2884,24 @@ static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sampl /* Consume the data. */ pMP3->memory.currentReadPos += (size_t)info.frame_bytes; + pMP3->streamCursor += (size_t)info.frame_bytes; return pcmFramesRead; } -static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) +static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames, drmp3dec_frame_info* pMP3FrameInfo, const drmp3_uint8** ppMP3FrameData) { if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) { - return drmp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); + return drmp3_decode_next_frame_ex__memory(pMP3, pPCMFrames, pMP3FrameInfo, ppMP3FrameData); } else { - return drmp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); + return drmp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames, pMP3FrameInfo, ppMP3FrameData); } } static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3) { DRMP3_ASSERT(pMP3 != NULL); - return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames); + return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames, NULL, NULL); } #if 0 @@ -2818,7 +2911,7 @@ static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3) DRMP3_ASSERT(pMP3 != NULL); - pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL); + pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL); if (pcmFrameCount == 0) { return 0; } @@ -2832,8 +2925,13 @@ static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3) } #endif -static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks) +static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, drmp3_meta_proc onMeta, void* pUserData, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks) { + drmp3dec_frame_info firstFrameInfo; + const drmp3_uint8* pFirstFrameData; + drmp3_uint32 firstFramePCMFrameCount; + drmp3_uint32 detectedMP3FrameCount = 0xFFFFFFFF; + DRMP3_ASSERT(pMP3 != NULL); DRMP3_ASSERT(onRead != NULL); @@ -2842,17 +2940,326 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm pMP3->onRead = onRead; pMP3->onSeek = onSeek; + pMP3->onMeta = onMeta; pMP3->pUserData = pUserData; + pMP3->pUserDataMeta = pUserDataMeta; pMP3->allocationCallbacks = drmp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) { return DRMP3_FALSE; /* Invalid allocation callbacks. */ } - /* Decode the first frame to confirm that it is indeed a valid MP3 stream. */ - if (drmp3_decode_next_frame(pMP3) == 0) { + pMP3->streamCursor = 0; + pMP3->streamLength = DRMP3_UINT64_MAX; + pMP3->streamStartOffset = 0; + pMP3->delayInPCMFrames = 0; + pMP3->paddingInPCMFrames = 0; + pMP3->totalPCMFrameCount = DRMP3_UINT64_MAX; + + /* We'll first check for any ID3v1 or APE tags. */ + #if 1 + if (onSeek != NULL && onTell != NULL) { + if (onSeek(pUserData, 0, DRMP3_SEEK_END)) { + drmp3_int64 streamLen; + int streamEndOffset = 0; + + /* First get the length of the stream. We need this so we can ensure the stream is big enough to store the tags. */ + if (onTell(pUserData, &streamLen)) { + /* ID3v1 */ + if (streamLen > 128) { + char id3[3]; + if (onSeek(pUserData, streamEndOffset - 128, DRMP3_SEEK_END)) { + if (onRead(pUserData, id3, 3) == 3 && id3[0] == 'T' && id3[1] == 'A' && id3[2] == 'G') { + /* We have an ID3v1 tag. */ + streamEndOffset -= 128; + streamLen -= 128; + + /* Fire a metadata callback for the TAG data. */ + if (onMeta != NULL) { + drmp3_uint8 tag[128]; + tag[0] = 'T'; tag[1] = 'A'; tag[2] = 'G'; + + if (onRead(pUserData, tag + 3, 125) == 125) { + drmp3__on_meta(pMP3, DRMP3_METADATA_TYPE_ID3V1, tag, 128); + } + } + } else { + /* No ID3v1 tag. */ + } + } else { + /* Failed to seek to the ID3v1 tag. */ + } + } else { + /* Stream too short. No ID3v1 tag. */ + } + + /* APE */ + if (streamLen > 32) { + char ape[32]; /* The footer. */ + if (onSeek(pUserData, streamEndOffset - 32, DRMP3_SEEK_END)) { + if (onRead(pUserData, ape, 32) == 32 && ape[0] == 'A' && ape[1] == 'P' && ape[2] == 'E' && ape[3] == 'T' && ape[4] == 'A' && ape[5] == 'G' && ape[6] == 'E' && ape[7] == 'X') { + /* We have an APE tag. */ + drmp3_uint32 tagSize = + ((drmp3_uint32)ape[24] << 0) | + ((drmp3_uint32)ape[25] << 8) | + ((drmp3_uint32)ape[26] << 16) | + ((drmp3_uint32)ape[27] << 24); + + streamEndOffset -= 32 + tagSize; + streamLen -= 32 + tagSize; + + /* Fire a metadata callback for the APE data. Must include both the main content and footer. */ + if (onMeta != NULL) { + /* We first need to seek to the start of the APE tag. */ + if (onSeek(pUserData, streamEndOffset, DRMP3_SEEK_END)) { + size_t apeTagSize = (size_t)tagSize + 32; + drmp3_uint8* pTagData = (drmp3_uint8*)drmp3_malloc(apeTagSize, pAllocationCallbacks); + if (pTagData != NULL) { + if (onRead(pUserData, pTagData, apeTagSize) == apeTagSize) { + drmp3__on_meta(pMP3, DRMP3_METADATA_TYPE_APE, pTagData, apeTagSize); + } + + drmp3_free(pTagData, pAllocationCallbacks); + } + } + } + } + } + } else { + /* Stream too short. No APE tag. */ + } + + /* Seek back to the start. */ + if (!onSeek(pUserData, 0, DRMP3_SEEK_SET)) { + return DRMP3_FALSE; /* Failed to seek back to the start. */ + } + + pMP3->streamLength = (drmp3_uint64)streamLen; + + if (pMP3->memory.pData != NULL) { + pMP3->memory.dataSize = (size_t)pMP3->streamLength; + } + } else { + /* Failed to get the length of the stream. ID3v1 and APE tags cannot be skipped. */ + if (!onSeek(pUserData, 0, DRMP3_SEEK_SET)) { + return DRMP3_FALSE; /* Failed to seek back to the start. */ + } + } + } else { + /* Failed to seek to the end. Cannot skip ID3v1 or APE tags. */ + } + } else { + /* No onSeek or onTell callback. Cannot skip ID3v1 or APE tags. */ + } + #endif + + + /* ID3v2 tags */ + #if 1 + { + char header[10]; + if (onRead(pUserData, header, 10) == 10) { + if (header[0] == 'I' && header[1] == 'D' && header[2] == '3') { + drmp3_uint32 tagSize = + (((drmp3_uint32)header[6] & 0x7F) << 21) | + (((drmp3_uint32)header[7] & 0x7F) << 14) | + (((drmp3_uint32)header[8] & 0x7F) << 7) | + (((drmp3_uint32)header[9] & 0x7F) << 0); + + /* Account for the footer. */ + if (header[5] & 0x10) { + tagSize += 10; + } + + /* Read the tag content and fire a metadata callback. */ + if (onMeta != NULL) { + size_t tagSizeWithHeader = 10 + tagSize; + drmp3_uint8* pTagData = (drmp3_uint8*)drmp3_malloc(tagSizeWithHeader, pAllocationCallbacks); + if (pTagData != NULL) { + DRMP3_COPY_MEMORY(pTagData, header, 10); + + if (onRead(pUserData, pTagData + 10, tagSize) == tagSize) { + drmp3__on_meta(pMP3, DRMP3_METADATA_TYPE_ID3V2, pTagData, tagSizeWithHeader); + } + + drmp3_free(pTagData, pAllocationCallbacks); + } + } else { + /* Don't have a metadata callback, so just skip the tag. */ + if (onSeek != NULL) { + if (!onSeek(pUserData, tagSize, DRMP3_SEEK_CUR)) { + return DRMP3_FALSE; /* Failed to seek past the ID3v2 tag. */ + } + } else { + /* Don't have a seek callback. Read and discard. */ + char discard[1024]; + + while (tagSize > 0) { + size_t bytesToRead = tagSize; + if (bytesToRead > sizeof(discard)) { + bytesToRead = sizeof(discard); + } + + if (onRead(pUserData, discard, bytesToRead) != bytesToRead) { + return DRMP3_FALSE; /* Failed to read data. */ + } + + tagSize -= (drmp3_uint32)bytesToRead; + } + } + } + + pMP3->streamStartOffset += 10 + tagSize; /* +10 for the header. */ + pMP3->streamCursor = pMP3->streamStartOffset; + } else { + /* Not an ID3v2 tag. Seek back to the start. */ + if (onSeek != NULL) { + if (!onSeek(pUserData, 0, DRMP3_SEEK_SET)) { + return DRMP3_FALSE; /* Failed to seek back to the start. */ + } + } else { + /* Don't have a seek callback to move backwards. We'll just fall through and let the decoding process re-sync. The ideal solution here would be to read into the cache. */ + + /* + TODO: Copy the header into the cache. Will need to allocate space. See drmp3_decode_next_frame_ex__callbacks. There is not need + to handle the memory case because that will always have a seek implementation and will never hit this code path. + */ + } + } + } else { + /* Failed to read the header. We can return false here. If we couldn't read 10 bytes there's no way we'll have a valid MP3 stream. */ + return DRMP3_FALSE; + } + } + #endif + + /* + Decode the first frame to confirm that it is indeed a valid MP3 stream. Note that it's possible the first frame + is actually a Xing/LAME/VBRI header. If this is the case we need to skip over it. + */ + firstFramePCMFrameCount = drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames, &firstFrameInfo, &pFirstFrameData); + if (firstFramePCMFrameCount > 0) { + DRMP3_ASSERT(pFirstFrameData != NULL); + + /* + It might be a header. If so, we need to clear out the cached PCM frames in order to trigger a reload of fresh + data when decoding starts. We can assume all validation has already been performed to check if this is a valid + MP3 frame and that there is more than 0 bytes making up the frame. + + We're going to be basing this parsing code off the minimp3_ex implementation. + */ + #if 1 + DRMP3_ASSERT(firstFrameInfo.frame_bytes > 0); + { + drmp3_bs bs; + drmp3_L3_gr_info grInfo[4]; + const drmp3_uint8* pTagData = pFirstFrameData; + + drmp3_bs_init(&bs, pFirstFrameData + DRMP3_HDR_SIZE, firstFrameInfo.frame_bytes - DRMP3_HDR_SIZE); + + if (DRMP3_HDR_IS_CRC(pFirstFrameData)) { + drmp3_bs_get_bits(&bs, 16); /* CRC. */ + } + + if (drmp3_L3_read_side_info(&bs, grInfo, pFirstFrameData) >= 0) { + drmp3_bool32 isXing = DRMP3_FALSE; + drmp3_bool32 isInfo = DRMP3_FALSE; + const drmp3_uint8* pTagDataBeg; + + pTagDataBeg = pFirstFrameData + DRMP3_HDR_SIZE + (bs.pos/8); + pTagData = pTagDataBeg; + + /* Check for both "Xing" and "Info" identifiers. */ + isXing = (pTagData[0] == 'X' && pTagData[1] == 'i' && pTagData[2] == 'n' && pTagData[3] == 'g'); + isInfo = (pTagData[0] == 'I' && pTagData[1] == 'n' && pTagData[2] == 'f' && pTagData[3] == 'o'); + + if (isXing || isInfo) { + drmp3_uint32 bytes = 0; + drmp3_uint32 flags = pTagData[7]; + + pTagData += 8; /* Skip past the ID and flags. */ + + if (flags & 0x01) { /* FRAMES flag. */ + detectedMP3FrameCount = (drmp3_uint32)pTagData[0] << 24 | (drmp3_uint32)pTagData[1] << 16 | (drmp3_uint32)pTagData[2] << 8 | (drmp3_uint32)pTagData[3]; + pTagData += 4; + } + + if (flags & 0x02) { /* BYTES flag. */ + bytes = (drmp3_uint32)pTagData[0] << 24 | (drmp3_uint32)pTagData[1] << 16 | (drmp3_uint32)pTagData[2] << 8 | (drmp3_uint32)pTagData[3]; + (void)bytes; /* <-- Just to silence a warning about `bytes` being assigned but unused. Want to leave this here in case I want to make use of it later. */ + pTagData += 4; + } + + if (flags & 0x04) { /* TOC flag. */ + /* TODO: Extract and bind seek points. */ + pTagData += 100; + } + + if (flags & 0x08) { /* SCALE flag. */ + pTagData += 4; + } + + /* At this point we're done with the Xing/Info header. Now we can look at the LAME data. */ + if (pTagData[0]) { + pTagData += 21; + + if (pTagData - pFirstFrameData + 14 < firstFrameInfo.frame_bytes) { + int delayInPCMFrames; + int paddingInPCMFrames; + + delayInPCMFrames = (( (drmp3_uint32)pTagData[0] << 4) | ((drmp3_uint32)pTagData[1] >> 4)) + (528 + 1); + paddingInPCMFrames = ((((drmp3_uint32)pTagData[1] & 0xF) << 8) | ((drmp3_uint32)pTagData[2] )) - (528 + 1); + if (paddingInPCMFrames < 0) { + paddingInPCMFrames = 0; /* Padding cannot be negative. Probably a malformed file. Ignore. */ + } + + pMP3->delayInPCMFrames = (drmp3_uint32)delayInPCMFrames; + pMP3->paddingInPCMFrames = (drmp3_uint32)paddingInPCMFrames; + } + } + + /* + My understanding is that if the "Xing" header is present we can consider this to be a VBR stream and if the "Info" header is + present it's a CBR stream. If this is not the case let me know! I'm just tracking this for the time being in case I want to + look at doing some CBR optimizations later on, such as faster seeking. + */ + if (isXing) { + pMP3->isVBR = DRMP3_TRUE; + } else if (isInfo) { + pMP3->isCBR = DRMP3_TRUE; + } + + /* Post the raw data of the tag to the metadata callback. */ + if (onMeta != NULL) { + drmp3_metadata_type metadataType = isXing ? DRMP3_METADATA_TYPE_XING : DRMP3_METADATA_TYPE_VBRI; + size_t tagDataSize; + + tagDataSize = (size_t)firstFrameInfo.frame_bytes; + tagDataSize -= (size_t)(pTagDataBeg - pFirstFrameData); + + drmp3__on_meta(pMP3, metadataType, pTagDataBeg, tagDataSize); + } + + /* Since this was identified as a tag, we don't want to treat it as audio. We need to clear out the PCM cache. */ + pMP3->pcmFramesRemainingInMP3Frame = 0; + + /* The start offset needs to be moved to the end of this frame so it's not included in any audio processing after seeking. */ + pMP3->streamStartOffset += (drmp3_uint32)(firstFrameInfo.frame_bytes); + pMP3->streamCursor = pMP3->streamStartOffset; + } + } else { + /* Failed to read the side info. */ + } + } + #endif + } else { + /* Not a valid MP3 stream. */ drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); /* The call above may have allocated memory. Need to make sure it's freed before aborting. */ - return DRMP3_FALSE; /* Not a valid MP3 stream. */ + return DRMP3_FALSE; + } + + if (detectedMP3FrameCount != 0xFFFFFFFF) { + pMP3->totalPCMFrameCount = detectedMP3FrameCount * firstFramePCMFrameCount; } pMP3->channels = pMP3->mp3FrameChannels; @@ -2861,14 +3268,14 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm return DRMP3_TRUE; } -DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks) +DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, drmp3_meta_proc onMeta, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks) { if (pMP3 == NULL || onRead == NULL) { return DRMP3_FALSE; } DRMP3_ZERO_OBJECT(pMP3); - return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); + return drmp3_init_internal(pMP3, onRead, onSeek, onTell, onMeta, pUserData, pUserData, pAllocationCallbacks); } @@ -2896,35 +3303,52 @@ static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t by static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin) { drmp3* pMP3 = (drmp3*)pUserData; + drmp3_int64 newCursor; DRMP3_ASSERT(pMP3 != NULL); - if (origin == drmp3_seek_origin_current) { - if (byteOffset > 0) { - if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) { - byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); /* Trying to seek too far forward. */ - } - } else { - if (pMP3->memory.currentReadPos < (size_t)-byteOffset) { - byteOffset = -(int)pMP3->memory.currentReadPos; /* Trying to seek too far backwards. */ - } - } + newCursor = pMP3->memory.currentReadPos; - /* This will never underflow thanks to the clamps above. */ - pMP3->memory.currentReadPos += byteOffset; + if (origin == DRMP3_SEEK_SET) { + newCursor = 0; + } else if (origin == DRMP3_SEEK_CUR) { + newCursor = (drmp3_int64)pMP3->memory.currentReadPos; + } else if (origin == DRMP3_SEEK_END) { + newCursor = (drmp3_int64)pMP3->memory.dataSize; } else { - if ((drmp3_uint32)byteOffset <= pMP3->memory.dataSize) { - pMP3->memory.currentReadPos = byteOffset; - } else { - pMP3->memory.currentReadPos = pMP3->memory.dataSize; /* Trying to seek too far forward. */ - } + DRMP3_ASSERT(!"Invalid seek origin"); + return DRMP3_FALSE; } + newCursor += byteOffset; + + if (newCursor < 0) { + return DRMP3_FALSE; /* Trying to seek prior to the start of the buffer. */ + } + if ((size_t)newCursor > pMP3->memory.dataSize) { + return DRMP3_FALSE; /* Trying to seek beyond the end of the buffer. */ + } + + pMP3->memory.currentReadPos = (size_t)newCursor; + return DRMP3_TRUE; } -DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks) +static drmp3_bool32 drmp3__on_tell_memory(void* pUserData, drmp3_int64* pCursor) { + drmp3* pMP3 = (drmp3*)pUserData; + + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pCursor != NULL); + + *pCursor = (drmp3_int64)pMP3->memory.currentReadPos; + return DRMP3_TRUE; +} + +DRMP3_API drmp3_bool32 drmp3_init_memory_with_metadata(drmp3* pMP3, const void* pData, size_t dataSize, drmp3_meta_proc onMeta, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3_bool32 result; + if (pMP3 == NULL) { return DRMP3_FALSE; } @@ -2939,7 +3363,26 @@ DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t pMP3->memory.dataSize = dataSize; pMP3->memory.currentReadPos = 0; - return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, pAllocationCallbacks); + result = drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, drmp3__on_tell_memory, onMeta, pMP3, pUserDataMeta, pAllocationCallbacks); + if (result == DRMP3_FALSE) { + return DRMP3_FALSE; + } + + /* Adjust the length of the memory stream to account for ID3v1 and APE tags. */ + if (pMP3->streamLength <= (drmp3_uint64)DRMP3_SIZE_MAX) { + pMP3->memory.dataSize = (size_t)pMP3->streamLength; /* Safe cast. */ + } + + if (pMP3->streamStartOffset > (drmp3_uint64)DRMP3_SIZE_MAX) { + return DRMP3_FALSE; /* Tags too big. */ + } + + return DRMP3_TRUE; +} + +DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + return drmp3_init_memory_with_metadata(pMP3, pData, dataSize, NULL, NULL, pAllocationCallbacks); } @@ -3069,7 +3512,7 @@ static drmp3_result drmp3_result_from_errno(int e) #ifdef ENOSYS case ENOSYS: return DRMP3_NOT_IMPLEMENTED; #endif - #ifdef ENOTEMPTY + #if defined(ENOTEMPTY) && ENOTEMPTY != EEXIST /* In AIX, ENOTEMPTY and EEXIST use the same value. */ case ENOTEMPTY: return DRMP3_DIRECTORY_NOT_EMPTY; #endif #ifdef ELOOP @@ -3519,19 +3962,56 @@ static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t byt static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin) { - return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; + int whence = SEEK_SET; + if (origin == DRMP3_SEEK_CUR) { + whence = SEEK_CUR; + } else if (origin == DRMP3_SEEK_END) { + whence = SEEK_END; + } + + return fseek((FILE*)pUserData, offset, whence) == 0; } -DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) +static drmp3_bool32 drmp3__on_tell_stdio(void* pUserData, drmp3_int64* pCursor) +{ + FILE* pFileStdio = (FILE*)pUserData; + drmp3_int64 result; + + /* These were all validated at a higher level. */ + DRMP3_ASSERT(pFileStdio != NULL); + DRMP3_ASSERT(pCursor != NULL); + +#if defined(_WIN32) + #if defined(_MSC_VER) && _MSC_VER > 1200 + result = _ftelli64(pFileStdio); + #else + result = ftell(pFileStdio); + #endif +#else + result = ftell(pFileStdio); +#endif + + *pCursor = result; + + return DRMP3_TRUE; +} + +DRMP3_API drmp3_bool32 drmp3_init_file_with_metadata(drmp3* pMP3, const char* pFilePath, drmp3_meta_proc onMeta, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks) { drmp3_bool32 result; FILE* pFile; + if (pMP3 == NULL) { + return DRMP3_FALSE; + } + + DRMP3_ZERO_OBJECT(pMP3); + if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) { return DRMP3_FALSE; } - result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + result = drmp3_init_internal(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, drmp3__on_tell_stdio, onMeta, (void*)pFile, pUserDataMeta, pAllocationCallbacks); if (result != DRMP3_TRUE) { fclose(pFile); return result; @@ -3540,16 +4020,22 @@ DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const return DRMP3_TRUE; } -DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) +DRMP3_API drmp3_bool32 drmp3_init_file_with_metadata_w(drmp3* pMP3, const wchar_t* pFilePath, drmp3_meta_proc onMeta, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks) { drmp3_bool32 result; FILE* pFile; + if (pMP3 == NULL) { + return DRMP3_FALSE; + } + + DRMP3_ZERO_OBJECT(pMP3); + if (drmp3_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != DRMP3_SUCCESS) { return DRMP3_FALSE; } - result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + result = drmp3_init_internal(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, drmp3__on_tell_stdio, onMeta, (void*)pFile, pUserDataMeta, pAllocationCallbacks); if (result != DRMP3_TRUE) { fclose(pFile); return result; @@ -3557,6 +4043,16 @@ DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, return DRMP3_TRUE; } + +DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + return drmp3_init_file_with_metadata(pMP3, pFilePath, NULL, NULL, pAllocationCallbacks); +} + +DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + return drmp3_init_file_with_metadata_w(pMP3, pFilePath, NULL, NULL, pAllocationCallbacks); +} #endif DRMP3_API void drmp3_uninit(drmp3* pMP3) @@ -3564,7 +4060,7 @@ DRMP3_API void drmp3_uninit(drmp3* pMP3) if (pMP3 == NULL) { return; } - + #ifndef DR_MP3_NO_STDIO if (pMP3->onRead == drmp3__on_read_stdio) { FILE* pFile = (FILE*)pMP3->pUserData; @@ -3644,19 +4140,48 @@ static drmp3_uint64 drmp3_read_pcm_frames_raw(drmp3* pMP3, drmp3_uint64 framesTo DRMP3_ASSERT(pMP3->onRead != NULL); while (framesToRead > 0) { - drmp3_uint32 framesToConsume = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); + drmp3_uint32 framesToConsume; + + /* Skip frames if necessary. */ + if (pMP3->currentPCMFrame < pMP3->delayInPCMFrames) { + drmp3_uint32 framesToSkip = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, pMP3->delayInPCMFrames - pMP3->currentPCMFrame); + + pMP3->currentPCMFrame += framesToSkip; + pMP3->pcmFramesConsumedInMP3Frame += framesToSkip; + pMP3->pcmFramesRemainingInMP3Frame -= framesToSkip; + } + + framesToConsume = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); + + /* Clamp the number of frames to read to the padding. */ + if (pMP3->totalPCMFrameCount != DRMP3_UINT64_MAX && pMP3->totalPCMFrameCount > pMP3->paddingInPCMFrames) { + if (pMP3->currentPCMFrame < (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames)) { + drmp3_uint64 framesRemainigToPadding = (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames) - pMP3->currentPCMFrame; + if (framesToConsume > framesRemainigToPadding) { + framesToConsume = (drmp3_uint32)framesRemainigToPadding; + } + } else { + /* We're into the padding. Abort. */ + break; + } + } + if (pBufferOut != NULL) { - #if defined(DR_MP3_FLOAT_OUTPUT) - /* f32 */ - float* pFramesOutF32 = (float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); - float* pFramesInF32 = (float*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); - DRMP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); - #else - /* s16 */ - drmp3_int16* pFramesOutS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalFramesRead * pMP3->channels); - drmp3_int16* pFramesInS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(drmp3_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); - DRMP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(drmp3_int16) * framesToConsume * pMP3->channels); - #endif + #if defined(DR_MP3_FLOAT_OUTPUT) + { + /* f32 */ + float* pFramesOutF32 = (float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); + float* pFramesInF32 = (float*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + DRMP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); + } + #else + { + /* s16 */ + drmp3_int16* pFramesOutS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalFramesRead * pMP3->channels); + drmp3_int16* pFramesInS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(drmp3_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + DRMP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(drmp3_int16) * framesToConsume * pMP3->channels); + } + #endif } pMP3->currentPCMFrame += framesToConsume; @@ -3669,12 +4194,14 @@ static drmp3_uint64 drmp3_read_pcm_frames_raw(drmp3* pMP3, drmp3_uint64 framesTo break; } + /* If the cursor is already at the padding we need to abort. */ + if (pMP3->totalPCMFrameCount != DRMP3_UINT64_MAX && pMP3->totalPCMFrameCount > pMP3->paddingInPCMFrames && pMP3->currentPCMFrame >= (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames)) { + break; + } + DRMP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); - /* - At this point we have exhausted our in-memory buffer so we need to re-fill. Note that the sample rate may have changed - at this point which means we'll also need to update our sample rate conversion pipeline. - */ + /* At this point we have exhausted our in-memory buffer so we need to re-fill. */ if (drmp3_decode_next_frame(pMP3) == 0) { break; } @@ -3776,7 +4303,7 @@ static drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3) DRMP3_ASSERT(pMP3->onSeek != NULL); /* Seek to the start of the stream to begin with. */ - if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) { + if (!drmp3__on_seek_64(pMP3, pMP3->streamStartOffset, DRMP3_SEEK_SET)) { return DRMP3_FALSE; } @@ -3876,7 +4403,7 @@ static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint6 } /* First thing to do is seek to the first byte of the relevant MP3 frame. */ - if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, drmp3_seek_origin_start)) { + if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, DRMP3_SEEK_SET)) { return DRMP3_FALSE; /* Failed to seek. */ } @@ -3895,7 +4422,7 @@ static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint6 } /* We first need to decode the next frame. */ - pcmFramesRead = drmp3_decode_next_frame_ex(pMP3, pPCMFrames); + pcmFramesRead = drmp3_decode_next_frame_ex(pMP3, pPCMFrames, NULL, NULL); if (pcmFramesRead == 0) { return DRMP3_FALSE; } @@ -3952,7 +4479,7 @@ DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint /* We'll need to seek back to where we were, so grab the PCM frame we're currently sitting on so we can restore later. */ currentPCMFrame = pMP3->currentPCMFrame; - + if (!drmp3_seek_to_start_of_stream(pMP3)) { return DRMP3_FALSE; } @@ -3963,7 +4490,7 @@ DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint for (;;) { drmp3_uint32 pcmFramesInCurrentMP3Frame; - pcmFramesInCurrentMP3Frame = drmp3_decode_next_frame_ex(pMP3, NULL); + pcmFramesInCurrentMP3Frame = drmp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL); if (pcmFramesInCurrentMP3Frame == 0) { break; } @@ -3994,11 +4521,35 @@ DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3) { drmp3_uint64 totalPCMFrameCount; - if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { + + if (pMP3 == NULL) { return 0; } - return totalPCMFrameCount; + if (pMP3->totalPCMFrameCount != DRMP3_UINT64_MAX) { + totalPCMFrameCount = pMP3->totalPCMFrameCount; + + if (totalPCMFrameCount >= pMP3->delayInPCMFrames) { + totalPCMFrameCount -= pMP3->delayInPCMFrames; + } else { + /* The delay is greater than the frame count reported by the Xing/Info tag. Assume it's invalid and ignore. */ + } + + if (totalPCMFrameCount >= pMP3->paddingInPCMFrames) { + totalPCMFrameCount -= pMP3->paddingInPCMFrames; + } else { + /* The padding is greater than the frame count reported by the Xing/Info tag. Assume it's invalid and ignore. */ + } + + return totalPCMFrameCount; + } else { + /* Unknown frame count. Need to calculate it. */ + if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { + return 0; + } + + return totalPCMFrameCount; + } } DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3) @@ -4050,7 +4601,7 @@ DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pS /* We'll need to seek back to the current sample after calculating the seekpoints so we need to go ahead and grab the current location at the top. */ currentPCMFrame = pMP3->currentPCMFrame; - + /* We never do more than the total number of MP3 frames and we limit it to 32-bits. */ if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) { return DRMP3_FALSE; @@ -4101,7 +4652,7 @@ DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pS mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount; /* We need to get information about this frame so we can know how many samples it contained. */ - pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); + pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL); if (pcmFramesInCurrentMP3FrameIn == 0) { return DRMP3_FALSE; /* This should never happen. */ } @@ -4145,7 +4696,7 @@ DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pS Go to the next MP3 frame. This shouldn't ever fail, but just in case it does we just set the seek point and break. If it happens, it should only ever do it for the last seek point. */ - pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); + pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL); if (pcmFramesInCurrentMP3FrameIn == 0) { pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; @@ -4327,20 +4878,20 @@ static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pC } -DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) { drmp3 mp3; - if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!drmp3_init(&mp3, onRead, onSeek, onTell, NULL, pUserData, pAllocationCallbacks)) { return NULL; } return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); } -DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) { drmp3 mp3; - if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!drmp3_init(&mp3, onRead, onSeek, onTell, NULL, pUserData, pAllocationCallbacks)) { return NULL; } @@ -4427,74 +4978,26 @@ DIFFERENCES BETWEEN minimp3 AND dr_mp3 using minimp3 in conjunction with stb_vorbis. dr_mp3 addresses this. */ -/* -RELEASE NOTES - v0.5.0 -======================= -Version 0.5.0 has breaking API changes. - -Improved Client-Defined Memory Allocation ------------------------------------------ -The main change with this release is the addition of a more flexible way of implementing custom memory allocation routines. The -existing system of DRMP3_MALLOC, DRMP3_REALLOC and DRMP3_FREE are still in place and will be used by default when no custom -allocation callbacks are specified. - -To use the new system, you pass in a pointer to a drmp3_allocation_callbacks object to drmp3_init() and family, like this: - - void* my_malloc(size_t sz, void* pUserData) - { - return malloc(sz); - } - void* my_realloc(void* p, size_t sz, void* pUserData) - { - return realloc(p, sz); - } - void my_free(void* p, void* pUserData) - { - free(p); - } - - ... - - drmp3_allocation_callbacks allocationCallbacks; - allocationCallbacks.pUserData = &myData; - allocationCallbacks.onMalloc = my_malloc; - allocationCallbacks.onRealloc = my_realloc; - allocationCallbacks.onFree = my_free; - drmp3_init_file(&mp3, "my_file.mp3", NULL, &allocationCallbacks); - -The advantage of this new system is that it allows you to specify user data which will be passed in to the allocation routines. - -Passing in null for the allocation callbacks object will cause dr_mp3 to use defaults which is the same as DRMP3_MALLOC, -DRMP3_REALLOC and DRMP3_FREE and the equivalent of how it worked in previous versions. - -Every API that opens a drmp3 object now takes this extra parameter. These include the following: - - drmp3_init() - drmp3_init_file() - drmp3_init_memory() - drmp3_open_and_read_pcm_frames_f32() - drmp3_open_and_read_pcm_frames_s16() - drmp3_open_memory_and_read_pcm_frames_f32() - drmp3_open_memory_and_read_pcm_frames_s16() - drmp3_open_file_and_read_pcm_frames_f32() - drmp3_open_file_and_read_pcm_frames_s16() - -Renamed APIs ------------- -The following APIs have been renamed for consistency with other dr_* libraries and to make it clear that they return PCM frame -counts rather than sample counts. - - drmp3_open_and_read_f32() -> drmp3_open_and_read_pcm_frames_f32() - drmp3_open_and_read_s16() -> drmp3_open_and_read_pcm_frames_s16() - drmp3_open_memory_and_read_f32() -> drmp3_open_memory_and_read_pcm_frames_f32() - drmp3_open_memory_and_read_s16() -> drmp3_open_memory_and_read_pcm_frames_s16() - drmp3_open_file_and_read_f32() -> drmp3_open_file_and_read_pcm_frames_f32() - drmp3_open_file_and_read_s16() -> drmp3_open_file_and_read_pcm_frames_s16() -*/ - /* REVISION HISTORY ================ +v0.7.0 - TBD + - The old `DRMP3_IMPLEMENTATION` has been removed. Use `DR_MP3_IMPLEMENTATION` instead. The reason for this change is that in the future everything will eventually be using the underscored naming convention in the future, so `drmp3` will become `dr_mp3`. + - API CHANGE: Seek origins have been renamed to match the naming convention used by dr_wav and my other libraries. + - drmp3_seek_origin_start -> DRMP3_SEEK_SET + - drmp3_seek_origin_current -> DRMP3_SEEK_CUR + - DRMP3_SEEK_END (new) + - API CHANGE: Add DRMP3_SEEK_END as a seek origin for the seek callback. This is required for detection of ID3v1 and APE tags. + - API CHANGE: Add onTell callback to `drmp3_init()`. This is needed in order to track the location of ID3v1 and APE tags. + - API CHANGE: Add onMeta callback to `drmp3_init()`. This is used for reporting tag data back to the caller. Currently this only reports the raw tag data which means applications need to parse the data themselves. + - API CHANGE: Rename `drmp3dec_frame_info.hz` to `drmp3dec_frame_info.sample_rate`. + - Add detection of ID3v2, ID3v1, APE and Xing/VBRI tags. This should fix errors with some files where the decoder was reading tags as audio data. + - Delay and padding samples from LAME tags are now handled. + - Fix compilation for AIX OS. + +v0.6.40 - 2024-12-17 + - Improve detection of ARM64EC + v0.6.39 - 2024-02-27 - Fix a Wdouble-promotion warning. diff --git a/src/external/dr_wav.h b/src/external/dr_wav.h index a8207ab90..7a7e02246 100644 --- a/src/external/dr_wav.h +++ b/src/external/dr_wav.h @@ -1,6 +1,6 @@ /* WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file. -dr_wav - v0.13.16 - 2024-02-27 +dr_wav - v0.14.0 - TBD David Reid - mackron@gmail.com @@ -146,8 +146,8 @@ extern "C" { #define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x) #define DRWAV_VERSION_MAJOR 0 -#define DRWAV_VERSION_MINOR 13 -#define DRWAV_VERSION_REVISION 16 +#define DRWAV_VERSION_MINOR 14 +#define DRWAV_VERSION_REVISION 0 #define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) #include /* For size_t. */ @@ -176,7 +176,7 @@ typedef unsigned int drwav_uint32; #pragma GCC diagnostic pop #endif #endif -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) || defined(__powerpc64__) typedef drwav_uint64 drwav_uintptr; #else typedef drwav_uint32 drwav_uintptr; @@ -305,8 +305,9 @@ typedef struct typedef enum { - drwav_seek_origin_start, - drwav_seek_origin_current + DRWAV_SEEK_SET, + DRWAV_SEEK_CUR, + DRWAV_SEEK_END } drwav_seek_origin; typedef enum @@ -415,11 +416,21 @@ origin [in] The origin of the seek - the current position or the start of the Returns whether or not the seek was successful. -Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which will be either drwav_seek_origin_start or -drwav_seek_origin_current. +Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which will be either DRWAV_SEEK_SET or +DRWAV_SEEK_CUR. */ typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin); +/* +Callback for when the current position in the stream needs to be retrieved. + +pUserData [in] The user data that was passed to drwav_init() and family. +pCursor [out] A pointer to a variable to receive the current position in the stream. + +Returns whether or not the operation was successful. +*/ +typedef drwav_bool32 (* drwav_tell_proc)(void* pUserData, drwav_int64* pCursor); + /* Callback for when drwav_init_ex() finds a chunk. @@ -514,6 +525,11 @@ typedef enum drwav_metadata_type_list_info_genre = 1 << 15, drwav_metadata_type_list_info_album = 1 << 16, drwav_metadata_type_list_info_tracknumber = 1 << 17, + drwav_metadata_type_list_info_location = 1 << 18, + drwav_metadata_type_list_info_organization = 1 << 19, + drwav_metadata_type_list_info_keywords = 1 << 20, + drwav_metadata_type_list_info_medium = 1 << 21, + drwav_metadata_type_list_info_description = 1 << 22, /* Other type constants for convenience. */ drwav_metadata_type_list_all_info_strings = drwav_metadata_type_list_info_software @@ -524,7 +540,12 @@ typedef enum | drwav_metadata_type_list_info_date | drwav_metadata_type_list_info_genre | drwav_metadata_type_list_info_album - | drwav_metadata_type_list_info_tracknumber, + | drwav_metadata_type_list_info_tracknumber + | drwav_metadata_type_list_info_location + | drwav_metadata_type_list_info_organization + | drwav_metadata_type_list_info_keywords + | drwav_metadata_type_list_info_medium + | drwav_metadata_type_list_info_description, drwav_metadata_type_list_all_adtl = drwav_metadata_type_list_label | drwav_metadata_type_list_note @@ -555,11 +576,11 @@ typedef struct /* See drwav_smpl_loop_type. */ drwav_uint32 type; - /* The byte offset of the first sample to be played in the loop. */ - drwav_uint32 firstSampleByteOffset; + /* The offset of the first sample to be played in the loop. */ + drwav_uint32 firstSampleOffset; - /* The byte offset into the audio data of the last sample to be played in the loop. */ - drwav_uint32 lastSampleByteOffset; + /* The offset into the audio data of the last sample to be played in the loop. */ + drwav_uint32 lastSampleOffset; /* A value to represent that playback should occur at a point between samples. This value ranges from 0 to UINT32_MAX. Where a value of 0 means no fraction, and a value of (UINT32_MAX / 2) would mean half a sample. */ drwav_uint32 sampleFraction; @@ -637,8 +658,8 @@ typedef struct /* Set to 0 for uncompressed formats. Else the last byte in compressed wave data where decompression can begin to find the value of the corresponding sample value. */ drwav_uint32 blockStart; - /* For uncompressed formats this is the byte offset of the cue point into the audio data. For compressed formats this is relative to the block specified with blockStart. */ - drwav_uint32 sampleByteOffset; + /* For uncompressed formats this is the offset of the cue point into the audio data. For compressed formats this is relative to the block specified with blockStart. */ + drwav_uint32 sampleOffset; } drwav_cue_point; typedef struct @@ -846,6 +867,9 @@ typedef struct /* A pointer to the function to call when the wav file needs to be seeked. */ drwav_seek_proc onSeek; + /* A pointer to the function to call when the position of the stream needs to be retrieved. */ + drwav_tell_proc onTell; + /* The user data to pass to callbacks. */ void* pUserData; @@ -968,9 +992,9 @@ after the function returns. See also: drwav_init_file(), drwav_init_memory(), drwav_uninit() */ -DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, drwav_chunk_proc onChunk, void* pReadSeekTellUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); /* Initializes a pre-allocated drwav object for writing. @@ -1273,9 +1297,9 @@ Opens and reads an entire wav file in a single operation. The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer. */ -DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); #ifndef DR_WAV_NO_STDIO /* Opens and decodes an entire wav file in a single operation. @@ -1384,7 +1408,7 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b); #define DRWAV_MAX_SIMD_VECTOR_SIZE 32 /* Architecture Detection */ -#if defined(__x86_64__) || defined(_M_X64) +#if defined(__x86_64__) || (defined(_M_X64) && !defined(_M_ARM64EC)) #define DRWAV_X64 #elif defined(__i386) || defined(_M_IX86) #define DRWAV_X86 @@ -1962,12 +1986,12 @@ DRWAV_PRIVATE drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uin drwav_uint64 bytesRemainingToSeek = offset; while (bytesRemainingToSeek > 0) { if (bytesRemainingToSeek > 0x7FFFFFFF) { - if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { + if (!onSeek(pUserData, 0x7FFFFFFF, DRWAV_SEEK_CUR)) { return DRWAV_FALSE; } bytesRemainingToSeek -= 0x7FFFFFFF; } else { - if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) { + if (!onSeek(pUserData, (int)bytesRemainingToSeek, DRWAV_SEEK_CUR)) { return DRWAV_FALSE; } bytesRemainingToSeek = 0; @@ -1980,21 +2004,21 @@ DRWAV_PRIVATE drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uin DRWAV_PRIVATE drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) { if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, drwav_seek_origin_start); + return onSeek(pUserData, (int)offset, DRWAV_SEEK_SET); } /* Larger than 32-bit seek. */ - if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) { + if (!onSeek(pUserData, 0x7FFFFFFF, DRWAV_SEEK_SET)) { return DRWAV_FALSE; } offset -= 0x7FFFFFFF; for (;;) { if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, drwav_seek_origin_current); + return onSeek(pUserData, (int)offset, DRWAV_SEEK_CUR); } - if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { + if (!onSeek(pUserData, 0x7FFFFFFF, DRWAV_SEEK_CUR)) { return DRWAV_FALSE; } offset -= 0x7FFFFFFF; @@ -2028,7 +2052,7 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserDat return DRWAV_FALSE; } - if (origin == drwav_seek_origin_start) { + if (origin == DRWAV_SEEK_SET) { *pCursor = offset; } else { *pCursor += offset; @@ -2096,7 +2120,7 @@ DRWAV_PRIVATE drwav_uint8* drwav__metadata_get_memory(drwav__metadata_parser* pP pParser->pDataCursor += align - modulo; } } - + pResult = pParser->pDataCursor; /* @@ -2189,12 +2213,12 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars bytesJustRead = drwav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); if (bytesJustRead == sizeof(smplLoopData)) { - pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = drwav_bytes_to_u32(smplLoopData + 0); - pMetadata->data.smpl.pLoops[iSampleLoop].type = drwav_bytes_to_u32(smplLoopData + 4); - pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 8); - pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 12); - pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = drwav_bytes_to_u32(smplLoopData + 16); - pMetadata->data.smpl.pLoops[iSampleLoop].playCount = drwav_bytes_to_u32(smplLoopData + 20); + pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = drwav_bytes_to_u32(smplLoopData + 0); + pMetadata->data.smpl.pLoops[iSampleLoop].type = drwav_bytes_to_u32(smplLoopData + 4); + pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleOffset = drwav_bytes_to_u32(smplLoopData + 8); + pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleOffset = drwav_bytes_to_u32(smplLoopData + 12); + pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = drwav_bytes_to_u32(smplLoopData + 16); + pMetadata->data.smpl.pLoops[iSampleLoop].playCount = drwav_bytes_to_u32(smplLoopData + 20); } else { break; } @@ -2254,7 +2278,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parse pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11]; pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = drwav_bytes_to_u32(cuePointData + 12); pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = drwav_bytes_to_u32(cuePointData + 16); - pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = drwav_bytes_to_u32(cuePointData + 20); + pMetadata->data.cue.pCuePoints[iCuePoint].sampleOffset = drwav_bytes_to_u32(cuePointData + 20); } else { break; } @@ -2407,7 +2431,7 @@ DRWAV_PRIVATE drwav_result drwav_buffer_reader_read(drwav_buffer_reader* pReader size_t bytesRemaining; DRWAV_ASSERT(pReader != NULL); - + if (pBytesRead != NULL) { *pBytesRead = 0; } @@ -2487,7 +2511,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_bext_to_metadata_obj(drwav__metadata_pars size_t bytesRead = drwav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); - + if (bytesRead == sizeof(bextData)) { drwav_buffer_reader reader; drwav_uint32 timeReferenceLow; @@ -2549,7 +2573,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_list_label_or_note_to_metadata_obj(drwav_ drwav_uint64 totalBytesRead = 0; size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesJustRead == sizeof(cueIDBuffer)) { drwav_uint32 sizeIncludingNullTerminator; @@ -2698,7 +2722,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* drwav_uint8 buffer[4]; size_t bytesJustRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, 28, drwav_seek_origin_current)) { + if (!pParser->onSeek(pParser->pReadSeekUserData, 28, DRWAV_SEEK_CUR)) { return bytesRead; } bytesRead += 28; @@ -2721,7 +2745,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* } } else { /* Loop count in header does not match the size of the chunk. */ - } + } } } else { bytesRead = drwav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); @@ -2811,7 +2835,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* return bytesRead; } allocSizeNeeded += drwav__strlen(buffer) + 1; - allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES; /* Coding history. */ + allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES + 1; /* Coding history. */ drwav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); @@ -2916,6 +2940,16 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_album); } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_tracknumber, "ITRK")) { subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_tracknumber); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_location, "IARL")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_location); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_organization, "ICMS")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_organization); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_keywords, "IKEY")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_keywords); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_medium, "IMED")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_medium); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_description, "ISBJ")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_description); } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) { subchunkBytesRead = drwav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); } @@ -2926,14 +2960,14 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* if (subchunkBytesRead < subchunkDataSize) { drwav_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, drwav_seek_origin_current)) { + if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, DRWAV_SEEK_CUR)) { break; } bytesRead += bytesToSeek; } if ((subchunkDataSize % 2) == 1) { - if (!pParser->onSeek(pParser->pReadSeekUserData, 1, drwav_seek_origin_current)) { + if (!pParser->onSeek(pParser->pReadSeekUserData, 1, DRWAV_SEEK_CUR)) { break; } bytesRead += 1; @@ -2985,16 +3019,17 @@ DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT) } } -DRWAV_PRIVATE drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +DRWAV_PRIVATE drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pReadSeekTellUserData, const drwav_allocation_callbacks* pAllocationCallbacks) { - if (pWav == NULL || onRead == NULL || onSeek == NULL) { + if (pWav == NULL || onRead == NULL || onSeek == NULL) { /* <-- onTell is optional. */ return DRWAV_FALSE; } DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav)); pWav->onRead = onRead; pWav->onSeek = onSeek; - pWav->pUserData = pReadSeekUserData; + pWav->onTell = onTell; + pWav->pUserData = pReadSeekTellUserData; pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { @@ -3311,7 +3346,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on fmt.channelMask = drwav_bytes_to_u32_ex(fmtext + 2, pWav->container); drwav_bytes_to_guid(fmtext + 6, fmt.subFormat); } else { - if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, drwav_seek_origin_current) == DRWAV_FALSE) { + if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, DRWAV_SEEK_CUR) == DRWAV_FALSE) { return DRWAV_FALSE; } } @@ -3321,7 +3356,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on } /* Seek past any leftover bytes. For w64 the leftover will be defined based on the chunk size. */ - if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current) == DRWAV_FALSE) { + if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), DRWAV_SEEK_CUR) == DRWAV_FALSE) { return DRWAV_FALSE; } cursor += (header.sizeInBytes - bytesReadSoFar); @@ -3342,7 +3377,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on if (((pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx || pWav->container == drwav_container_rf64) && drwav_fourcc_equal(header.id.fourcc, "data")) || ((pWav->container == drwav_container_w64) && drwav_guid_equal(header.id.guid, drwavGUID_W64_DATA))) { foundChunk_data = DRWAV_TRUE; - + pWav->dataChunkDataPos = cursor; if (pWav->container != drwav_container_rf64) { /* The data chunk size for RF64 will always be set to 0xFFFFFFFF here. It was set to it's true value earlier. */ @@ -3432,7 +3467,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on return DRWAV_FALSE; } - + channels = drwav_bytes_to_u16_ex (commData + 0, pWav->container); frameCount = drwav_bytes_to_u32_ex (commData + 2, pWav->container); sampleSizeInBits = drwav_bytes_to_u16_ex (commData + 6, pWav->container); @@ -3465,12 +3500,15 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on compressionFormat = DR_WAVE_FORMAT_MULAW; } else if (drwav_fourcc_equal(type, "ima4")) { compressionFormat = DR_WAVE_FORMAT_DVI_ADPCM; - sampleSizeInBits = 4; + sampleSizeInBits = 4; /* I haven't been able to figure out how to get correct decoding for IMA ADPCM. Until this is figured out we'll need to abort when we encounter such an encoding. Advice welcome! */ + (void)compressionFormat; + (void)sampleSizeInBits; + return DRWAV_FALSE; } else { return DRWAV_FALSE; /* Unknown or unsupported compression format. Need to abort. */ @@ -3507,7 +3545,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on /* In AIFF, samples are padded to 8 byte boundaries. We need to round up our bits per sample here. */ fmt.bitsPerSample += (fmt.bitsPerSample & 7); - + /* If the form type is AIFC there will be some additional data in the chunk. We need to seek past it. */ if (isAIFCFormType) { @@ -3533,20 +3571,46 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on return DRWAV_FALSE; } - /* We need to seek forward by the offset. */ + /* The position of the audio data starts at an offset. */ offset = drwav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container); - if (drwav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == DRWAV_FALSE) { - return DRWAV_FALSE; - } - cursor += offset; + pWav->dataChunkDataPos = cursor + offset; - pWav->dataChunkDataPos = cursor; + /* The data chunk size needs to be reduced by the offset or else seeking will break. */ dataChunkSize = chunkSize; - - /* If we're running in sequential mode, or we're not reading metadata, we have enough now that we can get out of the loop. */ - if (sequential || !isProcessingMetadata) { - break; /* No need to keep reading beyond the data chunk. */ + if (dataChunkSize > offset) { + dataChunkSize -= offset; } else { + dataChunkSize = 0; + } + + if (sequential) { + if (foundChunk_fmt) { /* <-- Name is misleading, but will be set to true if the COMM chunk has been parsed. */ + /* + Getting here means we're opening in sequential mode and we've found the SSND (data) and COMM (fmt) chunks. We need + to get out of the loop here or else we'll end up going past the data chunk and will have no way of getting back to + it since we're not allowed to seek backwards. + + One subtle detail here is that there is an offset with the SSND chunk. We need to make sure we seek past this offset + so we're left sitting on the first byte of actual audio data. + */ + if (drwav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == DRWAV_FALSE) { + return DRWAV_FALSE; + } + cursor += offset; + + break; + } else { + /* + Getting here means the COMM chunk was not found. In sequential mode, if we haven't yet found the COMM chunk + we'll need to abort because we can't be doing a backwards seek back to the SSND chunk in order to read the + data. For this reason, this configuration of AIFF files are not supported with sequential mode. + */ + return DRWAV_FALSE; + } + } else { + chunkSize += header.paddingSize; /* <-- Make sure we seek past the padding. */ + chunkSize -= sizeof(offsetAndBlockSizeData); /* <-- This was read earlier. */ + if (drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == DRWAV_FALSE) { break; } @@ -3557,7 +3621,6 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on } - /* Getting here means it's not a chunk that we care about internally, but might need to be handled as metadata by the caller. */ if (isProcessingMetadata) { drwav__metadata_process_chunk(&metadataParser, &header, drwav_metadata_type_all_including_unknown); @@ -3647,8 +3710,26 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on pWav->metadataCount = metadataParser.metadataCount; } - - /* At this point we should be sitting on the first byte of the raw audio data. */ + /* + It's possible for the size reported in the data chunk to be greater than that of the file. We + need to do a validation check here to make sure we don't exceed the file size. To skip this + check, set the onTell callback to NULL. + */ + if (pWav->onTell != NULL && pWav->onSeek != NULL) { + if (pWav->onSeek(pWav->pUserData, 0, DRWAV_SEEK_END) == DRWAV_TRUE) { + drwav_int64 fileSize; + if (pWav->onTell(pWav->pUserData, &fileSize)) { + if (dataChunkSize + pWav->dataChunkDataPos > (drwav_uint64)fileSize) { + dataChunkSize = (drwav_uint64)fileSize - pWav->dataChunkDataPos; + } + } + } else { + /* + Failed to seek to the end of the file. It might not be supported by the backend so in + this case we cannot perform the validation check. + */ + } + } /* I've seen a WAV file in the wild where a RIFF-ecapsulated file has the size of it's "RIFF" and @@ -3670,6 +3751,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on } } + /* At this point we want to be sitting on the first byte of the raw audio data. */ if (drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == DRWAV_FALSE) { drwav_free(pWav->pMetadata, &pWav->allocationCallbacks); return DRWAV_FALSE; @@ -3680,8 +3762,26 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on pWav->sampleRate = fmt.sampleRate; pWav->channels = fmt.channels; pWav->bitsPerSample = fmt.bitsPerSample; - pWav->bytesRemaining = dataChunkSize; pWav->translatedFormatTag = translatedFormatTag; + + /* + I've had a report where files would start glitching after seeking. The reason for this is the data + chunk is not a clean multiple of the PCM frame size in bytes. Where this becomes a problem is when + seeking, because the number of bytes remaining in the data chunk is used to calculate the current + byte position. If this byte position is not aligned to the number of bytes in a PCM frame, it will + result in the seek not being cleanly positioned at the start of the PCM frame thereby resulting in + all decoded frames after that being corrupted. + + To address this, we need to round the data chunk size down to the nearest multiple of the frame size. + */ + if (!drwav__is_compressed_format_tag(translatedFormatTag)) { + drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame > 0) { + dataChunkSize -= (dataChunkSize % bytesPerFrame); + } + } + + pWav->bytesRemaining = dataChunkSize; pWav->dataChunkDataSize = dataChunkSize; if (sampleCountFromFactChunk != 0) { @@ -3764,23 +3864,23 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on return DRWAV_TRUE; } -DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); + return drwav_init_ex(pWav, onRead, onSeek, onTell, NULL, pUserData, NULL, 0, pAllocationCallbacks); } -DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, drwav_chunk_proc onChunk, void* pReadSeekTellUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { - if (!drwav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { + if (!drwav_preinit(pWav, onRead, onSeek, onTell, pReadSeekTellUserData, pAllocationCallbacks)) { return DRWAV_FALSE; } return drwav_init__internal(pWav, onChunk, pChunkUserData, flags); } -DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { - if (!drwav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!drwav_preinit(pWav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) { return DRWAV_FALSE; } @@ -3995,8 +4095,8 @@ DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) { bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleOffset); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleOffset); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); } @@ -4036,7 +4136,7 @@ DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* bytesWritten += drwav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleOffset); } } break; @@ -4142,15 +4242,20 @@ DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* const char* pID = NULL; switch (pMetadata->type) { - case drwav_metadata_type_list_info_software: pID = "ISFT"; break; - case drwav_metadata_type_list_info_copyright: pID = "ICOP"; break; - case drwav_metadata_type_list_info_title: pID = "INAM"; break; - case drwav_metadata_type_list_info_artist: pID = "IART"; break; - case drwav_metadata_type_list_info_comment: pID = "ICMT"; break; - case drwav_metadata_type_list_info_date: pID = "ICRD"; break; - case drwav_metadata_type_list_info_genre: pID = "IGNR"; break; - case drwav_metadata_type_list_info_album: pID = "IPRD"; break; - case drwav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; + case drwav_metadata_type_list_info_software: pID = "ISFT"; break; + case drwav_metadata_type_list_info_copyright: pID = "ICOP"; break; + case drwav_metadata_type_list_info_title: pID = "INAM"; break; + case drwav_metadata_type_list_info_artist: pID = "IART"; break; + case drwav_metadata_type_list_info_comment: pID = "ICMT"; break; + case drwav_metadata_type_list_info_date: pID = "ICRD"; break; + case drwav_metadata_type_list_info_genre: pID = "IGNR"; break; + case drwav_metadata_type_list_info_album: pID = "IPRD"; break; + case drwav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; + case drwav_metadata_type_list_info_location: pID = "IARL"; break; + case drwav_metadata_type_list_info_organization: pID = "ICMS"; break; + case drwav_metadata_type_list_info_keywords: pID = "IKEY"; break; + case drwav_metadata_type_list_info_medium: pID = "IMED"; break; + case drwav_metadata_type_list_info_description: pID = "ISBJ"; break; default: break; } @@ -4195,7 +4300,7 @@ DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* if (pMetadata->data.labelOrNote.stringLength > 0) { chunkSize += pMetadata->data.labelOrNote.stringLength + 1; - } + } } break; case drwav_metadata_type_list_labelled_cue_region: @@ -4434,7 +4539,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_d /* "RIFF" chunk. */ if (pFormat->container == drwav_container_riff) { - drwav_uint32 chunkSizeRIFF = 28 + (drwav_uint32)initialDataChunkSize; /* +28 = "WAVE" + [sizeof "fmt " chunk] */ + drwav_uint32 chunkSizeRIFF = 36 + (drwav_uint32)initialDataChunkSize; /* +36 = "WAVE" + [sizeof "fmt " chunk] + [data chunk header] */ runningPos += drwav__write(pWav, "RIFF", 4); runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeRIFF); runningPos += drwav__write(pWav, "WAVE", 4); @@ -4704,7 +4809,7 @@ DRWAV_PRIVATE drwav_result drwav_result_from_errno(int e) #ifdef ENOSYS case ENOSYS: return DRWAV_NOT_IMPLEMENTED; #endif - #ifdef ENOTEMPTY + #if defined(ENOTEMPTY) && ENOTEMPTY != EEXIST /* In AIX, ENOTEMPTY and EEXIST use the same value. */ case ENOTEMPTY: return DRWAV_DIRECTORY_NOT_EMPTY; #endif #ifdef ELOOP @@ -5161,7 +5266,38 @@ DRWAV_PRIVATE size_t drwav__on_write_stdio(void* pUserData, const void* pData, s DRWAV_PRIVATE drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin) { - return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; + int whence = SEEK_SET; + if (origin == DRWAV_SEEK_CUR) { + whence = SEEK_CUR; + } else if (origin == DRWAV_SEEK_END) { + whence = SEEK_END; + } + + return fseek((FILE*)pUserData, offset, whence) == 0; +} + +DRWAV_PRIVATE drwav_bool32 drwav__on_tell_stdio(void* pUserData, drwav_int64* pCursor) +{ + FILE* pFileStdio = (FILE*)pUserData; + drwav_int64 result; + + /* These were all validated at a higher level. */ + DRWAV_ASSERT(pFileStdio != NULL); + DRWAV_ASSERT(pCursor != NULL); + +#if defined(_WIN32) + #if defined(_MSC_VER) && _MSC_VER > 1200 + result = _ftelli64(pFileStdio); + #else + result = ftell(pFileStdio); + #endif +#else + result = ftell(pFileStdio); +#endif + + *pCursor = result; + + return DRWAV_TRUE; } DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks) @@ -5174,12 +5310,12 @@ DRWAV_PRIVATE drwav_bool32 drwav_init_file__internal_FILE(drwav* pWav, FILE* pFi { drwav_bool32 result; - result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, drwav__on_tell_stdio, (void*)pFile, pAllocationCallbacks); if (result != DRWAV_TRUE) { fclose(pFile); return result; } - + result = drwav_init__internal(pWav, onChunk, pChunkUserData, flags); if (result != DRWAV_TRUE) { fclose(pFile); @@ -5352,29 +5488,34 @@ DRWAV_PRIVATE size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, si DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin) { drwav* pWav = (drwav*)pUserData; + drwav_int64 newCursor; + DRWAV_ASSERT(pWav != NULL); - if (origin == drwav_seek_origin_current) { - if (offset > 0) { - if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) { - return DRWAV_FALSE; /* Trying to seek too far forward. */ - } - } else { - if (pWav->memoryStream.currentReadPos < (size_t)-offset) { - return DRWAV_FALSE; /* Trying to seek too far backwards. */ - } - } + newCursor = pWav->memoryStream.currentReadPos; - /* This will never underflow thanks to the clamps above. */ - pWav->memoryStream.currentReadPos += offset; + if (origin == DRWAV_SEEK_SET) { + newCursor = 0; + } else if (origin == DRWAV_SEEK_CUR) { + newCursor = (drwav_int64)pWav->memoryStream.currentReadPos; + } else if (origin == DRWAV_SEEK_END) { + newCursor = (drwav_int64)pWav->memoryStream.dataSize; } else { - if ((drwav_uint32)offset <= pWav->memoryStream.dataSize) { - pWav->memoryStream.currentReadPos = offset; - } else { - return DRWAV_FALSE; /* Trying to seek too far forward. */ - } + DRWAV_ASSERT(!"Invalid seek origin"); + return DRWAV_FALSE; } + newCursor += offset; + + if (newCursor < 0) { + return DRWAV_FALSE; /* Trying to seek prior to the start of the buffer. */ + } + if ((size_t)newCursor > pWav->memoryStream.dataSize) { + return DRWAV_FALSE; /* Trying to seek beyond the end of the buffer. */ + } + + pWav->memoryStream.currentReadPos = (size_t)newCursor; + return DRWAV_TRUE; } @@ -5421,29 +5562,45 @@ DRWAV_PRIVATE size_t drwav__on_write_memory(void* pUserData, const void* pDataIn DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin) { drwav* pWav = (drwav*)pUserData; + drwav_int64 newCursor; + DRWAV_ASSERT(pWav != NULL); - if (origin == drwav_seek_origin_current) { - if (offset > 0) { - if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) { - offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); /* Trying to seek too far forward. */ - } - } else { - if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) { - offset = -(int)pWav->memoryStreamWrite.currentWritePos; /* Trying to seek too far backwards. */ - } - } + newCursor = pWav->memoryStreamWrite.currentWritePos; - /* This will never underflow thanks to the clamps above. */ - pWav->memoryStreamWrite.currentWritePos += offset; + if (origin == DRWAV_SEEK_SET) { + newCursor = 0; + } else if (origin == DRWAV_SEEK_CUR) { + newCursor = (drwav_int64)pWav->memoryStreamWrite.currentWritePos; + } else if (origin == DRWAV_SEEK_END) { + newCursor = (drwav_int64)pWav->memoryStreamWrite.dataSize; } else { - if ((drwav_uint32)offset <= pWav->memoryStreamWrite.dataSize) { - pWav->memoryStreamWrite.currentWritePos = offset; - } else { - pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; /* Trying to seek too far forward. */ - } + DRWAV_ASSERT(!"Invalid seek origin"); + return DRWAV_INVALID_ARGS; } + newCursor += offset; + + if (newCursor < 0) { + return DRWAV_FALSE; /* Trying to seek prior to the start of the buffer. */ + } + if ((size_t)newCursor > pWav->memoryStreamWrite.dataSize) { + return DRWAV_FALSE; /* Trying to seek beyond the end of the buffer. */ + } + + pWav->memoryStreamWrite.currentWritePos = (size_t)newCursor; + + return DRWAV_TRUE; +} + +DRWAV_PRIVATE drwav_bool32 drwav__on_tell_memory(void* pUserData, drwav_int64* pCursor) +{ + drwav* pWav = (drwav*)pUserData; + + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pCursor != NULL); + + *pCursor = (drwav_int64)pWav->memoryStream.currentReadPos; return DRWAV_TRUE; } @@ -5458,7 +5615,7 @@ DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_ return DRWAV_FALSE; } - if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) { + if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, drwav__on_tell_memory, pWav, pAllocationCallbacks)) { return DRWAV_FALSE; } @@ -5475,7 +5632,7 @@ DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* return DRWAV_FALSE; } - if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) { + if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, drwav__on_tell_memory, pWav, pAllocationCallbacks)) { return DRWAV_FALSE; } @@ -5565,25 +5722,25 @@ DRWAV_API drwav_result drwav_uninit(drwav* pWav) if (pWav->onSeek && !pWav->isSequentialWrite) { if (pWav->container == drwav_container_riff) { /* The "RIFF" chunk size. */ - if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) { + if (pWav->onSeek(pWav->pUserData, 4, DRWAV_SEEK_SET)) { drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); drwav__write_u32ne_to_le(pWav, riffChunkSize); } /* The "data" chunk size. */ - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, drwav_seek_origin_start)) { + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, DRWAV_SEEK_SET)) { drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize); drwav__write_u32ne_to_le(pWav, dataChunkSize); } } else if (pWav->container == drwav_container_w64) { /* The "RIFF" chunk size. */ - if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) { + if (pWav->onSeek(pWav->pUserData, 16, DRWAV_SEEK_SET)) { drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize); drwav__write_u64ne_to_le(pWav, riffChunkSize); } /* The "data" chunk size. */ - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, drwav_seek_origin_start)) { + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, DRWAV_SEEK_SET)) { drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize); drwav__write_u64ne_to_le(pWav, dataChunkSize); } @@ -5592,13 +5749,13 @@ DRWAV_API drwav_result drwav_uninit(drwav* pWav) int ds64BodyPos = 12 + 8; /* The "RIFF" chunk size. */ - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, drwav_seek_origin_start)) { + if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, DRWAV_SEEK_SET)) { drwav_uint64 riffChunkSize = drwav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); drwav__write_u64ne_to_le(pWav, riffChunkSize); } /* The "data" chunk size. */ - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, drwav_seek_origin_start)) { + if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, DRWAV_SEEK_SET)) { drwav_uint64 dataChunkSize = drwav__data_chunk_size_rf64(pWav->dataChunkDataSize); drwav__write_u64ne_to_le(pWav, dataChunkSize); } @@ -5663,7 +5820,7 @@ DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOu bytesToSeek = 0x7FFFFFFF; } - if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, drwav_seek_origin_current) == DRWAV_FALSE) { + if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, DRWAV_SEEK_CUR) == DRWAV_FALSE) { break; } @@ -5810,7 +5967,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav) return DRWAV_FALSE; /* No seeking in write mode. */ } - if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) { + if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, DRWAV_SEEK_SET)) { return DRWAV_FALSE; } @@ -5928,7 +6085,7 @@ DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetF while (offset > 0) { int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); - if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) { + if (!pWav->onSeek(pWav->pUserData, offset32, DRWAV_SEEK_CUR)) { return DRWAV_FALSE; } @@ -6101,6 +6258,13 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav { drwav_uint64 totalFramesRead = 0; + static drwav_int32 adaptationTable[] = { + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 + }; + static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; + static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; + DRWAV_ASSERT(pWav != NULL); DRWAV_ASSERT(framesToRead > 0); @@ -6126,6 +6290,11 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0]; pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1]; pWav->msadpcm.cachedFrameCount = 2; + + /* The predictor is used as an index into coeff1Table so we'll need to validate to ensure it never overflows. */ + if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table)) { + return totalFramesRead; /* Invalid file. */ + } } else { /* Stereo. */ drwav_uint8 header[14]; @@ -6148,6 +6317,11 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1]; pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1]; pWav->msadpcm.cachedFrameCount = 2; + + /* The predictor is used as an index into coeff1Table so we'll need to validate to ensure it never overflows. */ + if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= drwav_countof(coeff2Table)) { + return totalFramesRead; /* Invalid file. */ + } } } @@ -6181,13 +6355,6 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav if (pWav->msadpcm.bytesRemainingInBlock == 0) { continue; } else { - static drwav_int32 adaptationTable[] = { - 230, 230, 230, 230, 307, 409, 512, 614, - 768, 614, 512, 409, 307, 230, 230, 230 - }; - static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; - static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; - drwav_uint8 nibbles; drwav_int32 nibble0; drwav_int32 nibble1; @@ -6320,7 +6487,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); if (header[2] >= drwav_countof(stepTable)) { - pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current); + pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, DRWAV_SEEK_CUR); pWav->ima.bytesRemainingInBlock = 0; return totalFramesRead; /* Invalid data. */ } @@ -6338,7 +6505,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); if (header[2] >= drwav_countof(stepTable) || header[6] >= drwav_countof(stepTable)) { - pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current); + pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, DRWAV_SEEK_CUR); pWav->ima.bytesRemainingInBlock = 0; return totalFramesRead; /* Invalid data. */ } @@ -8006,7 +8173,7 @@ DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, uns -DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav wav; @@ -8020,14 +8187,14 @@ DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead *totalFrameCountOut = 0; } - if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!drwav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) { return NULL; } return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav wav; @@ -8041,14 +8208,14 @@ DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwa *totalFrameCountOut = 0; } - if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!drwav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) { return NULL; } return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav wav; @@ -8062,7 +8229,7 @@ DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead *totalFrameCountOut = 0; } - if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!drwav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) { return NULL; } @@ -8350,6 +8517,27 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) /* REVISION HISTORY ================ +v0.14.0 - TBD + - API CHANGE: Seek origin enums have been renamed to the following: + - drwav_seek_origin_start -> DRWAV_SEEK_SET + - drwav_seek_origin_current -> DRWAV_SEEK_CUR + - DRWAV_SEEK_END (new) + - API CHANGE: A new seek origin has been added to allow seeking from the end of the file. If you implement your own `onSeek` callback, you must now handle `DRWAV_SEEK_END`. If you only use `*_init_file()` or `*_init_memory()`, you need not change anything. + - API CHANGE: An `onTell` callback has been added to the following functions: + - drwav_init() + - drwav_init_ex() + - drwav_init_with_metadata() + - drwav_open_and_read_pcm_frames_s16() + - drwav_open_and_read_pcm_frames_f32() + - drwav_open_and_read_pcm_frames_s32() + - API CHANGE: The `firstSampleByteOffset`, `lastSampleByteOffset` and `sampleByteOffset` members of `drwav_cue_point` have been renamed to `firstSampleOffset`, `lastSampleOffset` and `sampleOffset`, respectively. + - Fix a static analysis warning. + - Fix compilation for AIX OS. + +v0.13.17 - 2024-12-17 + - Fix a possible crash when reading from MS-ADPCM encoded files. + - Improve detection of ARM64EC + v0.13.16 - 2024-02-27 - Fix a Wdouble-promotion warning. From e91a3697ffce4c51c5805278a1703bf90286f3bf Mon Sep 17 00:00:00 2001 From: Jordan Zedeck <98184287+zedeckj@users.noreply.github.com> Date: Sun, 6 Jul 2025 01:59:03 -0400 Subject: [PATCH 066/242] Fixed typo --- examples/text/text_codepoints_loading.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/text/text_codepoints_loading.c b/examples/text/text_codepoints_loading.c index aa9f7bb76..a176148ea 100644 --- a/examples/text/text_codepoints_loading.c +++ b/examples/text/text_codepoints_loading.c @@ -108,7 +108,7 @@ int main(void) } else { - // Draw provided text with laoded font, containing all required codepoint glyphs + // Draw provided text with loaded font, containing all required codepoint glyphs DrawTextEx(font, text, (Vector2) { 160, 110 }, 48, 5, BLACK); } From 79c29cbe247a3977ff5c5327b9b8d4f4d79ea51c Mon Sep 17 00:00:00 2001 From: jonathandw743 Date: Sun, 6 Jul 2025 11:36:44 +0100 Subject: [PATCH 067/242] fixed compile error for PLATFORM sdl --- src/platforms/rcore_desktop_sdl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 60753caf4..59f286307 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1612,7 +1612,7 @@ void PollInputEvents(void) unsigned int codepoint = (unsigned int)SDL_StepUTF8(&event.text.text, textLen); #else int codepointSize = 0; - codepoint = GetCodepointNextSDL(event.text.text, &codepointSize); + int codepoint = GetCodepointNextSDL(event.text.text, &codepointSize); #endif CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = codepoint; CORE.Input.Keyboard.charPressedQueueCount++; From de62be0ec5695e849a0f0c56aa72b7c64b2442ab Mon Sep 17 00:00:00 2001 From: katonar Date: Mon, 7 Jul 2025 17:28:23 +0200 Subject: [PATCH 068/242] - created complier flag SUPPORT_DRM_CACHE, to enable triple buffered DRM caching --- src/config.h | 5 +++++ src/platforms/rcore_drm.c | 42 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/config.h b/src/config.h index dc6cc5893..0fd685e7a 100644 --- a/src/config.h +++ b/src/config.h @@ -299,4 +299,9 @@ //------------------------------------------------------------------------------------ #define MAX_TRACELOG_MSG_LENGTH 256 // Max length of one trace-log message +//DRM configuration +#if defined(PLATFORM_DRM) +//#define SUPPORT_DRM_CACHE 1 //enable triple buffered DRM caching +#endif //PLATFORM_DRM + #endif // CONFIG_H diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 9aebe6cf0..33b5e2a05 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -71,6 +71,7 @@ #include "EGL/egl.h" // Native platform windowing system interface #include "EGL/eglext.h" // EGL extensions +#if defined(SUPPORT_DRM_CACHE) #include // for drmHandleEvent poll #include //for EBUSY, EAGAIN @@ -86,6 +87,8 @@ static volatile int fbCacheCount = 0; static volatile bool pendingFlip = false; static bool crtcSet = false; +#endif //SUPPORT_DRM_CACHE + #ifndef EGL_OPENGL_ES3_BIT #define EGL_OPENGL_ES3_BIT 0x40 #endif @@ -232,7 +235,6 @@ static const short linuxToRaylibMap[KEYMAP_SIZE] = { //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -int InitSwapScreenBuffer(void); int InitPlatform(void); // Initialize platform (graphics, inputs and more) void ClosePlatform(void); // Close platform @@ -567,6 +569,7 @@ void DisableCursor(void) CORE.Input.Mouse.cursorHidden = true; } +#if defined(SUPPORT_DRM_CACHE) static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data) { uint32_t fb_id = (uintptr_t)data; // Remove from cache @@ -769,7 +772,37 @@ void SwapScreenBuffer() { loopCnt = 0; } } +#else //SUPPORT_DRM_CACHE is not defined +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + eglSwapBuffers(platform.device, platform.surface); + if (!platform.gbmSurface || (-1 == platform.fd) || !platform.connector || !platform.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); + + struct gbm_bo *bo = gbm_surface_lock_front_buffer(platform.gbmSurface); + if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer"); + + uint32_t fb = 0; + int result = drmModeAddFB(platform.fd, platform.connector->modes[platform.modeIndex].hdisplay, platform.connector->modes[platform.modeIndex].vdisplay, 24, 32, gbm_bo_get_stride(bo), gbm_bo_get_handle(bo).u32, &fb); + if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d", result); + + result = drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fb, 0, 0, &platform.connector->connector_id, 1, &platform.connector->modes[platform.modeIndex]); + if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result); + + if (platform.prevFB) + { + result = drmModeRmFB(platform.fd, platform.prevFB); + if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result); + } + + platform.prevFB = fb; + + if (platform.prevBO) gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); + + platform.prevBO = bo; +} +#endif //SUPPORT_DRM_CACHE //---------------------------------------------------------------------------------- // Module Functions Definition: Misc //---------------------------------------------------------------------------------- @@ -1099,8 +1132,7 @@ int InitPlatform(void) EGL_BLUE_SIZE, 8, // BLUE color bit depth (alternative: 5) EGL_ALPHA_SIZE, 8, // ALPHA bit depth (required for transparent framebuffer) //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI) - //ToDo: verify this. In 5.5 it is 16, in master it was 24 - EGL_DEPTH_SIZE, 16, // Depth buffer size (Required to use Depth testing!) + EGL_DEPTH_SIZE, 24, // Depth buffer size (Required to use Depth testing!) //EGL_STENCIL_SIZE, 8, // Stencil buffer size EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs) @@ -1273,13 +1305,17 @@ int InitPlatform(void) CORE.Storage.basePath = GetWorkingDirectory(); //---------------------------------------------------------------------------- +#if defined(SUPPORT_DRM_CACHE) if(InitSwapScreenBuffer() == 0) { TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully"); return 0; } else { +#endif//SUPPORT_DRM_CACHE TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized failed"); +#if defined(SUPPORT_DRM_CACHE) return -1; } +#endif //SUPPORT_DRM_CACHE } From 5b182139ae3e3223398af825fec8082e5a98f748 Mon Sep 17 00:00:00 2001 From: katonar Date: Mon, 7 Jul 2025 17:55:32 +0200 Subject: [PATCH 069/242] - implementing Raylib coding convention --- src/platforms/rcore_drm.c | 45 ++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 33b5e2a05..09684291c 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -79,10 +79,10 @@ typedef struct { struct gbm_bo *bo; - uint32_t fb_id; // DRM framebuffer ID + uint32_t fbId; // DRM framebuffer ID } FramebufferCache; -static FramebufferCache fbCache[MAX_CACHED_BOS]; +static FramebufferCache fbCache[MAX_CACHED_BOS] = {0}; static volatile int fbCacheCount = 0; static volatile bool pendingFlip = false; static bool crtcSet = false; @@ -570,13 +570,14 @@ void DisableCursor(void) } #if defined(SUPPORT_DRM_CACHE) -static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data) { - uint32_t fb_id = (uintptr_t)data; +//callback to destroy cached framebuffer, set by gbm_bo_set_user_data() +static void DestroyFrameBufferCallback(struct gbm_bo *bo, void *data) { + uint32_t fbId = (uintptr_t)data; // Remove from cache for (int i = 0; i < fbCacheCount; i++) { if (fbCache[i].bo == bo) { - TRACELOG(LOG_INFO, "DRM: fb removed %u", (uintptr_t)fb_id); - drmModeRmFB(platform.fd, fbCache[i].fb_id); // Release DRM FB + TRACELOG(LOG_INFO, "DRM: fb removed %u", (uintptr_t)fbId); + drmModeRmFB(platform.fd, fbCache[i].fbId); // Release DRM FB // Shift remaining entries for (int j = i; j < fbCacheCount - 1; j++) { fbCache[j] = fbCache[j + 1]; @@ -588,11 +589,11 @@ static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data) { } // Create or retrieve cached DRM FB for BO -static uint32_t get_or_create_fb_for_bo(struct gbm_bo *bo) { +static uint32_t GetOrCreateFbForBo(struct gbm_bo *bo) { // Try to find existing cache entry for (int i = 0; i < fbCacheCount; i++) { if (fbCache[i].bo == bo) { - return fbCache[i].fb_id; + return fbCache[i].fbId; } } @@ -607,21 +608,21 @@ static uint32_t get_or_create_fb_for_bo(struct gbm_bo *bo) { uint32_t width = gbm_bo_get_width(bo); uint32_t height = gbm_bo_get_height(bo); - uint32_t fb_id; - if (drmModeAddFB(platform.fd, width, height, 24, 32, stride, handle, &fb_id)) { + uint32_t fbId; + if (drmModeAddFB(platform.fd, width, height, 24, 32, stride, handle, &fbId)) { //rmModeAddFB failed return 0; } // Store in cache - fbCache[fbCacheCount] = (FramebufferCache){ .bo = bo, .fb_id = fb_id }; + fbCache[fbCacheCount] = (FramebufferCache){ .bo = bo, .fbId = fbId }; fbCacheCount++; // Set destroy callback to auto-cleanup - gbm_bo_set_user_data(bo, (void*)(uintptr_t)fb_id, drm_fb_destroy_callback); + gbm_bo_set_user_data(bo, (void*)(uintptr_t)fbId, DestroyFrameBufferCallback); - TRACELOG(LOG_INFO, "DRM: added new bo %u" , (uintptr_t)fb_id); - return fb_id; + TRACELOG(LOG_INFO, "DRM: added new bo %u" , (uintptr_t)fbId); + return fbId; } // Renders a blank frame to allocate initial buffers @@ -652,14 +653,14 @@ int InitSwapScreenBuffer() { } // Create FB for first buffer - uint32_t fb_id = get_or_create_fb_for_bo(bo); - if (!fb_id) { + uint32_t fbId = GetOrCreateFbForBo(bo); + if (!fbId) { gbm_surface_release_buffer(platform.gbmSurface, bo); return -1; } // Initial CRTC setup - if (drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fb_id, + if (drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fbId, 0, 0, &platform.connector->connector_id, 1, &platform.connector->modes[platform.modeIndex])) { TRACELOG(LOG_ERROR, "Initial CRTC setup failed: %s", strerror(errno)); @@ -675,7 +676,7 @@ int InitSwapScreenBuffer() { // Static page flip handler // this will be called once the drmModePageFlip() finished from the drmHandleEvent(platform.fd, &evctx); context -static void page_flip_handler(int fd, unsigned int frame, +static void PageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data) { (void)fd; (void)frame; (void)sec; (void)usec; // Unused @@ -701,7 +702,7 @@ void SwapScreenBuffer() { // Process pending events non-blocking drmEventContext evctx = { .version = DRM_EVENT_CONTEXT_VERSION, - .page_flip_handler = page_flip_handler + .page_flip_handler = PageFlipHandler }; struct pollfd pfd = { .fd = platform.fd, .events = POLLIN }; @@ -726,8 +727,8 @@ void SwapScreenBuffer() { } // Get FB ID (creates new one if needed) - uint32_t fb_id = get_or_create_fb_for_bo(next_bo); - if (!fb_id) { + uint32_t fbId = GetOrCreateFbForBo(next_bo); + if (!fbId) { gbm_surface_release_buffer(platform.gbmSurface, next_bo); errCnt[2]++; return; @@ -744,7 +745,7 @@ void SwapScreenBuffer() { * is rendered.. * returns immediately. */ - if (drmModePageFlip(platform.fd, platform.crtc->crtc_id, fb_id, + if (drmModePageFlip(platform.fd, platform.crtc->crtc_id, fbId, DRM_MODE_PAGE_FLIP_EVENT, platform.prevBO)) { if (errno == EBUSY) { //Display busy - skip flip From d4f09984acb427f5ab67ee49235546b01ad09199 Mon Sep 17 00:00:00 2001 From: Amy Wilder Date: Mon, 7 Jul 2025 21:51:27 -0400 Subject: [PATCH 070/242] Add safety notes to 'Update_' functions --- src/raudio.c | 2 ++ src/raylib.h | 6 +++--- src/rtextures.c | 7 +++++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 2b6628a24..8929c9ef9 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1033,6 +1033,8 @@ void UnloadSoundAlias(Sound alias) } // Update sound buffer with new data +// NOTE 1: data format must match sound.stream.sampleSize +// NOTE 2: frameCount must not exceed sound.frameCount void UpdateSound(Sound sound, const void *data, int frameCount) { if (sound.stream.buffer != NULL) diff --git a/src/raylib.h b/src/raylib.h index 7919db775..38b703e98 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1422,8 +1422,8 @@ RLAPI bool IsTextureValid(Texture2D texture); RLAPI void UnloadTexture(Texture2D texture); // Unload texture from GPU memory (VRAM) RLAPI bool IsRenderTextureValid(RenderTexture2D target); // Check if a render texture is valid (loaded in GPU) RLAPI void UnloadRenderTexture(RenderTexture2D target); // Unload render texture from GPU memory (VRAM) -RLAPI void UpdateTexture(Texture2D texture, const void *pixels); // Update GPU texture with new data -RLAPI void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels); // Update GPU texture rectangle with new data +RLAPI void UpdateTexture(Texture2D texture, const void *pixels); // Update GPU texture with new data (pixels should be able to fill texture) +RLAPI void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels); // Update GPU texture rectangle with new data (pixels and rec should fit in texture) // Texture configuration functions RLAPI void GenTextureMipmaps(Texture2D *texture); // Generate GPU mipmaps for a texture @@ -1644,7 +1644,7 @@ RLAPI Sound LoadSound(const char *fileName); // Load so RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data RLAPI Sound LoadSoundAlias(Sound source); // Create a new sound that shares the same sample data as the source sound, does not own the sound data RLAPI bool IsSoundValid(Sound sound); // Checks if a sound is valid (data loaded and buffers initialized) -RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data +RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data (data and frame count should fit in sound) RLAPI void UnloadWave(Wave wave); // Unload wave data RLAPI void UnloadSound(Sound sound); // Unload sound RLAPI void UnloadSoundAlias(Sound alias); // Unload a sound alias (does not deallocate sample data) diff --git a/src/rtextures.c b/src/rtextures.c index ee116b0e5..337d01b1d 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -4337,14 +4337,17 @@ void UnloadRenderTexture(RenderTexture2D target) } // Update GPU texture with new data -// NOTE: pixels data must match texture.format +// NOTE 1: pixels data must match texture.format +// NOTE 2: pixels data must contain at least as many pixels as texture void UpdateTexture(Texture2D texture, const void *pixels) { rlUpdateTexture(texture.id, 0, 0, texture.width, texture.height, texture.format, pixels); } // Update GPU texture rectangle with new data -// NOTE: pixels data must match texture.format +// NOTE 1: pixels data must match texture.format +// NOTE 2: pixels data must contain as many pixels as rec contains +// NOTE 3: rec must fit completely within texture's width and height void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels) { rlUpdateTexture(texture.id, (int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, texture.format, pixels); From 9f6d37ecb4774a5f1f00c53fd85dfc15db69467b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 8 Jul 2025 01:51:52 +0000 Subject: [PATCH 071/242] Update raylib_api.* by CI --- parser/output/raylib_api.json | 6 +++--- parser/output/raylib_api.lua | 6 +++--- parser/output/raylib_api.txt | 6 +++--- parser/output/raylib_api.xml | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index f5872ec8a..2fddc75e7 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -8483,7 +8483,7 @@ }, { "name": "UpdateTexture", - "description": "Update GPU texture with new data", + "description": "Update GPU texture with new data (pixels should be able to fill texture)", "returnType": "void", "params": [ { @@ -8498,7 +8498,7 @@ }, { "name": "UpdateTextureRec", - "description": "Update GPU texture rectangle with new data", + "description": "Update GPU texture rectangle with new data (pixels and rec should fit in texture)", "returnType": "void", "params": [ { @@ -11465,7 +11465,7 @@ }, { "name": "UpdateSound", - "description": "Update sound buffer with new data", + "description": "Update sound buffer with new data (data and frame count should fit in sound)", "returnType": "void", "params": [ { diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index 81dd7f932..49e2e1f47 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -6242,7 +6242,7 @@ return { }, { name = "UpdateTexture", - description = "Update GPU texture with new data", + description = "Update GPU texture with new data (pixels should be able to fill texture)", returnType = "void", params = { {type = "Texture2D", name = "texture"}, @@ -6251,7 +6251,7 @@ return { }, { name = "UpdateTextureRec", - description = "Update GPU texture rectangle with new data", + description = "Update GPU texture rectangle with new data (pixels and rec should fit in texture)", returnType = "void", params = { {type = "Texture2D", name = "texture"}, @@ -7850,7 +7850,7 @@ return { }, { name = "UpdateSound", - description = "Update sound buffer with new data", + description = "Update sound buffer with new data (data and frame count should fit in sound)", returnType = "void", params = { {type = "Sound", name = "sound"}, diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index c74a79a5f..a92a96de3 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -3255,13 +3255,13 @@ Function 362: UnloadRenderTexture() (1 input parameters) Function 363: UpdateTexture() (2 input parameters) Name: UpdateTexture Return type: void - Description: Update GPU texture with new data + Description: Update GPU texture with new data (pixels should be able to fill texture) Param[1]: texture (type: Texture2D) Param[2]: pixels (type: const void *) Function 364: UpdateTextureRec() (3 input parameters) Name: UpdateTextureRec Return type: void - Description: Update GPU texture rectangle with new data + Description: Update GPU texture rectangle with new data (pixels and rec should fit in texture) Param[1]: texture (type: Texture2D) Param[2]: rec (type: Rectangle) Param[3]: pixels (type: const void *) @@ -4383,7 +4383,7 @@ Function 528: IsSoundValid() (1 input parameters) Function 529: UpdateSound() (3 input parameters) Name: UpdateSound Return type: void - Description: Update sound buffer with new data + Description: Update sound buffer with new data (data and frame count should fit in sound) Param[1]: sound (type: Sound) Param[2]: data (type: const void *) Param[3]: sampleCount (type: int) diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index b7af0c41a..e9c83ad9c 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -2138,11 +2138,11 @@ - + - + @@ -2928,7 +2928,7 @@ - + From 510dc763e98edf494b01d8c0e8d7318b593375e6 Mon Sep 17 00:00:00 2001 From: sir-irk Date: Tue, 8 Jul 2025 05:05:30 -0500 Subject: [PATCH 072/242] adding normal map example --- examples/Makefile | 1 + examples/Makefile.Web | 1 + .../resources/shaders/glsl100/normalmap.fs | 64 +++++++ .../resources/shaders/glsl100/normalmap.vs | 76 ++++++++ .../resources/shaders/glsl120/normalmap.fs | 62 +++++++ .../resources/shaders/glsl120/normalmap.vs | 76 ++++++++ .../resources/shaders/glsl330/normalmap.fs | 67 +++++++ .../resources/shaders/glsl330/normalmap.vs | 48 +++++ examples/shaders/resources/tiles_diffuse.png | Bin 0 -> 540633 bytes examples/shaders/resources/tiles_normal.png | Bin 0 -> 679312 bytes examples/shaders/shaders_normalmap.c | 171 ++++++++++++++++++ 11 files changed, 566 insertions(+) create mode 100644 examples/shaders/resources/shaders/glsl100/normalmap.fs create mode 100644 examples/shaders/resources/shaders/glsl100/normalmap.vs create mode 100644 examples/shaders/resources/shaders/glsl120/normalmap.fs create mode 100644 examples/shaders/resources/shaders/glsl120/normalmap.vs create mode 100644 examples/shaders/resources/shaders/glsl330/normalmap.fs create mode 100644 examples/shaders/resources/shaders/glsl330/normalmap.vs create mode 100644 examples/shaders/resources/tiles_diffuse.png create mode 100644 examples/shaders/resources/tiles_normal.png create mode 100644 examples/shaders/shaders_normalmap.c diff --git a/examples/Makefile b/examples/Makefile index 32a3a75ab..a77a7ce65 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -631,6 +631,7 @@ SHADERS = \ shaders/shaders_mesh_instancing \ shaders/shaders_model_shader \ shaders/shaders_multi_sample2d \ + shaders/shaders_normalmap \ shaders/shaders_palette_switch \ shaders/shaders_postprocessing \ shaders/shaders_raymarching \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 35ae70a18..267832d25 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -513,6 +513,7 @@ SHADERS = \ shaders/shaders_mesh_instancing \ shaders/shaders_model_shader \ shaders/shaders_multi_sample2d \ + shaders/shaders_normalmap \ shaders/shaders_palette_switch \ shaders/shaders_postprocessing \ shaders/shaders_raymarching \ diff --git a/examples/shaders/resources/shaders/glsl100/normalmap.fs b/examples/shaders/resources/shaders/glsl100/normalmap.fs new file mode 100644 index 000000000..02902a61f --- /dev/null +++ b/examples/shaders/resources/shaders/glsl100/normalmap.fs @@ -0,0 +1,64 @@ +#version 100 + +precision mediump float; + +// Input vertex attributes (from vertex shader) +varying vec3 fragPosition; +varying vec2 fragTexCoord; +varying vec3 fragNormal; //used for when normal mapping is toggled off +varying vec4 fragColor; +varying mat3 TBN; + +// Input uniform values +uniform sampler2D texture0; +uniform sampler2D normalMap; +uniform vec4 colDiffuse; +uniform vec3 viewPos; + +// NOTE: Add your custom variables here + +uniform vec3 lightPos; +uniform bool useNormalMap; +uniform float specularExponent; + +void main() +{ + vec4 texelColor = texture(texture0, vec2(fragTexCoord.x, fragTexCoord.y)); + vec3 specular = vec3(0.0); + vec3 viewDir = normalize(viewPos - fragPosition); + vec3 lightDir = normalize(lightPos - fragPosition); + + vec3 normal; + if (useNormalMap) + { + normal = texture(normalMap, vec2(fragTexCoord.x, fragTexCoord.y)).rgb; + + //Transform normal values to the range -1.0 ... 1.0 + normal = normalize(normal * 2.0 - 1.0); + + //Transform the normal from tangent-space to world-space for lighting calculation + normal = normalize(normal * TBN); + } + else + { + normal = normalize(fragNormal); + } + + vec4 tint = colDiffuse * fragColor; + + vec3 lightColor = vec3(1.0, 1.0, 1.0); + float NdotL = max(dot(normal, lightDir), 0.0); + vec3 lightDot = lightColor * NdotL; + + float specCo = 0.0; + + if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewDir, reflect(-lightDir, normal))), specularExponent); // 16 refers to shine + + specular += specCo; + + finalColor = (texelColor * ((tint + vec4(specular, 1.0)) * vec4(lightDot, 1.0))); + finalColor += texelColor * (vec4(1.0, 1.0, 1.0, 1.0) / 40.0) * tint; + + // Gamma correction + gl_FragColor = pow(finalColor, vec4(1.0 / 2.2)); +} diff --git a/examples/shaders/resources/shaders/glsl100/normalmap.vs b/examples/shaders/resources/shaders/glsl100/normalmap.vs new file mode 100644 index 000000000..072e0a6c2 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl100/normalmap.vs @@ -0,0 +1,76 @@ +#version 100 + +// Input vertex attributes +attribute vec3 vertexPosition; +attribute vec2 vertexTexCoord; +attribute vec3 vertexNormal; +attribute vec4 vertexTangent; +attribute vec4 vertexColor; + +// Input uniform values +uniform mat4 mvp; +uniform mat4 matModel; + +// Output vertex attributes (to fragment shader) +varying vec3 fragPosition; +varying vec2 fragTexCoord; +varying vec3 fragNormal; //used for when normal mapping is toggled off +varying vec4 fragColor; +varying mat3 TBN; + +// NOTE: Add your custom variables here + +// https://github.com/glslify/glsl-inverse +mat3 inverse(mat3 m) +{ + float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2]; + float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2]; + float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2]; + + float b01 = a22 * a11 - a12 * a21; + float b11 = -a22 * a10 + a12 * a20; + float b21 = a21 * a10 - a11 * a20; + + float det = a00 * b01 + a01 * b11 + a02 * b21; + + return mat3(b01, (-a22 * a01 + a02 * a21), (a12 * a01 - a02 * a11), + b11, (a22 * a00 - a02 * a20), (-a12 * a00 + a02 * a10), + b21, (-a21 * a00 + a01 * a20), (a11 * a00 - a01 * a10)) / det; +} + +// https://github.com/glslify/glsl-transpose +mat3 transpose(mat3 m) +{ + return mat3(m[0][0], m[1][0], m[2][0], + m[0][1], m[1][1], m[2][1], + m[0][2], m[1][2], m[2][2]); +} + +void main() +{ + // Compute binormal from vertex normal and tangent. W component is the tangent handedness + vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz) * vertexTangent.w; + + // Compute fragment normal based on normal transformations + mat3 normalMatrix = transpose(inverse(mat3(matModel))); + + // Compute fragment position based on model transformations + fragPosition = vec3(matModel * vec4(vertexPosition, 1.0)); + + //Create TBN matrix for transforming the normal map values from tangent-space to world-space + fragNormal = normalize(normalMatrix * vertexNormal); + + vec3 fragTangent = normalize(normalMatrix * vertexTangent.xyz); + fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal) * fragNormal); + + vec3 fragBinormal = normalize(normalMatrix * vertexBinormal); + fragBinormal = cross(fragNormal, fragTangent); + + TBN = transpose(mat3(fragTangent, fragBinormal, fragNormal)); + + fragColor = vertexColor; + + fragTexCoord = vertexTexCoord; + + gl_Position = mvp * vec4(vertexPosition, 1.0); +} diff --git a/examples/shaders/resources/shaders/glsl120/normalmap.fs b/examples/shaders/resources/shaders/glsl120/normalmap.fs new file mode 100644 index 000000000..caeb4d5c1 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/normalmap.fs @@ -0,0 +1,62 @@ +#version 120 + +// Input vertex attributes (from vertex shader) +varying vec3 fragPosition; +varying vec2 fragTexCoord; +varying vec3 fragNormal; //used for when normal mapping is toggled off +varying vec4 fragColor; +varying mat3 TBN; + +// Input uniform values +uniform sampler2D texture0; +uniform sampler2D normalMap; +uniform vec4 colDiffuse; +uniform vec3 viewPos; + +// NOTE: Add your custom variables here + +uniform vec3 lightPos; +uniform bool useNormalMap; +uniform float specularExponent; + +void main() +{ + vec4 texelColor = texture(texture0, vec2(fragTexCoord.x, fragTexCoord.y)); + vec3 specular = vec3(0.0); + vec3 viewDir = normalize(viewPos - fragPosition); + vec3 lightDir = normalize(lightPos - fragPosition); + + vec3 normal; + if (useNormalMap) + { + normal = texture(normalMap, vec2(fragTexCoord.x, fragTexCoord.y)).rgb; + + //Transform normal values to the range -1.0 ... 1.0 + normal = normalize(normal * 2.0 - 1.0); + + //Transform the normal from tangent-space to world-space for lighting calculation + normal = normalize(normal * TBN); + } + else + { + normal = normalize(fragNormal); + } + + vec4 tint = colDiffuse * fragColor; + + vec3 lightColor = vec3(1.0, 1.0, 1.0); + float NdotL = max(dot(normal, lightDir), 0.0); + vec3 lightDot = lightColor * NdotL; + + float specCo = 0.0; + + if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewDir, reflect(-lightDir, normal))), specularExponent); // 16 refers to shine + + specular += specCo; + + finalColor = (texelColor * ((tint + vec4(specular, 1.0)) * vec4(lightDot, 1.0))); + finalColor += texelColor * (vec4(1.0, 1.0, 1.0, 1.0) / 40.0) * tint; + + // Gamma correction + gl_FragColor = pow(finalColor, vec4(1.0 / 2.2)); +} diff --git a/examples/shaders/resources/shaders/glsl120/normalmap.vs b/examples/shaders/resources/shaders/glsl120/normalmap.vs new file mode 100644 index 000000000..d8921a68f --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/normalmap.vs @@ -0,0 +1,76 @@ +#version 120 + +// Input vertex attributes +attribute vec3 vertexPosition; +attribute vec2 vertexTexCoord; +attribute vec3 vertexNormal; +attribute vec4 vertexTangent; +attribute vec4 vertexColor; + +// Input uniform values +uniform mat4 mvp; +uniform mat4 matModel; + +// Output vertex attributes (to fragment shader) +varying vec3 fragPosition; +varying vec2 fragTexCoord; +varying vec3 fragNormal; //used for when normal mapping is toggled off +varying vec4 fragColor; +varying mat3 TBN; + +// NOTE: Add your custom variables here + +// https://github.com/glslify/glsl-inverse +mat3 inverse(mat3 m) +{ + float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2]; + float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2]; + float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2]; + + float b01 = a22 * a11 - a12 * a21; + float b11 = -a22 * a10 + a12 * a20; + float b21 = a21 * a10 - a11 * a20; + + float det = a00 * b01 + a01 * b11 + a02 * b21; + + return mat3(b01, (-a22 * a01 + a02 * a21), (a12 * a01 - a02 * a11), + b11, (a22 * a00 - a02 * a20), (-a12 * a00 + a02 * a10), + b21, (-a21 * a00 + a01 * a20), (a11 * a00 - a01 * a10)) / det; +} + +// https://github.com/glslify/glsl-transpose +mat3 transpose(mat3 m) +{ + return mat3(m[0][0], m[1][0], m[2][0], + m[0][1], m[1][1], m[2][1], + m[0][2], m[1][2], m[2][2]); +} + +void main() +{ + // Compute binormal from vertex normal and tangent. W component is the tangent handedness + vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz) * vertexTangent.w; + + // Compute fragment normal based on normal transformations + mat3 normalMatrix = transpose(inverse(mat3(matModel))); + + // Compute fragment position based on model transformations + fragPosition = vec3(matModel * vec4(vertexPosition, 1.0)); + + //Create TBN matrix for transforming the normal map values from tangent-space to world-space + fragNormal = normalize(normalMatrix * vertexNormal); + + vec3 fragTangent = normalize(normalMatrix * vertexTangent.xyz); + fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal) * fragNormal); + + vec3 fragBinormal = normalize(normalMatrix * vertexBinormal); + fragBinormal = cross(fragNormal, fragTangent); + + TBN = transpose(mat3(fragTangent, fragBinormal, fragNormal)); + + fragColor = vertexColor; + + fragTexCoord = vertexTexCoord; + + gl_Position = mvp * vec4(vertexPosition, 1.0); +} diff --git a/examples/shaders/resources/shaders/glsl330/normalmap.fs b/examples/shaders/resources/shaders/glsl330/normalmap.fs new file mode 100644 index 000000000..644130ab6 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/normalmap.fs @@ -0,0 +1,67 @@ +#version 330 + +// Input vertex attributes (from vertex shader) +in vec3 fragPosition; +in vec2 fragTexCoord; +in vec3 fragNormal; //used for when normal mapping is toggled off +in vec4 fragColor; + +// Input uniform values +uniform sampler2D texture0; +uniform sampler2D normalMap; +uniform vec4 colDiffuse; + +uniform vec3 viewPos; +uniform vec4 tintColor; + +uniform vec3 lightPos; +uniform bool useNormalMap; +uniform float specularExponent; + +// Output fragment color +out vec4 finalColor; + +in mat3 TBN; + +void main() +{ + vec4 texelColor = texture(texture0, vec2(fragTexCoord.x, fragTexCoord.y)); + vec3 specular = vec3(0.0); + vec3 viewDir = normalize(viewPos - fragPosition); + vec3 lightDir = normalize(lightPos - fragPosition); + + vec3 normal; + if (useNormalMap) + { + normal = texture(normalMap, vec2(fragTexCoord.x, fragTexCoord.y)).rgb; + + //Transform normal values to the range -1.0 ... 1.0 + normal = normalize(normal * 2.0 - 1.0); + + //Transform the normal from tangent-space to world-space for lighting calculation + normal = normalize(normal * TBN); + } + else + { + normal = normalize(fragNormal); + } + + vec4 tint = colDiffuse * fragColor; + + vec3 lightColor = vec3(1.0, 1.0, 1.0); + float NdotL = max(dot(normal, lightDir), 0.0); + vec3 lightDot = lightColor * NdotL; + + float specCo = 0.0; + + if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewDir, reflect(-lightDir, normal))), specularExponent); // 16 refers to shine + + specular += specCo; + + finalColor = (texelColor * ((tint + vec4(specular, 1.0)) * vec4(lightDot, 1.0))); + finalColor += texelColor * (vec4(1.0, 1.0, 1.0, 1.0) / 40.0) * tint; + + // Gamma correction + finalColor = pow(finalColor, vec4(1.0 / 2.2)); + //finalColor = vec4(normal, 1.0); +} diff --git a/examples/shaders/resources/shaders/glsl330/normalmap.vs b/examples/shaders/resources/shaders/glsl330/normalmap.vs new file mode 100644 index 000000000..1e0a161b8 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/normalmap.vs @@ -0,0 +1,48 @@ +#version 330 + +// Input vertex attributes +in vec3 vertexPosition; +in vec2 vertexTexCoord; +in vec3 vertexNormal; +in vec4 vertexTangent; +in vec4 vertexColor; + +// Input uniform values +uniform mat4 mvp; +uniform mat4 matModel; + +// Output vertex attributes (to fragment shader) +out vec3 fragPosition; +out vec2 fragTexCoord; +out vec3 fragNormal; //used for when normal mapping is toggled off +out vec4 fragColor; +out mat3 TBN; + +void main() +{ + // Compute binormal from vertex normal and tangent. W component is the tangent handedness + vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz) * vertexTangent.w; + + // Compute fragment normal based on normal transformations + mat3 normalMatrix = transpose(inverse(mat3(matModel))); + + // Compute fragment position based on model transformations + fragPosition = vec3(matModel * vec4(vertexPosition, 1.0)); + + //Create TBN matrix for transforming the normal map values from tangent-space to world-space + fragNormal = normalize(normalMatrix * vertexNormal); + + vec3 fragTangent = normalize(normalMatrix * vertexTangent.xyz); + fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal) * fragNormal); + + vec3 fragBinormal = normalize(normalMatrix * vertexBinormal); + fragBinormal = cross(fragNormal, fragTangent); + + TBN = transpose(mat3(fragTangent, fragBinormal, fragNormal)); + + fragColor = vertexColor; + + fragTexCoord = vertexTexCoord; + + gl_Position = mvp * vec4(vertexPosition, 1.0); +} diff --git a/examples/shaders/resources/tiles_diffuse.png b/examples/shaders/resources/tiles_diffuse.png new file mode 100644 index 0000000000000000000000000000000000000000..1c7b840354fc248d99d46eee19cbaa776b6fd130 GIT binary patch literal 540633 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

`v>1npaX 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 DF`OT7 literal 0 HcmV?d00001 diff --git a/examples/shaders/resources/tiles_normal.png b/examples/shaders/resources/tiles_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..719cb2e0d09a0b6234798bc276c2e274f3bb9c79 GIT binary patch literal 679312 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

&hICl--PYMpFnu|$>s$+tkBb|@jYfW~Q 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%0DxUoAUnr*kMOh8=$!S(M~{ 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

ZrMwb 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

XsKoMRgbz0hg5ynwV0!&+c^#*gYjp)E;NrH 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 z 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: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 rE_Tn{-mt{vJ%Yr;|9Z-wRlxrRD} +#include + +#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 + //-------------------------------------------------------------------------------------- + + SetConfigFlags(FLAG_MSAA_4X_HINT); + InitWindow(800, 450, "Normal Map"); + + Camera camera = {0}; + camera.position = (Vector3){0.0f, 2.0f, -4.0f}; + camera.target = (Vector3){0.0f, 0.0f, 0.0f}; + camera.up = (Vector3){0.0f, 1.0f, 0.0f}; + camera.fovy = 45.0f; + camera.projection = CAMERA_PERSPECTIVE; + + // Load basic normal map lighting shader + Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/normalmap.vs", GLSL_VERSION), + TextFormat("resources/shaders/glsl%i/normalmap.fs", GLSL_VERSION)); + + // Get some required shader locations + shader.locs[SHADER_LOC_MAP_NORMAL] = GetShaderLocation(shader, "normalMap"); + shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos"); + // NOTE: "matModel" location name is automatically assigned on shader loading, + // no need to get the location again if using that uniform name + // shader.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocation(shader, "matModel"); + + // This example uses just 1 point light. + Vector3 lightPosition = {0.0f, 1.0f, 0.0f}; + int lightPosLoc = GetShaderLocation(shader, "lightPos"); + + // Load a plane model that has proper normals and tangents + Model plane = LoadModel("resources/models/plane.glb"); + + // Set the plane model's shader and texture maps + plane.materials[0].shader = shader; + plane.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTexture("resources/tiles_diffuse.png"); + plane.materials[0].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture("resources/tiles_normal.png"); + + // Generate Mipmaps and use TRILINEAR filtering to help with texture aliasing + GenTextureMipmaps(&plane.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture); + GenTextureMipmaps(&plane.materials[0].maps[MATERIAL_MAP_NORMAL].texture); + + SetTextureFilter(plane.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture, TEXTURE_FILTER_TRILINEAR); + SetTextureFilter(plane.materials[0].maps[MATERIAL_MAP_NORMAL].texture, TEXTURE_FILTER_TRILINEAR); + + // Specular exponent AKA shininess of the material. + float specularExponent = 8.0f; + int specularExponentLoc = GetShaderLocation(shader, "specularExponent"); + + // Allow toggling the normal map on and off for comparison purposes + int useNormalMap = 1; + int useNormalMapLoc = GetShaderLocation(shader, "useNormalMap"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + + // Move the light around on the X and Z axis using WASD keys + Vector3 direction = {0}; + if (IsKeyDown(KEY_W)) direction = Vector3Add(direction, (Vector3){0.0f, 0.0f, 1.0f}); + if (IsKeyDown(KEY_S)) direction = Vector3Add(direction, (Vector3){0.0f, 0.0f, -1.0f}); + if (IsKeyDown(KEY_D)) direction = Vector3Add(direction, (Vector3){-1.0f, 0.0f, 0.0f}); + if (IsKeyDown(KEY_A)) direction = Vector3Add(direction, (Vector3){1.0f, 0.0f, 0.0f}); + + direction = Vector3Normalize(direction); + lightPosition = Vector3Add(lightPosition, Vector3Scale(direction, GetFrameTime() * 3.0f)); + + // Increase/Decrease the specular exponent(shininess) + if (IsKeyDown(KEY_UP)) specularExponent = Clamp(specularExponent + 40.0f * GetFrameTime(), 2.0f, 128.0f); + if (IsKeyDown(KEY_DOWN)) specularExponent = Clamp(specularExponent - 40.0f * GetFrameTime(), 2.0f, 128.0f); + + // Toggle normal map on and off + if (IsKeyPressed(KEY_N)) useNormalMap = !useNormalMap; + + // Spin plane model at a constant rate + plane.transform = MatrixRotateY(GetTime() * 0.5f); + + // Update shader values + float lightPos[3] = {lightPosition.x, lightPosition.y, lightPosition.z}; + SetShaderValue(shader, lightPosLoc, lightPos, SHADER_UNIFORM_VEC3); + + float camPos[3] = {camera.position.x, camera.position.y, camera.position.z}; + SetShaderValue(shader, shader.locs[SHADER_LOC_VECTOR_VIEW], camPos, SHADER_UNIFORM_VEC3); + + SetShaderValue(shader, specularExponentLoc, &specularExponent, SHADER_UNIFORM_FLOAT); + + SetShaderValue(shader, useNormalMapLoc, &useNormalMap, SHADER_UNIFORM_INT); + //-------------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + BeginShaderMode(shader); + + DrawModel(plane, Vector3Zero(), 2.0f, WHITE); + + EndShaderMode(); + + //Draw sphere to show light position + DrawSphereWires(lightPosition, 0.2f, 8, 8, ORANGE); + + EndMode3D(); + + Color textColor = (useNormalMap) ? DARKGREEN : RED; + const char *toggleStr = (useNormalMap) ? "On" : "Off"; + DrawText(TextFormat("Use key [N] to toggle normal map: %s", toggleStr), 10, 30, 20, textColor); + + int yOffset = 24; + DrawText("Use keys [W][A][S][D] to move the light", 10, 30 + yOffset * 1, 20, BLACK); + DrawText("Use kyes [Up][Down] to change specular exponent", 10, 30 + yOffset * 2, 20, BLACK); + DrawText(TextFormat("Specular Exponent: %.2f", specularExponent), 10, 30 + yOffset * 3, 20, BLUE); + + DrawFPS(10, 10); + + EndDrawing(); + //-------------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadShader(shader); + UnloadModel(plane); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} From 897a8fbc9ffd24be13d808e8e0b0af90a529a5a7 Mon Sep 17 00:00:00 2001 From: sir-irk Date: Tue, 8 Jul 2025 05:28:15 -0500 Subject: [PATCH 073/242] adding preview image --- examples/shaders/shader_normalmap.png | Bin 0 -> 276980 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/shaders/shader_normalmap.png diff --git a/examples/shaders/shader_normalmap.png b/examples/shaders/shader_normalmap.png new file mode 100644 index 0000000000000000000000000000000000000000..b8aa428aa9b9c0d33410b502bd7eefd5a03e3808 GIT binary patch literal 276980 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 z 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 literal 0 HcmV?d00001 From a94feef6d0534ca8c351d43d92d26cbfe3f01c79 Mon Sep 17 00:00:00 2001 From: sir-irk Date: Tue, 8 Jul 2025 05:36:39 -0500 Subject: [PATCH 074/242] update readme --- examples/README.md | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/examples/README.md b/examples/README.md index 7553ad113..f29362ef6 100644 --- a/examples/README.md +++ b/examples/README.md @@ -202,6 +202,7 @@ Examples using raylib shaders functionality, including shaders loading, paramete | 141 | [shaders_lightmap](shaders/shaders_lightmap.c) | shaders_lightmap | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Jussi Viitala](https://github.com/nullstare) | | 142 | [shaders_rounded_rectangle](shaders/shaders_rounded_rectangle.c) | shaders_rounded_rectangle | ⭐️⭐️⭐️☆ | 5.5 | 5.5 | [Anstro Pleuton](https://github.com/anstropleuton) | | 143 | [shaders_view_depth](shaders/shaders_view_depth.c) | shaders_view_depth | ⭐️⭐️⭐️☆ | 5.6-dev | 5.6-dev | [Luís Almeida](https://github.com/luis605) | +| 144 | [shaders_normalmap](shaders/shaders_normalmap.c) | shaders_normalmap | ⭐️⭐️⭐️⭐️ | 5.6-dev | 5.6-dev | [Jeremy Montgomery](https://github.com/Sir-Irk) | ### category: audio @@ -209,14 +210,14 @@ Examples using raylib audio functionality, including sound/music loading and pla | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 144 | [audio_module_playing](audio/audio_module_playing.c) | audio_module_playing | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 145 | [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐️☆☆☆ | 1.3 | 4.2 | [Ray](https://github.com/raysan5) | -| 146 | [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐️⭐️⭐️☆ | 1.6 | 4.2 | [Ray](https://github.com/raysan5) | -| 147 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | -| 148 | [audio_mixed_processor](audio/audio_mixed_processor.c) | audio_mixed_processor | ⭐️⭐️⭐️⭐️ | 4.2 | 4.2 | [hkc](https://github.com/hatkidchan) | -| 149 | [audio_stream_effects](audio/audio_stream_effects.c) | audio_stream_effects | ⭐️⭐️⭐️⭐️ | 4.2 | 5.0 | [Ray](https://github.com/raysan5) | -| 150 [audio_sound_multi](audio/audio_sound_multi.c) | audio_sound_multi | ⭐️⭐️☆☆ | 4.6 | 4.6 | [Jeffery Myers](https://github.com/JeffM2501) | -| 151 | [audio_sound_positioning](audio/audio_sound_positioning.c) | audio_sound_positioning | ⭐️⭐️☆☆ | 5.5 | 5.5 | [Le Juez Victor](https://github.com/Bigfoot71) | +| 145 | [audio_module_playing](audio/audio_module_playing.c) | audio_module_playing | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 146 | [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐️☆☆☆ | 1.3 | 4.2 | [Ray](https://github.com/raysan5) | +| 147 | [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐️⭐️⭐️☆ | 1.6 | 4.2 | [Ray](https://github.com/raysan5) | +| 148 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | +| 149 | [audio_mixed_processor](audio/audio_mixed_processor.c) | audio_mixed_processor | ⭐️⭐️⭐️⭐️ | 4.2 | 4.2 | [hkc](https://github.com/hatkidchan) | +| 150 | [audio_stream_effects](audio/audio_stream_effects.c) | audio_stream_effects | ⭐️⭐️⭐️⭐️ | 4.2 | 5.0 | [Ray](https://github.com/raysan5) | +| 151 | [audio_sound_multi](audio/audio_sound_multi.c) | audio_sound_multi | ⭐️⭐️☆☆ | 4.6 | 4.6 | [Jeffery Myers](https://github.com/JeffM2501) | +| 152 | [audio_sound_positioning](audio/audio_sound_positioning.c) | audio_sound_positioning | ⭐️⭐️☆☆ | 5.5 | 5.5 | [Le Juez Victor](https://github.com/Bigfoot71) | ### category: others @@ -224,11 +225,11 @@ Ex150amples showing raylib misc functionality that does not fit in other categor | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 152 | [rlgl_standalone](others/rlgl_standalone.c) | rlgl_standalone | ⭐️⭐️⭐️⭐️ | 1.6 | 4.0 | [Ray](https://github.com/raysan5) | -| 153 | [rlgl_compute_shader](others/rlgl_compute_shader.c) | rlgl_compute_shader | ⭐️⭐️⭐️⭐️ | 4.0 | 4.0 | [Teddy Astie](https://github.com/tsnake41) | -| 154 | [easings_testbed](others/easings_testbed.c) | easings_testbed | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) | -| 155 | [raylib_opengl_interop](others/raylib_opengl_interop.c) | raylib_opengl_interop | ⭐️⭐️⭐️⭐️ | 3.8 | 4.0 | [Stephan Soller](https://github.com/arkanis) | -| 156 | [embedded_files_loading](others/embedded_files_loading.c) | embedded_files_loading | ⭐️⭐️☆☆ | 3.0 | 3.5 | [Kristian Holmgren](https://github.com/defutura) | -| 157 | [raymath_vector_angle](others/raymath_vector_angle.c) | raymath_vector_angle | ⭐️⭐️☆☆ | 1.0 | 4.6 | [Ray](https://github.com/raysan5) | +| 153 | [rlgl_standalone](others/rlgl_standalone.c) | rlgl_standalone | ⭐️⭐️⭐️⭐️ | 1.6 | 4.0 | [Ray](https://github.com/raysan5) | +| 154 | [rlgl_compute_shader](others/rlgl_compute_shader.c) | rlgl_compute_shader | ⭐️⭐️⭐️⭐️ | 4.0 | 4.0 | [Teddy Astie](https://github.com/tsnake41) | +| 155 | [easings_testbed](others/easings_testbed.c) | easings_testbed | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) | +| 156 | [raylib_opengl_interop](others/raylib_opengl_interop.c) | raylib_opengl_interop | ⭐️⭐️⭐️⭐️ | 3.8 | 4.0 | [Stephan Soller](https://github.com/arkanis) | +| 157 | [embedded_files_loading](others/embedded_files_loading.c) | embedded_files_loading | ⭐️⭐️☆☆ | 3.0 | 3.5 | [Kristian Holmgren](https://github.com/defutura) | +| 158 | [raymath_vector_angle](others/raymath_vector_angle.c) | raymath_vector_angle | ⭐️⭐️☆☆ | 1.0 | 4.6 | [Ray](https://github.com/raysan5) | As always contributions are welcome, feel free to send new examples! Here is an [examples template](examples_template.c) to start with! From 9a578dbce0dcee78b05afa57fd6c959323129f38 Mon Sep 17 00:00:00 2001 From: sir-irk Date: Tue, 8 Jul 2025 05:47:11 -0500 Subject: [PATCH 075/242] fix formatting --- examples/shaders/shaders_normalmap.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/shaders/shaders_normalmap.c b/examples/shaders/shaders_normalmap.c index f71093109..3e92567b0 100644 --- a/examples/shaders/shaders_normalmap.c +++ b/examples/shaders/shaders_normalmap.c @@ -30,8 +30,7 @@ //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ -int -main(void) +int main(void) { // Initialization //-------------------------------------------------------------------------------------- From 86d2db2aa921f51d41887cb49cbba0feb5a0a37d Mon Sep 17 00:00:00 2001 From: sir-irk Date: Tue, 8 Jul 2025 05:51:14 -0500 Subject: [PATCH 076/242] fix typo on UI and preview image --- examples/shaders/shader_normalmap.png | Bin 276980 -> 267835 bytes examples/shaders/shaders_normalmap.c | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shaders/shader_normalmap.png b/examples/shaders/shader_normalmap.png index b8aa428aa9b9c0d33410b502bd7eefd5a03e3808..b2db75eeceb02020ad430ad7c017540d445f5b81 100644 GIT binary patch literal 267835 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%0AHf}Jt;7rA>8Ppiz?fT?{IObtC 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

(^g^(7+DP21t1zYI=Ng_4bWg z&dPR@W&=*_Ftwvkrb(vp_p+&T?Nwe`fT?BL$2lVx3k60nX&4|EbR}2d=%KY???`oO z@8BSOrNV8q)}IEKGZdeSHwEJIO^1HqQyxn}Mu0|pU=i7K5W?lvm;6T-uCEI3xjy5A zQ3Bn?7Zq=TS1zD@Mo?F)a;esDOcnQYUvJNx0bZkn7c45MaG=3uhxuJZlp)|+wiYjX zf5L$bMRPg>d3mhrJy%@A$i6lCc?bc>z@QpTf1cwbC?Fw1)e$Z~BWx75%GiT87u!g2 zX7@{2_It8)yqAa5iD$xZ?UuPq;-{XRxxhBiLVEL^m5Zsk5PkV`)LKtJJDo!W_U)EM zTkZ#Ac4tjCE&o=!LDv~~`MY(M&_Kni)|IDN6X1v0t|_b+7j<>8E=L7nv?V5x2dB~> zd&c#8^?7#S`%(%;BTeISBCvxqm=whf*N;Ww{5JpI1$G?waX{ER-!8=80#2}4$Tx?$ z`fYSjlRP@ejd~hM9+9KbQoo9Q=LM<}X0_X=txHj@$}V^htW&rgid#0~T;CZ8iqDDp zquUZSV%`#Xuhq*qq~=m&v`8mgKEo?$bt8yN0;UI*usmOKEpUz1wETyrlIU-|QV71Z zR(z1FCT+)#Gl-aaTJ05{O52N#t^J73#Lm#Ge~s%$d%=0Zd4~Zc;~|m^v$PmXINwYe$cDtH$d3Y<#VYWxusP z-kw9>`}?=8j162BH8wmpWFjqHpR}TW4l!tMEl)KDi2uW`cDB=T6F<~+FtHj#kiHUrcMpr8Av zO2Zm44L9X-Yt7#%^#&@|RVAbqx5ME>GG}wv7aOWB!z})l4n? zeE8DY$<%w>i`-#gei&T^pH#=#x6hXFs6<||#6(=ts&vk3;W#CJ33m^Bp`QYbp zvp~y{wnkvWZ-Yc&c|d#Ypesx4C(O|hSc`lN_djqVk+*o(wG9X_7-~aJ)vfjsMvfj?vJp^%ubmNI- z#D7fXSz;@x^2aaRO5IyJ<0E>nE*zG=^{-x(X-@Bft7z^i+G9n|FU1kAB{Iw~B( z)&19M+3M^yI;q0jJL`@fTHIi3v+w)?^c}m=u(!JrOY)m?>1xoGsk9`YemEF!pytMz zz}wH)LCYp!n#%IAe*Z|)hZpIrE}L^z*O}cv?a7!?lEcGOnGu=IYCT(d4?IJx8U1$e zqU>Jm^-Jh4&rDWtyChdgs~}|a5G7uRnXA`*_d4RvOhc>(8&tN8I1X;{oMFly98}L8 zA8ubN?`tENRQJHfH}`$z!C*Nvoh&KKloXDz8h69MMIU*~;0p@NYrZQ5{1L3Z3M}P? z{JRnrCZ#2Ol7lX?g&F&^??oN>(7pqRZubi(7`qc zBT-~!5&Zh8YH(W!dD(BoS?p_&xJSO7^E zuDPVvQ@5}Jbi>{EhLe%4R(v`(vkEt?72$;YbD;QrcKAzuuF|3+aE(U93A!P&aJQH{ z0=aUO;lahQ3Y;K_YP*+1g@lq7!-2m@VM_(SM34?4NybEh<`jFH@^45PUN#hQEkBWq zS_|Ak&oaO5d4kB@n;?PFN{JEAtGD5<*0tWW?{1sYi3*2N?hXIU(;Q@EcZf=zZ5Xyx zRZ+uz|90|sHo3XM|H^hO?)Km*_AAoi0Dc`Wx>+$#N$b=Mo`^jjb6+WT*&aW*Hn{Lk zeczirqPvad3@m_h=H=ztyz-aMnezMU-5=)>!DPa1ryWO)hbpS%&-7mQu|m{cvIg!# zlxL#F`G=+-TLfJ7MT5E_rd6>Y?mV>Y&$j0z=APG-ADr1TLny=SDJjoPN8Snb1ksS- zR1RP5pwqCFSsJ(@4}H06bA_04h(SLR=CL+5bK7gTv6-%0Ok!u6)ejMA?_2nU-sowc zSj3^P4jd|MfuZ$-y^M@Tcq;gh55*#w^A&g;q^ZID`Z2$Sj>wj@uepfMXfn?3o_3A&%Tz^#YA~)L@zLM1VE_Da` zHUUQwVJRH>-1YFupe7%}+uZC(R@&wFB0Iq0st&P%UrMUE(H1#mlg#YwI%tG} z)~Fp|VNrvJYf+o%4^kN5H{QR2G@~y>+Mj?H)_(y7apWHfiUTcQRuR8Dx7~f5NE3Mr zu-?gwGSlf=DKX}5b%@7hnWcmya(owOIlqbV!GTX#{wnMqwjhAh#83y=oW|c4G$SfBpn%j7X@_W^k*=b(vBg zKI7Em0c7115+cmp#~MF36hf$^q0s_OwuQel2+trf_c_A~p_W?A!fw%Q>8zr}gwyX4 zMV{Q_ColY@q=Z_udcqa=uVpw_6^V&H_Wyds8~#aQ57BIaKZXyCJmvH#_T}|x2EH|WtkQrL=+B(*-E%JELW~Vg z8pGPEJ8%q>Dl`%&&z{0XlB?CiI9JXdgOx|NPg;(T@W9`coqvV4_2Cn`_BA@!eTatT(L2ucO@P!tO-C|$Zw!70OtVifp6n0t=~A@q1`NP zM(EMZAO=#<*l^^BiJ$KL|hJkL(Ggxh6wrSn|h zRo-29N6$fx9>8#j6#@2%P^q~Uf!x7lulRvaX`BG68ZAx9dHgzNH05L7D}wgv!w(aU z5616Xwi(k4UpmaTk#O=W-(uaBJJcS#J4F@ag6|Kz5GWR8D^>fHGW}@)4R0LKVgdidpU1Bx=(ykO=WJL;Sp|59eKb$q_V>m*y(?`zJ~)xA`t}ju1tJT zhip8MOSwulAyb!rBCt&?0)FU3zhW@-*X4sk{AZ~PC_T;s%|~;`T^c?BZ1A{nIi{OL zw5z@CJreD>o{yhiWQ+U(5hqn}7nhQ!9X=VY2+G6kE~bX?-h})+b~_dSYk<=xvFq^k zh1&Y0qVgkE|E_kjkeDZJAV3+0-;FQwNRPQvZu&jgvG{YpB> z{h%=dJ(mJBB`;BauOxg;pu3Y%D^|jCmKMI=mYpBKSiZ^ONqX>rSZtU^5}#aC zu$7R$B)okMFi(;$TwYcXn(c+#v&0CWH4AKLzFBdt0HsCT22|K1`GF!4;t2jaZ+XGu zg@ZP)d#Zfi&*vaaQSqt5pGn|G$2Cx<2v-9YvY;=^CfM=g5EJ{2LMDU!W*jq~7 zxWY_pr4o+{Nw&P25l)S3zI=(<#q803ZoO>Ej=OYv^{QC>L48=@dh*Lh{md`Z(43pr zie&bizsILDRugp5rE0NWm#2~NT<}Ab)Vyh+TdTj$vuWi*e8{L#Sm%?6)E|T+^d~qY zCrR_6L{N8mm@c`LqBITffq|;F1$lhPWqT1={lMV-BzYiMl&6MgHj!9Or~cc=^#yhL z%RrnFqOo~X*;|#2l~rXu(`SJFrSK9=)S5k>Ne|}elx^tY;o45j%sqdQ2{rEjw4Ne# zvi;Xjxkj}DTxu?6(A|Chwj!h?@H7vfxPYIqsMg{g6#qI316J1TyFphfD?ifc_T~r@ zFsI0(&Y)a69A~4cmb+0iDHB6O{9`J&{ztlm7^i2#auk@oDS}UcAV<^WnqPTMA`00$ zgQiU-a}Y%ek+InVWh0&!i`vK1F|s1njc7(?3wjgv^vxnbEkpP191tck19BHiCtE-5 zk7)6f4q+pE;{Wol!UtnBjmI1zM)9x-=xy0vB)vAqg0hoh*Pt;t2b4dq758p3Ip3 z$^mPaw7b&5$+RV$A#+ILm*lmwq8^qB>Hvt)`3b#!3o2+0fh?=4(a^(xN}scj(p7|svj@d*hLhdKFX^m`A5(LvBxqt@_e?0KE z4nQmEDbrNq(ThCzbdw;<5ZAL`pA}5yPd2k}(ym*L@Et^R>;Dy3mkgQuq`hu-)U&>M z1A*!XWc9SYGBPqy6$0{PWjqA82chp<0W6*NSp#|7_Z(F~4p0Ma8M=}K+*Dt-XYSMQ zY{pDB_^y?hy^Tc%piRaJI+JUN?2(E&>hk0}CFAAhosunK=XOpzMDglY^{&(S-0tqtz5h+jNwe_B{9iGNrXmP|Q@4MKL@>xc5lFP?6}2eOBpmE4#F?}k`J)$|>#Nd1l?+ED5-Pu!~f(B`B= z6&dj7yVYeTX(r+E&$zWdU-~@7Z*nB%T(o#UkRIWIazd%xS$2@#`ge@Wul^?;1W%0V z`dFAb?uMM({qJ_G|L^U#6UoIDaLmrHo07Wpm+(UpxsjK~o82FmFJ;?{sFH>o1yRv} zI;4bRahR~1`$c~sAV17~KL$}~Xg*B+;uTa*-7NbvdpyD1*jR}^P+jp;p{r&Bfe%t( zU>m!uh^f%@g8~&7AkNo{mTiJ<0=fkJ)b_!b`3dO!pB|2#vDW$7*tZ49>6NayVL_GK zT@#_&t*S)GFO<1TFFQ7~Ug^hqesaNcBj0!pA4^>^!3YjGIOYAJ`xUyF%TL4AURqFj z4U29282Bv>8XllH-UxL&9x>;>I`i%WlhZco`f{4O3lfea<|0T!DYQqwPyfvP5GJWr zvpl>4(9k%9%3nmbb;c`ce`6t59og2u3LofT>LrO z1YT83x~`xBtvI_K?yy{IdG&P9ZMGj6DL-oDh2_oBJZx#7G35FRyjj%UovCD{o=YR& zJ%Wfocyox|cslZwiap>22x<*+y~StDNbm~)wJFm>6?7@AAr2QlY) zY_mWqun{xl7cVR`?JAnD`WI!juCES%Ux*IWNhEY`<3);cWm26iY16sO?(h5OtO{fs zzUM`9T(@%{Go`JOozY2+#NVWR z3H4agwV`WMg?SZuRNyjQhl&MazvZG?;V$Scw@Jx=y3q0ecA?FmC8`4~^9`aX>On9e z^A?6Sp-5IR(XmEJk_VA2K%K+6aH%g(T{qz}nlRpJZ;L=MUlPfyeE(mhNG!R^0mTU@ zKUAkpct49zmtfcE@zN1b! zN2B$g{+IZImG=&Z>fUM`Jo_HIv)l@Oua$mbB&9_YPW;+6B9WRr?tWVlrb**Jx(t`N)ZxfUb5 zg~GJ4&U5YV?zvcya6D`c`0mJB-&>Z?k##P=ydYFX^(fxJ}|BMabP1%LwRtq7&rF?8cNM+>#Xvv-sui?D!8Hm z&OBq~9$2$ADQBR{X*|Av3v)E~9qt2r!)-XmBvog7tP>oENyZWM^&1U0WmHSCj)9c! zTMmG#TpmT_ty7AwvP2Y$#XW}#j2YJS#+^MBoWJh(=?z_zPdFR<*JDonuMEo74^>7g zUyA!|`a?km%SKi*R}TSSeFri!dioknyXC+eOe`D%TGSlGNgx4CPR$YK=RN>QYvHhv zhz{g|)c!H~)PsMBV@XU%m)y3^Mw?AWay}K^&xxw(eURkB^LnwH*gEv(^GLf!f@}{i2_BPl=hQFci&G)h2(Uj4ef^weL(ElU8Ec3+G0#HTpw_VkfNy;< zr4;tT$7xsHUNj9=YGaS*>Gf!=!`DnOqoAG$N5hd5YLdtUu`0Hq7M3fk+B@?{z){v;+nBn#ZU_81oCK3|1X%0fe$-}aY$E2w<)ss3*%xNgRBBfGAa7~6 z|GtLlOZUZ5@o8|RC08$P1z3&><-0n2@p#a)i}MXo<)S>kTm%|e^-~kP%>D(uU!!xg zJGFU-d~piR2LnopJfV?VRze3cgu;_#sY%~Ngzrz}3X=0c3XfS|!?%7-+VyES*bS%$ zII5Q7)|fu;`HejUOXNT6Z>e7TWvsE&%O#zwi_bI_7Sw%`IDYWgHKbnl=2Pnaqkj+i z*3N%zS%6)e%47K_6eRNR+4?sUj z&BO-99yun!dhgzzyRj%Yhx>`q)uZ-}w9hAY3cwzCJjY!g^1O=h)=-Q6yC_O+&|hl( zxArjTXl=tIZS54hY&r0q{=wbnIPZww)6=H_3AYk9;Wp}#;&_I^=?U-~{zdbIx~r+` z0OL7Ornx2hUBG7Q>qX_!h_3-!b|#osLr>-UxYnc13SL*+z?YcAW0wKuTHdkW4;~C2 z)^oxrUMOe64-l#S=;nC-X#3my9|;Jj15{$+Et#(?ir_YK<{*?B6`T?*bn#9ke6aAo z-KaXro*oy~E+dT+45bgNYbes}RA(!#KH~^nbAvwdXdJD8L>v89U!&;0&p|&SYjA8f z7w)Ah1c$1rhpR07t$Z-XcAJLA$bs+YZ^YLE9 z0NAULo44bl@;lsdt^0ojS0qC&qIP<34F1jfFRbv($Ypg?bCL#yJ^}O3{SMR>?mGY7 zCra<_&@x`8Uo8mQp8Pz{&FUj~FF?x!Q!8)`h$5hb>B!C~`kFdU`@2%e5f}eaLPQKe z$%|}9njefat#48~EbbA{0k-g|$bsRdM2{0v+qiEX0+z}_(L4=})nZ7B=Lx^iyst|f zxd767B`iFykfFE9qi|6?vHu_n_z9+@`RC6aC4pr7p36XY23Rv|MLCS@~My?o~1;btj#nHG3(&mGOBHNmBl?3*{toY;4$_oVpLh6Y4Hr zRlUV{JiudEHfXMtvDK zqHvq5PEgrptGo^Qoip0+d)$nh+w9uxN8ApPptf{pFx^4rI9gSe#jw&~=oho%lAwc$ zDdL{H?XO(@<7r4)@*3gB1lC;XQSjxtsZ@2YpiCie4-WVA?5D!{c!8D2+QspW;XfI3 z&Qx3R72f!9GTK;woMZ*{$pZjx(`dkz4DrHya%YwPvvk&_vg8AZuu>dh8&TC_2T}gz z)o=J%c|YJz4yud(`Jxd~4=_D)uhLUl9g2|ieG7cGzyb(ZYybhfw$=St9zaL|`QvyC zjnVvqOT2a0VP8TemVd!l>=s-bf|9b>)6tZ<657ygo^vSjO{ zGDI3+6M`Q>!E3P_FnQKI(W+b)i!~EQ)C698)Wld|+@#K=E!2CkXt+W=ahp+xIOXMB z;K5(_l|Q4{?^|i4QCIa{@|#nnwEI;`@)+B>&XsGdIn>m&v1jSW6;7NjaP6_*0pAXs zV5F%`X5eimd-l~aXP^}7A$xyQQ$25J`l0qaz931&J6ctmcY|g-{j*3PBI+6ScY+G6 z`VVOMAhW(JotaVrLb#5pC`I62MCx$;v7)LlA^oI8oN2v_O$?YG@8hrEGxvLlZ9LfK zD{wX~ZVw7rXu+mef32)vMFvh<+1<6X1imjU#DN<)@1)vMrKyU?3^a28Aq`t=H%VgR zG+}aJ5PdeL+yZ842hn&OOVrn|{erllC0mVhWI`78`>FQti^-XUlU(%Hw_Y@hHdu{c zoQM4X$7r^(&gotN+P^L-`MpE9dQaY^K%6K*TJ8aj2njBc>pp}oo~v(o6Nqf}Skn=Z z>t+Q!4RvoICj~x-59k^Ty*^&}uENFHIu&@*B3XmYaeK6B= zKVIZ!rkaG{RZL=P+WV}vH1%|ntW>7;WNt2D#eOm<0KiRx8I%_sP~0Z)BNwg~P?vO> zD|TnaA!}*~!J1R6qAAp`SBlAXl5raCM5roW%|AkSA-t4aB(hUb0(^0Vk$tx|gNl%j zkF@(9o_zqa$plL)8g&RlhJB*VRaDyr$a>>A_}mw#=D6v5YJDeISqRY7EddXMj1LJb zImVRo3AiS!9qrN|5)4^8DN2yhoFzxZ1np;x2pavWJ4vxa&l!8`=sqEI^?I;rJCm2E zg2&`4oZZ{LSIj-7WI^~;@`1SREWpc_%3Aq&f@2EQu92h~9O3?>u|5$dcQvwIWoIjT zrGyys_peWJ4Y?BJa<#IuvT<`0!LOhoxzxr~e#n@Ns(GqdQ#Q1*ti&P;8H*Hm5zX*O%`CkN+f>6j|HIE6UOm?MB^VlknvAm^)y?@6_N%w^EJvMMaW-0?BI|Umpy_q0cqHLIjr8(%X#$}LBE9JH*wS=wZQ2` zo*SZ6P7YX<7uBRQWJ<;w3d$Y~6iR|e4PCWH@$k#p_+JeTug9kY^st-IE#I@Au*=ODg(W_Pe_rCWM)+%kuVhG~OlyL7XJKI0~4 z8Zx|U>u}1)%Y%z~d-%;Q^thgGt2g8ua<;iONE z6T6x9!u?--`nB~@sL5yhm%M=U)OduMDsbhbtK{AYZ*dT$(E!-4dh zcQaF*s5=3nOx+dxbDh;N(Zv}TxO$zZcj3s_%~p?f`~Bptw_jr9)MHf-1Lq!Oa(0l% zvz+P6+f`&r!8(qlsN=!0Z?FH#IV=@rM>OAVfbmtQtiFlgkV=$ueEe%M`0V~TSZkA*KdGqpi>UT0s zypIH<#1%3u@!)seq_zxzz+mq|i3!{ILonGF;J17}07r8RZqfI(q%*mqvJg^;l|AbAGK||#6@HbbkNe~bAg*#(O$M))}*An zMaJRQ>#UM%1Z*);{qro2ntVHNE-5w?)uPQUsNyLfM;!xFw<7^h4=C@%xoit?XyAfR z28ZB~^z?L)5D#mwlp-RYisLm6xgU6y5^gG1#adMgGx==2eHG6$%ptj4sV*8g=8u*N zkWa{4O(Fl95hv&AEG2QS@l>IKJXb#$U=|=DDU*s}8m&m898>8yvDFBBX4;#5xhRQW zI*76fe3=^^9c#@YfSe3yp9p@)-PYpxffm>|UNMWHZ6=+IEvgg`A2}dcH1Y+Yh`woD zSzPW?to+Wt^gSMKB-lelt%ls89n49*9ChD6ob*iOQ@O2CZHraI9xW(F5bVdmWd!{j zBf`SK>n~esWkO7i8^q|zCT|y|cr2!U_14~`p@rjTkpun9>bg}a{mlVQ;X4;PXjIap zFFk9}pQO>v39|UiIpQ?es7L#H<*FP^1i2C4m#)CS>!~>UC$0N8@L`uSD}t66odZWV z1^*^X&HpFK_^`5Y#6;Wto^s#VTF_QtrFF+M=CE+Uoi^9;=e?Y zo@!(U3Qe)1=&}Y_VG9z369*z)d2{+lj(Cj4EzR_mES_W_xVucB!>gH-=ne%P&EofY z#48K`Z@0-3uDNrGf6C6u$pMeAn^C9ts<<1Tlz$&o41wv%Hv6nG5|sM<&AgkZ0=n}X zFS@2&!`{cDv6sF%F{Yvy1%l(ER5|8Jp!@L)0cSl&h#*A$>gwuJsDygX?s-guZ)%Lu zA+c2YS6uuHv*598&4F#Nw9<9!e<>6^!vVK+1>Rjh7f^SM% zOO$A2UbAsO1Ogh(~1pQUc12A;SAAq zLrM|9Mq6pZ1-hF)N9ZdfFQKZ| zj(D;&Lp0gNb7vTin|rLo2f>?F5?|3FFz~^QR?6sC+v#OlYCqn-be5-ZExA$oA7|>p z|IQC5>GardOX;bp-W4sz;Adwf9}}>B%Y9Lg7zjTSRKV^67{DepVEy>wNG@c*7PaER zo2U-}DJ1Pee`3i&{U(7brYe_9aUY9y!wktTV!8m+iXkD$^nmVEzz;-LV0c(mEZWTW zr4y{X*59Z`qCOwr9uqz0l=GH_*}rnX`<$Bjae?DYr*DT20u!IPtkjW<9lhhCK4Hi$ z3M2?XL&S20cAiZ(e%4X$wLyQz42U4ynz~P4(K7QM_fyu-jA6UaE)zm5n01e z6lD*LhLRxfJZ>6V1A@IRZNkeOLo6*jb;x;4*xCqNfF`J16sovH7W(>nsl%*GnxGzY ziiqCfU(sYqZZ_GW)1zUwaUAc7*_2#rV05S?DZV%P)2d6ykR~*|xa-};hd+=Qm;z}* zgW;OmQOWyBQvlQ6V>vDHyo!20(elF_dp(P~&zDnc@}}FuLWLxKvu7Rd$6IG+y66u( zP1K@(TN8U=>LF^Jn*1Q~chnkmajwx3;t&_zW#XYDZpFsJg7vvM-U*JcoZv*sj!Op~ z`gVqeHo4%-@0MN<%jdn7pYf2ejz%ul;>_-n*LA9&GRf==MzD?i_>Uy}L>6)g5bZ@DdHk!@k{gC%_Wz%LVL|`W|0y+wbuIWjFFr9Y{OaybIas z%$B-qRHZgLg}*Fr^S$ACfG%?l+?=)6wpr z)59Bi8A1F<<|kl{p>Kmns%{CJG-S_Akzb1qa>0P={WdI(TSY6WqSKA4C7cS2heC_| zm0aSQoND3HO8AuZ9<>;{Us4fVEH0_JWZX~;6+9n|ci+QtGmVM+@j9283>?=T2dd@w zqSqliuFcP;rlt+hPglfO3g{K$)p4@W=d`#im2sXD(6y>Gb~N5Z|8C?B=}SZg@7qm$ zqZM{>hQ1`rm(?%!M*TPj+pLW+OER>ohJo$F@S(MVnL!~awT2- zEa6x301vD3b4-74;Ms68AVAIWrvDT73dzmxGP160Hmjhlpy=+5<)dsW+;ZAyZ@hK4 zG#PH?`%5=Vj#IQFgYp!(jN}85MSyYz9Ac=bS3KI-K(@^!xAt33PL5IwR#tpJ;^Tnz zy0bZYIqh{srSA+)VBI#>A5R&bxDTVpV3DBqVrO8_#?IgvgEC&qHUwN1^rmvxqt>Yj zn;7=18uIqvfPRpEzz|rxb`07&#V_pqI-7CzwdRM6%Jo~0cs^e8)xs=9a89DUR2h&~ zo33S{Ph0Cx4#OgP0sybNf1?9dH>;qvkXs6!_TRAf5D&?JT>cpQ{?CivIA~dtAg;N*KNh^EaK5Y{lzsWDL~7ta zhT)L^xz|H$W&@5%NNXi4h}MBOvP1Y0{3A)*Hmc{j??~E#FCa8u^lBr!7y+sr5{)Vq zrR4>l!r=7-&$YJHop_VKGY#ApKBA>b(auiM>e5-gJ9Ct7iPN9*?`~1!8mMU#t&g0i zMGNuJr+-*V!Q_N+;yV1l+5t8{D_o}O)iE7%cv=K_!QtgDJ?_8SW}u<}gtGS>q88x2^TFx6VMgF^B+I10)6^0s6`D z6jBgPxOwAd@@+(^^bkR|>?f(93SsuhyxohL8v^2*NU7%mF;j`kIY+W2kQ*-o=RSzG z($cPvV2kv5-CBwK<=N5ke7!21J8e8S2$3<#%W~l53n^r9=G^&|kro|$=IN(W2m!Hi z4be#BHD8JOkCvD7Tx!m!`dC#GyGlA2wcEdMcbS|RE#Lg^pL`(yAF8dk+TI)czmofM U=I*2-CIA2c07*qoM6N<$g3RZuEC2ui literal 276980 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 z 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 diff --git a/examples/shaders/shaders_normalmap.c b/examples/shaders/shaders_normalmap.c index 3e92567b0..96cbf414a 100644 --- a/examples/shaders/shaders_normalmap.c +++ b/examples/shaders/shaders_normalmap.c @@ -149,7 +149,7 @@ int main(void) int yOffset = 24; DrawText("Use keys [W][A][S][D] to move the light", 10, 30 + yOffset * 1, 20, BLACK); - DrawText("Use kyes [Up][Down] to change specular exponent", 10, 30 + yOffset * 2, 20, BLACK); + DrawText("Use keys [Up][Down] to change specular exponent", 10, 30 + yOffset * 2, 20, BLACK); DrawText(TextFormat("Specular Exponent: %.2f", specularExponent), 10, 30 + yOffset * 3, 20, BLUE); DrawFPS(10, 10); From 32026ca78b644c07275e85c4364e99d981e88582 Mon Sep 17 00:00:00 2001 From: sir-irk Date: Tue, 8 Jul 2025 06:34:25 -0500 Subject: [PATCH 077/242] fix preview image file name --- .../{shader_normalmap.png => shaders_normalmap.png} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/shaders/{shader_normalmap.png => shaders_normalmap.png} (100%) diff --git a/examples/shaders/shader_normalmap.png b/examples/shaders/shaders_normalmap.png similarity index 100% rename from examples/shaders/shader_normalmap.png rename to examples/shaders/shaders_normalmap.png From 8ef51850bf86424180505824f75530e92ed5d9ca Mon Sep 17 00:00:00 2001 From: wwderw <24878362+wwderw@users.noreply.github.com> Date: Tue, 8 Jul 2025 08:51:33 -0500 Subject: [PATCH 078/242] Update raudio.c Allow for the use of raudio stand alone --- src/raudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index 368a8c18a..40db999ea 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -81,7 +81,7 @@ #include "utils.h" // Required for: fopen() Android mapping #endif -#if defined(SUPPORT_MODULE_RAUDIO) +#if defined(SUPPORT_MODULE_RAUDIO) || defined(RAUDIO_STANDALONE) #if defined(_WIN32) // To avoid conflicting windows.h symbols with raylib, some flags are defined From 930890555c46c43cd55a1b5c1ee42d2042182157 Mon Sep 17 00:00:00 2001 From: sleeptightAnsiC <91839286+sleeptightAnsiC@users.noreply.github.com> Date: Thu, 10 Jul 2025 01:37:25 +0200 Subject: [PATCH 079/242] [glfw] update mappings.h using GenerateMappings.cmake I did this with the intention of fixing https://github.com/raysan5/raylib/issues/5027 but it's hard to tell if it will help in that case or not. Anyway, bumping the header with mappings should be done once in a while. I haven't found any Raylib script for doing this, so here's how I did it step-by-step with CMake rule provided by GLFW (I'm on Linux, but this should be similar on any other POSIX system): ```sh $ git clone https://github.com/raysan5/raylib.git $ cd raylib/src/external/glfw/ $ cmake -B ./build -DGLFW_BUILD_EXAMPLES=OFF -DGLFW_BUILD_TESTS=OFF -DGLFW_BUILD_DOCS=OFF -G "Unix Makefiles" $ make -C ./build update_mappings $ rm -fr ./build ``` then I checked, if any mappings were deleted (without updating) with following shell one-liner: ```sh # you must be at 'raylib/src/external/glfw/' when executing this $ for i in $(git diff ./src/mappings.h | sed -n -e 's/^-"\([0-9a-z]*\),.*$/\1/p'); do grep <./src/mappings.h "$i" > /dev/null || echo "$i"; done; ``` which gave me the following IDs: ```txt 0300000066f700000500000000000000 030000000d0f00001600000000007803 03000000efbe0000edfe000000000000 03000000786901006e70000000000000 050000005e040000e002000030110000 03000000790000000600000010010000 030000000d0f00000d00000000010000 030000000d0f0000d800000072056800 03000000c0160000dc27000001010000 030000008916000000fd000024010000 ``` I have checked all those IDs with `git log -p` against https://github.com/mdqinc/SDL_GameControllerDB and it seems that all of them were deleted by upstream (I was afraid that maybe some mappings were added manually by a Raylib contributor and then overwritten by my patch, but they weren't; All good!) --- src/external/glfw/src/mappings.h | 2107 +++++++++++++++++++++--------- 1 file changed, 1503 insertions(+), 604 deletions(-) diff --git a/src/external/glfw/src/mappings.h b/src/external/glfw/src/mappings.h index 7b0f35a26..ccba5924a 100644 --- a/src/external/glfw/src/mappings.h +++ b/src/external/glfw/src/mappings.h @@ -61,53 +61,94 @@ const char* _glfwDefaultMappings[] = { #if defined(_GLFW_WIN32) -"03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows,", -"03000000c82d00002038000000000000,8bitdo,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000951000000000000,8BitDo Dogbone Modkit,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows,", -"03000000c82d000011ab000000000000,8BitDo F30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"03000000300f00000a01000000000000,3 In 1 Conversion Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows,", +"03000000fa190000918d000000000000,3 In 1 Conversion Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows,", +"03000000fa2d00000100000000000000,3dRudder Foot Motion Controller,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows,", +"03000000d0160000040d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows,", +"03000000d0160000050d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows,", +"03000000d0160000060d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows,", +"03000000d0160000070d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows,", +"03000000d0160000600a000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows,", +"03000000c82d00000031000000000000,8BitDo Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c82d00000531000000000000,8BitDo Adapter 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c82d00000951000000000000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightx:a3,righty:a5,start:b11,platform:Windows,", +"03000000008000000210000000000000,8BitDo F30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", +"030000003512000011ab000000000000,8BitDo F30 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000c82d00001028000000000000,8BitDo F30 Arcade Joystick,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c82d000011ab000000000000,8BitDo F30 Arcade Joystick,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"03000000801000000900000000000000,8BitDo F30 Arcade Stick,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", "03000000c82d00001038000000000000,8BitDo F30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00000090000000000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,", -"03000000c82d00005106000000000000,8BitDo M30 Gamepad,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000151000000000000,8BitDo M30 ModKit,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c82d00006a28000000000000,8BitDo GameCube,a:b0,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b9,paddle2:b8,rightshoulder:b10,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b1,y:b4,platform:Windows,", +"03000000c82d00001251000000000000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00001151000000000000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00000150000000000000,8BitDo M30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00000151000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c82d00005106000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,guide:b2,leftshoulder:b8,lefttrigger:b9,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c82d00002090000000000000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00000310000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c82d00000451000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightx:a3,righty:a5,start:b11,platform:Windows,", "03000000c82d00002028000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00008010000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,", -"03000000c82d00000451000000000000,8BitDo N30 Modkit,a:b1,b:b0,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,start:b11,platform:Windows,", +"03000000c82d0000e002000000000000,8BitDo N30,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b6,platform:Windows,", "03000000c82d00000190000000000000,8BitDo N30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00001590000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00006528000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", -"03000000022000000090000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", -"03000000203800000900000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00000290000000000000,8BitDo N64,+rightx:b9,+righty:b3,-rightx:b4,-righty:b8,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,platform:Windows,", +"03000000c82d00003038000000000000,8BitDo N64,+rightx:b9,+righty:b3,-rightx:b4,-righty:b8,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,platform:Windows,", +"03000000c82d00006928000000000000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b11,platform:Windows,", +"03000000c82d00002590000000000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"030000003512000012ab000000000000,8BitDo NES30,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Windows,", +"03000000c82d000012ab000000000000,8BitDo NES30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", +"03000000022000000090000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"03000000203800000900000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00002038000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00000751000000000000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c82d00000851000000000000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", "03000000c82d00000360000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00002867000000000000,8BitDo S30 Modkit,a:b0,b:b1,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b8,lefttrigger:b9,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c82d00000361000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00000660000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00000131000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00000231000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00000331000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00000431000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00002867000000000000,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a2,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b10,x:b3,y:b4,platform:Windows,", "03000000c82d00000130000000000000,8BitDo SF30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000060000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000061000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d000021ab000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", -"03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00003028000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000030000000000000,8BitDo SN30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00000060000000000000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00000061000000000000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", +"03000000102800000900000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d000021ab000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00003028000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", +"030000003512000020ab000000000000,8BitDo SN30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00000030000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00000351000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a2,rightshoulder:b7,rightx:a3,righty:a5,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00001290000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d000020ab000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00004028000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00006228000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000351000000000000,8BitDo SN30 Modkit,a:b1,b:b0,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00000021000000000000,8BitDo SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", "03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000121000000000000,8BitDo SN30 Pro for Android,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000c82d00000260000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000261000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", -"03000000c82d00000031000000000000,8BitDo Wireless Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00000260000000000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00000261000000000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", +"03000000c82d00001230000000000000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c82d00001b30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c82d00001d30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c82d00001530000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c82d00001630000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c82d00001730000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c82d00001130000000000000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c82d00001330000000000000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c82d00000121000000000000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000a00500003232000000000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00003032000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", -"03000000a00500003232000000000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,", -"03000000a30c00002700000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,", -"03000000a30c00002800000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,", -"030000008f0e00001200000000000000,Acme GA-02,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,", -"03000000c01100000355000011010000,ACRUX USB GAME PAD,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000fa190000f0ff000000000000,Acteck AGJ-3200,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"030000006f0e00001413000000000000,Afterglow,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000008f0e00001200000000000000,Acme GA02,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,", +"03000000c01100000355000000000000,Acrux,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000fa190000f0ff000000000000,Acteck AGJ 3200,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000d1180000402c000000000000,ADT1,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a3,rightx:a2,righty:a5,x:b3,y:b4,platform:Windows,", +"030000006f0e00008801000000000000,Afterglow Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006f0e00000263000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006f0e00001101000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", @@ -115,310 +156,746 @@ const char* _glfwDefaultMappings[] = "030000006f0e00001402000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006f0e00001901000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006f0e00001a01000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000d62000001d57000000000000,Airflo PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000006f0e00001301000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000006f0e00001302000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000006f0e00001304000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000006f0e00001413000000000000,Afterglow Xbox Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000006f0e00003901000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000ab1200000103000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000ad1b000000f9000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000100000008200000000000000,Akishop Customs PS360,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000007c1800000006000000000000,Alienware Dual Compatible PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", "03000000491900001904000000000000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows,", "03000000710100001904000000000000,Amazon Luna Controller,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b8,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b4,rightstick:b7,rightx:a3,righty:a4,start:b6,x:b3,y:b2,platform:Windows,", -"03000000ef0500000300000000000000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows,", -"03000000d6200000e557000000000000,Batarang,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"0300000008100000e501000000000000,Anbernic Game Pad,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000020500000913000000000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", +"03000000373500000710000000000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000373500004610000000000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000190e00000110000000000000,Aquaplus Piece,a:b1,b:b0,back:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b2,platform:Windows,", +"03000000830500000160000000000000,Arcade,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b3,x:b4,y:b4,platform:Windows,", +"03000000120c0000100e000000000000,Armor 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000490b00004406000000000000,ASCII Seamic Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,", +"03000000869800002500000000000000,Astro C40 TR PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000a30c00002700000000000000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,", +"03000000a30c00002800000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,", +"03000000050b00000579000000000000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000050b00000679000000000000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,start:b3,platform:Windows,", +"03000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,", +"03000000380800001889000000000000,AtGames Legends Gamer Pro,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b13,lefttrigger:b14,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000008a3500000102000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows,", +"030000008a3500000201000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows,", +"030000008a3500000302000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows,", +"030000008a3500000402000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows,", +"03000000e4150000103f000000000000,Batarang,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000d6200000e557000000000000,Batarang PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows,", "030000006f0e00003201000000000000,Battlefield 4 PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000d62000002a79000000000000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000ad1b000001f9000000000000,BB 070,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000d62000002a79000000000000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000bc2000005250000000000000,Beitong G3,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a3,righty:a4,start:b15,x:b3,y:b4,platform:Windows,", +"030000000d0500000208000000000000,Belkin Nostromo N40,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,", "03000000bc2000006012000000000000,Betop 2126F,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000bc2000000055000000000000,Betop BFM Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000bc2000000055000000000000,Betop BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000790000000700000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", +"03000000808300000300000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", "03000000bc2000006312000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000bc2000006321000000000000,BETOP CONTROLLER,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000bc2000006321000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000bc2000006412000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000c01100000555000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000c01100000655000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000790000000700000000000000,Betop Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", -"03000000808300000300000000000000,Betop Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", +"030000006f0e00006401000000000000,BF One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows,", +"03000000300f00000202000000000000,Bigben,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a5,righty:a2,start:b7,x:b2,y:b3,platform:Windows,", +"030000006b1400000209000000000000,Bigben,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006b1400000055000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "030000006b1400000103000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,", -"03000000120c0000210e000000000000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"0300000066f700000500000000000000,BrutalLegendTest,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", -"03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows,", +"03000000120c0000200e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000120c0000210e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000120c0000f10e000000000000,Brook PS2 Adapter,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000120c0000310c000000000000,Brook Super Converter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,", +"03000000d81d00000b00000000000000,Buffalo BSGP1601 Series,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows,", +"030000005a1c00002400000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows,", +"030000005b1c00002400000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows,", +"030000005b1c00002500000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows,", +"030000006d04000042c2000000000000,ChillStream,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", "03000000e82000006058000000000000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000457500000401000000000000,Cobra,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000005e0400008e02000000000000,Controller (XBOX 360 For Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", -"030000005e040000a102000000000000,Controller (Xbox 360 Wireless Receiver for Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", -"030000005e040000ff02000000000000,Controller (Xbox One For Windows) - Wired,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", -"030000005e040000ea02000000000000,Controller (Xbox One For Windows) - Wireless,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000000b0400003365000000000000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows,", +"030000004c050000c505000000000000,CronusMax Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000d814000007cd000000000000,Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000d8140000cefa000000000000,Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Windows,", -"03000000a306000022f6000000000000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"030000003807000002cb000000000000,Cyborg,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000a306000022f6000000000000,Cyborg V.3 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000f806000000a3000000000000,DA Leader,a:b7,b:b6,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b0,leftstick:b8,lefttrigger:b1,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:b3,rightx:a2,righty:a3,start:b12,x:b4,y:b5,platform:Windows,", +"030000001a1c00000001000000000000,Datel Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "03000000451300000830000000000000,Defender Game Racer X7,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"030000007d0400000840000000000000,Destroyer Tiltpad,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,x:b0,y:b3,platform:Windows,", -"03000000791d00000103000000000000,Dual Box WII,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000bd12000002e0000000000000,Dual USB Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows,", -"030000008f0e00000910000000000000,DualShock 2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows,", -"030000006f0e00003001000000000000,EA SPORTS PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000791d00000103000000000000,Dual Box Wii,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000c0160000e105000000000000,Dual Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"030000004f040000070f000000000000,Dual Power,a:b8,b:b9,back:b4,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,leftshoulder:b13,leftstick:b6,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b12,rightstick:b7,righttrigger:b15,start:b5,x:b10,y:b11,platform:Windows,", +"030000004f04000012b3000000000000,Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,", +"030000004f04000020b3000000000000,Dual Trigger,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,", +"03000000bd12000002e0000000000000,Dual Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows,", +"03000000ff1100003133000000000000,DualForce,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b1,platform:Windows,", +"030000006f0e00003001000000000000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000fc0400000250000000000000,Easy Grip,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,", +"03000000bc2000000091000000000000,EasySMX Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"030000006e0500000a20000000000000,Elecom DUX60 MMO,a:b2,b:b3,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b14,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b15,righttrigger:b13,rightx:a3,righty:a4,start:b20,x:b0,y:b1,platform:Windows,", "03000000b80500000410000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,", "03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,", +"03000095090000010000000000000000,Elecom JC-U609,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b8,x:b3,y:b4,platform:Windows,", +"0300004112000000e500000000000000,Elecom JC-U909Z,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b8,x:b3,y:b4,platform:Windows,", +"03000041120000001050000000000000,Elecom JC-U911,a:b1,b:b2,back:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b0,x:b4,y:b5,platform:Windows,", +"030000006e0500000520000000000000,Elecom P301U PlayStation Controller Adapter,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,", +"03000000411200004450000000000000,Elecom U1012,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,", +"030000006e0500000320000000000000,Elecom U3613M,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,", +"030000006e0500000e20000000000000,Elecom U3912T,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,", +"030000006e0500000f20000000000000,Elecom U4013S,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,", +"030000006e0500001320000000000000,Elecom U4113,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000006e0500001020000000000000,Elecom U4113S,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows,", +"030000006e0500000720000000000000,Elecom W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,", +"030000007d0400000640000000000000,Eliminator AfterShock,a:b1,b:b2,back:b9,dpdown:+a3,dpleft:-a5,dpright:+a5,dpup:-a3,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a4,righty:a2,start:b8,x:b0,y:b3,platform:Windows,", "03000000120c0000f61c000000000000,Elite,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000430b00000300000000000000,EMS Production PS2 Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000062000001801000000000000,EMS TrioLinker Plus II,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Windows,", +"03000000242f000000b7000000000000,ESM 9110,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Windows,", +"03000000101c0000181c000000000000,Essential,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b4,leftx:a1,lefty:a0,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", "030000008f0e00000f31000000000000,EXEQ,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,", -"03000000341a00000108000000000000,EXEQ RF USB Gamepad 8206,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"030000006f0e00008401000000000000,Faceoff Deluxe+ Audio Wired Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000006f0e00008001000000000000,Faceoff Wired Pro Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000852100000201000000000000,FF-GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00008500000000000000,Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00008400000000000000,Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00008700000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,", -"030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows,", -"03000000790000002201000000000000,Game Controller for PC,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000341a00000108000000000000,EXEQ RF Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"030000006f0e00008401000000000000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000006f0e00008101000000000000,Faceoff Deluxe Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000006f0e00008001000000000000,Faceoff Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000021000000090000000000000,FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"0300000011040000c600000000000000,FC801,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,", +"03000000852100000201000000000000,FF GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000ad1b000028f0000000000000,Fightpad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000ad1b00002ef0000000000000,Fightpad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000ad1b000038f0000000000000,Fightpad TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows,", +"03005036852100000000000000000000,Final Fantasy XIV Online Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000f806000001a3000000000000,Firestorm,a:b9,b:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b0,leftstick:b10,lefttrigger:b1,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b12,x:b8,y:b4,platform:Windows,", +"03000000b50700000399000000000000,Firestorm 2,a:b2,b:b4,back:b10,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,righttrigger:b9,start:b11,x:b3,y:b5,platform:Windows,", +"03000000b50700001302000000000000,Firestorm D3,a:b0,b:b2,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,x:b1,y:b3,platform:Windows,", +"03000000b40400001024000000000000,Flydigi Apex,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", +"03000000151900004000000000000000,Flydigi Vader 2,a:b27,b:b26,back:b19,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b23,leftstick:b17,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b22,rightstick:b16,righttrigger:b20,rightx:a3,righty:a4,start:b18,x:b25,y:b24,platform:Windows,", +"03000000b40400001124000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b14,paddle1:b4,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,y:b3,platform:Windows,", +"03000000b40400001224000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"030000008305000000a0000000000000,G08XU,a:b0,b:b1,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b5,x:b2,y:b3,platform:Windows,", "0300000066f700000100000000000000,Game VIB Joystick,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows,", -"03000000260900002625000000000000,Gamecube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows,", -"03000000790000004618000000000000,GameCube Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", -"030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000280400000140000000000000,GamePad Pro USB,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"03000000ac0500003d03000000000000,GameSir,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000ac0500004d04000000000000,GameSir,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000ffff00000000000000000000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"03000000c01100000140000000000000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000009b2800003200000000000000,GC/N64 to USB v3.4,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,", -"030000009b2800006000000000000000,GC/N64 to USB v3.6,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,", +"03000000260900002625000000000000,GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows,", +"03000000341a000005f7000000000000,GameCube Controller,a:b2,b:b3,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b1,y:b0,platform:Windows,", +"03000000430b00000500000000000000,GameCube Controller,a:b0,b:b2,dpdown:b10,dpleft:b8,dpright:b9,dpup:b11,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a3,rightx:a5,righty:a2,start:b7,x:b1,y:b3,platform:Windows,", +"03000000790000004718000000000000,GameCube Controller,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,", +"03000000790000004618000000000000,GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,", +"030000008f0e00000d31000000000000,Gamepad 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000ac0500003d03000000000000,GameSir G3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000ac0500005b05000000000000,GameSir G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000ac0500002d02000000000000,GameSir G4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", +"03000000ac0500004d04000000000000,GameSir G4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000ac0500001a06000000000000,GameSir T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000373500009410000000000000,GameSir Tegenaria Lite,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"030000004c0e00001035000000000000,Gamester,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,", +"030000000d0f00001110000000000000,GameStick Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", +"0300000047530000616d000000000000,GameStop,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"03000000c01100000140000000000000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000b62500000100000000000000,Gametel GT004 01,a:b3,b:b0,dpdown:b10,dpleft:b9,dpright:b8,dpup:b11,leftshoulder:b4,rightshoulder:b5,start:b7,x:b1,y:b2,platform:Windows,", +"030000008f0e00001411000000000000,Gamo2 Divaller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000120c0000a857000000000000,Gator Claw,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000c9110000f055000000000000,GC100XF,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "030000008305000009a0000000000000,Genius,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "030000008305000031b0000000000000,Genius Maxfire Blaze 3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "03000000451300000010000000000000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "030000005c1a00003330000000000000,Genius MaxFire Grandias 12V,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,", -"03000000300f00000b01000000000000,GGE909 Recoil Pad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", -"03000000f0250000c283000000000000,Gioteck,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000300f00000b01000000000000,GGE909 Recoil,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000f0250000c283000000000000,Gioteck PlayStation Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000f025000021c1000000000000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000f0250000c383000000000000,Gioteck VX2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000f0250000c483000000000000,Gioteck VX2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"030000007d0400000540000000000000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"03000000f025000031c1000000000000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000f0250000c383000000000000,Gioteck VX2 PlayStation Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000f0250000c483000000000000,Gioteck VX2 PlayStation Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000d11800000094000000000000,Google Stadia Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b11,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,", +"030000004f04000026b3000000000000,GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"0300000079000000d418000000000000,GPD Win,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000c6240000025b000000000000,GPX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000007d0400000840000000000000,Gravis Destroyer Tilt,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,x:b0,y:b3,platform:Windows,", +"030000007d0400000540000000000000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"03000000280400000140000000000000,Gravis GamePad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a3,dpup:-a4,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000008f0e00000610000000000000,GreenAsia,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a5,righty:a2,start:b11,x:b3,y:b0,platform:Windows,", +"03000000ac0500006b05000000000000,GT2a,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", "03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00004900000000000000,Hatsune Miku Sho Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000001008000001e1000000000000,Havit HV-G60,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows,", -"03000000d81400000862000000000000,HitBox Edition Cthulhu+,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", -"03000000632500002605000000000000,HJD-X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"030000000d0f00004900000000000000,Hatsune Miku Sho PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000001008000001e1000000000000,Havit HV G60,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows,", +"030000000d0f00000c00000000000000,HEXT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000d81400000862000000000000,HitBox Edition Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", +"03000000632500002605000000000000,HJD X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"030000000d0f00000a00000000000000,Hori DOA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000000d0f00008500000000000000,Hori Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00002500000000000000,Hori Fighting Commander 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00002d00000000000000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00005f00000000000000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00005e00000000000000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00004000000000000000,Hori Fighting Stick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00005f00000000000000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00005e00000000000000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"030000000d0f00008400000000000000,Hori Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00006201000000000000,Hori Fighting Commander Octa,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00006401000000000000,Hori Fighting Commander Octa,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,start:b7,x:b2,y:b3,platform:Windows,", +"030000000d0f00005100000000000000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00008600000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000000d0f0000ba00000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000000d0f00008800000000000000,Hori Fighting Stick mini 4 PS3,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,", +"030000000d0f00008700000000000000,Hori Fighting Stick mini 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"030000000d0f00001000000000000000,Hori Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00003200000000000000,Hori Fightstick 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f0000c000000000000000,Hori Fightstick 4,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000000d0f00000d00000000000000,Hori Fightstick EX2,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,", +"030000000d0f00003701000000000000,Hori Fightstick Mini,a:b1,b:b0,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Windows,", +"030000000d0f00004000000000000000,Hori Fightstick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00002100000000000000,Hori Fightstick V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00002700000000000000,Hori Fightstick V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f0000a000000000000000,Hori Grip TAC4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b13,x:b0,y:b3,platform:Windows,", +"030000000d0f0000a500000000000000,Hori Miku Project Diva X HD PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"030000000d0f0000a600000000000000,Hori Miku Project Diva X HD PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"030000000d0f00000101000000000000,Hori Mini Hatsune Miku FT,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00005400000000000000,Hori Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00000900000000000000,Hori Pad 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00004d00000000000000,Hori Pad A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00009200000000000000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00001600000000007803,HORI Real Arcade Pro EX-SE (Xbox 360),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows,", +"030000000d0f00003801000000000000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Windows,", +"030000000d0f00009200000000000000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00002301000000000000,Hori PS4 Controller Light,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", +"030000000d0f00001100000000000000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00002600000000000000,Hori Real Arcade Pro 3P,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00004b00000000000000,Hori Real Arcade Pro 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00006a00000000000000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00006b00000000000000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00008a00000000000000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00008b00000000000000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00006f00000000000000,Hori Real Arcade Pro 4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00007000000000000000,Hori Real Arcade Pro 4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00003d00000000000000,Hori Real Arcade Pro N3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b10,leftstick:b4,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b6,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f0000ae00000000000000,Hori Real Arcade Pro N4,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000000d0f00008c00000000000000,Hori Real Arcade Pro P4,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000000d0f0000aa00000000000000,Hori Real Arcade Pro S,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f0000d800000000000000,Hori Real Arcade Pro S,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Windows,", +"030000000d0f00002200000000000000,Hori Real Arcade Pro V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00005b00000000000000,Hori Real Arcade Pro V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00005c00000000000000,Hori Real Arcade Pro V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f0000af00000000000000,Hori Real Arcade Pro VHS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00001b00000000000000,Hori Real Arcade Pro VX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000ad1b000002f5000000000000,Hori Real Arcade Pro VX,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Windows,", "030000000d0f00009c00000000000000,Hori TAC Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f0000c100000000000000,Horipad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00006e00000000000000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00006600000000000000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f0000c900000000000000,Hori Taiko Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00006400000000000000,Horipad 3TP,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00001300000000000000,Horipad 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00005500000000000000,Horipad 4 FPS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f0000ee00000000000000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"03000000250900000017000000000000,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows,", -"030000008f0e00001330000000000000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Windows,", -"03000000d81d00000f00000000000000,iBUFFALO BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000d81d00001000000000000000,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000830500006020000000000000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Windows,", +"030000000d0f00006e00000000000000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00006600000000000000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"030000000d0f00004200000000000000,Horipad A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000ad1b000001f5000000000000,Horipad EXT2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000000d0f0000ee00000000000000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f0000c100000000000000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f0000f600000000000000,Horipad Nintendo Switch Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"030000000d0f00000202000000000000,Horipad O Nintendo Switch 2 Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,misc2:b14,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,", +"030000000d0f00006700000000000000,Horipad One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000000d0f00009601000000000000,Horipad Steam,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc2:b2,paddle1:b5,paddle2:b15,paddle3:b18,paddle4:b19,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"030000000d0f0000dc00000000000000,Horipad Switch,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000242e00000b20000000000000,Hyperkin Admiral N64 Controller,+rightx:b11,+righty:b13,-rightx:b8,-righty:b12,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,platform:Windows,", +"03000000242e0000ff0b000000000000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Windows,", +"03000000790000004e95000000000000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Windows,", +"03000000242e00006a48000000000000,Hyperkin RetroN Sq,a:b3,b:b7,back:b5,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b0,rightshoulder:b1,start:b4,x:b2,y:b6,platform:Windows,", +"03000000242f00000a20000000000000,Hyperkin Scout,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,", +"03000000242e00000a20000000000000,Hyperkin Scout Premium SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,", +"03000000242e00006a38000000000000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,platform:Windows,", +"03000000f00300008d04000000000000,HyperX Clutch,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:-a2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:+a5,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000d81d00000e00000000000000,iBuffalo AC02 Arcade Joystick,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,rightx:a2,righty:a5,start:b8,x:b4,y:b5,platform:Windows,", +"03000000d81d00000f00000000000000,iBuffalo BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000d81d00001000000000000000,iBuffalo BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"030000005c0a00000285000000000000,iDroidCon,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b6,platform:Windows,", +"03000000696400006964000000000000,iDroidCon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000511d00000230000000000000,iGUGU Gamecore,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b1,leftstick:b4,lefttrigger:b3,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b2,platform:Windows,", "03000000b50700001403000000000000,Impact Black,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", -"030000006f0e00002401000000000000,INJUSTICE FightStick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"03000000ac0500002c02000000000000,IPEGA,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000491900000204000000000000,Ipega PG-9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000491900000304000000000000,Ipega PG-9087 - Bluetooth Gamepad,+righty:+a5,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows,", -"030000006e0500000a20000000000000,JC-DUX60 ELECOM MMO Gamepad,a:b2,b:b3,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b14,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b15,righttrigger:b13,rightx:a3,righty:a4,start:b20,x:b0,y:b1,platform:Windows,", -"030000006e0500000520000000000000,JC-P301U,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,", -"030000006e0500000320000000000000,JC-U3613M (DInput),a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,", -"030000006e0500000720000000000000,JC-W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,", +"030000006f0e00002401000000000000,Injustice Fightstick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"03000000830500005130000000000000,InterAct ActionPad,a:b0,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,", +"03000000ef0500000300000000000000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows,", +"03000000fd0500000230000000000000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a5,start:b11,x:b0,y:b1,platform:Windows,", +"03000000fd0500000030000000000000,Interact GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Windows,", +"03000000fd0500003902000000000000,InterAct Hammerhead,a:b3,b:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b2,lefttrigger:b8,rightshoulder:b7,rightstick:b5,righttrigger:b9,start:b10,x:b0,y:b1,platform:Windows,", +"03000000fd0500002a26000000000000,InterAct Hammerhead FX,a:b3,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b0,y:b1,platform:Windows,", +"03000000fd0500002f26000000000000,InterAct Hammerhead FX,a:b4,b:b5,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b1,y:b2,platform:Windows,", +"03000000fd0500005302000000000000,InterAct ProPad,a:b3,b:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Windows,", +"03000000ac0500002c02000000000000,Ipega Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000491900000204000000000000,Ipega PG9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000491900000304000000000000,Ipega PG9087,+righty:+a5,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows,", "030000007e0500000620000000000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows,", -"030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows,", "030000007e0500000720000000000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,", -"030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,", -"03000000bd12000003c0000010010000,Joypad Alpha Shock,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000bd12000003c0000000000000,JY-P70UR,a:b1,b:b0,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b11,righttrigger:b9,rightx:a3,righty:a2,start:b4,x:b3,y:b2,platform:Windows,", -"03000000242f00002d00000000000000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000242f00008a00000000000000,JYS Wireless Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,", +"03000000250900000017000000000000,Joypad Adapter,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows,", +"03000000bd12000003c0000000000000,Joypad Alpha Shock,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000ff1100004033000000000000,JPD FFB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a2,start:b15,x:b3,y:b0,platform:Windows,", +"03000000242f00002d00000000000000,JYS Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000242f00008a00000000000000,JYS Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,", +"03000000c4100000c082000000000000,KADE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000828200000180000000000000,Keio,a:b4,b:b5,back:b8,leftshoulder:b2,lefttrigger:b3,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b9,x:b0,y:b1,platform:Windows,", "03000000790000000200000000000000,King PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", +"03000000bd12000001e0000000000000,Leadership,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"030000006f0e00000103000000000000,Logic3,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000006f0e00000104000000000000,Logic3,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000008f0e00001300000000000000,Logic3,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "030000006d040000d1ca000000000000,Logitech ChillStream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006d040000d2ca000000000000,Logitech Cordless Precision,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows,", "030000006d04000016c2000000000000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000006d04000018c2000000000000,Logitech F510 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000006d04000019c2000000000000,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000006d0400001ac2000000000000,Logitech Precision Gamepad,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000006d0400001dc2000000000000,Logitech F310,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000006d04000018c2000000000000,Logitech F510,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000006d0400001ec2000000000000,Logitech F510,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000006d04000019c2000000000000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000006d0400001fc2000000000000,Logitech F710,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000006d0400001ac2000000000000,Logitech Precision,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000006d04000009c2000000000000,Logitech WingMan,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,", +"030000006d0400000bc2000000000000,Logitech WingMan Action Pad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b8,lefttrigger:a5~,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:a2~,start:b8,x:b3,y:b4,platform:Windows,", "030000006d0400000ac2000000000000,Logitech WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,platform:Windows,", -"03000000380700006652000000000000,Mad Catz C.T.R.L.R,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700005032000000000000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700005082000000000000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700008433000000000000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700008483000000000000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700008134000000000000,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700008184000000000000,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700006252000000000000,Mad Catz Micro C.T.R.L.R,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700005645000000000000,Lynx,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000222200006000000000000000,Macally,a:b1,b:b2,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b33,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700003888000000000000,Mad Catz Arcade Fightstick TE S Plus PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008532000000000000,Mad Catz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700006352000000000000,Mad Catz CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700006652000000000000,Mad Catz CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700005032000000000000,Mad Catz Fightpad Pro PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700005082000000000000,Mad Catz Fightpad Pro PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000380700008031000000000000,Mad Catz FightStick Alpha PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000003807000038b7000000000000,Mad Catz Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows,", +"03000000380700008433000000000000,Mad Catz Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008483000000000000,Mad Catz Fightstick TE S PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000380700008134000000000000,Mad Catz Fightstick TE2 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008184000000000000,Mad Catz Fightstick TE2 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000380700006252000000000000,Mad Catz Micro CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008232000000000000,Mad Catz PlayStation Brawlpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008731000000000000,Mad Catz PlayStation Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000003807000056a8000000000000,Mad Catz PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700001888000000000000,Mad Catz SFIV Fightstick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"03000000380700008081000000000000,Mad Catz SFV Arcade Fightstick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000380700001847000000000000,Mad Catz Street Fighter 4 Xbox 360 FightStick,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows,", "03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700008532000000000000,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700003888000000000000,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000380700001888000000000000,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", "030000002a0600001024000000000000,Matricom,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,", "030000009f000000adbb000000000000,MaxJoypad Virtual Controller,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", "03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows,", +"030000008f0e00001330000000000000,Mayflash Controller Adapter,a:b1,b:b2,back:b8,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3~,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", +"03000000242f00003700000000000000,Mayflash F101,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"03000000790000003018000000000000,Mayflash F300 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"03000000242f00003900000000000000,Mayflash F300 Elite Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", -"03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", +"03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", "03000000242f00007300000000000000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,", "0300000079000000d218000000000000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000d620000010a7000000000000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000008f0e00001030000000000000,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,rightshoulder:b2,righttrigger:b7,start:b9,x:b3,y:b4,platform:Windows,", -"0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,", -"03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000790000002418000000000000,Mega Drive,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b2,start:b9,x:b3,y:b4,platform:Windows,", -"03000000380700006382000000000000,MLG GamePad PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000c62400002a89000000000000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000c62400002b89000000000000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000c62400001a89000000000000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000c62400001b89000000000000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000efbe0000edfe000000000000,Monect Virtual Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", +"03000000242f0000f500000000000000,Mayflash N64 Adapter,a:b2,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,platform:Windows,", +"03000000242f0000f400000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,platform:Windows,", +"03000000790000007918000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,righttrigger:b7,rightx:a3,righty:a2,start:b8,platform:Windows,", +"030000008f0e00001030000000000000,Mayflash Saturn Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,", +"0300000025090000e803000000000000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,", +"03000000790000000318000000000000,Mayflash Wii DolphinBar,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,", +"03000000790000000018000000000000,Mayflash Wii U Pro Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000790000002418000000000000,Mega Drive Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b2,start:b9,x:b3,y:b4,platform:Windows,", +"0300000079000000ae18000000000000,Mega Drive Controller,a:b0,b:b1,back:b7,dpdown:b14,dpleft:b15,dpright:b13,dpup:b2,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,", +"03000000c0160000990a000000000000,Mega Drive Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,righttrigger:b2,start:b3,platform:Windows,", +"030000005e0400002800000000000000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Windows,", +"030000005e0400000300000000000000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,", +"030000005e0400000700000000000000,Microsoft SideWinder,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,", +"030000005e0400000e00000000000000,Microsoft SideWinder Freestyle Pro,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b8,x:b3,y:b4,platform:Windows,", +"030000005e0400002700000000000000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Windows,", +"03000000280d00000202000000000000,Miller Lite Cantroller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b5,x:b2,y:b3,platform:Windows,", +"03000000ad1b000023f0000000000000,MLG,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a6,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,", +"03000000ad1b00003ef0000000000000,MLG Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows,", +"03000000380700006382000000000000,MLG PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000004523000015e0000000000000,Mobapad Chitu HD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"03000000491900000904000000000000,Mobapad Chitu HD,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000ffff00000000000000000000,Mocute M053,a:b3,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b11,leftstick:b7,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b6,righttrigger:b4,rightx:a3,righty:a4,start:b8,x:b1,y:b0,platform:Windows,", +"03000000d6200000e589000000000000,Moga 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows,", +"03000000d62000007162000000000000,Moga Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows,", +"03000000d6200000ad0d000000000000,Moga Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000c62400002a89000000000000,Moga XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c62400002b89000000000000,Moga XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c62400001a89000000000000,Moga XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c62400001b89000000000000,Moga XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000250900006688000000000000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", -"030000006b140000010c000000000000,NACON GC-400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"03000000921200004b46000000000000,NES 2-port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows,", -"03000000790000004518000000000000,NEXILUX GAMECUBE Controller Adapter,platform:Windows,a:b1,b:b0,x:b2,y:b3,start:b9,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,", +"03000000091200004488000000000000,MUSIA PlayStation 2 Input Display,a:b0,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:b11,rightx:a2,righty:a3,start:b5,x:b1,y:b3,platform:Windows,", +"03000000f70600000100000000000000,N64 Adaptoid,+rightx:b2,+righty:b1,-rightx:b4,-righty:b5,a:b0,b:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,platform:Windows,", +"030000006b140000010c000000000000,Nacon GC 400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"030000006b1400001106000000000000,Nacon Revolution 3 PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"0300000085320000170d000000000000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"0300000085320000190d000000000000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"030000006b140000100d000000000000,Nacon Revolution Infinity PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"030000006b140000080d000000000000,Nacon Revolution Unlimited Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000bd12000001c0000000000000,Nebular,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000eb0300000000000000000000,NeGcon Adapter,a:a2,b:b13,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,lefttrigger:a4,leftx:a1,righttrigger:b11,start:b3,x:a3,y:b12,platform:Windows,", +"0300000038070000efbe000000000000,NEO SE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"0300000092120000474e000000000000,NeoGeo X Arcade Stick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b3,y:b2,platform:Windows,", +"03000000921200004b46000000000000,NES 2 port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows,", +"03000000000f00000100000000000000,NES Controller,a:b1,b:b0,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Windows,", +"03000000921200004346000000000000,NES Controller,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Windows,", +"03000000790000004518000000000000,NEXILUX GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,", "030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Windows,", +"03000000050b00000045000000000000,Nexus,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Windows,", "03000000152000000182000000000000,NGDS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", -"03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,", -"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"030000007e0500006920000000000000,Nintendo Switch 2 Pro Controller,a:b0,b:b1,back:b14,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b12,leftstick:b15,lefttrigger:b13,leftx:a0,lefty:a1~,misc1:b17,misc2:b20,paddle1:b18,paddle2:b19,rightshoulder:b4,rightstick:b7,righttrigger:b5,rightx:a2,righty:a3~,start:b6,x:b2,y:b3,platform:Windows,", +"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "030000000d0500000308000000000000,Nostromo N45,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Windows,", -"03000000550900001472000000000000,NVIDIA Controller v01.04,a:b11,b:b10,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b5,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b4,righttrigger:a5,rightx:a3,righty:a6,start:b3,x:b9,y:b8,platform:Windows,", -"030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,rightshoulder:b5,rightstick:a2,righttrigger:b7,start:b9,x:b2,y:b3,platform:Windows,", -"03000000d620000013a7000000000000,NSW wired controller,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,", -"03000000782300000a10000000000000,Onlive Wireless Controller,a:b15,b:b14,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b11,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b13,y:b12,platform:Windows,", +"030000007e0500007320000000000000,NSO GameCube Controller,a:b1,b:b3,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b13,lefttrigger:b12,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b4,rightx:a2,righty:a3~,start:b6,x:b0,y:b2,platform:Windows,", +"030000007e0500001920000000000000,NSO N64 Controller,+rightx:b8,+righty:b2,-rightx:b3,-righty:b7,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Windows,", +"030000007e0500001720000000000000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b15,start:b9,x:b2,y:b3,platform:Windows,", +"03000000550900001472000000000000,NVIDIA Controller,a:b11,b:b10,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b5,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b4,righttrigger:a5,rightx:a3,righty:a6,start:b3,x:b9,y:b8,platform:Windows,", +"03000000550900001072000000000000,NVIDIA Shield,a:b9,b:b8,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b3,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b2,righttrigger:a4,rightx:a2,righty:a5,start:b0,x:b7,y:b6,platform:Windows,", +"030000005509000000b4000000000000,NVIDIA Virtual,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000120c00000288000000000000,Nyko Air Flo Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,", +"030000004b120000014d000000000000,NYKO Airflo EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,", +"03000000d62000001d57000000000000,Nyko Airflo PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000791d00000900000000000000,Nyko Playpad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", +"03000000782300000a10000000000000,Onlive Controller,a:b15,b:b14,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b11,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b13,y:b12,platform:Windows,", +"030000000d0f00000401000000000000,Onyx,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000008916000001fd000000000000,Onza CE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a3,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000008916000000fd000000000000,Onza TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", "03000000d62000006d57000000000000,OPP PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006b14000001a1000000000000,Orange Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,", -"03000000362800000100000000000000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows,", -"03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", -"030000006f0e00000901000000000000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", -"030000004c050000da0c000000000000,PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,", -"030000004c0500003713000000000000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"0300000009120000072f000000000000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:-a2,leftx:a0,lefty:a1,righttrigger:-a5,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000362800000100000000000000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows,", +"03000000120c0000f60e000000000000,P4 Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", +"03000000790000002201000000000000,PC Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"030000006f0e00008501000000000000,PDP Fightpad Pro GameCube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"030000006f0e00000901000000000000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000006f0e00008901000000000000,PDP Realmz Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000008f0e00004100000000000000,PlaySega,a:b1,b:b0,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b4,y:b3,platform:Windows,", +"03000000d620000011a7000000000000,PowerA Core Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000dd62000015a7000000000000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000d620000012a7000000000000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000dd62000016a7000000000000,PowerA Fusion Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000d620000013a7000000000000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000d62000003340000000000000,PowerA OPS Pro Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000d62000002640000000000000,PowerA OPS Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"0300000062060000d570000000000000,PowerA PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000d620000014a7000000000000,PowerA Spectra Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000006d04000084ca000000000000,Precision,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,", "03000000d62000009557000000000000,Pro Elite PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000c62400001a53000000000000,Pro Ex Mini,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", "03000000d62000009f31000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000d6200000c757000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000632500002306000000000000,PS Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,", -"03000000e30500009605000000000000,PS to USB convert cable,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", +"03000000120c0000110e000000000000,Pro5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "03000000100800000100000000000000,PS1 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", "030000008f0e00007530000000000000,PS1 Controller,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000100800000300000000000000,PS2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000250900000088000000000000,PS2 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", +"03000000250900006888000000000000,PS2 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b6,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", "03000000250900008888000000000000,PS2 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", -"03000000666600006706000000000000,PS2 Controller,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Windows,", "030000006b1400000303000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "030000009d0d00001330000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"03000000151a00006222000000000000,PS2 Dual Plus Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", +"03000000120a00000100000000000000,PS3 Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", +"03000000120c00001307000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000120c00001cf1000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000120c0000f90e000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000250900000118000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", +"03000000250900000218000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", "03000000250900000500000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows,", "030000004c0500006802000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b10,lefttrigger:a3~,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:a4~,rightx:a2,righty:a5,start:b8,x:b3,y:b0,platform:Windows,", +"030000004f1f00000800000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", "03000000632500007505000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000888800000803000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,", +"03000000888800000803000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b0,platform:Windows,", +"03000000888800000804000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows,", +"030000008f0e00000300000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows,", "030000008f0e00001431000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000003807000056a8000000000000,PS3 RF pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000100000008200000000000000,PS360+ v1.66,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000004c050000e60c000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"03000000ff000000cb01000000000000,PSP,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,", -"03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows,", -"03000000300f00001611000000000000,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,", -"03000000222c00000020000000000000,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows,", -"03000000300f00001210000000000000,QanBa Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,", -"03000000341a00000104000000000000,QanBa Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows,", -"03000000222c00000223000000000000,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000222c00000023000000000000,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000ba2200002010000000000000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b2,platform:Windows,", +"03000000120c00000807000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000120c0000111e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000120c0000121e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000120c0000130e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000120c0000150e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000120c0000180e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000120c0000181e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000120c0000191e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000120c00001e0e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000120c0000a957000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000120c0000aa57000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000120c0000f21c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000120c0000f31c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000120c0000f41c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000120c0000f51c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000120c0000f70e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000120e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000160e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"030000001a1e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"030000004c0500005f0e000000000000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"030000004c050000e60c000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"030000004c050000f20d000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000830500005020000000000000,PSX,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b2,y:b3,platform:Windows,", +"03000000300f00000111000000000000,Qanba 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000300f00000211000000000000,Qanba 2P,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"03000000300f00000011000000000000,Qanba Arcade Stick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows,", +"03000000300f00001611000000000000,Qanba Arcade Stick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,", +"03000000222c00000025000000000000,Qanba Dragon Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000222c00000020000000000000,Qanba Drone Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000300f00001211000000000000,Qanba Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000300f00001210000000000000,Qanba Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,", +"03000000341a00000104000000000000,Qanba Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows,", +"03000000222c00000223000000000000,Qanba Obsidian Arcade Stick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000222c00000023000000000000,Qanba Obsidian Arcade Stick PS4,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"030000008a2400006682000000000000,R1 Mobile Controller,a:b3,b:b1,back:b7,leftx:a0,lefty:a1,start:b6,x:b4,y:b0,platform:Windows,", +"03000000086700006626000000000000,RadioShack,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows,", +"03000000ff1100004733000000000000,Ramox FPS Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows,", +"030000009b2800002300000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows,", +"030000009b2800006900000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows,", +"030000009b2800000800000000000000,Raphnet Dreamcast Adapter,a:b2,b:b1,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,lefttrigger:a2,leftx:a0,righttrigger:a3,righty:a1,start:b3,x:b10,y:b9,platform:Windows,", +"030000009b280000d000000000000000,Raphnet Dreamcast Adapter,a:b1,b:b0,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,lefttrigger:+a5,leftx:a0,lefty:a1,righttrigger:+a2,start:b3,x:b5,y:b4,platform:Windows,", +"030000009b2800006200000000000000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,", +"030000009b2800003200000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,", +"030000009b2800006000000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,", +"030000009b2800001800000000000000,Raphnet Jaguar Adapter,a:b2,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b10,start:b3,x:b11,y:b12,platform:Windows,", +"030000009b2800003c00000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows,", +"030000009b2800006100000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows,", +"030000009b2800006300000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows,", +"030000009b2800006400000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows,", +"030000009b2800000200000000000000,Raphnet NES Adapter,a:b7,b:b6,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b4,platform:Windows,", +"030000009b2800004400000000000000,Raphnet PS1 and PS2 Adapter,a:b1,b:b2,back:b5,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b9,rightx:a3,righty:a4,start:b4,x:b0,y:b3,platform:Windows,", +"030000009b2800004300000000000000,Raphnet Saturn,a:b0,b:b1,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,", +"030000009b2800000500000000000000,Raphnet Saturn Adapter 2.0,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,", +"030000009b2800000300000000000000,Raphnet SNES Adapter,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,", +"030000009b2800002600000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,", +"030000009b2800002e00000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,", +"030000009b2800002f00000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,", +"030000009b2800005600000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,", +"030000009b2800005700000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,", +"030000009b2800001e00000000000000,Raphnet Vectrex Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a1,lefty:a2,x:b2,y:b3,platform:Windows,", +"030000009b2800002b00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,", +"030000009b2800002c00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,", +"030000009b2800008000000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,", +"03000000790000008f18000000000000,Rapoo Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows,", +"0300000032150000a602000000000000,Razer Huntsman V3 Pro,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b12,dpright:b13,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", "03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", -"03000000321500000204000000000000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000321500000104000000000000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000f8270000bf0b000000000000,Razer Kishi,a:b6,b:b7,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b18,leftshoulder:b12,leftstick:b19,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b13,rightstick:b20,righttrigger:b15,rightx:a3,righty:a4,start:b17,x:b9,y:b10,platform:Windows,", +"03000000321500000204000000000000,Razer Panthera PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000321500000104000000000000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000321500000010000000000000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "03000000321500000507000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000321500000707000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000321500000011000000000000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000321500000710000000000000,Razer Raiju TE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000321500000a10000000000000,Razer Raiju TE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000321500000410000000000000,Razer Raiju UE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000321500000910000000000000,Razer Raiju UE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000321500000011000000000000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", "03000000321500000009000000000000,Razer Serval,+lefty:+a2,-lefty:-a1,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,leftx:a0,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", -"030000000d0f00001100000000000000,REAL ARCADE PRO.3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00006a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00006b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00008a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00008b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00007000000000000000,REAL ARCADE PRO.4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00002200000000000000,REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00005b00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00005c00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000790000001100000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,", -"03000000bd12000013d0000000000000,Retrolink USB SEGA Saturn Classic,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,lefttrigger:b6,rightshoulder:b2,righttrigger:b7,start:b8,x:b3,y:b4,platform:Windows,", -"0300000000f000000300000000000000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,", -"0300000000f00000f100000000000000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,", +"03000000921200004547000000000000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b6,x:b3,y:b4,platform:Windows,", +"03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000830500006020000000000000,Retro Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b8,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows,", +"03000000632500007805000000000000,Retro Fighters Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"0300000003040000c197000000000000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,", +"03000000bd12000013d0000000000000,Retrolink Sega Saturn Classic Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,lefttrigger:b6,rightshoulder:b2,righttrigger:b7,start:b8,x:b3,y:b4,platform:Windows,", +"03000000bd12000015d0000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,", +"03000000341200000400000000000000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,platform:Windows,", +"0300000000f000000300000000000000,RetroUSB RetroPad,a:b1,b:b5,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,", +"0300000000f00000f100000000000000,RetroUSB Super RetroPort,a:b1,b:b5,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,", +"03000000830500000960000000000000,Revenger,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b3,x:b4,y:b5,platform:Windows,", "030000006b140000010d000000000000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000006b140000020d000000000000,Revolution Pro Controller 2(1/2),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000006b140000020d000000000000,Revolution Pro Controller 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000006b140000130d000000000000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000006f0e00001f01000000000000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000006f0e00004601000000000000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000c6240000fefa000000000000,Rock Candy Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000006f0e00008701000000000000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006f0e00001e01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006f0e00002801000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006f0e00002f01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000004f04000003d0000000000000,run'n'drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000830500007030000000000000,Rockfire Space Ranger,a:b0,b:b1,back:b5,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b9,righttrigger:b8,start:b2,x:b3,y:b4,platform:Windows,", +"03000000050b0000e318000000000000,ROG Chakram,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows,", +"03000000050b0000e518000000000000,ROG Chakram,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows,", +"03000000050b00005819000000000000,ROG Chakram Core,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows,", +"03000000050b0000181a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows,", +"03000000050b00001a1a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows,", +"03000000050b00001c1a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows,", +"030000004f04000001d0000000000000,Rumble Force,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,", +"030000000d0f0000ad00000000000000,RX Gamepad,a:b0,b:b4,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,rightshoulder:b6,start:b9,x:b2,y:b1,platform:Windows,", +"030000008916000000fe000000000000,Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000c6240000045d000000000000,Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000006f0e00001311000000000000,Saffun Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b0,platform:Windows,", "03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", -"03000000a306000023f6000000000000,Saitek Cyborg V.1 Game pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", -"03000000300f00001201000000000000,Saitek Dual Analog Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"03000000a306000023f6000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000300f00001201000000000000,Saitek Dual Analog,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", "03000000a30600000701000000000000,Saitek P220,a:b2,b:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,x:b0,y:b1,platform:Windows,", -"03000000a30600000cff000000000000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b0,y:b1,platform:Windows,", +"03000000a30600000cff000000000000,Saitek P2500 Force Rumble,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b0,y:b1,platform:Windows,", +"03000000a30600000d5f000000000000,Saitek P2600,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Windows,", +"03000000a30600000dff000000000000,Saitek P2600,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b8,x:b0,y:b3,platform:Windows,", "03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", -"03000000300f00001001000000000000,Saitek P480 Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"03000000a306000018f5000000000000,Saitek P3200,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000300f00001001000000000000,Saitek P480 Rumble,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"03000000a30600000901000000000000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b8,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b5,rightx:a3,righty:a2,x:b0,y:b1,platform:Windows,", "03000000a30600000b04000000000000,Saitek P990,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", -"03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Windows,", -"03000000a30600002106000000000000,Saitek PS1000,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", -"03000000a306000020f6000000000000,Saitek PS2700,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", -"03000000300f00001101000000000000,Saitek Rumble Pad,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", -"03000000730700000401000000000000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows,", -"0300000000050000289b000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,", -"030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,", -"03000000a30c00002500000000000000,Sega Genesis Mini 3B controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Windows,", -"03000000a30c00002400000000000000,Sega Mega Drive Mini 6B controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,", -"03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows,", -"03000000341a00000908000000000000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"030000008f0e00000800000000000000,SpeedLink Strike FX,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000a30600002106000000000000,Saitek PS1000 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000a306000020f6000000000000,Saitek PS2700 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000300f00001101000000000000,Saitek Rumble,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"03000000e804000000a0000000000000,Samsung EIGP20,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000c01100000252000000000000,Sanwa Easy Grip,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,", +"03000000c01100004350000000000000,Sanwa Micro Grip P3,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,x:b3,y:b2,platform:Windows,", +"03000000411200004550000000000000,Sanwa Micro Grip Pro,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a1,righty:a2,start:b9,x:b1,y:b3,platform:Windows,", +"03000000c01100004150000000000000,Sanwa Micro Grip Pro,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,", +"03000000c01100004450000000000000,Sanwa Online Grip,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b11,righttrigger:b9,rightx:a3,righty:a2,start:b14,x:b3,y:b4,platform:Windows,", +"03000000730700000401000000000000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Windows,", +"03000000830500006120000000000000,Sanwa Smart Grip II,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,x:b1,y:b3,platform:Windows,", +"03000000c01100000051000000000000,Satechi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", +"030000004f04000028b3000000000000,Score A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000952e00002577000000000000,Scuf PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000a30c00002500000000000000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Windows,", +"03000000a30c00002400000000000000,Sega Mega Drive Mini 6B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,", +"03000000d804000086e6000000000000,Sega Multi Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a2,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,", +"0300000000050000289b000000000000,Sega Saturn Adapter,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,", +"0300000000f000000800000000000000,Sega Saturn Controller,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,righttrigger:b3,start:b0,x:b5,y:b6,platform:Windows,", +"03000000730700000601000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,", +"03000000b40400000a01000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,", +"030000003b07000004a1000000000000,SFX,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Windows,", +"03000000f82100001900000000000000,Shogun Bros Chameleon X1,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", +"03000000120c00001c1e000000000000,SnakeByte 4S PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"03000000140300000918000000000000,SNES Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,", +"0300000081170000960a000000000000,SNES Controller,a:b4,b:b0,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b5,y:b1,platform:Windows,", +"03000000811700009d0a000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,", +"030000008b2800000300000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,", +"03000000921200004653000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,", +"030000008f0e00000910000000000000,Sony DualShock 2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows,", +"03000000317300000100000000000000,Sony DualShock 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", +"03000000666600006706000000000000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Windows,", +"03000000e30500009605000000000000,Sony PlayStation Adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", +"03000000fe1400002a23000000000000,Sony PlayStation Adapter,a:b0,b:b1,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,x:b2,y:b3,platform:Windows,", +"030000004c050000da0c000000000000,Sony PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,", +"03000000632500002306000000000000,Sony PlayStation Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,", +"03000000f0250000c183000000000000,Sony PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000d9040000160f000000000000,Sony PlayStation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000ff000000cb01000000000000,Sony PlayStation Portable,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,", +"030000004c0500003713000000000000,Sony PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000341a00000208000000000000,Speedlink 6555,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows,", +"03000000341a00000908000000000000,Speedlink 6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"03000000380700001722000000000000,Speedlink Competition Pro,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,x:b2,y:b3,platform:Windows,", +"030000008f0e00000800000000000000,Speedlink Strike FX,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000c01100000591000000000000,Speedlink Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000d11800000094000000000000,Stadia Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b11,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,", +"03000000de280000fc11000000000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000de280000ff11000000000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000120c0000160e000000000000,Steel Play Metaltech PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", "03000000110100001914000000000000,SteelSeries,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000381000001214000000000000,SteelSeries Free,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,", "03000000110100003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000381000003014000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000381000003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", "03000000381000001814000000000000,SteelSeries Stratus XL,a:b0,b:b1,back:b18,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b19,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b2,y:b3,platform:Windows,", -"03000000790000001c18000000000000,STK-7024X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000ff1100003133000000000000,SVEN X-PAD,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a4,start:b5,x:b0,y:b1,platform:Windows,", -"03000000d620000011a7000000000000,Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000457500002211000000000000,SZMY-POWER PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000004f04000007d0000000000000,T Mini Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"030000004f0400000ab1000000000000,T.16000M,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b10,x:b2,y:b3,platform:Windows,", +"03000000380700003847000000000000,Street Fighter Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,start:b7,x:b2,y:b3,platform:Windows,", +"030000001f08000001e4000000000000,Super Famicom Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,", +"03000000790000000418000000000000,Super Famicom Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b33,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,", +"03000000341200001300000000000000,Super Racer,a:b2,b:b3,back:b8,leftshoulder:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b7,x:b0,y:b1,platform:Windows,", +"03000000457500002211000000000000,Szmy Power PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000004f0400000ab1000000000000,T16000M,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b10,x:b2,y:b3,platform:Windows,", +"030000000d0f00007b00000000000000,TAC GEAR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000e40a00000307000000000000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Windows,", +"03000000e40a00000207000000000000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b9,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Windows,", +"03000000d814000001a0000000000000,TE Kitty,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"03000000ba2200000701000000000000,Technology Innovation PS2 Adapter,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b2,platform:Windows,", +"03000000c61100001000000000000000,Tencent Xianyou Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,x:b3,y:b4,platform:Windows,", +"03000000790000001c18000000000000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000790000002601000000000000,TGZ Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows,", +"03000000591c00002400000000000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,", +"03000000591c00002600000000000000,THEGamepad,a:b2,b:b1,back:b6,leftx:a0,lefty:a1,start:b7,x:b3,y:b0,platform:Windows,", "030000004f04000015b3000000000000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,", -"030000004f04000023b3000000000000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000004f0400000ed0000000000000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000004f04000023b3000000000000,Thrustmaster Dual Trigger PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000004f0400000ed0000000000000,Thrustmaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000004f04000008d0000000000000,Thrustmaster Ferrari 150 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows,", -"030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,", -"03000000666600000488000000000000,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", +"030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,", +"030000004f04000003d0000000000000,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000004f04000009d0000000000000,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000006d04000088ca000000000000,Thunderpad,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,", +"03000000666600000288000000000000,TigerGame PlayStation Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", +"03000000666600000488000000000000,TigerGame PlayStation Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", +"030000004f04000007d0000000000000,TMini,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000571d00002100000000000000,Tomee NES Controller Adapter,a:b1,b:b0,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,start:b3,platform:Windows,", +"03000000571d00002000000000000000,Tomee SNES Controller Adapter,a:b0,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,", "03000000d62000006000000000000000,Tournament PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000c01100000055000000000000,Tronsmart,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "030000005f140000c501000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000b80500000210000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "030000004f04000087b6000000000000,TWCS Throttle,dpdown:b8,dpleft:b9,dpright:b7,dpup:b6,leftstick:b5,lefttrigger:-a5,leftx:a0,lefty:a1,righttrigger:+a5,platform:Windows,", -"03000000d90400000200000000000000,TwinShock PS2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", -"030000006e0500001320000000000000,U4113,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000411200000450000000000000,Twin Shock,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a4,start:b11,x:b3,y:b0,platform:Windows,", +"03000000d90400000200000000000000,TwinShock PS2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000151900005678000000000000,Uniplay U6,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000101c0000171c000000000000,uRage Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000300f00000701000000000000,USB 4-Axis 12-Button Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", -"03000000341a00002308000000000000,USB gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"030000005509000000b4000000000000,USB gamepad,a:b10,b:b11,back:b5,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,guide:b14,leftshoulder:b8,leftstick:b6,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b4,x:b12,y:b13,platform:Windows,", -"030000006b1400000203000000000000,USB gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"03000000790000000a00000000000000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", -"03000000f0250000c183000000000000,USB gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000ff1100004133000000000000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", -"03000000632500002305000000000000,USB Vibration Joystick (BM),a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000790000001a18000000000000,Venom,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", -"03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"030000006f0e00000302000000000000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", -"030000006f0e00000702000000000000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000000b0400003065000000000000,USB Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows,", +"03000000242f00006e00000000000000,USB Controller,a:b1,b:b4,back:b10,leftshoulder:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b7,rightx:a2,righty:a5,start:b11,x:b0,y:b3,platform:Windows,", +"03000000300f00000701000000000000,USB Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000341a00002308000000000000,USB Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"03000000666600000188000000000000,USB Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", +"030000006b1400000203000000000000,USB Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"03000000790000000a00000000000000,USB Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", +"03000000b404000081c6000000000000,USB Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows,", +"03000000b50700001503000000000000,USB Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"03000000bd12000012d0000000000000,USB Controller,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,", +"03000000ff1100004133000000000000,USB Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000632500002305000000000000,USB Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000882800000305000000000000,V5 Game Pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,x:b2,y:b3,platform:Windows,", +"03000000790000001a18000000000000,Venom PS4 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000790000001b18000000000000,Venom PS4 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000006f0e00000302000000000000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", +"030000006f0e00000702000000000000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,", "0300000034120000adbe000000000000,vJoy Device,a:b0,b:b1,back:b15,dpdown:b6,dpleft:b7,dpright:b8,dpup:b5,guide:b16,leftshoulder:b9,leftstick:b13,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b14,righttrigger:b12,rightx:a3,righty:a4,start:b4,x:b2,y:b3,platform:Windows,", +"03000000120c0000ab57000000000000,Warrior Joypad JS083,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000007e0500003003000000000000,Wii U Pro,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b6,leftstick:b11,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b12,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,", +"0300000032150000030a000000000000,Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"0300000032150000140a000000000000,Wolverine,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000002e160000efbe000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,rightshoulder:b5,righttrigger:b11,start:b7,x:b2,y:b3,platform:Windows,", +"03000000380700001647000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000380700002045000000000000,Xbox 360 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700002644000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a2,righty:a5,start:b8,x:b2,y:b3,platform:Windows,", +"03000000380700002647000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000003807000026b7000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000380700003647000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a7,righty:a5,start:b7,x:b2,y:b3,platform:Windows,", +"030000005e0400001907000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000005e0400008e02000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000005e0400009102000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000ad1b000000fd000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000ad1b000001fd000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000ad1b000016f0000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000ad1b00008e02000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000c62400000053000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000c6240000fdfa000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000380700002847000000000000,Xbox 360 Fightpad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000005e040000a102000000000000,Xbox 360 Wireless Receiver,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", "030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000120c00000a88000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a2,righty:a4,start:b6,x:b2,y:b3,platform:Windows,", +"03000000120c00001088000000000000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2~,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5~,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000002a0600002000000000000000,Xbox Controller,a:b0,b:b1,back:b13,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b5,leftstick:b14,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b15,righttrigger:b7,rightx:a2,righty:a5,start:b12,x:b2,y:b3,platform:Windows,", +"03000000380700001645000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,", +"03000000380700002645000000000000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000380700003645000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,", +"03000000380700008645000000000000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000005e0400000202000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,", +"030000005e0400008502000000000000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000005e0400008702000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,", +"030000005e0400008902000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b10,leftstick:b8,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b9,righttrigger:b4,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,", +"030000005e0400000c0b000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000005e040000d102000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000005e040000dd02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000005e040000e002000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000005e040000e302000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000005e040000ea02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000005e040000fd02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000005e040000ff02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000006f0e0000a802000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000006f0e0000c802000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000c62400003a54000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", "030000005e040000130b000000000000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", "03000000341a00000608000000000000,Xeox,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"03000000450c00002043000000000000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", -"03000000ac0500005b05000000000000,Xiaoji Gamesir-G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", -"03000000172700004431000000000000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", -"03000000786901006e70000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", -"03000000790000004f18000000000000,ZD-T Android,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", -"03000000120c0000101e000000000000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000450c00002043000000000000,Xeox SL6556BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"030000006f0e00000300000000000000,XGear,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000e0ff00000201000000000000,Xiaomi Black Shark (L),back:b0,dpdown:b11,dpleft:b9,dpright:b10,dpup:b8,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,platform:Windows,", +"03000000172700004431000000000000,Xiaomi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", +"03000000172700003350000000000000,Xiaomi XMGP01YM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000bc2000005060000000000000,Xiaomi XMGP01YM,+lefty:+a2,+righty:+a5,-lefty:-a1,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows,", +"030000007d0400000340000000000000,Xterminator Digital Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:-a4,lefttrigger:+a4,leftx:a0,lefty:a1,paddle1:b7,paddle2:b6,rightshoulder:b5,rightstick:b9,righttrigger:b2,rightx:a3,righty:a5,start:b8,x:b3,y:b4,platform:Windows,", +"030000002c3600000100000000000000,Yawman Arrow,+rightx:h0.2,+righty:h0.4,-rightx:h0.8,-righty:h0.1,a:b4,b:b5,back:b6,dpdown:b15,dpleft:b14,dpright:b16,dpup:b13,leftshoulder:b10,leftstick:b0,lefttrigger:-a4,leftx:a0,lefty:a1,paddle1:b11,paddle2:b12,rightshoulder:b8,rightstick:b9,righttrigger:+a4,start:b3,x:b1,y:b2,platform:Windows,", +"03000000790000004f18000000000000,ZDT Android Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"03000000120c00000500000000000000,Zeroplus Adapter,a:b2,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows,", +"03000000120c0000101e000000000000,Zeroplus P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", "78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", "78696e70757403000000000000000000,XInput Arcade Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", @@ -429,423 +906,740 @@ const char* _glfwDefaultMappings[] = #endif // _GLFW_WIN32 #if defined(_GLFW_COCOA) -"030000008f0e00000300000009010000,2In1 USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", +"030000008f0e00000300000009010000,2 In 1 Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000c82d00000031000001000000,8BitDo Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000c82d00000531000000020000,8BitDo Adapter 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000c82d00000951000000010000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Mac OS X,", "03000000c82d00000090000001000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000c82d00005106000000010000,8BitDo M30 Gamepad,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d00006a28000000010000,8BitDo GameCube,a:b0,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b9,paddle2:b8,rightshoulder:b10,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b1,y:b4,platform:Mac OS X,", +"03000000c82d00001251000000010000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d00001251000000020000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d00001151000000010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d00001151000000020000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000a30c00002400000006020000,8BitDo M30,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,guide:b9,leftshoulder:b6,lefttrigger:b5,rightshoulder:b4,righttrigger:b7,start:b8,x:b3,y:b0,platform:Mac OS X,", +"03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000c82d00005106000000010000,8BitDo M30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b2,leftshoulder:b6,lefttrigger:a5,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d00002090000000010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d00000451000000010000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Mac OS X,", "03000000c82d00001590000001000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", -"030000003512000012ab000001000000,8BitDo NES30 Gamepad,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000022000000090000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000c82d00000190000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000102800000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000c82d00001290000001000000,8BitDo SN30 Gamepad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000c82d00004028000000010000,8Bitdo SN30 GamePad,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,platform:Mac OS X,", +"03000000c82d00006928000000010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X,", +"03000000c82d00002590000000010000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000c82d00002590000001000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000c82d00002690000000010000,8BitDo NEOGEO,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b10,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000003512000012ab000001000000,8BitDo NES30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d000012ab000001000000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000c82d00002028000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000022000000090000001000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000203800000900000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d00000190000001000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d00000751000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000c82d00000851000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d00000660000000020000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d00000131000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d00000231000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d00000331000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d00000431000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d00002867000000010000,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b3,y:b4,platform:Mac OS X,", +"03000000c82d00003028000000010000,8Bitdo SFC30 Gamepad,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000102800000900000000000000,8BitDo SFC30 Joystick,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d00000351000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d00001290000001000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d00004028000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000c82d00000160000001000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000c82d00000260000001000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000c82d00000261000000010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000c82d00000031000001000000,8BitDo Wireless Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000c82d00001890000001000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d00000260000001000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000c82d00001b30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000c82d00001d30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000c82d00001530000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000c82d00001630000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000c82d00001730000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000c82d00001130000000020000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000c82d00001330000000020000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000c82d00001330000001000000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000a00500003232000009010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000c82d00001890000001000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a31,start:b11,x:b4,y:b3,platform:Mac OS X,", -"03000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000a00500003232000009010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000a30c00002700000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", -"03000000a30c00002800000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", -"03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", -"03000000ef0500000300000000020000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Mac OS X,", "03000000491900001904000001010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Mac OS X,", "03000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,", +"0300000008100000e501000019040000,Anbernic Handheld,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000373500004610000001000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000a30c00002700000003030000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000a30c00002800000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", +"03000000050b00000579000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b42,paddle1:b9,paddle2:b11,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,", +"03000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b23,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,", +"03000000503200000110000045010000,Atari VCS Classic,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X,", +"03000000503200000110000047010000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X,", +"03000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000008a3500000102000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X,", +"030000008a3500000201000000010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000008a3500000202000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X,", +"030000008a3500000402000000010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000008a3500000302000000010000,Backbone One PlayStation Edition,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X,", "03000000c62400001a89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,", "03000000c62400001b89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000d62000002a79000000010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000120c0000200e000000010000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000120c0000210e000000010000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000d62000002a79000000010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"03000000120c0000200e000000010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"03000000120c0000210e000000010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", "030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000d8140000cecf000000000000,Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X,", -"03000000a306000022f6000001030000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000a306000022f6000001030000,Cyborg V3 Rumble Pad PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000791d00000103000009010000,Dual Box Wii Classic Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", +"030000006e0500000720000010020000,Elecom JC-W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Mac OS X,", +"030000006f0e00008401000003010000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000151900004000000001000000,Flydigi Vader 2,a:b14,b:b15,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Mac OS X,", +"03000000b40400001124000001040000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000b40400001224000003030000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", "03000000790000004618000000010000,GameCube Controller Adapter,a:b4,b:b0,dpdown:b56,dpleft:b60,dpright:b52,dpup:b48,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,platform:Mac OS X,", -"03000000ad1b000001f9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"03000000ac0500001a06000002020000,GameSir T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000373500000411000023000000,GameSir X4A Xbox Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000ad1b000001f9000000000000,Gamestop BB070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", -"03000000c01100000140000000010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000006f0e00000102000000000000,GameStop Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"030000007d0400000540000001010000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000280400000140000000020000,Gravis Gamepad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000008f0e00000300000007010000,GreenAsia Inc. USB Joystick,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Mac OS X,", +"03000000c01100000140000000010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"030000006f0e00000102000000000000,GameStop Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"03000000ff1100003133000007010000,GameWare PC Control Pad,a:b2,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b0,platform:Mac OS X,", +"03000000d11800000094000000010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,", +"030000007d0400000540000001010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000280400000140000000020000,Gravis GamePad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000008f0e00000300000007010000,GreenAsia Joystick,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Mac OS X,", "030000000d0f00002d00000000100000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000000d0f00005f00000000010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000000d0f00005e00000000010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000000d0f00006e00000000010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000000d0f00006600000000010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000000d0f00006600000000000000,HORIPAD FPS PLUS 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000000d0f0000ee00000000010000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000008f0e00001330000011010000,HuiJia SNES Controller,a:b4,b:b2,back:b16,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b12,rightshoulder:b14,start:b18,x:b6,y:b0,platform:Mac OS X,", -"03000000830500006020000000010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,", -"03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,", +"030000000d0f00005f00000000000000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00005f00000000010000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00005e00000000000000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00005e00000000010000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00008400000000010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00008500000000010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000341a00000302000014010000,Hori Fighting Stick Mini,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00008800000000010000,Hori Fighting Stick mini 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00008700000000010000,Hori Fighting Stick mini 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00004d00000000000000,Hori Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00003801000008010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Mac OS X,", +"030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f0000aa00000072050000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", +"030000000d0f00000002000017010000,Hori Split Pad Fit,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00000002000015010000,Hori Switch Split Pad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00006e00000000010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00006600000000010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00006600000000000000,Horipad FPS Plus 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f0000ee00000000010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f0000c100000072050000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000242e0000ff0b000000010000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Mac OS X,", +"03000000790000004e95000000010000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Mac OS X,", +"03000000830500006020000000000000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,", +"03000000ef0500000300000000020000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Mac OS X,", +"03000000fd0500000030000010010000,Interact GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Mac OS X,", "030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Mac OS X,", "030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Mac OS X,", -"03000000242f00002d00000007010000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000242f00002d00000007010000,JYS Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", +"030000006d04000019c2000000000000,Logitech Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000006d04000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000006d04000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000006d04000016c2000000000000,Logitech F310,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000006d04000018c2000000000000,Logitech F510,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000006d04000019c2000005030000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000006d0400001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"030000006d04000018c2000000010000,Logitech RumblePad 2 USB,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000006d04000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000380700005032000000010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000380700005082000000010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000380700008433000000010000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000380700008483000000010000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000790000000600000007010000,Marvo GT-004,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Mac OS X,", +"030000006d0400001fc2000000000000,Logitech F710,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000006d04000018c2000000010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000380700005032000000010000,Mad Catz PS3 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000380700008433000000010000,Mad Catz PS3 Fightstick TE S Plus,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000380700005082000000010000,Mad Catz PS4 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"03000000380700008483000000010000,Mad Catz PS4 Fightstick TE S Plus,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"0300000049190000020400001b010000,Manba One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b22,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000790000000600000007010000,Marvo GT-004,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", +"030000008f0e00001330000011010000,Mayflash Controller Adapter,a:b2,b:b4,back:b16,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b12,lefttrigger:b16,leftx:a0,lefty:a2,rightshoulder:b14,rightx:a6~,righty:a4,start:b18,x:b0,y:b6,platform:Mac OS X,", +"03000000790000004318000000010000,Mayflash GameCube Adapter,a:b4,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,platform:Mac OS X,", "03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000242f00007300000000020000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Mac OS X,", "0300000079000000d218000026010000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", "03000000d620000010a7000003010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X,", -"03000000790000000018000000010000,Mayflash Wii U Pro Controller Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,", -"03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,", -"03000000d8140000cecf000000000000,MC Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000005e0400002700000001010000,Microsoft SideWinder Plug & Play Game Pad,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,leftx:a0,lefty:a1,righttrigger:b5,x:b2,y:b3,platform:Mac OS X,", -"03000000d62000007162000001000000,Moga Pro 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,", -"03000000c62400002a89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000c62400002b89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000632500007505000000020000,NEOGEO mini PAD Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b2,y:b3,platform:Mac OS X,", +"030000008f0e00001030000011010000,Mayflash Saturn Adapter,a:b0,b:b2,dpdown:b28,dpleft:b30,dpright:b26,dpup:b24,leftshoulder:b10,lefttrigger:b14,rightshoulder:b12,righttrigger:b4,start:b18,x:b6,y:b8,platform:Mac OS X,", +"0300000025090000e803000000000000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X,", +"03000000790000000318000000010000,Mayflash Wii DolphinBar,a:b8,b:b12,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b44,leftshoulder:b16,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b4,platform:Mac OS X,", +"03000000790000000018000000000000,Mayflash Wii U Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,", +"03000000790000000018000000010000,Mayflash Wii U Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,", +"030000005e0400002800000002010000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Mac OS X,", +"030000005e0400000300000006010000,Microsoft SideWinder,a:b0,b:b1,back:b9,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Mac OS X,", +"030000005e0400000700000006010000,Microsoft SideWinder,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Mac OS X,", +"030000005e0400002700000001010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Mac OS X,", +"030000004523000015e0000072050000,Mobapad Chitu HD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", +"03000000d62000007162000001000000,Moga Pro 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,", +"03000000c62400002a89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000c62400002b89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000853200008906000000010000,Nacon Revolution X Unlimited,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000632500007505000000020000,NeoGeo mini PAD Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b2,y:b3,platform:Mac OS X,", "03000000921200004b46000003020000,NES 2-port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Mac OS X,", "030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Mac OS X,", -"03000000d620000011a7000000020000,Nintendo Switch Core (Plus) Wired Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000d620000011a7000010050000,Nintendo Switch PowerA Wired Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000007e0500006920000001010000,Nintendo Switch 2 Pro Controller,a:b0,b:b1,back:b14,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b12,leftstick:b15,lefttrigger:b13,leftx:a0,lefty:a1~,misc1:b17,misc2:b20,paddle1:b18,paddle2:b19,rightshoulder:b4,rightstick:b7,righttrigger:b5,rightx:a2,righty:a3~,start:b6,x:b2,y:b3,platform:Mac OS X,", "030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", "030000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", -"03000000550900001472000025050000,NVIDIA Controller v01.04,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Mac OS X,", -"030000006f0e00000901000002010000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,", -"030000004c050000da0c000000010000,Playstation Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", -"030000004c0500003713000000010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000007e0500000920000010020000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:Mac OS X,", +"050000007e05000009200000ff070000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:Mac OS X,", +"030000007e0500007320000001010000,NSO GameCube Controller,a:b1,b:b3,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b13,lefttrigger:b12,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b4,rightx:a2,righty:a3~,start:b6,x:b0,y:b2,platform:Mac OS X,", +"030000007e0500001920000001000000,NSO N64 Controller,+rightx:b8,+righty:b7,-rightx:b3,-righty:b2,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Mac OS X,", +"030000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b15,start:b9,x:b2,y:b3,platform:Mac OS X,", +"03000000550900001472000025050000,NVIDIA Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Mac OS X,", +"030000004b120000014d000000010000,Nyko Airflo EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Mac OS X,", +"0300000009120000072f000000010000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a2,leftx:a0,lefty:a1,righttrigger:a5,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000006f0e00000901000002010000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000008f0e00000300000000000000,Piranha Xtreme PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000d620000011a7000000020000,PowerA Core Plus Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", +"03000000d620000011a7000010050000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000d62000006dca000000010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000100800000300000006010000,PS2 Adapter,a:b2,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", "030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,", "030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,", -"030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000004c0500006802000072050000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,", +"030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"0300004b4c0500005f0e000000010000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"030000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"030000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"050000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"030000005e040000e002000001000000,PXN P30 Pro Mobile,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,", +"03000000222c00000225000000010000,Qanba Dragon Arcade Joystick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000222c00000020000000010000,Qanba Drone Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000009b2800005600000020020000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Mac OS X,", +"030000009b2800008000000022020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Mac OS X,", "030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"03000000321500000204000000010000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000321500000104000000010000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000321500000010000000010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000321500000204000000010000,Razer Panthera PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000321500000104000000010000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"03000000321500000010000000010000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000321500000507000001010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000321500000011000000010000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000321500000011000000010000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", "03000000321500000009000000020000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,", "030000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,", "0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"03000000790000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000632500008005000000010000,Redgear,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000632500002305000000010000,Redragon Saturn,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000921200004547000000020000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b2,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,lefttrigger:b14,rightshoulder:b10,righttrigger:b4,start:b12,x:b6,y:b8,platform:Mac OS X,", +"03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000790000001100000005010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000830500006020000000010000,Retro Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b8,righttrigger:b9,start:b7,x:b2,y:b3,platform:Mac OS X,", +"0300000003040000c197000000000000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Mac OS X,", "03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000341200000400000000000000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,platform:Mac OS X,", "030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000006b140000130d000000010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000c6240000fefa000000000000,Rock Candy Gamepad for PS3,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Mac OS X,", +"030000004c0500006802000002100000,Rii RK707,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b3,righttrigger:b9,rightx:a2,righty:a3,start:b1,x:b15,y:b12,platform:Mac OS X,", +"030000006f0e00008701000005010000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000c6240000fefa000000000000,Rock Candy PS3,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"03000000e804000000a000001b010000,Samsung EIGP20,a:b1,b:b3,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b11,leftx:a1,lefty:a3,rightshoulder:b12,rightx:a4,righty:a5,start:b16,x:b7,y:b9,platform:Mac OS X,", +"03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Mac OS X,", +"03000000a30c00002500000006020000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Mac OS X,", "03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X,", -"03000000b40400000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X,", -"030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000b40400000a01000000000000,Sega Saturn,a:b0,b:b1,back:b5,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b2,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X,", +"030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", "0300000000f00000f100000000000000,SNES RetroPort,a:b2,b:b3,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,rightshoulder:b7,start:b6,x:b0,y:b1,platform:Mac OS X,", -"030000004c050000e60c000000010000,Sony DualSense,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000d11800000094000000010000,Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,", +"030000004c050000a00b000000000000,Sony DualShock 4 Adapter,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"03000000666600006706000088020000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Mac OS X,", +"030000004c050000da0c000000010000,Sony PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", +"030000004c0500003713000000010000,Sony PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000005e0400008e02000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X,", +"03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,", "03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,", "03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,", +"05000000484944204465766963650000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,platform:Mac OS X,", "050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,platform:Mac OS X,", +"03000000381000003014000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"03000000381000003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,", "03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,", -"03000000457500002211000000010000,SZMY-POWER PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f0000f600000000010000,Switch Hori Pad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", +"03000000457500002211000000010000,SZMY Power PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000e40a00000307000001000000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Mac OS X,", +"03000000e40a00000207000001000000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Mac OS X,", +"03000000790000001c18000000010000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000790000001c18000003100000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000591c00002400000021000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Mac OS X,", +"03000000591c00002600000021000000,THEGamepad,a:b2,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Mac OS X,", "030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X,", -"030000004f0400000ed0000000020000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000004f0400000ed0000000020000,Thrustmaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X,", -"03000000bd12000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", -"03000000bd12000015d0000000010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000571d00002100000021000000,Tomee NES Controller Adapter,a:b1,b:b0,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,start:b3,platform:Mac OS X,", +"03000000bd12000015d0000000010000,Tomee Retro Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000bd12000015d0000000000000,Tomee SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000571d00002000000021000000,Tomee SNES Controller Adapter,a:b0,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Mac OS X,", +"030000005f140000c501000000020000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", "03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X,", -"030000006f0e00000302000025040000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", -"030000006f0e00000702000003060000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000791d00000103000009010000,Wii Classic Controller,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000632500002605000000010000,Uberwith Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000151900005678000010010000,Uniplay U6,a:b3,b:b6,back:b25,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,leftstick:b31,lefttrigger:b21,leftx:a1,lefty:a3,rightshoulder:b19,rightstick:b33,righttrigger:b23,rightx:a4,righty:a5,start:b27,x:b11,y:b13,platform:Mac OS X,", +"030000006f0e00000302000025040000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", +"030000006f0e00000702000003060000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,", "050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X,", "050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X,", -"030000005e0400008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"030000006f0e00000104000000000000,Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"03000000c6240000045d000000000000,Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000005e0400008e02000000000000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000005e0400008e02000010010000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4~,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000006f0e00000104000000000000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"03000000c6240000045d000000000000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"030000005e040000050b000003090000,Xbox Elite Wireless Controller Series 2,a:b0,b:b1,back:b31,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b53,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000c62400003a54000000000000,Xbox One PowerA Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"030000005e040000d102000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"030000005e040000dd02000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"030000005e040000e302000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000005e040000050b000003090000,Xbox Elite Controller Series 2,a:b0,b:b1,back:b31,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b53,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000005e040000130b000011050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000005e040000200b000011050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000005e040000200b000013050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000005e040000200b000015050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000005e040000d102000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000005e040000dd02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000005e040000e002000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,", +"030000005e040000e002000003090000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,", +"030000005e040000e302000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000005e040000ea02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000005e040000fd02000003090000,Xbox One Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000c62400003a54000000000000,Xbox One PowerA Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "030000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", "030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", -"030000005e040000e002000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,", -"030000005e040000e002000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,", -"030000005e040000ea02000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", -"030000005e040000fd02000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X,", -"03000000120c0000100e000000010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", -"03000000120c0000101e000000010000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000005e040000130b000009050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000005e040000130b000013050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000005e040000130b000015050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000005e040000130b000007050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000005e040000130b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000005e040000130b000022050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000005e040000220b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000120c0000100e000000010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000120c0000101e000000010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", #endif // _GLFW_COCOA #if defined(GLFW_BUILD_LINUX_JOYSTICK) +"03000000c82d00000031000011010000,8BitDo Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000c82d00000631000000010000,8BitDo Adapter 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000c82d00000951000000010000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Linux,", +"03000000021000000090000011010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "03000000c82d00000090000011010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"05000000c82d00001038000000010000,8Bitdo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"05000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"05000000c82d00006a28000000010000,8BitDo GameCube,a:b0,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b9,paddle2:b8,rightshoulder:b10,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b1,y:b4,platform:Linux,", +"03000000c82d00001251000011010000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"05000000c82d00001251000000010000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"03000000c82d00001151000011010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"05000000c82d00001151000000010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000c82d00000650000011010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "05000000c82d00005106000000010000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Linux,", +"03000000c82d00000a20000000020000,8BitDo M30 Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux,", +"03000000c82d00002090000011010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"05000000c82d00002090000000010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"03000000c82d00000451000000010000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Linux,", "03000000c82d00001590000011010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "05000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"03000000c82d00006928000011010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Linux,", +"05000000c82d00006928000000010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Linux,", +"05000000c82d00002590000001000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000008000000210000011010000,8BitDo NES30,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", "03000000c82d00000310000011010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux,", "05000000c82d00008010000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux,", -"03000000022000000090000011010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"05000000c82d00002038000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"03000000022000000090000011010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"03000000c82d00000190000011010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"05000000203800000900000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"05000000c82d00002038000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"03000000c82d00000751000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"05000000c82d00000851000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000c82d00000660000011010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"03000000c82d00001030000011010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"05000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"03000000c82d00000020000000000000,8BitDo Pro 2 for Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"06000000c82d00000020000006010000,8BitDo Pro 2 for Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000c82d00000131000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"03000000c82d00000231000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"03000000c82d00000331000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"03000000c82d00000431000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"03000000c82d00002867000000010000,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b3,y:b4,platform:Linux,", +"03000000c82d00000060000011010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "05000000c82d00000060000000010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"05000000c82d00000061000000010000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"03000000c82d000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", -"030000003512000012ab000010010000,8Bitdo SFC30 GamePad,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Linux,", -"05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", -"05000000c82d00003028000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", +"05000000c82d00000061000000010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"030000003512000012ab000010010000,8BitDo SFC30,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Linux,", +"030000003512000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", +"03000000c82d000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", +"05000000102800000900000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", +"05000000c82d00003028000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", +"05000000c82d00000351000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,", "03000000c82d00000160000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,", "03000000c82d00001290000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,", "05000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "05000000c82d00006228000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"03000000c82d00000260000011010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"05000000c82d00000261000000010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"05000000202800000900000000010000,8BitDo SNES30 Gamepad,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", -"03000000c82d00000031000011010000,8BitDo Wireless Adapter (DInput),a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"030000005e0400008e02000020010000,8BitDo Wireless Adapter (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", +"03000000c82d00000260000011010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"05000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"05000000202800000900000000010000,8BitDo SNES30,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", +"05000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000c82d00000a31000014010000,8BitDo Ultimate 2C,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000c82d00001d30000011010000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"05000000c82d00001b30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000c82d00001530000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000c82d00001630000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000c82d00001730000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000c82d00001130000011010000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000c82d00000631000010010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000c82d00000631000014010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000c82d00000760000011010000,8BitDo Ultimate Wireless,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"03000000c82d00001230000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000c82d00001330000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000c82d00000121000011010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"05000000c82d00000121000000010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"05000000a00500003232000001000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,", +"05000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,", +"03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", "05000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", -"050000005e040000e002000030110000,8BitDo Zero 2 (XInput),a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,", -"05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,", -"05000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,", -"03000000c01100000355000011010000,ACRUX USB GAME PAD,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000006f0e00001302000000010000,Afterglow,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006f0e00003901000020060000,Afterglow Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000c01100000355000011010000,Acrux Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006f0e00008801000011010000,Afterglow Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000006f0e00003901000000430000,Afterglow Prismatic Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006f0e00003901000013020000,Afterglow Prismatic Wired Controller 048-007-NA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"030000007c1800000006000010010000,Alienware Dual Compatible Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Linux,", -"05000000491900000204000021000000,Amazon Fire Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b17,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b12,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"030000006f0e00003901000013020000,Afterglow Prismatic Controller 048-007-NA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006f0e00001302000000010000,Afterglow Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006f0e00003901000020060000,Afterglow Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000100000008200000011010000,Akishop Customs PS360,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"030000007c1800000006000010010000,Alienware Dual Compatible Game PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Linux,", +"05000000491900000204000021000000,Amazon Fire Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b17,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b12,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "03000000491900001904000011010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux,", "05000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,", +"0300000008100000e501000001010000,Anbernic Handheld,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b3,y:b4,platform:Linux,", +"03000000020500000913000010010000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000373500000710000010010000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"05000000373500004610000001000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000190e00000110000010010000,Aquaplus Piece,a:b1,b:b0,back:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b2,platform:Linux,", "03000000790000003018000011010000,Arcade Fightstick F300,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"03000000a30c00002700000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", +"03000000a30c00002700000011010000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", "03000000a30c00002800000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", "05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,", "05000000050b00000045000040000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,", -"03000000503200000110000000000000,Atari Classic Controller,a:b0,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,x:b1,platform:Linux,", -"05000000503200000110000000000000,Atari Classic Controller,a:b0,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,x:b1,platform:Linux,", -"03000000503200000210000000000000,Atari Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,", -"05000000503200000210000000000000,Atari Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,", -"03000000120c00000500000010010000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux,", -"03000000ef0500000300000000010000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux,", -"03000000c62400001b89000011010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"03000000d62000002a79000011010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000c21100000791000011010000,Be1 GC101 Controller 1.03 mode,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"03000000c31100000791000011010000,Be1 GC101 GAMEPAD 1.03 mode,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"030000005e0400008e02000003030000,Be1 GC101 Xbox 360 Controller mode,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"05000000bc2000000055000001000000,BETOP AX1 BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux,", -"03000000120c0000200e000011010000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000120c0000210e000011010000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000050b00000579000011010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b36,paddle1:b52,paddle2:b53,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"05000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b21,paddle1:b22,paddle2:b23,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux,", +"03000000503200000110000011010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux,", +"05000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux,", +"05000000503200000110000044010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux,", +"05000000503200000110000046010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux,", +"03000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Linux,", +"03000000503200000210000011010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,", +"05000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,", +"05000000503200000210000045010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,", +"05000000503200000210000046010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,", +"05000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:-a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Linux,", +"030000008a3500000201000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"030000008a3500000202000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"030000008a3500000302000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"030000008a3500000402000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000c62400001b89000011010000,BDA MOGA XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000d62000002a79000011010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"03000000c21100000791000011010000,Be1 GC101 Controller 1.03,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", +"03000000c31100000791000011010000,Be1 GC101 Controller 1.03,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"030000005e0400008e02000003030000,Be1 GC101 Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000bc2000004d50000011010000,Beitong A1T2 BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"05000000bc2000000055000001000000,Betop AX1 BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000bc2000006412000011010000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b30,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", +"030000006b1400000209000011010000,Bigben,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000120c0000300e000011010000,Brook Audio Fighting Board PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000120c0000310e000011010000,Brook Audio Fighting Board PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"03000000120c0000200e000011010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"03000000120c0000210e000011010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", "03000000120c0000f70e000011010000,Brook Universal Fighting Board,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"03000000ffff0000ffff000000010000,Chinese-made Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", +"03000000d81d00000b00000010010000,Buffalo BSGP1601,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Linux,", "03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", +"03000000af1e00002400000010010000,Clockwork Pi DevTerm,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b9,x:b3,y:b0,platform:Linux,", "030000000b0400003365000000010000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Linux,", "03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux,", -"03000000a306000022f6000011010000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", -"03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,", -"03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,", -"030000004f04000004b3000010010000,Dual Power 2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", +"03000000a306000022f6000011010000,Cyborg V3 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", +"030000005e0400008e02000002010000,Data Frog S80,a:b1,b:b0,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,", +"03000000791d00000103000010010000,Dual Box Wii Classic Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,", +"03000000c11100000191000011010000,EasySMX,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", +"03000000242f00009100000000010000,EasySMX ESM-9101,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006e0500000320000010010000,Elecom U3613M,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux,", +"030000006e0500000720000010010000,Elecom W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Linux,", +"030000007d0400000640000010010000,Eliminator AfterShock,a:b1,b:b2,back:b9,dpdown:+a3,dpleft:-a5,dpright:+a5,dpup:-a3,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a4,righty:a2,start:b8,x:b0,y:b3,platform:Linux,", +"03000000430b00000300000000010000,EMS Production PS2 Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", +"030000006f0e00008401000011010000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006f0e00008101000011010000,Faceoff Deluxe Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006f0e00008001000011010000,Faceoff Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000852100000201000010010000,FF GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"05000000b40400001224000001010000,Flydigi APEX 4,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b4,leftstick:b10,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b20,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"03000000b40400001124000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000b40400001224000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"05000000151900004000000001000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"030000007e0500003703000000000000,GameCube Adapter,a:b0,b:b1,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,", +"19000000030000000300000002030000,GameForce Controller,a:b1,b:b0,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b5,rightstick:b15,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", +"03000000ac0500005b05000010010000,GameSir G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "03000000bc2000000055000011010000,GameSir G3w,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000558500001b06000010010000,GameSir G4 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"05000000ac0500002d0200001b010000,GameSir G4s,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b33,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000ac0500007a05000011010000,GameSir G5,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000373500009710000001020000,GameSir Kaleid Flux,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000ac0500001a06000011010000,GameSir T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000bc2000005656000011010000,GameSir T4w,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000373500009410000010010000,GameSir Tegenaria Lite,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", "030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000008f0e00000800000010010000,Gasia Co. Ltd PS(R) Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000008f0e00000800000010010000,Gasia PlayStation Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "03000000451300000010000010010000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000f0250000c283000010010000,Gioteck VX2 PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"190000004b4800000010000000010000,GO-Advance Controller,a:b1,b:b0,back:b10,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,leftshoulder:b4,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b13,start:b15,x:b2,y:b3,platform:Linux,", +"190000004b4800000010000001010000,GO-Advance Controller,a:b1,b:b0,back:b12,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b4,leftstick:b13,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b16,righttrigger:b15,start:b17,x:b2,y:b3,platform:Linux,", +"190000004b4800000011000000010000,GO-Super Gamepad,a:b0,b:b1,back:b12,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b16,leftshoulder:b4,leftstick:b14,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b3,y:b2,platform:Linux,", +"03000000f0250000c183000010010000,Goodbetterbest Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000d11800000094000011010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", +"05000000d11800000094000000010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", "0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000007d0400000540000000010000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"030000008f0e00000610000000010000,GreenAsia Electronics 4Axes 12Keys GamePad ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux,", -"030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", +"030000005e0400008e02000001010000,GPD Win Max 2 6800U Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000007d0400000540000000010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"03000000280400000140000000010000,Gravis GamePad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"030000008f0e00000610000000010000,GreenAsia Electronics Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux,", +"030000008f0e00001200000010010000,GreenAsia Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", "0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", "03000000f0250000c383000010010000,GT VX2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux,", -"03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"03000000632500002605000010010000,HJD-X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux,", -"030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f0000c100000011010000,HORI CO. LTD. HORIPAD S,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f00006a00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f00006b00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f00008500000010010000,HORI Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f00008600000002010000,Hori Fighting Commander,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"06000000adde0000efbe000002010000,Hidromancer Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000d81400000862000011010000,HitBox PS3 PC Analog Mode,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux,", +"03000000c9110000f055000011010000,HJC Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"030000000d0f00006d00000020010000,Hori EDGE 301,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:+a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000000d0f00008400000011010000,Hori Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00005f00000011010000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00005e00000011010000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"030000000d0f00005001000009040000,Hori Fighting Commander Octa Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000000d0f00008500000010010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00008600000002010000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"030000000d0f00003701000013010000,Hori Fighting Stick Mini,a:b1,b:b0,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b3,y:b2,platform:Linux,", +"030000000d0f00008800000011010000,Hori Fighting Stick mini 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00008700000011010000,Hori Fighting Stick mini 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,rightshoulder:b5,rightstick:b11,righttrigger:a4,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"030000000d0f00001000000011010000,Hori Fightstick 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"03000000ad1b000003f5000033050000,Hori Fightstick VX,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b8,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,", +"030000000d0f00004d00000011010000,Hori Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000000d0f00009200000011010000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f0000aa00000011010000,HORI Real Arcade Pro,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"030000000d0f0000d800000072056800,HORI Real Arcade Pro S,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,", -"030000000d0f00001600000000010000,Hori Real Arcade Pro.EX-SE (Xbox 360),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,", -"030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f00006600000011010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f0000ee00000011010000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"030000000d0f00006700000001010000,HORIPAD ONE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000008f0e00001330000010010000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Linux,", +"030000000d0f00003801000011010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Linux,", +"030000000d0f00009200000011010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00001100000011010000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00002200000011010000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00006a00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00006b00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00001600000000010000,Hori Real Arcade Pro EXSE,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,", +"030000000d0f0000aa00000011010000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", +"030000000d0f00008501000017010000,Hori Split Pad Fit,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000000d0f00008501000015010000,Hori Switch Split Pad Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000000d0f00006e00000011010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00006600000011010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"030000000d0f0000ee00000011010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f0000c100000011010000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00006700000001010000,Horipad One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000000d0f0000ab01000011010000,Horipad Steam,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc2:b2,paddle1:b19,paddle2:b18,paddle3:b15,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"050000000d0f00009601000091000000,Horipad Steam,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc2:b2,paddle1:b19,paddle2:b18,paddle3:b15,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"050000000d0f0000f600000001000000,Horipad Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"03000000341a000005f7000010010000,HuiJia GameCube Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,", +"05000000242e00000b20000001000000,Hyperkin Admiral N64 Controller,+rightx:b11,+righty:b13,-rightx:b8,-righty:b12,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,platform:Linux,", +"03000000242e0000ff0b000011010000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Linux,", +"03000000242e00006a38000010010000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,platform:Linux,", "03000000242e00008816000001010000,Hyperkin X91,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,", -"050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000b50700001503000010010000,impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,", -"03000000d80400008200000003000000,IMS PCU#0 Gamepad Interface,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,platform:Linux,", -"03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux,", -"0500000049190000020400001b010000,Ipega PG-9069 - Bluetooth Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b161,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"03000000632500007505000011010000,Ipega PG-9099 - Bluetooth Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux,", -"03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,", -"03000000300f00000b01000010010000,Jess Tech GGE909 PC Recoil Pad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", -"03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", +"03000000f00300008d03000011010000,HyperX Clutch,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000830500006020000010010000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,", +"030000008f0e00001330000001010000,iCode Retro Adapter,b:b3,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b1,start:b7,x:b2,y:b0,platform:Linux,", +"050000006964726f69643a636f6e0000,idroidcon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000b50700001503000010010000,Impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,", +"03000000d80400008200000003000000,IMS PCU0,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,platform:Linux,", +"03000000120c00000500000010010000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux,", +"03000000ef0500000300000000010000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux,", +"03000000fd0500000030000000010000,InterAct GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Linux,", +"03000000fd0500002a26000000010000,InterAct HammerHead FX,a:b3,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux,", +"0500000049190000020400001b010000,Ipega PG 9069,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b161,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000632500007505000011010000,Ipega PG 9099,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", +"0500000049190000030400001b010000,Ipega PG9099,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"05000000491900000204000000000000,Ipega PG9118,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000300f00001101000010010000,Jess Tech Colour Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,", +"03000000300f00001001000010010000,Jess Tech Dual Analog Rumble,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,", +"03000000300f00000b01000010010000,Jess Tech GGE909 PC Recoil,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", +"03000000ba2200002010000001010000,Jess Technology Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", "030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux,", "050000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux,", "030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux,", "050000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux,", "03000000bd12000003c0000010010000,Joypad Alpha Shock,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000242f00002d00000011010000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"03000000242f00008a00000011010000,JYS Wireless Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux,", +"03000000242f00002d00000011010000,JYS Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", +"03000000242f00008a00000011010000,JYS Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux,", "030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006d040000d1ca000000000000,Logitech ChillStream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006d040000d1ca000000000000,Logitech Chillstream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006d040000d1ca000011010000,Logitech Chillstream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006d0400001ec2000019200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006d0400000ac2000010010000,Logitech Inc. WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,platform:Linux,", +"030000006d0400001dc2000014400000,Logitech F310,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006d0400001ec2000019200000,Logitech F510,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006d0400001ec2000020200000,Logitech F510,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006d04000019c2000011010000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006d0400001fc2000005030000,Logitech F710,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,platform:Linux,", -"050000004d4f435554452d3035305800,M54-PC,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"05000000380700006652000025010000,Mad Catz C.T.R.L.R ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000380700005032000011010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000380700005082000011010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000006d0400000ac2000010010000,Logitech WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,platform:Linux,", +"05000000380700006652000025010000,Mad Catz CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700008532000010010000,Mad Catz Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700005032000011010000,Mad Catz Fightpad Pro PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700005082000011010000,Mad Catz Fightpad Pro PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", "03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux,", -"03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000380700008433000011010000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000380700008483000011010000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"03000000380700008031000011010000,Mad Catz FightStick Alpha PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700008081000011010000,Mad Catz FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700008034000011010000,Mad Catz Fightstick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700008084000011010000,Mad Catz Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"03000000380700008433000011010000,Mad Catz Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700008483000011010000,Mad Catz Fightstick TE S PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"03000000380700001888000010010000,Mad Catz Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700003888000010010000,Mad Catz Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700001647000010040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000380700003847000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", "03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000242f0000f700000001010000,Magic-S Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000120c00000500000000010000,Manta Dualshock 2,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", +"03000000120c00000500000000010000,Manta DualShock 2,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", +"030000008f0e00001330000010010000,Mayflash Controller Adapter,a:b1,b:b2,back:b8,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3~,righty:a2,start:b9,x:b0,y:b3,platform:Linux,", +"03000000790000004318000010010000,Mayflash GameCube Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", "03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", -"03000000790000004318000010010000,Mayflash GameCube Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,", "03000000242f00007300000011010000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux,", "0300000079000000d218000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "03000000d620000010a7000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"0300000025090000e803000001010000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,", -"03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", -"030000005e0400000e00000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,", -"030000005e0400008e02000004010000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"050000005e040000050b000003090000,Microsoft X-Box One Elite 2 pad,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"030000005e040000e302000003020000,Microsoft X-Box One Elite pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e040000d102000001010000,Microsoft X-Box One pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e040000dd02000003020000,Microsoft X-Box One pad (Firmware 2015),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e040000d102000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", -"030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", -"030000005e040000000b000008040000,Microsoft Xbox One Elite 2 pad - Wired,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e040000ea02000008040000,Microsoft Xbox One S pad - Wired,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000c62400001a53000000010000,Mini PE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000242f0000f700000001010000,Mayflash Magic S Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000008f0e00001030000010010000,Mayflash Saturn Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,platform:Linux,", +"0300000025090000e803000001010000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,", +"03000000790000000318000011010000,Mayflash Wii DolphinBar,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Linux,", +"03000000790000000018000011010000,Mayflash Wii U Pro Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000b50700001203000010010000,Mega World Logic 3 Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,", +"03000000b50700004f00000000010000,Mega World Logic 3 Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Linux,", +"03000000780000000600000010010000,Microntek Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", +"030000005e0400002800000000010000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Linux,", +"030000005e0400000300000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux,", +"030000005e0400000700000000010000,Microsoft SideWinder,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Linux,", +"030000005e0400000e00000000010000,Microsoft SideWinder Freestyle Pro,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,", +"030000005e0400002700000000010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Linux,", +"030000005e0400008502000000010000,Microsoft Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", +"030000005e0400008902000021010000,Microsoft Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", +"030000005e0400008e02000001000000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.1,dpleft:h0.2,dpright:h0.8,dpup:h0.4,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400008e02000004010000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400008e02000056210000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400008e02000062230000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000d102000001010000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000d102000003020000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000dd02000003020000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000ea02000008040000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000ea0200000f050000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"060000005e040000120b000009050000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000e302000003020000,Microsoft Xbox One Elite,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000000b000007040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b12,paddle2:b14,paddle3:b13,paddle4:b15,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000000b000008040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b12,paddle2:b14,paddle3:b13,paddle4:b15,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"050000005e040000050b000003090000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"050000005e0400008e02000030110000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000120b00000b050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000120b000016050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000120b000017050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"060000005e040000120b000001050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,", -"05000000d6200000e589000001000000,Moga 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,", +"03000000790000001c18000010010000,Mobapad Chitu HD,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"050000004d4f435554452d3035335800,Mocute 053X,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"05000000e80400006e0400001b010000,Mocute 053X M59,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"050000004d4f435554452d3035305800,Mocute 054X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"05000000d6200000e589000001000000,Moga 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,", "05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,", -"05000000d62000007162000001000000,Moga Pro 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,", -"03000000c62400002b89000011010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"05000000c62400002a89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b22,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"05000000c62400001a89000000010000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"03000000250900006688000000010000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,", -"030000006b140000010c000010010000,NACON GC-400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"05000000d62000007162000001000000,Moga Pro 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,", +"03000000c62400002b89000011010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"05000000c62400002a89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b22,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"05000000c62400001a89000000010000,MOGA XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000250900006688000000010000,MP8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,", +"030000005e0400008e02000010020000,MSI GC20 V2,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000f70600000100000000010000,N64 Adaptoid,+rightx:b2,+righty:b1,-rightx:b4,-righty:b5,a:b0,b:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,platform:Linux,", +"030000006b1400000906000014010000,Nacon Asymmetric Wireless PS4 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006b140000010c000010010000,Nacon GC 400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"03000000853200000706000012010000,Nacon GC-100,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"05000000853200000503000000010000,Nacon MG-X Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"0300000085320000170d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"0300000085320000190d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", "030000000d0f00000900000010010000,Natec Genesis P44,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000790000004518000010010000,NEXILUX GAMECUBE Controller Adapter,a:b1,b:b0,x:b2,y:b3,start:b9,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,platform:Linux,", +"030000004f1f00000800000011010000,NeoGeo PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"0300000092120000474e000000010000,NeoGeo X Arcade Stick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b3,y:b2,platform:Linux,", +"03000000790000004518000010010000,Nexilux GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", "030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Linux,", "060000007e0500003713000000000000,Nintendo 3DS,a:b0,b:b1,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,", -"060000007e0500000820000000000000,Nintendo Combined Joy-Cons (joycond),a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,", "030000007e0500003703000000016800,Nintendo GameCube Controller,a:b0,b:b2,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b1,y:b3,platform:Linux,", "03000000790000004618000010010000,Nintendo GameCube Controller Adapter,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5~,righty:a2~,start:b9,x:b2,y:b3,platform:Linux,", -"050000007e0500000620000001800000,Nintendo Switch Left Joy-Con,a:b9,b:b8,back:b5,leftshoulder:b2,leftstick:b6,leftx:a1,lefty:a0~,rightshoulder:b4,start:b0,x:b7,y:b10,platform:Linux,", -"030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,", -"050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"050000007e0500000920000001800000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,", -"050000007e0500000720000001800000,Nintendo Switch Right Joy-Con,a:b1,b:b2,back:b9,leftshoulder:b4,leftstick:b10,leftx:a1~,lefty:a0~,rightshoulder:b6,start:b8,x:b0,y:b3,platform:Linux,", -"050000007e0500001720000001000000,Nintendo Switch SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux,", -"050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,", -"05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux,", +"030000007e0500006920000011010000,Nintendo Switch 2 Pro Controller,a:b0,b:b1,back:b14,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b12,leftstick:b15,lefttrigger:b13,leftx:a0,lefty:a1~,misc1:b17,misc2:b20,paddle1:b18,paddle2:b19,rightshoulder:b4,rightstick:b7,righttrigger:b5,rightx:a2,righty:a3~,start:b6,x:b2,y:b3,platform:Linux,", +"060000004e696e74656e646f20537700,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,", +"060000007e0500000620000000000000,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,", +"060000007e0500000820000000000000,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,", +"050000004c69632050726f20436f6e00,Nintendo Switch Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"050000007e0500000620000001800000,Nintendo Switch Left Joy-Con,a:b16,b:b15,back:b4,leftshoulder:b6,leftstick:b12,leftx:a1,lefty:a0~,rightshoulder:b8,start:b9,x:b14,y:b17,platform:Linux,", +"030000007e0500000920000000026803,Nintendo Switch Pro Controller,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Linux,", +"030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,", +"050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"050000007e0500000920000001800000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,", +"050000007e0500000720000001800000,Nintendo Switch Right Joy-Con,a:b1,b:b2,back:b9,leftshoulder:b4,leftstick:b10,leftx:a1~,lefty:a0,rightshoulder:b6,start:b8,x:b0,y:b3,platform:Linux,", +"05000000010000000100000003000000,Nintendo Wii Remote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"050000007e0500003003000001000000,Nintendo Wii U Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,", +"050000005a1d00000218000003000000,Nokia GC 5000,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"030000000d0500000308000010010000,Nostromo n45 Dual Analog,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux,", +"030000007e0500007320000011010000,NSO GameCube Controller,a:b1,b:b3,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b13,lefttrigger:b12,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b4,rightx:a2,righty:a3~,start:b6,x:b0,y:b2,platform:Linux,", +"030000007e0500001920000011810000,NSO N64 Controller,+rightx:b2,+righty:b3,-rightx:b4,-righty:b10,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b5,rightshoulder:b7,righttrigger:b9,start:b11,platform:Linux,", +"050000007e0500001920000001000000,NSO N64 Controller,+rightx:b8,+righty:b7,-rightx:b3,-righty:b2,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Linux,", +"050000007e0500001920000001800000,NSO N64 Controller,+rightx:b2,+righty:b3,-rightx:b4,-righty:b10,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b5,rightshoulder:b7,righttrigger:b9,start:b11,platform:Linux,", +"030000007e0500001e20000011810000,NSO Sega Genesis Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,misc1:b3,rightshoulder:b2,righttrigger:b4,start:b5,platform:Linux,", +"030000007e0500001720000011810000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,", +"050000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b8,start:b10,x:b3,y:b2,platform:Linux,", +"050000007e0500001720000001800000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,", "03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", -"03000000550900001472000011010000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux,", -"05000000550900001472000001000000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux,", +"03000000550900001472000011010000,NVIDIA Controller,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux,", +"05000000550900001472000001000000,NVIDIA Controller,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux,", +"030000004b120000014d000000010000,NYKO Airflo EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", "03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"19000000010000000100000001010000,odroidgo2_joypad,a:b1,b:b0,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,guide:b10,leftshoulder:b4,leftstick:b12,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b14,start:b15,x:b2,y:b3,platform:Linux,", -"19000000010000000200000011000000,odroidgo2_joypad_v11,a:b1,b:b0,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b12,leftshoulder:b4,leftstick:b14,lefttrigger:b13,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b16,start:b17,x:b2,y:b3,platform:Linux,", -"030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", -"03000000c0160000dc27000001010000,OnyxSoft Dual JoyDivision,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:Linux,", -"05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,", -"05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,", -"03000000830500005020000010010000,Padix Co. Ltd. Rockfire PSX/USB Bridge,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b2,y:b3,platform:Linux,", -"03000000790000001c18000011010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"030000006f0e0000b802000001010000,PDP AFTERGLOW Wired Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006f0e0000b802000013020000,PDP AFTERGLOW Wired Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"19000000010000000100000001010000,ODROID Go 2,a:b1,b:b0,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,guide:b10,leftshoulder:b4,leftstick:b12,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b14,start:b15,x:b2,y:b3,platform:Linux,", +"19000000010000000200000011000000,ODROID Go 2,a:b1,b:b0,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b12,leftshoulder:b4,leftstick:b14,lefttrigger:b13,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b16,start:b17,x:b2,y:b3,platform:Linux,", +"05000000362800000100000002010000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,", +"05000000362800000100000003010000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,", +"05000000362800000100000004010000,OUYA Controller,a:b0,b:b3,back:b14,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,start:b16,x:b1,y:b2,platform:Linux,", +"03000000830500005020000010010000,Padix Rockfire PlayStation Bridge,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b2,y:b3,platform:Linux,", +"03000000ff1100003133000010010000,PC Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", +"030000006f0e0000b802000001010000,PDP Afterglow Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006f0e0000b802000013020000,PDP Afterglow Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006f0e00008001000011010000,PDP CO. LTD. Faceoff Wired Pro Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006f0e0000d702000006640000,PDP Black Camo Wired Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b13,dpup:b14,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000006f0e00003101000000010000,PDP EA Sports Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006f0e00008501000011010000,PDP Fightpad Pro Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", "030000006f0e0000c802000012010000,PDP Kingdom Hearts Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000006f0e00008701000011010000,PDP Rock Candy Wired Controller for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"030000006f0e00000901000011010000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"030000006f0e0000a802000023020000,PDP Wired Controller for Xbox One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"030000006f0e00008501000011010000,PDP Wired Fight Pad Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"0500000049190000030400001b010000,PG-9099,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"05000000491900000204000000000000,PG-9118,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"030000004c050000da0c000011010000,Playstation Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", -"030000004c0500003713000011010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", +"030000006f0e00002801000011010000,PDP PS3 Rock Candy Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006f0e00000901000011010000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"030000006f0e00002f01000011010000,PDP Wired PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000ad1b000004f9000000010000,PDP Xbox 360 Versus Fighting,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux,", +"030000006f0e0000f102000000000000,PDP Xbox Atomic,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006f0e0000a802000023020000,PDP Xbox One Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"030000006f0e0000a702000023020000,PDP Xbox One Raven Black,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006f0e0000d802000006640000,PDP Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006f0e0000ef02000007640000,PDP Xbox Series Kinetic Wired Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000d62000000540000001010000,PowerA Advantage Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000d620000011a7000011010000,PowerA Core Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000dd62000015a7000011010000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000d620000012a7000011010000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000d62000000140000001010000,PowerA Fusion Pro 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000dd62000016a7000000000000,PowerA Fusion Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000c62400001a53000000010000,PowerA Mini Pro Ex,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000d620000013a7000011010000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "03000000d62000006dca000011010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000d62000000228000001010000,PowerA Wired Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000c62400001a58000001010000,PowerA Xbox One Cabled,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000c62400001a54000001010000,PowerA Xbox One Mini Wired Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000d620000014a7000011010000,PowerA Spectra Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000c62400001a58000001010000,PowerA Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000d62000000220000001010000,PowerA Xbox One Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux,", +"03000000d62000000228000001010000,PowerA Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000c62400001a54000001010000,PowerA Xbox One Mini Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000d62000000240000001010000,PowerA Xbox One Spectra Infinity,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000d62000000520000050010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000d62000000b20000001010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000d62000000f20000001010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000006d040000d2ca000011010000,Precision Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000250900000017000010010000,PS/SS/N64 Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b5,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2~,righty:a3,start:b8,platform:Linux,", "03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", "03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", "030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", "030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", "030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"030000005f1400003102000010010000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "030000006f0e00001402000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000008f0e00000300000010010000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "050000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", @@ -854,149 +1648,254 @@ const char* _glfwDefaultMappings[] = "050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", "05000000504c415953544154494f4e00,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", "060000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", -"030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", "030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", -"030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", "030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", -"030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", "030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", -"03000000c01100000140000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000c01100000140000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", "050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", "050000004c050000c405000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", -"050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", "050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", "050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", -"030000004c050000e60c000011010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000ff000000cb01000010010000,PSP,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,", -"03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux,", -"030000009b2800004200000001010000,Raphnet Technologies Dual NES to USB v2.0,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Linux,", -"030000009b2800003200000001010000,Raphnet Technologies GC/N64 to USB v3.4,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,", -"030000009b2800006000000001010000,Raphnet Technologies GC/N64 to USB v3.6,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,", -"030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,", +"0300004b4c0500005f0e000011010000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"030000004c050000e60c000011010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"030000004c050000e60c000011810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"030000004c050000f20d000011010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"030000004c050000f20d000011810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"050000004c050000e60c000000810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"050000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"050000004c050000f20d000000810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"03000000300f00001211000011010000,Qanba Arcade Joystick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux,", +"03000000222c00000225000011010000,Qanba Dragon Arcade Joystick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000222c00000025000011010000,Qanba Dragon Arcade Joystick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"03000000222c00001220000011010000,Qanba Drone 2 Arcade Joystick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000222c00001020000011010000,Qanba Drone 2 Arcade Joystick PS5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000222c00000020000011010000,Qanba Drone Arcade PS4 Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"03000000300f00001210000010010000,Qanba Joystick Plus,a:b0,b:b1,back:b8,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,start:b9,x:b2,y:b3,platform:Linux,", +"03000000222c00000223000011010000,Qanba Obsidian Arcade Joystick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000222c00000023000011010000,Qanba Obsidian Arcade Joystick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"030000009b2800000300000001010000,Raphnet 4nes4snes,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,", +"030000009b2800004200000001010000,Raphnet Dual NES Adapter,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Linux,", +"0300132d9b2800006500000000000000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,", +"0300132d9b2800006500000001010000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,", +"030000009b2800003200000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,", +"030000009b2800006000000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,", +"030000009b2800006100000001010000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Linux,", +"030000009b2800006400000001010000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Linux,", +"030000009b2800008000000020020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Linux,", +"030000009b2800008000000001010000,Raphnet Wii Classic Adapter V3,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Linux,", +"03000000f8270000bf0b000011010000,Razer Kishi,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000008916000000fd000024010000,Razer Onza Tournament Edition,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000321500000204000011010000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"03000000321500000104000011010000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000321500000810000011010000,Razer Panthera Evo Arcade Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000321500000010000011010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000321500000204000011010000,Razer Panthera PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000321500000104000011010000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"03000000321500000810000011010000,Razer Panthera PS4 Evo Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"03000000321500000010000011010000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "03000000321500000507000000010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"03000000321500000011000011010000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"05000000321500000a10000001000000,Razer Raiju Tournament Edition,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000321500000011000011010000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", "030000008916000000fe000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000c6240000045d000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", "050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", "0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000790000001100000010010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,", +"03000000321500000b10000011010000,Razer Wolverine PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"0300000032150000140a000001010000,Razer Wolverine Ultimate Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000000d0f0000c100000010010000,Retro Bit Legacy16,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b12,leftshoulder:b4,lefttrigger:b6,misc1:b13,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f0000c100000072056800,Retro Bit Legacy16,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b5,leftshoulder:b9,lefttrigger:+a4,misc1:b11,rightshoulder:b10,righttrigger:+a5,start:b6,x:b3,y:b2,platform:Linux,", +"03000000790000001100000010010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Linux,", +"0300000003040000c197000011010000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,", +"190000004b4800000111000000010000,RetroGame Joypad,a:b1,b:b0,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", "0300000081170000990a000001010000,Retronic Adapter,a:b0,leftx:a0,lefty:a1,platform:Linux,", "0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,", +"00000000526574726f53746f6e653200,RetroStone 2 Controller,a:b1,b:b0,back:b10,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,righttrigger:b9,start:b11,x:b4,y:b3,platform:Linux,", +"03000000341200000400000000010000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,platform:Linux,", "030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "030000006b140000130d000011010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "030000006f0e00001f01000000010000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006f0e00008701000011010000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000006f0e00001e01000011010000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000c6240000fefa000000010000,Rock Candy Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000006f0e00004601000001010000,Rock Candy Xbox One Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", +"030000006f0e00001311000011010000,Saffun Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b0,platform:Linux,", +"03000000a306000023f6000011010000,Saitek Cyborg PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", "03000000a30600001005000000010000,Saitek P150,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b2,righttrigger:b5,x:b3,y:b4,platform:Linux,", "03000000a30600000701000000010000,Saitek P220,a:b2,b:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,x:b0,y:b1,platform:Linux,", -"03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b0,y:b1,platform:Linux,", -"03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux,", +"03000000a30600000cff000010010000,Saitek P2500 Force Rumble,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b0,y:b1,platform:Linux,", +"03000000a30600000d5f000010010000,Saitek P2600,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux,", +"03000000a30600000c04000011010000,Saitek P2900,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux,", +"03000000a306000018f5000010010000,Saitek P3200 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", "03000000300f00001201000010010000,Saitek P380,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,", "03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,", -"03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux,", -"03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", -"03000000a306000020f6000011010000,Saitek PS2700 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", +"03000000a30600000b04000000010000,Saitek P990 Dual Analog,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux,", +"03000000a306000020f6000011010000,Saitek PS2700 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", +"05000000e804000000a000001b010000,Samsung EIGP20,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "03000000d81d00000e00000010010000,Savior,a:b0,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b9,x:b4,y:b5,platform:Linux,", -"03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"03000000632500007505000010010000,SHANWAN PS3/PC Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"03000000bc2000000055000010010000,ShanWan PS3/PC Wired GamePad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"030000005f140000c501000010010000,SHANWAN Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"03000000632500002305000010010000,ShanWan USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"03000000341a00000908000010010000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"030000004c050000e60c000011810000,Sony DualSense,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", -"050000004c050000e60c000000810000,Sony DualSense ,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", -"03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,", -"030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000d11800000094000011010000,Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", +"03000000952e00004b43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux,", +"03000000952e00004d43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux,", +"03000000952e00004e43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux,", +"03000000a30c00002500000011010000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Linux,", +"03000000790000001100000011010000,Sega Saturn,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Linux,", +"03000000790000002201000011010000,Sega Saturn,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,", +"03000000b40400000a01000000010000,Sega Saturn,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux,", +"03000000632500002305000010010000,ShanWan Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", +"03000000632500002605000010010000,Shanwan Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000632500007505000010010000,Shanwan Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", +"03000000bc2000000055000010010000,Shanwan Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000f025000021c1000010010000,Shanwan Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", +"03000000341a00000908000010010000,SL6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"030000004b2900000430000011000000,Snakebyte Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"050000004c050000cc09000001000000,Sony DualShock 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"03000000666600006706000000010000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux,", +"030000004c050000da0c000011010000,Sony PlayStation Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", +"03000000d9040000160f000000010000,Sony PlayStation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", +"03000000ff000000cb01000010010000,Sony PlayStation Portable,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,", +"030000004c0500003713000011010000,Sony PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", +"03000000250900000500000000010000,Sony PS2 pad with SmartJoy Adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,", +"030000005e0400008e02000073050000,Speedlink Torid,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400008e02000020200000,SpeedLink Xeox Pro Analog,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", +"03000000de2800000112000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:+a5,dpleft:-a4,dpright:+a4,dpup:-a5,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,", "03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", -"03000000de2800000211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,", +"03000000de2800000211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b16,paddle2:b15,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,", "03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", -"03000000de2800004211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,", +"03000000de2800004211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,paddle1:b16,paddle2:b15,rightshoulder:b7,righttrigger:a6,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,", "03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", "05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", "05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", +"03000000de2800000512000010010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,platform:Linux,", +"03000000de2800000512000011010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,misc1:b2,paddle1:b21,paddle2:b20,paddle3:b23,paddle4:b22,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,platform:Linux,", "03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b10,guide:b11,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Linux,", "03000000381000003014000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000381000003114000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "0500000011010000311400001b010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b32,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "05000000110100001914000009010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"03000000ad1b000038f0000090040000,Street Fighter IV FightStick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000003b07000004a1000000010000,Suncom SFX Plus for USB,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Linux,", +"03000000ad1b000038f0000090040000,Street Fighter IV Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000003b07000004a1000000010000,Suncom SFX Plus,a:b0,b:b2,back:b7,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Linux,", +"030000001f08000001e4000010010000,Super Famicom Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,", "03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,", -"0300000000f00000f100000000010000,Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,", -"03000000457500002211000010010000,SZMY-POWER CO. LTD. GAMEPAD,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"030000008f0e00000d31000010010000,SZMY-POWER CO. LTD. GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000008f0e00001431000010010000,SZMY-POWER CO. LTD. PS3 gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", +"0300000000f00000f100000000010000,Super RetroPort,a:b1,b:b5,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,", +"030000008f0e00000d31000010010000,SZMY Power 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000457500000401000011010000,SZMY Power DS4 Wired Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000457500002211000010010000,SZMY Power Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", +"030000008f0e00001431000010010000,SZMY Power PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000e40a00000307000011010000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Linux,", +"03000000e40a00000207000011010000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Linux,", +"03000000ba2200000701000001010000,Technology Innovation PS2 Adapter,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b2,platform:Linux,", +"03000000790000001c18000011010000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000591c00002400000010010000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,", +"03000000591c00002600000010010000,THEGamepad,a:b2,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Linux,", +"030000004f04000015b3000001010000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", "030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", -"030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"030000004f0400000ed0000011010000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000004f04000020b3000010010000,Thrustmaster Dual Trigger,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", +"030000004f04000023b3000000010000,Thrustmaster Dual Trigger PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000004f0400000ed0000011010000,Thrustmaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "03000000b50700000399000000010000,Thrustmaster Firestorm Digital 2,a:b2,b:b4,back:b11,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b0,righttrigger:b9,start:b1,x:b3,y:b5,platform:Linux,", "030000004f04000003b3000010010000,Thrustmaster Firestorm Dual Analog 2,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b9,rightx:a2,righty:a3,x:b1,y:b3,platform:Linux,", "030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux,", -"030000004f04000026b3000002040000,Thrustmaster Gamepad GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000c6240000025b000002020000,Thrustmaster GPX Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000004f04000007d0000000010000,Thrustmaster T Mini Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", -"030000004f04000012b3000010010000,Thrustmaster vibrating gamepad,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", -"03000000bd12000015d0000010010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,", -"03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,", +"030000004f04000004b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", +"030000004f04000026b3000002040000,Thrustmaster GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000c6240000025b000002020000,Thrustmaster GPX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000004f04000008d0000000010000,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000004f04000009d0000000010000,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000004f04000007d0000000010000,Thrustmaster T Mini,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000004f04000012b3000010010000,Thrustmaster Vibrating Gamepad,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", +"03000000571d00002000000010010000,Tomee SNES Adapter,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,", +"03000000bd12000015d0000010010000,Tomee SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,", +"03000000d814000007cd000011010000,Toodles 2008 Chimp PC PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,", "030000005e0400008e02000070050000,Torid,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000c01100000591000011010000,Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", +"03000000680a00000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux,", +"03000000780300000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux,", +"03000000e00d00000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux,", +"03000000f00600000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux,", +"030000005f140000c501000010010000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", +"06000000f51000000870000003010000,Turtle Beach Recon,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000100800000100000010010000,Twin PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", +"03000000151900005678000010010000,Uniplay U6,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", "03000000790000000600000007010000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,", -"03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux,", -"030000006f0e00000302000011010000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"030000006f0e00000702000011010000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", -"05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", -"03000000791d00000103000010010000,Wii Classic Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"050000000d0f0000f600000001000000,Wireless HORIPAD Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e040000a102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux,", -"030000005e040000a102000014010000,Xbox 360 Wireless Receiver (XBOX),a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", +"03000000790000001100000000010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux,", +"03000000790000001a18000011010000,Venom PS4 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000790000001b18000011010000,Venom PS4 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000006f0e00000302000011010000,Victrix Pro Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"030000006f0e00000702000011010000,Victrix Pro Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,", +"05000000ac0500003232000001000000,VR Box Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", +"05000000434f4d4d414e440000000000,VX Gaming Command Series,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"0000000058626f782033363020576900,Xbox 360 Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux,", +"030000005e0400001907000000010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400008e02000010010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400008e02000014010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400009102000007010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000a102000000010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000a102000007010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000a102000030060000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006f0e00001503000000020000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400008e02000000010000,Xbox 360 EasySMX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000a102000014010000,Xbox 360 Receiver,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"0000000058626f782047616d65706100,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400000202000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", +"030000005e0400008e02000072050000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006f0e00001304000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000ffff0000ffff000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", +"030000005e0400000a0b000005040000,Xbox One Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,", "030000005e040000d102000002010000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000ea02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000ea02000001030000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"050000005e040000e002000003090000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"050000005e040000fd02000003090000,Xbox One Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "050000005e040000fd02000030110000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"060000005e040000dd02000003020000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"050000005e040000e302000002090000,Xbox One Elite,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"050000005e040000220b000013050000,Xbox One Elite 2 Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "050000005e040000050b000002090000,Xbox One Elite Series 2,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"030000005e040000ea02000000000000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"030000005e040000ea02000001030000,Xbox One Wireless Controller (Model 1708),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000ea02000011050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000ea02000015050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"060000005e040000ea0200000b050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"060000005e040000ea0200000d050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"060000005e040000ea02000016050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000005e040000120b000001050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000120b000005050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000120b000009050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000120b00000d050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000120b000011050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000120b000014050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000120b000015050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "050000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "050000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", -"030000005e040000120b000005050000,XBox Series pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"030000005e0400008e02000000010000,xbox360 Wireless EasySMX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", -"03000000450c00002043000010010000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", -"03000000ac0500005b05000010010000,Xiaoji Gamesir-G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", -"05000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,", -"03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux,", -"03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000120c0000101e000011010000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000af1e00002400000010010000,Clockwork Pi DevTerm,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b9,x:b3,y:b0,platform:Linux,", +"050000005e040000130b000007050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"050000005e040000130b000009050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"050000005e040000130b000011050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"050000005e040000130b000013050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"050000005e040000130b000015050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"050000005e040000130b000017050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"060000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"060000005e040000120b00000b050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"060000005e040000120b00000d050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"060000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"050000005e040000130b000022050000,Xbox Series X Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"050000005e040000200b000013050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"050000005e040000200b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"050000005e040000220b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000450c00002043000010010000,XEOX SL6556 BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"05000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,", +"03000000c0160000e105000001010000,XinMo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux,", +"030000005e0400008e02000020010000,XInput Adapter,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000120c0000100e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000120c0000101e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000120c0000182e000011010000,Zeroplus PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", #endif // GLFW_BUILD_LINUX_JOYSTICK }; From 8dbacafbe6e78e7fcb93d637945b286b0743e163 Mon Sep 17 00:00:00 2001 From: garrisonhh Date: Thu, 10 Jul 2025 12:48:29 -0400 Subject: [PATCH 080/242] fix overflow in cast for ImageBlurGaussian --- src/rtextures.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 87aa900a3..3c0ed10f7 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -2217,9 +2217,9 @@ void ImageBlurGaussian(Image *image, int blurSize) else if (pixelsCopy1[i].w <= 255.0f) { float alpha = (float)pixelsCopy1[i].w/255.0f; - pixels[i].r = (unsigned char)((float)pixelsCopy1[i].x/alpha); - pixels[i].g = (unsigned char)((float)pixelsCopy1[i].y/alpha); - pixels[i].b = (unsigned char)((float)pixelsCopy1[i].z/alpha); + pixels[i].r = (unsigned char)fminf((float)pixelsCopy1[i].x/alpha, 255.0); + pixels[i].g = (unsigned char)fminf((float)pixelsCopy1[i].y/alpha, 255.0); + pixels[i].b = (unsigned char)fminf((float)pixelsCopy1[i].z/alpha, 255.0); pixels[i].a = (unsigned char) pixelsCopy1[i].w; } } From 9e908e4a76958c6922ceab4e788ccca647e68853 Mon Sep 17 00:00:00 2001 From: PanicTitan <66186142+PanicTitan@users.noreply.github.com> Date: Mon, 14 Jul 2025 00:27:56 -0300 Subject: [PATCH 081/242] Update core_custom_frame_control.c to work properly on web --- examples/core/core_custom_frame_control.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/core/core_custom_frame_control.c b/examples/core/core_custom_frame_control.c index a9b98b039..2e9595e98 100644 --- a/examples/core/core_custom_frame_control.c +++ b/examples/core/core_custom_frame_control.c @@ -61,7 +61,9 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - PollInputEvents(); // Poll input events (SUPPORT_CUSTOM_FRAME_CONTROL) + #ifndef PLATFORM_WEB // NOTE: On non web platforms the PollInputEvents just works before the inputs checks + PollInputEvents(); // Poll input events (SUPPORT_CUSTOM_FRAME_CONTROL) + #endif if (IsKeyPressed(KEY_SPACE)) pause = !pause; @@ -76,6 +78,10 @@ int main(void) if (position >= GetScreenWidth()) position = 0; timeCounter += deltaTime; // We count time (seconds) } + + #ifdef PLATFORM_WEB // NOTE: On web platform for some reason the PollInputEvents only works after the inputs check, so just call it after check all your inputs (on web) + PollInputEvents(); // Poll input events (SUPPORT_CUSTOM_FRAME_CONTROL) + #endif //---------------------------------------------------------------------------------- // Draw From 0c69c43c3a125f2850b0ae3af6144577a2e7ad86 Mon Sep 17 00:00:00 2001 From: sir-irk Date: Sun, 13 Jul 2025 22:37:41 -0500 Subject: [PATCH 082/242] fix ImageDrawLineEx to be able to draw even numbered thicknesses --- src/rtextures.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 3c0ed10f7..a153bb318 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3565,34 +3565,39 @@ void ImageDrawLineEx(Image *dst, Vector2 start, Vector2 end, int thick, Color co int dx = x2 - x1; int dy = y2 - y1; - // Draw the main line between (x1, y1) and (x2, y2) - ImageDrawLine(dst, x1, y1, x2, y2, color); - // Determine if the line is more horizontal or vertical if ((dx != 0) && (abs(dy/dx) < 1)) { // Line is more horizontal - // Calculate half the width of the line - int wy = (thick - 1)*(int)sqrtf((float)(dx*dx + dy*dy))/(2*abs(dx)); + int wy = thick - 1; - // Draw additional lines above and below the main line - for (int i = 1; i <= wy; i++) + //Draw the main line and lower half + for (int i = 0; i <= ((wy+1)/2); i++) + { + ImageDrawLine(dst, x1, y1 + i, x2, y2 + i, color); // Draw below the main line + } + + //Draw the upper half + for (int i = 1; i <= (wy/2); i++) { ImageDrawLine(dst, x1, y1 - i, x2, y2 - i, color); // Draw above the main line - ImageDrawLine(dst, x1, y1 + i, x2, y2 + i, color); // Draw below the main line } } else if (dy != 0) { // Line is more vertical or perfectly horizontal - // Calculate half the width of the line - int wx = (thick - 1)*(int)sqrtf((float)(dx*dx + dy*dy))/(2*abs(dy)); + int wx = thick - 1; - // Draw additional lines to the left and right of the main line - for (int i = 1; i <= wx; i++) + //Draw the main line and right half + for (int i = 0; i <= ((wx+1)/2); i++) + { + ImageDrawLine(dst, x1 + i, y1, x2 + i, y2, color); // Draw right of the main line + } + + //Draw the the left half + for (int i = 1; i <= (wx/2); i++) { ImageDrawLine(dst, x1 - i, y1, x2 - i, y2, color); // Draw left of the main line - ImageDrawLine(dst, x1 + i, y1, x2 + i, y2, color); // Draw right of the main line } } } From 9f03d7c426239fb6065cff76852caa417edff00f Mon Sep 17 00:00:00 2001 From: sir-irk Date: Sun, 13 Jul 2025 23:02:02 -0500 Subject: [PATCH 083/242] fixing comments --- src/rtextures.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index a153bb318..72ad68c40 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3569,15 +3569,17 @@ void ImageDrawLineEx(Image *dst, Vector2 start, Vector2 end, int thick, Color co if ((dx != 0) && (abs(dy/dx) < 1)) { // Line is more horizontal + + // How many additional lines to draw int wy = thick - 1; - //Draw the main line and lower half + // Draw the main line and lower half for (int i = 0; i <= ((wy+1)/2); i++) { ImageDrawLine(dst, x1, y1 + i, x2, y2 + i, color); // Draw below the main line } - //Draw the upper half + // Draw the upper half for (int i = 1; i <= (wy/2); i++) { ImageDrawLine(dst, x1, y1 - i, x2, y2 - i, color); // Draw above the main line @@ -3586,6 +3588,8 @@ void ImageDrawLineEx(Image *dst, Vector2 start, Vector2 end, int thick, Color co else if (dy != 0) { // Line is more vertical or perfectly horizontal + + // How many additional lines to draw int wx = thick - 1; //Draw the main line and right half @@ -3594,7 +3598,7 @@ void ImageDrawLineEx(Image *dst, Vector2 start, Vector2 end, int thick, Color co ImageDrawLine(dst, x1 + i, y1, x2 + i, y2, color); // Draw right of the main line } - //Draw the the left half + // Draw the left half for (int i = 1; i <= (wx/2); i++) { ImageDrawLine(dst, x1 - i, y1, x2 - i, y2, color); // Draw left of the main line From fce3102f1c98c8e8758df92e2f07fcdc39470d15 Mon Sep 17 00:00:00 2001 From: Jeremy Montgomery Date: Mon, 14 Jul 2025 00:11:17 -0500 Subject: [PATCH 084/242] Remove excess comments --- src/rtextures.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 72ad68c40..02845b40b 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3576,13 +3576,13 @@ void ImageDrawLineEx(Image *dst, Vector2 start, Vector2 end, int thick, Color co // Draw the main line and lower half for (int i = 0; i <= ((wy+1)/2); i++) { - ImageDrawLine(dst, x1, y1 + i, x2, y2 + i, color); // Draw below the main line + ImageDrawLine(dst, x1, y1 + i, x2, y2 + i, color); } // Draw the upper half for (int i = 1; i <= (wy/2); i++) { - ImageDrawLine(dst, x1, y1 - i, x2, y2 - i, color); // Draw above the main line + ImageDrawLine(dst, x1, y1 - i, x2, y2 - i, color); } } else if (dy != 0) @@ -3595,13 +3595,13 @@ void ImageDrawLineEx(Image *dst, Vector2 start, Vector2 end, int thick, Color co //Draw the main line and right half for (int i = 0; i <= ((wx+1)/2); i++) { - ImageDrawLine(dst, x1 + i, y1, x2 + i, y2, color); // Draw right of the main line + ImageDrawLine(dst, x1 + i, y1, x2 + i, y2, color); } // Draw the left half for (int i = 1; i <= (wx/2); i++) { - ImageDrawLine(dst, x1 - i, y1, x2 - i, y2, color); // Draw left of the main line + ImageDrawLine(dst, x1 - i, y1, x2 - i, y2, color); } } } From 901afadbf1c629aabb66749cc2c72301d3729321 Mon Sep 17 00:00:00 2001 From: Daniel Vincent Horgan Date: Mon, 14 Jul 2025 15:36:02 +0200 Subject: [PATCH 085/242] fix warning log macro in rlgl --- src/rlgl.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 2562ad45d..b6703c923 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1885,7 +1885,7 @@ void rlActiveDrawBuffers(int count) if (count > 0) { - if (count > 8) TRACELOG(LOG_WARNING, "GL: Max color buffers limited to 8"); + if (count > 8) TRACELOG(RL_LOG_WARNING, "GL: Max color buffers limited to 8"); else { unsigned int buffers[8] = { @@ -1902,7 +1902,7 @@ void rlActiveDrawBuffers(int count) glDrawBuffers(count, buffers); } } - else TRACELOG(LOG_WARNING, "GL: One color buffer active by default"); + else TRACELOG(RL_LOG_WARNING, "GL: One color buffer active by default"); #endif } @@ -2219,10 +2219,10 @@ static void GLAPIENTRY rlDebugMessageCallback(GLenum source, GLenum type, GLuint default: break; } - TRACELOG(LOG_WARNING, "GL: OpenGL debug message: %s", message); - TRACELOG(LOG_WARNING, " > Type: %s", msgType); - TRACELOG(LOG_WARNING, " > Source = %s", msgSource); - TRACELOG(LOG_WARNING, " > Severity = %s", msgSeverity); + TRACELOG(RL_LOG_WARNING, "GL: OpenGL debug message: %s", message); + TRACELOG(RL_LOG_WARNING, " > Type: %s", msgType); + TRACELOG(RL_LOG_WARNING, " > Source = %s", msgSource); + TRACELOG(RL_LOG_WARNING, " > Severity = %s", msgSeverity); } #endif @@ -4178,7 +4178,7 @@ unsigned int rlCompileShader(const char *shaderCode, int type) RL_FREE(log); } - // Unload object allocated by glCreateShader(), + // Unload object allocated by glCreateShader(), // despite failing in the compilation process glDeleteShader(shader); shader = 0; From 44ebf32389997677570b4d16aea48bf244fb441e Mon Sep 17 00:00:00 2001 From: backspaceoverload <220821874+backspaceoverload@users.noreply.github.com> Date: Tue, 15 Jul 2025 00:52:47 -0300 Subject: [PATCH 086/242] Fix HalfToFloat Mantissa hex value --- src/rtextures.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 02845b40b..90938fe6a 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3576,32 +3576,32 @@ void ImageDrawLineEx(Image *dst, Vector2 start, Vector2 end, int thick, Color co // Draw the main line and lower half for (int i = 0; i <= ((wy+1)/2); i++) { - ImageDrawLine(dst, x1, y1 + i, x2, y2 + i, color); + ImageDrawLine(dst, x1, y1 + i, x2, y2 + i, color); } // Draw the upper half for (int i = 1; i <= (wy/2); i++) { - ImageDrawLine(dst, x1, y1 - i, x2, y2 - i, color); + ImageDrawLine(dst, x1, y1 - i, x2, y2 - i, color); } } else if (dy != 0) { // Line is more vertical or perfectly horizontal - + // How many additional lines to draw int wx = thick - 1; //Draw the main line and right half for (int i = 0; i <= ((wx+1)/2); i++) { - ImageDrawLine(dst, x1 + i, y1, x2 + i, y2, color); + ImageDrawLine(dst, x1 + i, y1, x2 + i, y2, color); } // Draw the left half for (int i = 1; i <= (wx/2); i++) { - ImageDrawLine(dst, x1 - i, y1, x2 - i, y2, color); + ImageDrawLine(dst, x1 - i, y1, x2 - i, y2, color); } } } @@ -5409,7 +5409,7 @@ static float HalfToFloat(unsigned short x) } uni; const unsigned int e = (x & 0x7c00) >> 10; // Exponent - const unsigned int m = (x & 0x03cc) << 13; // Mantissa + const unsigned int m = (x & 0x03ff) << 13; // Mantissa uni.fm = (float)m; const unsigned int v = uni.ui >> 23; // Evil log2 bit hack to count leading zeros in denormalized format uni.ui = (x & 0x8000) << 16 | (e != 0)*((e + 112) << 23 | m) | ((e == 0)&(m != 0))*((v - 37) << 23 | ((m << (150 - v)) & 0x007fe000)); // sign : normalized : denormalized From 85bf04c7bf01b226fb413a1bdcebfa271e83d216 Mon Sep 17 00:00:00 2001 From: sleeptightAnsiC <91839286+sleeptightAnsiC@users.noreply.github.com> Date: Tue, 15 Jul 2025 07:53:50 +0200 Subject: [PATCH 087/242] [rl_gputex] fix: decouple logging and memory allocation from Raylib - make sure that src/external/rl_gputex.h uses it's own macros for printing warnings and allocating memory - add few additional macros in order to decouple rl_gputex from headers that are being included by Raylib (e.g. *_MEMSET, *_NULL and so on) - make sure that rl_gputex uses RL_*ALLOC, TRACELOG and RLAPI macros when (and only when) being included by src/rtextures.c - replace LOG() and RL_GPUTEX_SHOW_LOG_INFO macros with: RL_GPUTEX_WARN and RL_GPUTEX_SHOW_WARN_INFO (this is a breaking change, but it was broken and unusable anyway, see: https://github.com/raysan5/raylib/issues/5039#issuecomment-3065732596 ) - fixes issue all issues mentioned in: https://github.com/raysan5/raylib/issues/5039#issuecomment-3065732596 - remove logging upon successfully saving a file; Raylib does this anyway and we want rl_gputex to only print WARNings upon failures; see: https://discord.com/channels/426912293134270465/541710686468702250/1394406734306480352 - add additional condition when saving a file; now checks for both fwrite() and fclose() failures; before it would warn about both, but only return result based on fclose() - add some notes about the current state of compiling rl_gputex without depending on Raylib (spoiler: it's still broken...) - bump rl_gputex version to 1.1 since this is a potential breaking change (only for people attempting to use it without Raylib) Fixes: https://github.com/raysan5/raylib/issues/5039 Reference: https://discord.com/channels/426912293134270465/541710686468702250/1394403611852931255 --- src/external/rl_gputex.h | 225 +++++++++++++++++++++++++++------------ src/rtextures.c | 12 +++ 2 files changed, 168 insertions(+), 69 deletions(-) diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index 4043a9239..4b4e9beb4 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rl_gputex v1.0 - GPU compressed textures loading and saving +* rl_gputex v1.1 - GPU compressed textures loading and saving * * DESCRIPTION: * @@ -10,8 +10,15 @@ * Note that some file formats (DDS, PVR, KTX) also support uncompressed data storage. * In those cases data is loaded uncompressed and format is returned. * +* FIXME: This library still depends on Raylib due to the following reasons: +* - rl_save_ktx_to_memory() requires rlGetGlTextureFormats() from rlgl.h +* though this is not a problem, if you don't need KTX support +* - all constants starting with PIXELFORMAT_* come from raylib.h +* this one is pretty hard to change, because it depends on exact ABI of 'enum PixelFormat' +* however, you can copy-paste said enum or include the header without Raylib sources +* - (probably) few minor issues that are hidden by Raylib inclusion and haven't been spotted yet +* * TODO: -* - Implement raylib function: rlGetGlTextureFormats(), required by rl_save_ktx_to_memory() * - Review rl_load_ktx_from_memory() to support KTX v2.2 specs * * CONFIGURATION: @@ -23,6 +30,40 @@ * #define RL_GPUTEX_SUPPORT_ASTC * Define desired file formats to be supported * +* #define RL_GPUTEX_SHOW_WARN_INFO +* Define, if you wish to see warnings generated by the library +* This will include unless you provide your own RL_GPUTEX_WARN +* #define RL_GPUTEX_WARN +* Define, if you wish to provide your own warning function +* Make sure that this macro puts newline character '\n' at the end +* +* #define RL_GPUTEX_MALLOC +* #define RL_GPUTEX_CALLOC +* #define RL_GPUTEX_REALLOC +* #define RL_GPUTEX_FREE +* Define those macros in order to provide your own libc-compliant allocator; +* not doing so will include +* If you're providing any of those, you must provide ALL of them, +* otherwise the code will (most likely) crash... +* +* #define RL_GPUTEX_NULL +* Define in order to provide your own libc-compliant NULL pointer; +* not doing so will include +* +* #define RL_GPUTEX_MEMCPY +* Define in order to provide your own libc-compliant 'memcpy' function; +* not doing so will include +* +* #define RLGPUTEXAPI +* Define to compiler-specific intrinsic, if you wish to export public functions +* There is no need to do so when statically linking +* +* VERSIONS HISTORY: +* 1.0 (17-Sep-2022) First version has been created by migrating part of compressed-texture-loading +* functionality from Raylib src/rtextures.c into self-contained library +* 1.1 (15-Jul-2025) Several minor fixes related to specific image formats; some work has been done +* in order to decouple the library from Raylib by introducing few new macros; library still +* requires Raylib in order to function properly * * LICENSE: zlib/libpng * @@ -48,8 +89,8 @@ #ifndef RL_GPUTEX_H #define RL_GPUTEX_H -#ifndef RLAPI - #define RLAPI // Functions defined as 'extern' by default (implicit specifiers) +#ifndef RLGPUTEXAPI + #define RLGPUTEXAPI // Functions defined as 'extern' by default (implicit specifiers) #endif //---------------------------------------------------------------------------------- @@ -60,13 +101,13 @@ extern "C" { // Prevents name mangling of functions #endif // Load image data from memory data files -RLAPI void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips); -RLAPI void *rl_load_pkm_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips); -RLAPI void *rl_load_ktx_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips); -RLAPI void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips); -RLAPI void *rl_load_astc_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips); +RLGPUTEXAPI void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips); +RLGPUTEXAPI void *rl_load_pkm_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips); +RLGPUTEXAPI void *rl_load_ktx_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips); +RLGPUTEXAPI void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips); +RLGPUTEXAPI void *rl_load_astc_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips); -RLAPI int rl_save_ktx_to_memory(const char *fileName, void *data, int width, int height, int format, int mipmaps); // Save image data as KTX file +RLGPUTEXAPI int rl_save_ktx_to_memory(const char *fileName, void *data, int width, int height, int format, int mipmaps); // Save image data as KTX file #if defined(__cplusplus) } @@ -83,13 +124,57 @@ RLAPI int rl_save_ktx_to_memory(const char *fileName, void *data, int width, int #if defined(RL_GPUTEX_IMPLEMENTATION) -// Simple log system to avoid RPNG_LOG() calls if required +#if defined(RL_GPUTEX_SHOW_WARN_INFO) && !defined(RL_GPUTEX_WARN) + #include // Required for: printf() +#endif + +#if !defined(RL_GPUTEX_MALLOC) || !defined(RL_GPUTEX_NULL) + #include // Required for: NULL, malloc(), calloc(), free(), realloc() +#endif + +#if !defined(RL_GPUTEX_MEMCPY) + #include // Required for: memcpy() +#endif + +#if defined(RL_GPUTEX_SUPPORT_KTX) && !defined(RAYLIB_H) + #error "FIXME: RL_GPUTEX_SUPPORT_KTX require Raylib function: rlGetGlTextureFormats()" +#endif + +#if defined(RL_GPUTEX_MALLOC) || defined(RL_GPUTEX_CALLOC) || defined(RL_GPUTEX_REALLOC) || defined(RL_GPUTEX_FREE) + #if !defined(RL_GPUTEX_MALLOC) || !defined(RL_GPUTEX_CALLOC) || !defined(RL_GPUTEX_REALLOC) || !defined(RL_GPUTEX_FREE) + #error "You must provide all RL_GPUTEX_* allocating functions - not just few!" + #endif +#endif + +#if !defined(RL_GPUTEX_MALLOC) + #define RL_GPUTEX_MALLOC(sz) malloc(sz) + #define RL_GPUTEX_CALLOC(num, sz) calloc(num, sz) + #define RL_GPUTEX_REALLOC(ptr, sz) realloc(ptr, sz) + #define RL_GPUTEX_FREE(ptr) free(ptr) +#endif + +#if !defined(RL_GPUTEX_NULL) + #define RL_GPUTEX_NULL NULL +#endif + +#if !defined(RL_GPUTEX_MEMCPY) + #define RL_GPUTEX_MEMCPY(dest, src, num) memcpy(dest, src, num) +#endif + +// Simple warning system to avoid RPNG_LOG() calls if required // NOTE: Avoiding those calls, also avoids const strings memory usage -#define RL_GPUTEX_SHOW_LOG_INFO -#if defined(RL_GPUTEX_SHOW_LOG_INFO) && !defined(LOG) -#define LOG(...) printf(__VA_ARGS__) +// WARN: This macro expects that newline character is added automatically +// in order to match the functionality of Raylib's TRACELOG() +#if defined(RL_GPUTEX_SHOW_WARN_INFO) + #if !defined(RL_GPUTEX_WARN) + #define RL_GPUTEX_WARN(...) (void)(printf("RL_GPUTEX: WARNING: " __VA_ARGS__), printf("\n")) + #endif #else -#define LOG(...) + #if defined(RL_GPUTEX_WARN) + // undefine it first in order to supress warnings about macro redefinition + #undef RL_GPUTEX_WARN + #endif + #define RL_GPUTEX_WARN(...) #endif //---------------------------------------------------------------------------------- @@ -105,8 +190,8 @@ static int get_pixel_data_size(int width, int height, int format); // Loading DDS from memory image data (compressed or uncompressed) void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips) { - void *image_data = NULL; // Image data pointer - int image_pixel_size = 0; // Image pixel size + void *image_data = RL_GPUTEX_NULL; // Image data pointer + int image_pixel_size = 0; // Image pixel size unsigned char *file_data_ptr = (unsigned char *)file_data; @@ -153,7 +238,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ unsigned int reserved2; } dds_header; - if (file_data_ptr != NULL) + if (file_data_ptr != RL_GPUTEX_NULL) { // Verify the type of file unsigned char *dds_header_id = file_data_ptr; @@ -161,7 +246,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ if ((dds_header_id[0] != 'D') || (dds_header_id[1] != 'D') || (dds_header_id[2] != 'S') || (dds_header_id[3] != ' ')) { - LOG("WARNING: IMAGE: DDS file data not valid"); + RL_GPUTEX_WARN("DDS file data not valid"); } else { @@ -172,8 +257,8 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ *width = header->width; *height = header->height; - if (*width % 4 != 0) LOG("WARNING: IMAGE: DDS file width must be multiple of 4. Image will not display correctly"); - if (*height % 4 != 0) LOG("WARNING: IMAGE: DDS file height must be multiple of 4. Image will not display correctly"); + if (*width % 4 != 0) RL_GPUTEX_WARN("DDS file width must be multiple of 4. Image will not display correctly"); + if (*height % 4 != 0) RL_GPUTEX_WARN("DDS file height must be multiple of 4. Image will not display correctly"); image_pixel_size = header->width*header->height; @@ -186,9 +271,9 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ { int data_size = image_pixel_size*sizeof(unsigned short); if (header->mipmap_count > 1) data_size = data_size + data_size / 3; - image_data = RL_MALLOC(data_size); + image_data = RL_GPUTEX_MALLOC(data_size); - memcpy(image_data, file_data_ptr, data_size); + RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); *format = PIXELFORMAT_UNCOMPRESSED_R5G6B5; } @@ -198,9 +283,9 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ { int data_size = image_pixel_size*sizeof(unsigned short); if (header->mipmap_count > 1) data_size = data_size + data_size / 3; - image_data = RL_MALLOC(data_size); + image_data = RL_GPUTEX_MALLOC(data_size); - memcpy(image_data, file_data_ptr, data_size); + RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); unsigned char alpha = 0; @@ -218,9 +303,9 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ { int data_size = image_pixel_size*sizeof(unsigned short); if (header->mipmap_count > 1) data_size = data_size + data_size / 3; - image_data = RL_MALLOC(data_size); + image_data = RL_GPUTEX_MALLOC(data_size); - memcpy(image_data, file_data_ptr, data_size); + RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); unsigned char alpha = 0; @@ -240,9 +325,9 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ { int data_size = image_pixel_size*3*sizeof(unsigned char); if (header->mipmap_count > 1) data_size = data_size + data_size / 3; - image_data = RL_MALLOC(data_size); + image_data = RL_GPUTEX_MALLOC(data_size); - memcpy(image_data, file_data_ptr, data_size); + RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8; } @@ -250,9 +335,9 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ { int data_size = image_pixel_size*4*sizeof(unsigned char); if (header->mipmap_count > 1) data_size = data_size + data_size / 3; - image_data = RL_MALLOC(data_size); + image_data = RL_GPUTEX_MALLOC(data_size); - memcpy(image_data, file_data_ptr, data_size); + RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); unsigned char blue = 0; @@ -276,9 +361,9 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ if (header->mipmap_count > 1) data_size = header->pitch_or_linear_size + header->pitch_or_linear_size / 3; else data_size = header->pitch_or_linear_size; - image_data = RL_MALLOC(data_size*sizeof(unsigned char)); + image_data = RL_GPUTEX_MALLOC(data_size*sizeof(unsigned char)); - memcpy(image_data, file_data_ptr, data_size); + RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); switch (header->ddspf.fourcc) { @@ -305,7 +390,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ // PKM is a much simpler file format used mainly to contain a single ETC1/ETC2 compressed image (no mipmaps) void *rl_load_pkm_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips) { - void *image_data = NULL; // Image data pointer + void *image_data = RL_GPUTEX_NULL; // Image data pointer unsigned char *file_data_ptr = (unsigned char *)file_data; @@ -336,13 +421,13 @@ void *rl_load_pkm_from_memory(const unsigned char *file_data, unsigned int file_ // NOTE: The extended width and height are the widths rounded up to a multiple of 4. // NOTE: ETC is always 4bit per pixel (64 bit for each 4x4 block of pixels) - if (file_data_ptr != NULL) + if (file_data_ptr != RL_GPUTEX_NULL) { pkm_header *header = (pkm_header *)file_data_ptr; if ((header->id[0] != 'P') || (header->id[1] != 'K') || (header->id[2] != 'M') || (header->id[3] != ' ')) { - LOG("WARNING: IMAGE: PKM file data not valid"); + RL_GPUTEX_WARN("PKM file data not valid"); } else { @@ -362,9 +447,9 @@ void *rl_load_pkm_from_memory(const unsigned char *file_data, unsigned int file_ int data_size = (*width)*(*height)*bpp/8; // Total data size in bytes - image_data = RL_MALLOC(data_size*sizeof(unsigned char)); + image_data = RL_GPUTEX_MALLOC(data_size*sizeof(unsigned char)); - memcpy(image_data, file_data_ptr, data_size); + RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); if (header->format == 0) *format = PIXELFORMAT_COMPRESSED_ETC1_RGB; else if (header->format == 1) *format = PIXELFORMAT_COMPRESSED_ETC2_RGB; @@ -381,7 +466,7 @@ void *rl_load_pkm_from_memory(const unsigned char *file_data, unsigned int file_ // TODO: Review KTX loading, many things changed! void *rl_load_ktx_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips) { - void *image_data = NULL; // Image data pointer + void *image_data = RL_GPUTEX_NULL; // Image data pointer unsigned char *file_data_ptr = (unsigned char *)file_data; @@ -419,14 +504,14 @@ void *rl_load_ktx_from_memory(const unsigned char *file_data, unsigned int file_ // NOTE: Before start of every mipmap data block, we have: unsigned int data_size - if (file_data_ptr != NULL) + if (file_data_ptr != RL_GPUTEX_NULL) { ktx_header *header = (ktx_header *)file_data_ptr; if ((header->id[1] != 'K') || (header->id[2] != 'T') || (header->id[3] != 'X') || (header->id[4] != ' ') || (header->id[5] != '1') || (header->id[6] != '1')) { - LOG("WARNING: IMAGE: KTX file data not valid"); + RL_GPUTEX_WARN("KTX file data not valid"); } else { @@ -441,9 +526,9 @@ void *rl_load_ktx_from_memory(const unsigned char *file_data, unsigned int file_ int data_size = ((int *)file_data_ptr)[0]; file_data_ptr += sizeof(int); - image_data = RL_MALLOC(data_size*sizeof(unsigned char)); + image_data = RL_GPUTEX_MALLOC(data_size*sizeof(unsigned char)); - memcpy(image_data, file_data_ptr, data_size); + RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); if (header->gl_internal_format == 0x8D64) *format = PIXELFORMAT_COMPRESSED_ETC1_RGB; else if (header->gl_internal_format == 0x9274) *format = PIXELFORMAT_COMPRESSED_ETC2_RGB; @@ -492,7 +577,7 @@ int rl_save_ktx(const char *file_name, void *data, int width, int height, int fo w /= 2; h /= 2; } - unsigned char *file_data = RL_CALLOC(data_size, 1); + unsigned char *file_data = RL_GPUTEX_CALLOC(data_size, 1); unsigned char *file_data_ptr = file_data; ktx_header header = { 0 }; @@ -504,7 +589,7 @@ int rl_save_ktx(const char *file_name, void *data, int width, int height, int fo const char ktx_identifier[12] = { 0xAB, 'K', 'T', 'X', ' ', '1', '1', 0xBB, '\r', '\n', 0x1A, '\n' }; // Get the image header - memcpy(header.id, ktx_identifier, 12); // KTX 1.1 signature + RL_GPUTEX_MEMCPY(header.id, ktx_identifier, 12); // KTX 1.1 signature header.endianness = 0; header.gl_type = 0; // Obtained from format header.gl_type_size = 1; @@ -524,10 +609,10 @@ int rl_save_ktx(const char *file_name, void *data, int width, int height, int fo // NOTE: We can save into a .ktx all PixelFormats supported by raylib, including compressed formats like DXT, ETC or ASTC - if (header.gl_format == -1) LOG("WARNING: IMAGE: GL format not supported for KTX export (%i)", header.gl_format); + if (header.gl_format == -1) RL_GPUTEX_WARN("GL format not supported for KTX export (%i)", header.gl_format); else { - memcpy(file_data_ptr, &header, sizeof(ktx_header)); + RL_GPUTEX_MEMCPY(file_data_ptr, &header, sizeof(ktx_header)); file_data_ptr += sizeof(ktx_header); int temp_width = width; @@ -539,8 +624,8 @@ int rl_save_ktx(const char *file_name, void *data, int width, int height, int fo { unsigned int data_size = get_pixel_data_size(temp_width, temp_height, format); - memcpy(file_data_ptr, &data_size, sizeof(unsigned int)); - memcpy(file_data_ptr + 4, (unsigned char *)data + data_offset, data_size); + RL_GPUTEX_MEMCPY(file_data_ptr, &data_size, sizeof(unsigned int)); + RL_GPUTEX_MEMCPY(file_data_ptr + 4, (unsigned char *)data + data_offset, data_size); temp_width /= 2; temp_height /= 2; @@ -553,20 +638,22 @@ int rl_save_ktx(const char *file_name, void *data, int width, int height, int fo int success = false; FILE *file = fopen(file_name, "wb"); - if (file != NULL) + if (file != RL_GPUTEX_NULL) { unsigned int count = (unsigned int)fwrite(file_data, sizeof(unsigned char), data_size, file); - if (count == 0) LOG("WARNING: FILEIO: [%s] Failed to write file", file_name); - else if (count != data_size) LOG("WARNING: FILEIO: [%s] File partially written", file_name); - else LOG("INFO: FILEIO: [%s] File saved successfully", file_name); + if (count == 0) RL_GPUTEX_WARN("FILEIO: [%s] Failed to write file", file_name); + else if (count != data_size) RL_GPUTEX_WARN("FILEIO: [%s] File partially written", file_name); + else (void)0; // WARN: this branch is handled by Raylib, since rl_gputex only prints warnings int result = fclose(file); - if (result == 0) success = true; - } - else LOG("WARNING: FILEIO: [%s] Failed to open file", file_name); + if (result != 0) RL_GPUTEX_WARN("FILEIO: [%s] Failed to close file", file_name); - RL_FREE(file_data); // Free file data buffer + if (result == 0 && count == data_size) success = true; + } + else RL_GPUTEX_WARN("FILEIO: [%s] Failed to open file", file_name); + + RL_GPUTEX_FREE(file_data); // Free file data buffer // If all data has been written correctly to file, success = 1 return success; @@ -578,7 +665,7 @@ int rl_save_ktx(const char *file_name, void *data, int width, int height, int fo // NOTE: PVR v2 not supported, use PVR v3 instead void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips) { - void *image_data = NULL; // Image data pointer + void *image_data = RL_GPUTEX_NULL; // Image data pointer unsigned char *file_data_ptr = (unsigned char *)file_data; @@ -636,7 +723,7 @@ void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_ } PVRMetadata; #endif - if (file_data_ptr != NULL) + if (file_data_ptr != RL_GPUTEX_NULL) { // Check PVR image version unsigned char pvr_version = file_data_ptr[0]; @@ -648,7 +735,7 @@ void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_ if ((header->id[0] != 'P') || (header->id[1] != 'V') || (header->id[2] != 'R') || (header->id[3] != 3)) { - LOG("WARNING: IMAGE: PVR file data not valid"); + RL_GPUTEX_WARN("PVR file data not valid"); } else { @@ -697,12 +784,12 @@ void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_ } int data_size = (*width)*(*height)*bpp/8; // Total data size in bytes - image_data = RL_MALLOC(data_size*sizeof(unsigned char)); + image_data = RL_GPUTEX_MALLOC(data_size*sizeof(unsigned char)); - memcpy(image_data, file_data_ptr, data_size); + RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); } } - else if (pvr_version == 52) LOG("INFO: IMAGE: PVRv2 format not supported, update your files to PVRv3"); + else if (pvr_version == 52) RL_GPUTEX_WARN("PVRv2 format not supported, update your files to PVRv3"); } return image_data; @@ -713,7 +800,7 @@ void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_ // Load ASTC compressed image data (ASTC compression) void *rl_load_astc_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips) { - void *image_data = NULL; // Image data pointer + void *image_data = RL_GPUTEX_NULL; // Image data pointer unsigned char *file_data_ptr = (unsigned char *)file_data; @@ -736,13 +823,13 @@ void *rl_load_astc_from_memory(const unsigned char *file_data, unsigned int file unsigned char length[3]; // void *Z-size (1 for 2D images) } astc_header; - if (file_data_ptr != NULL) + if (file_data_ptr != RL_GPUTEX_NULL) { astc_header *header = (astc_header *)file_data_ptr; if ((header->id[3] != 0x5c) || (header->id[2] != 0xa1) || (header->id[1] != 0xab) || (header->id[0] != 0x13)) { - LOG("WARNING: IMAGE: ASTC file data not valid"); + RL_GPUTEX_WARN("ASTC file data not valid"); } else { @@ -761,14 +848,14 @@ void *rl_load_astc_from_memory(const unsigned char *file_data, unsigned int file { int data_size = (*width)*(*height)*bpp/8; // Data size in bytes - image_data = RL_MALLOC(data_size*sizeof(unsigned char)); + image_data = RL_GPUTEX_MALLOC(data_size*sizeof(unsigned char)); - memcpy(image_data, file_data_ptr, data_size); + RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); if (bpp == 8) *format = PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA; else if (bpp == 2) *format = PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA; } - else LOG("WARNING: IMAGE: ASTC block size configuration not supported"); + else RL_GPUTEX_WARN("ASTC block size configuration not supported"); } } diff --git a/src/rtextures.c b/src/rtextures.c index 02845b40b..a8cf1e70a 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -168,6 +168,18 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" #endif + + + #define RL_GPUTEX_MALLOC RL_MALLOC + #define RL_GPUTEX_CALLOC RL_CALLOC + #define RL_GPUTEX_REALLOC RL_REALLOC + #define RL_GPUTEX_FREE RL_FREE + #define RL_GPUTEX_WARN(...) TRACELOG(LOG_WARNING, "IMAGE: " __VA_ARGS__) + #define RL_GPUTEX_SHOW_WARN_INFO + + // FIXME: probably, we should NOT export public functions from rl_gputex + // but this is how it always worked... so let's keep it this way + #define RLGPUTEXAPI RLAPI #define RL_GPUTEX_IMPLEMENTATION #include "external/rl_gputex.h" // Required for: rl_load_xxx_from_memory() From 8388160c32bda047c9a9ff079ff90b3f803ef8dd Mon Sep 17 00:00:00 2001 From: katonar Date: Tue, 15 Jul 2025 13:58:23 +0200 Subject: [PATCH 088/242] - fixing SUPPORT_DRM_CACHE define check at the end of InitPlatform() --- src/platforms/rcore_drm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 09684291c..fc945d71f 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -1308,12 +1308,12 @@ int InitPlatform(void) #if defined(SUPPORT_DRM_CACHE) if(InitSwapScreenBuffer() == 0) { +#endif//SUPPORT_DRM_CACHE TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully"); return 0; - } else { -#endif//SUPPORT_DRM_CACHE - TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized failed"); #if defined(SUPPORT_DRM_CACHE) + } else { + TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized failed"); return -1; } #endif //SUPPORT_DRM_CACHE From 3320a2c837897c13fd4f97405f82785b5983e425 Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Tue, 15 Jul 2025 19:07:15 +0100 Subject: [PATCH 089/242] Fix to prevent UpdateMusicStream to run without music playing --- src/raudio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/raudio.c b/src/raudio.c index 40db999ea..d71fd1012 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1871,6 +1871,7 @@ void SeekMusicStream(Music music, float position) void UpdateMusicStream(Music music) { if (music.stream.buffer == NULL) return; + if (!music.stream.buffer->playing) return; ma_mutex_lock(&AUDIO.System.lock); From 34af70866c93530e2360761c31f9a322d8d01ccb Mon Sep 17 00:00:00 2001 From: Romain Plumaugat Date: Tue, 15 Jul 2025 23:19:39 +0200 Subject: [PATCH 090/242] [rtextures] Fix ImageColorReplace() alpha issue #5009 --- src/rtextures.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 90938fe6a..e872985b3 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -2921,13 +2921,27 @@ void ImageColorReplace(Image *image, Color color, Color replace) } } - int format = image->format; + const int format = image->format; RL_FREE(image->data); image->data = pixels; image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; - ImageFormat(image, format); + const bool formatHasNoAlpha = (format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) || + (format == PIXELFORMAT_UNCOMPRESSED_R5G6B5) || + (format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) || + (format == PIXELFORMAT_UNCOMPRESSED_R32G32B32) || + (format == PIXELFORMAT_UNCOMPRESSED_R16G16B16) || + (format == PIXELFORMAT_COMPRESSED_DXT1_RGB) || + (format == PIXELFORMAT_COMPRESSED_ETC1_RGB) || + (format == PIXELFORMAT_COMPRESSED_ETC2_RGB) || + (format == PIXELFORMAT_COMPRESSED_PVRT_RGB); + + // Only convert back to original format if it supported alpha + if (!formatHasNoAlpha) + { + ImageFormat(image, format); + } } #endif // SUPPORT_IMAGE_MANIPULATION From d03ac97effc77e7cb3a3d7033f6c1f2eab36b3c8 Mon Sep 17 00:00:00 2001 From: veins1 <19636663+veins1@users.noreply.github.com> Date: Wed, 16 Jul 2025 14:18:38 +0000 Subject: [PATCH 091/242] GetMusicTimePlayed fix for music shorter than buffer size --- src/raudio.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index d71fd1012..0502cf481 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -2086,8 +2086,12 @@ float GetMusicTimePlayed(Music music) int subBufferSize = (int)music.stream.buffer->sizeInFrames/2; int framesInFirstBuffer = music.stream.buffer->isSubBufferProcessed[0]? 0 : subBufferSize; int framesInSecondBuffer = music.stream.buffer->isSubBufferProcessed[1]? 0 : subBufferSize; + int framesInBuffers = framesInFirstBuffer + framesInSecondBuffer; + if (framesInBuffers > music.frameCount) { + if (!music.looping) framesInBuffers = music.frameCount; + } int framesSentToMix = music.stream.buffer->frameCursorPos%subBufferSize; - int framesPlayed = (framesProcessed - framesInFirstBuffer - framesInSecondBuffer + framesSentToMix)%(int)music.frameCount; + int framesPlayed = (framesProcessed - framesInBuffers + framesSentToMix)%(int)music.frameCount; if (framesPlayed < 0) framesPlayed += music.frameCount; secondsPlayed = (float)framesPlayed/music.stream.sampleRate; ma_mutex_unlock(&AUDIO.System.lock); From 0405de794ad9b542c73b4fee76e8ff90c8b9e8cc Mon Sep 17 00:00:00 2001 From: veins1 <19636663+veins1@users.noreply.github.com> Date: Wed, 16 Jul 2025 14:22:57 +0000 Subject: [PATCH 092/242] Fix for music stopping too early --- src/raudio.c | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 0502cf481..bf3745ff2 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1891,14 +1891,26 @@ void UpdateMusicStream(Music music) // Check both sub-buffers to check if they require refilling for (int i = 0; i < 2; i++) { - if (!music.stream.buffer->isSubBufferProcessed[i]) continue; // No refilling required, move to next sub-buffer - unsigned int framesLeft = music.frameCount - music.stream.buffer->framesProcessed; // Frames left to be processed unsigned int framesToStream = 0; // Total frames to be streamed if ((framesLeft >= subBufferSizeInFrames) || music.looping) framesToStream = subBufferSizeInFrames; else framesToStream = framesLeft; + if (framesToStream == 0) { + bool framesInBufferPlayed = music.stream.buffer->isSubBufferProcessed[0] && music.stream.buffer->isSubBufferProcessed[1]; + if (framesInBufferPlayed) { + + ma_mutex_unlock(&AUDIO.System.lock); + StopMusicStream(music); + return; + } + ma_mutex_unlock(&AUDIO.System.lock); + return; + } + + if (!music.stream.buffer->isSubBufferProcessed[i]) continue; // No refilling required, move to next sub-buffer + int frameCountStillNeeded = framesToStream; int frameCountReadTotal = 0; @@ -2009,21 +2021,7 @@ void UpdateMusicStream(Music music) #endif default: break; } - UpdateAudioStreamInLockedState(music.stream, AUDIO.System.pcmBuffer, framesToStream); - - music.stream.buffer->framesProcessed = music.stream.buffer->framesProcessed%music.frameCount; - - if (framesLeft <= subBufferSizeInFrames) - { - if (!music.looping) - { - ma_mutex_unlock(&AUDIO.System.lock); - // Streaming is ending, we filled latest frames from input - StopMusicStream(music); - return; - } - } } ma_mutex_unlock(&AUDIO.System.lock); @@ -2688,8 +2686,7 @@ static void UpdateAudioStreamInLockedState(AudioStream stream, const void *data, ma_uint32 subBufferSizeInFrames = stream.buffer->sizeInFrames/2; unsigned char *subBuffer = stream.buffer->data + ((subBufferSizeInFrames*stream.channels*(stream.sampleSize/8))*subBufferToUpdate); - // Total frames processed in buffer is always the complete size, filled with 0 if required - stream.buffer->framesProcessed += subBufferSizeInFrames; + stream.buffer->framesProcessed += frameCount; // Does this API expect a whole buffer to be updated in one go? // Assuming so, but if not will need to change this logic From 093e5200d034b365323f7c9dadcb003c28945d1d Mon Sep 17 00:00:00 2001 From: wwderw <24878362+wwderw@users.noreply.github.com> Date: Thu, 17 Jul 2025 05:19:39 -0500 Subject: [PATCH 093/242] Update Makefile Clean up library build for the web --- src/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 833f955a9..5b42237ef 100644 --- a/src/Makefile +++ b/src/Makefile @@ -868,7 +868,7 @@ clean: clean_shell_$(PLATFORM_SHELL) @echo "removed all generated files!" clean_shell_sh: - rm -fv *.o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).bc $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so* raygui.c $(RAYLIB_RELEASE_PATH)/*-protocol.h $(RAYLIB_RELEASE_PATH)/*-protocol-code.h + rm -fv *.o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).web.a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).bc $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so* raygui.c $(RAYLIB_RELEASE_PATH)/*-protocol.h $(RAYLIB_RELEASE_PATH)/*-protocol-code.h ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) rm -fv $(NATIVE_APP_GLUE)/android_native_app_glue.o endif @@ -879,6 +879,7 @@ clean_shell_cmd: del *.o /s cd $(RAYLIB_RELEASE_PATH) & \ del lib$(RAYLIB_LIB_NAME).a /s & \ + del lib$(RAYLIB_LIB_NAME).web.a /s & \ del lib$(RAYLIB_LIB_NAME)dll.a /s & \ del $(RAYLIB_LIB_NAME).dll /s & \ del raygui.c /s & \ From 8823aba9df6c83cf3f6cef36288a180c0e7a77f6 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 17 Jul 2025 18:49:34 +0200 Subject: [PATCH 094/242] Update rprand.h --- src/external/rprand.h | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/external/rprand.h b/src/external/rprand.h index ba8368726..95147d270 100644 --- a/src/external/rprand.h +++ b/src/external/rprand.h @@ -15,15 +15,15 @@ * - Support 64 bits generation * * ADDITIONAL NOTES: -* This library implements two pseudo-random number generation algorithms: +* This library implements two pseudo-random number generation algorithms: * * - Xoshiro128** : https://prng.di.unimi.it/xoshiro128starstar.c * - SplitMix64 : https://prng.di.unimi.it/splitmix64.c * * SplitMix64 is used to initialize the Xoshiro128** state, from a provided seed * -* It's suggested to use SplitMix64 to initialize the state of the generators starting from -* a 64-bit seed, as research has shown that initialization must be performed with a generator +* It's suggested to use SplitMix64 to initialize the state of the generators starting from +* a 64-bit seed, as research has shown that initialization must be performed with a generator * radically different in nature from the one initialized to avoid correlation on similar seeds. * * CONFIGURATION: @@ -31,7 +31,7 @@ * Generates the implementation of the library into the included file. * If not defined, the library is in header only mode and can be included in other headers * or source files without problems. But only ONE file should hold the implementation. -* +* * DEPENDENCIES: none * * VERSIONS HISTORY: @@ -153,7 +153,7 @@ static uint32_t rprand_state[4] = { // Xoshiro128** state, initializ 0x218b21e5, 0xaa91febd, 0x976414d4 -}; +}; //---------------------------------------------------------------------------------- // Module internal functions declaration @@ -190,8 +190,8 @@ int rprand_get_value(int min, int max) int *rprand_load_sequence(unsigned int count, int min, int max) { int *sequence = NULL; - - if (count > (unsigned int)(abs(max - min) + 1)) + + if (count > (unsigned int)(abs(max - min) + 1)) { RPRAND_LOG("WARNING: Sequence count required is greater than range provided\n"); //count = (max - min); @@ -244,26 +244,26 @@ static inline uint32_t rprand_rotate_left(const uint32_t x, int k) } // Xoshiro128** generator info: -// +// // Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) -// +// // To the extent possible under law, the author has dedicated all copyright // and related and neighboring rights to this software to the public domain // worldwide. This software is distributed without any warranty. -// +// // See . -// +// // This is xoshiro128** 1.1, one of our 32-bit all-purpose, rock-solid // generators. It has excellent speed, a state size (128 bits) that is // large enough for mild parallelism, and it passes all tests we are aware // of. -// +// // Note that version 1.0 had mistakenly s[0] instead of s[1] as state // word passed to the scrambler. -// +// // For generating just single-precision (i.e., 32-bit) floating-point // numbers, xoshiro128+ is even faster. -// +// // The state must be seeded so that it is not everywhere zero. // uint32_t rprand_xoshiro(void) @@ -275,29 +275,29 @@ uint32_t rprand_xoshiro(void) rprand_state[3] ^= rprand_state[1]; rprand_state[1] ^= rprand_state[2]; rprand_state[0] ^= rprand_state[3]; - + rprand_state[2] ^= t; - + rprand_state[3] = rprand_rotate_left(rprand_state[3], 11); return result; } // SplitMix64 generator info: -// +// // Written in 2015 by Sebastiano Vigna (vigna@acm.org) -// +// // To the extent possible under law, the author has dedicated all copyright // and related and neighboring rights to this software to the public domain // worldwide. This software is distributed without any warranty. -// +// // See . -// +// // // This is a fixed-increment version of Java 8's SplittableRandom generator // See http://dx.doi.org/10.1145/2714064.2660195 and // http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html -// +// // It is a very fast generator passing BigCrush, and it can be useful if // for some reason you absolutely want 64 bits of state. uint64_t rprand_splitmix64() From 71b302846a0354274f5584adabcfa042f6519e03 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 17 Jul 2025 18:49:54 +0200 Subject: [PATCH 095/242] Review formatting, avoid variable --- src/raudio.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index bf3745ff2..ba1fba081 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1897,14 +1897,16 @@ void UpdateMusicStream(Music music) if ((framesLeft >= subBufferSizeInFrames) || music.looping) framesToStream = subBufferSizeInFrames; else framesToStream = framesLeft; - if (framesToStream == 0) { - bool framesInBufferPlayed = music.stream.buffer->isSubBufferProcessed[0] && music.stream.buffer->isSubBufferProcessed[1]; - if (framesInBufferPlayed) { - + if (framesToStream == 0) + { + // Check if both buffers have been processed + if (music.stream.buffer->isSubBufferProcessed[0] && music.stream.buffer->isSubBufferProcessed[1]) + { ma_mutex_unlock(&AUDIO.System.lock); StopMusicStream(music); return; } + ma_mutex_unlock(&AUDIO.System.lock); return; } @@ -2021,6 +2023,7 @@ void UpdateMusicStream(Music music) #endif default: break; } + UpdateAudioStreamInLockedState(music.stream, AUDIO.System.pcmBuffer, framesToStream); } From 82b80a6998d16a6be51b40868c6c436faebec86d Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 17 Jul 2025 18:49:58 +0200 Subject: [PATCH 096/242] Review formatting, avoid variable --- src/rtextures.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index e872985b3..771702306 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -2921,27 +2921,22 @@ void ImageColorReplace(Image *image, Color color, Color replace) } } - const int format = image->format; + int format = image->format; RL_FREE(image->data); image->data = pixels; image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; - const bool formatHasNoAlpha = (format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) || - (format == PIXELFORMAT_UNCOMPRESSED_R5G6B5) || - (format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) || - (format == PIXELFORMAT_UNCOMPRESSED_R32G32B32) || - (format == PIXELFORMAT_UNCOMPRESSED_R16G16B16) || - (format == PIXELFORMAT_COMPRESSED_DXT1_RGB) || - (format == PIXELFORMAT_COMPRESSED_ETC1_RGB) || - (format == PIXELFORMAT_COMPRESSED_ETC2_RGB) || - (format == PIXELFORMAT_COMPRESSED_PVRT_RGB); - // Only convert back to original format if it supported alpha - if (!formatHasNoAlpha) - { - ImageFormat(image, format); - } + if ((format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) || + (format == PIXELFORMAT_UNCOMPRESSED_R5G6B5) || + (format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) || + (format == PIXELFORMAT_UNCOMPRESSED_R32G32B32) || + (format == PIXELFORMAT_UNCOMPRESSED_R16G16B16) || + (format == PIXELFORMAT_COMPRESSED_DXT1_RGB) || + (format == PIXELFORMAT_COMPRESSED_ETC1_RGB) || + (format == PIXELFORMAT_COMPRESSED_ETC2_RGB) || + (format == PIXELFORMAT_COMPRESSED_PVRT_RGB)) ImageFormat(image, format); } #endif // SUPPORT_IMAGE_MANIPULATION From 3d93b30262020367414cf7ecc57be30a8133b371 Mon Sep 17 00:00:00 2001 From: Catania <79325830+katanya04@users.noreply.github.com> Date: Fri, 18 Jul 2025 15:20:38 +0200 Subject: [PATCH 097/242] Update font load message --- src/rtext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index d7818d2db..769b4783c 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -391,7 +391,7 @@ Font LoadFont(const char *fileName) else { SetTextureFilter(font.texture, TEXTURE_FILTER_POINT); // By default, we set point filter (the best performance) - TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", FONT_TTF_DEFAULT_SIZE, FONT_TTF_DEFAULT_NUMCHARS); + TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", font.baseSize, font.glyphCount); } } From 0b125a5fd78feb3a5ad76bfb0c0d61528e684854 Mon Sep 17 00:00:00 2001 From: wwderw <24878362+wwderw@users.noreply.github.com> Date: Sat, 19 Jul 2025 04:54:05 -0500 Subject: [PATCH 098/242] Update Makefile Remove .bc file removal from clean shell for windows configuration. --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 5b42237ef..9a06be576 100644 --- a/src/Makefile +++ b/src/Makefile @@ -868,7 +868,7 @@ clean: clean_shell_$(PLATFORM_SHELL) @echo "removed all generated files!" clean_shell_sh: - rm -fv *.o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).web.a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).bc $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so* raygui.c $(RAYLIB_RELEASE_PATH)/*-protocol.h $(RAYLIB_RELEASE_PATH)/*-protocol-code.h + rm -fv *.o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).web.a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so* raygui.c $(RAYLIB_RELEASE_PATH)/*-protocol.h $(RAYLIB_RELEASE_PATH)/*-protocol-code.h ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) rm -fv $(NATIVE_APP_GLUE)/android_native_app_glue.o endif From 36364192d59212dc48102e8ccb6d9162bf7fa78f Mon Sep 17 00:00:00 2001 From: Didas72 Date: Sat, 19 Jul 2025 22:29:08 +0100 Subject: [PATCH 099/242] Fixes GetWindowHandle returning a bad handle under Linux X11 --- src/platforms/rcore_desktop_glfw.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 9bbd6559a..038e1c649 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -74,9 +74,14 @@ #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) #include // Required for: timespec, nanosleep(), select() - POSIX - //#define GLFW_EXPOSE_NATIVE_X11 // WARNING: Exposing Xlib.h > X.h results in dup symbols for Font type //#define GLFW_EXPOSE_NATIVE_WAYLAND + #define GLFW_EXPOSE_NATIVE_X11 + #define Font X11Font // Hack to fix 'Font' name collision + // The definition and references to the X11 Font type will be replaced by 'X11Font' + // Works as long as the current file consistently references any X11 Font as X11Font + // Since it is never referenced (as of writting), this does not pose an issue #include "GLFW/glfw3native.h" // Required for: glfwGetX11Window() + #undef Font // Revert hack and allow normal raylib Font usage #endif #if defined(__APPLE__) #include // Required for: usleep() @@ -705,6 +710,12 @@ void SetWindowFocused(void) glfwFocusWindow(platform.handle); } +#if defined(__linux__) +// Local storage for the window handle returned by glfwGetX11Window +// This is needed as X11 handles are integers and may not fit inside a pointer depending on platform +// Storing the handle locally and returning a pointer in GetWindowHandle allows the code to work regardless of pointer width +static XID X11WindowHandle; +#endif // Get native window handle void *GetWindowHandle(void) { @@ -713,12 +724,10 @@ void *GetWindowHandle(void) return glfwGetWin32Window(platform.handle); #endif #if defined(__linux__) - // NOTE: Returned handle is: unsigned long Window (X.h) - // typedef unsigned long XID; - // typedef XID Window; - //unsigned long id = (unsigned long)glfwGetX11Window(platform.handle); - //return NULL; // TODO: Find a way to return value... cast to void *? - return (void *)platform.handle; + // Store the window handle localy and return a pointer to the variable instead. + // Reasoning detailed in the declaration of X11WindowHandle + X11WindowHandle = glfwGetX11Window(platform.handle); + return &X11WindowHandle; #endif #if defined(__APPLE__) // NOTE: Returned handle is: (objc_object *) From 7b017b60d9fa05c1d386c449ce2de07282ea689a Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 21 Jul 2025 10:57:21 +0200 Subject: [PATCH 100/242] Move global variable to right code section --- src/rtext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index 769b4783c..1330c5906 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -130,6 +130,7 @@ extern bool isGpuReady; // NOTE: Default font is loaded on InitWindow() and disposed on CloseWindow() [module: core] static Font defaultFont = { 0 }; #endif +static int textLineSpacing = 2; // Text vertical line spacing in pixels (between lines) //---------------------------------------------------------------------------------- // Other Modules Functions Declaration (required by text) @@ -145,7 +146,6 @@ static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode #if defined(SUPPORT_FILEFORMAT_BDF) static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, int *codepoints, int codepointCount, int *outFontSize); #endif -static int textLineSpacing = 2; // Text vertical line spacing in pixels (between lines) #if defined(SUPPORT_DEFAULT_FONT) extern void LoadFontDefault(void); From f1714a6234dcccdd5d4b293262cf07d5f99a42b8 Mon Sep 17 00:00:00 2001 From: Joecheong2006 Date: Mon, 21 Jul 2025 14:59:51 -0700 Subject: [PATCH 101/242] [examples]: New shapes example - double pendulum --- examples/Makefile | 3 +- examples/Makefile.Web | 6 +- examples/README.md | 209 +++++++++++---------- examples/shapes/shapes_double_pendulum.c | 188 ++++++++++++++++++ examples/shapes/shapes_double_pendulum.png | Bin 0 -> 291645 bytes 5 files changed, 300 insertions(+), 106 deletions(-) create mode 100644 examples/shapes/shapes_double_pendulum.c create mode 100644 examples/shapes/shapes_double_pendulum.png diff --git a/examples/Makefile b/examples/Makefile index 32a3a75ab..2f32f654f 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -548,7 +548,8 @@ SHAPES = \ shapes/shapes_rectangle_scaling \ shapes/shapes_splines_drawing \ shapes/shapes_top_down_lights \ - shapes/shapes_digital_clock + shapes/shapes_digital_clock \ + shapes/shapes_dobule_pendulum TEXTURES = \ textures/textures_background_scrolling \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 35ae70a18..ee5564599 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -430,7 +430,8 @@ SHAPES = \ shapes/shapes_rectangle_scaling \ shapes/shapes_splines_drawing \ shapes/shapes_top_down_lights \ - shapes/shapes_digital_clock + shapes/shapes_digital_clock \ + shapes/shapes_double_pendulum TEXTURES = \ textures/textures_background_scrolling \ @@ -731,6 +732,9 @@ shapes/shapes_top_down_lights: shapes/shapes_top_down_lights.c shapes/shapes_rectangle_advanced: shapes/shapes_rectangle_advanced.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +shapes/shapes_double_pendulum: shapes/shapes_double_pendulum.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + # Compile TEXTURES examples textures/textures_background_scrolling: textures/textures_background_scrolling.c diff --git a/examples/README.md b/examples/README.md index 7553ad113..e075facbe 100644 --- a/examples/README.md +++ b/examples/README.md @@ -86,38 +86,39 @@ Examples using raylib shapes drawing functionality, provided by raylib [shapes]( | 52 | [shapes_rectangle_advanced](shapes/shapes_rectangle_advanced.c) | shapes_rectangle_advanced | ⭐️⭐️⭐️⭐️ | 5.5 | 5.5 | [Everton Jr.](https://github.com/evertonse) | | 53 | [shapes_splines_drawing](shapes/shapes_splines_drawing.c) | shapes_splines_drawing | ⭐️⭐️⭐️☆ | 5.0 | 5.0 | [Ray](https://github.com/raysan5) | | 54 | [shapes_digital_clock](shapes/shapes_digital_clock.c) | shapes_digital_clock | ⭐️⭐️☆☆ | 5.5 | 5.5 | [Hamza RAHAL](https://github.com/rhmz-rhl) | +| 55 | [shapes_double_pendulum](shapes/shapes_double_pendulum.c) | shapes_double_pendulum | ⭐️⭐️☆☆ | 5.5 | 5.5 | [JoeCheong](https://github.com/Joecheong2006) | ### category: textures Examples using raylib textures functionality, including image/textures loading/generation and drawing, provided by raylib [textures](../src/textures.c) modul | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 55 | [textures_logo_raylib](textures/textures_logo_raylib.c) | textures_logo_raylib | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 56 | [textures_srcrec_dstrec](textures/textures_srcrec_dstrec.c) | textures_srcrec_dstrec | ⭐️⭐️⭐️☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 57 | [textures_image_drawing](textures/textures_image_drawing.c) | textures_image_drawing | ⭐️⭐️☆☆ | 1.4 | 1.4 | [Ray](https://github.com/raysan5) | -| 58 | [textures_image_generation](textures/textures_image_generation.c) | textures_image_generation | ⭐️⭐️☆☆ | 1.8 | 1.8 | [Wilhem Barbier](https://github.com/nounoursheureux) | -| 59 | [textures_image_loading](textures/textures_image_loading.c) | textures_image_loading | ⭐️☆☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 60 | [textures_image_processing](textures/textures_image_processing.c) | textures_image_processing | ⭐️⭐️⭐️☆ | 1.4 | 3.5 | [Ray](https://github.com/raysan5) | -| 61 | [textures_image_text](textures/textures_image_text.c) | textures_image_text | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) | -| 62 | [textures_to_image](textures/textures_to_image.c) | textures_to_image | ⭐️☆☆☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | -| 63 | [textures_raw_data](textures/textures_raw_data.c) | textures_raw_data | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | -| 64 | [textures_particles_blending](textures/textures_particles_blending.c) | textures_particles_blending | ⭐️☆☆☆ | 1.7 | 3.5 | [Ray](https://github.com/raysan5) | -| 65 | [textures_npatch_drawing](textures/textures_npatch_drawing.c) | textures_npatch_drawing | ⭐️⭐️⭐️☆ | 2.0 | 2.5 | [Jorge A. Gomes](https://github.com/overdev) | -| 66 | [textures_background_scrolling](textures/textures_background_scrolling.c) | textures_background_scrolling | ⭐️☆☆☆ | 2.0 | 2.5 | [Ray](https://github.com/raysan5) | -| 67 | [textures_sprite_anim](textures/textures_sprite_anim.c) | textures_sprite_anim | ⭐️⭐️☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 68 | [textures_sprite_button](textures/textures_sprite_button.c) | textures_sprite_button | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | -| 69 | [textures_sprite_explosion](textures/textures_sprite_explosion.c) | textures_sprite_explosion | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 70 | [textures_bunnymark](textures/textures_bunnymark.c) | textures_bunnymark | ⭐️⭐️⭐️☆ | 1.6 | 2.5 | [Ray](https://github.com/raysan5) | -| 71 | [textures_mouse_painting](textures/textures_mouse_painting.c) | textures_mouse_painting | ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Chris Dill](https://github.com/MysteriousSpace) | -| 72 | [textures_blend_modes](textures/textures_blend_modes.c) | textures_blend_modes | ⭐️☆☆☆ | 3.5 | 3.5 | [Karlo Licudine](https://github.com/accidentalrebel) | -| 73 | [textures_draw_tiled](textures/textures_draw_tiled.c) | textures_draw_tiled | ⭐️⭐️⭐️☆ | 3.0 | 4.2 | [Vlad Adrian](https://github.com/demizdor) | -| 74 | [textures_polygon](textures/textures_polygon.c) | textures_polygon | ⭐️☆☆☆ | 3.7 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | -| 75 | [textures_fog_of_war](textures/textures_fog_of_war.c) | textures_fog_of_war | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) | -| 76 | [textures_gif_player](textures/textures_gif_player.c) | textures_gif_player | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) | -| 77 | [textures_image_kernel](textures/textures_image_kernel.c) | textures_image_kernel | ⭐️⭐️⭐️⭐️ | 1.3 | 1.3 | [Karim Salem](https://github.com/kimo-s) | -| 78 | [textures_image_channel](textures/textures_image_channel.c) | textures_image_channel | ⭐️⭐️☆☆ | 5.1-dev | 5.1-dev | [Bruno Cabral](https://github.com/brccabral) | -| 79 | [textures_image_rotate](textures/textures_image_rotate.c) | textures_image_rotate | ⭐️⭐️☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 80 | [textures_textured_curve](textures/textures_textured_curve.c) | textures_textured_curve | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Jeffery Myers](https://github.com/JeffM2501) | +| 56 | [textures_logo_raylib](textures/textures_logo_raylib.c) | textures_logo_raylib | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | +| 57 | [textures_srcrec_dstrec](textures/textures_srcrec_dstrec.c) | textures_srcrec_dstrec | ⭐️⭐️⭐️☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | +| 58 | [textures_image_drawing](textures/textures_image_drawing.c) | textures_image_drawing | ⭐️⭐️☆☆ | 1.4 | 1.4 | [Ray](https://github.com/raysan5) | +| 59 | [textures_image_generation](textures/textures_image_generation.c) | textures_image_generation | ⭐️⭐️☆☆ | 1.8 | 1.8 | [Wilhem Barbier](https://github.com/nounoursheureux) | +| 60 | [textures_image_loading](textures/textures_image_loading.c) | textures_image_loading | ⭐️☆☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | +| 61 | [textures_image_processing](textures/textures_image_processing.c) | textures_image_processing | ⭐️⭐️⭐️☆ | 1.4 | 3.5 | [Ray](https://github.com/raysan5) | +| 62 | [textures_image_text](textures/textures_image_text.c) | textures_image_text | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) | +| 63 | [textures_to_image](textures/textures_to_image.c) | textures_to_image | ⭐️☆☆☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | +| 64 | [textures_raw_data](textures/textures_raw_data.c) | textures_raw_data | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | +| 65 | [textures_particles_blending](textures/textures_particles_blending.c) | textures_particles_blending | ⭐️☆☆☆ | 1.7 | 3.5 | [Ray](https://github.com/raysan5) | +| 66 | [textures_npatch_drawing](textures/textures_npatch_drawing.c) | textures_npatch_drawing | ⭐️⭐️⭐️☆ | 2.0 | 2.5 | [Jorge A. Gomes](https://github.com/overdev) | +| 67 | [textures_background_scrolling](textures/textures_background_scrolling.c) | textures_background_scrolling | ⭐️☆☆☆ | 2.0 | 2.5 | [Ray](https://github.com/raysan5) | +| 68 | [textures_sprite_anim](textures/textures_sprite_anim.c) | textures_sprite_anim | ⭐️⭐️☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | +| 69 | [textures_sprite_button](textures/textures_sprite_button.c) | textures_sprite_button | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | +| 70 | [textures_sprite_explosion](textures/textures_sprite_explosion.c) | textures_sprite_explosion | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 71 | [textures_bunnymark](textures/textures_bunnymark.c) | textures_bunnymark | ⭐️⭐️⭐️☆ | 1.6 | 2.5 | [Ray](https://github.com/raysan5) | +| 72 | [textures_mouse_painting](textures/textures_mouse_painting.c) | textures_mouse_painting | ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Chris Dill](https://github.com/MysteriousSpace) | +| 73 | [textures_blend_modes](textures/textures_blend_modes.c) | textures_blend_modes | ⭐️☆☆☆ | 3.5 | 3.5 | [Karlo Licudine](https://github.com/accidentalrebel) | +| 74 | [textures_draw_tiled](textures/textures_draw_tiled.c) | textures_draw_tiled | ⭐️⭐️⭐️☆ | 3.0 | 4.2 | [Vlad Adrian](https://github.com/demizdor) | +| 75 | [textures_polygon](textures/textures_polygon.c) | textures_polygon | ⭐️☆☆☆ | 3.7 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | +| 76 | [textures_fog_of_war](textures/textures_fog_of_war.c) | textures_fog_of_war | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) | +| 77 | [textures_gif_player](textures/textures_gif_player.c) | textures_gif_player | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) | +| 78 | [textures_image_kernel](textures/textures_image_kernel.c) | textures_image_kernel | ⭐️⭐️⭐️⭐️ | 1.3 | 1.3 | [Karim Salem](https://github.com/kimo-s) | +| 79 | [textures_image_channel](textures/textures_image_channel.c) | textures_image_channel | ⭐️⭐️☆☆ | 5.1-dev | 5.1-dev | [Bruno Cabral](https://github.com/brccabral) | +| 80 | [textures_image_rotate](textures/textures_image_rotate.c) | textures_image_rotate | ⭐️⭐️☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | +| 81 | [textures_textured_curve](textures/textures_textured_curve.c) | textures_textured_curve | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Jeffery Myers](https://github.com/JeffM2501) | ### category: text @@ -125,18 +126,18 @@ Examples using raylib text functionality, including sprite fonts loading/generat | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 81 | [text_raylib_fonts](text/text_raylib_fonts.c) | text_raylib_fonts | ⭐️☆☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) | -| 82 | [text_font_spritefont](text/text_font_spritefont.c) | text_font_spritefont | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 83 | [text_font_filters](text/text_font_filters.c) | text_font_filters | ⭐️⭐️☆☆ | 1.3 | 4.2 | [Ray](https://github.com/raysan5) | -| 84 | [text_font_loading](text/text_font_loading.c) | text_font_loading | ⭐️☆☆☆ | 1.4 | 3.0 | [Ray](https://github.com/raysan5) | -| 85 | [text_font_sdf](text/text_font_sdf.c) | text_font_sdf | ⭐️⭐️⭐️☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | -| 86 | [text_format_text](text/text_format_text.c) | text_format_text | ⭐️☆☆☆ | 1.1 | 3.0 | [Ray](https://github.com/raysan5) | -| 87 | [text_input_box](text/text_input_box.c) | text_input_box | ⭐️⭐️☆☆ | 1.7 | 3.5 | [Ray](https://github.com/raysan5) | -| 88 | [text_writing_anim](text/text_writing_anim.c) | text_writing_anim | ⭐️⭐️☆☆ | 1.4 | 1.4 | [Ray](https://github.com/raysan5) | -| 89 | [text_rectangle_bounds](text/text_rectangle_bounds.c) | text_rectangle_bounds | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) | -| 90 | [text_unicode](text/text_unicode.c) | text_unicode | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) | -| 91 | [text_draw_3d](text/text_draw_3d.c) | text_draw_3d | ⭐️⭐️⭐️⭐️ | 3.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) | -| 92 | [text_codepoints_loading](text/text_codepoints_loading.c) | text_codepoints_loading | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) | +| 82 | [text_raylib_fonts](text/text_raylib_fonts.c) | text_raylib_fonts | ⭐️☆☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) | +| 83 | [text_font_spritefont](text/text_font_spritefont.c) | text_font_spritefont | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | +| 84 | [text_font_filters](text/text_font_filters.c) | text_font_filters | ⭐️⭐️☆☆ | 1.3 | 4.2 | [Ray](https://github.com/raysan5) | +| 85 | [text_font_loading](text/text_font_loading.c) | text_font_loading | ⭐️☆☆☆ | 1.4 | 3.0 | [Ray](https://github.com/raysan5) | +| 86 | [text_font_sdf](text/text_font_sdf.c) | text_font_sdf | ⭐️⭐️⭐️☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | +| 87 | [text_format_text](text/text_format_text.c) | text_format_text | ⭐️☆☆☆ | 1.1 | 3.0 | [Ray](https://github.com/raysan5) | +| 88 | [text_input_box](text/text_input_box.c) | text_input_box | ⭐️⭐️☆☆ | 1.7 | 3.5 | [Ray](https://github.com/raysan5) | +| 89 | [text_writing_anim](text/text_writing_anim.c) | text_writing_anim | ⭐️⭐️☆☆ | 1.4 | 1.4 | [Ray](https://github.com/raysan5) | +| 90 | [text_rectangle_bounds](text/text_rectangle_bounds.c) | text_rectangle_bounds | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) | +| 91 | [text_unicode](text/text_unicode.c) | text_unicode | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) | +| 92 | [text_draw_3d](text/text_draw_3d.c) | text_draw_3d | ⭐️⭐️⭐️⭐️ | 3.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) | +| 93 | [text_codepoints_loading](text/text_codepoints_loading.c) | text_codepoints_loading | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) | ### category: models @@ -144,29 +145,29 @@ Examples using raylib models functionality, including models loading/generation | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 93 | [models_animation](models/models_animation.c) | models_animation | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Culacant](https://github.com/culacant) | -| 94 | [models_billboard](models/models_billboard.c) | models_billboard | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | -| 95 | [models_box_collisions](models/models_box_collisions.c) | models_box_collisions | ⭐️☆☆☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | -| 96 | [models_cubicmap](models/models_cubicmap.c) | models_cubicmap | ⭐️⭐️☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) | -| 97 | [models_first_person_maze](models/models_first_person_maze.c) | models_first_person_maze | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 98 | [models_geometric_shapes](models/models_geometric_shapes.c) | models_geometric_shapes | ⭐️☆☆☆ | 1.0 | 3.5 | [Ray](https://github.com/raysan5) | -| 99 | [models_mesh_generation](models/models_mesh_generation.c) | models_mesh_generation | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) | -| 100 | [models_mesh_picking](models/models_mesh_picking.c) | models_mesh_picking | ⭐️⭐️⭐️☆ | 1.7 | 4.0 | [Joel Davis](https://github.com/joeld42) | -| 101 | [models_loading](models/models_loading.c) | models_loading | ⭐️☆☆☆ | 2.0 | 4.2 | [Ray](https://github.com/raysan5) | -| 102 | [models_loading_gltf](models/models_loading_gltf.c) | models_loading_gltf | ⭐️☆☆☆ | 3.7 | 4.2 | [Ray](https://github.com/raysan5) | -| 103 | [models_loading_vox](models/models_loading_vox.c) | models_loading_vox | ⭐️☆☆☆ | 4.0 | 4.0 | [Johann Nadalutti](https://github.com/procfxgen) | -| 104 | [models_loading_m3d](models/models_loading_m3d.c) | models_loading_m3d | ⭐️⭐️☆☆ | 4.5 | 4.5 | [bzt](https://bztsrc.gitlab.io/model3d) | -| 105 | [models_orthographic_projection](models/models_orthographic_projection.c) | models_orthographic_projection | ⭐️☆☆☆ | 2.0 | 3.7 | [Max Danielsson](https://github.com/autious) | -| 106 | [models_point_rendering](models/models_point_rendering.c) | models_point_rendering | ⭐️⭐️⭐️☆ | 5.0 | 5.0 | [Reese Gallagher](https://github.com/satchelfrost) | -| 107 | [models_rlgl_solar_system](models/models_rlgl_solar_system.c) | models_rlgl_solar_system | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Ray](https://github.com/raysan5) | -| 108 | [models_yaw_pitch_roll](models/models_yaw_pitch_roll.c) | models_yaw_pitch_roll | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Berni](https://github.com/Berni8k) | -| 109 | [models_waving_cubes](models/models_waving_cubes.c) | models_waving_cubes | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Codecat](https://github.com/codecat) | -| 110 | [models_heightmap](models/models_heightmap.c) | models_heightmap | ⭐️☆☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) | -| 111 | [models_skybox](models/models_skybox.c) | models_skybox | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) | -| 112 | [models_draw_cube_texture](models/models_draw_cube_texture.c) | models_draw_cube_texture | ⭐️⭐️☆☆ | 4.5 | 4.5 | [Ray](https://github.com/raysan5) | -| 113 | [models_gpu_skinning](models/models_gpu_skinning.c) | models_gpu_skinning | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Daniel Holden](https://github.com/orangeduck) | -| 114 | [models_bone_socket](models/models_bone_socket.c) | models_bone_socket | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [iP](https://github.com/ipzaur) | -| 115 | [models_tesseract_view](models/models_tesseract_view.c) | models_tesseract_view | ⭐️⭐️☆☆ | 5.6-dev | 5.6-dev | [Timothy van der Valk](https://github.com/arceryz) | +| 94 | [models_animation](models/models_animation.c) | models_animation | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Culacant](https://github.com/culacant) | +| 95 | [models_billboard](models/models_billboard.c) | models_billboard | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | +| 96 | [models_box_collisions](models/models_box_collisions.c) | models_box_collisions | ⭐️☆☆☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | +| 97 | [models_cubicmap](models/models_cubicmap.c) | models_cubicmap | ⭐️⭐️☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) | +| 98 | [models_first_person_maze](models/models_first_person_maze.c) | models_first_person_maze | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 99 | [models_geometric_shapes](models/models_geometric_shapes.c) | models_geometric_shapes | ⭐️☆☆☆ | 1.0 | 3.5 | [Ray](https://github.com/raysan5) | +| 100 | [models_mesh_generation](models/models_mesh_generation.c) | models_mesh_generation | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) | +| 101 | [models_mesh_picking](models/models_mesh_picking.c) | models_mesh_picking | ⭐️⭐️⭐️☆ | 1.7 | 4.0 | [Joel Davis](https://github.com/joeld42) | +| 102 | [models_loading](models/models_loading.c) | models_loading | ⭐️☆☆☆ | 2.0 | 4.2 | [Ray](https://github.com/raysan5) | +| 103 | [models_loading_gltf](models/models_loading_gltf.c) | models_loading_gltf | ⭐️☆☆☆ | 3.7 | 4.2 | [Ray](https://github.com/raysan5) | +| 104 | [models_loading_vox](models/models_loading_vox.c) | models_loading_vox | ⭐️☆☆☆ | 4.0 | 4.0 | [Johann Nadalutti](https://github.com/procfxgen) | +| 105 | [models_loading_m3d](models/models_loading_m3d.c) | models_loading_m3d | ⭐️⭐️☆☆ | 4.5 | 4.5 | [bzt](https://bztsrc.gitlab.io/model3d) | +| 106 | [models_orthographic_projection](models/models_orthographic_projection.c) | models_orthographic_projection | ⭐️☆☆☆ | 2.0 | 3.7 | [Max Danielsson](https://github.com/autious) | +| 107 | [models_point_rendering](models/models_point_rendering.c) | models_point_rendering | ⭐️⭐️⭐️☆ | 5.0 | 5.0 | [Reese Gallagher](https://github.com/satchelfrost) | +| 108 | [models_rlgl_solar_system](models/models_rlgl_solar_system.c) | models_rlgl_solar_system | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Ray](https://github.com/raysan5) | +| 109 | [models_yaw_pitch_roll](models/models_yaw_pitch_roll.c) | models_yaw_pitch_roll | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Berni](https://github.com/Berni8k) | +| 110 | [models_waving_cubes](models/models_waving_cubes.c) | models_waving_cubes | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Codecat](https://github.com/codecat) | +| 111 | [models_heightmap](models/models_heightmap.c) | models_heightmap | ⭐️☆☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) | +| 112 | [models_skybox](models/models_skybox.c) | models_skybox | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) | +| 113 | [models_draw_cube_texture](models/models_draw_cube_texture.c) | models_draw_cube_texture | ⭐️⭐️☆☆ | 4.5 | 4.5 | [Ray](https://github.com/raysan5) | +| 114 | [models_gpu_skinning](models/models_gpu_skinning.c) | models_gpu_skinning | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Daniel Holden](https://github.com/orangeduck) | +| 115 | [models_bone_socket](models/models_bone_socket.c) | models_bone_socket | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [iP](https://github.com/ipzaur) | +| 116 | [models_tesseract_view](models/models_tesseract_view.c) | models_tesseract_view | ⭐️⭐️☆☆ | 5.6-dev | 5.6-dev | [Timothy van der Valk](https://github.com/arceryz) | ### category: shaders @@ -174,34 +175,34 @@ Examples using raylib shaders functionality, including shaders loading, paramete | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 116 | [shaders_basic_lighting](shaders/shaders_basic_lighting.c) | shaders_basic_lighting | ⭐️⭐️⭐️⭐️ | 3.0 | 4.2 | [Chris Camacho](https://github.com/chriscamacho) | -| 117 | [shaders_model_shader](shaders/shaders_model_shader.c) | shaders_model_shader | ⭐️⭐️☆☆ | 1.3 | 3.7 | [Ray](https://github.com/raysan5) | -| 118 | [shaders_shapes_textures](shaders/shaders_shapes_textures.c) | shaders_shapes_textures | ⭐️⭐️☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) | -| 119 | [shaders_custom_uniform](shaders/shaders_custom_uniform.c) | shaders_custom_uniform | ⭐️⭐️☆☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | -| 120 | [shaders_postprocessing](shaders/shaders_postprocessing.c) | shaders_postprocessing | ⭐️⭐️⭐️☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | -| 121 | [shaders_palette_switch](shaders/shaders_palette_switch.c) | shaders_palette_switch | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Marco Lizza](https://github.com/MarcoLizza) | -| 122 | [shaders_raymarching](shaders/shaders_raymarching.c) | shaders_raymarching | ⭐️⭐️⭐️⭐️ | 2.0 | 4.2 | [Ray](https://github.com/raysan5) | -| 123 | [shaders_texture_drawing](shaders/shaders_texture_drawing.c) | shaders_texture_drawing | ⭐️⭐️☆☆ | 2.0 | 3.7 | [Michał Ciesielski](https://github.com/ciessielski) | -| 124 | [shaders_texture_outline](shaders/shaders_texture_outline.c) | shaders_texture_outline | ⭐️⭐️⭐️☆ | 4.0 | 4.0 | [Samuel Skiff](https://github.com/GoldenThumbs) | -| 125 | [shaders_texture_waves](shaders/shaders_texture_waves.c) | shaders_texture_waves | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Anata](https://github.com/anatagawa) | -| 126 | [shaders_julia_set](shaders/shaders_julia_set.c) | shaders_julia_set | ⭐️⭐️⭐️☆ | 2.5 | 4.0 | [Josh Colclough](https://github.com/joshcol9232) | -| 127 | [shaders_eratosthenes](shaders/shaders_eratosthenes.c) | shaders_eratosthenes | ⭐️⭐️⭐️☆ | 2.5 | 4.0 | [ProfJski](https://github.com/ProfJski) | -| 128 | [shaders_fog](shaders/shaders_fog.c) | shaders_fog | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | -| 129 | [shaders_simple_mask](shaders/shaders_simple_mask.c) | shaders_simple_mask | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | -| 130 | [shaders_hot_reloading](shaders/shaders_hot_reloading.c) | shaders_hot_reloading | ⭐️⭐️⭐️☆ | 3.0 | 3.5 | [Ray](https://github.com/raysan5) | -| 131 | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | shaders_mesh_instancing | ⭐️⭐️⭐️⭐️ | 3.7 | 4.2 | [seanpringle](https://github.com/seanpringle) | -| 132 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | shaders_multi_sample2d | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 133 | [shaders_spotlight](shaders/shaders_spotlight.c) | shaders_spotlight | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | -| 134 | [shaders_deferred_render](shaders/shaders_deferred_render.c) | shaders_deferred_render | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [Justin Andreas Lacoste](https://github.com/27justin) | -| 135 | [shaders_hybrid_render](shaders/shaders_hybrid_render.c) | shaders_hybrid_render | ⭐️⭐️⭐️⭐️ | 4.2 | 4.2 | [Buğra Alptekin Sarı](https://github.com/BugraAlptekinSari) | -| 136 | [shaders_texture_tiling](shaders/shaders_texture_tiling.c) | shaders_texture_tiling | ⭐️⭐️☆☆ | 4.5 | 4.5 | [Luis Almeida](https://github.com/luis605) | -| 137 | [shaders_shadowmap](shaders/shaders_shadowmap.c) | shaders_shadowmap | ⭐️⭐️⭐️⭐️ | 5.0 | 5.0 | [TheManTheMythTheGameDev](https://github.com/TheManTheMythTheGameDev) | -| 138 | [shaders_vertex_displacement](shaders/shaders_vertex_displacement.c) | shaders_vertex_displacement | ⭐️⭐️⭐️☆ | 5.0 | 4.5 | [Alex ZH](https://github.com/ZzzhHe) | -| 139 | [shaders_write_depth](shaders/shaders_write_depth.c) | shaders_write_depth | ⭐️⭐️☆☆ | 4.2 | 4.2 | [Buğra Alptekin Sarı](https://github.com/BugraAlptekinSari) | -| 140 | [shaders_basic_pbr](shaders/shaders_basic_pbr.c) | shaders_basic_pbr | ⭐️⭐️⭐️⭐️ | 5.0 | 5.1-dev | [Afan OLOVCIC](https://github.com/_DevDad) | -| 141 | [shaders_lightmap](shaders/shaders_lightmap.c) | shaders_lightmap | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Jussi Viitala](https://github.com/nullstare) | -| 142 | [shaders_rounded_rectangle](shaders/shaders_rounded_rectangle.c) | shaders_rounded_rectangle | ⭐️⭐️⭐️☆ | 5.5 | 5.5 | [Anstro Pleuton](https://github.com/anstropleuton) | -| 143 | [shaders_view_depth](shaders/shaders_view_depth.c) | shaders_view_depth | ⭐️⭐️⭐️☆ | 5.6-dev | 5.6-dev | [Luís Almeida](https://github.com/luis605) | +| 117 | [shaders_basic_lighting](shaders/shaders_basic_lighting.c) | shaders_basic_lighting | ⭐️⭐️⭐️⭐️ | 3.0 | 4.2 | [Chris Camacho](https://github.com/chriscamacho) | +| 118 | [shaders_model_shader](shaders/shaders_model_shader.c) | shaders_model_shader | ⭐️⭐️☆☆ | 1.3 | 3.7 | [Ray](https://github.com/raysan5) | +| 119 | [shaders_shapes_textures](shaders/shaders_shapes_textures.c) | shaders_shapes_textures | ⭐️⭐️☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) | +| 120 | [shaders_custom_uniform](shaders/shaders_custom_uniform.c) | shaders_custom_uniform | ⭐️⭐️☆☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | +| 121 | [shaders_postprocessing](shaders/shaders_postprocessing.c) | shaders_postprocessing | ⭐️⭐️⭐️☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | +| 122 | [shaders_palette_switch](shaders/shaders_palette_switch.c) | shaders_palette_switch | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Marco Lizza](https://github.com/MarcoLizza) | +| 123 | [shaders_raymarching](shaders/shaders_raymarching.c) | shaders_raymarching | ⭐️⭐️⭐️⭐️ | 2.0 | 4.2 | [Ray](https://github.com/raysan5) | +| 124 | [shaders_texture_drawing](shaders/shaders_texture_drawing.c) | shaders_texture_drawing | ⭐️⭐️☆☆ | 2.0 | 3.7 | [Michał Ciesielski](https://github.com/ciessielski) | +| 125 | [shaders_texture_outline](shaders/shaders_texture_outline.c) | shaders_texture_outline | ⭐️⭐️⭐️☆ | 4.0 | 4.0 | [Samuel Skiff](https://github.com/GoldenThumbs) | +| 126 | [shaders_texture_waves](shaders/shaders_texture_waves.c) | shaders_texture_waves | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Anata](https://github.com/anatagawa) | +| 127 | [shaders_julia_set](shaders/shaders_julia_set.c) | shaders_julia_set | ⭐️⭐️⭐️☆ | 2.5 | 4.0 | [Josh Colclough](https://github.com/joshcol9232) | +| 128 | [shaders_eratosthenes](shaders/shaders_eratosthenes.c) | shaders_eratosthenes | ⭐️⭐️⭐️☆ | 2.5 | 4.0 | [ProfJski](https://github.com/ProfJski) | +| 129 | [shaders_fog](shaders/shaders_fog.c) | shaders_fog | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | +| 130 | [shaders_simple_mask](shaders/shaders_simple_mask.c) | shaders_simple_mask | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | +| 131 | [shaders_hot_reloading](shaders/shaders_hot_reloading.c) | shaders_hot_reloading | ⭐️⭐️⭐️☆ | 3.0 | 3.5 | [Ray](https://github.com/raysan5) | +| 132 | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | shaders_mesh_instancing | ⭐️⭐️⭐️⭐️ | 3.7 | 4.2 | [seanpringle](https://github.com/seanpringle) | +| 133 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | shaders_multi_sample2d | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 134 | [shaders_spotlight](shaders/shaders_spotlight.c) | shaders_spotlight | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | +| 135 | [shaders_deferred_render](shaders/shaders_deferred_render.c) | shaders_deferred_render | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [Justin Andreas Lacoste](https://github.com/27justin) | +| 136 | [shaders_hybrid_render](shaders/shaders_hybrid_render.c) | shaders_hybrid_render | ⭐️⭐️⭐️⭐️ | 4.2 | 4.2 | [Buğra Alptekin Sarı](https://github.com/BugraAlptekinSari) | +| 137 | [shaders_texture_tiling](shaders/shaders_texture_tiling.c) | shaders_texture_tiling | ⭐️⭐️☆☆ | 4.5 | 4.5 | [Luis Almeida](https://github.com/luis605) | +| 138 | [shaders_shadowmap](shaders/shaders_shadowmap.c) | shaders_shadowmap | ⭐️⭐️⭐️⭐️ | 5.0 | 5.0 | [TheManTheMythTheGameDev](https://github.com/TheManTheMythTheGameDev) | +| 139 | [shaders_vertex_displacement](shaders/shaders_vertex_displacement.c) | shaders_vertex_displacement | ⭐️⭐️⭐️☆ | 5.0 | 4.5 | [Alex ZH](https://github.com/ZzzhHe) | +| 140 | [shaders_write_depth](shaders/shaders_write_depth.c) | shaders_write_depth | ⭐️⭐️☆☆ | 4.2 | 4.2 | [Buğra Alptekin Sarı](https://github.com/BugraAlptekinSari) | +| 141 | [shaders_basic_pbr](shaders/shaders_basic_pbr.c) | shaders_basic_pbr | ⭐️⭐️⭐️⭐️ | 5.0 | 5.1-dev | [Afan OLOVCIC](https://github.com/_DevDad) | +| 142 | [shaders_lightmap](shaders/shaders_lightmap.c) | shaders_lightmap | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Jussi Viitala](https://github.com/nullstare) | +| 143 | [shaders_rounded_rectangle](shaders/shaders_rounded_rectangle.c) | shaders_rounded_rectangle | ⭐️⭐️⭐️☆ | 5.5 | 5.5 | [Anstro Pleuton](https://github.com/anstropleuton) | +| 144 | [shaders_view_depth](shaders/shaders_view_depth.c) | shaders_view_depth | ⭐️⭐️⭐️☆ | 5.6-dev | 5.6-dev | [Luís Almeida](https://github.com/luis605) | ### category: audio @@ -209,26 +210,26 @@ Examples using raylib audio functionality, including sound/music loading and pla | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 144 | [audio_module_playing](audio/audio_module_playing.c) | audio_module_playing | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 145 | [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐️☆☆☆ | 1.3 | 4.2 | [Ray](https://github.com/raysan5) | -| 146 | [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐️⭐️⭐️☆ | 1.6 | 4.2 | [Ray](https://github.com/raysan5) | -| 147 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | -| 148 | [audio_mixed_processor](audio/audio_mixed_processor.c) | audio_mixed_processor | ⭐️⭐️⭐️⭐️ | 4.2 | 4.2 | [hkc](https://github.com/hatkidchan) | -| 149 | [audio_stream_effects](audio/audio_stream_effects.c) | audio_stream_effects | ⭐️⭐️⭐️⭐️ | 4.2 | 5.0 | [Ray](https://github.com/raysan5) | -| 150 [audio_sound_multi](audio/audio_sound_multi.c) | audio_sound_multi | ⭐️⭐️☆☆ | 4.6 | 4.6 | [Jeffery Myers](https://github.com/JeffM2501) | -| 151 | [audio_sound_positioning](audio/audio_sound_positioning.c) | audio_sound_positioning | ⭐️⭐️☆☆ | 5.5 | 5.5 | [Le Juez Victor](https://github.com/Bigfoot71) | +| 145 | [audio_module_playing](audio/audio_module_playing.c) | audio_module_playing | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 146 | [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐️☆☆☆ | 1.3 | 4.2 | [Ray](https://github.com/raysan5) | +| 147 | [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐️⭐️⭐️☆ | 1.6 | 4.2 | [Ray](https://github.com/raysan5) | +| 148 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | +| 149 | [audio_mixed_processor](audio/audio_mixed_processor.c) | audio_mixed_processor | ⭐️⭐️⭐️⭐️ | 4.2 | 4.2 | [hkc](https://github.com/hatkidchan) | +| 150 | [audio_stream_effects](audio/audio_stream_effects.c) | audio_stream_effects | ⭐️⭐️⭐️⭐️ | 4.2 | 5.0 | [Ray](https://github.com/raysan5) | +| 151 [audio_sound_multi](audio/audio_sound_multi.c) | audio_sound_multi | ⭐️⭐️☆☆ | 4.6 | 4.6 | [Jeffery Myers](https://github.com/JeffM2501) | +| 152 | [audio_sound_positioning](audio/audio_sound_positioning.c) | audio_sound_positioning | ⭐️⭐️☆☆ | 5.5 | 5.5 | [Le Juez Victor](https://github.com/Bigfoot71) | ### category: others -Ex150amples showing raylib misc functionality that does not fit in other categories, like standalone modules usage or examples integrating external libraries. +Ex151amples showing raylib misc functionality that does not fit in other categories, like standalone modules usage or examples integrating external libraries. | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 152 | [rlgl_standalone](others/rlgl_standalone.c) | rlgl_standalone | ⭐️⭐️⭐️⭐️ | 1.6 | 4.0 | [Ray](https://github.com/raysan5) | -| 153 | [rlgl_compute_shader](others/rlgl_compute_shader.c) | rlgl_compute_shader | ⭐️⭐️⭐️⭐️ | 4.0 | 4.0 | [Teddy Astie](https://github.com/tsnake41) | -| 154 | [easings_testbed](others/easings_testbed.c) | easings_testbed | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) | -| 155 | [raylib_opengl_interop](others/raylib_opengl_interop.c) | raylib_opengl_interop | ⭐️⭐️⭐️⭐️ | 3.8 | 4.0 | [Stephan Soller](https://github.com/arkanis) | -| 156 | [embedded_files_loading](others/embedded_files_loading.c) | embedded_files_loading | ⭐️⭐️☆☆ | 3.0 | 3.5 | [Kristian Holmgren](https://github.com/defutura) | -| 157 | [raymath_vector_angle](others/raymath_vector_angle.c) | raymath_vector_angle | ⭐️⭐️☆☆ | 1.0 | 4.6 | [Ray](https://github.com/raysan5) | +| 153 | [rlgl_standalone](others/rlgl_standalone.c) | rlgl_standalone | ⭐️⭐️⭐️⭐️ | 1.6 | 4.0 | [Ray](https://github.com/raysan5) | +| 154 | [rlgl_compute_shader](others/rlgl_compute_shader.c) | rlgl_compute_shader | ⭐️⭐️⭐️⭐️ | 4.0 | 4.0 | [Teddy Astie](https://github.com/tsnake41) | +| 155 | [easings_testbed](others/easings_testbed.c) | easings_testbed | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) | +| 156 | [raylib_opengl_interop](others/raylib_opengl_interop.c) | raylib_opengl_interop | ⭐️⭐️⭐️⭐️ | 3.8 | 4.0 | [Stephan Soller](https://github.com/arkanis) | +| 157 | [embedded_files_loading](others/embedded_files_loading.c) | embedded_files_loading | ⭐️⭐️☆☆ | 3.0 | 3.5 | [Kristian Holmgren](https://github.com/defutura) | +| 158 | [raymath_vector_angle](others/raymath_vector_angle.c) | raymath_vector_angle | ⭐️⭐️☆☆ | 1.0 | 4.6 | [Ray](https://github.com/raysan5) | 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/shapes/shapes_double_pendulum.c b/examples/shapes/shapes_double_pendulum.c new file mode 100644 index 000000000..b4c080be2 --- /dev/null +++ b/examples/shapes/shapes_double_pendulum.c @@ -0,0 +1,188 @@ +/******************************************************************************************* +* +* raylib [shapes] example - Double Pendulum +* +* Example complexity rating: [★★☆☆] 2/4 +* +* Example originally created with raylib 5.5, last time updated with raylib 5.5 +* +* Example contributed by JoeCheong (@Joecheong2006) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025 JoeCheong (@Joecheong2006) +* +********************************************************************************************/ + +#include "raylib.h" + +#include // Required for: sin(), cos(), PI + +//---------------------------------------------------------------------------------- +// Macro Helpers +//---------------------------------------------------------------------------------- +#define SCREEN_WIDTH 800 +#define SCREEN_HEIGHT 450 + +#define CENTER_X SCREEN_WIDTH * 0.5 +#define CENTER_Y SCREEN_HEIGHT * 0.5 - 100 + +// Constant for Simulation +#define SIMULATION_STEPS 30 +#define G 9.81 + +// Helpers for Angles Conversion +#define RAD(x) x * PI / 180.0 +#define DEG(x) x * 180.0 / PI + +#define scalar float + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +static Vector2 CalculatePendulumEndPoint(scalar l, scalar theta); +static Vector2 CalculateDoublePendulumEndPoint(scalar l1, scalar theta1, scalar l2, scalar theta2); + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + SetConfigFlags(FLAG_WINDOW_HIGHDPI); + InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [shapes] example - Double Pendulum"); + + // Simulation Paramters + //-------------------------------------------------------------------------------------- + scalar l1 = 15, m1 = 0.2, theta1 = RAD(170), w1 = 0; + scalar l2 = 15, m2 = 0.1, theta2 = RAD(0), w2 = 0; + scalar lengthScaler = 0.1; + scalar totalM = m1 + m2; + + Vector2 previousPosition = CalculateDoublePendulumEndPoint(l1, theta1, l2, theta2); + previousPosition.x += CENTER_X; + previousPosition.y += CENTER_Y; + + // Scale length + scalar L1 = l1 * lengthScaler; + scalar L2 = l2 * lengthScaler; + + // Draw Parameters + //-------------------------------------------------------------------------------------- + int lineThick = 20, trailThick = 2; + float fateAlpha = 0.01; + + // Create Framebuffer + //-------------------------------------------------------------------------------------- + RenderTexture2D target = LoadRenderTexture(SCREEN_WIDTH, SCREEN_HEIGHT); + SetTextureFilter(target.texture, TEXTURE_FILTER_BILINEAR); + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + scalar dt = GetFrameTime(); + scalar step = dt / SIMULATION_STEPS, step2 = step * step; + + // Update Physics - larger steps = better approximation + //---------------------------------------------------------------------------------- + for (int i = 0; i < SIMULATION_STEPS; ++i) + { + scalar delta = theta1 - theta2; + scalar sinD = sin(delta), cosD = cos(delta), cos2D = cos(2 * delta); + scalar ww1 = w1 * w1, ww2 = w2 * w2; + + // Calculate a1 + scalar a1 = (-G * (2 * m1 + m2) * sin(theta1) + - m2 * G * sin(theta1 - 2 * theta2) + - 2 * sinD * m2 * (ww2 * L2 + ww1 * L1 * cosD)) + / (L1 * (2 * m1 + m2 - m2 * cos2D)); + + // Calculate a2 + scalar a2 = (2 * sinD * (ww1 * L1 * totalM + + G * totalM * cos(theta1) + + ww2 * L2 * m2 * cosD)) + / (L2 * (2 * m1 + m2 - m2 * cos2D)); + + // Update thetas + theta1 += w1 * step + 0.5 * a1 * step2; + theta2 += w2 * step + 0.5 * a2 * step2; + + // Update omegas + w1 += a1 * step; + w2 += a2 * step; + } + //---------------------------------------------------------------------------------- + + // Calculate position + Vector2 currentPosition = CalculateDoublePendulumEndPoint(l1, theta1, l2, theta2); + currentPosition.x += CENTER_X; + currentPosition.y += CENTER_Y; + + // Draw to framebuffer + //---------------------------------------------------------------------------------- + BeginTextureMode(target); + + // Draw a transparent rectangle - smaller alpha = longer trails + DrawRectangle(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, Fade(BLACK, fateAlpha)); + + // Draw trail + DrawCircleV(previousPosition, trailThick, RED); + DrawLineEx(previousPosition, currentPosition, trailThick * 2, RED); + + EndTextureMode(); + //---------------------------------------------------------------------------------- + + // Update previous position + previousPosition = currentPosition; + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(BLACK); + + // Draw Trails Texture + DrawTextureRec(target.texture, (Rectangle){ 0, 0, target.texture.width, -target.texture.height }, + (Vector2){ 0, 0 }, WHITE); + + // Draw Double Pendulum + DrawRectanglePro((Rectangle){ CENTER_X, CENTER_Y, 10 * l1, lineThick }, + (Vector2){0, lineThick * 0.5}, 90 - DEG(theta1), RAYWHITE); + + Vector2 endpoint1 = CalculatePendulumEndPoint(l1, theta1); + DrawRectanglePro((Rectangle){ CENTER_X + endpoint1.x, CENTER_Y + endpoint1.y, 10 * l2, lineThick }, + (Vector2){0, lineThick * 0.5}, 90 - DEG(theta2), RAYWHITE); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadRenderTexture(target); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + +// Calculate Pendulum End Point +static Vector2 CalculatePendulumEndPoint(scalar l, scalar theta) +{ + return (Vector2){ 10 * l * sin(theta), 10 * l * cos(theta) }; +} + +// Calculate Double Pendulum End Point +static Vector2 CalculateDoublePendulumEndPoint(scalar l1, scalar theta1, scalar l2, scalar theta2) +{ + Vector2 endpoint1 = CalculatePendulumEndPoint(l1, theta1); + Vector2 endpoint2 = CalculatePendulumEndPoint(l2, theta2); + return (Vector2){ endpoint1.x + endpoint2.x, endpoint1.y + endpoint2.y }; +} diff --git a/examples/shapes/shapes_double_pendulum.png b/examples/shapes/shapes_double_pendulum.png new file mode 100644 index 0000000000000000000000000000000000000000..bee7c6a5e7a42a385116d88e1778e5005171125e GIT binary patch literal 291645 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%0A(o!~iM} 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Ü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 literal 0 HcmV?d00001 From f3854cb4061273f7a40a627a41df7723bbac788a Mon Sep 17 00:00:00 2001 From: JoeCheong <95924085+Joecheong2006@users.noreply.github.com> Date: Tue, 22 Jul 2025 11:39:24 -0700 Subject: [PATCH 102/242] Fixed typo --- examples/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Makefile b/examples/Makefile index 2f32f654f..b5b6ef2bc 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -549,7 +549,7 @@ SHAPES = \ shapes/shapes_splines_drawing \ shapes/shapes_top_down_lights \ shapes/shapes_digital_clock \ - shapes/shapes_dobule_pendulum + shapes/shapes_double_pendulum TEXTURES = \ textures/textures_background_scrolling \ From 8a0ab163d3da1ea3fde0b85e7c705c39127cc6f8 Mon Sep 17 00:00:00 2001 From: Joecheong2006 Date: Tue, 22 Jul 2025 11:50:48 -0700 Subject: [PATCH 103/242] Changed to 4 spaces --- examples/shapes/shapes_double_pendulum.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shapes/shapes_double_pendulum.c b/examples/shapes/shapes_double_pendulum.c index b4c080be2..301022da6 100644 --- a/examples/shapes/shapes_double_pendulum.c +++ b/examples/shapes/shapes_double_pendulum.c @@ -51,7 +51,7 @@ int main(void) { // Initialization //-------------------------------------------------------------------------------------- - SetConfigFlags(FLAG_WINDOW_HIGHDPI); + SetConfigFlags(FLAG_WINDOW_HIGHDPI); InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [shapes] example - Double Pendulum"); // Simulation Paramters From b6ae5af4e360d71ae3df8db1d5a92202c5d3ae0c Mon Sep 17 00:00:00 2001 From: Joecheong2006 Date: Tue, 22 Jul 2025 11:57:16 -0700 Subject: [PATCH 104/242] Removed redefined RAD and DEG --- examples/shapes/shapes_double_pendulum.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/examples/shapes/shapes_double_pendulum.c b/examples/shapes/shapes_double_pendulum.c index 301022da6..cf94c8747 100644 --- a/examples/shapes/shapes_double_pendulum.c +++ b/examples/shapes/shapes_double_pendulum.c @@ -32,10 +32,6 @@ #define SIMULATION_STEPS 30 #define G 9.81 -// Helpers for Angles Conversion -#define RAD(x) x * PI / 180.0 -#define DEG(x) x * 180.0 / PI - #define scalar float //---------------------------------------------------------------------------------- @@ -56,8 +52,8 @@ int main(void) // Simulation Paramters //-------------------------------------------------------------------------------------- - scalar l1 = 15, m1 = 0.2, theta1 = RAD(170), w1 = 0; - scalar l2 = 15, m2 = 0.1, theta2 = RAD(0), w2 = 0; + scalar l1 = 15, m1 = 0.2, theta1 = DEG2RAD * 170, w1 = 0; + scalar l2 = 15, m2 = 0.1, theta2 = DEG2RAD * 0, w2 = 0; scalar lengthScaler = 0.1; scalar totalM = m1 + m2; @@ -153,11 +149,11 @@ int main(void) // Draw Double Pendulum DrawRectanglePro((Rectangle){ CENTER_X, CENTER_Y, 10 * l1, lineThick }, - (Vector2){0, lineThick * 0.5}, 90 - DEG(theta1), RAYWHITE); + (Vector2){0, lineThick * 0.5}, 90 - RAD2DEG * theta1, RAYWHITE); Vector2 endpoint1 = CalculatePendulumEndPoint(l1, theta1); DrawRectanglePro((Rectangle){ CENTER_X + endpoint1.x, CENTER_Y + endpoint1.y, 10 * l2, lineThick }, - (Vector2){0, lineThick * 0.5}, 90 - DEG(theta2), RAYWHITE); + (Vector2){0, lineThick * 0.5}, 90 - RAD2DEG * theta2, RAYWHITE); EndDrawing(); //---------------------------------------------------------------------------------- From 10fadd91856b682ad45c9626f65ad6614c5f4268 Mon Sep 17 00:00:00 2001 From: Joecheong2006 Date: Tue, 22 Jul 2025 11:59:46 -0700 Subject: [PATCH 105/242] Replaced scalar to float --- examples/shapes/shapes_double_pendulum.c | 36 +++++++++++------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/examples/shapes/shapes_double_pendulum.c b/examples/shapes/shapes_double_pendulum.c index cf94c8747..85ca4af66 100644 --- a/examples/shapes/shapes_double_pendulum.c +++ b/examples/shapes/shapes_double_pendulum.c @@ -32,13 +32,11 @@ #define SIMULATION_STEPS 30 #define G 9.81 -#define scalar float - //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- -static Vector2 CalculatePendulumEndPoint(scalar l, scalar theta); -static Vector2 CalculateDoublePendulumEndPoint(scalar l1, scalar theta1, scalar l2, scalar theta2); +static Vector2 CalculatePendulumEndPoint(float l, float theta); +static Vector2 CalculateDoublePendulumEndPoint(float l1, float theta1, float l2, float theta2); //------------------------------------------------------------------------------------ // Program main entry point @@ -52,18 +50,18 @@ int main(void) // Simulation Paramters //-------------------------------------------------------------------------------------- - scalar l1 = 15, m1 = 0.2, theta1 = DEG2RAD * 170, w1 = 0; - scalar l2 = 15, m2 = 0.1, theta2 = DEG2RAD * 0, w2 = 0; - scalar lengthScaler = 0.1; - scalar totalM = m1 + m2; + float l1 = 15, m1 = 0.2, theta1 = DEG2RAD * 170, w1 = 0; + float l2 = 15, m2 = 0.1, theta2 = DEG2RAD * 0, w2 = 0; + float lengthScaler = 0.1; + float totalM = m1 + m2; Vector2 previousPosition = CalculateDoublePendulumEndPoint(l1, theta1, l2, theta2); previousPosition.x += CENTER_X; previousPosition.y += CENTER_Y; // Scale length - scalar L1 = l1 * lengthScaler; - scalar L2 = l2 * lengthScaler; + float L1 = l1 * lengthScaler; + float L2 = l2 * lengthScaler; // Draw Parameters //-------------------------------------------------------------------------------------- @@ -82,25 +80,25 @@ int main(void) while (!WindowShouldClose()) // Detect window close button or ESC key { // Update - scalar dt = GetFrameTime(); - scalar step = dt / SIMULATION_STEPS, step2 = step * step; + float dt = GetFrameTime(); + float step = dt / SIMULATION_STEPS, step2 = step * step; // Update Physics - larger steps = better approximation //---------------------------------------------------------------------------------- for (int i = 0; i < SIMULATION_STEPS; ++i) { - scalar delta = theta1 - theta2; - scalar sinD = sin(delta), cosD = cos(delta), cos2D = cos(2 * delta); - scalar ww1 = w1 * w1, ww2 = w2 * w2; + float delta = theta1 - theta2; + float sinD = sin(delta), cosD = cos(delta), cos2D = cos(2 * delta); + float ww1 = w1 * w1, ww2 = w2 * w2; // Calculate a1 - scalar a1 = (-G * (2 * m1 + m2) * sin(theta1) + float a1 = (-G * (2 * m1 + m2) * sin(theta1) - m2 * G * sin(theta1 - 2 * theta2) - 2 * sinD * m2 * (ww2 * L2 + ww1 * L1 * cosD)) / (L1 * (2 * m1 + m2 - m2 * cos2D)); // Calculate a2 - scalar a2 = (2 * sinD * (ww1 * L1 * totalM + float a2 = (2 * sinD * (ww1 * L1 * totalM + G * totalM * cos(theta1) + ww2 * L2 * m2 * cosD)) / (L2 * (2 * m1 + m2 - m2 * cos2D)); @@ -170,13 +168,13 @@ int main(void) } // Calculate Pendulum End Point -static Vector2 CalculatePendulumEndPoint(scalar l, scalar theta) +static Vector2 CalculatePendulumEndPoint(float l, float theta) { return (Vector2){ 10 * l * sin(theta), 10 * l * cos(theta) }; } // Calculate Double Pendulum End Point -static Vector2 CalculateDoublePendulumEndPoint(scalar l1, scalar theta1, scalar l2, scalar theta2) +static Vector2 CalculateDoublePendulumEndPoint(float l1, float theta1, float l2, float theta2) { Vector2 endpoint1 = CalculatePendulumEndPoint(l1, theta1); Vector2 endpoint2 = CalculatePendulumEndPoint(l2, theta2); From dfa21c2d1f83f7327a484837a9345db58aaeb110 Mon Sep 17 00:00:00 2001 From: Mike Will Date: Wed, 23 Jul 2025 11:56:36 -0400 Subject: [PATCH 106/242] [build][zig] use `addLibrary()` and set root modules manually These changes will be required by Zig version 0.15.0. --- build.zig | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/build.zig b/build.zig index 501d7a229..409c57ab5 100644 --- a/build.zig +++ b/build.zig @@ -153,18 +153,14 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. try raylib_flags_arr.appendSlice(&config_h_flags); } - const raylib = if (options.shared) - b.addSharedLibrary(.{ - .name = "raylib", + const raylib = b.addLibrary(.{ + .name = "raylib", + .linkage = if (options.shared) .dynamic else .static, + .root_module = b.createModule(.{ .target = target, .optimize = optimize, - }) - else - b.addStaticLibrary(.{ - .name = "raylib", - .target = target, - .optimize = optimize, - }); + }), + }); raylib.linkLibC(); // No GLFW required on PLATFORM_DRM @@ -550,10 +546,13 @@ fn addExamples( if (std.mem.eql(u8, "core_loading_thread", name) and target.result.os.tag == .windows) continue; if (target.result.os.tag == .emscripten) { - const exe_lib = b.addStaticLibrary(.{ + const exe_lib = b.addLibrary(.{ .name = name, - .target = target, - .optimize = optimize, + .linkage = .static, + .root_module = b.createModule(.{ + .target = target, + .optimize = optimize, + }), }); exe_lib.addCSourceFile(.{ .file = b.path(path), @@ -633,8 +632,10 @@ fn addExamples( } else { const exe = b.addExecutable(.{ .name = name, - .target = target, - .optimize = optimize, + .root_module = b.createModule(.{ + .target = target, + .optimize = optimize, + }), }); exe.addCSourceFile(.{ .file = b.path(path), .flags = &.{} }); exe.linkLibC(); From d6a897e5519a346825dd7109c642abddab734b9b Mon Sep 17 00:00:00 2001 From: danil <61111955+danilwhale@users.noreply.github.com> Date: Wed, 23 Jul 2025 21:41:45 +0300 Subject: [PATCH 107/242] [rlgl]: Add rlGetProcAddress --- src/rlgl.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index b6703c923..2adf1753d 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -710,6 +710,7 @@ RLAPI void rlSetBlendFactorsSeparate(int glSrcRGB, int glDstRGB, int glSrcAlpha, RLAPI void rlglInit(int width, int height); // Initialize rlgl (buffers, shaders, textures, states) RLAPI void rlglClose(void); // De-initialize rlgl (buffers, shaders, textures) RLAPI void rlLoadExtensions(void *loader); // Load OpenGL extensions (loader function required) +RLAPI void *rlGetProcAddress(const char *procName); // Get OpenGL procedure address RLAPI int rlGetVersion(void); // Get current OpenGL version RLAPI void rlSetFramebufferWidth(int width); // Set current framebuffer width RLAPI int rlGetFramebufferWidth(void); // Get default framebuffer width @@ -1041,10 +1042,15 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad // Types and Structures Definition //---------------------------------------------------------------------------------- #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + +typedef void *(*rlglLoadProc)(const char *name); // OpenGL extension functions loader signature (same as GLADloadproc) + typedef struct rlglData { rlRenderBatch *currentBatch; // Current render batch rlRenderBatch defaultBatch; // Default internal render batch + rlglLoadProc loader; // OpenGL function loader + struct { int vertexCounter; // Current active render batch vertex counter (generic, used for all batches) float texcoordx, texcoordy; // Current active texture coordinate (added on glVertex*()) @@ -1114,8 +1120,6 @@ typedef struct rlglData { } ExtSupported; // Extensions supported flags } rlglData; -typedef void *(*rlglLoadProc)(const char *name); // OpenGL extension functions loader signature (same as GLADloadproc) - #endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 //---------------------------------------------------------------------------------- @@ -2567,6 +2571,8 @@ void rlLoadExtensions(void *loader) TRACELOG(RL_LOG_INFO, " > Version: %s", glGetString(GL_VERSION)); TRACELOG(RL_LOG_INFO, " > GLSL: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); + RLGL.loader = (rlglLoadProc)loader; + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // NOTE: Anisotropy levels capability is an extension #ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT @@ -2625,6 +2631,11 @@ void rlLoadExtensions(void *loader) #endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 } +// Get OpenGL procedure address +void *rlGetProcAddress(const char *procName) { + return RLGL.loader(procName); +} + // Get current OpenGL version int rlGetVersion(void) { From 32960af1dc3905f046fe64a56dbb50d32375990e Mon Sep 17 00:00:00 2001 From: Karim Ahmed Date: Thu, 24 Jul 2025 09:48:52 +0100 Subject: [PATCH 108/242] Fixed: Added CMake support for SDL3. Now supports including SDL2 or SDL3 as a subdirectory within the project. The system will first check for SDL3, then SDL2. If neither is found, it will attempt find_package(SDL3), followed by find_package(SDL2). If all these checks fail, the process will terminate with an error. --- cmake/LibraryConfigurations.cmake | 33 ++++++++++++++++++++++++++++--- src/platforms/rcore_desktop_sdl.c | 18 +++++++++++++++-- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index 00dda033a..e4655500e 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -101,10 +101,37 @@ elseif ("${PLATFORM}" MATCHES "DRM") set(LIBS_PRIVATE ${GLESV2} ${EGL} ${DRM} ${GBM} atomic pthread m dl) elseif ("${PLATFORM}" MATCHES "SDL") - find_package(SDL2 REQUIRED) - set(PLATFORM_CPP "PLATFORM_DESKTOP_SDL") - set(LIBS_PRIVATE SDL2::SDL2) + # First, check if SDL is included as a subdirectory + if(TARGET SDL3::SDL3) + message(STATUS "Using SDL3 from subdirectory") + set(PLATFORM_CPP "PLATFORM_DESKTOP_SDL") + set(LIBS_PRIVATE SDL3::SDL3) + add_compile_definitions(USING_SDL3_PROJECT) + elseif(TARGET SDL2::SDL2) + message(STATUS "Using SDL2 from subdirectory") + set(PLATFORM_CPP "PLATFORM_DESKTOP_SDL") + set(LIBS_PRIVATE SDL2::SDL2) + add_compile_definitions(USING_SDL2_PROJECT) + else() + # No SDL added via add_subdirectory(), try find_package() + message(STATUS "No SDL target from subdirectory, searching via find_package()...") + # First try SDL3 + find_package(SDL3 QUIET) + if(SDL3_FOUND) + message(STATUS "Found SDL3 via find_package()") + set(PLATFORM_CPP "PLATFORM_DESKTOP_SDL") + set(LIBS_PRIVATE SDL3::SDL3) + add_compile_definitions(USING_SDL3_PACKAGE) + else() + # Fallback to SDL2 + find_package(SDL2 REQUIRED) + message(STATUS "Found SDL2 via find_package()") + set(PLATFORM_CPP "PLATFORM_DESKTOP_SDL") + set(LIBS_PRIVATE SDL2::SDL2) + add_compile_definitions(USING_SDL2_PACKAGE) + endif() + endif() endif () if (NOT ${OPENGL_VERSION} MATCHES "OFF") diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 59f286307..82337dd4e 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -52,13 +52,27 @@ #ifndef SDL_ENABLE_OLD_NAMES #define SDL_ENABLE_OLD_NAMES // Just in case we're on SDL3, we need some in-between compatibily #endif -#include "SDL.h" // SDL base library (window/rendered, input, timing... functionality) +// SDL base library (window/rendered, input, timing... functionality) +#ifdef USING_SDL3_PROJECT + #include "SDL3/SDL.h" +#elif USING_SDL2_PROJECT + #include "SDL2/SDL.h" +#else + #include "SDL.h" +#endif #if defined(GRAPHICS_API_OPENGL_ES2) // It seems it does not need to be included to work //#include "SDL_opengles2.h" #else - #include "SDL_opengl.h" // SDL OpenGL functionality (if required, instead of internal renderer) + // SDL OpenGL functionality (if required, instead of internal renderer) + #ifdef USING_SDL3_PROJECT + #include "SDL3/SDL_opengl.h" + #elif USING_SDL2_PROJECT + #include "SDL2/SDL_opengl.h" + #else + #include "SDL_opengl.h" + #endif #endif //---------------------------------------------------------------------------------- From d7893141f3d8ec4d8ce525bad02a8a12cce4e84b Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 26 Jul 2025 11:44:36 +0200 Subject: [PATCH 109/242] REVIEWED: `rl_gputex.h` library to be usable standalone --- src/external/rl_gputex.h | 209 ++++++++++++++++++++++----------------- src/rtextures.c | 18 +--- 2 files changed, 125 insertions(+), 102 deletions(-) diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index 4b4e9beb4..276791430 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -30,10 +30,10 @@ * #define RL_GPUTEX_SUPPORT_ASTC * Define desired file formats to be supported * -* #define RL_GPUTEX_SHOW_WARN_INFO +* #define RL_GPUTEX_SHOW_LOG_INFO * Define, if you wish to see warnings generated by the library -* This will include unless you provide your own RL_GPUTEX_WARN -* #define RL_GPUTEX_WARN +* This will include unless you provide your own RL_GPUTEX_LOG +* #define RL_GPUTEX_LOG * Define, if you wish to provide your own warning function * Make sure that this macro puts newline character '\n' at the end * @@ -59,15 +59,16 @@ * There is no need to do so when statically linking * * VERSIONS HISTORY: -* 1.0 (17-Sep-2022) First version has been created by migrating part of compressed-texture-loading -* functionality from Raylib src/rtextures.c into self-contained library * 1.1 (15-Jul-2025) Several minor fixes related to specific image formats; some work has been done * in order to decouple the library from Raylib by introducing few new macros; library still * requires Raylib in order to function properly * +* 1.0 (17-Sep-2022) First version has been created by migrating part of compressed-texture-loading +* functionality from Raylib src/rtextures.c into self-contained library +* * LICENSE: zlib/libpng * -* Copyright (c) 2013-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -93,6 +94,37 @@ #define RLGPUTEXAPI // Functions defined as 'extern' by default (implicit specifiers) #endif +// Texture pixel formats +// NOTE: Support depends on OpenGL version +// WARNING: Enum values aligned with raylib/rlgl equivalent PixelFormat/rlPixelFormat enum, +// to avoid value mapping between the 3 libraries format values +typedef enum { + RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha) + RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA, // 8*2 bpp (2 channels) + RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G6B5, // 16 bpp + RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8, // 24 bpp + RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) + RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha) + RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, // 32 bpp + RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) + RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) + RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) + RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16, // 16 bpp (1 channel - half float) + RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16G16B16, // 16*3 bpp (3 channels - half float) + RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16, // 16*4 bpp (4 channels - half float) + RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) + RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) + RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp + RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT5_RGBA, // 8 bpp + RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC1_RGB, // 4 bpp + RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC2_RGB, // 4 bpp + RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA, // 8 bpp + RL_GPUTEX_PIXELFORMAT_COMPRESSED_PVRT_RGB, // 4 bpp + RL_GPUTEX_PIXELFORMAT_COMPRESSED_PVRT_RGBA, // 4 bpp + RL_GPUTEX_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA, // 8 bpp + RL_GPUTEX_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA // 2 bpp +} rlGpuTexPixelFormat; + //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- @@ -124,12 +156,12 @@ RLGPUTEXAPI int rl_save_ktx_to_memory(const char *fileName, void *data, int widt #if defined(RL_GPUTEX_IMPLEMENTATION) -#if defined(RL_GPUTEX_SHOW_WARN_INFO) && !defined(RL_GPUTEX_WARN) +#if defined(RL_GPUTEX_SHOW_LOG_INFO) && !defined(RL_GPUTEX_LOG) #include // Required for: printf() #endif #if !defined(RL_GPUTEX_MALLOC) || !defined(RL_GPUTEX_NULL) - #include // Required for: NULL, malloc(), calloc(), free(), realloc() + #include // Required for: NULL, malloc(), calloc(), free() #endif #if !defined(RL_GPUTEX_MEMCPY) @@ -149,7 +181,6 @@ RLGPUTEXAPI int rl_save_ktx_to_memory(const char *fileName, void *data, int widt #if !defined(RL_GPUTEX_MALLOC) #define RL_GPUTEX_MALLOC(sz) malloc(sz) #define RL_GPUTEX_CALLOC(num, sz) calloc(num, sz) - #define RL_GPUTEX_REALLOC(ptr, sz) realloc(ptr, sz) #define RL_GPUTEX_FREE(ptr) free(ptr) #endif @@ -161,20 +192,20 @@ RLGPUTEXAPI int rl_save_ktx_to_memory(const char *fileName, void *data, int widt #define RL_GPUTEX_MEMCPY(dest, src, num) memcpy(dest, src, num) #endif -// Simple warning system to avoid RPNG_LOG() calls if required +// Simple warning logging system to avoid LOG() calls if required // NOTE: Avoiding those calls, also avoids const strings memory usage // WARN: This macro expects that newline character is added automatically // in order to match the functionality of Raylib's TRACELOG() -#if defined(RL_GPUTEX_SHOW_WARN_INFO) - #if !defined(RL_GPUTEX_WARN) - #define RL_GPUTEX_WARN(...) (void)(printf("RL_GPUTEX: WARNING: " __VA_ARGS__), printf("\n")) +#if defined(RL_GPUTEX_SHOW_LOG_INFO) + #if !defined(RL_GPUTEX_LOG) + #define RL_GPUTEX_LOG(...) (void)(printf("RL_GPUTEX: WARNING: " __VA_ARGS__), printf("\n")) #endif #else - #if defined(RL_GPUTEX_WARN) - // undefine it first in order to supress warnings about macro redefinition - #undef RL_GPUTEX_WARN + #if defined(RL_GPUTEX_LOG) + // Undefine it first in order to supress warnings about macro redefinition + #undef RL_GPUTEX_LOG #endif - #define RL_GPUTEX_WARN(...) + #define RL_GPUTEX_LOG(...) #endif //---------------------------------------------------------------------------------- @@ -246,7 +277,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ if ((dds_header_id[0] != 'D') || (dds_header_id[1] != 'D') || (dds_header_id[2] != 'S') || (dds_header_id[3] != ' ')) { - RL_GPUTEX_WARN("DDS file data not valid"); + RL_GPUTEX_LOG("DDS file data not valid"); } else { @@ -257,8 +288,8 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ *width = header->width; *height = header->height; - if (*width % 4 != 0) RL_GPUTEX_WARN("DDS file width must be multiple of 4. Image will not display correctly"); - if (*height % 4 != 0) RL_GPUTEX_WARN("DDS file height must be multiple of 4. Image will not display correctly"); + if (*width % 4 != 0) RL_GPUTEX_LOG("DDS file width must be multiple of 4. Image will not display correctly"); + if (*height % 4 != 0) RL_GPUTEX_LOG("DDS file height must be multiple of 4. Image will not display correctly"); image_pixel_size = header->width*header->height; @@ -275,7 +306,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); - *format = PIXELFORMAT_UNCOMPRESSED_R5G6B5; + *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G6B5; } else if (header->ddspf.flags == 0x41) // With alpha channel { @@ -297,7 +328,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ ((unsigned short *)image_data)[i] += alpha; } - *format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1; + *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1; } else if (header->ddspf.a_bit_mask == 0xf000) // 4bit alpha { @@ -317,7 +348,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ ((unsigned short *)image_data)[i] += alpha; } - *format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4; + *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4; } } } @@ -329,7 +360,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); - *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8; + *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8; } else if ((header->ddspf.flags == 0x41) && (header->ddspf.rgb_bit_count == 32)) // DDS_RGBA, no compressed { @@ -351,7 +382,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ ((unsigned char *)image_data)[i + 2] = blue; } - *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; } else if (((header->ddspf.flags == 0x04) || (header->ddspf.flags == 0x05)) && (header->ddspf.fourcc > 0)) // Compressed { @@ -369,11 +400,11 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ { case FOURCC_DXT1: { - if (header->ddspf.flags == 0x04) *format = PIXELFORMAT_COMPRESSED_DXT1_RGB; - else *format = PIXELFORMAT_COMPRESSED_DXT1_RGBA; + if (header->ddspf.flags == 0x04) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT1_RGB; + else *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT1_RGBA; } break; - case FOURCC_DXT3: *format = PIXELFORMAT_COMPRESSED_DXT3_RGBA; break; - case FOURCC_DXT5: *format = PIXELFORMAT_COMPRESSED_DXT5_RGBA; break; + case FOURCC_DXT3: *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT3_RGBA; break; + case FOURCC_DXT5: *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT5_RGBA; break; default: break; } } @@ -427,7 +458,7 @@ void *rl_load_pkm_from_memory(const unsigned char *file_data, unsigned int file_ if ((header->id[0] != 'P') || (header->id[1] != 'K') || (header->id[2] != 'M') || (header->id[3] != ' ')) { - RL_GPUTEX_WARN("PKM file data not valid"); + RL_GPUTEX_LOG("PKM file data not valid"); } else { @@ -451,9 +482,9 @@ void *rl_load_pkm_from_memory(const unsigned char *file_data, unsigned int file_ RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); - if (header->format == 0) *format = PIXELFORMAT_COMPRESSED_ETC1_RGB; - else if (header->format == 1) *format = PIXELFORMAT_COMPRESSED_ETC2_RGB; - else if (header->format == 3) *format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA; + if (header->format == 0) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC1_RGB; + else if (header->format == 1) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC2_RGB; + else if (header->format == 3) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA; } } @@ -511,7 +542,7 @@ void *rl_load_ktx_from_memory(const unsigned char *file_data, unsigned int file_ if ((header->id[1] != 'K') || (header->id[2] != 'T') || (header->id[3] != 'X') || (header->id[4] != ' ') || (header->id[5] != '1') || (header->id[6] != '1')) { - RL_GPUTEX_WARN("KTX file data not valid"); + RL_GPUTEX_LOG("KTX file data not valid"); } else { @@ -530,9 +561,9 @@ void *rl_load_ktx_from_memory(const unsigned char *file_data, unsigned int file_ RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); - if (header->gl_internal_format == 0x8D64) *format = PIXELFORMAT_COMPRESSED_ETC1_RGB; - else if (header->gl_internal_format == 0x9274) *format = PIXELFORMAT_COMPRESSED_ETC2_RGB; - else if (header->gl_internal_format == 0x9278) *format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA; + if (header->gl_internal_format == 0x8D64) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC1_RGB; + else if (header->gl_internal_format == 0x9274) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC2_RGB; + else if (header->gl_internal_format == 0x9278) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA; // TODO: Support uncompressed data formats? Right now it returns format = 0! } @@ -609,7 +640,7 @@ int rl_save_ktx(const char *file_name, void *data, int width, int height, int fo // NOTE: We can save into a .ktx all PixelFormats supported by raylib, including compressed formats like DXT, ETC or ASTC - if (header.gl_format == -1) RL_GPUTEX_WARN("GL format not supported for KTX export (%i)", header.gl_format); + if (header.gl_format == -1) RL_GPUTEX_LOG("GL format not supported for KTX export (%i)", header.gl_format); else { RL_GPUTEX_MEMCPY(file_data_ptr, &header, sizeof(ktx_header)); @@ -642,16 +673,16 @@ int rl_save_ktx(const char *file_name, void *data, int width, int height, int fo { unsigned int count = (unsigned int)fwrite(file_data, sizeof(unsigned char), data_size, file); - if (count == 0) RL_GPUTEX_WARN("FILEIO: [%s] Failed to write file", file_name); - else if (count != data_size) RL_GPUTEX_WARN("FILEIO: [%s] File partially written", file_name); + if (count == 0) RL_GPUTEX_LOG("FILEIO: [%s] Failed to write file", file_name); + else if (count != data_size) RL_GPUTEX_LOG("FILEIO: [%s] File partially written", file_name); else (void)0; // WARN: this branch is handled by Raylib, since rl_gputex only prints warnings int result = fclose(file); - if (result != 0) RL_GPUTEX_WARN("FILEIO: [%s] Failed to close file", file_name); + if (result != 0) RL_GPUTEX_LOG("FILEIO: [%s] Failed to close file", file_name); if (result == 0 && count == data_size) success = true; } - else RL_GPUTEX_WARN("FILEIO: [%s] Failed to open file", file_name); + else RL_GPUTEX_LOG("FILEIO: [%s] Failed to open file", file_name); RL_GPUTEX_FREE(file_data); // Free file data buffer @@ -735,7 +766,7 @@ void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_ if ((header->id[0] != 'P') || (header->id[1] != 'V') || (header->id[2] != 'R') || (header->id[3] != 3)) { - RL_GPUTEX_WARN("PVR file data not valid"); + RL_GPUTEX_LOG("PVR file data not valid"); } else { @@ -746,24 +777,24 @@ void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_ *mips = header->num_mipmaps; // Check data format - if (((header->channels[0] == 'l') && (header->channels[1] == 0)) && (header->channel_depth[0] == 8)) *format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; - else if (((header->channels[0] == 'l') && (header->channels[1] == 'a')) && ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8))) *format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA; + if (((header->channels[0] == 'l') && (header->channels[1] == 0)) && (header->channel_depth[0] == 8)) *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; + else if (((header->channels[0] == 'l') && (header->channels[1] == 'a')) && ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8))) *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA; else if ((header->channels[0] == 'r') && (header->channels[1] == 'g') && (header->channels[2] == 'b')) { if (header->channels[3] == 'a') { - if ((header->channel_depth[0] == 5) && (header->channel_depth[1] == 5) && (header->channel_depth[2] == 5) && (header->channel_depth[3] == 1)) *format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1; - else if ((header->channel_depth[0] == 4) && (header->channel_depth[1] == 4) && (header->channel_depth[2] == 4) && (header->channel_depth[3] == 4)) *format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4; - else if ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8) && (header->channel_depth[2] == 8) && (header->channel_depth[3] == 8)) *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + if ((header->channel_depth[0] == 5) && (header->channel_depth[1] == 5) && (header->channel_depth[2] == 5) && (header->channel_depth[3] == 1)) *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1; + else if ((header->channel_depth[0] == 4) && (header->channel_depth[1] == 4) && (header->channel_depth[2] == 4) && (header->channel_depth[3] == 4)) *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4; + else if ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8) && (header->channel_depth[2] == 8) && (header->channel_depth[3] == 8)) *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; } else if (header->channels[3] == 0) { - if ((header->channel_depth[0] == 5) && (header->channel_depth[1] == 6) && (header->channel_depth[2] == 5)) *format = PIXELFORMAT_UNCOMPRESSED_R5G6B5; - else if ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8) && (header->channel_depth[2] == 8)) *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8; + if ((header->channel_depth[0] == 5) && (header->channel_depth[1] == 6) && (header->channel_depth[2] == 5)) *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G6B5; + else if ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8) && (header->channel_depth[2] == 8)) *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8; } } - else if (header->channels[0] == 2) *format = PIXELFORMAT_COMPRESSED_PVRT_RGB; - else if (header->channels[0] == 3) *format = PIXELFORMAT_COMPRESSED_PVRT_RGBA; + else if (header->channels[0] == 2) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_PVRT_RGB; + else if (header->channels[0] == 3) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_PVRT_RGBA; file_data_ptr += header->metadata_size; // Skip meta data header @@ -771,15 +802,15 @@ void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_ int bpp = 0; switch (*format) { - case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break; - case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: - case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: - case PIXELFORMAT_UNCOMPRESSED_R5G6B5: - case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break; - case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break; - case PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break; - case PIXELFORMAT_COMPRESSED_PVRT_RGB: - case PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G6B5: + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break; + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_PVRT_RGB: + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break; default: break; } @@ -789,7 +820,7 @@ void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_ RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); } } - else if (pvr_version == 52) RL_GPUTEX_WARN("PVRv2 format not supported, update your files to PVRv3"); + else if (pvr_version == 52) RL_GPUTEX_LOG("PVRv2 format not supported, update your files to PVRv3"); } return image_data; @@ -829,7 +860,7 @@ void *rl_load_astc_from_memory(const unsigned char *file_data, unsigned int file if ((header->id[3] != 0x5c) || (header->id[2] != 0xa1) || (header->id[1] != 0xab) || (header->id[0] != 0x13)) { - RL_GPUTEX_WARN("ASTC file data not valid"); + RL_GPUTEX_LOG("ASTC file data not valid"); } else { @@ -852,10 +883,10 @@ void *rl_load_astc_from_memory(const unsigned char *file_data, unsigned int file RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); - if (bpp == 8) *format = PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA; - else if (bpp == 2) *format = PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA; + if (bpp == 8) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA; + else if (bpp == 2) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA; } - else RL_GPUTEX_WARN("ASTC block size configuration not supported"); + else RL_GPUTEX_LOG("ASTC block size configuration not supported"); } } @@ -874,27 +905,27 @@ static int get_pixel_data_size(int width, int height, int format) switch (format) { - case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break; - case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: - case PIXELFORMAT_UNCOMPRESSED_R5G6B5: - case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: - case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break; - case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break; - case PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break; - case PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break; - case PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; break; - case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break; - case PIXELFORMAT_COMPRESSED_DXT1_RGB: - case PIXELFORMAT_COMPRESSED_DXT1_RGBA: - case PIXELFORMAT_COMPRESSED_ETC1_RGB: - case PIXELFORMAT_COMPRESSED_ETC2_RGB: - case PIXELFORMAT_COMPRESSED_PVRT_RGB: - case PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break; - case PIXELFORMAT_COMPRESSED_DXT3_RGBA: - case PIXELFORMAT_COMPRESSED_DXT5_RGBA: - case PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: - case PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: bpp = 8; break; - case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G6B5: + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break; + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT1_RGB: + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT1_RGBA: + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC1_RGB: + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC2_RGB: + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_PVRT_RGB: + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break; + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT3_RGBA: + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT5_RGBA: + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: bpp = 8; break; + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break; default: break; } @@ -904,8 +935,8 @@ static int get_pixel_data_size(int width, int height, int format) // if texture is smaller, minimum dataSize is 8 or 16 if ((width < 4) && (height < 4)) { - if ((format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < PIXELFORMAT_COMPRESSED_DXT3_RGBA)) data_size = 8; - else if ((format >= PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) data_size = 16; + if ((format >= RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT3_RGBA)) data_size = 8; + else if ((format >= RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < RL_GPUTEX_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) data_size = 16; } return data_size; diff --git a/src/rtextures.c b/src/rtextures.c index 7dfdc8b8d..022991531 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -169,22 +169,14 @@ #pragma GCC diagnostic ignored "-Wunused-function" #endif - - #define RL_GPUTEX_MALLOC RL_MALLOC - #define RL_GPUTEX_CALLOC RL_CALLOC - #define RL_GPUTEX_REALLOC RL_REALLOC - #define RL_GPUTEX_FREE RL_FREE - #define RL_GPUTEX_WARN(...) TRACELOG(LOG_WARNING, "IMAGE: " __VA_ARGS__) - #define RL_GPUTEX_SHOW_WARN_INFO - - // FIXME: probably, we should NOT export public functions from rl_gputex - // but this is how it always worked... so let's keep it this way - #define RLGPUTEXAPI RLAPI - + #define RL_GPUTEX_MALLOC RL_MALLOC + #define RL_GPUTEX_CALLOC RL_CALLOC + #define RL_GPUTEX_FREE RL_FREE + #define RL_GPUTEX_LOG(...) TRACELOG(LOG_WARNING, "IMAGE: " __VA_ARGS__) + #define RL_GPUTEX_SHOW_LOG_INFO #define RL_GPUTEX_IMPLEMENTATION #include "external/rl_gputex.h" // Required for: rl_load_xxx_from_memory() // NOTE: Used to read compressed textures data (multiple formats support) - #if defined(__GNUC__) // GCC and Clang #pragma GCC diagnostic pop #endif From 7262be85fdfa46cbf22899a776b40a7ccd8fad29 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 26 Jul 2025 12:06:24 +0200 Subject: [PATCH 110/242] Minor format tweaks --- src/platforms/rcore_desktop_sdl.c | 13 ++++++------- src/rcore.c | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 82337dd4e..98bbda76c 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -252,8 +252,7 @@ static const int CursorsLUT[] = { //SDL_SYSTEM_CURSOR_WAITARROW, // No equivalent implemented on MouseCursor enum on raylib.h }; - -// SDL3 Migration Layer made to avoid `ifdefs` inside functions when we can. +// SDL3 Migration Layer made to avoid 'ifdefs' inside functions when we can. #if defined(PLATFORM_DESKTOP_SDL3) // SDL3 Migration: @@ -304,13 +303,13 @@ int SDL_GetNumVideoDisplays(void) int monitorCount = 0; SDL_DisplayID *displays = SDL_GetDisplays(&monitorCount); - // Safe because If `mem` is NULL, SDL_free does nothing + // Safe because If 'mem' is NULL, SDL_free does nothing SDL_free(displays); return monitorCount; } -// SLD3 Migration: To emulate SDL2 this function should return `SDL_DISABLE` or `SDL_ENABLE` +// SLD3 Migration: To emulate SDL2 this function should return 'SDL_DISABLE' or 'SDL_ENABLE' // representing the *processing state* of the event before this function makes any changes to it Uint8 SDL_EventState(Uint32 type, int state) { @@ -581,7 +580,7 @@ void SetWindowState(unsigned int flags) if (flags & FLAG_WINDOW_UNFOCUSED) { // NOTE: To be able to implement this part it seems that we should - // do it ourselves, via `Windows.h`, `X11/Xlib.h` or even `Cocoa.h` + // do it ourselves, via 'windows.h', 'X11/Xlib.h' or even 'Cocoa.h' TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_SDL"); } if (flags & FLAG_WINDOW_TOPMOST) @@ -1169,13 +1168,13 @@ Image GetClipboardImage(void) image = LoadImageFromMemory(imageExtensions[i], fileData, dataSize); if (IsImageValid(image)) { - TRACELOG(LOG_INFO, "Clipboard image: Got image from clipboard as a `%s` successfully", imageExtensions[i]); + TRACELOG(LOG_INFO, "Clipboard: Got image from clipboard successfully: %s", imageExtensions[i]); return image; } } } - if (!IsImageValid(image)) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data. Error: %s", SDL_GetError()); + if (!IsImageValid(image)) TRACELOG(LOG_WARNING, "Clipboard: Couldn't get clipboard data. ERROR: %s", SDL_GetError()); #endif return image; diff --git a/src/rcore.c b/src/rcore.c index e97ef357c..c0af6654a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -539,7 +539,7 @@ const char *TextFormat(const char *text, ...); // Formatting of tex #pragma message ("WARNING: Getting image from the clipboard might not work without SUPPORT_FILEFORMAT_PNG or SUPPORT_FILEFORMAT_JPG") #endif - // Not needed because `rtexture.c` will automatically defined STBI_REQUIRED when any SUPPORT_FILEFORMAT_* is defined + // Not needed because 'rtexture.c' will automatically defined STBI_REQUIRED when any SUPPORT_FILEFORMAT_* is defined // #if !defined(STBI_REQUIRED) // #pragma message ("WARNING: "STBI_REQUIRED is not defined, that means we can't load images from clipbard" // #endif From 5c680739bd7077c318b316503004137b7edcc79d Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 26 Jul 2025 12:49:03 +0200 Subject: [PATCH 111/242] Removed platform-specific flag from config.h #4988 --- src/config.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/config.h b/src/config.h index 8b8e1074d..ef01013b3 100644 --- a/src/config.h +++ b/src/config.h @@ -299,9 +299,4 @@ //------------------------------------------------------------------------------------ #define MAX_TRACELOG_MSG_LENGTH 256 // Max length of one trace-log message -//DRM configuration -#if defined(PLATFORM_DRM) -//#define SUPPORT_DRM_CACHE 1 //enable triple buffered DRM caching -#endif //PLATFORM_DRM - #endif // CONFIG_H From c9f9219fa62d117eedb146d216f11f63aade30a7 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 26 Jul 2025 12:49:49 +0200 Subject: [PATCH 112/242] REVIEWED: DRM cache buffers support #4988 --- src/platforms/rcore_drm.c | 269 +++++++++++++++++++------------------- 1 file changed, 137 insertions(+), 132 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index a5c36977e..a3051f485 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -48,11 +48,11 @@ * **********************************************************************************************/ -#include // POSIX file control definitions - open(), creat(), fcntl() -#include // POSIX standard function definitions - read(), close(), STDIN_FILENO -#include // POSIX terminal control definitions - tcgetattr(), tcsetattr() -#include // POSIX threads management (inputs reading) -#include // POSIX directory browsing +#include // POSIX file control definitions - open(), creat(), fcntl() +#include // POSIX standard function definitions - read(), close(), STDIN_FILENO +#include // POSIX terminal control definitions - tcgetattr(), tcsetattr() +#include // POSIX threads management (inputs reading) +#include // POSIX directory browsing #include // Required for: ioctl() - UNIX System call for device-specific input/output operations #include // Linux: KDSKBMODE, K_MEDIUMRAM constants definition @@ -71,23 +71,13 @@ #include "EGL/egl.h" // Native platform windowing system interface #include "EGL/eglext.h" // EGL extensions +// NOTE: DRM cache enables triple buffered DRM caching #if defined(SUPPORT_DRM_CACHE) -#include // for drmHandleEvent poll -#include //for EBUSY, EAGAIN + #include // Required for: drmHandleEvent() poll + #include // Required for: EBUSY, EAGAIN -#define MAX_CACHED_BOS 3 - -typedef struct { - struct gbm_bo *bo; - uint32_t fbId; // DRM framebuffer ID -} FramebufferCache; - -static FramebufferCache fbCache[MAX_CACHED_BOS] = {0}; -static volatile int fbCacheCount = 0; -static volatile bool pendingFlip = false; -static bool crtcSet = false; - -#endif //SUPPORT_DRM_CACHE + #define MAX_DRM_CACHED_BUFFERS 3 +#endif // SUPPORT_DRM_CACHE #ifndef EGL_OPENGL_ES3_BIT #define EGL_OPENGL_ES3_BIT 0x40 @@ -96,18 +86,16 @@ static bool crtcSet = false; //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event number +#define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event number -#define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events +#define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events -// So actually the biggest key is KEY_CNT but we only really map the keys up to -// KEY_ALS_TOGGLE +// Actually biggest key is KEY_CNT but we only really map the keys up to KEY_ALS_TOGGLE #define KEYMAP_SIZE KEY_ALS_TOGGLE //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- - typedef struct { // Display data int fd; // File descriptor for /dev/dri/... @@ -147,6 +135,18 @@ typedef struct { int gamepadCount; // The number of gamepads registered } PlatformData; +#if defined(SUPPORT_DRM_CACHE) +typedef struct { + struct gbm_bo *bo; // Graphics buffer object + uint32_t fbId; // DRM framebuffer ID +} FramebufferCache; + +static FramebufferCache fbCache[MAX_DRM_CACHED_BUFFERS] = { 0 }; +static volatile int fbCacheCount = 0; +static volatile bool pendingFlip = false; +static bool crtcSet = false; +#endif // SUPPORT_DRM_CACHE + //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- @@ -570,18 +570,23 @@ void DisableCursor(void) } #if defined(SUPPORT_DRM_CACHE) -//callback to destroy cached framebuffer, set by gbm_bo_set_user_data() -static void DestroyFrameBufferCallback(struct gbm_bo *bo, void *data) { + +// Destroy cached framebuffer callback, set by gbm_bo_set_user_data() +static void DestroyFrameBufferCallback(struct gbm_bo *bo, void *data) +{ uint32_t fbId = (uintptr_t)data; + // Remove from cache - for (int i = 0; i < fbCacheCount; i++) { - if (fbCache[i].bo == bo) { - TRACELOG(LOG_INFO, "DRM: fb removed %u", (uintptr_t)fbId); + for (int i = 0; i < fbCacheCount; i++) + { + if (fbCache[i].bo == bo) + { + TRACELOG(LOG_INFO, "DISPLAY: DRM: Framebuffer removed [%u]", (uintptr_t)fbId); drmModeRmFB(platform.fd, fbCache[i].fbId); // Release DRM FB + // Shift remaining entries - for (int j = i; j < fbCacheCount - 1; j++) { - fbCache[j] = fbCache[j + 1]; - } + for (int j = i; j < fbCacheCount - 1; j++) fbCache[j] = fbCache[j + 1]; + fbCacheCount--; break; } @@ -589,56 +594,53 @@ static void DestroyFrameBufferCallback(struct gbm_bo *bo, void *data) { } // Create or retrieve cached DRM FB for BO -static uint32_t GetOrCreateFbForBo(struct gbm_bo *bo) { +static uint32_t GetOrCreateFbForBo(struct gbm_bo *bo) +{ // Try to find existing cache entry - for (int i = 0; i < fbCacheCount; i++) { - if (fbCache[i].bo == bo) { - return fbCache[i].fbId; - } + for (int i = 0; i < fbCacheCount; i++) + { + if (fbCache[i].bo == bo) return fbCache[i].fbId; } // Create new entry if cache not full - if (fbCacheCount >= MAX_CACHED_BOS) { - //FB cache full! - return 0; - } + if (fbCacheCount >= MAX_DRM_CACHED_BUFFERS) return 0; // FB cache full uint32_t handle = gbm_bo_get_handle(bo).u32; uint32_t stride = gbm_bo_get_stride(bo); uint32_t width = gbm_bo_get_width(bo); uint32_t height = gbm_bo_get_height(bo); - uint32_t fbId; - if (drmModeAddFB(platform.fd, width, height, 24, 32, stride, handle, &fbId)) { - //rmModeAddFB failed - return 0; - } + uint32_t fbId = 0; + if (drmModeAddFB(platform.fd, width, height, 24, 32, stride, handle, &fbId)) return 0; // Store in cache fbCache[fbCacheCount] = (FramebufferCache){ .bo = bo, .fbId = fbId }; fbCacheCount++; // Set destroy callback to auto-cleanup - gbm_bo_set_user_data(bo, (void*)(uintptr_t)fbId, DestroyFrameBufferCallback); + gbm_bo_set_user_data(bo, (void *)(uintptr_t)fbId, DestroyFrameBufferCallback); + + TRACELOG(LOG_INFO, "DISPLAY: DRM: Added new buffer object [%u]" , (uintptr_t)fbId); - TRACELOG(LOG_INFO, "DRM: added new bo %u" , (uintptr_t)fbId); return fbId; } // Renders a blank frame to allocate initial buffers -void RenderBlankFrame() { +// TODO: WARNING: Platform layers do not include OpenGL code! +void RenderBlankFrame() +{ glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(platform.device, platform.surface); - - // Ensure the buffer is processed - glFinish(); + glFinish(); // Ensure the buffer is processed } // Initialize with first buffer only -int InitSwapScreenBuffer() { - if (!platform.gbmSurface || platform.fd < 0) { - TRACELOG(LOG_ERROR, "DRM not initialized"); +int InitSwapScreenBuffer() +{ + if (!platform.gbmSurface || (platform.fd < 0)) + { + TRACELOG(LOG_ERROR, "DISPLAY: DRM: Swap buffers can not be initialized"); return -1; } @@ -647,23 +649,24 @@ int InitSwapScreenBuffer() { // Get first buffer struct gbm_bo *bo = gbm_surface_lock_front_buffer(platform.gbmSurface); - if (!bo) { - TRACELOG(LOG_ERROR, "Failed to lock initial buffer"); + if (!bo) + { + TRACELOG(LOG_ERROR, "DISPLAY: DRM: Failed to lock initial swap buffer"); return -1; } // Create FB for first buffer uint32_t fbId = GetOrCreateFbForBo(bo); - if (!fbId) { + if (!fbId) + { gbm_surface_release_buffer(platform.gbmSurface, bo); return -1; } // Initial CRTC setup - if (drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fbId, - 0, 0, &platform.connector->connector_id, 1, - &platform.connector->modes[platform.modeIndex])) { - TRACELOG(LOG_ERROR, "Initial CRTC setup failed: %s", strerror(errno)); + if (drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fbId, 0, 0, &platform.connector->connector_id, 1, &platform.connector->modes[platform.modeIndex])) + { + TRACELOG(LOG_ERROR, "DISPLAY: DRM: Failed to initialize CRTC setup. ERROR: %s", strerror(errno)); gbm_surface_release_buffer(platform.gbmSurface, bo); return -1; } @@ -671,32 +674,38 @@ int InitSwapScreenBuffer() { // Keep first buffer locked until flipped platform.prevBO = bo; crtcSet = true; + return 0; } -// Static page flip handler -// this will be called once the drmModePageFlip() finished from the drmHandleEvent(platform.fd, &evctx); context -static void PageFlipHandler(int fd, unsigned int frame, - unsigned int sec, unsigned int usec, - void *data) { - (void)fd; (void)frame; (void)sec; (void)usec; // Unused +// Static page flip handler +// NOTE: Called once the drmModePageFlip() finished from the drmHandleEvent() context +static void PageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data) +{ + // Unused inputs + (void)fd; + (void)frame; + (void)sec; + (void)usec; + pendingFlip = false; - struct gbm_bo *bo_to_release = (struct gbm_bo *)data; - //Buffers are released after the flip completes (via page_flip_handler), ensuring they're no longer in use. + struct gbm_bo *boToRelease = (struct gbm_bo *)data; + + // Buffers are released after the flip completes (via page_flip_handler), ensuring they're no longer in use // Prevents the GPU from writing to a buffer being scanned out - if (bo_to_release) { - gbm_surface_release_buffer(platform.gbmSurface, bo_to_release); - } + if (boToRelease) gbm_surface_release_buffer(platform.gbmSurface, boToRelease); } // Swap implementation with proper caching -void SwapScreenBuffer() { - static int loopCnt = 0; - loopCnt++; - static int errCnt[5] = {0}; +void SwapScreenBuffer() +{ if (!crtcSet || !platform.gbmSurface) return; - //call this only, if pendingFlip is not set + static int loopCnt = 0; + static int errCnt[5] = {0}; + loopCnt++; + + // Call this only, if pendingFlip is not set eglSwapBuffers(platform.device, platform.surface); // Process pending events non-blocking @@ -704,76 +713,68 @@ void SwapScreenBuffer() { .version = DRM_EVENT_CONTEXT_VERSION, .page_flip_handler = PageFlipHandler }; - + struct pollfd pfd = { .fd = platform.fd, .events = POLLIN }; - //polling for event for 0ms - while (poll(&pfd, 1, 0) > 0) { - drmHandleEvent(platform.fd, &evctx); - } + + // Polling for event for 0ms + while (poll(&pfd, 1, 0) > 0) drmHandleEvent(platform.fd, &evctx); // Skip if previous flip pending - if (pendingFlip) { - //Skip frame: flip pending - errCnt[0]++; + if (pendingFlip) + { + errCnt[0]++; // Skip frame: flip pending return; - } + } // Get new front buffer - struct gbm_bo *next_bo = gbm_surface_lock_front_buffer(platform.gbmSurface); - if (!next_bo) { - //Failed to lock front buffer + struct gbm_bo *nextBO = gbm_surface_lock_front_buffer(platform.gbmSurface); + if (!nextBO) // Failed to lock front buffer + { errCnt[1]++; return; } // Get FB ID (creates new one if needed) - uint32_t fbId = GetOrCreateFbForBo(next_bo); - if (!fbId) { - gbm_surface_release_buffer(platform.gbmSurface, next_bo); + uint32_t fbId = GetOrCreateFbForBo(nextBO); + if (!fbId) + { + gbm_surface_release_buffer(platform.gbmSurface, nextBO); errCnt[2]++; return; } // Attempt page flip - /* rmModePageFlip() schedules a buffer-flip for the next vblank and then - * notifies us about it. It takes a CRTC-id, fb-id and an arbitrary - * data-pointer and then schedules the page-flip. This is fully asynchronous and - * When the page-flip happens, the DRM-fd will become readable and we can call - * drmHandleEvent(). This will read all vblank/page-flip events and call our - * modeset_page_flip_event() callback with the data-pointer that we passed to - * drmModePageFlip(). We simply call modeset_draw_dev() then so the next frame - * is rendered.. - * returns immediately. - */ - if (drmModePageFlip(platform.fd, platform.crtc->crtc_id, fbId, - DRM_MODE_PAGE_FLIP_EVENT, platform.prevBO)) { - if (errno == EBUSY) { - //Display busy - skip flip - errCnt[3]++; - } else { - //Page flip failed - errCnt[4]++; - } - gbm_surface_release_buffer(platform.gbmSurface, next_bo); + // NOTE: rmModePageFlip() schedules a buffer-flip for the next vblank and then notifies us about it. + // It takes a CRTC-id, fb-id and an arbitrary data-pointer and then schedules the page-flip. + // This is fully asynchronous and when the page-flip happens, the DRM-fd will become readable and we can call drmHandleEvent(). + // This will read all vblank/page-flip events and call our modeset_page_flip_event() callback with the data-pointer that we passed to drmModePageFlip(). + // We simply call modeset_draw_dev() then so the next frame is rendered... returns immediately. + if (drmModePageFlip(platform.fd, platform.crtc->crtc_id, fbId, DRM_MODE_PAGE_FLIP_EVENT, platform.prevBO)) + { + if (errno == EBUSY) errCnt[3]++; // Display busy - skip flip + else errCnt[4]++; // Page flip failed + + gbm_surface_release_buffer(platform.gbmSurface, nextBO); return; } // Success: update state pendingFlip = true; - - platform.prevBO = next_bo; - //successful usage, do benchmarking - //in every 10 sec, at 60FPS 60*10 -> 600 - if(loopCnt >= 600) { - TRACELOG(LOG_INFO, "DRM err counters: %d, %d, %d, %d, %d, %d",errCnt[0],errCnt[1],errCnt[2],errCnt[3],errCnt[4], loopCnt); - //reinit the errors - for(int i=0;i<5;i++) { - errCnt[i] = 0; - } + platform.prevBO = nextBO; + +/* + // Some benchmarking code + if (loopCnt >= 600) + { + TRACELOG(LOG_INFO, "DRM: Error counters: %d, %d, %d, %d, %d, %d", errCnt[0], errCnt[1], errCnt[2], errCnt[3], errCnt[4], loopCnt); + for (int i = 0; i < 5; i++) errCnt[i] = 0; loopCnt = 0; } +*/ } -#else //SUPPORT_DRM_CACHE is not defined + +#else // !SUPPORT_DRM_CACHE + // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { @@ -803,7 +804,8 @@ void SwapScreenBuffer(void) platform.prevBO = bo; } -#endif //SUPPORT_DRM_CACHE +#endif // SUPPORT_DRM_CACHE + //---------------------------------------------------------------------------------- // Module Functions Definition: Misc //---------------------------------------------------------------------------------- @@ -1307,17 +1309,20 @@ int InitPlatform(void) //---------------------------------------------------------------------------- #if defined(SUPPORT_DRM_CACHE) - if(InitSwapScreenBuffer() == 0) { -#endif//SUPPORT_DRM_CACHE + if (InitSwapScreenBuffer() == 0) + { TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully"); return 0; -#if defined(SUPPORT_DRM_CACHE) - } else { + } + else + { TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized failed"); return -1; } -#endif //SUPPORT_DRM_CACHE - +#else // !SUPPORT_DRM_CACHE + TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully"); + return 0; +#endif } // Close platform From 8343aed4f6e7a52e2483b1103bf43287807b64b8 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 26 Jul 2025 12:50:12 +0200 Subject: [PATCH 113/242] Avoid fatal errors on OBJ model loading --- src/rmodels.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index ac6f4c594..0dc7572f1 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -4292,7 +4292,7 @@ static Model LoadOBJ(const char *fileName) if (fileText == NULL) { - TRACELOG(LOG_ERROR, "MODEL: [%s] Unable to read obj file", fileName); + TRACELOG(LOG_WARNING, "MODEL: [%s] Unable to read obj file", fileName); return model; } @@ -4308,7 +4308,7 @@ static Model LoadOBJ(const char *fileName) if (ret != TINYOBJ_SUCCESS) { - TRACELOG(LOG_ERROR, "MODEL: Unable to read obj data %s", fileName); + TRACELOG(LOG_WARNING, "MODEL: Unable to read obj data %s", fileName); return model; } From eb7f8912f8d735c9d078744839659b73598fdb24 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 26 Jul 2025 12:50:29 +0200 Subject: [PATCH 114/242] Minor format tweaks --- src/platforms/rcore_desktop_glfw.c | 4 ++-- src/platforms/rcore_desktop_sdl.c | 14 +++++++------- src/raudio.c | 3 ++- src/rtextures.c | 2 +- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 038e1c649..d701691a1 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1862,8 +1862,8 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i // WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1 // to work properly with our implementation (IsKeyDown/IsKeyUp checks) if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0; - else if(action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1; - else if(action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1; + else if (action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1; + else if (action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1; // WARNING: Check if CAPS/NUM key modifiers are enabled and force down state for those keys if (((key == KEY_CAPS_LOCK) && ((mods & GLFW_MOD_CAPS_LOCK) > 0)) || diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 98bbda76c..374acd72b 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -57,8 +57,8 @@ #include "SDL3/SDL.h" #elif USING_SDL2_PROJECT #include "SDL2/SDL.h" -#else - #include "SDL.h" +#else + #include "SDL.h" #endif #if defined(GRAPHICS_API_OPENGL_ES2) @@ -66,12 +66,12 @@ //#include "SDL_opengles2.h" #else // SDL OpenGL functionality (if required, instead of internal renderer) - #ifdef USING_SDL3_PROJECT - #include "SDL3/SDL_opengl.h" + #ifdef USING_SDL3_PROJECT + #include "SDL3/SDL_opengl.h" #elif USING_SDL2_PROJECT - #include "SDL2/SDL_opengl.h" - #else - #include "SDL_opengl.h" + #include "SDL2/SDL_opengl.h" + #else + #include "SDL_opengl.h" #endif #endif diff --git a/src/raudio.c b/src/raudio.c index ba1fba081..3301d7009 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1547,7 +1547,8 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, music.looping = true; // Looping enabled by default musicLoaded = true; } - else { + else + { drwav_uninit(ctxWav); RL_FREE(ctxWav); } diff --git a/src/rtextures.c b/src/rtextures.c index 022991531..bdfe83c71 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -168,7 +168,7 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" #endif - + #define RL_GPUTEX_MALLOC RL_MALLOC #define RL_GPUTEX_CALLOC RL_CALLOC #define RL_GPUTEX_FREE RL_FREE From 1fc0b4955fb9ad0defc4fb0473358f0b59afb9e9 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 26 Jul 2025 13:36:03 +0200 Subject: [PATCH 115/242] REVIEWED: `rl_gputex.h`, added some info for future improvements --- src/external/rl_gputex.h | 602 +++++++++++++++++++++++++++++++++++++-- src/rtextures.c | 1 - 2 files changed, 574 insertions(+), 29 deletions(-) diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index 276791430..07ff773a5 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -13,10 +13,6 @@ * FIXME: This library still depends on Raylib due to the following reasons: * - rl_save_ktx_to_memory() requires rlGetGlTextureFormats() from rlgl.h * though this is not a problem, if you don't need KTX support -* - all constants starting with PIXELFORMAT_* come from raylib.h -* this one is pretty hard to change, because it depends on exact ABI of 'enum PixelFormat' -* however, you can copy-paste said enum or include the header without Raylib sources -* - (probably) few minor issues that are hidden by Raylib inclusion and haven't been spotted yet * * TODO: * - Review rl_load_ktx_from_memory() to support KTX v2.2 specs @@ -38,8 +34,6 @@ * Make sure that this macro puts newline character '\n' at the end * * #define RL_GPUTEX_MALLOC -* #define RL_GPUTEX_CALLOC -* #define RL_GPUTEX_REALLOC * #define RL_GPUTEX_FREE * Define those macros in order to provide your own libc-compliant allocator; * not doing so will include @@ -157,37 +151,28 @@ RLGPUTEXAPI int rl_save_ktx_to_memory(const char *fileName, void *data, int widt #if defined(RL_GPUTEX_IMPLEMENTATION) #if defined(RL_GPUTEX_SHOW_LOG_INFO) && !defined(RL_GPUTEX_LOG) - #include // Required for: printf() + #include // Required for: printf() #endif - #if !defined(RL_GPUTEX_MALLOC) || !defined(RL_GPUTEX_NULL) - #include // Required for: NULL, malloc(), calloc(), free() + #include // Required for: NULL, malloc(), calloc(), free() #endif - #if !defined(RL_GPUTEX_MEMCPY) - #include // Required for: memcpy() + #include // Required for: memcpy() #endif -#if defined(RL_GPUTEX_SUPPORT_KTX) && !defined(RAYLIB_H) - #error "FIXME: RL_GPUTEX_SUPPORT_KTX require Raylib function: rlGetGlTextureFormats()" -#endif - -#if defined(RL_GPUTEX_MALLOC) || defined(RL_GPUTEX_CALLOC) || defined(RL_GPUTEX_REALLOC) || defined(RL_GPUTEX_FREE) - #if !defined(RL_GPUTEX_MALLOC) || !defined(RL_GPUTEX_CALLOC) || !defined(RL_GPUTEX_REALLOC) || !defined(RL_GPUTEX_FREE) - #error "You must provide all RL_GPUTEX_* allocating functions - not just few!" +#if defined(RL_GPUTEX_MALLOC) || defined(RL_GPUTEX_FREE) + #if !defined(RL_GPUTEX_MALLOC) || !defined(RL_GPUTEX_FREE) + #warning "RL_GPUTEX_MALLOC and RL_GPUTEX_FREE allocation functions are required to be provided" #endif #endif - #if !defined(RL_GPUTEX_MALLOC) #define RL_GPUTEX_MALLOC(sz) malloc(sz) - #define RL_GPUTEX_CALLOC(num, sz) calloc(num, sz) #define RL_GPUTEX_FREE(ptr) free(ptr) #endif #if !defined(RL_GPUTEX_NULL) #define RL_GPUTEX_NULL NULL #endif - #if !defined(RL_GPUTEX_MEMCPY) #define RL_GPUTEX_MEMCPY(dest, src, num) memcpy(dest, src, num) #endif @@ -214,6 +199,9 @@ RLGPUTEXAPI int rl_save_ktx_to_memory(const char *fileName, void *data, int widt // Get pixel data size in bytes for certain pixel format static int get_pixel_data_size(int width, int height, int format); +// Get OpenGL internal formats and data type from rlGpuTexPixelFormat +void get_gl_texture_formats(int format, unsigned int *gl_internal_format, unsigned int *gl_format, unsigned int *gl_type); + //---------------------------------------------------------------------------------- // Module Functions Definition //---------------------------------------------------------------------------------- @@ -579,9 +567,9 @@ int rl_save_ktx(const char *file_name, void *data, int width, int height, int fo { // KTX file Header (64 bytes) // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ - // v2.0 - http://github.khronos.org/KTX-Specification/ - Final specs by 2021-04-18 + // v2.0 - https://github.khronos.org/KTX-Specification/ktxspec.v2.html typedef struct { - char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" // KTX 2.0: "«KTX 22»\r\n\x1A\n" + char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" // KTX 2.0: "«KTX 20»\r\n\x1A\n" unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04 unsigned int gl_type; // For compressed textures, glType must equal 0 unsigned int gl_type_size; // For compressed texture data, usually 1 @@ -598,6 +586,17 @@ int rl_save_ktx(const char *file_name, void *data, int width, int height, int fo // KTX 2.0: UInt32 supercompressionScheme - 0 (None), 1 (Crunch CRN), 2 (Zlib DEFLATE)... // KTX 2.0 defines additional header elements... } ktx_header; + + Byte[12] identifier + UInt32 vkFormat + UInt32 typeSize + UInt32 pixelWidth + UInt32 pixelHeight + UInt32 pixelDepth + UInt32 layerCount + UInt32 faceCount + UInt32 levelCount + UInt32 supercompressionScheme // Calculate file data_size required int data_size = sizeof(ktx_header); @@ -608,7 +607,7 @@ int rl_save_ktx(const char *file_name, void *data, int width, int height, int fo w /= 2; h /= 2; } - unsigned char *file_data = RL_GPUTEX_CALLOC(data_size, 1); + unsigned char *file_data = RL_GPUTEX_MALLOC(data_size); unsigned char *file_data_ptr = file_data; ktx_header header = { 0 }; @@ -634,13 +633,15 @@ int rl_save_ktx(const char *file_name, void *data, int width, int height, int fo header.faces = 1; header.mipmap_levels = mipmaps; // If it was 0, it means mipmaps should be generated on loading (not for compressed formats) header.key_value_data_size = 0; // No extra data after the header - - rlGetGlTextureFormats(format, &header.gl_internal_format, &header.gl_format, &header.gl_type); // rlgl module function - header.gl_base_internal_format = header.gl_format; // KTX 1.1 only + + // TODO: WARNING: Function dependant on rlgl library! + rlGetGlTextureFormats(format, &header.gl_internal_format, &header.gl_format, &header.gl_type); // rlgl module function + + header.gl_base_internal_format = header.gl_format; // TODO: WARNING: KTX 1.1 only // NOTE: We can save into a .ktx all PixelFormats supported by raylib, including compressed formats like DXT, ETC or ASTC - if (header.gl_format == -1) RL_GPUTEX_LOG("GL format not supported for KTX export (%i)", header.gl_format); + if (header.gl_format == -1) RL_GPUTEX_LOG("RL_GPUTEX: GL format not supported for KTX export (%i)", header.gl_format); else { RL_GPUTEX_MEMCPY(file_data_ptr, &header, sizeof(ktx_header)); @@ -941,4 +942,549 @@ static int get_pixel_data_size(int width, int height, int format) return data_size; } + +// Get OpenGL internal formats and data type from rlGpuTexPixelFormat +void get_gl_texture_formats(int format, unsigned int *gl_internal_format, unsigned int *gl_format, unsigned int *gl_type) +{ + // KTX 1.1 uses OpenGL formats on header info but KTX 2.0 uses Vulkan texture formats, + // if this library is being improved to support KTX 2.0, it requires those formats to be provided -> View list at the end! + + /* + *glInternalFormat = 0; + *glFormat = 0; + *glType = 0; + + switch (format) + { + #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *gl_internal_format = GL_LUMINANCE; *gl_format = GL_LUMINANCE; *gl_type = GL_UNSIGNED_BYTE; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: *gl_internal_format = GL_LUMINANCE_ALPHA; *gl_format = GL_LUMINANCE_ALPHA; *gl_type = GL_UNSIGNED_BYTE; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G6B5: *gl_internal_format = GL_RGB; *gl_format = GL_RGB; *gl_type = GL_UNSIGNED_SHORT_5_6_5; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8: *gl_internal_format = GL_RGB; *gl_format = GL_RGB; *gl_type = GL_UNSIGNED_BYTE; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: *gl_internal_format = GL_RGBA; *gl_format = GL_RGBA; *gl_type = GL_UNSIGNED_SHORT_5_5_5_1; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: *gl_internal_format = GL_RGBA; *gl_format = GL_RGBA; *gl_type = GL_UNSIGNED_SHORT_4_4_4_4; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: *gl_internal_format = GL_RGBA; *gl_format = GL_RGBA; *gl_type = GL_UNSIGNED_BYTE; break; + #if !defined(GRAPHICS_API_OPENGL_11) + #if defined(GRAPHICS_API_OPENGL_ES3) + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32: *gl_internal_format = GL_R32F_EXT; *gl_format = GL_RED_EXT; *gl_type = GL_FLOAT; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32G32B32: *gl_internal_format = GL_RGB32F_EXT; *gl_format = GL_RGB; *gl_type = GL_FLOAT; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: *gl_internal_format = GL_RGBA32F_EXT; *gl_format = GL_RGBA; *gl_type = GL_FLOAT; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16: *gl_internal_format = GL_R16F_EXT; *gl_format = GL_RED_EXT; *gl_type = GL_HALF_FLOAT; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16G16B16: *gl_internal_format = GL_RGB16F_EXT; *gl_format = GL_RGB; *gl_type = GL_HALF_FLOAT; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: *gl_internal_format = GL_RGBA16F_EXT; *gl_format = GL_RGBA; *gl_type = GL_HALF_FLOAT; break; + #else + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32: *gl_internal_format = GL_LUMINANCE; *gl_format = GL_LUMINANCE; *gl_type = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32G32B32: *gl_internal_format = GL_RGB; *gl_format = GL_RGB; *gl_type = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: *gl_internal_format = GL_RGBA; *gl_format = GL_RGBA; *gl_type = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + #if defined(GRAPHICS_API_OPENGL_21) + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16: *gl_internal_format = GL_LUMINANCE; *gl_format = GL_LUMINANCE; *gl_type = GL_HALF_FLOAT_ARB; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16G16B16: *gl_internal_format = GL_RGB; *gl_format = GL_RGB; *gl_type = GL_HALF_FLOAT_ARB; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: *gl_internal_format = GL_RGBA; *gl_format = GL_RGBA; *gl_type = GL_HALF_FLOAT_ARB; break; + #else // defined(GRAPHICS_API_OPENGL_ES2) + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16: *gl_internal_format = GL_LUMINANCE; *gl_format = GL_LUMINANCE; *gl_type = GL_HALF_FLOAT_OES; break; // NOTE: Requires extension OES_texture_half_float + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16G16B16: *gl_internal_format = GL_RGB; *gl_format = GL_RGB; *gl_type = GL_HALF_FLOAT_OES; break; // NOTE: Requires extension OES_texture_half_float + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: *gl_internal_format = GL_RGBA; *gl_format = GL_RGBA; *gl_type = GL_HALF_FLOAT_OES; break; // NOTE: Requires extension OES_texture_half_float + #endif + #endif + #endif + #elif defined(GRAPHICS_API_OPENGL_33) + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *gl_internal_format = GL_R8; *gl_format = GL_RED; *gl_type = GL_UNSIGNED_BYTE; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: *gl_internal_format = GL_RG8; *gl_format = GL_RG; *gl_type = GL_UNSIGNED_BYTE; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G6B5: *gl_internal_format = GL_RGB565; *gl_format = GL_RGB; *gl_type = GL_UNSIGNED_SHORT_5_6_5; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8: *gl_internal_format = GL_RGB8; *gl_format = GL_RGB; *gl_type = GL_UNSIGNED_BYTE; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: *gl_internal_format = GL_RGB5_A1; *gl_format = GL_RGBA; *gl_type = GL_UNSIGNED_SHORT_5_5_5_1; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: *gl_internal_format = GL_RGBA4; *gl_format = GL_RGBA; *gl_type = GL_UNSIGNED_SHORT_4_4_4_4; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: *gl_internal_format = GL_RGBA8; *gl_format = GL_RGBA; *gl_type = GL_UNSIGNED_BYTE; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32: *gl_internal_format = GL_R32F; *gl_format = GL_RED; *gl_type = GL_FLOAT; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32G32B32: *gl_internal_format = GL_RGB32F; *gl_format = GL_RGB; *gl_type = GL_FLOAT; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: *gl_internal_format = GL_RGBA32F; *gl_format = GL_RGBA; *gl_type = GL_FLOAT; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16: *gl_internal_format = GL_R16F; *gl_format = GL_RED; *gl_type = GL_HALF_FLOAT; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16G16B16: *gl_internal_format = GL_RGB16F; *gl_format = GL_RGB; *gl_type = GL_HALF_FLOAT; break; + case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: *gl_internal_format = GL_RGBA16F; *gl_format = GL_RGBA; *gl_type = GL_HALF_FLOAT; break; + #endif + #if !defined(GRAPHICS_API_OPENGL_11) + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT1_RGB: *gl_internal_format = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT1_RGBA: *gl_internal_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT3_RGBA: *gl_internal_format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT5_RGBA: *gl_internal_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC1_RGB: *gl_internal_format = GL_ETC1_RGB8_OES; break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC2_RGB: *gl_internal_format = GL_COMPRESSED_RGB8_ETC2; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: *gl_internal_format = GL_COMPRESSED_RGBA8_ETC2_EAC; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_PVRT_RGB: *gl_internal_format = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_PVRT_RGBA: *gl_internal_format = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: *gl_internal_format = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + case RL_GPUTEX_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: *gl_internal_format = GL_COMPRESSED_RGBA_ASTC_8x8_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + #endif + default: RL_GPUTEX_LOG("GPUTEX: Current format not supported (%i)", format); break; + } + */ +} #endif // RL_GPUTEX_IMPLEMENTATION + +/* +// Vulkan texture formats +typedef enum VkFormat { + // Provided by VK_VERSION_1_0 + VK_FORMAT_UNDEFINED = 0, + VK_FORMAT_R4G4_UNORM_PACK8 = 1, + VK_FORMAT_R4G4B4A4_UNORM_PACK16 = 2, + VK_FORMAT_B4G4R4A4_UNORM_PACK16 = 3, + VK_FORMAT_R5G6B5_UNORM_PACK16 = 4, + VK_FORMAT_B5G6R5_UNORM_PACK16 = 5, + VK_FORMAT_R5G5B5A1_UNORM_PACK16 = 6, + VK_FORMAT_B5G5R5A1_UNORM_PACK16 = 7, + VK_FORMAT_A1R5G5B5_UNORM_PACK16 = 8, + VK_FORMAT_R8_UNORM = 9, + VK_FORMAT_R8_SNORM = 10, + VK_FORMAT_R8_USCALED = 11, + VK_FORMAT_R8_SSCALED = 12, + VK_FORMAT_R8_UINT = 13, + VK_FORMAT_R8_SINT = 14, + VK_FORMAT_R8_SRGB = 15, + VK_FORMAT_R8G8_UNORM = 16, + VK_FORMAT_R8G8_SNORM = 17, + VK_FORMAT_R8G8_USCALED = 18, + VK_FORMAT_R8G8_SSCALED = 19, + VK_FORMAT_R8G8_UINT = 20, + VK_FORMAT_R8G8_SINT = 21, + VK_FORMAT_R8G8_SRGB = 22, + VK_FORMAT_R8G8B8_UNORM = 23, + VK_FORMAT_R8G8B8_SNORM = 24, + VK_FORMAT_R8G8B8_USCALED = 25, + VK_FORMAT_R8G8B8_SSCALED = 26, + VK_FORMAT_R8G8B8_UINT = 27, + VK_FORMAT_R8G8B8_SINT = 28, + VK_FORMAT_R8G8B8_SRGB = 29, + VK_FORMAT_B8G8R8_UNORM = 30, + VK_FORMAT_B8G8R8_SNORM = 31, + VK_FORMAT_B8G8R8_USCALED = 32, + VK_FORMAT_B8G8R8_SSCALED = 33, + VK_FORMAT_B8G8R8_UINT = 34, + VK_FORMAT_B8G8R8_SINT = 35, + VK_FORMAT_B8G8R8_SRGB = 36, + VK_FORMAT_R8G8B8A8_UNORM = 37, + VK_FORMAT_R8G8B8A8_SNORM = 38, + VK_FORMAT_R8G8B8A8_USCALED = 39, + VK_FORMAT_R8G8B8A8_SSCALED = 40, + VK_FORMAT_R8G8B8A8_UINT = 41, + VK_FORMAT_R8G8B8A8_SINT = 42, + VK_FORMAT_R8G8B8A8_SRGB = 43, + VK_FORMAT_B8G8R8A8_UNORM = 44, + VK_FORMAT_B8G8R8A8_SNORM = 45, + VK_FORMAT_B8G8R8A8_USCALED = 46, + VK_FORMAT_B8G8R8A8_SSCALED = 47, + VK_FORMAT_B8G8R8A8_UINT = 48, + VK_FORMAT_B8G8R8A8_SINT = 49, + VK_FORMAT_B8G8R8A8_SRGB = 50, + VK_FORMAT_A8B8G8R8_UNORM_PACK32 = 51, + VK_FORMAT_A8B8G8R8_SNORM_PACK32 = 52, + VK_FORMAT_A8B8G8R8_USCALED_PACK32 = 53, + VK_FORMAT_A8B8G8R8_SSCALED_PACK32 = 54, + VK_FORMAT_A8B8G8R8_UINT_PACK32 = 55, + VK_FORMAT_A8B8G8R8_SINT_PACK32 = 56, + VK_FORMAT_A8B8G8R8_SRGB_PACK32 = 57, + VK_FORMAT_A2R10G10B10_UNORM_PACK32 = 58, + VK_FORMAT_A2R10G10B10_SNORM_PACK32 = 59, + VK_FORMAT_A2R10G10B10_USCALED_PACK32 = 60, + VK_FORMAT_A2R10G10B10_SSCALED_PACK32 = 61, + VK_FORMAT_A2R10G10B10_UINT_PACK32 = 62, + VK_FORMAT_A2R10G10B10_SINT_PACK32 = 63, + VK_FORMAT_A2B10G10R10_UNORM_PACK32 = 64, + VK_FORMAT_A2B10G10R10_SNORM_PACK32 = 65, + VK_FORMAT_A2B10G10R10_USCALED_PACK32 = 66, + VK_FORMAT_A2B10G10R10_SSCALED_PACK32 = 67, + VK_FORMAT_A2B10G10R10_UINT_PACK32 = 68, + VK_FORMAT_A2B10G10R10_SINT_PACK32 = 69, + VK_FORMAT_R16_UNORM = 70, + VK_FORMAT_R16_SNORM = 71, + VK_FORMAT_R16_USCALED = 72, + VK_FORMAT_R16_SSCALED = 73, + VK_FORMAT_R16_UINT = 74, + VK_FORMAT_R16_SINT = 75, + VK_FORMAT_R16_SFLOAT = 76, + VK_FORMAT_R16G16_UNORM = 77, + VK_FORMAT_R16G16_SNORM = 78, + VK_FORMAT_R16G16_USCALED = 79, + VK_FORMAT_R16G16_SSCALED = 80, + VK_FORMAT_R16G16_UINT = 81, + VK_FORMAT_R16G16_SINT = 82, + VK_FORMAT_R16G16_SFLOAT = 83, + VK_FORMAT_R16G16B16_UNORM = 84, + VK_FORMAT_R16G16B16_SNORM = 85, + VK_FORMAT_R16G16B16_USCALED = 86, + VK_FORMAT_R16G16B16_SSCALED = 87, + VK_FORMAT_R16G16B16_UINT = 88, + VK_FORMAT_R16G16B16_SINT = 89, + VK_FORMAT_R16G16B16_SFLOAT = 90, + VK_FORMAT_R16G16B16A16_UNORM = 91, + VK_FORMAT_R16G16B16A16_SNORM = 92, + VK_FORMAT_R16G16B16A16_USCALED = 93, + VK_FORMAT_R16G16B16A16_SSCALED = 94, + VK_FORMAT_R16G16B16A16_UINT = 95, + VK_FORMAT_R16G16B16A16_SINT = 96, + VK_FORMAT_R16G16B16A16_SFLOAT = 97, + VK_FORMAT_R32_UINT = 98, + VK_FORMAT_R32_SINT = 99, + VK_FORMAT_R32_SFLOAT = 100, + VK_FORMAT_R32G32_UINT = 101, + VK_FORMAT_R32G32_SINT = 102, + VK_FORMAT_R32G32_SFLOAT = 103, + VK_FORMAT_R32G32B32_UINT = 104, + VK_FORMAT_R32G32B32_SINT = 105, + VK_FORMAT_R32G32B32_SFLOAT = 106, + VK_FORMAT_R32G32B32A32_UINT = 107, + VK_FORMAT_R32G32B32A32_SINT = 108, + VK_FORMAT_R32G32B32A32_SFLOAT = 109, + VK_FORMAT_R64_UINT = 110, + VK_FORMAT_R64_SINT = 111, + VK_FORMAT_R64_SFLOAT = 112, + VK_FORMAT_R64G64_UINT = 113, + VK_FORMAT_R64G64_SINT = 114, + VK_FORMAT_R64G64_SFLOAT = 115, + VK_FORMAT_R64G64B64_UINT = 116, + VK_FORMAT_R64G64B64_SINT = 117, + VK_FORMAT_R64G64B64_SFLOAT = 118, + VK_FORMAT_R64G64B64A64_UINT = 119, + VK_FORMAT_R64G64B64A64_SINT = 120, + VK_FORMAT_R64G64B64A64_SFLOAT = 121, + VK_FORMAT_B10G11R11_UFLOAT_PACK32 = 122, + VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 = 123, + VK_FORMAT_D16_UNORM = 124, + VK_FORMAT_X8_D24_UNORM_PACK32 = 125, + VK_FORMAT_D32_SFLOAT = 126, + VK_FORMAT_S8_UINT = 127, + VK_FORMAT_D16_UNORM_S8_UINT = 128, + VK_FORMAT_D24_UNORM_S8_UINT = 129, + VK_FORMAT_D32_SFLOAT_S8_UINT = 130, + VK_FORMAT_BC1_RGB_UNORM_BLOCK = 131, + VK_FORMAT_BC1_RGB_SRGB_BLOCK = 132, + VK_FORMAT_BC1_RGBA_UNORM_BLOCK = 133, + VK_FORMAT_BC1_RGBA_SRGB_BLOCK = 134, + VK_FORMAT_BC2_UNORM_BLOCK = 135, + VK_FORMAT_BC2_SRGB_BLOCK = 136, + VK_FORMAT_BC3_UNORM_BLOCK = 137, + VK_FORMAT_BC3_SRGB_BLOCK = 138, + VK_FORMAT_BC4_UNORM_BLOCK = 139, + VK_FORMAT_BC4_SNORM_BLOCK = 140, + VK_FORMAT_BC5_UNORM_BLOCK = 141, + VK_FORMAT_BC5_SNORM_BLOCK = 142, + VK_FORMAT_BC6H_UFLOAT_BLOCK = 143, + VK_FORMAT_BC6H_SFLOAT_BLOCK = 144, + VK_FORMAT_BC7_UNORM_BLOCK = 145, + VK_FORMAT_BC7_SRGB_BLOCK = 146, + VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK = 147, + VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK = 148, + VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK = 149, + VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK = 150, + VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK = 151, + VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK = 152, + VK_FORMAT_EAC_R11_UNORM_BLOCK = 153, + VK_FORMAT_EAC_R11_SNORM_BLOCK = 154, + VK_FORMAT_EAC_R11G11_UNORM_BLOCK = 155, + VK_FORMAT_EAC_R11G11_SNORM_BLOCK = 156, + VK_FORMAT_ASTC_4x4_UNORM_BLOCK = 157, + VK_FORMAT_ASTC_4x4_SRGB_BLOCK = 158, + VK_FORMAT_ASTC_5x4_UNORM_BLOCK = 159, + VK_FORMAT_ASTC_5x4_SRGB_BLOCK = 160, + VK_FORMAT_ASTC_5x5_UNORM_BLOCK = 161, + VK_FORMAT_ASTC_5x5_SRGB_BLOCK = 162, + VK_FORMAT_ASTC_6x5_UNORM_BLOCK = 163, + VK_FORMAT_ASTC_6x5_SRGB_BLOCK = 164, + VK_FORMAT_ASTC_6x6_UNORM_BLOCK = 165, + VK_FORMAT_ASTC_6x6_SRGB_BLOCK = 166, + VK_FORMAT_ASTC_8x5_UNORM_BLOCK = 167, + VK_FORMAT_ASTC_8x5_SRGB_BLOCK = 168, + VK_FORMAT_ASTC_8x6_UNORM_BLOCK = 169, + VK_FORMAT_ASTC_8x6_SRGB_BLOCK = 170, + VK_FORMAT_ASTC_8x8_UNORM_BLOCK = 171, + VK_FORMAT_ASTC_8x8_SRGB_BLOCK = 172, + VK_FORMAT_ASTC_10x5_UNORM_BLOCK = 173, + VK_FORMAT_ASTC_10x5_SRGB_BLOCK = 174, + VK_FORMAT_ASTC_10x6_UNORM_BLOCK = 175, + VK_FORMAT_ASTC_10x6_SRGB_BLOCK = 176, + VK_FORMAT_ASTC_10x8_UNORM_BLOCK = 177, + VK_FORMAT_ASTC_10x8_SRGB_BLOCK = 178, + VK_FORMAT_ASTC_10x10_UNORM_BLOCK = 179, + VK_FORMAT_ASTC_10x10_SRGB_BLOCK = 180, + VK_FORMAT_ASTC_12x10_UNORM_BLOCK = 181, + VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182, + VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183, + VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G8B8G8R8_422_UNORM = 1000156000, + // Provided by VK_VERSION_1_1 + VK_FORMAT_B8G8R8G8_422_UNORM = 1000156001, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM = 1000156002, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G8_B8R8_2PLANE_420_UNORM = 1000156003, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM = 1000156004, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G8_B8R8_2PLANE_422_UNORM = 1000156005, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM = 1000156006, + // Provided by VK_VERSION_1_1 + VK_FORMAT_R10X6_UNORM_PACK16 = 1000156007, + // Provided by VK_VERSION_1_1 + VK_FORMAT_R10X6G10X6_UNORM_2PACK16 = 1000156008, + // Provided by VK_VERSION_1_1 + VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16 = 1000156009, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16 = 1000156010, + // Provided by VK_VERSION_1_1 + VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16 = 1000156011, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16 = 1000156012, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16 = 1000156013, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16 = 1000156014, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16 = 1000156015, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16 = 1000156016, + // Provided by VK_VERSION_1_1 + VK_FORMAT_R12X4_UNORM_PACK16 = 1000156017, + // Provided by VK_VERSION_1_1 + VK_FORMAT_R12X4G12X4_UNORM_2PACK16 = 1000156018, + // Provided by VK_VERSION_1_1 + VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16 = 1000156019, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16 = 1000156020, + // Provided by VK_VERSION_1_1 + VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16 = 1000156021, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16 = 1000156022, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16 = 1000156023, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16 = 1000156024, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16 = 1000156025, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16 = 1000156026, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G16B16G16R16_422_UNORM = 1000156027, + // Provided by VK_VERSION_1_1 + VK_FORMAT_B16G16R16G16_422_UNORM = 1000156028, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM = 1000156029, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G16_B16R16_2PLANE_420_UNORM = 1000156030, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM = 1000156031, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G16_B16R16_2PLANE_422_UNORM = 1000156032, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM = 1000156033, + // Provided by VK_VERSION_1_3 + VK_FORMAT_G8_B8R8_2PLANE_444_UNORM = 1000330000, + // Provided by VK_VERSION_1_3 + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16 = 1000330001, + // Provided by VK_VERSION_1_3 + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16 = 1000330002, + // Provided by VK_VERSION_1_3 + VK_FORMAT_G16_B16R16_2PLANE_444_UNORM = 1000330003, + // Provided by VK_VERSION_1_3 + VK_FORMAT_A4R4G4B4_UNORM_PACK16 = 1000340000, + // Provided by VK_VERSION_1_3 + VK_FORMAT_A4B4G4R4_UNORM_PACK16 = 1000340001, + // Provided by VK_VERSION_1_3 + VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK = 1000066000, + // Provided by VK_VERSION_1_3 + VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK = 1000066001, + // Provided by VK_VERSION_1_3 + VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK = 1000066002, + // Provided by VK_VERSION_1_3 + VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK = 1000066003, + // Provided by VK_VERSION_1_3 + VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK = 1000066004, + // Provided by VK_VERSION_1_3 + VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK = 1000066005, + // Provided by VK_VERSION_1_3 + VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK = 1000066006, + // Provided by VK_VERSION_1_3 + VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK = 1000066007, + // Provided by VK_VERSION_1_3 + VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK = 1000066008, + // Provided by VK_VERSION_1_3 + VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK = 1000066009, + // Provided by VK_VERSION_1_3 + VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK = 1000066010, + // Provided by VK_VERSION_1_3 + VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK = 1000066011, + // Provided by VK_VERSION_1_3 + VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK = 1000066012, + // Provided by VK_VERSION_1_3 + VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK = 1000066013, + // Provided by VK_VERSION_1_4 + VK_FORMAT_A1B5G5R5_UNORM_PACK16 = 1000470000, + // Provided by VK_VERSION_1_4 + VK_FORMAT_A8_UNORM = 1000470001, + // Provided by VK_IMG_format_pvrtc + VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000, + // Provided by VK_IMG_format_pvrtc + VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001, + // Provided by VK_IMG_format_pvrtc + VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002, + // Provided by VK_IMG_format_pvrtc + VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG = 1000054003, + // Provided by VK_IMG_format_pvrtc + VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG = 1000054004, + // Provided by VK_IMG_format_pvrtc + VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005, + // Provided by VK_IMG_format_pvrtc + VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006, + // Provided by VK_IMG_format_pvrtc + VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007, + // Provided by VK_ARM_tensors + VK_FORMAT_R8_BOOL_ARM = 1000460000, + // Provided by VK_NV_optical_flow + VK_FORMAT_R16G16_SFIXED5_NV = 1000464000, + // Provided by VK_ARM_format_pack + VK_FORMAT_R10X6_UINT_PACK16_ARM = 1000609000, + // Provided by VK_ARM_format_pack + VK_FORMAT_R10X6G10X6_UINT_2PACK16_ARM = 1000609001, + // Provided by VK_ARM_format_pack + VK_FORMAT_R10X6G10X6B10X6A10X6_UINT_4PACK16_ARM = 1000609002, + // Provided by VK_ARM_format_pack + VK_FORMAT_R12X4_UINT_PACK16_ARM = 1000609003, + // Provided by VK_ARM_format_pack + VK_FORMAT_R12X4G12X4_UINT_2PACK16_ARM = 1000609004, + // Provided by VK_ARM_format_pack + VK_FORMAT_R12X4G12X4B12X4A12X4_UINT_4PACK16_ARM = 1000609005, + // Provided by VK_ARM_format_pack + VK_FORMAT_R14X2_UINT_PACK16_ARM = 1000609006, + // Provided by VK_ARM_format_pack + VK_FORMAT_R14X2G14X2_UINT_2PACK16_ARM = 1000609007, + // Provided by VK_ARM_format_pack + VK_FORMAT_R14X2G14X2B14X2A14X2_UINT_4PACK16_ARM = 1000609008, + // Provided by VK_ARM_format_pack + VK_FORMAT_R14X2_UNORM_PACK16_ARM = 1000609009, + // Provided by VK_ARM_format_pack + VK_FORMAT_R14X2G14X2_UNORM_2PACK16_ARM = 1000609010, + // Provided by VK_ARM_format_pack + VK_FORMAT_R14X2G14X2B14X2A14X2_UNORM_4PACK16_ARM = 1000609011, + // Provided by VK_ARM_format_pack + VK_FORMAT_G14X2_B14X2R14X2_2PLANE_420_UNORM_3PACK16_ARM = 1000609012, + // Provided by VK_ARM_format_pack + VK_FORMAT_G14X2_B14X2R14X2_2PLANE_422_UNORM_3PACK16_ARM = 1000609013, + // Provided by VK_EXT_texture_compression_astc_hdr + VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK, + // Provided by VK_EXT_texture_compression_astc_hdr + VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK, + // Provided by VK_EXT_texture_compression_astc_hdr + VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK, + // Provided by VK_EXT_texture_compression_astc_hdr + VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK, + // Provided by VK_EXT_texture_compression_astc_hdr + VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK, + // Provided by VK_EXT_texture_compression_astc_hdr + VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK, + // Provided by VK_EXT_texture_compression_astc_hdr + VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK, + // Provided by VK_EXT_texture_compression_astc_hdr + VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK, + // Provided by VK_EXT_texture_compression_astc_hdr + VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK, + // Provided by VK_EXT_texture_compression_astc_hdr + VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK, + // Provided by VK_EXT_texture_compression_astc_hdr + VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK, + // Provided by VK_EXT_texture_compression_astc_hdr + VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK, + // Provided by VK_EXT_texture_compression_astc_hdr + VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK, + // Provided by VK_EXT_texture_compression_astc_hdr + VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G8B8G8R8_422_UNORM_KHR = VK_FORMAT_G8B8G8R8_422_UNORM, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_B8G8R8G8_422_UNORM_KHR = VK_FORMAT_B8G8R8G8_422_UNORM, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM_KHR = VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G8_B8R8_2PLANE_420_UNORM_KHR = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM_KHR = VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G8_B8R8_2PLANE_422_UNORM_KHR = VK_FORMAT_G8_B8R8_2PLANE_422_UNORM, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM_KHR = VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_R10X6_UNORM_PACK16_KHR = VK_FORMAT_R10X6_UNORM_PACK16, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_R10X6G10X6_UNORM_2PACK16_KHR = VK_FORMAT_R10X6G10X6_UNORM_2PACK16, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16_KHR = VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16_KHR = VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16_KHR = VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16_KHR = VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16_KHR = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16_KHR = VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16_KHR = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16_KHR = VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_R12X4_UNORM_PACK16_KHR = VK_FORMAT_R12X4_UNORM_PACK16, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_R12X4G12X4_UNORM_2PACK16_KHR = VK_FORMAT_R12X4G12X4_UNORM_2PACK16, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16_KHR = VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16_KHR = VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16_KHR = VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16_KHR = VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16_KHR = VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16_KHR = VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16_KHR = VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16_KHR = VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G16B16G16R16_422_UNORM_KHR = VK_FORMAT_G16B16G16R16_422_UNORM, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_B16G16R16G16_422_UNORM_KHR = VK_FORMAT_B16G16R16G16_422_UNORM, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM_KHR = VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G16_B16R16_2PLANE_420_UNORM_KHR = VK_FORMAT_G16_B16R16_2PLANE_420_UNORM, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM_KHR = VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G16_B16R16_2PLANE_422_UNORM_KHR = VK_FORMAT_G16_B16R16_2PLANE_422_UNORM, + // Provided by VK_KHR_sampler_ycbcr_conversion + VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM_KHR = VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, + // Provided by VK_EXT_ycbcr_2plane_444_formats + VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT = VK_FORMAT_G8_B8R8_2PLANE_444_UNORM, + // Provided by VK_EXT_ycbcr_2plane_444_formats + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16_EXT = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16, + // Provided by VK_EXT_ycbcr_2plane_444_formats + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16_EXT = VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16, + // Provided by VK_EXT_ycbcr_2plane_444_formats + VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT = VK_FORMAT_G16_B16R16_2PLANE_444_UNORM, + // Provided by VK_EXT_4444_formats + VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT = VK_FORMAT_A4R4G4B4_UNORM_PACK16, + // Provided by VK_EXT_4444_formats + VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT = VK_FORMAT_A4B4G4R4_UNORM_PACK16, + // Provided by VK_NV_optical_flow + // VK_FORMAT_R16G16_S10_5_NV is a deprecated alias + VK_FORMAT_R16G16_S10_5_NV = VK_FORMAT_R16G16_SFIXED5_NV, + // Provided by VK_KHR_maintenance5 + VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR = VK_FORMAT_A1B5G5R5_UNORM_PACK16, + // Provided by VK_KHR_maintenance5 + VK_FORMAT_A8_UNORM_KHR = VK_FORMAT_A8_UNORM, +} VkFormat; +*/ \ No newline at end of file diff --git a/src/rtextures.c b/src/rtextures.c index bdfe83c71..b798caed6 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -170,7 +170,6 @@ #endif #define RL_GPUTEX_MALLOC RL_MALLOC - #define RL_GPUTEX_CALLOC RL_CALLOC #define RL_GPUTEX_FREE RL_FREE #define RL_GPUTEX_LOG(...) TRACELOG(LOG_WARNING, "IMAGE: " __VA_ARGS__) #define RL_GPUTEX_SHOW_LOG_INFO From 1da6b281de21fc41a33daae41f53841a2a8b002b Mon Sep 17 00:00:00 2001 From: lepasona <223069396+lepasona@users.noreply.github.com> Date: Sun, 27 Jul 2025 07:37:07 +0000 Subject: [PATCH 116/242] Update glfw `mappings.h` --- src/external/glfw/src/mappings.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/external/glfw/src/mappings.h b/src/external/glfw/src/mappings.h index ccba5924a..5d8b9e1e1 100644 --- a/src/external/glfw/src/mappings.h +++ b/src/external/glfw/src/mappings.h @@ -321,6 +321,8 @@ const char* _glfwDefaultMappings[] = "030000008f0e00000610000000000000,GreenAsia,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a5,righty:a2,start:b11,x:b3,y:b0,platform:Windows,", "03000000ac0500006b05000000000000,GT2a,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", "03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000008a2e0000dd10000000000000,Hand Held Legend GC Ultimate,a:b0,b:b2,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,misc2:b24,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b1,y:b3,platform:Windows,", +"030000008a2e0000df10000000000000,Hand Held Legend ProGCC,a:b1,b:b0,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b3,y:b2,platform:Windows,", "030000000d0f00004900000000000000,Hatsune Miku Sho PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000001008000001e1000000000000,Havit HV G60,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows,", "030000000d0f00000c00000000000000,HEXT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", @@ -1196,6 +1198,7 @@ const char* _glfwDefaultMappings[] = "030000005e040000e302000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "030000005e040000ea02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "030000005e040000fd02000003090000,Xbox One Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000005e040000220b000013050000,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,", "03000000c62400003a54000000000000,Xbox One PowerA Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "030000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", "030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", @@ -1396,6 +1399,8 @@ const char* _glfwDefaultMappings[] = "030000008f0e00001200000010010000,GreenAsia Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", "0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", "03000000f0250000c383000010010000,GT VX2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", +"030000008a2e0000dd10000011010000,Hand Held Legend GC Ultimate,a:b0,b:b2,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,misc2:b24,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b1,y:b3,platform:Linux,", +"030000008a2e0000df10000011010000,Hand Held Legend ProGCC,a:b1,b:b0,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b3,y:b2,platform:Linux,", "06000000adde0000efbe000002010000,Hidromancer Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000d81400000862000011010000,HitBox PS3 PC Analog Mode,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux,", "03000000c9110000f055000011010000,HJC Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", @@ -1860,6 +1865,7 @@ const char* _glfwDefaultMappings[] = "050000005e040000050b000002090000,Xbox One Elite Series 2,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "030000005e040000ea02000011050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000005e040000ea02000015050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000ea02000017050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "060000005e040000ea0200000b050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "060000005e040000ea0200000d050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "060000005e040000ea02000016050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", @@ -1885,9 +1891,11 @@ const char* _glfwDefaultMappings[] = "060000005e040000120b00000b050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "060000005e040000120b00000d050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "060000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"060000005e040000120b000011050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "050000005e040000130b000022050000,Xbox Series X Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "050000005e040000200b000013050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "050000005e040000200b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"050000005e040000200b000023050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "050000005e040000220b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "03000000450c00002043000010010000,XEOX SL6556 BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", "05000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,", From b23072084248c578a6930d757b9bcc1d9a20a3b6 Mon Sep 17 00:00:00 2001 From: lepasona <223069396+lepasona@users.noreply.github.com> Date: Sun, 27 Jul 2025 14:35:38 +0000 Subject: [PATCH 117/242] example core_input_gamepad.c: fix hardcoded gamepad 0 --- examples/core/core_input_gamepad.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/core/core_input_gamepad.c b/examples/core/core_input_gamepad.c index 6df29f8c8..1ab6b55ae 100644 --- a/examples/core/core_input_gamepad.c +++ b/examples/core/core_input_gamepad.c @@ -256,11 +256,11 @@ int main(void) } - DrawText(TextFormat("DETECTED AXIS [%i]:", GetGamepadAxisCount(0)), 10, 50, 10, MAROON); + DrawText(TextFormat("DETECTED AXIS [%i]:", GetGamepadAxisCount(gamepad)), 10, 50, 10, MAROON); - for (int i = 0; i < GetGamepadAxisCount(0); i++) + for (int i = 0; i < GetGamepadAxisCount(gamepad); i++) { - DrawText(TextFormat("AXIS %i: %.02f", i, GetGamepadAxisMovement(0, i)), 20, 70 + 20*i, 10, DARKGRAY); + DrawText(TextFormat("AXIS %i: %.02f", i, GetGamepadAxisMovement(gamepad, i)), 20, 70 + 20*i, 10, DARKGRAY); } if (GetGamepadButtonPressed() != GAMEPAD_BUTTON_UNKNOWN) DrawText(TextFormat("DETECTED BUTTON: %i", GetGamepadButtonPressed()), 10, 430, 10, RED); From 5ca6bff8ca4cad096413703365cc84aba4fe2a80 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 28 Jul 2025 23:37:48 +0200 Subject: [PATCH 118/242] Update rl_gputex.h --- src/external/rl_gputex.h | 65 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index 07ff773a5..c8d1c6aba 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -950,9 +950,9 @@ void get_gl_texture_formats(int format, unsigned int *gl_internal_format, unsign // if this library is being improved to support KTX 2.0, it requires those formats to be provided -> View list at the end! /* - *glInternalFormat = 0; - *glFormat = 0; - *glType = 0; + *gl_internal_format = 0; + *gl_format = 0; + *gl_type = 0; switch (format) { @@ -1022,6 +1022,65 @@ void get_gl_texture_formats(int format, unsigned int *gl_internal_format, unsign } #endif // RL_GPUTEX_IMPLEMENTATION +/* +// OpenGL texture data formats +// NOTE: Those values can be useful for KTX 1.1 saving, +// probably only the latest OpenGL ones, not the extensions ones +// So, there is no need to include full OpenGL headers +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_FLOAT 0x1406 +#define GL_HALF_FLOAT 0x140b +#define GL_HALF_FLOAT_ARB 0x140b +#define GL_HALF_FLOAT_OES 0x8d61 + +#define GL_RED_EXT 0x1903 +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 +#define GL_LUMINANCE 0x1909 +#define GL_R8 0x8229 +#define GL_RG8 0x822b +#define GL_RGB565 0x8d62 +#define GL_RGB8 0x8051 +#define GL_RGB5_A1 0x8057 +#define GL_RGBA4 0x8056 +#define GL_RGBA8 0x8058 +#define GL_R16F 0x822d +#define GL_RGB16F 0x881b +#define GL_RGBA16F 0x881a +#define GL_R32F 0x822e +#define GL_RGB32F 0x8815 +#define GL_RGBA32F 0x8814 + +#define GL_R32F_EXT 0x822e +#define GL_RGB32F_EXT 0x8815 +#define GL_RGBA32F_EXT 0x8814 +#define GL_R16F_EXT 0x822d +#define GL_RGB16F_EXT 0x881b +#define GL_RGBA16F_EXT 0x881a + +// S3TC (DXT) compression +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83f0 +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83f1 +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83f2 +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83f3 + +// ETC formats +#define GL_ETC1_RGB8_OES 0x8d64 +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 + +// PVRTC +#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8c00 +#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8c02 + +// ASTC +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93b0 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93b7 +*/ + /* // Vulkan texture formats typedef enum VkFormat { From 37104195b4991e9286c82c7ba541358465860bff Mon Sep 17 00:00:00 2001 From: RANDRIA Luca Date: Wed, 30 Jul 2025 16:54:06 +0300 Subject: [PATCH 119/242] add screenshot core_basic_window_web There are no screenshot for this and the alt is seen --- examples/core/core_basic_window_web.png | Bin 0 -> 4402 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/core/core_basic_window_web.png diff --git a/examples/core/core_basic_window_web.png b/examples/core/core_basic_window_web.png new file mode 100644 index 0000000000000000000000000000000000000000..c01b78748e9480c2d20f18c94601e04b0b8356b5 GIT binary patch literal 4402 zcmZWsd0bOh7Je#I7_EY}?h7f*XsZ>Y6j_Hz1zCfWSalhOJ;CL>(7wtvFVS zKv-N5$wEMks1Q;C#Wi$xv|O|!tTDYs2N!&fq=n(oFQMhV|<@ixK#9WdFKj{m(d(akS$S8P~f zGCe*N3y+D74qcrP5+52Glkkt&#DQrNXZ+A=bZBFIC_OPEHiqOK5gm&8hVCO-lSzM1 ziXyEeud^YMzjLr5JJ_rzZTBR(xp?1ypMhU(_R|(u7w;5Kf177)fFkL$=OW=Z+|7Ua zDeJPU+Oqtm+VXnT#kuP29Zv@ryeymYY{#K%|2!xiGko*%WgZ*Ca=T# zlN&Z2`Z9X?mcKpnK|c$(n&VGWmmPGN7H}q48~8;^BaJaI{EQ*g zOvNyhlmT}NSJWH7`oNpp#v#H#Vc5Ajsj7=J>k2LKZCAcWvB)8{ba}`cBYelag?c)h zgs)$+al!w-r>2U?!*~b>N8Wa1uF|lA(zd#x^@TCYQtlV7_=Xdli#%+CVW;JPnvG$5 zjnKQ<3Y6A3c(u;e(4204jTY<+TE)90Ng6J?qbn*VM$X>D7Cs|?p8DzzzCB0}br8sU zum1Sa8cCJr_$xYmgm5T=DieiG!LSfBbh#b6JU#NS$h0M_TnU6LFWbz(umy(_;E)P! zyNIu$-_cuE@MhovZP>27IUPOE(UU&<9H#gd!$^-x{qc407Gj@+F0X%DGat8ibRixe zNqS$6@1I4#BS}xi29H{134}Kr#X=S!-#BvQrx&V3`mWjd9h*q#$POvI z7ZS0fTaQFM#{VaF>0!vgY!eG)0ByQzpn#KBj@Tn7O&CQe5Wr(i2FUp zVdXInpGQBq)dQY>$6xI63w|@1fJX2m$PSSy%ZW^dY(kYyIEDLerbsTGKy*65!wAZh z-sfl_5Dci~XMy8Y1WqBWS|S!2ss<5@w*n!s3(V94GEYMqF4@%K zupjrHn+&<~V*n7Yl}d3kRm&PY)(H5hsN3o^wsk@PX5Y~7D>ta&o`R0*5bAg zr1aj&Qn&VsXbV_4=6?Hdqx|ilTJpggnk7N9bn8SVxQ@S@Vc6zzsQy2ap>;Y4RDBAJ zv%esi+O0rPj`M<6vkIu$V|eqrN_LF?GGq-d6z5FC8#yI2R0=BU8ctR6fc)p5XQCFp z=g)p|baXh`KqK6G{pt^<{*gaGStZSL)Vy#vhq}oBebf?_?sP)$yKbgxH=B;ide1-K ztFJ53A80fZ=zzq}f@T#FEGolUVMDv7PtiB3=?hsiZEvD}*}6tR4VTJ0T0VbD$kYC| zV&B01-3NW*Z?v)V@9Ia)la?M26>s~gJb#<8v;9&*WwA#0LTCI9RJJUz$fEWDtJ(1t z7P9}T^86UPp{IM6zWM`)Z0e2TBU@X%$zE1HIDBR9nXv-xC5pbrm@*Jokabq&{W@*clN&9H{zBz^W6fAu%fgbnclwlbVrPl5F2VV|E_qC&?He$05=1F+ z0x3z^=4zrebMhsq|XrBrx z8LGJOwZo&S%eth_WkC>9S*gX;g6@d^{GD?8+Jqt#30wf07S~dz1~P7bQ09PhGZ1M#RI6iD3PD~VC|$sv zcQ+8fj!dx%s@W4Rmjfm__xJ6T7&3+4xH3^byuf|W7|^KE*%Z5|&r_3pVvDG7lynkzD zCGY9E47c)21v0gom>jFwotvVgqCiga(Qf_Fv-VnFbWQLxq7uQQ(&Mn>LF35@A$Sxf z9*yz=cm?B-9x1!(BuH4bf`GliU)X|c4Ki_tM$HA&@=LFf8XAf6*x+q3H4}8~T6-0^ zH1rycb0k4@5V6fbGfyp3laqwRa>MVc{Osne`~Vn{INQR->t9RZU^tH()7Ao@Eo^6W zY}kk!DG~MSSTNw+Ma{k;kVn*J=PKhxxK+CNy2_QfMXSC(fO~c3)M~5dSm9D=PtG$qU1E3Q>j>}*gj|*MnlFBj!0={zWK|*h(^aksiD3aZHUi5E{F8 Date: Wed, 30 Jul 2025 16:06:43 +0200 Subject: [PATCH 120/242] Update shaders_multi_sample2d.c --- examples/shaders/shaders_multi_sample2d.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/examples/shaders/shaders_multi_sample2d.c b/examples/shaders/shaders_multi_sample2d.c index 8409e35cb..c13864607 100644 --- a/examples/shaders/shaders_multi_sample2d.c +++ b/examples/shaders/shaders_multi_sample2d.c @@ -82,16 +82,18 @@ int main(void) BeginShaderMode(shader); - // WARNING: Additional samplers are enabled for all draw calls in the batch, - // EndShaderMode() forces batch drawing and consequently resets active textures - // to let other sampler2D to be activated on consequent drawings (if required) + // WARNING: Additional textures (sampler2D) are enabled for ALL draw calls in the batch, + // but EndShaderMode() forces batch drawing and resets active textures, this way + // other textures (sampler2D) can be activated on consequent drawings (if required) + // The downside of this approach is that SetShaderValue() must be called inside the loop, + // to be set again after every EndShaderMode() reset SetShaderValueTexture(shader, texBlueLoc, texBlue); - // We are drawing texRed using default sampler2D texture0 but - // an additional texture units is enabled for texBlue (sampler2D texture1) + // We are drawing texRed using default [sampler2D texture0] but + // an additional texture units is enabled for texBlue [sampler2D texture1] DrawTexture(texRed, 0, 0, WHITE); - EndShaderMode(); + EndShaderMode(); // Texture sampler2D is reseted, needs to be set again for next frame DrawText("Use KEY_LEFT/KEY_RIGHT to move texture mixing in shader!", 80, GetScreenHeight() - 40, 20, RAYWHITE); From b40e53f11c90f9ccf5e57077928c46c4a74d0998 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 30 Jul 2025 16:07:08 +0200 Subject: [PATCH 121/242] Remove double line spacing --- src/config.h | 6 ------ src/external/rl_gputex.h | 1 - src/raudio.c | 3 --- 3 files changed, 10 deletions(-) diff --git a/src/config.h b/src/config.h index ef01013b3..bfd1fad13 100644 --- a/src/config.h +++ b/src/config.h @@ -95,7 +95,6 @@ #endif #endif - // rcore: Configuration values //------------------------------------------------------------------------------------ #define MAX_FILEPATH_CAPACITY 8192 // Maximum file paths capacity @@ -153,7 +152,6 @@ #endif #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCE_TX 9 - // Default shader vertex attribute names to set location points // NOTE: When a new shader is loaded, the following locations are tried to be set for convenience #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION @@ -173,7 +171,6 @@ #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1) #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2) - //------------------------------------------------------------------------------------ // Module: rshapes - Configuration Flags //------------------------------------------------------------------------------------ @@ -185,7 +182,6 @@ //------------------------------------------------------------------------------------ #define SPLINE_SEGMENT_DIVISIONS 24 // Spline segments subdivisions - //------------------------------------------------------------------------------------ // Module: rtextures - Configuration Flags //------------------------------------------------------------------------------------ @@ -213,7 +209,6 @@ // If not defined, still some functions are supported: ImageFormat(), ImageCrop(), ImageToPOT() #define SUPPORT_IMAGE_MANIPULATION 1 - //------------------------------------------------------------------------------------ // Module: rtext - Configuration Flags //------------------------------------------------------------------------------------ @@ -240,7 +235,6 @@ // TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal(), TextSplit() #define MAX_TEXTSPLIT_COUNT 128 // Maximum number of substrings to split: TextSplit() - //------------------------------------------------------------------------------------ // Module: rmodels - Configuration Flags //------------------------------------------------------------------------------------ diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index c8d1c6aba..1f0575300 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -141,7 +141,6 @@ RLGPUTEXAPI int rl_save_ktx_to_memory(const char *fileName, void *data, int widt #endif // RL_GPUTEX_H - /*********************************************************************************** * * RL_GPUTEX IMPLEMENTATION diff --git a/src/raudio.c b/src/raudio.c index 3301d7009..eb159beeb 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -451,7 +451,6 @@ void SetAudioBufferPan(AudioBuffer *buffer, float pan); void TrackAudioBuffer(AudioBuffer *buffer); void UntrackAudioBuffer(AudioBuffer *buffer); - //---------------------------------------------------------------------------------- // Module Functions Definition - Audio Device initialization and Closing //---------------------------------------------------------------------------------- @@ -996,7 +995,6 @@ Sound LoadSoundAlias(Sound source) return sound; } - // Checks if a sound is valid (data loaded and buffers initialized) bool IsSoundValid(Sound sound) { @@ -2349,7 +2347,6 @@ void DetachAudioMixedProcessor(AudioCallback process) ma_mutex_unlock(&AUDIO.System.lock); } - //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- From 12f0943bd9e32fe840d5bab4c69ceba913cdf504 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 30 Jul 2025 16:07:14 +0200 Subject: [PATCH 122/242] Update rcore.c --- src/rcore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index c0af6654a..013ea434a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1889,7 +1889,7 @@ void TakeScreenshot(const char *fileName) char path[512] = { 0 }; strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); - ExportImage(image, path); // WARNING: Module required: rtextures + ExportImage(image, path); // WARNING: Module required: rtextures RL_FREE(imgData); if (FileExists(path)) TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); @@ -1936,7 +1936,7 @@ bool FileExists(const char *fileName) } // Check file extension -// NOTE: Extensions checking is not case-sensitive +// TODO: Avoid [rtext] module dependency bool IsFileExtension(const char *fileName, const char *ext) { #define MAX_FILE_EXTENSION_LENGTH 16 From 50acba3ce838c5eeae1509491a4266a0443c8ece Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 30 Jul 2025 17:31:49 +0200 Subject: [PATCH 123/242] REVIEWED: `rcore_web`, cleaning and reorganizing --- src/platforms/rcore_web.c | 315 ++++++++++++++++++-------------------- 1 file changed, 148 insertions(+), 167 deletions(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 1a763a84a..3107c30e4 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -111,23 +111,23 @@ int InitPlatform(void); // Initialize platform (graphics, inputs and mo void ClosePlatform(void); // Close platform // Error callback event -static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error +static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error // Window callbacks events -static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized -static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored +static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 Window Size Callback, runs when window is resized +static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 Window Iconify Callback, runs when window is minimized/restored //static void WindowMaximizeCallback(GLFWwindow *window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized -static void WindowFocusCallback(GLFWwindow *window, int focused); // GLFW3 WindowFocus Callback, runs when window get/lose focus +static void WindowFocusCallback(GLFWwindow *window, int focused); // GLFW3 Window Focus Callback, runs when window get/lose focus static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley); // GLFW3 Window Content Scale Callback, runs when window changes scale // Input callbacks events static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed -static void CharCallback(GLFWwindow *window, unsigned int key); // GLFW3 Char Key Callback, runs on key pressed (get char value) -static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods); // GLFW3 Mouse Button Callback, runs on mouse button pressed -static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); // GLFW3 Cursor Position Callback, runs on mouse move -static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel -static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area +static void CharCallback(GLFWwindow *window, unsigned int key); // GLFW3 Char Key Callback, runs on key pressed (get char value) +static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods); // GLFW3 Mouse Button Callback, runs on mouse button pressed +static void MouseMoveCallback(GLFWwindow *window, double x, double y); // GLFW3 Mouse Move Callback, runs on mouse move +static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Mouse Scrolling Callback, runs on mouse wheel +static void MouseEnterCallback(GLFWwindow *window, int enter); // GLFW3 Mouse Enter Callback, cursor enters client area // Emscripten window callback events static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData); @@ -1293,23 +1293,24 @@ int InitPlatform(void) emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " "); // Set window callback events - glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! + glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback); glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback); glfwSetDropCallback(platform.handle, WindowDropCallback); if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) { - glfwSetWindowContentScaleCallback(platform.handle, WindowContentScaleCallback); + // Window content (framebuffer) scale callback + glfwSetWindowContentScaleCallback(platform.handle, WindowContentScaleCallback); } // Set input callback events glfwSetKeyCallback(platform.handle, KeyCallback); glfwSetCharCallback(platform.handle, CharCallback); glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback); - glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes + glfwSetCursorPosCallback(platform.handle, MouseMoveCallback); glfwSetScrollCallback(platform.handle, MouseScrollCallback); - glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); + glfwSetCursorEnterCallback(platform.handle, MouseEnterCallback); glfwMakeContextCurrent(platform.handle); result = true; // TODO: WARNING: glfwGetError(NULL); symbol can not be found in Web @@ -1349,50 +1350,36 @@ int InitPlatform(void) rlLoadExtensions(glfwGetProcAddress); //---------------------------------------------------------------------------- - // Initialize input events callbacks + // Initialize events callbacks //---------------------------------------------------------------------------- - // Setup callback functions for the DOM events + // Setup window events callbacks emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenFullscreenChangeCallback); - + emscripten_set_blur_callback(GetCanvasId(), platform.handle, 1, EmscriptenFocusCallback); + emscripten_set_focus_callback(GetCanvasId(), platform.handle, 1, EmscriptenFocusCallback); + emscripten_set_visibilitychange_callback(NULL, 1, EmscriptenVisibilityChangeCallback); + // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas) // emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); // Check Resize event (note this is done on the window since most browsers don't support this on #canvas) emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - // Trigger this once to get initial window sizing + // Trigger resize callback to force initial size EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); - // Support keyboard events - // NOTE: used only to consume keyboard events. GLFW.JS takes care of - // the actual input. + // Setup input events + // NOTE: Keyboard callbacks only used to consume some events, libglfw.js takes care of the actual input emscripten_set_keypress_callback(GetCanvasId(), NULL, 1, EmscriptenKeyboardCallback); emscripten_set_keydown_callback(GetCanvasId(), NULL, 1, EmscriptenKeyboardCallback); - - // Support mouse events emscripten_set_click_callback(GetCanvasId(), NULL, 1, EmscriptenMouseCallback); emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenPointerlockCallback); - - // Following the mouse delta when the mouse is locked emscripten_set_mousemove_callback(GetCanvasId(), NULL, 1, EmscriptenMouseMoveCallback); - - // Support touch events emscripten_set_touchstart_callback(GetCanvasId(), NULL, 1, EmscriptenTouchCallback); emscripten_set_touchend_callback(GetCanvasId(), NULL, 1, EmscriptenTouchCallback); emscripten_set_touchmove_callback(GetCanvasId(), NULL, 1, EmscriptenTouchCallback); emscripten_set_touchcancel_callback(GetCanvasId(), NULL, 1, EmscriptenTouchCallback); - - // Support gamepad events (not provided by GLFW3 on emscripten) emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); - - // Support focus events - emscripten_set_blur_callback(GetCanvasId(), platform.handle, 1, EmscriptenFocusCallback); - emscripten_set_focus_callback(GetCanvasId(), platform.handle, 1, EmscriptenFocusCallback); - - // Support visibility events - emscripten_set_visibilitychange_callback(NULL, 1, EmscriptenVisibilityChangeCallback); - //---------------------------------------------------------------------------- // Initialize timing system @@ -1417,14 +1404,15 @@ void ClosePlatform(void) glfwTerminate(); } -// GLFW3 Error Callback, runs on GLFW3 error +// GLFW3 callback functions, called on GLFW registered events +//------------------------------------------------------------------------------------------------------- +// GLFW3: Called on errors static void ErrorCallback(int error, const char *description) { TRACELOG(LOG_WARNING, "GLFW: Error: %i Description: %s", error, description); } -// GLFW3 WindowSize Callback, runs when window is resizedLastFrame -// NOTE: Window resizing not allowed by default +// GLFW3: Called on window resizing, runs when window is resizedLastFrame static void WindowSizeCallback(GLFWwindow *window, int width, int height) { // Reset viewport and projection matrix for new size @@ -1453,34 +1441,27 @@ static void WindowSizeCallback(GLFWwindow *window, int width, int height) // NOTE: Postprocessing texture is not scaled to new size } +// GLFW3: Called on window content (framebuffer) scaled static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley) { CORE.Window.screenScale = MatrixScale(scalex, scaley, 1.0f); } -// GLFW3 WindowIconify Callback, runs when window is minimized/restored +// GLFW3: Called on windows minimized/restored static void WindowIconifyCallback(GLFWwindow *window, int iconified) { if (iconified) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; // The window was iconified else CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // The window was restored } -/* -// GLFW3 Window Maximize Callback, runs when window is maximized -static void WindowMaximizeCallback(GLFWwindow *window, int maximized) -{ - // TODO. -} -*/ - -// GLFW3 WindowFocus Callback, runs when window get/lose focus +// GLFW3: Called on windows get/lose focus static void WindowFocusCallback(GLFWwindow *window, int focused) { if (focused) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // The window was focused else CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; // The window lost focus } -// GLFW3 Window Drop Callback, runs when drop files into window +// GLFW3: Called on file-drop over the window static void WindowDropCallback(GLFWwindow *window, int count, const char **paths) { if (count > 0) @@ -1508,7 +1489,7 @@ static void WindowDropCallback(GLFWwindow *window, int count, const char **paths } } -// GLFW3 Keyboard Callback, runs on key pressed +// GLFW3: Called on keyboard interaction static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) { if (key < 0) return; // Security check, macOS fn key generates -1 @@ -1531,7 +1512,7 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE); } -// GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value) +// GLFW3: Called on key down interaction, gets equivalent unicode char value for the key static void CharCallback(GLFWwindow *window, unsigned int key) { //TRACELOG(LOG_DEBUG, "Char Callback: KEY:%i(%c)", key, key); @@ -1550,7 +1531,7 @@ static void CharCallback(GLFWwindow *window, unsigned int key) } } -// GLFW3 Mouse Button Callback, runs on mouse button pressed +// GLFW3: Called on mouse button interaction static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods) { // WARNING: GLFW could only return GLFW_PRESS (1) or GLFW_RELEASE (0) for now, @@ -1566,7 +1547,7 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) gestureEvent.touchAction = TOUCH_ACTION_DOWN; else if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) gestureEvent.touchAction = TOUCH_ACTION_UP; - // NOTE: TOUCH_ACTION_MOVE event is registered in MouseCursorPosCallback() + // NOTE: TOUCH_ACTION_MOVE event is registered in MouseMoveCallback() // Assign a pointer ID gestureEvent.pointId[0] = 0; @@ -1588,8 +1569,8 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int #endif } -// GLFW3 Cursor Position Callback, runs on mouse move -static void MouseCursorPosCallback(GLFWwindow *window, double x, double y) +// GLFW3: Called on mouse move +static void MouseMoveCallback(GLFWwindow *window, double x, double y) { // If the pointer is not locked, follow the position if (!CORE.Input.Mouse.cursorHidden) @@ -1623,14 +1604,39 @@ static void MouseCursorPosCallback(GLFWwindow *window, double x, double y) #endif } +// GLFW3: Called on mouse wheel scrolling +static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset) +{ + CORE.Input.Mouse.currentWheelMove = (Vector2){ (float)xoffset, (float)yoffset }; +} + +// GLFW3: Called on mouse entering the window +static void MouseEnterCallback(GLFWwindow *window, int enter) +{ + if (enter) CORE.Input.Mouse.cursorOnScreen = true; + else CORE.Input.Mouse.cursorOnScreen = false; +} +//------------------------------------------------------------------------------------------------------- + +// Emscripten callback functions, called on specific browser events +//------------------------------------------------------------------------------------------------------- +// Emscripten: Called on key events static EM_BOOL EmscriptenKeyboardCallback(int eventType, const EmscriptenKeyboardEvent *keyboardEvent, void *userData) { - // NOTE: handled by GLFW, this is only to consume the keyboard events so we - // make use of F-keys and other shortcuts without triggering browser - // functions. + // NOTE: Only used to consume some keyboard events without triggering browser functions + return 1; // The event was consumed by the callback handler } +// Emscripten: Called on mouse input events +static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) +{ + // This is only for registering mouse click events with emscripten and doesn't need to do anything + + return 1; // The event was consumed by the callback handler +} + +// Emscripten: Called on mouse move events static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) { // To emulate the GLFW_RAW_MOUSE_MOTION property. @@ -1643,92 +1649,7 @@ static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseE return 1; // The event was consumed by the callback handler } -// GLFW3 Scrolling Callback, runs on mouse wheel -static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset) -{ - CORE.Input.Mouse.currentWheelMove = (Vector2){ (float)xoffset, (float)yoffset }; -} - -// GLFW3 CursorEnter Callback, when cursor enters the window -static void CursorEnterCallback(GLFWwindow *window, int enter) -{ - if (enter) CORE.Input.Mouse.cursorOnScreen = true; - else CORE.Input.Mouse.cursorOnScreen = false; -} - -// Register fullscreen change events -static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData) -{ - // NOTE: 1. Reset the fullscreen flags if the user left fullscreen manually by pressing the Escape key - // 2. Which is a necessary safeguard because that case will bypass the toggles CORE.Window.flags resets - if (platform.ourFullscreen) platform.ourFullscreen = false; - else - { - const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0); - if (!wasFullscreen) - { - CORE.Window.fullscreen = false; - CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; - CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; - } - } - - return 1; // The event was consumed by the callback handler -} - -// Register window resize event -// static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData) -// { -// // TODO: Implement EmscriptenWindowResizedCallback()? - -// return 1; // The event was consumed by the callback handler -// } - -// Register DOM element resize event -static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData) -{ - // Don't resize non-resizeable windows - if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) return 1; - - // This event is called whenever the window changes sizes, - // so the size of the canvas object is explicitly retrieved below - int width = EM_ASM_INT( return window.innerWidth; ); - int height = EM_ASM_INT( return window.innerHeight; ); - - if (width < (int)CORE.Window.screenMin.width) width = CORE.Window.screenMin.width; - else if ((width > (int)CORE.Window.screenMax.width) && (CORE.Window.screenMax.width > 0)) width = CORE.Window.screenMax.width; - - if (height < (int)CORE.Window.screenMin.height) height = CORE.Window.screenMin.height; - else if ((height > (int)CORE.Window.screenMax.height) && (CORE.Window.screenMax.height > 0)) height = CORE.Window.screenMax.height; - - emscripten_set_canvas_element_size(GetCanvasId(), width, height); - - SetupViewport(width, height); // Reset viewport and projection matrix for new size - - CORE.Window.currentFbo.width = width; - CORE.Window.currentFbo.height = height; - CORE.Window.resizedLastFrame = true; - - if (IsWindowFullscreen()) return 1; - - // Set current screen size - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - - // NOTE: Postprocessing texture is not scaled to new size - - return 0; -} - -// Register mouse input events -static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) -{ - // This is only for registering mouse click events with emscripten and doesn't need to do anything - - return 1; // The event was consumed by the callback handler -} - -// Register pointer lock events +// Emscripten: Called on pointer lock events static EM_BOOL EmscriptenPointerlockCallback(int eventType, const EmscriptenPointerlockChangeEvent *pointerlockChangeEvent, void *userData) { CORE.Input.Mouse.cursorHidden = EM_ASM_INT( { if (document.pointerLockElement) return 1; }, 0); @@ -1742,7 +1663,7 @@ static EM_BOOL EmscriptenPointerlockCallback(int eventType, const EmscriptenPoin return 1; // The event was consumed by the callback handler } -// Register connected/disconnected gamepads events +// Emscripten: Called on connect/disconnect gamepads events static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) { /* @@ -1764,26 +1685,7 @@ static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadE return 1; // The event was consumed by the callback handler } -static EM_BOOL EmscriptenFocusCallback(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData) -{ - EM_BOOL consumed = 1; - switch (eventType) - { - case EMSCRIPTEN_EVENT_BLUR: WindowFocusCallback(userData, 0); break; - case EMSCRIPTEN_EVENT_FOCUS: WindowFocusCallback(userData, 1); break; - default: consumed = 0; break; - } - return consumed; -} - -static EM_BOOL EmscriptenVisibilityChangeCallback(int eventType, const EmscriptenVisibilityChangeEvent *visibilityChangeEvent, void *userData) -{ - if (visibilityChangeEvent->hidden) CORE.Window.flags |= FLAG_WINDOW_HIDDEN; // The window was hidden - else CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // The window was restored - return 1; // The event was consumed by the callback handler -} - -// Register touch input events +// Emscripten: Called on touch input events static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData) { // Register touch points count @@ -1869,7 +1771,85 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent return 1; // The event was consumed by the callback handler } -// obtaining the canvas id provided by the module configuration +// Emscripten: Called on fullscreen change events +static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData) +{ + // NOTE: 1. Reset the fullscreen flags if the user left fullscreen manually by pressing the Escape key + // 2. Which is a necessary safeguard because that case will bypass the toggles CORE.Window.flags resets + if (platform.ourFullscreen) platform.ourFullscreen = false; + else + { + const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0); + if (!wasFullscreen) + { + CORE.Window.fullscreen = false; + CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; + } + } + + return 1; // The event was consumed by the callback handler +} + +// Emscripten: Called on resize event +static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData) +{ + // Don't resize non-resizeable windows + if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) return 1; + + // This event is called whenever the window changes sizes, + // so the size of the canvas object is explicitly retrieved below + int width = EM_ASM_INT( return window.innerWidth; ); + int height = EM_ASM_INT( return window.innerHeight; ); + + if (width < (int)CORE.Window.screenMin.width) width = CORE.Window.screenMin.width; + else if ((width > (int)CORE.Window.screenMax.width) && (CORE.Window.screenMax.width > 0)) width = CORE.Window.screenMax.width; + + if (height < (int)CORE.Window.screenMin.height) height = CORE.Window.screenMin.height; + else if ((height > (int)CORE.Window.screenMax.height) && (CORE.Window.screenMax.height > 0)) height = CORE.Window.screenMax.height; + + emscripten_set_canvas_element_size(GetCanvasId(), width, height); + + SetupViewport(width, height); // Reset viewport and projection matrix for new size + + CORE.Window.currentFbo.width = width; + CORE.Window.currentFbo.height = height; + CORE.Window.resizedLastFrame = true; + + if (IsWindowFullscreen()) return 1; + + // Set current screen size + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + + // NOTE: Postprocessing texture is not scaled to new size + + return 0; +} + +// Emscripten: Called on windows focus change events +static EM_BOOL EmscriptenFocusCallback(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData) +{ + EM_BOOL consumed = 1; + switch (eventType) + { + case EMSCRIPTEN_EVENT_BLUR: WindowFocusCallback(userData, 0); break; + case EMSCRIPTEN_EVENT_FOCUS: WindowFocusCallback(userData, 1); break; + default: consumed = 0; break; + } + return consumed; +} + +// Emscripten: Called on visibility change events +static EM_BOOL EmscriptenVisibilityChangeCallback(int eventType, const EmscriptenVisibilityChangeEvent *visibilityChangeEvent, void *userData) +{ + if (visibilityChangeEvent->hidden) CORE.Window.flags |= FLAG_WINDOW_HIDDEN; // The window was hidden + else CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // The window was restored + return 1; // The event was consumed by the callback handler +} +//------------------------------------------------------------------------------------------------------- + +// JS: Get the canvas id provided by the module configuration EM_JS(char*, GetCanvasIdJs, (), { var canvasId = "#" + Module.canvas.id; var lengthBytes = lengthBytesUTF8(canvasId) + 1; @@ -1878,6 +1858,7 @@ EM_JS(char*, GetCanvasIdJs, (), { return stringOnWasmHeap; }); +// Get canvas id (using embedded JS function) static const char *GetCanvasId(void) { static char *canvasId = NULL; From b273ba930b431bd834acd381d219837cbb8d56e9 Mon Sep 17 00:00:00 2001 From: RANDRIA Luca Date: Thu, 31 Jul 2025 01:05:00 +0300 Subject: [PATCH 124/242] Add screenshot for core_basic_window_web The veritable screenshot is this --- examples/core/core_basic_window_web.png | Bin 4402 -> 15144 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/examples/core/core_basic_window_web.png b/examples/core/core_basic_window_web.png index c01b78748e9480c2d20f18c94601e04b0b8356b5..6cf44a0c87340dcc7e216ed531216a4e649241eb 100644 GIT binary patch literal 15144 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYU_8XZ1{4ubUVDOp!Ro!Ii(^PdT=Jh^Utia^ z1sX^(Emp)qlyEREyBHwH+jvnx0xQ8-)p{U5u-U`G)Ii)2=qSoXt^+2 zE{v87qvgVAxqxl_bF_3EEgeTo$I;ReZ|UgpU0_GePa|fIezw1`Lgb5pgf74P=K~%* zjTiY&WbBg1IyVEHv{?|%v7x8!D^TAN0fo{pvEkv<6II$TJN%Xxi*LE(xMtVI82=a! zo{);ZW6G79Q|>)9i@AI}<0upl>C4JB1o^686!yGY_VybXr zs9n-~W3RG7t#hocPR515j+r|yXkVNt{cnd`glDX@oz(vohQ3+*6wKxdbcvT#MlI(K zI;(zd{*rf%i%NefZ>v#wVqf&Q|JqX>z5fqC@$af$5!%-ib_r;=jB`Q>TZ&0+KXSr@ zg)B(yf-Z|&I&jYFb_T};L8cbBZ5MiFG`=3$#k`E~ddEt=iOU;aUiz`cAj!Q%lea!A zoh#^YYt*ZYhr4eGmIPhq^|4)h*>dlr);lF`c{?ud5|CT*#eLm=kGYvk!hb27)-ImD zdYAGS6IbxOTZ9wXDUq;30NE8_F(jw-ED%_5?S$Ss1?3dGoyrYIUM=4GpDjB`@!EZ6 z+5d}Mc~cb~UiFCW+;&k*zW>;Ns7VJP7U&XV!2?dlqU%ch-yBrSb&d7vC+^ywP@-#6 z8qBfwThD}7r!H13%{{lg^v9%G{qEZQuUZad?348MR5k>M)n$0nL=RMkyAUUA?0q4i zQ11IDAl}=5)o;$ZeXi>&zuda*sQZ7??8uD0%jSiAUMc&3L&3tW)*P+&Yah?N_OosQ zcXH8R_Z2k?*WzD1yI{5F<=nV}o9x2!b2%k*C-g3XB&YXyk`v>x_3``v$@9JkPaQ54 zWLm78x>&Q+gvEouapAmtx6gkwRzdPk9wen z*^FIM&>Xl>6r3eA%dqDdH|GYRO}l^-rno07k>eaaG@&I0P(LruDCOA?37D&R0tQ@c zBuV2bHo&vCKo8=wX4DCwkQohy(NKWp(b2RpnifXW0&-h+G)Ii)h|wG|nj_F#k)!3p zXt^+2E{v87qvZm&?)7NtI9fW6mX4#P<7nwPS~`MC!qHF|4F%9t!)PdshQjEq3}{Mw oG!#Zd0lXcRU|ImqJ~J@H^+w%v&e6>S?to?RboFyt=akR{0D#NXK>z>% literal 4402 zcmZWsd0bOh7Je#I7_EY}?h7f*XsZ>Y6j_Hz1zCfWSalhOJ;CL>(7wtvFVS zKv-N5$wEMks1Q;C#Wi$xv|O|!tTDYs2N!&fq=n(oFQMhV|<@ixK#9WdFKj{m(d(akS$S8P~f zGCe*N3y+D74qcrP5+52Glkkt&#DQrNXZ+A=bZBFIC_OPEHiqOK5gm&8hVCO-lSzM1 ziXyEeud^YMzjLr5JJ_rzZTBR(xp?1ypMhU(_R|(u7w;5Kf177)fFkL$=OW=Z+|7Ua zDeJPU+Oqtm+VXnT#kuP29Zv@ryeymYY{#K%|2!xiGko*%WgZ*Ca=T# zlN&Z2`Z9X?mcKpnK|c$(n&VGWmmPGN7H}q48~8;^BaJaI{EQ*g zOvNyhlmT}NSJWH7`oNpp#v#H#Vc5Ajsj7=J>k2LKZCAcWvB)8{ba}`cBYelag?c)h zgs)$+al!w-r>2U?!*~b>N8Wa1uF|lA(zd#x^@TCYQtlV7_=Xdli#%+CVW;JPnvG$5 zjnKQ<3Y6A3c(u;e(4204jTY<+TE)90Ng6J?qbn*VM$X>D7Cs|?p8DzzzCB0}br8sU zum1Sa8cCJr_$xYmgm5T=DieiG!LSfBbh#b6JU#NS$h0M_TnU6LFWbz(umy(_;E)P! zyNIu$-_cuE@MhovZP>27IUPOE(UU&<9H#gd!$^-x{qc407Gj@+F0X%DGat8ibRixe zNqS$6@1I4#BS}xi29H{134}Kr#X=S!-#BvQrx&V3`mWjd9h*q#$POvI z7ZS0fTaQFM#{VaF>0!vgY!eG)0ByQzpn#KBj@Tn7O&CQe5Wr(i2FUp zVdXInpGQBq)dQY>$6xI63w|@1fJX2m$PSSy%ZW^dY(kYyIEDLerbsTGKy*65!wAZh z-sfl_5Dci~XMy8Y1WqBWS|S!2ss<5@w*n!s3(V94GEYMqF4@%K zupjrHn+&<~V*n7Yl}d3kRm&PY)(H5hsN3o^wsk@PX5Y~7D>ta&o`R0*5bAg zr1aj&Qn&VsXbV_4=6?Hdqx|ilTJpggnk7N9bn8SVxQ@S@Vc6zzsQy2ap>;Y4RDBAJ zv%esi+O0rPj`M<6vkIu$V|eqrN_LF?GGq-d6z5FC8#yI2R0=BU8ctR6fc)p5XQCFp z=g)p|baXh`KqK6G{pt^<{*gaGStZSL)Vy#vhq}oBebf?_?sP)$yKbgxH=B;ide1-K ztFJ53A80fZ=zzq}f@T#FEGolUVMDv7PtiB3=?hsiZEvD}*}6tR4VTJ0T0VbD$kYC| zV&B01-3NW*Z?v)V@9Ia)la?M26>s~gJb#<8v;9&*WwA#0LTCI9RJJUz$fEWDtJ(1t z7P9}T^86UPp{IM6zWM`)Z0e2TBU@X%$zE1HIDBR9nXv-xC5pbrm@*Jokabq&{W@*clN&9H{zBz^W6fAu%fgbnclwlbVrPl5F2VV|E_qC&?He$05=1F+ z0x3z^=4zrebMhsq|XrBrx z8LGJOwZo&S%eth_WkC>9S*gX;g6@d^{GD?8+Jqt#30wf07S~dz1~P7bQ09PhGZ1M#RI6iD3PD~VC|$sv zcQ+8fj!dx%s@W4Rmjfm__xJ6T7&3+4xH3^byuf|W7|^KE*%Z5|&r_3pVvDG7lynkzD zCGY9E47c)21v0gom>jFwotvVgqCiga(Qf_Fv-VnFbWQLxq7uQQ(&Mn>LF35@A$Sxf z9*yz=cm?B-9x1!(BuH4bf`GliU)X|c4Ki_tM$HA&@=LFf8XAf6*x+q3H4}8~T6-0^ zH1rycb0k4@5V6fbGfyp3laqwRa>MVc{Osne`~Vn{INQR->t9RZU^tH()7Ao@Eo^6W zY}kk!DG~MSSTNw+Ma{k;kVn*J=PKhxxK+CNy2_QfMXSC(fO~c3)M~5dSm9D=PtG$qU1E3Q>j>}*gj|*MnlFBj!0={zWK|*h(^aksiD3aZHUi5E{F8 Date: Wed, 30 Jul 2025 22:40:54 -0600 Subject: [PATCH 125/242] [build] Add ARM64 support to Linux builds - Add ubuntu-24.04-arm runner for native ARM64 compilation - Support building both static and shared libraries for ARM64 - Maintain backwards compatibility with existing x86/x64 builds - Addresses issue #5051 for ARM64 pre-compiled binaries Expected output: raylib-X.X-linux_arm64.tar.gz in releases --- .github/workflows/linux.yml | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index a4ce5329b..a4879841c 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -22,22 +22,28 @@ jobs: build: permissions: contents: write # for actions/upload-release-asset to upload release asset - runs-on: ubuntu-latest strategy: fail-fast: false max-parallel: 1 matrix: - bits: [32, 64] include: - bits: 32 ARCH: "i386" ARCH_NAME: "i386" COMPILER_PATH: "/user/bin" + runner: "ubuntu-latest" - bits: 64 ARCH: "x86_64" ARCH_NAME: "amd64" COMPILER_PATH: "/user/bin" + runner: "ubuntu-latest" + - bits: 64 + ARCH: "aarch64" + ARCH_NAME: "arm64" + COMPILER_PATH: "/user/bin" + runner: "ubuntu-24.04-arm" + runs-on: ${{ matrix.runner }} env: RELEASE_NAME: raylib-dev_linux_${{ matrix.ARCH_NAME }} @@ -66,7 +72,7 @@ jobs: # ${{ matrix.ARCH }}-linux-gnu-gcc -v # TODO: Support 32bit (i386) static/shared library building - - name: Build Library + - name: Build Library (32-bit) run: | cd src make PLATFORM=PLATFORM_DESKTOP CC=gcc RAYLIB_LIBTYPE=STATIC RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" CUSTOM_CFLAGS="-m32" -B @@ -74,13 +80,21 @@ jobs: cd .. if: matrix.bits == 32 - - name: Build Library + - name: Build Library (64-bit x86) run: | cd src make PLATFORM=PLATFORM_DESKTOP CC=gcc RAYLIB_LIBTYPE=STATIC RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" -B make PLATFORM=PLATFORM_DESKTOP CC=gcc RAYLIB_LIBTYPE=SHARED RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" -B cd .. - if: matrix.bits == 64 + if: matrix.bits == 64 && matrix.ARCH == 'x86_64' + + - name: Build Library (64-bit ARM) + run: | + cd src + make PLATFORM=PLATFORM_DESKTOP CC=gcc RAYLIB_LIBTYPE=STATIC RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" -B + make PLATFORM=PLATFORM_DESKTOP CC=gcc RAYLIB_LIBTYPE=SHARED RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" -B + cd .. + if: matrix.bits == 64 && matrix.ARCH == 'aarch64' - name: Generate Artifacts run: | From 38ff5d00a028f29ff53be881a910517883cb0f7d Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 31 Jul 2025 23:55:26 +0200 Subject: [PATCH 126/242] Create build_example_web.bat --- examples/build_example_web.bat | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 examples/build_example_web.bat diff --git a/examples/build_example_web.bat b/examples/build_example_web.bat new file mode 100644 index 000000000..b04439c8a --- /dev/null +++ b/examples/build_example_web.bat @@ -0,0 +1,43 @@ +::@echo off +:: . +:: Compile your examples for web using: build_example_web.bat \ +:: . +set "INPUT_FILE=%1" +:: Change delimiter for the FOR loop +for /f "tokens=1-10 delims=\" %%a in ("%INPUT_FILE%") do ( + set CATEGORY=%%a + set FILENAME=%%b +) +:: > Setup required Environment +:: ------------------------------------- +set RAYLIB_PATH=C:\GitHub\raylib +set EMSDK_PATH=C:\raylib\emsdk +echo +:: Set required web compilation options +:: ------------------------------------- +set CC=%EMSDK_PATH%\upstream\emscripten\emcc +set CFLAGS=-Wall -std=c99 -D_DEFAULT_SOURCE -Wno-missing-braces -Wunused-result -O3 -I. -Iexternal -I%RAYLIB_PATH%\src -I%RAYLIB_PATH%\external -DPLATFORM_WEB +set LDFLAGS=-L. -L$(RAYLIB_PATH)\src -sUSE_GLFW=3 -sEXPORTED_RUNTIME_METHODS=ccall -sASYNCIFY --shell-file %RAYLIB_PATH%\src\shell.html +set LDLIBS=%RAYLIB_PATH%\src\libraylib.web.a +:: TODO: If using some resources/assets, set the directory path as shown in the commented line! +set RESOURCES= +::set RESOURCES=--preload-file $(RESOURCES_PATH) +cd %RAYLIB_PATH%\examples\%CATEGORY% +echo +:: Clean latest build +:: ------------------------ +cmd /c if exist %FILENAME%.html del /F %FILENAME%.html +cmd /c if exist %FILENAME%.wasm del /F %FILENAME%.wasm +cmd /c if exist %FILENAME%.js del /F %FILENAME%.js +cmd /c if exist %FILENAME%.data del /F %FILENAME%.data +echo +:: Setup emsdk environment +:: -------------------------- +cmd /c %EMSDK_PATH%\emsdk_env.bat +echo +:: Compile program +:: ----------------------- +cmd /c %CC% --version +cmd /c %CC% -o %FILENAME%.html %FILENAME%.c %CFLAGS% %LDFLAGS% %LDLIBS% %RESOURCES% +cd .. +echo From 968385b5f4b38096c42b38eb741feddd54e8afd2 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 31 Jul 2025 23:55:46 +0200 Subject: [PATCH 127/242] Update examples_template.c --- examples/examples_template.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/examples_template.c b/examples/examples_template.c index 49136a4d4..6ef7214eb 100644 --- a/examples/examples_template.c +++ b/examples/examples_template.c @@ -41,7 +41,7 @@ - raylib/examples//_example_name.c - raylib/examples//_example_name.png - - raylib/examples//resources/*.* + - raylib/examples//resources/.. - raylib/examples/Makefile - raylib/examples/Makefile.Web - raylib/examples/README.md @@ -104,9 +104,11 @@ int main(void) ClearBackground(RAYWHITE); - // TODO: Draw everything that requires to be drawn at this point: + // TODO: Draw everything that requires to be drawn at this point - DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); // Example + DrawLineEx((Vector2){ 0, 0 }, (Vector2){ screenWidth, screenHeight }, 2.0f, RED); + DrawLineEx((Vector2){ 0, screenHeight }, (Vector2){ screenWidth, 0 }, 2.0f, RED); + DrawText("example base code template", 260, 400, 20, LIGHTGRAY); EndDrawing(); //---------------------------------------------------------------------------------- From 4b2654f85109df88d558a6fb2d2884dd4ca7b952 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 31 Jul 2025 23:55:50 +0200 Subject: [PATCH 128/242] Create examples_template.png --- examples/examples_template.png | Bin 0 -> 17323 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/examples_template.png diff --git a/examples/examples_template.png b/examples/examples_template.png new file mode 100644 index 0000000000000000000000000000000000000000..da99bbb0d97118eef3ca41c16b62182acced63f9 GIT binary patch literal 17323 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 aXx{Fh`%kLYY!czW3FF=h-xU_U{Qm(sHl%3) literal 0 HcmV?d00001 From d88f27f08d160edaae89a04828aee6cc3650fa04 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 1 Aug 2025 00:30:01 +0200 Subject: [PATCH 129/242] NEW: ADDED: `rexm` - A simple command-line tool to manage raylib examples -WIP- --- examples/rexm.c | 349 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 349 insertions(+) create mode 100644 examples/rexm.c diff --git a/examples/rexm.c b/examples/rexm.c new file mode 100644 index 000000000..d40c2ce1c --- /dev/null +++ b/examples/rexm.c @@ -0,0 +1,349 @@ +/******************************************************************************************* +* +* rexm [raylib examples manager] - A simple command-line tool to manage raylib examples +* +* Supported processes: +* - create +* - add +* - rename +* - remove +* +* Files involved in the processes: +* - raylib/examples//_example_name.c +* - raylib/examples//_example_name.png +* - raylib/examples//resources/.. +* - raylib/examples/Makefile +* - raylib/examples/Makefile.Web +* - raylib/examples/README.md +* - raylib/projects/VS2022/examples/_example_name.vcxproj +* - raylib/projects/VS2022/raylib.sln +* - raylib.com/common/examples.js +* - raylib.com/examples//_example_name.html +* - raylib.com/examples//_example_name.data +* - raylib.com/examples//_example_name.wasm +* - raylib.com/examples//_example_name.js +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2025 Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#include "raylib.h" + +#include +#include // Required for: rename(), remove() +#include // Required for: strcmp(), strcpy() + +#define SUPPORT_LOG_INFO +#if defined(SUPPORT_LOG_INFO) && defined(_DEBUG) + #define LOG(...) printf(__VA_ARGS__) +#else + #define LOG(...) +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +// Example management operations +typedef enum { + OP_NONE = 0, + OP_CREATE = 1, + OP_ADD = 2, + OP_RENAME = 3, + OP_REMOVE = 4 +} ExampleOperation; + +//---------------------------------------------------------------------------------- +// Module specific functions declaration +//---------------------------------------------------------------------------------- +static int FileTextReplace(const char *fileName, const char *textLookUp, const char *textReplace); +static int FileCopy(const char *srcPath, const char *dstPath); +static int FileRename(const char *fileName, const char *fileRename); // TODO: Implement, make sure to deal with paths moving +static int FileRemove(const char *fileName); // TODO: Implement + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(int argc, char *argv[]) +{ + // Examples path for building + // TODO: Avoid hardcoding path values... + char *exBasePath = "C:/GitHub/raylib/examples"; + char *exWebPath = "C:/GitHub/raylib.com/examples"; + char *exTemplateFilePath = "C:/GitHub/raylib/examples/examples_template.c"; + + char inFileName[1024] = { 0 }; // Example input filename + + char exName[1024] = { 0 }; // Example name, without extension: core_basic_window + char exCategory[32] = { 0 }; // Example category: core + char exRename[1024] = { 0 }; // Example re-name, without extension + char exPath[1024] = { 0 }; // Example path -NOT USED- + char exFullPath[1024] = { 0 }; // Example full path -NOT USED- + + int opCode = OP_NONE; // Operation code: 0-None(Help), 1-Create, 2-Add, 3-Rename, 4-Remove + + // Command-line usage mode + //-------------------------------------------------------------------------------------- + if (argc > 1) + { + // Supported commands: + // help : Provides command-line usage information (default) + // create : Creates an empty example, from internal template + // add : Add existing example, category extracted from name + // rename : Rename an existing example + // remove : Remove an existing example + if (strcmp(argv[1], "create") == 0) + { + // Check for valid upcoming argument + if (argc == 2) LOG("WARNING: No filename provided to create\n"); + else if (argc > 3) LOG("WARNING: Too many arguments provided\n"); + else + { + // TODO: Additional security checks for file name? + + strcpy(inFileName, argv[2]); // Register filename for creation + opCode = 1; + } + } + else if (strcmp(argv[1], "add") == 0) + { + // Check for valid upcoming argument + if (argc == 2) LOG("WARNING: No filename provided to create\n"); + else if (argc > 3) LOG("WARNING: Too many arguments provided\n"); + else + { + if (IsFileExtension(argv[2], ".c")) // Check for valid file extension: input + { + // TODO: Parse category name from filename provided! + + strcpy(inFileName, argv[2]); // Register filename for creation + opCode = 2; + } + else LOG("WARNING: Input file extension not recognized (.c)\n"); + } + } + else if (strcmp(argv[1], "rename") == 0) + { + if (argc == 2) LOG("WARNING: No filename provided to create\n"); + else if (argc == 3) LOG("WARNING: No enough arguments provided\n"); + else if (argc > 4) LOG("WARNING: Too many arguments provided\n"); + else + { + // TODO: Register exName, exCategory and exRename + + opCode = 3; + } + } + else if (strcmp(argv[1], "remove") == 0) + { + // Check for valid upcoming argument + if (argc == 2) LOG("WARNING: No filename provided to create\n"); + else if (argc > 3) LOG("WARNING: Too many arguments provided\n"); + else + { + strcpy(inFileName, argv[2]); // Register filename for removal + opCode = 4; + } + } + } + + switch (opCode) + { + case 1: // Create: New example from template + { + // Copy template file as new example + FileCopy(exTemplateFilePath, TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); + } + case 2: // Add: Example from command-line input filename + { + if ((opCode != 1) && FileExists(inFileName)) + { + FileCopy(inFileName, TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); + } + + // Generate all required files + //-------------------------------------------------------------------------------- + // Create: raylib/examples//_example_name.c + // Create: raylib/examples//_example_name.png + FileCopy("C:/GitHub/raylib/examples/examples_template.png", + TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName)); // To be updated manually! + + // Copy: raylib/examples//resources/*.* ---> To be updated manually! + + // TODO: Update the required files to add new example in the required position (ordered by category and name), + // it could require some logic to make it possible... + + // Edit: raylib/examples/Makefile --> Add new example + // Edit: raylib/examples/Makefile.Web --> Add new example + // Edit: raylib/examples/README.md --> Add new example + + // Create: raylib/projects/VS2022/examples/_example_name.vcxproj + // Edit: raylib/projects/VS2022/raylib.sln --> Add new example + // Edit: raylib.com/common/examples.js --> Add new example + + // Compile to: raylib.com/examples//_example_name.html + // Compile to: raylib.com/examples//_example_name.data + // Compile to: raylib.com/examples//_example_name.wasm + // Compile to: raylib.com/examples//_example_name.js + + // Recompile example (on raylib side) + // NOTE: Tools requirements: emscripten, w64devkit + system(TextFormat("%s/../build_example_web.bat %s\%s", exBasePath, exCategory, exName)); + + // Copy results to web side + FileCopy(TextFormat("%s/%s/%s.html", exBasePath, exCategory, exName), + TextFormat("%s/%s/%s.html", exWebPath, exCategory, exName)); + FileCopy(TextFormat("%s/%s/%s.data", exBasePath, exCategory, exName), + TextFormat("%s/%s/%s.data", exWebPath, exCategory, exName)); + FileCopy(TextFormat("%s/%s/%s.wasm", exBasePath, exCategory, exName), + TextFormat("%s/%s/%s.wasm", exWebPath, exCategory, exName)); + FileCopy(TextFormat("%s/%s/%s.js", exBasePath, exCategory, exName), + TextFormat("%s/%s/%s.js", exWebPath, exCategory, exName)); + } break; + case 3: // Rename + { + // Rename all required files + rename(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), + TextFormat("%s/%s/%s.c", exBasePath, exCategory, exRename)); + rename(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName), + TextFormat("%s/%s/%s.png", exBasePath, exCategory, exRename)); + + FileTextReplace(TextFormat("%s/Makefile", exBasePath), exName, exRename); + FileTextReplace(TextFormat("%s/Makefile.Web", exBasePath), exName, exRename); + FileTextReplace(TextFormat("%s/README.md", exBasePath), exName, exRename); + + rename(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName), + TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exRename)); + FileTextReplace(TextFormat("%s/../projects/VS2022/raylib.sln", exBasePath), exName, exRename); + + // Remove old web compilation + FileTextReplace(TextFormat("%s/../common/examples.js", exWebPath), exName, exRename); + remove(TextFormat("%s/%s/%s.html", exWebPath, exCategory, exName)); + remove(TextFormat("%s/%s/%s.data", exWebPath, exCategory, exName)); + remove(TextFormat("%s/%s/%s.wasm", exWebPath, exCategory, exName)); + remove(TextFormat("%s/%s/%s.js", exWebPath, exCategory, exName)); + + // Recompile example (on raylib side) + // NOTE: Tools requirements: emscripten, w64devkit + system(TextFormat("%s/../build_example_web.bat %s\%s", exBasePath, exCategory, exName)); + + // Copy results to web side + FileCopy(TextFormat("%s/%s/%s.html", exBasePath, exCategory, exName), + TextFormat("%s/%s/%s.html", exWebPath, exCategory, exName)); + FileCopy(TextFormat("%s/%s/%s.data", exBasePath, exCategory, exName), + TextFormat("%s/%s/%s.data", exWebPath, exCategory, exName)); + FileCopy(TextFormat("%s/%s/%s.wasm", exBasePath, exCategory, exName), + TextFormat("%s/%s/%s.wasm", exWebPath, exCategory, exName)); + FileCopy(TextFormat("%s/%s/%s.js", exBasePath, exCategory, exName), + TextFormat("%s/%s/%s.js", exWebPath, exCategory, exName)); + } break; + case 4: // Remove + { + // TODO: Remove and update all required files... + + } break; + default: // Help + { + // Supported commands: + // help : Provides command-line usage information + // create : Creates an empty example, from internal template + // add : Add existing example, category extracted from name + // rename : Rename an existing example + // remove : Remove an existing example + + printf("\n////////////////////////////////////////////////////////////////////////////////////////////\n"); + printf("// //\n"); + printf("// rem [raylib examples manager] - A simple command-line tool to manage raylib examples //\n"); + printf("// powered by raylib v5.6-dev //\n"); + printf("// //\n"); + printf("// Copyright (c) 2025 Ramon Santamaria (@raysan5) //\n"); + printf("// //\n"); + printf("////////////////////////////////////////////////////////////////////////////////////////////\n\n"); + + printf("USAGE:\n\n"); + printf(" > rem help|create|add|rename|remove []\n"); + + printf("\nOPTIONS:\n\n"); + printf(" help : Provides command-line usage information\n"); + printf(" create : Creates an empty example, from internal template\n"); + printf(" add : Add existing example, category extracted from name\n"); + printf(" Supported categories: core, shapes, textures, text, models\n\n"); + printf(" rename : Rename an existing example\n"); + printf(" remove : Remove an existing example\n\n"); + printf("\nEXAMPLES:\n\n"); + printf(" > rem rename core_basic_window core_cool_window\n"); + printf(" Renames and updates example to \n\n"); + printf(" > rem add shapes_custom_stars\n"); + printf(" Add and updates new example provided \n\n"); + } break; + } + + return 0; +} + +//---------------------------------------------------------------------------------- +// Module specific functions definition +//---------------------------------------------------------------------------------- +static int FileTextReplace(const char *fileName, const char *textLookUp, const char *textReplace) +{ + int result = 0; + char *fileText = NULL; + char *fileTextUpdated = { 0 }; + + fileText = LoadFileText(fileName); + fileTextUpdated = TextReplace(fileText, textLookUp, textReplace); + result = SaveFileText(fileName, fileTextUpdated); + MemFree(fileTextUpdated); + UnloadFileText(fileText); + + return result; +} + +static int FileCopy(const char *srcPath, const char *dstPath) +{ + int result = 0; + int srcDataSize = 0; + unsigned char *srcFileData = LoadFileData(srcPath, &srcDataSize); + + if ((srcFileData != NULL) && (srcDataSize > 0)) result = SaveFileData(dstPath, srcFileData, srcDataSize); + + UnloadFileData(srcFileData); + + return result; +} + +static int FileRename(const char *fileName, const char *fileRename) +{ + int result = 0; + + // TODO: Make sure to deal with paths properly for file moving if required + + if (FileExists(fileName)) rename(fileName, TextFormat("%s/%s", GetDirectoryPath(fileName), fileRename)); + + return result; +} + +static int FileRemove(const char *fileName) +{ + int result = 0; + + if (FileExists(fileName)) remove(fileName); + + return result; +} From e0b58b7eee8a794110af496e19394cdc7085fe8a Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 1 Aug 2025 01:08:31 +0200 Subject: [PATCH 130/242] Update rexm.c --- examples/rexm.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/rexm.c b/examples/rexm.c index d40c2ce1c..ef22411d5 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -269,7 +269,7 @@ int main(int argc, char *argv[]) printf("\n////////////////////////////////////////////////////////////////////////////////////////////\n"); printf("// //\n"); - printf("// rem [raylib examples manager] - A simple command-line tool to manage raylib examples //\n"); + printf("// rexm [raylib examples manager] - A simple command-line tool to manage raylib examples //\n"); printf("// powered by raylib v5.6-dev //\n"); printf("// //\n"); printf("// Copyright (c) 2025 Ramon Santamaria (@raysan5) //\n"); @@ -277,20 +277,20 @@ int main(int argc, char *argv[]) printf("////////////////////////////////////////////////////////////////////////////////////////////\n\n"); printf("USAGE:\n\n"); - printf(" > rem help|create|add|rename|remove []\n"); + printf(" > rexm help|create|add|rename|remove []\n"); printf("\nOPTIONS:\n\n"); printf(" help : Provides command-line usage information\n"); printf(" create : Creates an empty example, from internal template\n"); printf(" add : Add existing example, category extracted from name\n"); - printf(" Supported categories: core, shapes, textures, text, models\n\n"); + printf(" Supported categories: core, shapes, textures, text, models\n"); printf(" rename : Rename an existing example\n"); printf(" remove : Remove an existing example\n\n"); printf("\nEXAMPLES:\n\n"); - printf(" > rem rename core_basic_window core_cool_window\n"); - printf(" Renames and updates example to \n\n"); - printf(" > rem add shapes_custom_stars\n"); + printf(" > rexm add shapes_custom_stars\n"); printf(" Add and updates new example provided \n\n"); + printf(" > rexm rename core_basic_window core_cool_window\n"); + printf(" Renames and updates example to \n\n"); } break; } From 58e3c8604a37e0b8ed1546af3615ddeb3e6f0349 Mon Sep 17 00:00:00 2001 From: lpow100 Date: Thu, 31 Jul 2025 16:16:07 -0700 Subject: [PATCH 131/242] Added setup for the rename command (NOT TESTED) --- examples/rexm.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/examples/rexm.c b/examples/rexm.c index d40c2ce1c..21f26ceb2 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -141,12 +141,18 @@ int main(int argc, char *argv[]) else if (strcmp(argv[1], "rename") == 0) { if (argc == 2) LOG("WARNING: No filename provided to create\n"); - else if (argc == 3) LOG("WARNING: No enough arguments provided\n"); - else if (argc > 4) LOG("WARNING: Too many arguments provided\n"); + //else if (argc == 3) LOG("WARNING: No enough arguments provided\n"); All the documentation says 3 args but I don't mind being wrong + else if (argc > 3) LOG("WARNING: Too many arguments provided\n"); else { - // TODO: Register exName, exCategory and exRename - + strcpy(exName, argv[2]); + for (int index = 0; index < 32; index++) + { + if (exName[index] == '_') break; + exCategory[index] = exName[index]; + } + strcpy(exRename, argv[3]); + opCode = 3; } } From 121c996c6e51ab6e03c42543c8704876863a0f69 Mon Sep 17 00:00:00 2001 From: Eike Decker Date: Fri, 1 Aug 2025 08:40:28 +0200 Subject: [PATCH 132/242] fixing OBJ loading crash when there are no normals present --- src/rmodels.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 0dc7572f1..246c2b0a3 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -4473,9 +4473,17 @@ static Model LoadOBJ(const char *fileName) for (int i = 0; i < 3; i++) model.meshes[meshIndex].vertices[localMeshVertexCount*3 + i] = objAttributes.vertices[vertIndex*3 + i]; - for (int i = 0; i < 3; i++) model.meshes[meshIndex].normals[localMeshVertexCount*3 + i] = objAttributes.normals[normalIndex*3 + i]; - for (int i = 0; i < 2; i++) model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + i] = objAttributes.texcoords[texcordIndex*2 + i]; + if (objAttributes.normals != NULL && normalIndex != TINYOBJ_INVALID_INDEX && normalIndex >= 0) + { + for (int i = 0; i < 3; i++) model.meshes[meshIndex].normals[localMeshVertexCount*3 + i] = objAttributes.normals[normalIndex*3 + i]; + } + else + { + model.meshes[meshIndex].normals[localMeshVertexCount*3 + 0] = 0.0f; + model.meshes[meshIndex].normals[localMeshVertexCount*3 + 1] = 1.0f; + model.meshes[meshIndex].normals[localMeshVertexCount*3 + 2] = 0.0f; + } model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1] = 1.0f - model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1]; From 43bc00379c7586c1acc1097b3ddee8f44513b7ad Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 1 Aug 2025 10:26:28 +0200 Subject: [PATCH 133/242] removed trailing spaces --- src/external/rl_gputex.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index 1f0575300..906a863b1 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -585,7 +585,7 @@ int rl_save_ktx(const char *file_name, void *data, int width, int height, int fo // KTX 2.0: UInt32 supercompressionScheme - 0 (None), 1 (Crunch CRN), 2 (Zlib DEFLATE)... // KTX 2.0 defines additional header elements... } ktx_header; - + Byte[12] identifier UInt32 vkFormat UInt32 typeSize @@ -632,10 +632,10 @@ int rl_save_ktx(const char *file_name, void *data, int width, int height, int fo header.faces = 1; header.mipmap_levels = mipmaps; // If it was 0, it means mipmaps should be generated on loading (not for compressed formats) header.key_value_data_size = 0; // No extra data after the header - + // TODO: WARNING: Function dependant on rlgl library! rlGetGlTextureFormats(format, &header.gl_internal_format, &header.gl_format, &header.gl_type); // rlgl module function - + header.gl_base_internal_format = header.gl_format; // TODO: WARNING: KTX 1.1 only // NOTE: We can save into a .ktx all PixelFormats supported by raylib, including compressed formats like DXT, ETC or ASTC @@ -947,7 +947,7 @@ void get_gl_texture_formats(int format, unsigned int *gl_internal_format, unsign { // KTX 1.1 uses OpenGL formats on header info but KTX 2.0 uses Vulkan texture formats, // if this library is being improved to support KTX 2.0, it requires those formats to be provided -> View list at the end! - + /* *gl_internal_format = 0; *gl_format = 0; @@ -1023,7 +1023,7 @@ void get_gl_texture_formats(int format, unsigned int *gl_internal_format, unsign /* // OpenGL texture data formats -// NOTE: Those values can be useful for KTX 1.1 saving, +// NOTE: Those values can be useful for KTX 1.1 saving, // probably only the latest OpenGL ones, not the extensions ones // So, there is no need to include full OpenGL headers #define GL_UNSIGNED_BYTE 0x1401 From 9fa53aac6f9edaab0fe246328423e14e690e6fcf Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 1 Aug 2025 18:56:54 +0200 Subject: [PATCH 134/242] Update examples_template.c --- examples/examples_template.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/examples_template.c b/examples/examples_template.c index 6ef7214eb..2ee806b54 100644 --- a/examples/examples_template.c +++ b/examples/examples_template.c @@ -60,7 +60,7 @@ * * Example complexity rating: [★☆??] ?/4 * -* Example originally created with raylib 5.5, last time updated with raylib 5.5 +* Example originally created with raylib 5.5, last time updated with raylib 5.6-dev * * Example contributed by (@) and reviewed by Ramon Santamaria (@raysan5) * From 6b0174d0348350e328e8ce5dbda7780cf9532bae Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 1 Aug 2025 18:58:05 +0200 Subject: [PATCH 135/242] REVIEWED: Examples Makefile to locate examples list: `#EXAMPLES_LIST_*` --- examples/Makefile | 45 +++++----- examples/Makefile.Web | 198 +++++++++++++++++++++++++++++++++--------- 2 files changed, 182 insertions(+), 61 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index b5b6ef2bc..33a9d1327 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -106,6 +106,9 @@ BUILD_WEB_SHELL ?= $(RAYLIB_PATH)/src/minshell.html BUILD_WEB_HEAP_SIZE ?= 134217728 BUILD_WEB_RESOURCES ?= TRUE BUILD_WEB_RESOURCES_PATH ?= $(dir $<)resources@resources +# Use WebGL2 backend (OpenGL 3.0) +# WARNING: Requires raylib compiled with GRAPHICS_API_OPENGL_ES3 +BUILD_WEB_WEBGL2 ?= FALSE # Determine PLATFORM_OS when required ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW PLATFORM_DESKTOP_SDL PLATFORM_DESKTOP_RGFW PLATFORM_WEB PLATFORM_WEB_RGFW)) @@ -186,7 +189,7 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) endif endif ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) - # HTML5 emscripten compiler + # HTML5 emscripten compiler # WARNING: To compile to HTML5, code must be redesigned # to use emscripten.h and emscripten_set_main_loop() CC = emcc @@ -269,8 +272,8 @@ endif # NOTE: Some external/extras libraries could be required (stb, easings...) #------------------------------------------------------------------------------------------------ INCLUDE_PATHS = -I. -I$(RAYLIB_PATH)/src -I$(RAYLIB_PATH)/src/external $(EXTRA_INCLUDE_PATHS) -# Define additional directories containing required header files +# Define additional directories containing required header files ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS),BSD) INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) -I/usr/pkg/include -I/usr/X11R7/include @@ -287,11 +290,6 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) INCLUDE_PATHS += -I/usr/include/libdrm endif -# Include GLFW required for examples/others/rlgl_standalone.c -ifeq ($(USE_EXTERNAL_GLFW),FALSE) -all others: INCLUDE_PATHS += -I$(RAYLIB_PATH)/src/external/glfw/include -endif - # Define library paths containing required libs: LDFLAGS #------------------------------------------------------------------------------------------------ LDFLAGS = -L. -L$(RAYLIB_RELEASE_PATH) -L$(RAYLIB_PATH)/src @@ -340,18 +338,24 @@ ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_R # --preload-file resources # specify a resources folder for data compilation # --source-map-base # allow debugging in browser with source map # --shell-file shell.html # define a custom shell .html and output extension - LDFLAGS += -sTOTAL_MEMORY=$(BUILD_WEB_HEAP_SIZE) -sFORCE_FILESYSTEM=1 -sMINIFY_HTML=0 + LDFLAGS += -sTOTAL_MEMORY=$(BUILD_WEB_HEAP_SIZE) -sFORCE_FILESYSTEM=1 -sEXPORTED_RUNTIME_METHODS=ccall -sMINIFY_HTML=0 # Using GLFW3 library (instead of RGFW) ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) LDFLAGS += -sUSE_GLFW=3 endif - # Build using asyncify + # Build using asyncify ifeq ($(BUILD_WEB_ASYNCIFY),TRUE) LDFLAGS += -sASYNCIFY endif + # NOTE: Flags required for WebGL 2.0 (OpenGL ES 3.0) + # WARNING: Requires raylib compiled with GRAPHICS_API_OPENGL_ES3 + ifeq ($(BUILD_WEB_WEBGL2),TRUE) + LDFLAGS += -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2 + endif + # Add resources building if required ifeq ($(BUILD_WEB_RESOURCES),TRUE) LDFLAGS += --preload-file $(BUILD_WEB_RESOURCES_PATH) @@ -491,6 +495,7 @@ endif # Define source code object files required #------------------------------------------------------------------------------------------------ +#EXAMPLES_LIST_START CORE = \ core/core_2d_camera \ core/core_2d_camera_mouse_zoom \ @@ -534,6 +539,8 @@ SHAPES = \ shapes/shapes_bouncing_ball \ shapes/shapes_collision_area \ shapes/shapes_colors_palette \ + shapes/shapes_digital_clock \ + shapes/shapes_double_pendulum \ shapes/shapes_draw_circle_sector \ shapes/shapes_draw_rectangle_rounded \ shapes/shapes_draw_ring \ @@ -547,9 +554,7 @@ SHAPES = \ shapes/shapes_rectangle_advanced \ shapes/shapes_rectangle_scaling \ shapes/shapes_splines_drawing \ - shapes/shapes_top_down_lights \ - shapes/shapes_digital_clock \ - shapes/shapes_double_pendulum + shapes/shapes_top_down_lights TEXTURES = \ textures/textures_background_scrolling \ @@ -644,9 +649,9 @@ SHADERS = \ shaders/shaders_texture_outline \ shaders/shaders_texture_tiling \ shaders/shaders_texture_waves \ + shaders/shaders_vertex_displacement \ shaders/shaders_view_depth \ - shaders/shaders_write_depth \ - shaders/shaders_vertex_displacement + shaders/shaders_write_depth AUDIO = \ audio/audio_mixed_processor \ @@ -663,18 +668,16 @@ OTHERS = \ others/embedded_files_loading \ others/raylib_opengl_interop \ others/raymath_vector_angle \ - others/rlgl_compute_shader - -ifeq ($(TARGET_PLATFORM), PLATFORM_DESKTOP_GFLW) - OTHERS += others/rlgl_standalone -endif + others/rlgl_compute_shader \ + others/rlgl_standalone +#EXAMPLES_LIST_END CURRENT_MAKEFILE = $(lastword $(MAKEFILE_LIST)) # Define processes to execute #------------------------------------------------------------------------------------------------ # Default target entry -all: $(CORE) $(SHAPES) $(TEXT) $(TEXTURES) $(MODELS) $(SHADERS) $(AUDIO) $(OTHERS) +all: $(CORE) $(SHAPES) $(TEXT) $(TEXTURES) $(MODELS) $(SHADERS) $(AUDIO) core: $(CORE) shapes: $(SHAPES) @@ -683,7 +686,7 @@ text: $(TEXT) models: $(MODELS) shaders: $(SHADERS) audio: $(AUDIO) -others: $(OTHERS) + # Generic compilation pattern # NOTE: Examples must be ready for Android compilation! diff --git a/examples/Makefile.Web b/examples/Makefile.Web index ee5564599..c0969dfeb 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -1,6 +1,34 @@ #************************************************************************************************** # -# raylib makefile for Web platform +# raylib makefile for multiple platforms +# +# This file supports building raylib examples for the following platforms: +# +# > PLATFORM_DESKTOP +# - Defaults to PLATFORM_DESKTOP_GLFW +# > PLATFORM_DESKTOP_GFLW (GLFW backend): +# - Windows (Win32, Win64) +# - Linux (X11/Wayland desktop mode) +# - macOS/OSX (x64, arm64) +# - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +# > PLATFORM_DESKTOP_SDL (SDL backend): +# - Windows (Win32, Win64) +# - Linux (X11/Wayland desktop mode) +# - Others (not tested) +# > PLATFORM_DESKTOP_RGFW (RGFW backend): +# - Windows (Win32, Win64) +# - Linux (X11 desktop mode) +# - macOS/OSX (x64, arm64 (not tested)) +# - Others (not tested) +# > PLATFORM_WEB_RGFW: +# - HTML5 (WebAssembly) +# > PLATFORM_WEB: +# - HTML5 (WebAssembly) +# > PLATFORM_DRM: +# - Raspberry Pi 0-5 (DRM/KMS) +# - Linux DRM subsystem (KMS mode) +# > PLATFORM_ANDROID: +# - Android (ARM, ARM64) # # Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) # @@ -25,9 +53,20 @@ # Define required environment variables #------------------------------------------------------------------------------------------------ -# Define target platform: PLATFORM_DESKTOP, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB +# Define target platform: PLATFORM_DESKTOP, PLATFORM_DESKTOP_SDL, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB, PLATFORM_WEB_RGFW PLATFORM ?= PLATFORM_WEB +ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP_GLFW PLATFORM_DESKTOP_SDL PLATFORM_DESKTOP_RGFW)) + TARGET_PLATFORM := $(PLATFORM) + override PLATFORM = PLATFORM_DESKTOP +else + ifeq ($(PLATFORM), PLATFORM_DESKTOP) + TARGET_PLATFORM = PLATFORM_DESKTOP_GLFW + else + TARGET_PLATFORM = $(PLATFORM) + endif +endif + # Define required raylib variables PROJECT_NAME ?= raylib_examples RAYLIB_VERSION ?= 5.5.0 @@ -51,6 +90,12 @@ BUILD_MODE ?= RELEASE # Use external GLFW library instead of rglfw module USE_EXTERNAL_GLFW ?= FALSE +# PLATFORM_DESKTOP_SDL: It requires SDL library to be provided externally +# WARNING: Library is not included in raylib, it MUST be configured by users +SDL_INCLUDE_PATH ?= $(RAYLIB_SRC_PATH)/external/SDL2/include +SDL_LIBRARY_PATH ?= $(RAYLIB_SRC_PATH)/external/SDL2/lib +SDL_LIBRARIES ?= -lSDL2 -lSDL2main + # Use Wayland display server protocol on Linux desktop (by default it uses X11 windowing system) # NOTE: This variable is only used for PLATFORM_OS: LINUX USE_WAYLAND_DISPLAY ?= FALSE @@ -59,13 +104,14 @@ USE_WAYLAND_DISPLAY ?= FALSE BUILD_WEB_ASYNCIFY ?= TRUE BUILD_WEB_SHELL ?= $(RAYLIB_PATH)/src/shell.html BUILD_WEB_HEAP_SIZE ?= 134217728 - +BUILD_WEB_RESOURCES ?= TRUE +BUILD_WEB_RESOURCES_PATH ?= $(dir $<)resources@resources # Use WebGL2 backend (OpenGL 3.0) # WARNING: Requires raylib compiled with GRAPHICS_API_OPENGL_ES3 -USE_WEBGL2 ?= FALSE +BUILD_WEB_WEBGL2 ?= FALSE -# Determine PLATFORM_OS in case PLATFORM_DESKTOP or PLATFORM_WEB selected -ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_WEB PLATFORM_WEB_RGFW)) +# Determine PLATFORM_OS when required +ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW PLATFORM_DESKTOP_SDL PLATFORM_DESKTOP_RGFW PLATFORM_WEB PLATFORM_WEB_RGFW)) # No uname.exe on MinGW!, but OS=Windows_NT on Windows! # ifeq ($(UNAME),Msys) -> Windows ifeq ($(OS),Windows_NT) @@ -92,7 +138,7 @@ ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_WEB PLATFORM_WE endif endif endif -ifeq ($(PLATFORM),PLATFORM_DRM) +ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) UNAMEOS = $(shell uname) ifeq ($(UNAMEOS),Linux) PLATFORM_OS = LINUX @@ -101,7 +147,7 @@ endif # RAYLIB_PATH adjustment for LINUX platform # TODO: Do we really need this? -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS),LINUX) RAYLIB_PREFIX ?= .. RAYLIB_PATH = $(realpath $(RAYLIB_PREFIX)) @@ -109,14 +155,14 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) endif # Default path for raylib on Raspberry Pi -ifeq ($(PLATFORM),PLATFORM_DRM) +ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) RAYLIB_PATH ?= /home/pi/raylib endif # Define raylib release directory for compiled library RAYLIB_RELEASE_PATH ?= $(RAYLIB_PATH)/src -ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) +ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) ifeq ($(PLATFORM_OS),WINDOWS) # Emscripten required variables EMSDK_PATH ?= C:/raylib/emsdk @@ -132,7 +178,7 @@ endif #------------------------------------------------------------------------------------------------ CC = gcc -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS),OSX) # OSX default compiler CC = clang @@ -142,7 +188,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) CC = clang endif endif -ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) +ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) # HTML5 emscripten compiler # WARNING: To compile to HTML5, code must be redesigned # to use emscripten.h and emscripten_set_main_loop() @@ -153,15 +199,15 @@ endif #------------------------------------------------------------------------------------------------ MAKE ?= make -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS),WINDOWS) MAKE = mingw32-make endif endif -ifeq ($(PLATFORM),PLATFORM_ANDROID) +ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) MAKE = mingw32-make endif -ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) +ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) ifeq ($(OS),Windows_NT) MAKE = mingw32-make else @@ -190,14 +236,14 @@ CFLAGS = -Wall -std=c99 -D_DEFAULT_SOURCE -Wno-missing-braces -Wunused-result ifeq ($(BUILD_MODE),DEBUG) CFLAGS += -g -D_DEBUG else - ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) + ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) ifeq ($(BUILD_WEB_ASYNCIFY),TRUE) CFLAGS += -O3 else CFLAGS += -Os endif else - CFLAGS += -s -O2 + CFLAGS += -O2 endif endif @@ -207,7 +253,7 @@ endif # -Wstrict-prototypes warn if a function is declared or defined without specifying the argument types # -Werror=implicit-function-declaration catch function calls without prior declaration #CFLAGS += -Wextra -Wmissing-prototypes -Wstrict-prototypes -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS),LINUX) ifeq ($(RAYLIB_LIBTYPE),STATIC) CFLAGS += -D_DEFAULT_SOURCE @@ -218,25 +264,29 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) endif endif endif -ifeq ($(PLATFORM),PLATFORM_DRM) +ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) CFLAGS += -std=gnu99 -DEGL_NO_X11 endif # Define include paths for required headers: INCLUDE_PATHS # NOTE: Some external/extras libraries could be required (stb, easings...) #------------------------------------------------------------------------------------------------ -INCLUDE_PATHS = -I. -I$(RAYLIB_PATH)/src -I$(RAYLIB_PATH)/src/external +INCLUDE_PATHS = -I. -I$(RAYLIB_PATH)/src -I$(RAYLIB_PATH)/src/external $(EXTRA_INCLUDE_PATHS) # Define additional directories containing required header files -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS),BSD) - INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) + INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) -I/usr/pkg/include -I/usr/X11R7/include endif ifeq ($(PLATFORM_OS),LINUX) INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) endif endif -ifeq ($(PLATFORM),PLATFORM_DRM) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL) + INCLUDE_PATHS += -I$(SDL_INCLUDE_PATH) +endif +ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) + INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) INCLUDE_PATHS += -I/usr/include/libdrm endif @@ -244,7 +294,7 @@ endif #------------------------------------------------------------------------------------------------ LDFLAGS = -L. -L$(RAYLIB_RELEASE_PATH) -L$(RAYLIB_PATH)/src -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS),WINDOWS) # NOTE: The resource .rc file contains windows executable icon and properties LDFLAGS += $(RAYLIB_PATH)/src/raylib.rc.data @@ -260,7 +310,18 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDFLAGS += -Lsrc -L$(RAYLIB_LIB_PATH) endif endif -ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL) + ifeq ($(PLATFORM_OS),WINDOWS) + # NOTE: The resource .rc file contains windows executable icon and properties + LDFLAGS += $(RAYLIB_PATH)/src/raylib.rc.data + # -Wl,--subsystem,windows hides the console window + ifeq ($(BUILD_MODE), RELEASE) + LDFLAGS += -Wl,--subsystem,windows + endif + endif + LDFLAGS += -L$(SDL_LIBRARY_PATH) +endif +ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) # -Os # size optimization # -O2 # optimization level 2, if used, also set --memory-init-file 0 # -sUSE_GLFW=3 # Use glfw3 library (context/input management) @@ -277,10 +338,10 @@ ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) # --preload-file resources # specify a resources folder for data compilation # --source-map-base # allow debugging in browser with source map # --shell-file shell.html # define a custom shell .html and output extension - LDFLAGS += -sTOTAL_MEMORY=$(BUILD_WEB_HEAP_SIZE) -sFORCE_FILESYSTEM=1 -sEXPORTED_RUNTIME_METHODS=ccall -sMINIFY_HTML=0 + LDFLAGS += -sTOTAL_MEMORY=$(BUILD_WEB_HEAP_SIZE) -sFORCE_FILESYSTEM=1 -sEXPORTED_RUNTIME_METHODS=ccall -sMINIFY_HTML=0 # Using GLFW3 library (instead of RGFW) - ifeq ($(PLATFORM),PLATFORM_WEB) + ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) LDFLAGS += -sUSE_GLFW=3 endif @@ -291,9 +352,14 @@ ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) # NOTE: Flags required for WebGL 2.0 (OpenGL ES 3.0) # WARNING: Requires raylib compiled with GRAPHICS_API_OPENGL_ES3 - ifeq ($(USE_WEBGL2),TRUE) + ifeq ($(BUILD_WEB_WEBGL2),TRUE) LDFLAGS += -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2 endif + + # Add resources building if required + ifeq ($(BUILD_WEB_RESOURCES),TRUE) + LDFLAGS += --preload-file $(BUILD_WEB_RESOURCES_PATH) + endif # Add debug mode flags if required ifeq ($(BUILD_MODE),DEBUG) @@ -314,7 +380,7 @@ endif # Define libraries required on linking: LDLIBS # NOTE: To link libraries (lib.so or lib.a), use -l #------------------------------------------------------------------------------------------------ -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS),WINDOWS) # Libraries for Windows desktop compilation # NOTE: WinMM library required to set high-res timer resolution @@ -361,18 +427,75 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDLIBS += -lglfw endif endif -ifeq ($(PLATFORM),PLATFORM_DRM) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL) + ifeq ($(PLATFORM_OS),WINDOWS) + # Libraries for Windows desktop compilation + LDLIBS = -lraylib $(SDL_LIBRARIES) -lopengl32 -lgdi32 + endif + ifeq ($(PLATFORM_OS),LINUX) + # Libraries for Debian GNU/Linux desktop compiling + # NOTE: Required packages: libegl1-mesa-dev + LDLIBS = -lraylib $(SDL_LIBRARIES) -lGL -lm -lpthread -ldl -lrt + + # On X11 requires also below libraries + LDLIBS += -lX11 + # NOTE: It seems additional libraries are not required any more, latest GLFW just dlopen them + #LDLIBS += -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor + + # On Wayland windowing system, additional libraries requires + ifeq ($(USE_WAYLAND_DISPLAY),TRUE) + LDLIBS += -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon + endif + # Explicit link to libc + ifeq ($(RAYLIB_LIBTYPE),SHARED) + LDLIBS += -lc + endif + + # NOTE: On ARM 32bit arch, miniaudio requires atomics library + LDLIBS += -latomic + endif +endif +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW) + ifeq ($(PLATFORM_OS),WINDOWS) + # Libraries for Windows desktop compilation + LDFLAGS += -L..\src + LDLIBS = -lraylib -lgdi32 -lwinmm -lopengl32 + endif + ifeq ($(PLATFORM_OS),LINUX) + # Libraries for Debian GNU/Linux desktop compipling + # NOTE: Required packages: libegl1-mesa-dev + LDFLAGS += -L../src + LDLIBS = -lraylib -lGL -lX11 -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor -lm -lpthread -ldl -lrt + + # Explicit link to libc + ifeq ($(RAYLIB_LIBTYPE),SHARED) + LDLIBS += -lc + endif + + # NOTE: On ARM 32bit arch, miniaudio requires atomics library + LDLIBS += -latomic + endif + ifeq ($(PLATFORM_OS),OSX) + # Libraries for Debian GNU/Linux desktop compiling + # NOTE: Required packages: libegl1-mesa-dev + LDFLAGS += -L../src + LDLIBS = -lraylib -lm + LDLIBS += -framework Foundation -framework AppKit -framework IOKit -framework OpenGL -framework CoreVideo + endif +endif +ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) # Libraries for DRM compiling # NOTE: Required packages: libasound2-dev (ALSA) LDLIBS = -lraylib -lGLESv2 -lEGL -lpthread -lrt -lm -lgbm -ldrm -ldl -latomic endif -ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) +ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) # Libraries for web (HTML5) compiling LDLIBS = $(RAYLIB_RELEASE_PATH)/libraylib.web.a endif # Define source code object files required #------------------------------------------------------------------------------------------------ +#EXAMPLES_LIST_START CORE = \ core/core_2d_camera \ core/core_2d_camera_mouse_zoom \ @@ -416,6 +539,8 @@ SHAPES = \ shapes/shapes_bouncing_ball \ shapes/shapes_collision_area \ shapes/shapes_colors_palette \ + shapes/shapes_digital_clock \ + shapes/shapes_double_pendulum \ shapes/shapes_draw_circle_sector \ shapes/shapes_draw_rectangle_rounded \ shapes/shapes_draw_ring \ @@ -429,9 +554,7 @@ SHAPES = \ shapes/shapes_rectangle_advanced \ shapes/shapes_rectangle_scaling \ shapes/shapes_splines_drawing \ - shapes/shapes_top_down_lights \ - shapes/shapes_digital_clock \ - shapes/shapes_double_pendulum + shapes/shapes_top_down_lights TEXTURES = \ textures/textures_background_scrolling \ @@ -547,6 +670,7 @@ OTHERS = \ others/raymath_vector_angle \ others/rlgl_compute_shader \ others/rlgl_standalone +#EXAMPLES_LIST_END CURRENT_MAKEFILE = $(lastword $(MAKEFILE_LIST)) @@ -676,7 +800,6 @@ core/core_window_should_close: core/core_window_should_close.c core/core_world_screen: core/core_world_screen.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - # Compile SHAPES examples shapes/shapes_basic_shapes: shapes/shapes_basic_shapes.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -735,7 +858,6 @@ shapes/shapes_rectangle_advanced: shapes/shapes_rectangle_advanced.c shapes/shapes_double_pendulum: shapes/shapes_double_pendulum.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - # Compile TEXTURES examples textures/textures_background_scrolling: textures/textures_background_scrolling.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -846,7 +968,6 @@ textures/textures_to_image: textures/textures_to_image.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file textures/resources/raylib_logo.png@resources/raylib_logo.png - # Compile TEXT examples text/text_codepoints_loading: text/text_codepoints_loading.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -1014,7 +1135,6 @@ models/models_yaw_pitch_roll: models/models_yaw_pitch_roll.c --preload-file models/resources/models/obj/plane.obj@resources/models/obj/plane.obj \ --preload-file models/resources/models/obj/plane_diffuse.png@resources/models/obj/plane_diffuse.png - # Compile SHADER examples shaders/shaders_basic_lighting: shaders/shaders_basic_lighting.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -1172,7 +1292,6 @@ shaders/shaders_vertex_displacement: shaders/shaders_vertex_displacement.c --preload-file shaders/resources/shaders/glsl100/vertex_displacement.fs@resources/shaders/glsl100/vertex_displacement.fs \ --preload-file shaders/resources/shaders/glsl330/vertex_displacement.fs@resources/shaders/glsl330/vertex_displacement.fs - # Compile AUDIO examples audio/audio_mixed_processor: audio/audio_mixed_processor.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 \ @@ -1203,7 +1322,6 @@ audio/audio_stream_effects: audio/audio_stream_effects.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 \ --preload-file audio/resources/country.mp3@resources/country.mp3 - # Compile OTHERS examples others/easings_testbed: others/easings_testbed.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) From 10079e4e65fadcea33aa10d1b701b690c838994d Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 1 Aug 2025 18:58:24 +0200 Subject: [PATCH 136/242] Reviewed minor formating issues --- examples/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/README.md b/examples/README.md index e075facbe..666fda04b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -216,12 +216,12 @@ Examples using raylib audio functionality, including sound/music loading and pla | 148 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | | 149 | [audio_mixed_processor](audio/audio_mixed_processor.c) | audio_mixed_processor | ⭐️⭐️⭐️⭐️ | 4.2 | 4.2 | [hkc](https://github.com/hatkidchan) | | 150 | [audio_stream_effects](audio/audio_stream_effects.c) | audio_stream_effects | ⭐️⭐️⭐️⭐️ | 4.2 | 5.0 | [Ray](https://github.com/raysan5) | -| 151 [audio_sound_multi](audio/audio_sound_multi.c) | audio_sound_multi | ⭐️⭐️☆☆ | 4.6 | 4.6 | [Jeffery Myers](https://github.com/JeffM2501) | +| 151 | [audio_sound_multi](audio/audio_sound_multi.c) | audio_sound_multi | ⭐️⭐️☆☆ | 4.6 | 4.6 | [Jeffery Myers](https://github.com/JeffM2501) | | 152 | [audio_sound_positioning](audio/audio_sound_positioning.c) | audio_sound_positioning | ⭐️⭐️☆☆ | 5.5 | 5.5 | [Le Juez Victor](https://github.com/Bigfoot71) | ### category: others -Ex151amples showing raylib misc functionality that does not fit in other categories, like standalone modules usage or examples integrating external libraries. +Examples showing raylib misc functionality that does not fit in other categories, like standalone modules usage or examples integrating external libraries. | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| From 234f4b2b10f3e405c591a88035f86a4cbae993cd Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 1 Aug 2025 19:38:02 +0200 Subject: [PATCH 137/242] Update raylib.h --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 0ea0df316..6b8b4acf3 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1514,7 +1514,7 @@ RLAPI const char *TextSubtext(const char *text, int position, int length); RLAPI char *TextReplace(const char *text, const char *replace, const char *by); // Replace text string (WARNING: memory must be freed!) RLAPI char *TextInsert(const char *text, const char *insert, int position); // Insert text in a position (WARNING: memory must be freed!) RLAPI char *TextJoin(char **textList, int count, const char *delimiter); // Join text strings with delimiter -RLAPI char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings +RLAPI char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings, using MAX_TEXTSPLIT_COUNT static strings RLAPI void TextAppend(char *text, const char *append, int *position); // Append text at specific position and move cursor! RLAPI int TextFindIndex(const char *text, const char *find); // Find first text occurrence within a string RLAPI char *TextToUpper(const char *text); // Get upper case version of provided string From 9d07be6bded1912f098d94b7741a8d4cb68fc082 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 1 Aug 2025 17:38:20 +0000 Subject: [PATCH 138/242] Update raylib_api.* by CI --- parser/output/raylib_api.json | 2 +- parser/output/raylib_api.lua | 2 +- parser/output/raylib_api.txt | 2 +- parser/output/raylib_api.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index 9d85c3135..cf32ce524 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -9739,7 +9739,7 @@ }, { "name": "TextSplit", - "description": "Split text into multiple strings", + "description": "Split text into multiple strings, using MAX_TEXTSPLIT_COUNT static strings", "returnType": "char **", "params": [ { diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index 985a62c8b..cb6cf9db3 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -6922,7 +6922,7 @@ return { }, { name = "TextSplit", - description = "Split text into multiple strings", + description = "Split text into multiple strings, using MAX_TEXTSPLIT_COUNT static strings", returnType = "char **", params = { {type = "const char *", name = "text"}, diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index 2f9268cbf..9f8ab8ef5 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -3728,7 +3728,7 @@ Function 432: TextJoin() (3 input parameters) Function 433: TextSplit() (3 input parameters) Name: TextSplit Return type: char ** - Description: Split text into multiple strings + Description: Split text into multiple strings, using MAX_TEXTSPLIT_COUNT static strings Param[1]: text (type: const char *) Param[2]: delimiter (type: char) Param[3]: count (type: int *) diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index 8125824e7..f3ebb74be 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -2470,7 +2470,7 @@ - + From a872de82edf8f070810b3157fe87e8ed154bf17a Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 1 Aug 2025 19:39:30 +0200 Subject: [PATCH 139/242] Create `examples_list`, to be used by `rexm` or other tools for reference NOTE: This file should be the source of truth for raylib examples collection, everything will be generated using this file as primary reference. --- examples/examples_list.txt | 168 +++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 examples/examples_list.txt diff --git a/examples/examples_list.txt b/examples/examples_list.txt new file mode 100644 index 000000000..f25097a56 --- /dev/null +++ b/examples/examples_list.txt @@ -0,0 +1,168 @@ +# +# raylib examples list used to generate/update collection +# examples must be provided as: ;;;;;""; +# +# This list is used as the main reference by [rexm] tool for examples collection validation and management +# New examples must be added to this list and any possible rename must be made on this list first +# +# WARNING: List is not ordered by example name but by the display order on web +# +core;core_basic_window;⭐️☆☆☆;1.0;1.0;"Ray";@raysan5 +core;core_input_keys;⭐️☆☆☆;1.0;1.0;"Ray";@raysan5 +core;core_input_mouse;⭐️☆☆☆;1.0;5.5;"Ray";@raysan5 +core;core_input_mouse_wheel;⭐️☆☆☆;1.1;1.3;"Ray";@raysan5 +core;core_input_gamepad;⭐️☆☆☆;1.1;4.2;"Ray";@raysan5 +core;core_input_multitouch;⭐️☆☆☆;2.1;2.5;"Berni";@Berni8k +core;core_input_gestures;⭐️⭐️☆☆;1.4;4.2;"Ray";@raysan5 +core;core_input_virtual_controls;⭐️⭐️☆☆;5.0;5.0;"oblerion";@oblerion +core;core_2d_camera;⭐️⭐️☆☆;1.5;3.0;"Ray";@raysan5 +core;core_2d_camera_mouse_zoom;⭐️⭐️☆☆;4.2;4.2;"Jeffery Myers";@JeffM2501 +core;core_2d_camera_platformer;⭐️⭐️⭐️☆;2.5;3.0;"arvyy";@arvyy +core;core_2d_camera_split_screen;⭐️⭐️⭐️⭐️;4.5;4.5;"Gabriel dos Santos Sanches";@gabrielssanches +core;core_3d_camera_mode;⭐️☆☆☆;1.0;1.0;"Ray";@raysan5 +core;core_3d_camera_free;⭐️☆☆☆;1.3;1.3;"Ray";@raysan5 +core;core_3d_camera_first_person;⭐️⭐️☆☆;1.3;1.3;"Ray";@raysan5 +core;core_3d_camera_split_screen;⭐️⭐️⭐️☆;3.7;4.0;"Jeffery Myers";@JeffM2501 +core;core_3d_picking;⭐️⭐️☆☆;1.3;4.0;"Ray";@raysan5 +core;core_world_screen;⭐️⭐️☆☆;1.3;1.4;"Ray";@raysan5 +core;core_custom_logging;⭐️⭐️⭐️☆;2.5;2.5;"Pablo Marcos Oltra";@pamarcos +core;core_window_flags;⭐️⭐️⭐️☆;3.5;3.5;"Ray";@raysan5 +core;core_window_letterbox;⭐️⭐️☆☆;2.5;4.0;"Anata";@anatagawa +core;core_window_should_close;⭐️☆☆☆;4.2;4.2;"Ray";@raysan5 +core;core_drop_files;⭐️⭐️☆☆;1.3;4.2;"Ray";@raysan5 +core;core_random_values;⭐️☆☆☆;1.1;1.1;"Ray";@raysan5 +core;core_storage_values;⭐️⭐️☆☆;1.4;4.2;"Ray";@raysan5 +core;core_vr_simulator;⭐️⭐️⭐️☆;2.5;4.0;"Ray";@raysan5 +core;core_loading_thread;⭐️⭐️⭐️☆;2.5;3.0;"Ray";@raysan5 +core;core_scissor_test;⭐️☆☆☆;2.5;3.0;"Chris Dill";@MysteriousSpace +core;core_basic_screen_manager;⭐️☆☆☆;4.0;4.0;"Ray";@raysan5 +core;core_custom_frame_control;⭐️⭐️⭐️⭐️;4.0;4.0;"Ray";@raysan5 +core;core_smooth_pixelperfect;⭐️⭐️⭐️☆;3.7;4.0;"Giancamillo Alessandroni";@NotManyIdeasDev +core;core_random_sequence;⭐️☆☆☆;5.0;5.0;"Dalton Overmyer";@REDl3east +core;core_basic_window_web;⭐️☆☆☆;1.3;1.3;"Ray";@raysan5 +core;core_input_gestures_web;⭐️⭐️☆☆;4.6-dev;4.6-dev;"ubkp";@ubkp +core;core_automation_events;⭐️⭐️⭐️☆;5.0;5.0;"Ray";@raysan5 +core;core_high_dpi;⭐️☆☆☆;5.0;5.0;"Jonathan Marler";@marler8997 +shapes;shapes_basic_shapes;⭐️☆☆☆;1.0;4.2;"Ray";@raysan5 +shapes;shapes_bouncing_ball;⭐️☆☆☆;2.5;2.5;"Ray";@raysan5 +shapes;shapes_colors_palette;⭐️⭐️☆☆;1.0;2.5;"Ray";@raysan5 +shapes;shapes_logo_raylib;⭐️☆☆☆;1.0;1.0;"Ray";@raysan5 +shapes;shapes_logo_raylib_anim;⭐️⭐️☆☆;2.5;4.0;"Ray";@raysan5 +shapes;shapes_rectangle_scaling;⭐️⭐️☆☆;2.5;2.5;"Vlad Adrian";@demizdor +shapes;shapes_lines_bezier;⭐️☆☆☆;1.7;1.7;"Ray";@raysan5 +shapes;shapes_collision_area;⭐️⭐️☆☆;2.5;2.5;"Ray";@raysan5 +shapes;shapes_following_eyes;⭐️⭐️☆☆;2.5;2.5;"Ray";@raysan5 +shapes;shapes_easings_ball_anim;⭐️⭐️☆☆;2.5;2.5;"Ray";@raysan5 +shapes;shapes_easings_box_anim;⭐️⭐️☆☆;2.5;2.5;"Ray";@raysan5 +shapes;shapes_easings_rectangle_array;⭐️⭐️⭐️☆;2.0;2.5;"Ray";@raysan5 +shapes;shapes_draw_ring;⭐️⭐️⭐️☆;2.5;2.5;"Vlad Adrian";@demizdor +shapes;shapes_draw_circle_sector;⭐️⭐️⭐️☆;2.5;2.5;"Vlad Adrian";@demizdor +shapes;shapes_draw_rectangle_rounded;⭐️⭐️⭐️☆;2.5;2.5;"Vlad Adrian";@demizdor +shapes;shapes_top_down_lights;⭐️⭐️⭐️⭐️;4.2;4.2;"Jeffery Myers";@JeffM2501 +shapes;shapes_rectangle_advanced;⭐️⭐️⭐️⭐️;5.5;5.5;"Everton Jr.";@evertonse +shapes;shapes_splines_drawing;⭐️⭐️⭐️☆;5.0;5.0;"Ray";@raysan5 +shapes;shapes_digital_clock;⭐️⭐️☆☆;5.5;5.5;"Hamza RAHAL";@rhmz-rhl +shapes;shapes_double_pendulum;⭐️⭐️☆☆;5.5;5.5;"JoeCheong";@Joecheong2006 +textures;textures_logo_raylib;⭐️☆☆☆;1.0;1.0;"Ray";@raysan5 +textures;textures_srcrec_dstrec;⭐️⭐️⭐️☆;1.3;1.3;"Ray";@raysan5 +textures;textures_image_drawing;⭐️⭐️☆☆;1.4;1.4;"Ray";@raysan5 +textures;textures_image_generation;⭐️⭐️☆☆;1.8;1.8;"Wilhem Barbier";@nounoursheureux +textures;textures_image_loading;⭐️☆☆☆;1.3;1.3;"Ray";@raysan5 +textures;textures_image_processing;⭐️⭐️⭐️☆;1.4;3.5;"Ray";@raysan5 +textures;textures_image_text;⭐️⭐️☆☆;1.8;4.0;"Ray";@raysan5 +textures;textures_to_image;⭐️☆☆☆;1.3;4.0;"Ray";@raysan5 +textures;textures_raw_data;⭐️⭐️⭐️☆;1.3;3.5;"Ray";@raysan5 +textures;textures_particles_blending;⭐️☆☆☆;1.7;3.5;"Ray";@raysan5 +textures;textures_npatch_drawing;⭐️⭐️⭐️☆;2.0;2.5;"Jorge A. Gomes";@overdev +textures;textures_background_scrolling;⭐️☆☆☆;2.0;2.5;"Ray";@raysan5 +textures;textures_sprite_anim;⭐️⭐️☆☆;1.3;1.3;"Ray";@raysan5 +textures;textures_sprite_button;⭐️⭐️☆☆;2.5;2.5;"Ray";@raysan5 +textures;textures_sprite_explosion;⭐️⭐️☆☆;2.5;3.5;"Ray";@raysan5 +textures;textures_bunnymark;⭐️⭐️⭐️☆;1.6;2.5;"Ray";@raysan5 +textures;textures_mouse_painting;⭐️⭐️⭐️☆;3.0;3.0;"Chris Dill";@MysteriousSpace +textures;textures_blend_modes;⭐️☆☆☆;3.5;3.5;"Karlo Licudine";@accidentalrebel +textures;textures_draw_tiled;⭐️⭐️⭐️☆;3.0;4.2;"Vlad Adrian";@demizdor +textures;textures_polygon;⭐️☆☆☆;3.7;3.7;"Chris Camacho";@chriscamacho +textures;textures_fog_of_war;⭐️⭐️⭐️☆;4.2;4.2;"Ray";@raysan5 +textures;textures_gif_player;⭐️⭐️⭐️☆;4.2;4.2;"Ray";@raysan5 +textures;textures_image_kernel;⭐️⭐️⭐️⭐️;1.3;1.3;"Karim Salem";@kimo-s +textures;textures_image_channel;⭐️⭐️☆☆;5.1-dev;5.1-dev;"Bruno Cabral";@brccabral +textures;textures_image_rotate;⭐️⭐️☆☆;1.0;1.0;"Ray";@raysan5 +textures;textures_textured_curve;⭐️⭐️⭐️☆;4.5;4.5;"Jeffery Myers";@JeffM2501 +text;text_raylib_fonts;⭐️☆☆☆;1.7;3.7;"Ray";@raysan5 +text;text_font_spritefont;⭐️☆☆☆;1.0;1.0;"Ray";@raysan5 +text;text_font_filters;⭐️⭐️☆☆;1.3;4.2;"Ray";@raysan5 +text;text_font_loading;⭐️☆☆☆;1.4;3.0;"Ray";@raysan5 +text;text_font_sdf;⭐️⭐️⭐️☆;1.3;4.0;"Ray";@raysan5 +text;text_format_text;⭐️☆☆☆;1.1;3.0;"Ray";@raysan5 +text;text_input_box;⭐️⭐️☆☆;1.7;3.5;"Ray";@raysan5 +text;text_writing_anim;⭐️⭐️☆☆;1.4;1.4;"Ray";@raysan5 +text;text_rectangle_bounds;⭐️⭐️⭐️⭐️;2.5;4.0;"Vlad Adrian";@demizdor +text;text_unicode;⭐️⭐️⭐️⭐️;2.5;4.0;"Vlad Adrian";@demizdor +text;text_draw_3d;⭐️⭐️⭐️⭐️;3.5;4.0;"Vlad Adrian";@demizdor +text;text_codepoints_loading;⭐️⭐️⭐️☆;4.2;4.2;"Ray";@raysan5 +models;models_animation;⭐️⭐️☆☆;2.5;3.5;"Culacant";@culacant +models;models_billboard;⭐️⭐️⭐️☆;1.3;3.5;"Ray";@raysan5 +models;models_box_collisions;⭐️☆☆☆;1.3;3.5;"Ray";@raysan5 +models;models_cubicmap;⭐️⭐️☆☆;1.8;3.5;"Ray";@raysan5 +models;models_first_person_maze;⭐️⭐️☆☆;2.5;3.5;"Ray";@raysan5 +models;models_geometric_shapes;⭐️☆☆☆;1.0;3.5;"Ray";@raysan5 +models;models_mesh_generation;⭐️⭐️☆☆;1.8;4.0;"Ray";@raysan5 +models;models_mesh_picking;⭐️⭐️⭐️☆;1.7;4.0;"Joel Davis";@joeld42 +models;models_loading;⭐️☆☆☆;2.0;4.2;"Ray";@raysan5 +models;models_loading_gltf;⭐️☆☆☆;3.7;4.2;"Ray";@raysan5 +models;models_loading_vox;⭐️☆☆☆;4.0;4.0;"Johann Nadalutti";@procfxgen +models;models_loading_m3d;⭐️⭐️☆☆;4.5;4.5;"bzt";@model3d +models;models_orthographic_projection;⭐️☆☆☆;2.0;3.7;"Max Danielsson";@autious +models;models_point_rendering;⭐️⭐️⭐️☆;5.0;5.0;"Reese Gallagher";@satchelfrost +models;models_rlgl_solar_system;⭐️⭐️⭐️⭐️;2.5;4.0;"Ray";@raysan5 +models;models_yaw_pitch_roll;⭐️⭐️☆☆;1.8;4.0;"Berni";@Berni8k +models;models_waving_cubes;⭐️⭐️⭐️☆;2.5;3.7;"Codecat";@codecat +models;models_heightmap;⭐️☆☆☆;1.8;3.5;"Ray";@raysan5 +models;models_skybox;⭐️⭐️☆☆;1.8;4.0;"Ray";@raysan5 +models;models_draw_cube_texture;⭐️⭐️☆☆;4.5;4.5;"Ray";@raysan5 +models;models_gpu_skinning;⭐️⭐️⭐️☆;4.5;4.5;"Daniel Holden";@orangeduck +models;models_bone_socket;⭐️⭐️⭐️⭐️;4.5;4.5;"iP";@ipzaur +models;models_tesseract_view;⭐️⭐️☆☆;5.6-dev;5.6-dev;"Timothy van der Valk";@arceryz +shaders;shaders_basic_lighting;⭐️⭐️⭐️⭐️;3.0;4.2;"Chris Camacho";@chriscamacho +shaders;shaders_model_shader;⭐️⭐️☆☆;1.3;3.7;"Ray";@raysan5 +shaders;shaders_shapes_textures;⭐️⭐️☆☆;1.7;3.7;"Ray";@raysan5 +shaders;shaders_custom_uniform;⭐️⭐️☆☆;1.3;4.0;"Ray";@raysan5 +shaders;shaders_postprocessing;⭐️⭐️⭐️☆;1.3;4.0;"Ray";@raysan5 +shaders;shaders_palette_switch;⭐️⭐️⭐️☆;2.5;3.7;"Marco Lizza";@MarcoLizza +shaders;shaders_raymarching;⭐️⭐️⭐️⭐️;2.0;4.2;"Ray";@raysan5 +shaders;shaders_texture_drawing;⭐️⭐️☆☆;2.0;3.7;"Michał Ciesielski";@ciessielski +shaders;shaders_texture_outline;⭐️⭐️⭐️☆;4.0;4.0;"Samuel Skiff";@GoldenThumbs +shaders;shaders_texture_waves;⭐️⭐️☆☆;2.5;3.7;"Anata";@anatagawa +shaders;shaders_julia_set;⭐️⭐️⭐️☆;2.5;4.0;"Josh Colclough";@joshcol9232 +shaders;shaders_eratosthenes;⭐️⭐️⭐️☆;2.5;4.0;"ProfJski";@ProfJski +shaders;shaders_fog;⭐️⭐️⭐️☆;2.5;3.7;"Chris Camacho";@chriscamacho +shaders;shaders_simple_mask;⭐️⭐️☆☆;2.5;3.7;"Chris Camacho";@chriscamacho +shaders;shaders_hot_reloading;⭐️⭐️⭐️☆;3.0;3.5;"Ray";@raysan5 +shaders;shaders_mesh_instancing;⭐️⭐️⭐️⭐️;3.7;4.2;"seanpringle";@seanpringle +shaders;shaders_multi_sample2d;⭐️⭐️☆☆;3.5;3.5;"Ray";@raysan5 +shaders;shaders_spotlight;⭐️⭐️☆☆;2.5;3.7;"Chris Camacho";@chriscamacho +shaders;shaders_deferred_render;⭐️⭐️⭐️⭐️;4.5;4.5;"Justin Andreas Lacoste";@27justin +shaders;shaders_hybrid_render;⭐️⭐️⭐️⭐️;4.2;4.2;"Buğra Alptekin Sarı";@BugraAlptekinSari +shaders;shaders_texture_tiling;⭐️⭐️☆☆;4.5;4.5;"Luis Almeida";@luis605 +shaders;shaders_shadowmap;⭐️⭐️⭐️⭐️;5.0;5.0;"TheManTheMythTheGameDev";@TheManTheMythTheGameDev +shaders;shaders_vertex_displacement;⭐️⭐️⭐️☆;5.0;4.5;"Alex ZH";@ZzzhHe +shaders;shaders_write_depth;⭐️⭐️☆☆;4.2;4.2;"Buğra Alptekin Sarı";@BugraAlptekinSari +shaders;shaders_basic_pbr;⭐️⭐️⭐️⭐️;5.0;5.1-dev;"Afan OLOVCIC";@_DevDad +shaders;shaders_lightmap;⭐️⭐️⭐️☆;4.5;4.5;"Jussi Viitala";@nullstare +shaders;shaders_rounded_rectangle;⭐️⭐️⭐️☆;5.5;5.5;"Anstro Pleuton";@anstropleuton +shaders;shaders_view_depth;⭐️⭐️⭐️☆;5.6-dev;5.6-dev;"Luís Almeida";@luis605 +audio;audio_module_playing;⭐️☆☆☆;1.5;3.5;"Ray";@raysan5 +audio;audio_music_stream;⭐️☆☆☆;1.3;4.2;"Ray";@raysan5 +audio;audio_raw_stream;⭐️⭐️⭐️☆;1.6;4.2;"Ray";@raysan5 +audio;audio_sound_loading;⭐️☆☆☆;1.1;3.5;"Ray";@raysan5 +audio;audio_mixed_processor;⭐️⭐️⭐️⭐️;4.2;4.2;"hkc";@hatkidchan +audio;audio_stream_effects;⭐️⭐️⭐️⭐️;4.2;5.0;"Ray";@raysan5 +audio;audio_sound_multi;⭐️⭐️☆☆;4.6;4.6;"Jeffery Myers";@JeffM2501 +audio;audio_sound_positioning;⭐️⭐️☆☆;5.5;5.5;"Le Juez Victor";@Bigfoot71 +others;rlgl_standalone;⭐️⭐️⭐️⭐️;1.6;4.0;"Ray";@raysan5 +others;rlgl_compute_shader;⭐️⭐️⭐️⭐️;4.0;4.0;"Teddy Astie";@tsnake41 +others;easings_testbed;⭐️⭐️⭐️☆;2.5;3.0;"Juan Miguel López";@flashback-fx +others;raylib_opengl_interop;⭐️⭐️⭐️⭐️;3.8;4.0;"Stephan Soller";@arkanis +others;embedded_files_loading;⭐️⭐️☆☆;3.0;3.5;"Kristian Holmgren";@defutura +others;raymath_vector_angle;⭐️⭐️☆☆;1.0;4.6;"Ray";@raysan5 From 947783819381ccb48caeb477b7d21e013bba16ea Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 1 Aug 2025 19:40:44 +0200 Subject: [PATCH 140/242] ADDED: Some useful functions for examples info loading, using `examples_list` --- examples/rexm.c | 294 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 240 insertions(+), 54 deletions(-) diff --git a/examples/rexm.c b/examples/rexm.c index 8440188c9..6a1c27826 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -7,6 +7,7 @@ * - add * - rename * - remove +* - validate * * Files involved in the processes: * - raylib/examples//_example_name.c @@ -60,22 +61,50 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- +// raylib example info struct +typedef struct { + char category[16]; + char name[64]; + char stars; + float verCreated; + float verUpdated; + char author[64]; + char authorGitHub[32]; +} rlExampleInfo; + // Example management operations typedef enum { - OP_NONE = 0, - OP_CREATE = 1, - OP_ADD = 2, - OP_RENAME = 3, - OP_REMOVE = 4 -} ExampleOperation; + OP_NONE = 0, // No process to do + OP_CREATE = 1, // Create new example, using default template + OP_ADD = 2, // Add existing examples (hopefully following template) + OP_RENAME = 3, // Rename existing example + OP_REMOVE = 4, // Remove existing example + OP_VALIDATE = 5, // Validate examples, using [examples_list.txt] as main source by default +} rlExampleOperation; //---------------------------------------------------------------------------------- // Module specific functions declaration //---------------------------------------------------------------------------------- static int FileTextReplace(const char *fileName, const char *textLookUp, const char *textReplace); static int FileCopy(const char *srcPath, const char *dstPath); -static int FileRename(const char *fileName, const char *fileRename); // TODO: Implement, make sure to deal with paths moving -static int FileRemove(const char *fileName); // TODO: Implement +static int FileRename(const char *fileName, const char *fileRename); +static int FileRemove(const char *fileName); + +// Load examples collection information +static rlExampleInfo *LoadExamplesData(const char *fileName, int *exCount); +static void UnloadExamplesData(rlExampleInfo *exInfo); + +// Get text lines (by line-breaks '\n') +// WARNING: It does not copy text data, just returns line pointers +static const char **GetTextLines(const char *text, int *count); + +// raylib example line info parser +// Parses following line format: core/core_basic_window;⭐️☆☆☆;1.0;1.0;"Ray"/@raysan5 +static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry); + +// Sort array of strings by name +// WARNING: items[] pointers are reorganized +static void SortStringsByName(char **items, int count); //------------------------------------------------------------------------------------ // Program main entry point @@ -87,15 +116,14 @@ int main(int argc, char *argv[]) char *exBasePath = "C:/GitHub/raylib/examples"; char *exWebPath = "C:/GitHub/raylib.com/examples"; char *exTemplateFilePath = "C:/GitHub/raylib/examples/examples_template.c"; - + char *exCollectionList = "C:/GitHub/raylib/examples/examples_list.txt"; + char inFileName[1024] = { 0 }; // Example input filename - char exName[1024] = { 0 }; // Example name, without extension: core_basic_window + char exName[64] = { 0 }; // Example name, without extension: core_basic_window char exCategory[32] = { 0 }; // Example category: core - char exRename[1024] = { 0 }; // Example re-name, without extension - char exPath[1024] = { 0 }; // Example path -NOT USED- - char exFullPath[1024] = { 0 }; // Example full path -NOT USED- - + char exRename[64] = { 0 }; // Example re-name, without extension + int opCode = OP_NONE; // Operation code: 0-None(Help), 1-Create, 2-Add, 3-Rename, 4-Remove // Command-line usage mode @@ -108,6 +136,7 @@ int main(int argc, char *argv[]) // add : Add existing example, category extracted from name // rename : Rename an existing example // remove : Remove an existing example + // validate : Validate examples collection if (strcmp(argv[1], "create") == 0) { // Check for valid upcoming argument @@ -116,7 +145,7 @@ int main(int argc, char *argv[]) else { // TODO: Additional security checks for file name? - + strcpy(inFileName, argv[2]); // Register filename for creation opCode = 1; } @@ -131,7 +160,7 @@ int main(int argc, char *argv[]) if (IsFileExtension(argv[2], ".c")) // Check for valid file extension: input { // TODO: Parse category name from filename provided! - + strcpy(inFileName, argv[2]); // Register filename for creation opCode = 2; } @@ -141,17 +170,11 @@ int main(int argc, char *argv[]) else if (strcmp(argv[1], "rename") == 0) { if (argc == 2) LOG("WARNING: No filename provided to create\n"); - //else if (argc == 3) LOG("WARNING: No enough arguments provided\n"); All the documentation says 3 args but I don't mind being wrong - else if (argc > 3) LOG("WARNING: Too many arguments provided\n"); + else if (argc == 3) LOG("WARNING: No enough arguments provided\n"); + else if (argc > 4) LOG("WARNING: Too many arguments provided\n"); else { - strcpy(exName, argv[2]); - for (int index = 0; index < 32; index++) - { - if (exName[index] == '_') break; - exCategory[index] = exName[index]; - } - strcpy(exRename, argv[3]); + // TODO: Register exName, exCategory and exRename opCode = 3; } @@ -167,8 +190,12 @@ int main(int argc, char *argv[]) opCode = 4; } } + else if (strcmp(argv[1], "validate") == 0) + { + opCode = 5; + } } - + switch (opCode) { case 1: // Create: New example from template @@ -181,33 +208,33 @@ int main(int argc, char *argv[]) if ((opCode != 1) && FileExists(inFileName)) { FileCopy(inFileName, TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); - } - + } + // Generate all required files //-------------------------------------------------------------------------------- // Create: raylib/examples//_example_name.c // Create: raylib/examples//_example_name.png - FileCopy("C:/GitHub/raylib/examples/examples_template.png", + FileCopy("C:/GitHub/raylib/examples/examples_template.png", TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName)); // To be updated manually! - + // Copy: raylib/examples//resources/*.* ---> To be updated manually! - + // TODO: Update the required files to add new example in the required position (ordered by category and name), // it could require some logic to make it possible... - + // Edit: raylib/examples/Makefile --> Add new example // Edit: raylib/examples/Makefile.Web --> Add new example // Edit: raylib/examples/README.md --> Add new example - + // Create: raylib/projects/VS2022/examples/_example_name.vcxproj // Edit: raylib/projects/VS2022/raylib.sln --> Add new example // Edit: raylib.com/common/examples.js --> Add new example - + // Compile to: raylib.com/examples//_example_name.html // Compile to: raylib.com/examples//_example_name.data // Compile to: raylib.com/examples//_example_name.wasm // Compile to: raylib.com/examples//_example_name.js - + // Recompile example (on raylib side) // NOTE: Tools requirements: emscripten, w64devkit system(TextFormat("%s/../build_example_web.bat %s\%s", exBasePath, exCategory, exName)); @@ -225,19 +252,19 @@ int main(int argc, char *argv[]) case 3: // Rename { // Rename all required files - rename(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), + rename(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), TextFormat("%s/%s/%s.c", exBasePath, exCategory, exRename)); - rename(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName), + rename(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName), TextFormat("%s/%s/%s.png", exBasePath, exCategory, exRename)); - + FileTextReplace(TextFormat("%s/Makefile", exBasePath), exName, exRename); FileTextReplace(TextFormat("%s/Makefile.Web", exBasePath), exName, exRename); FileTextReplace(TextFormat("%s/README.md", exBasePath), exName, exRename); - - rename(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName), + + rename(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName), TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exRename)); FileTextReplace(TextFormat("%s/../projects/VS2022/raylib.sln", exBasePath), exName, exRename); - + // Remove old web compilation FileTextReplace(TextFormat("%s/../common/examples.js", exWebPath), exName, exRename); remove(TextFormat("%s/%s/%s.html", exWebPath, exCategory, exName)); @@ -263,6 +290,25 @@ int main(int argc, char *argv[]) { // TODO: Remove and update all required files... + } break; + case 5: // Validate + { + // TODO: Validate examples collection against [examples_list.txt] + + // Validate: raylib/examples//_example_name.c + // Validate: raylib/examples//_example_name.png + // Validate: raylib/examples//resources/.. -> Not possible for now... + // Validate: raylib/examples/Makefile + // Validate: raylib/examples/Makefile.Web + // Validate: raylib/examples/README.md + // Validate: raylib/projects/VS2022/examples/_example_name.vcxproj + // Validate: raylib/projects/VS2022/raylib.sln + // Validate: raylib.com/common/examples.js + // Validate: raylib.com/examples//_example_name.html + // Validate: raylib.com/examples//_example_name.data + // Validate: raylib.com/examples//_example_name.wasm + // Validate: raylib.com/examples//_example_name.js + } break; default: // Help { @@ -272,7 +318,7 @@ int main(int argc, char *argv[]) // add : Add existing example, category extracted from name // rename : Rename an existing example // remove : Remove an existing example - + printf("\n////////////////////////////////////////////////////////////////////////////////////////////\n"); printf("// //\n"); printf("// rexm [raylib examples manager] - A simple command-line tool to manage raylib examples //\n"); @@ -299,57 +345,197 @@ int main(int argc, char *argv[]) printf(" Renames and updates example to \n\n"); } break; } - + return 0; } //---------------------------------------------------------------------------------- // Module specific functions definition //---------------------------------------------------------------------------------- +// Load examples collection information +static rlExampleInfo *LoadExamplesData(const char *fileName, int *exCount) +{ + #define MAX_EXAMPLES_INFO 256 + + *exCount = 0; + rlExampleInfo *exInfo = (rlExampleInfo *)RL_CALLOC(MAX_EXAMPLES_INFO, sizeof(rlExampleInfo)); + + const char *text = LoadFileText(fileName); + + if (text != NULL) + { + int lineCount = 0; + const char **linePtrs = GetTextLines(text, &lineCount); + + for (int i = 0; i < lineCount; i++) + { + // Basic validation for lines start categories + if ((linePtrs[i][0] != '#') && + ((linePtrs[i][0] == 'c') || // core + (linePtrs[i][0] == 's') || // shapes, shaders + (linePtrs[i][0] == 't') || // textures, text + (linePtrs[i][0] == 'm') || // models + (linePtrs[i][0] == 'a') || // audio + (linePtrs[i][0] == 'o'))) // others + { + if (ParseExampleInfoLine(linePtrs[i], &exInfo[*exCount]) == 0) *exCount += 1; + } + } + } + + return exInfo; +} + +// Unload examples collection data +static void UnloadExamplesData(rlExampleInfo *exInfo) +{ + RL_FREE(exInfo); +} + +// Replace text in an existing file static int FileTextReplace(const char *fileName, const char *textLookUp, const char *textReplace) { int result = 0; char *fileText = NULL; char *fileTextUpdated = { 0 }; - - fileText = LoadFileText(fileName); - fileTextUpdated = TextReplace(fileText, textLookUp, textReplace); - result = SaveFileText(fileName, fileTextUpdated); - MemFree(fileTextUpdated); - UnloadFileText(fileText); + if (FileExists(fileName)) + { + fileText = LoadFileText(fileName); + fileTextUpdated = TextReplace(fileText, textLookUp, textReplace); + result = SaveFileText(fileName, fileTextUpdated); + MemFree(fileTextUpdated); + UnloadFileText(fileText); + } + return result; } +// Copy file from one path to another +// WARNING: Destination path must exist static int FileCopy(const char *srcPath, const char *dstPath) { int result = 0; int srcDataSize = 0; unsigned char *srcFileData = LoadFileData(srcPath, &srcDataSize); + // TODO: Create required paths if they do not exist + if ((srcFileData != NULL) && (srcDataSize > 0)) result = SaveFileData(dstPath, srcFileData, srcDataSize); UnloadFileData(srcFileData); - + return result; } +// Rename file (if exists) +// NOTE: Only rename file name required, not full path static int FileRename(const char *fileName, const char *fileRename) { int result = 0; - - // TODO: Make sure to deal with paths properly for file moving if required - + if (FileExists(fileName)) rename(fileName, TextFormat("%s/%s", GetDirectoryPath(fileName), fileRename)); return result; } +// Remove file (if exists) static int FileRemove(const char *fileName) { int result = 0; - + if (FileExists(fileName)) remove(fileName); return result; } + +// Get text lines (by line-breaks '\n') +// WARNING: It does not copy text data, just returns line pointers +static const char **GetTextLines(const char *text, int *count) +{ + #define MAX_TEXT_LINE_PTRS 128 + + static const char *linePtrs[MAX_TEXT_LINE_PTRS] = { 0 }; + for (int i = 0; i < MAX_TEXT_LINE_PTRS; i++) linePtrs[i] = NULL; // Init NULL pointers to substrings + + int textSize = (int)strlen(text); + + linePtrs[0] = text; + int len = 0; + *count = 1; + + for (int i = 0, k = 0; (i < textSize) && (*count < MAX_TEXT_LINE_PTRS); i++) + { + if (text[i] == '\n') + { + k++; + linePtrs[k] = &text[i + 1]; // WARNING: next value is valid? + len = 0; + *count += 1; + } + else len++; + } + + return linePtrs; +} + +// raylib example line info parser +// Parses following line format: core/core_basic_window;⭐️☆☆☆;1.0;1.0;"Ray"/@raysan5 +static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry) +{ + #define MAX_EXAMPLE_INFO_LINE_LEN 512 + + char temp[MAX_EXAMPLE_INFO_LINE_LEN] = { 0 }; + strncpy(temp, line, MAX_EXAMPLE_INFO_LINE_LEN); // WARNING: Copy is needed because strtok() modifies string, adds '\0' + temp[MAX_EXAMPLE_INFO_LINE_LEN - 1] = '\0'; // Ensure null termination + + int tokenCount = 0; + char **tokens = TextSplit(line, ';', &tokenCount); + + // Get category and name + strncpy(entry->category, tokens[0], sizeof(entry->category)); + strncpy(entry->name, tokens[1], sizeof(entry->name)); + + // Parsing stars + // NOTE: Counting the unicode char occurrences: ⭐️ + const char *ptr = tokens[2]; + while (*ptr) + { + if (((unsigned char)ptr[0] == 0xE2) && + ((unsigned char)ptr[1] == 0xAD) && + ((unsigned char)ptr[2] == 0x90)) + { + entry->stars++; + ptr += 3; // Advance past multibyte character + } + else ptr++; + } + + // Get raylib creation/update versions + entry->verCreated = strtof(tokens[3], NULL); + entry->verUpdated = strtof(tokens[4], NULL); + + // Get author and github + char *quote1 = strchr(tokens[5], '"'); + char *quote2 = quote1? strchr(quote1 + 1, '"') : NULL; + if (quote1 && quote2) strncpy(entry->author, quote1 + 1, sizeof(entry->author)); + strncpy(entry->authorGitHub, tokens[6], sizeof(entry->authorGitHub)); + + return 1; +} + +// Text compare, required for qsort() function +static int SortTextCompare(const void *a, const void *b) +{ + const char *str1 = *(const char **)a; + const char *str2 = *(const char **)b; + + return strcmp(str1, str2); +} + +// Sort array of strings by name +// WARNING: items[] pointers are reorganized +static void SortStringsByName(char **items, int count) +{ + qsort(items, count, sizeof(char *), SortTextCompare); +} From 8adccfd521acea3f42d5b894e64bf10a52abca7c Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 1 Aug 2025 20:09:49 +0200 Subject: [PATCH 141/242] Update windows.yml --- .github/workflows/windows.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 2f0fbdbd0..a7d4a412b 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -22,7 +22,6 @@ jobs: build: permissions: contents: write # for actions/upload-release-asset to upload release asset - runs-on: windows-latest strategy: fail-fast: false max-parallel: 1 @@ -54,7 +53,6 @@ jobs: - compiler: mingw-w64 ziptarget: winarm64 # We don't build arm64 with mingw-w64 yet - env: RELEASE_NAME: raylib-dev_${{ matrix.ziptarget }}_${{ matrix.compiler }} GNUTARGET: default From 3e65b23f8952d5567a624cadd97b19ef75b918f6 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 1 Aug 2025 20:10:35 +0200 Subject: [PATCH 142/242] Update windows.yml --- .github/workflows/windows.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index a7d4a412b..876338de1 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -22,6 +22,7 @@ jobs: build: permissions: contents: write # for actions/upload-release-asset to upload release asset + runs-on: windows-latest strategy: fail-fast: false max-parallel: 1 From a36905607c178b5486cc1dc24e5b40efd330d602 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 1 Aug 2025 20:19:35 +0200 Subject: [PATCH 143/242] Update linux.yml --- .github/workflows/linux.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index a4879841c..222e2b289 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -60,7 +60,6 @@ jobs: - name: Setup Environment run: | sudo apt-get update -qq - sudo apt-get install gcc-multilib sudo apt-get install -y --no-install-recommends libglfw3 libglfw3-dev libx11-dev libxcursor-dev libxrandr-dev libxinerama-dev libxi-dev libxext-dev libxfixes-dev libwayland-dev libxkbcommon-dev mkdir build cd build @@ -70,6 +69,11 @@ jobs: mkdir lib cd ../../../raylib # ${{ matrix.ARCH }}-linux-gnu-gcc -v + + - name: Setup Environment (x86) + run : | + sudo apt-get install gcc-multilib + if: matrix.bits == 32 && matrix.ARCH == 'i386' # TODO: Support 32bit (i386) static/shared library building - name: Build Library (32-bit) From b01c8a7a906bd653035a1eec88fa65f76af5a365 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Aug 2025 00:36:17 +0200 Subject: [PATCH 144/242] UPDATE: Implementing examples commands -WIP- --- examples/rexm.c | 89 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 59 insertions(+), 30 deletions(-) diff --git a/examples/rexm.c b/examples/rexm.c index 6a1c27826..0ee60245e 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -111,14 +111,15 @@ static void SortStringsByName(char **items, int count); //------------------------------------------------------------------------------------ int main(int argc, char *argv[]) { - // Examples path for building + // Paths required for examples management // TODO: Avoid hardcoding path values... char *exBasePath = "C:/GitHub/raylib/examples"; char *exWebPath = "C:/GitHub/raylib.com/examples"; char *exTemplateFilePath = "C:/GitHub/raylib/examples/examples_template.c"; + char *exTemplateScreenshot = "C:/GitHub/raylib/examples/examples_template.png"; char *exCollectionList = "C:/GitHub/raylib/examples/examples_list.txt"; - char inFileName[1024] = { 0 }; // Example input filename + char inFileName[1024] = { 0 }; // Example input filename (to be added) char exName[64] = { 0 }; // Example name, without extension: core_basic_window char exCategory[32] = { 0 }; // Example category: core @@ -146,7 +147,8 @@ int main(int argc, char *argv[]) { // TODO: Additional security checks for file name? - strcpy(inFileName, argv[2]); // Register filename for creation + strcpy(exName, argv[2]); // Register filename for new example creation + strncpy(exCategory, exName, TextFindIndex(exName, "_")); opCode = 1; } } @@ -159,23 +161,28 @@ int main(int argc, char *argv[]) { if (IsFileExtension(argv[2], ".c")) // Check for valid file extension: input { - // TODO: Parse category name from filename provided! - - strcpy(inFileName, argv[2]); // Register filename for creation - opCode = 2; + if (FileExists(inFileName)) + { + strcpy(inFileName, argv[2]); // Register filename for addition + strcpy(exName, GetFileNameWithoutExt(argv[2])); // Register example name + strncpy(exCategory, exName, TextFindIndex(exName, "_")); + opCode = 2; + } + else LOG("WARNING: Input file not found, include path\n"); } else LOG("WARNING: Input file extension not recognized (.c)\n"); } } else if (strcmp(argv[1], "rename") == 0) { - if (argc == 2) LOG("WARNING: No filename provided to create\n"); - else if (argc == 3) LOG("WARNING: No enough arguments provided\n"); + if (argc == 2) LOG("WARNING: No filename provided to be renamed\n"); else if (argc > 4) LOG("WARNING: Too many arguments provided\n"); else { - // TODO: Register exName, exCategory and exRename - + strcpy(exName, argv[2]); // Register example name + strncpy(exCategory, exName, TextFindIndex(exName, "_")); + strcpy(exRename, argv[3]); + // TODO: Consider rename with change of category opCode = 3; } } @@ -186,13 +193,13 @@ int main(int argc, char *argv[]) else if (argc > 3) LOG("WARNING: Too many arguments provided\n"); else { - strcpy(inFileName, argv[2]); // Register filename for removal + strcpy(exName, argv[2]); // Register filename for removal opCode = 4; } } else if (strcmp(argv[1], "validate") == 0) { - opCode = 5; + opCode = 5; } } @@ -200,43 +207,65 @@ int main(int argc, char *argv[]) { case 1: // Create: New example from template { - // Copy template file as new example + // Create: raylib/examples//_example_name.c FileCopy(exTemplateFilePath, TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); } case 2: // Add: Example from command-line input filename { - if ((opCode != 1) && FileExists(inFileName)) - { - FileCopy(inFileName, TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); - } - - // Generate all required files - //-------------------------------------------------------------------------------- // Create: raylib/examples//_example_name.c - // Create: raylib/examples//_example_name.png - FileCopy("C:/GitHub/raylib/examples/examples_template.png", - TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName)); // To be updated manually! + if (opCode != 1) FileCopy(inFileName, TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); - // Copy: raylib/examples//resources/*.* ---> To be updated manually! + // Create: raylib/examples//_example_name.png + FileCopy(exTemplateScreenshot, TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName)); // WARNING: To be updated manually! + + // Copy: raylib/examples//resources/... // WARNING: To be updated manually! + + // Check if example is already listed + + // If not, add to the main examples_list // TODO: Update the required files to add new example in the required position (ordered by category and name), // it could require some logic to make it possible... // Edit: raylib/examples/Makefile --> Add new example + char *mkText = LoadFileText(TextFormat("%s/Makefile", exBasePath)); + int exListStartIndex = TextFindIndex(mkText, "#EXAMPLES_LIST_START"); + int exListEndIndex = TextFindIndex(mkText, "#EXAMPLES_LIST_END"); + char *mkTextUpdate = (char *)RL_CALLOC(2*1024*1024, 1); // 2MB + memcpy(mkTextUpdate, mkText, exListStartIndex); + // TODO: Update required lines... + //SaveFileText(TextFormat("%s/Makefile", exBasePath), mkTextUpdate); + UnloadFileText(mkText); + RL_FREE(mkTextUpdate); + // Edit: raylib/examples/Makefile.Web --> Add new example + // Edit: raylib/examples/README.md --> Add new example - + // TODO: Use [examples_list.txt] to update/regen README.md + // Create: raylib/projects/VS2022/examples/_example_name.vcxproj - // Edit: raylib/projects/VS2022/raylib.sln --> Add new example + FileCopy(TextFormat("%s/../projects/VS2022/examples/core_basic_window.vcxproj", exBasePath), + TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName)); + FileTextReplace(, "core_basic_window", exName); + FileTextReplace(, "..\..\examples\core", TextFormat("..\..\examples\%s", exCategory)); + + // Edit: raylib/projects/VS2022/raylib.sln --> Add new example project + system(TextFormat("dotnet solution raylib.sln add %s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName)); + // Edit: raylib.com/common/examples.js --> Add new example + //Entries format: exampleEntry('⭐️☆☆☆' , 'core' , 'basic_window'), + char *jsText = LoadFileText(TextFormat("%s/../common/examples.js", exWebPath)); + int exListStartIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_START"); + int exListEndIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_END"); + UnloadFileText(jsText); + + // Recompile example (on raylib side) + // NOTE: Tools requirements: emscripten, w64devkit // Compile to: raylib.com/examples//_example_name.html // Compile to: raylib.com/examples//_example_name.data // Compile to: raylib.com/examples//_example_name.wasm // Compile to: raylib.com/examples//_example_name.js - - // Recompile example (on raylib side) - // NOTE: Tools requirements: emscripten, w64devkit system(TextFormat("%s/../build_example_web.bat %s\%s", exBasePath, exCategory, exName)); // Copy results to web side From bf536d68e129762e21ecdb1a6f722d3d1ce6b7dd Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Sat, 2 Aug 2025 08:37:11 -0700 Subject: [PATCH 145/242] Make GetRenderWidth and GetREnderHeight return the FBO size if one is active. --- src/rcore.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rcore.c b/src/rcore.c index 013ea434a..099bbe9c8 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -827,6 +827,8 @@ int GetScreenHeight(void) // Get current render width which is equal to screen width*dpi scale int GetRenderWidth(void) { + if (CORE.Window.usingFbo) return CORE.Window.currentFbo.width; + int width = 0; #if defined(__APPLE__) Vector2 scale = GetWindowScaleDPI(); @@ -840,6 +842,8 @@ int GetRenderWidth(void) // Get current screen height which is equal to screen height*dpi scale int GetRenderHeight(void) { + if (CORE.Window.usingFbo) return CORE.Window.currentFbo.height; + int height = 0; #if defined(__APPLE__) Vector2 scale = GetWindowScaleDPI(); From 159a9eac4a743bb8ddb829c1071dc3d3af103b49 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Aug 2025 17:58:44 +0200 Subject: [PATCH 146/242] Update build_example_web.bat --- examples/build_example_web.bat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/build_example_web.bat b/examples/build_example_web.bat index b04439c8a..f09cbcc0b 100644 --- a/examples/build_example_web.bat +++ b/examples/build_example_web.bat @@ -1,10 +1,10 @@ ::@echo off :: . -:: Compile your examples for web using: build_example_web.bat \ +:: Compile your examples for web using: build_example_web.bat / :: . set "INPUT_FILE=%1" :: Change delimiter for the FOR loop -for /f "tokens=1-10 delims=\" %%a in ("%INPUT_FILE%") do ( +for /f "tokens=1-10 delims=/" %%a in ("%INPUT_FILE%") do ( set CATEGORY=%%a set FILENAME=%%b ) From 1c8eef9b0b9a0dd456af3c602e33f046a482b295 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Aug 2025 17:59:34 +0200 Subject: [PATCH 147/242] Update raylib and generate `ARM64` libraries for Windows, Linux, macOS --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 6b8b4acf3..4517cbcac 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1516,7 +1516,7 @@ RLAPI char *TextInsert(const char *text, const char *insert, int position); RLAPI char *TextJoin(char **textList, int count, const char *delimiter); // Join text strings with delimiter RLAPI char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings, using MAX_TEXTSPLIT_COUNT static strings RLAPI void TextAppend(char *text, const char *append, int *position); // Append text at specific position and move cursor! -RLAPI int TextFindIndex(const char *text, const char *find); // Find first text occurrence within a string +RLAPI int TextFindIndex(const char *text, const char *find); // Find first text occurrence within a string, -1 if not found RLAPI char *TextToUpper(const char *text); // Get upper case version of provided string RLAPI char *TextToLower(const char *text); // Get lower case version of provided string RLAPI char *TextToPascal(const char *text); // Get Pascal case notation version of provided string From c60763d19ef352287118d48e372b2c75d4e62d8b Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Aug 2025 18:00:41 +0200 Subject: [PATCH 148/242] UPDATED: `LoadExamplesData()` to support filtering and sorting --- examples/rexm.c | 247 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 206 insertions(+), 41 deletions(-) diff --git a/examples/rexm.c b/examples/rexm.c index 0ee60245e..70d2b8151 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -64,12 +64,12 @@ // raylib example info struct typedef struct { char category[16]; - char name[64]; + char name[128]; char stars; float verCreated; float verUpdated; char author[64]; - char authorGitHub[32]; + char authorGitHub[64]; } rlExampleInfo; // Example management operations @@ -82,6 +82,10 @@ typedef enum { OP_VALIDATE = 5, // Validate examples, using [examples_list.txt] as main source by default } rlExampleOperation; +#define MAX_EXAMPLE_CATEGORIES 8 + +static const char *exCategories[MAX_EXAMPLE_CATEGORIES] = { "core", "shapes", "textures", "text", "models", "shaders", "audio", "others" }; + //---------------------------------------------------------------------------------- // Module specific functions declaration //---------------------------------------------------------------------------------- @@ -91,7 +95,9 @@ static int FileRename(const char *fileName, const char *fileRename); static int FileRemove(const char *fileName); // Load examples collection information -static rlExampleInfo *LoadExamplesData(const char *fileName, int *exCount); +// NOTE 1: Load by category: "ALL", "core", "shapes", "textures", "text", "models", "shaders", others" +// NOTE 2: Sort examples list on request flag +static rlExampleInfo *LoadExamplesData(const char *fileName, const char *category, bool sort, int *exCount); static void UnloadExamplesData(rlExampleInfo *exInfo); // Get text lines (by line-breaks '\n') @@ -104,7 +110,7 @@ static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry); // Sort array of strings by name // WARNING: items[] pointers are reorganized -static void SortStringsByName(char **items, int count); +static void SortExampleByName(rlExampleInfo *items, int count); //------------------------------------------------------------------------------------ // Program main entry point @@ -117,8 +123,8 @@ int main(int argc, char *argv[]) char *exWebPath = "C:/GitHub/raylib.com/examples"; char *exTemplateFilePath = "C:/GitHub/raylib/examples/examples_template.c"; char *exTemplateScreenshot = "C:/GitHub/raylib/examples/examples_template.png"; - char *exCollectionList = "C:/GitHub/raylib/examples/examples_list.txt"; - + char *exCollectionListPath = "C:/GitHub/raylib/examples/examples_list.txt"; + char inFileName[1024] = { 0 }; // Example input filename (to be added) char exName[64] = { 0 }; // Example name, without extension: core_basic_window @@ -203,12 +209,29 @@ int main(int argc, char *argv[]) } } + // Load examples collection information + //exInfo = LoadExamplesData(exCollectionListPath, "core", true, &exInfoCount); + //for (int i = 0; i < exInfoCount; i++) printf("%i - %s [%i]\n", i + 1, exInfo[i].name, exInfo[i].stars); + switch (opCode) { case 1: // Create: New example from template { // Create: raylib/examples//_example_name.c - FileCopy(exTemplateFilePath, TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); + char *exText = LoadFileText(exTemplateFilePath); + char *exTextUpdated[6] = { 0 }; + int exIndex = TextFindIndex(exText, "/****************"); + + exTextUpdated[0] = TextReplace(exText + exIndex, "", exCategory); + exTextUpdated[1] = TextReplace(exTextUpdated[0], "", exName + strlen(exCategory) + 1); + //TextReplace(newExample, "", "Ray"); + //TextReplace(newExample, "@", "@raysan5"); + //TextReplace(newExample, "", 2025); + //TextReplace(newExample, "", 2025); + + SaveFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), exTextUpdated[1]); + for (int i = 0; i < 6; i++) { MemFree(exTextUpdated[i]); exTextUpdated[i] = NULL; } + UnloadFileText(exText); } case 2: // Add: Example from command-line input filename { @@ -220,45 +243,163 @@ int main(int argc, char *argv[]) // Copy: raylib/examples//resources/... // WARNING: To be updated manually! - // Check if example is already listed - - // If not, add to the main examples_list - - // TODO: Update the required files to add new example in the required position (ordered by category and name), - // it could require some logic to make it possible... + // Add example to the main collection list, if not already there + // NOTE: Required format: shapes;shapes_basic_shapes;⭐️☆☆☆;1.0;4.2;"Ray";@raysan5 + //------------------------------------------------------------------------------------------------ + char *exColInfo = LoadFileText(exCollectionListPath); + if (TextFindIndex(exColInfo, exName) == -1) // Example not found + { + char *exColInfoUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated list copy, 2MB + + // Add example to the main list, by category + // by default add it last in the category list + // NOTE: When populating to other files, lists are sorted by name + int nextCatIndex = 0; + if (strcmp(exCategory, "core") == 0) nextCatIndex = 1; + else if (strcmp(exCategory, "shapes") == 0) nextCatIndex = 2; + else if (strcmp(exCategory, "textures") == 0) nextCatIndex = 3; + else if (strcmp(exCategory, "text") == 0) nextCatIndex = 4; + else if (strcmp(exCategory, "models") == 0) nextCatIndex = 5; + else if (strcmp(exCategory, "shaders") == 0) nextCatIndex = 6; + else if (strcmp(exCategory, "audio") == 0) nextCatIndex = 7; + else if (strcmp(exCategory, "others") == 0) nextCatIndex = -1; // Add to EOF + + if (nextCatIndex == -1) + { + // Add example to the end of the list + int endIndex = strlen(exColInfo); + memcpy(exColInfoUpdated, exColInfo, endIndex); + sprintf(exColInfoUpdated + endIndex, TextFormat("\n%s/%s\n", exCategory, exName)); + } + else + { + // Add example to the end of the category list + // TODO: Get required example info from example file header (if provided) + // NOTE: If no example info is provided (other than category/name), just using some default values + int catIndex = TextFindIndex(exColInfo, exCategories[nextCatIndex]); + memcpy(exColInfoUpdated, exColInfo, catIndex); + int textWritenSize = sprintf(exColInfoUpdated + catIndex, TextFormat("%s;%s;⭐️☆☆☆;6.0;6.0;\"Ray\";@raysan5\n", exCategory, exName)); + memcpy(exColInfoUpdated + catIndex + textWritenSize, exColInfo + catIndex, strlen(exColInfo) - catIndex); + } + + SaveFileText(exCollectionListPath, exColInfoUpdated); + RL_FREE(exColInfoUpdated); + } + UnloadFileText(exColInfo); + //------------------------------------------------------------------------------------------------ // Edit: raylib/examples/Makefile --> Add new example + //------------------------------------------------------------------------------------------------ char *mkText = LoadFileText(TextFormat("%s/Makefile", exBasePath)); + char *mkTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated Makefile copy, 2MB + int exListStartIndex = TextFindIndex(mkText, "#EXAMPLES_LIST_START"); int exListEndIndex = TextFindIndex(mkText, "#EXAMPLES_LIST_END"); - char *mkTextUpdate = (char *)RL_CALLOC(2*1024*1024, 1); // 2MB - memcpy(mkTextUpdate, mkText, exListStartIndex); - // TODO: Update required lines... - //SaveFileText(TextFormat("%s/Makefile", exBasePath), mkTextUpdate); + + int mkIndex = exListStartIndex; + memcpy(mkTextUpdated, mkText, exListStartIndex); + TextAppend(mkTextUpdated + mkIndex, "#EXAMPLES_LIST_START\n", &mkIndex); + + for (int i = 0, exCount = 0; i < MAX_EXAMPLE_CATEGORIES; i++) + { + TextAppend(mkTextUpdated + mkIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i])), &mkIndex); // Category Makefile object ("CORE = \") + + rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], true, &exCount); + + printf("loaded category: %s\n", exCategories[i]); + + for (int x = 0; x < exCount - 1; x++) + TextAppend(mkTextUpdated + mkIndex, TextFormat(" %s/%s \\\n", exCatList[x].category, exCatList[x].name), &mkIndex); + + TextAppend(mkTextUpdated + mkIndex, TextFormat(" %s/%s\n\n", exCatList[exCount - 1].category, exCatList[exCount - 1].name), &mkIndex); + + UnloadExamplesData(exCatList); + } + + printf("got1\n"); + + // Add the remaining part of the original file + TextAppend(mkTextUpdated + mkIndex, mkText + exListEndIndex, &mkIndex); + + printf("got2\n"); + + // Save updated file + SaveFileText(TextFormat("%s/Makefile", exBasePath), mkTextUpdated); UnloadFileText(mkText); - RL_FREE(mkTextUpdate); + RL_FREE(mkTextUpdated); + + printf("got3\n"); + //------------------------------------------------------------------------------------------------ // Edit: raylib/examples/Makefile.Web --> Add new example - + //------------------------------------------------------------------------------------------------ + + // TODO. + + //------------------------------------------------------------------------------------------------ + // Edit: raylib/examples/README.md --> Add new example + //------------------------------------------------------------------------------------------------ + // TODO: Use [examples_list.txt] to update/regen README.md + //------------------------------------------------------------------------------------------------ + // Create: raylib/projects/VS2022/examples/_example_name.vcxproj + //------------------------------------------------------------------------------------------------ FileCopy(TextFormat("%s/../projects/VS2022/examples/core_basic_window.vcxproj", exBasePath), TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName)); - FileTextReplace(, "core_basic_window", exName); - FileTextReplace(, "..\..\examples\core", TextFormat("..\..\examples\%s", exCategory)); + FileTextReplace(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName), + "core_basic_window", exName); + FileTextReplace(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName), + "..\\..\\examples\\core", TextFormat("..\\..\\examples\\%s", exCategory)); // Edit: raylib/projects/VS2022/raylib.sln --> Add new example project system(TextFormat("dotnet solution raylib.sln add %s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName)); + //------------------------------------------------------------------------------------------------ // Edit: raylib.com/common/examples.js --> Add new example - //Entries format: exampleEntry('⭐️☆☆☆' , 'core' , 'basic_window'), + // NOTE: Entries format: exampleEntry('⭐️☆☆☆' , 'core' , 'basic_window'), + //------------------------------------------------------------------------------------------------ char *jsText = LoadFileText(TextFormat("%s/../common/examples.js", exWebPath)); - int exListStartIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_START"); - int exListEndIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_END"); + char *jsTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated examples.js copy, 2MB + + exListStartIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_START"); + exListEndIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_END"); + + int jsIndex = exListStartIndex; + memcpy(jsTextUpdated, jsText, exListStartIndex); + TextAppend(jsTextUpdated + jsIndex, "//EXAMPLE_DATA_LIST_START\n", &jsIndex); + TextAppend(jsTextUpdated + jsIndex, "var exampleData = [\n", &jsIndex); + // NOTE: We avoid "others" category + for (int i = 0, exCount = 0; i < MAX_EXAMPLE_CATEGORIES - 1; i++) + { + rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], true, &exCount); + for (int x = 0; x < exCount; x++) + { + //char stars[16] = { 0 }; + //for (int s = 0; s < 4; s++) strcpy(stars + 3) + + TextAppend(jsTextUpdated + mkIndex, + TextFormat(" exampleEntry('%s%s%s%s' , '%s' , '%s'),\n", + "⭐️", "☆", "☆", "☆", + exCatList[x].category, + exCatList[x].name + strlen(exCatList[x].category) + 1), + &jsIndex); + } + + UnloadExamplesData(exCatList); + } + + // Add the remaining part of the original file + TextAppend(jsTextUpdated + jsIndex, jsText + exListEndIndex, &jsIndex); + + // Save updated file + SaveFileText(TextFormat("%s/Makefile", exBasePath), jsTextUpdated); UnloadFileText(jsText); + RL_FREE(jsTextUpdated); + //------------------------------------------------------------------------------------------------ // Recompile example (on raylib side) // NOTE: Tools requirements: emscripten, w64devkit @@ -266,7 +407,9 @@ int main(int argc, char *argv[]) // Compile to: raylib.com/examples//_example_name.data // Compile to: raylib.com/examples//_example_name.wasm // Compile to: raylib.com/examples//_example_name.js - system(TextFormat("%s/../build_example_web.bat %s\%s", exBasePath, exCategory, exName)); + // TODO: WARNING: This .BAT is not portable and it does not consider RESOURCES for Web properly, + // Makefile.Web should be used... but it requires proper editing first! + system(TextFormat("%s/../build_example_web.bat %s/%s", exBasePath, exCategory, exName)); // Copy results to web side FileCopy(TextFormat("%s/%s/%s.html", exBasePath, exCategory, exName), @@ -303,7 +446,7 @@ int main(int argc, char *argv[]) // Recompile example (on raylib side) // NOTE: Tools requirements: emscripten, w64devkit - system(TextFormat("%s/../build_example_web.bat %s\%s", exBasePath, exCategory, exName)); + system(TextFormat("%s/../build_example_web.bat %s/%s", exBasePath, exCategory, exName)); // Copy results to web side FileCopy(TextFormat("%s/%s/%s.html", exBasePath, exCategory, exName), @@ -382,14 +525,14 @@ int main(int argc, char *argv[]) // Module specific functions definition //---------------------------------------------------------------------------------- // Load examples collection information -static rlExampleInfo *LoadExamplesData(const char *fileName, int *exCount) +static rlExampleInfo *LoadExamplesData(const char *fileName, const char *category, bool sort, int *exCount) { #define MAX_EXAMPLES_INFO 256 - *exCount = 0; rlExampleInfo *exInfo = (rlExampleInfo *)RL_CALLOC(MAX_EXAMPLES_INFO, sizeof(rlExampleInfo)); + int exCounter = 0; - const char *text = LoadFileText(fileName); + char *text = LoadFileText(fileName); if (text != NULL) { @@ -407,11 +550,33 @@ static rlExampleInfo *LoadExamplesData(const char *fileName, int *exCount) (linePtrs[i][0] == 'a') || // audio (linePtrs[i][0] == 'o'))) // others { - if (ParseExampleInfoLine(linePtrs[i], &exInfo[*exCount]) == 0) *exCount += 1; + rlExampleInfo info = { 0 }; + int result = ParseExampleInfoLine(linePtrs[i], &info); + if (result == 1) // Success on parsing + { + if (strcmp(category, "ALL") == 0) + { + // Add all examples to the list + memcpy(&exInfo[exCounter], &info, sizeof(rlExampleInfo)); + exCounter++; + } + else if (strcmp(info.category, category) == 0) + { + // Get only specific category examples + memcpy(&exInfo[exCounter], &info, sizeof(rlExampleInfo)); + exCounter++; + } + } } } + + UnloadFileText(text); } + // Sorting required + if (sort) SortExampleByName(exInfo, exCounter); + + *exCount = exCounter; return exInfo; } @@ -509,7 +674,7 @@ static const char **GetTextLines(const char *text, int *count) } // raylib example line info parser -// Parses following line format: core/core_basic_window;⭐️☆☆☆;1.0;1.0;"Ray"/@raysan5 +// Parses following line format: core;core_basic_window;⭐️☆☆☆;1.0;1.0;"Ray";@raysan5 static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry) { #define MAX_EXAMPLE_INFO_LINE_LEN 512 @@ -522,8 +687,8 @@ static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry) char **tokens = TextSplit(line, ';', &tokenCount); // Get category and name - strncpy(entry->category, tokens[0], sizeof(entry->category)); - strncpy(entry->name, tokens[1], sizeof(entry->name)); + strcpy(entry->category, tokens[0]); + strcpy(entry->name, tokens[1]); // Parsing stars // NOTE: Counting the unicode char occurrences: ⭐️ @@ -547,24 +712,24 @@ static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry) // Get author and github char *quote1 = strchr(tokens[5], '"'); char *quote2 = quote1? strchr(quote1 + 1, '"') : NULL; - if (quote1 && quote2) strncpy(entry->author, quote1 + 1, sizeof(entry->author)); - strncpy(entry->authorGitHub, tokens[6], sizeof(entry->authorGitHub)); + if (quote1 && quote2) strcpy(entry->author, quote1 + 1); + strcpy(entry->authorGitHub, tokens[6]); return 1; } // Text compare, required for qsort() function -static int SortTextCompare(const void *a, const void *b) +static int rlExampleInfoCompare(const void *a, const void *b) { - const char *str1 = *(const char **)a; - const char *str2 = *(const char **)b; + const rlExampleInfo *ex1 = (const rlExampleInfo *)a; + const rlExampleInfo *ex2 = (const rlExampleInfo *)b; - return strcmp(str1, str2); + return strcmp(ex1->name, ex2->name); } // Sort array of strings by name // WARNING: items[] pointers are reorganized -static void SortStringsByName(char **items, int count) +static void SortExampleByName(rlExampleInfo *items, int count) { - qsort(items, count, sizeof(char *), SortTextCompare); + qsort(items, count, sizeof(rlExampleInfo), rlExampleInfoCompare); } From 42619ea2dfb3493975518c61c4b13b76180b6a11 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 2 Aug 2025 16:01:00 +0000 Subject: [PATCH 149/242] Update raylib_api.* by CI --- parser/output/raylib_api.json | 2 +- parser/output/raylib_api.lua | 2 +- parser/output/raylib_api.txt | 2 +- parser/output/raylib_api.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index cf32ce524..c2a4a1827 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -9777,7 +9777,7 @@ }, { "name": "TextFindIndex", - "description": "Find first text occurrence within a string", + "description": "Find first text occurrence within a string, -1 if not found", "returnType": "int", "params": [ { diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index cb6cf9db3..23547d9f1 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -6942,7 +6942,7 @@ return { }, { name = "TextFindIndex", - description = "Find first text occurrence within a string", + description = "Find first text occurrence within a string, -1 if not found", returnType = "int", params = { {type = "const char *", name = "text"}, diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index 9f8ab8ef5..9539d72a5 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -3742,7 +3742,7 @@ Function 434: TextAppend() (3 input parameters) Function 435: TextFindIndex() (2 input parameters) Name: TextFindIndex Return type: int - Description: Find first text occurrence within a string + Description: Find first text occurrence within a string, -1 if not found Param[1]: text (type: const char *) Param[2]: find (type: const char *) Function 436: TextToUpper() (1 input parameters) diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index f3ebb74be..d99187adf 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -2480,7 +2480,7 @@ - + From e01f3f158ea950b5793d451a7816214195f8ade9 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Aug 2025 18:01:23 +0200 Subject: [PATCH 150/242] Remove some testing code --- examples/rexm.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/examples/rexm.c b/examples/rexm.c index 70d2b8151..d01e291cd 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -315,20 +315,14 @@ int main(int argc, char *argv[]) UnloadExamplesData(exCatList); } - - printf("got1\n"); - + // Add the remaining part of the original file TextAppend(mkTextUpdated + mkIndex, mkText + exListEndIndex, &mkIndex); - - printf("got2\n"); - + // Save updated file SaveFileText(TextFormat("%s/Makefile", exBasePath), mkTextUpdated); UnloadFileText(mkText); RL_FREE(mkTextUpdated); - - printf("got3\n"); //------------------------------------------------------------------------------------------------ // Edit: raylib/examples/Makefile.Web --> Add new example From e76c5f7d64dfc8e23509af7777862c88f207a606 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Aug 2025 18:11:30 +0200 Subject: [PATCH 151/242] Update rexm.c --- examples/rexm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/rexm.c b/examples/rexm.c index d01e291cd..c50aae08d 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -334,8 +334,10 @@ int main(int argc, char *argv[]) // Edit: raylib/examples/README.md --> Add new example //------------------------------------------------------------------------------------------------ - // TODO: Use [examples_list.txt] to update/regen README.md + //Look for "| 01 | " + // Lines format: | 01 | [core_basic_window](core/core_basic_window.c) | core_basic_window | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | + //------------------------------------------------------------------------------------------------ From 528ad4964d440e74ca55983b629c256e6873dc00 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Aug 2025 19:21:36 +0200 Subject: [PATCH 152/242] UPDATE: `Makefile` example addition working --- examples/rexm.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/examples/rexm.c b/examples/rexm.c index c50aae08d..5906c3ae7 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -267,7 +267,7 @@ int main(int argc, char *argv[]) if (nextCatIndex == -1) { // Add example to the end of the list - int endIndex = strlen(exColInfo); + int endIndex = (int)strlen(exColInfo); memcpy(exColInfoUpdated, exColInfo, endIndex); sprintf(exColInfoUpdated + endIndex, TextFormat("\n%s/%s\n", exCategory, exName)); } @@ -296,28 +296,25 @@ int main(int argc, char *argv[]) int exListStartIndex = TextFindIndex(mkText, "#EXAMPLES_LIST_START"); int exListEndIndex = TextFindIndex(mkText, "#EXAMPLES_LIST_END"); - int mkIndex = exListStartIndex; + int mkIndex = 0; memcpy(mkTextUpdated, mkText, exListStartIndex); - TextAppend(mkTextUpdated + mkIndex, "#EXAMPLES_LIST_START\n", &mkIndex); - - for (int i = 0, exCount = 0; i < MAX_EXAMPLE_CATEGORIES; i++) + mkIndex = sprintf(mkTextUpdated + exListStartIndex, "#EXAMPLES_LIST_START\n"); + + for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++) { - TextAppend(mkTextUpdated + mkIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i])), &mkIndex); // Category Makefile object ("CORE = \") + mkIndex += sprintf(mkTextUpdated + exListStartIndex + mkIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i]))); + + int exCount = 0; + rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], true, &exCount); - rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], true, &exCount); - - printf("loaded category: %s\n", exCategories[i]); - - for (int x = 0; x < exCount - 1; x++) - TextAppend(mkTextUpdated + mkIndex, TextFormat(" %s/%s \\\n", exCatList[x].category, exCatList[x].name), &mkIndex); - - TextAppend(mkTextUpdated + mkIndex, TextFormat(" %s/%s\n\n", exCatList[exCount - 1].category, exCatList[exCount - 1].name), &mkIndex); + for (int x = 0; x < exCount - 1; x++) mkIndex += sprintf(mkTextUpdated + exListStartIndex + mkIndex, TextFormat(" %s/%s \\\n", exCatList[x].category, exCatList[x].name)); + mkIndex += sprintf(mkTextUpdated + exListStartIndex + mkIndex, TextFormat(" %s/%s\n\n", exCatList[exCount - 1].category, exCatList[exCount - 1].name)); UnloadExamplesData(exCatList); } // Add the remaining part of the original file - TextAppend(mkTextUpdated + mkIndex, mkText + exListEndIndex, &mkIndex); + memcpy(mkTextUpdated + exListStartIndex + mkIndex, mkText + exListEndIndex, strlen(mkText) - exListEndIndex); // Save updated file SaveFileText(TextFormat("%s/Makefile", exBasePath), mkTextUpdated); @@ -351,12 +348,13 @@ int main(int argc, char *argv[]) "..\\..\\examples\\core", TextFormat("..\\..\\examples\\%s", exCategory)); // Edit: raylib/projects/VS2022/raylib.sln --> Add new example project - system(TextFormat("dotnet solution raylib.sln add %s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName)); + system(TextFormat("dotnet solution %s/../projects/VS2022/raylib.sln add %s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exBasePath, exName)); //------------------------------------------------------------------------------------------------ // Edit: raylib.com/common/examples.js --> Add new example // NOTE: Entries format: exampleEntry('⭐️☆☆☆' , 'core' , 'basic_window'), //------------------------------------------------------------------------------------------------ + /* char *jsText = LoadFileText(TextFormat("%s/../common/examples.js", exWebPath)); char *jsTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated examples.js copy, 2MB @@ -377,7 +375,7 @@ int main(int argc, char *argv[]) //char stars[16] = { 0 }; //for (int s = 0; s < 4; s++) strcpy(stars + 3) - TextAppend(jsTextUpdated + mkIndex, + TextAppend(jsTextUpdated + jsIndex, TextFormat(" exampleEntry('%s%s%s%s' , '%s' , '%s'),\n", "⭐️", "☆", "☆", "☆", exCatList[x].category, @@ -392,9 +390,10 @@ int main(int argc, char *argv[]) TextAppend(jsTextUpdated + jsIndex, jsText + exListEndIndex, &jsIndex); // Save updated file - SaveFileText(TextFormat("%s/Makefile", exBasePath), jsTextUpdated); + SaveFileText(TextFormat("%s/../common/examples.js", exWebPath), jsTextUpdated); UnloadFileText(jsText); RL_FREE(jsTextUpdated); + */ //------------------------------------------------------------------------------------------------ // Recompile example (on raylib side) @@ -405,7 +404,7 @@ int main(int argc, char *argv[]) // Compile to: raylib.com/examples//_example_name.js // TODO: WARNING: This .BAT is not portable and it does not consider RESOURCES for Web properly, // Makefile.Web should be used... but it requires proper editing first! - system(TextFormat("%s/../build_example_web.bat %s/%s", exBasePath, exCategory, exName)); + system(TextFormat("%s/build_example_web.bat %s/%s", exBasePath, exCategory, exName)); // Copy results to web side FileCopy(TextFormat("%s/%s/%s.html", exBasePath, exCategory, exName), @@ -527,6 +526,7 @@ static rlExampleInfo *LoadExamplesData(const char *fileName, const char *categor rlExampleInfo *exInfo = (rlExampleInfo *)RL_CALLOC(MAX_EXAMPLES_INFO, sizeof(rlExampleInfo)); int exCounter = 0; + *exCount = 0; char *text = LoadFileText(fileName); @@ -643,7 +643,7 @@ static int FileRemove(const char *fileName) // WARNING: It does not copy text data, just returns line pointers static const char **GetTextLines(const char *text, int *count) { - #define MAX_TEXT_LINE_PTRS 128 + #define MAX_TEXT_LINE_PTRS 512 static const char *linePtrs[MAX_TEXT_LINE_PTRS] = { 0 }; for (int i = 0; i < MAX_TEXT_LINE_PTRS; i++) linePtrs[i] = NULL; // Init NULL pointers to substrings From b044b5295540e230ee380a69105aea16e3076d17 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Aug 2025 19:43:27 +0200 Subject: [PATCH 153/242] UPDATED: `examples.js` example addition working --- examples/rexm.c | 52 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/examples/rexm.c b/examples/rexm.c index 5906c3ae7..be7f22325 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -290,6 +290,7 @@ int main(int argc, char *argv[]) // Edit: raylib/examples/Makefile --> Add new example //------------------------------------------------------------------------------------------------ + /* char *mkText = LoadFileText(TextFormat("%s/Makefile", exBasePath)); char *mkTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated Makefile copy, 2MB @@ -320,6 +321,7 @@ int main(int argc, char *argv[]) SaveFileText(TextFormat("%s/Makefile", exBasePath), mkTextUpdated); UnloadFileText(mkText); RL_FREE(mkTextUpdated); + */ //------------------------------------------------------------------------------------------------ // Edit: raylib/examples/Makefile.Web --> Add new example @@ -354,46 +356,60 @@ int main(int argc, char *argv[]) // Edit: raylib.com/common/examples.js --> Add new example // NOTE: Entries format: exampleEntry('⭐️☆☆☆' , 'core' , 'basic_window'), //------------------------------------------------------------------------------------------------ - /* + char *jsText = LoadFileText(TextFormat("%s/../common/examples.js", exWebPath)); char *jsTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated examples.js copy, 2MB - exListStartIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_START"); - exListEndIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_END"); + int exListStartIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_START"); + int exListEndIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_END"); - int jsIndex = exListStartIndex; + int mkIndex = 0; memcpy(jsTextUpdated, jsText, exListStartIndex); - TextAppend(jsTextUpdated + jsIndex, "//EXAMPLE_DATA_LIST_START\n", &jsIndex); - TextAppend(jsTextUpdated + jsIndex, "var exampleData = [\n", &jsIndex); + mkIndex = sprintf(jsTextUpdated + exListStartIndex, "#EXAMPLES_LIST_START\n"); + + int jsIndex = 0; + memcpy(jsTextUpdated, jsText, exListStartIndex); + jsIndex = sprintf(jsTextUpdated + exListStartIndex, "//EXAMPLE_DATA_LIST_START\n"); + jsIndex += sprintf(jsTextUpdated + exListStartIndex + jsIndex, " var exampleData = [\n"); // NOTE: We avoid "others" category for (int i = 0, exCount = 0; i < MAX_EXAMPLE_CATEGORIES - 1; i++) { - rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], true, &exCount); + rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], false, &exCount); for (int x = 0; x < exCount; x++) { - //char stars[16] = { 0 }; - //for (int s = 0; s < 4; s++) strcpy(stars + 3) - - TextAppend(jsTextUpdated + jsIndex, - TextFormat(" exampleEntry('%s%s%s%s' , '%s' , '%s'),\n", - "⭐️", "☆", "☆", "☆", - exCatList[x].category, - exCatList[x].name + strlen(exCatList[x].category) + 1), - &jsIndex); + char stars[16] = { 0 }; + for (int s = 0; s < 4; s++) + { + if (s < exCatList[x].stars) strcpy(stars + 3*s, "⭐️"); + else strcpy(stars + 3*s, "☆"); + } + + if ((i == 6) && (x == (exCount - 1))) + { + // Last line to add, special case to consider + jsIndex += sprintf(jsTextUpdated + exListStartIndex + jsIndex, + TextFormat(" exampleEntry('%s', '%s', '%s')];\n", stars, exCatList[x].category, exCatList[x].name + strlen(exCatList[x].category) + 1)); + } + else + { + jsIndex += sprintf(jsTextUpdated + exListStartIndex + jsIndex, + TextFormat(" exampleEntry('%s', '%s', '%s'),\n", stars, exCatList[x].category, exCatList[x].name + strlen(exCatList[x].category) + 1)); + + } } UnloadExamplesData(exCatList); } // Add the remaining part of the original file - TextAppend(jsTextUpdated + jsIndex, jsText + exListEndIndex, &jsIndex); + memcpy(jsTextUpdated + exListStartIndex + jsIndex, jsText + exListEndIndex, strlen(jsText) - exListEndIndex); // Save updated file SaveFileText(TextFormat("%s/../common/examples.js", exWebPath), jsTextUpdated); UnloadFileText(jsText); RL_FREE(jsTextUpdated); - */ + //------------------------------------------------------------------------------------------------ // Recompile example (on raylib side) From 19484533947cf2bbbb83a2bec6dae2dfa0221471 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Aug 2025 20:11:31 +0200 Subject: [PATCH 154/242] Update README.md --- examples/README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/README.md b/examples/README.md index 666fda04b..42a3a3de0 100644 --- a/examples/README.md +++ b/examples/README.md @@ -20,7 +20,7 @@ You may find it easier to use than other toolchains, especially when it comes to ### category: core -Examples using raylib core platform functionality like window creation, inputs, drawing modes and system functionality. +Examples using raylib [core](../src/rcore.c) platform functionality like window creation, inputs, drawing modes and system functionality. | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| @@ -63,7 +63,7 @@ Examples using raylib core platform functionality like window creation, inputs, ### category: shapes -Examples using raylib shapes drawing functionality, provided by raylib [shapes](../src/shapes.c) module. +Examples using raylib shapes drawing functionality, provided by raylib [shapes](../src/rshapes.c) module. | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| @@ -87,9 +87,10 @@ Examples using raylib shapes drawing functionality, provided by raylib [shapes]( | 53 | [shapes_splines_drawing](shapes/shapes_splines_drawing.c) | shapes_splines_drawing | ⭐️⭐️⭐️☆ | 5.0 | 5.0 | [Ray](https://github.com/raysan5) | | 54 | [shapes_digital_clock](shapes/shapes_digital_clock.c) | shapes_digital_clock | ⭐️⭐️☆☆ | 5.5 | 5.5 | [Hamza RAHAL](https://github.com/rhmz-rhl) | | 55 | [shapes_double_pendulum](shapes/shapes_double_pendulum.c) | shapes_double_pendulum | ⭐️⭐️☆☆ | 5.5 | 5.5 | [JoeCheong](https://github.com/Joecheong2006) | + ### category: textures -Examples using raylib textures functionality, including image/textures loading/generation and drawing, provided by raylib [textures](../src/textures.c) modul +Examples using raylib textures functionality, including image/textures loading/generation and drawing, provided by raylib [textures](../src/rtextures.c) module. | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| @@ -122,7 +123,7 @@ Examples using raylib textures functionality, including image/textures loading/g ### category: text -Examples using raylib text functionality, including sprite fonts loading/generation and text drawing, provided by raylib [text](../src/text.c) module. +Examples using raylib text functionality, including sprite fonts loading/generation and text drawing, provided by raylib [text](../src/rtext.c) module. | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| @@ -141,7 +142,7 @@ Examples using raylib text functionality, including sprite fonts loading/generat ### category: models -Examples using raylib models functionality, including models loading/generation and drawing, provided by raylib [models](../src/models.c) module. +Examples using raylib models functionality, including models loading/generation and drawing, provided by raylib [models](../src/rmodels.c) module. | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| @@ -206,7 +207,7 @@ Examples using raylib shaders functionality, including shaders loading, paramete ### category: audio -Examples using raylib audio functionality, including sound/music loading and playing. This functionality is provided by raylib [raudio](../src/raudio.c) module. Note this module can be used standalone independently of raylib, check [raudio_standalone](others/raudio_standalone.c) example. +Examples using raylib audio functionality, including sound/music loading and playing. This functionality is provided by raylib [raudio](../src/raudio.c) module. Note this module can be used standalone independently of raylib. | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| From ffe76a4b670716c5ff32626dfa0448729884b03f Mon Sep 17 00:00:00 2001 From: Jeff Myers Date: Sat, 2 Aug 2025 14:58:05 -0700 Subject: [PATCH 155/242] Don't Upload Meshes in LoadObj, LoadModel will upload them. None of the other interchange format readers upload --- src/rmodels.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 246c2b0a3..1a181c843 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -4501,8 +4501,6 @@ static Model LoadOBJ(const char *fileName) tinyobj_shapes_free(objShapes, objShapeCount); tinyobj_materials_free(objMaterials, objMaterialCount); - for (int i = 0; i < model.meshCount; i++) UploadMesh(model.meshes + i, true); - // Restore current working directory if (CHDIR(currentDir) != 0) { From 73baaeeb2e09b0e3b6ae9e69c9d813e7980f33a4 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 3 Aug 2025 01:17:50 +0200 Subject: [PATCH 156/242] REVIEWED: `LoadTextLines()` --- examples/rexm.c | 71 ++++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/examples/rexm.c b/examples/rexm.c index be7f22325..db9792e92 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -102,7 +102,8 @@ static void UnloadExamplesData(rlExampleInfo *exInfo); // Get text lines (by line-breaks '\n') // WARNING: It does not copy text data, just returns line pointers -static const char **GetTextLines(const char *text, int *count); +static char **LoadTextLines(const char *text, int *count); +static void UnloadTextLines(char **text); // raylib example line info parser // Parses following line format: core/core_basic_window;⭐️☆☆☆;1.0;1.0;"Ray"/@raysan5 @@ -356,7 +357,7 @@ int main(int argc, char *argv[]) // Edit: raylib.com/common/examples.js --> Add new example // NOTE: Entries format: exampleEntry('⭐️☆☆☆' , 'core' , 'basic_window'), //------------------------------------------------------------------------------------------------ - + /* char *jsText = LoadFileText(TextFormat("%s/../common/examples.js", exWebPath)); char *jsTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated examples.js copy, 2MB @@ -395,7 +396,6 @@ int main(int argc, char *argv[]) { jsIndex += sprintf(jsTextUpdated + exListStartIndex + jsIndex, TextFormat(" exampleEntry('%s', '%s', '%s'),\n", stars, exCatList[x].category, exCatList[x].name + strlen(exCatList[x].category) + 1)); - } } @@ -409,7 +409,7 @@ int main(int argc, char *argv[]) SaveFileText(TextFormat("%s/../common/examples.js", exWebPath), jsTextUpdated); UnloadFileText(jsText); RL_FREE(jsTextUpdated); - + */ //------------------------------------------------------------------------------------------------ // Recompile example (on raylib side) @@ -549,21 +549,21 @@ static rlExampleInfo *LoadExamplesData(const char *fileName, const char *categor if (text != NULL) { int lineCount = 0; - const char **linePtrs = GetTextLines(text, &lineCount); + const char **lines = LoadTextLines(text, &lineCount); for (int i = 0; i < lineCount; i++) { // Basic validation for lines start categories - if ((linePtrs[i][0] != '#') && - ((linePtrs[i][0] == 'c') || // core - (linePtrs[i][0] == 's') || // shapes, shaders - (linePtrs[i][0] == 't') || // textures, text - (linePtrs[i][0] == 'm') || // models - (linePtrs[i][0] == 'a') || // audio - (linePtrs[i][0] == 'o'))) // others + if ((lines[i][0] != '#') && + ((lines[i][0] == 'c') || // core + (lines[i][0] == 's') || // shapes, shaders + (lines[i][0] == 't') || // textures, text + (lines[i][0] == 'm') || // models + (lines[i][0] == 'a') || // audio + (lines[i][0] == 'o'))) // others { rlExampleInfo info = { 0 }; - int result = ParseExampleInfoLine(linePtrs[i], &info); + int result = ParseExampleInfoLine(lines[i], &info); if (result == 1) // Success on parsing { if (strcmp(category, "ALL") == 0) @@ -655,34 +655,37 @@ static int FileRemove(const char *fileName) return result; } -// Get text lines (by line-breaks '\n') -// WARNING: It does not copy text data, just returns line pointers -static const char **GetTextLines(const char *text, int *count) +// Load text lines +static char **LoadTextLines(const char *text, int *count) { - #define MAX_TEXT_LINE_PTRS 512 - - static const char *linePtrs[MAX_TEXT_LINE_PTRS] = { 0 }; - for (int i = 0; i < MAX_TEXT_LINE_PTRS; i++) linePtrs[i] = NULL; // Init NULL pointers to substrings + #define MAX_TEXT_LINES 512 + #define MAX_TEXT_LINE_LEN 256 + char **lines = (char **)RL_CALLOC(MAX_TEXT_LINES, sizeof(char *)); + for (int i = 0; i < MAX_TEXT_LINES; i++) lines[i] = (char *)RL_CALLOC(MAX_TEXT_LINE_LEN, 1); int textSize = (int)strlen(text); + int k = 0; - linePtrs[0] = text; - int len = 0; - *count = 1; - - for (int i = 0, k = 0; (i < textSize) && (*count < MAX_TEXT_LINE_PTRS); i++) + for (int i = 0, len = 0; (i < textSize) && (k < MAX_TEXT_LINES); i++) { if (text[i] == '\n') { - k++; - linePtrs[k] = &text[i + 1]; // WARNING: next value is valid? + strncpy(lines[k], &text[i - len], len); len = 0; - *count += 1; + k++; } else len++; } - return linePtrs; + *count += k; + return lines; +} + +// Unload text lines +static void UnloadTextLines(char **lines) +{ + for (int i = 0; i < MAX_TEXT_LINES; i++) RL_FREE(lines[i]); + RL_FREE(lines); } // raylib example line info parser @@ -692,7 +695,7 @@ static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry) #define MAX_EXAMPLE_INFO_LINE_LEN 512 char temp[MAX_EXAMPLE_INFO_LINE_LEN] = { 0 }; - strncpy(temp, line, MAX_EXAMPLE_INFO_LINE_LEN); // WARNING: Copy is needed because strtok() modifies string, adds '\0' + strncpy(temp, line, MAX_EXAMPLE_INFO_LINE_LEN); temp[MAX_EXAMPLE_INFO_LINE_LEN - 1] = '\0'; // Ensure null termination int tokenCount = 0; @@ -721,10 +724,10 @@ static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry) entry->verCreated = strtof(tokens[3], NULL); entry->verUpdated = strtof(tokens[4], NULL); - // Get author and github - char *quote1 = strchr(tokens[5], '"'); - char *quote2 = quote1? strchr(quote1 + 1, '"') : NULL; - if (quote1 && quote2) strcpy(entry->author, quote1 + 1); + // Get author and github + if (tokens[5][0] == '"') tokens[5] += 1; + if (tokens[5][strlen(tokens[5]) - 1] == '"') tokens[5][strlen(tokens[5]) - 1] = '\0'; + strcpy(entry->author, tokens[5]); strcpy(entry->authorGitHub, tokens[6]); return 1; From 6ddc9251c209a67cc733a0c81d63d6f988a0fc32 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 3 Aug 2025 01:18:19 +0200 Subject: [PATCH 157/242] UPDATE: `README.md` example addition working --- examples/rexm.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 3 deletions(-) diff --git a/examples/rexm.c b/examples/rexm.c index db9792e92..19c6124b6 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -334,11 +334,96 @@ int main(int argc, char *argv[]) // Edit: raylib/examples/README.md --> Add new example //------------------------------------------------------------------------------------------------ - // TODO: Use [examples_list.txt] to update/regen README.md - //Look for "| 01 | " + // NOTE: Using [examples_list.txt] to update/regen README.md // Lines format: | 01 | [core_basic_window](core/core_basic_window.c) | core_basic_window | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | - + char *mdText = LoadFileText(TextFormat("%s/README.md", exBasePath)); + char *mdTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated examples.js copy, 2MB + int mdListStartIndex = TextFindIndex(mdText, "| 01 | "); + + int mdIndex = 0; + memcpy(mdTextUpdated, mdText, mdListStartIndex); + + // NOTE: We keep a global examples counter + for (int i = 0, catCount = 0, gCount = 0; i < MAX_EXAMPLE_CATEGORIES; i++) + { + // Every category includes some introductory text, as it is quite short, just copying it here + // NOTE: "core" text already placed in the file + if (i == 1) // "shapes" + { + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: shapes\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, + "Examples using raylib shapes drawing functionality, provided by raylib [shapes](../src/rshapes.c) module.\n\n"); + } + else if (i == 2) // "textures" + { + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: textures\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, + "Examples using raylib textures functionality, including image/textures loading/generation and drawing, provided by raylib [textures](../src/rtextures.c) module.\n\n"); + } + else if (i == 3) // "text" + { + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: text\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, + "Examples using raylib text functionality, including sprite fonts loading/generation and text drawing, provided by raylib [text](../src/rtext.c) module.\n\n"); + } + else if (i == 4) // "models" + { + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: models\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, + "Examples using raylib models functionality, including models loading/generation and drawing, provided by raylib [models](../src/rmodels.c) module.\n\n"); + } + else if (i == 5) // "shaders" + { + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: shaders\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, + "Examples using raylib shaders functionality, including shaders loading, parameters configuration and drawing using them (model shaders and postprocessing shaders). This functionality is directly provided by raylib [rlgl](../src/rlgl.c) module.\n\n"); + } + else if (i == 6) // "audio" + { + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: audio\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, + "Examples using raylib audio functionality, including sound/music loading and playing. This functionality is provided by raylib [raudio](../src/raudio.c) module. Note this module can be used standalone independently of raylib.\n\n"); + } + else if (i == 7) // "others" + { + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: others\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, + "Examples showing raylib misc functionality that does not fit in other categories, like standalone modules usage or examples integrating external libraries.\n\n"); + } + + if (i > 0) + { + // Table header required + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "| ## | example | image | difficulty
level | version
created | last version
updated | original
developer |\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "|----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------|\n"); + } + + rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], false, &catCount); + for (int x = 0; x < catCount; x++) + { + char stars[16] = { 0 }; + for (int s = 0; s < 4; s++) + { + if (s < exCatList[x].stars) strcpy(stars + 3*s, "⭐️"); + else strcpy(stars + 3*s, "☆"); + } + + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, + TextFormat("| %02i | [%s](%s/%s.c) | \"%s\" | %s | %.1f | %.1f | [%s](https://github.com/%s) |\n", + gCount + 1, exCatList[x].name, exCatList[x].category, exCatList[x].name, exCatList[x].category, exCatList[x].name, exCatList[x].name, + stars, exCatList[x].verCreated, exCatList[x].verUpdated, exCatList[x].author, exCatList[x].authorGitHub + 1)); + + gCount++; + } + + UnloadExamplesData(exCatList); + } + + // Save updated file + SaveFileText(TextFormat("%s/README.md", exBasePath), mdTextUpdated); + UnloadFileText(mdText); + RL_FREE(mdTextUpdated); //------------------------------------------------------------------------------------------------ // Create: raylib/projects/VS2022/examples/_example_name.vcxproj From c146be16cb9a5c93b0d80a1ffc2a312db2a8b696 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 3 Aug 2025 09:43:38 +0200 Subject: [PATCH 158/242] Update rexm.c --- examples/rexm.c | 89 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 60 insertions(+), 29 deletions(-) diff --git a/examples/rexm.c b/examples/rexm.c index 19c6124b6..6804546f5 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -223,6 +223,7 @@ int main(int argc, char *argv[]) char *exTextUpdated[6] = { 0 }; int exIndex = TextFindIndex(exText, "/****************"); + // Update required info with some defaults exTextUpdated[0] = TextReplace(exText + exIndex, "", exCategory); exTextUpdated[1] = TextReplace(exTextUpdated[0], "", exName + strlen(exCategory) + 1); //TextReplace(newExample, "", "Ray"); @@ -236,13 +237,21 @@ int main(int argc, char *argv[]) } case 2: // Add: Example from command-line input filename { - // Create: raylib/examples//_example_name.c + // Add: raylib/examples//_example_name.c if (opCode != 1) FileCopy(inFileName, TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); + // TODO: Example to be added could be provided as a .zip, containing resources! + // Create: raylib/examples//_example_name.png FileCopy(exTemplateScreenshot, TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName)); // WARNING: To be updated manually! // Copy: raylib/examples//resources/... // WARNING: To be updated manually! + + // TODO: Copy provided resources to respective directories + // Possible strategy: + // 1. Scan code file for resources paths -> Resources list + // 2. Verify paths: resource files exist + // 3. Copy files to required resource dir // Add example to the main collection list, if not already there // NOTE: Required format: shapes;shapes_basic_shapes;⭐️☆☆☆;1.0;4.2;"Ray";@raysan5 @@ -291,45 +300,72 @@ int main(int argc, char *argv[]) // Edit: raylib/examples/Makefile --> Add new example //------------------------------------------------------------------------------------------------ - /* char *mkText = LoadFileText(TextFormat("%s/Makefile", exBasePath)); char *mkTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated Makefile copy, 2MB - int exListStartIndex = TextFindIndex(mkText, "#EXAMPLES_LIST_START"); - int exListEndIndex = TextFindIndex(mkText, "#EXAMPLES_LIST_END"); + int mkListStartIndex = TextFindIndex(mkText, "#EXAMPLES_LIST_START"); + int mkListEndIndex = TextFindIndex(mkText, "#EXAMPLES_LIST_END"); int mkIndex = 0; - memcpy(mkTextUpdated, mkText, exListStartIndex); - mkIndex = sprintf(mkTextUpdated + exListStartIndex, "#EXAMPLES_LIST_START\n"); + memcpy(mkTextUpdated, mkText, mkListStartIndex); + mkIndex = sprintf(mkTextUpdated + mkListStartIndex, "#EXAMPLES_LIST_START\n"); for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++) { - mkIndex += sprintf(mkTextUpdated + exListStartIndex + mkIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i]))); + mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i]))); int exCount = 0; rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], true, &exCount); - for (int x = 0; x < exCount - 1; x++) mkIndex += sprintf(mkTextUpdated + exListStartIndex + mkIndex, TextFormat(" %s/%s \\\n", exCatList[x].category, exCatList[x].name)); - mkIndex += sprintf(mkTextUpdated + exListStartIndex + mkIndex, TextFormat(" %s/%s\n\n", exCatList[exCount - 1].category, exCatList[exCount - 1].name)); + for (int x = 0; x < exCount - 1; x++) mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat(" %s/%s \\\n", exCatList[x].category, exCatList[x].name)); + mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat(" %s/%s\n\n", exCatList[exCount - 1].category, exCatList[exCount - 1].name)); UnloadExamplesData(exCatList); } // Add the remaining part of the original file - memcpy(mkTextUpdated + exListStartIndex + mkIndex, mkText + exListEndIndex, strlen(mkText) - exListEndIndex); + memcpy(mkTextUpdated + mkListStartIndex + mkIndex, mkText + mkListEndIndex, strlen(mkText) - mkListEndIndex); // Save updated file SaveFileText(TextFormat("%s/Makefile", exBasePath), mkTextUpdated); UnloadFileText(mkText); RL_FREE(mkTextUpdated); - */ //------------------------------------------------------------------------------------------------ // Edit: raylib/examples/Makefile.Web --> Add new example //------------------------------------------------------------------------------------------------ - - // TODO. - + char *mkwText = LoadFileText(TextFormat("%s/Makefile.Web", exBasePath)); + char *mkwTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated Makefile copy, 2MB + + int mkwListStartIndex = TextFindIndex(mkwText, "#EXAMPLES_LIST_START"); + int mkwListEndIndex = TextFindIndex(mkwText, "#EXAMPLES_LIST_END"); + + int mkwIndex = 0; + memcpy(mkwTextUpdated, mkwText, mkwListStartIndex); + mkwIndex = sprintf(mkwTextUpdated + mkwListStartIndex, "#EXAMPLES_LIST_START\n"); + + for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++) + { + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i]))); + + int exCount = 0; + rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], true, &exCount); + + for (int x = 0; x < exCount - 1; x++) mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat(" %s/%s \\\n", exCatList[x].category, exCatList[x].name)); + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat(" %s/%s\n\n", exCatList[exCount - 1].category, exCatList[exCount - 1].name)); + + UnloadExamplesData(exCatList); + } + + // Add the remaining part of the original file + memcpy(mkwTextUpdated + mkwListStartIndex + mkwIndex, mkwText + mkwListEndIndex, strlen(mkwText) - mkwListEndIndex); + + // TODO: Add new example target, considering resources + + // Save updated file + SaveFileText(TextFormat("%s/Makefile.Web", exBasePath), mkwTextUpdated); + UnloadFileText(mkwText); + RL_FREE(mkwTextUpdated); //------------------------------------------------------------------------------------------------ // Edit: raylib/examples/README.md --> Add new example @@ -442,21 +478,16 @@ int main(int argc, char *argv[]) // Edit: raylib.com/common/examples.js --> Add new example // NOTE: Entries format: exampleEntry('⭐️☆☆☆' , 'core' , 'basic_window'), //------------------------------------------------------------------------------------------------ - /* char *jsText = LoadFileText(TextFormat("%s/../common/examples.js", exWebPath)); char *jsTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated examples.js copy, 2MB - int exListStartIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_START"); - int exListEndIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_END"); - - int mkIndex = 0; - memcpy(jsTextUpdated, jsText, exListStartIndex); - mkIndex = sprintf(jsTextUpdated + exListStartIndex, "#EXAMPLES_LIST_START\n"); + int jsListStartIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_START"); + int jsListEndIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_END"); int jsIndex = 0; - memcpy(jsTextUpdated, jsText, exListStartIndex); - jsIndex = sprintf(jsTextUpdated + exListStartIndex, "//EXAMPLE_DATA_LIST_START\n"); - jsIndex += sprintf(jsTextUpdated + exListStartIndex + jsIndex, " var exampleData = [\n"); + memcpy(jsTextUpdated, jsText, jsListStartIndex); + jsIndex = sprintf(jsTextUpdated + jsListStartIndex, "//EXAMPLE_DATA_LIST_START\n"); + jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, " var exampleData = [\n"); // NOTE: We avoid "others" category for (int i = 0, exCount = 0; i < MAX_EXAMPLE_CATEGORIES - 1; i++) @@ -474,12 +505,12 @@ int main(int argc, char *argv[]) if ((i == 6) && (x == (exCount - 1))) { // Last line to add, special case to consider - jsIndex += sprintf(jsTextUpdated + exListStartIndex + jsIndex, + jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, TextFormat(" exampleEntry('%s', '%s', '%s')];\n", stars, exCatList[x].category, exCatList[x].name + strlen(exCatList[x].category) + 1)); } else { - jsIndex += sprintf(jsTextUpdated + exListStartIndex + jsIndex, + jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, TextFormat(" exampleEntry('%s', '%s', '%s'),\n", stars, exCatList[x].category, exCatList[x].name + strlen(exCatList[x].category) + 1)); } } @@ -488,13 +519,12 @@ int main(int argc, char *argv[]) } // Add the remaining part of the original file - memcpy(jsTextUpdated + exListStartIndex + jsIndex, jsText + exListEndIndex, strlen(jsText) - exListEndIndex); + memcpy(jsTextUpdated + jsListStartIndex + jsIndex, jsText + jsListEndIndex, strlen(jsText) - jsListEndIndex); // Save updated file SaveFileText(TextFormat("%s/../common/examples.js", exWebPath), jsTextUpdated); UnloadFileText(jsText); RL_FREE(jsTextUpdated); - */ //------------------------------------------------------------------------------------------------ // Recompile example (on raylib side) @@ -542,7 +572,8 @@ int main(int argc, char *argv[]) // Recompile example (on raylib side) // NOTE: Tools requirements: emscripten, w64devkit - system(TextFormat("%s/../build_example_web.bat %s/%s", exBasePath, exCategory, exName)); + // TODO: Avoid platform-specific .BAT file + system(TextFormat("%s/build_example_web.bat %s/%s", exBasePath, exCategory, exName)); // Copy results to web side FileCopy(TextFormat("%s/%s/%s.html", exBasePath, exCategory, exName), From 603219d6d644993bc21af0bd1ceb1b18451daf4b Mon Sep 17 00:00:00 2001 From: Karim Ahmed Date: Sun, 3 Aug 2025 18:48:24 +0100 Subject: [PATCH 159/242] Removed verbose logging on SDL3. Happens every time the property is read! --- src/platforms/rcore_desktop_sdl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 82337dd4e..6126b80d1 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1104,8 +1104,7 @@ Vector2 GetWindowScaleDPI(void) TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform"); #else scale.x = SDL_GetWindowDisplayScale(platform.window); - scale.y = scale.x; - TRACELOG(LOG_INFO, "WindowScaleDPI is %f", scale.x); + scale.y = scale.x; #endif return scale; From 8f8a5ada60868953d0f9e911ead91bddca2a9f70 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 3 Aug 2025 21:19:55 +0200 Subject: [PATCH 160/242] REVIEWED: Make sure environment is set in same process and not a child one --- examples/build_example_web.bat | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/build_example_web.bat b/examples/build_example_web.bat index f09cbcc0b..0fcdc6413 100644 --- a/examples/build_example_web.bat +++ b/examples/build_example_web.bat @@ -33,11 +33,11 @@ cmd /c if exist %FILENAME%.data del /F %FILENAME%.data echo :: Setup emsdk environment :: -------------------------- -cmd /c %EMSDK_PATH%\emsdk_env.bat -echo +call %EMSDK_PATH%\emsdk_env.bat +echo on :: Compile program :: ----------------------- -cmd /c %CC% --version -cmd /c %CC% -o %FILENAME%.html %FILENAME%.c %CFLAGS% %LDFLAGS% %LDLIBS% %RESOURCES% +C: +%CC% -o %FILENAME%.html %FILENAME%.c %CFLAGS% %LDFLAGS% %LDLIBS% %RESOURCES% cd .. echo From d194b8d5036270d03d1a093a9e33e7282dfbb8cc Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 3 Aug 2025 21:20:35 +0200 Subject: [PATCH 161/242] ADDED: Some security checks to verify examples categories provided --- examples/rexm.c | 108 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 84 insertions(+), 24 deletions(-) diff --git a/examples/rexm.c b/examples/rexm.c index 6804546f5..c090b69f9 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -152,11 +152,27 @@ int main(int argc, char *argv[]) else if (argc > 3) LOG("WARNING: Too many arguments provided\n"); else { - // TODO: Additional security checks for file name? + // Security checks for file name to verify category is included + int catIndex = TextFindIndex(argv[2], "_"); + if (catIndex > 3) + { + char cat[12] = { 0 }; + strncpy(cat, argv[2], catIndex); + bool catFound = false; + for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++) + { + if (TextIsEqual(cat, exCategories[i])) { catFound = true; break; } + } - strcpy(exName, argv[2]); // Register filename for new example creation - strncpy(exCategory, exName, TextFindIndex(exName, "_")); - opCode = 1; + if (catFound) + { + strcpy(exName, argv[2]); // Register filename for new example creation + strncpy(exCategory, exName, TextFindIndex(exName, "_")); + opCode = OP_CREATE; + } + else LOG("WARNING: Example category is not valid\n"); + } + else LOG("WARNING: Example name does not include category\n"); } } else if (strcmp(argv[1], "add") == 0) @@ -170,10 +186,28 @@ int main(int argc, char *argv[]) { if (FileExists(inFileName)) { - strcpy(inFileName, argv[2]); // Register filename for addition - strcpy(exName, GetFileNameWithoutExt(argv[2])); // Register example name - strncpy(exCategory, exName, TextFindIndex(exName, "_")); - opCode = 2; + // Security checks for file name to verify category is included + int catIndex = TextFindIndex(argv[2], "_"); + if (catIndex > 3) + { + char cat[12] = { 0 }; + strncpy(cat, argv[2], catIndex); + bool catFound = false; + for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++) + { + if (TextIsEqual(cat, exCategories[i])) { catFound = true; break; } + } + + if (catFound) + { + strcpy(inFileName, argv[2]); // Register filename for addition + strcpy(exName, GetFileNameWithoutExt(argv[2])); // Register example name + strncpy(exCategory, exName, TextFindIndex(exName, "_")); + opCode = OP_ADD; + } + else LOG("WARNING: Example category is not valid\n"); + } + else LOG("WARNING: Example name does not include category\n"); } else LOG("WARNING: Input file not found, include path\n"); } @@ -186,11 +220,27 @@ int main(int argc, char *argv[]) else if (argc > 4) LOG("WARNING: Too many arguments provided\n"); else { - strcpy(exName, argv[2]); // Register example name - strncpy(exCategory, exName, TextFindIndex(exName, "_")); - strcpy(exRename, argv[3]); - // TODO: Consider rename with change of category - opCode = 3; + // Verify example exists in collection to be removed + char *exColInfo = LoadFileText(exCollectionListPath); + if (TextFindIndex(exColInfo, argv[2]) != -1) // Example in the collection + { + strcpy(exName, argv[2]); // Register example name + strncpy(exCategory, exName, TextFindIndex(exName, "_")); + strcpy(exRename, argv[3]); + char exReCategory[32] = { 0 }; + strncpy(exReCategory, exRename, TextFindIndex(exRename, "_")); + + if (strcmp(exCategory, exReCategory) != 0) + { + // TODO: Consider rename with change of category + // Remove previous one from collection + // Add new one (copy) to collection + } + + opCode = OP_RENAME; + } + else LOG("WARNING: RENAME: Example not available in the collection\n"); + UnloadFileText(exColInfo); } } else if (strcmp(argv[1], "remove") == 0) @@ -200,20 +250,26 @@ int main(int argc, char *argv[]) else if (argc > 3) LOG("WARNING: Too many arguments provided\n"); else { - strcpy(exName, argv[2]); // Register filename for removal - opCode = 4; + // Verify example exists in collection to be removed + char *exColInfo = LoadFileText(exCollectionListPath); + if (TextFindIndex(exColInfo, argv[2]) != -1) // Example in the collection + { + strcpy(exName, argv[2]); // Register filename for removal + opCode = OP_REMOVE; + } + else LOG("WARNING: REMOVE: Example not available in the collection\n"); + UnloadFileText(exColInfo); } } else if (strcmp(argv[1], "validate") == 0) { - opCode = 5; + // Validate examples in collection + // All examples in collection match all requirements on required files + + opCode = OP_VALIDATE; } } - // Load examples collection information - //exInfo = LoadExamplesData(exCollectionListPath, "core", true, &exInfoCount); - //for (int i = 0; i < exInfoCount; i++) printf("%i - %s [%i]\n", i + 1, exInfo[i].name, exInfo[i].stars); - switch (opCode) { case 1: // Create: New example from template @@ -240,16 +296,18 @@ int main(int argc, char *argv[]) // Add: raylib/examples//_example_name.c if (opCode != 1) FileCopy(inFileName, TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); - // TODO: Example to be added could be provided as a .zip, containing resources! - // Create: raylib/examples//_example_name.png FileCopy(exTemplateScreenshot, TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName)); // WARNING: To be updated manually! // Copy: raylib/examples//resources/... // WARNING: To be updated manually! + // TODO: Example to be added could be provided as a .zip, containing resources! // TODO: Copy provided resources to respective directories // Possible strategy: // 1. Scan code file for resources paths -> Resources list + // Look for specific text: '.png"' + // Look for full path, previous '"' + // Be careful with shaders: '.vs"', '.fs"' -> Reconstruct path manually? // 2. Verify paths: resource files exist // 3. Copy files to required resource dir @@ -295,6 +353,8 @@ int main(int argc, char *argv[]) SaveFileText(exCollectionListPath, exColInfoUpdated); RL_FREE(exColInfoUpdated); } + else LOG("WARNING: ADD: Example is already on the collection\n"); + UnloadFileText(exColInfo); //------------------------------------------------------------------------------------------------ @@ -374,7 +434,7 @@ int main(int argc, char *argv[]) // Lines format: | 01 | [core_basic_window](core/core_basic_window.c) | core_basic_window | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | char *mdText = LoadFileText(TextFormat("%s/README.md", exBasePath)); char *mdTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated examples.js copy, 2MB - + int mdListStartIndex = TextFindIndex(mdText, "| 01 | "); int mdIndex = 0; @@ -504,7 +564,7 @@ int main(int argc, char *argv[]) if ((i == 6) && (x == (exCount - 1))) { - // Last line to add, special case to consider + // NOTE: Last line to add, special case to consider jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, TextFormat(" exampleEntry('%s', '%s', '%s')];\n", stars, exCatList[x].category, exCatList[x].name + strlen(exCatList[x].category) + 1)); } From b11d0892028fc3ef28b0914ab43d3cb14577cfd9 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 3 Aug 2025 22:56:15 +0200 Subject: [PATCH 162/242] UPDATED: Implementing file rename requirements Moved files update from collection to separate function --- examples/rexm.c | 597 ++++++++++++++++++++++++++++-------------------- 1 file changed, 345 insertions(+), 252 deletions(-) diff --git a/examples/rexm.c b/examples/rexm.c index c090b69f9..88dd22cc8 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -86,6 +86,14 @@ typedef enum { static const char *exCategories[MAX_EXAMPLE_CATEGORIES] = { "core", "shapes", "textures", "text", "models", "shaders", "audio", "others" }; +// Paths required for examples management +// TODO: Avoid hardcoding path values... +static const char *exBasePath = "C:/GitHub/raylib/examples"; +static const char *exWebPath = "C:/GitHub/raylib.com/examples"; +static const char *exTemplateFilePath = "C:/GitHub/raylib/examples/examples_template.c"; +static const char *exTemplateScreenshot = "C:/GitHub/raylib/examples/examples_template.png"; +static const char *exCollectionListPath = "C:/GitHub/raylib/examples/examples_list.txt"; + //---------------------------------------------------------------------------------- // Module specific functions declaration //---------------------------------------------------------------------------------- @@ -94,6 +102,10 @@ static int FileCopy(const char *srcPath, const char *dstPath); static int FileRename(const char *fileName, const char *fileRename); static int FileRemove(const char *fileName); +// Update required files from examples collection +// UPDATES: Makefile, Makefile.Web, README.md, examples.js +static int UpdateRequiredFiles(void); + // Load examples collection information // NOTE 1: Load by category: "ALL", "core", "shapes", "textures", "text", "models", "shaders", others" // NOTE 2: Sort examples list on request flag @@ -118,18 +130,11 @@ static void SortExampleByName(rlExampleInfo *items, int count); //------------------------------------------------------------------------------------ int main(int argc, char *argv[]) { - // Paths required for examples management - // TODO: Avoid hardcoding path values... - char *exBasePath = "C:/GitHub/raylib/examples"; - char *exWebPath = "C:/GitHub/raylib.com/examples"; - char *exTemplateFilePath = "C:/GitHub/raylib/examples/examples_template.c"; - char *exTemplateScreenshot = "C:/GitHub/raylib/examples/examples_template.png"; - char *exCollectionListPath = "C:/GitHub/raylib/examples/examples_list.txt"; - char inFileName[1024] = { 0 }; // Example input filename (to be added) char exName[64] = { 0 }; // Example name, without extension: core_basic_window char exCategory[32] = { 0 }; // Example category: core + char exRecategory[32] = { 0 }; // Example re-name category: shapes char exRename[64] = { 0 }; // Example re-name, without extension int opCode = OP_NONE; // Operation code: 0-None(Help), 1-Create, 2-Add, 3-Rename, 4-Remove @@ -227,16 +232,7 @@ int main(int argc, char *argv[]) strcpy(exName, argv[2]); // Register example name strncpy(exCategory, exName, TextFindIndex(exName, "_")); strcpy(exRename, argv[3]); - char exReCategory[32] = { 0 }; - strncpy(exReCategory, exRename, TextFindIndex(exRename, "_")); - - if (strcmp(exCategory, exReCategory) != 0) - { - // TODO: Consider rename with change of category - // Remove previous one from collection - // Add new one (copy) to collection - } - + strncpy(exRecategory, exRename, TextFindIndex(exRename, "_")); opCode = OP_RENAME; } else LOG("WARNING: RENAME: Example not available in the collection\n"); @@ -358,168 +354,9 @@ int main(int argc, char *argv[]) UnloadFileText(exColInfo); //------------------------------------------------------------------------------------------------ - // Edit: raylib/examples/Makefile --> Add new example + // Update: Makefile, Makefile.Web, README.md, examples.js //------------------------------------------------------------------------------------------------ - char *mkText = LoadFileText(TextFormat("%s/Makefile", exBasePath)); - char *mkTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated Makefile copy, 2MB - - int mkListStartIndex = TextFindIndex(mkText, "#EXAMPLES_LIST_START"); - int mkListEndIndex = TextFindIndex(mkText, "#EXAMPLES_LIST_END"); - - int mkIndex = 0; - memcpy(mkTextUpdated, mkText, mkListStartIndex); - mkIndex = sprintf(mkTextUpdated + mkListStartIndex, "#EXAMPLES_LIST_START\n"); - - for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++) - { - mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i]))); - - int exCount = 0; - rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], true, &exCount); - - for (int x = 0; x < exCount - 1; x++) mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat(" %s/%s \\\n", exCatList[x].category, exCatList[x].name)); - mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat(" %s/%s\n\n", exCatList[exCount - 1].category, exCatList[exCount - 1].name)); - - UnloadExamplesData(exCatList); - } - - // Add the remaining part of the original file - memcpy(mkTextUpdated + mkListStartIndex + mkIndex, mkText + mkListEndIndex, strlen(mkText) - mkListEndIndex); - - // Save updated file - SaveFileText(TextFormat("%s/Makefile", exBasePath), mkTextUpdated); - UnloadFileText(mkText); - RL_FREE(mkTextUpdated); - //------------------------------------------------------------------------------------------------ - - // Edit: raylib/examples/Makefile.Web --> Add new example - //------------------------------------------------------------------------------------------------ - char *mkwText = LoadFileText(TextFormat("%s/Makefile.Web", exBasePath)); - char *mkwTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated Makefile copy, 2MB - - int mkwListStartIndex = TextFindIndex(mkwText, "#EXAMPLES_LIST_START"); - int mkwListEndIndex = TextFindIndex(mkwText, "#EXAMPLES_LIST_END"); - - int mkwIndex = 0; - memcpy(mkwTextUpdated, mkwText, mkwListStartIndex); - mkwIndex = sprintf(mkwTextUpdated + mkwListStartIndex, "#EXAMPLES_LIST_START\n"); - - for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++) - { - mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i]))); - - int exCount = 0; - rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], true, &exCount); - - for (int x = 0; x < exCount - 1; x++) mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat(" %s/%s \\\n", exCatList[x].category, exCatList[x].name)); - mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat(" %s/%s\n\n", exCatList[exCount - 1].category, exCatList[exCount - 1].name)); - - UnloadExamplesData(exCatList); - } - - // Add the remaining part of the original file - memcpy(mkwTextUpdated + mkwListStartIndex + mkwIndex, mkwText + mkwListEndIndex, strlen(mkwText) - mkwListEndIndex); - - // TODO: Add new example target, considering resources - - // Save updated file - SaveFileText(TextFormat("%s/Makefile.Web", exBasePath), mkwTextUpdated); - UnloadFileText(mkwText); - RL_FREE(mkwTextUpdated); - //------------------------------------------------------------------------------------------------ - - // Edit: raylib/examples/README.md --> Add new example - //------------------------------------------------------------------------------------------------ - // NOTE: Using [examples_list.txt] to update/regen README.md - // Lines format: | 01 | [core_basic_window](core/core_basic_window.c) | core_basic_window | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | - char *mdText = LoadFileText(TextFormat("%s/README.md", exBasePath)); - char *mdTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated examples.js copy, 2MB - - int mdListStartIndex = TextFindIndex(mdText, "| 01 | "); - - int mdIndex = 0; - memcpy(mdTextUpdated, mdText, mdListStartIndex); - - // NOTE: We keep a global examples counter - for (int i = 0, catCount = 0, gCount = 0; i < MAX_EXAMPLE_CATEGORIES; i++) - { - // Every category includes some introductory text, as it is quite short, just copying it here - // NOTE: "core" text already placed in the file - if (i == 1) // "shapes" - { - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: shapes\n\n"); - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, - "Examples using raylib shapes drawing functionality, provided by raylib [shapes](../src/rshapes.c) module.\n\n"); - } - else if (i == 2) // "textures" - { - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: textures\n\n"); - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, - "Examples using raylib textures functionality, including image/textures loading/generation and drawing, provided by raylib [textures](../src/rtextures.c) module.\n\n"); - } - else if (i == 3) // "text" - { - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: text\n\n"); - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, - "Examples using raylib text functionality, including sprite fonts loading/generation and text drawing, provided by raylib [text](../src/rtext.c) module.\n\n"); - } - else if (i == 4) // "models" - { - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: models\n\n"); - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, - "Examples using raylib models functionality, including models loading/generation and drawing, provided by raylib [models](../src/rmodels.c) module.\n\n"); - } - else if (i == 5) // "shaders" - { - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: shaders\n\n"); - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, - "Examples using raylib shaders functionality, including shaders loading, parameters configuration and drawing using them (model shaders and postprocessing shaders). This functionality is directly provided by raylib [rlgl](../src/rlgl.c) module.\n\n"); - } - else if (i == 6) // "audio" - { - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: audio\n\n"); - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, - "Examples using raylib audio functionality, including sound/music loading and playing. This functionality is provided by raylib [raudio](../src/raudio.c) module. Note this module can be used standalone independently of raylib.\n\n"); - } - else if (i == 7) // "others" - { - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: others\n\n"); - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, - "Examples showing raylib misc functionality that does not fit in other categories, like standalone modules usage or examples integrating external libraries.\n\n"); - } - - if (i > 0) - { - // Table header required - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "| ## | example | image | difficulty
level | version
created | last version
updated | original
developer |\n"); - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "|----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------|\n"); - } - - rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], false, &catCount); - for (int x = 0; x < catCount; x++) - { - char stars[16] = { 0 }; - for (int s = 0; s < 4; s++) - { - if (s < exCatList[x].stars) strcpy(stars + 3*s, "⭐️"); - else strcpy(stars + 3*s, "☆"); - } - - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, - TextFormat("| %02i | [%s](%s/%s.c) | \"%s\" | %s | %.1f | %.1f | [%s](https://github.com/%s) |\n", - gCount + 1, exCatList[x].name, exCatList[x].category, exCatList[x].name, exCatList[x].category, exCatList[x].name, exCatList[x].name, - stars, exCatList[x].verCreated, exCatList[x].verUpdated, exCatList[x].author, exCatList[x].authorGitHub + 1)); - - gCount++; - } - - UnloadExamplesData(exCatList); - } - - // Save updated file - SaveFileText(TextFormat("%s/README.md", exBasePath), mdTextUpdated); - UnloadFileText(mdText); - RL_FREE(mdTextUpdated); + UpdateRequiredFiles(); //------------------------------------------------------------------------------------------------ // Create: raylib/projects/VS2022/examples/_example_name.vcxproj @@ -534,58 +371,6 @@ int main(int argc, char *argv[]) // Edit: raylib/projects/VS2022/raylib.sln --> Add new example project system(TextFormat("dotnet solution %s/../projects/VS2022/raylib.sln add %s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exBasePath, exName)); //------------------------------------------------------------------------------------------------ - - // Edit: raylib.com/common/examples.js --> Add new example - // NOTE: Entries format: exampleEntry('⭐️☆☆☆' , 'core' , 'basic_window'), - //------------------------------------------------------------------------------------------------ - char *jsText = LoadFileText(TextFormat("%s/../common/examples.js", exWebPath)); - char *jsTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated examples.js copy, 2MB - - int jsListStartIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_START"); - int jsListEndIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_END"); - - int jsIndex = 0; - memcpy(jsTextUpdated, jsText, jsListStartIndex); - jsIndex = sprintf(jsTextUpdated + jsListStartIndex, "//EXAMPLE_DATA_LIST_START\n"); - jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, " var exampleData = [\n"); - - // NOTE: We avoid "others" category - for (int i = 0, exCount = 0; i < MAX_EXAMPLE_CATEGORIES - 1; i++) - { - rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], false, &exCount); - for (int x = 0; x < exCount; x++) - { - char stars[16] = { 0 }; - for (int s = 0; s < 4; s++) - { - if (s < exCatList[x].stars) strcpy(stars + 3*s, "⭐️"); - else strcpy(stars + 3*s, "☆"); - } - - if ((i == 6) && (x == (exCount - 1))) - { - // NOTE: Last line to add, special case to consider - jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, - TextFormat(" exampleEntry('%s', '%s', '%s')];\n", stars, exCatList[x].category, exCatList[x].name + strlen(exCatList[x].category) + 1)); - } - else - { - jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, - TextFormat(" exampleEntry('%s', '%s', '%s'),\n", stars, exCatList[x].category, exCatList[x].name + strlen(exCatList[x].category) + 1)); - } - } - - UnloadExamplesData(exCatList); - } - - // Add the remaining part of the original file - memcpy(jsTextUpdated + jsListStartIndex + jsIndex, jsText + jsListEndIndex, strlen(jsText) - jsListEndIndex); - - // Save updated file - SaveFileText(TextFormat("%s/../common/examples.js", exWebPath), jsTextUpdated); - UnloadFileText(jsText); - RL_FREE(jsTextUpdated); - //------------------------------------------------------------------------------------------------ // Recompile example (on raylib side) // NOTE: Tools requirements: emscripten, w64devkit @@ -593,6 +378,7 @@ int main(int argc, char *argv[]) // Compile to: raylib.com/examples//_example_name.data // Compile to: raylib.com/examples//_example_name.wasm // Compile to: raylib.com/examples//_example_name.js + //------------------------------------------------------------------------------------------------ // TODO: WARNING: This .BAT is not portable and it does not consider RESOURCES for Web properly, // Makefile.Web should be used... but it requires proper editing first! system(TextFormat("%s/build_example_web.bat %s/%s", exBasePath, exCategory, exName)); @@ -606,25 +392,56 @@ int main(int argc, char *argv[]) TextFormat("%s/%s/%s.wasm", exWebPath, exCategory, exName)); FileCopy(TextFormat("%s/%s/%s.js", exBasePath, exCategory, exName), TextFormat("%s/%s/%s.js", exWebPath, exCategory, exName)); + //------------------------------------------------------------------------------------------------ + } break; case 3: // Rename { - // Rename all required files - rename(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), - TextFormat("%s/%s/%s.c", exBasePath, exCategory, exRename)); - rename(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName), - TextFormat("%s/%s/%s.png", exBasePath, exCategory, exRename)); + // NOTE: At this point provided values have been validated: + // exName, exCategory, exRename, exRecategory + if (strcmp(exCategory, exRecategory) == 0) + { + // Rename example on collection + FileTextReplace(exCollectionListPath, TextFormat("%s;%s", exCategory, exName), + TextFormat("%s;%s", exRecategory, exRename)); - FileTextReplace(TextFormat("%s/Makefile", exBasePath), exName, exRename); - FileTextReplace(TextFormat("%s/Makefile.Web", exBasePath), exName, exRename); - FileTextReplace(TextFormat("%s/README.md", exBasePath), exName, exRename); + // Rename all required files + rename(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), + TextFormat("%s/%s/%s.c", exBasePath, exCategory, exRename)); + rename(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName), + TextFormat("%s/%s/%s.png", exBasePath, exCategory, exRename)); - rename(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName), - TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exRename)); - FileTextReplace(TextFormat("%s/../projects/VS2022/raylib.sln", exBasePath), exName, exRename); + // Rename example on required files + FileTextReplace(TextFormat("%s/Makefile", exBasePath), exName, exRename); + FileTextReplace(TextFormat("%s/Makefile.Web", exBasePath), exName, exRename); + FileTextReplace(TextFormat("%s/README.md", exBasePath), exName, exRename); + FileTextReplace(TextFormat("%s/../common/examples.js", exWebPath), exName, exRename); + + // Rename example project and solution + rename(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName), + TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exRename)); + FileTextReplace(TextFormat("%s/../projects/VS2022/raylib.sln", exBasePath), exName, exRename); + } + else + { + // Rename with change of category + // TODO: Reorder collection as required + FileTextReplace(exCollectionListPath, TextFormat("%s;%s", exCategory, exName), + TextFormat("%s;%s", exRecategory, exRename)); + + // Rename all required files + FileCopy(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), + TextFormat("%s/%s/%s.c", exBasePath, exCategory, exRename)); + remove(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); + + FileCopy(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName), + TextFormat("%s/%s/%s.png", exBasePath, exCategory, exRename)); + remove(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName)); + + UpdateRequiredFiles(); + } // Remove old web compilation - FileTextReplace(TextFormat("%s/../common/examples.js", exWebPath), exName, exRename); remove(TextFormat("%s/%s/%s.html", exWebPath, exCategory, exName)); remove(TextFormat("%s/%s/%s.data", exWebPath, exCategory, exName)); remove(TextFormat("%s/%s/%s.wasm", exWebPath, exCategory, exName)); @@ -633,22 +450,72 @@ int main(int argc, char *argv[]) // Recompile example (on raylib side) // NOTE: Tools requirements: emscripten, w64devkit // TODO: Avoid platform-specific .BAT file - system(TextFormat("%s/build_example_web.bat %s/%s", exBasePath, exCategory, exName)); + system(TextFormat("%s/build_example_web.bat %s/%s", exBasePath, exRecategory, exRename)); // Copy results to web side - FileCopy(TextFormat("%s/%s/%s.html", exBasePath, exCategory, exName), - TextFormat("%s/%s/%s.html", exWebPath, exCategory, exName)); - FileCopy(TextFormat("%s/%s/%s.data", exBasePath, exCategory, exName), - TextFormat("%s/%s/%s.data", exWebPath, exCategory, exName)); - FileCopy(TextFormat("%s/%s/%s.wasm", exBasePath, exCategory, exName), - TextFormat("%s/%s/%s.wasm", exWebPath, exCategory, exName)); - FileCopy(TextFormat("%s/%s/%s.js", exBasePath, exCategory, exName), - TextFormat("%s/%s/%s.js", exWebPath, exCategory, exName)); + FileCopy(TextFormat("%s/%s/%s.html", exBasePath, exRecategory, exRename), + TextFormat("%s/%s/%s.html", exWebPath, exRecategory, exRename)); + FileCopy(TextFormat("%s/%s/%s.data", exBasePath, exRecategory, exRename), + TextFormat("%s/%s/%s.data", exWebPath, exRecategory, exRename)); + FileCopy(TextFormat("%s/%s/%s.wasm", exBasePath, exRecategory, exRename), + TextFormat("%s/%s/%s.wasm", exWebPath, exRecategory, exRename)); + FileCopy(TextFormat("%s/%s/%s.js", exBasePath, exRecategory, exRename), + TextFormat("%s/%s/%s.js", exWebPath, exRecategory, exRename)); + } break; case 4: // Remove { // TODO: Remove and update all required files... + // Remove: raylib/examples//_example_name.c + // Remove: raylib/examples//_example_name.png + remove(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); + remove(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName)); + + // TODO: Remove: raylib/examples//resources/.. + + // Edit: raylib/examples/Makefile + //--------------------------------------------------------------------------- + // + // + //--------------------------------------------------------------------------- + + // Edit: raylib/examples/Makefile.Web + //--------------------------------------------------------------------------- + // + // + //--------------------------------------------------------------------------- + + // Edit: raylib/examples/README.md + //--------------------------------------------------------------------------- + // + // + //--------------------------------------------------------------------------- + + // Remove: raylib/projects/VS2022/examples/_example_name.vcxproj + remove(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName)); + + // Edit: raylib/projects/VS2022/raylib.sln + //--------------------------------------------------------------------------- + // + // + //--------------------------------------------------------------------------- + + // Edit: raylib.com/common/examples.js + //--------------------------------------------------------------------------- + + + //--------------------------------------------------------------------------- + + // Remove: raylib.com/examples//_example_name.html + // Remove: raylib.com/examples//_example_name.data + // Remove: raylib.com/examples//_example_name.wasm + // Remove: raylib.com/examples//_example_name.js + remove(TextFormat("%s/%s/%s.html", exWebPath, exCategory, exName)); + remove(TextFormat("%s/%s/%s.data", exWebPath, exCategory, exName)); + remove(TextFormat("%s/%s/%s.wasm", exWebPath, exCategory, exName)); + remove(TextFormat("%s/%s/%s.js", exWebPath, exCategory, exName)); + } break; case 5: // Validate { @@ -711,6 +578,232 @@ int main(int argc, char *argv[]) //---------------------------------------------------------------------------------- // Module specific functions definition //---------------------------------------------------------------------------------- + +// Update required files from examples collection +static int UpdateRequiredFiles(void) +{ + int result = 0; + + // Edit: raylib/examples/Makefile --> Update from collection + //------------------------------------------------------------------------------------------------ + char *mkText = LoadFileText(TextFormat("%s/Makefile", exBasePath)); + char *mkTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated Makefile copy, 2MB + + int mkListStartIndex = TextFindIndex(mkText, "#EXAMPLES_LIST_START"); + int mkListEndIndex = TextFindIndex(mkText, "#EXAMPLES_LIST_END"); + + int mkIndex = 0; + memcpy(mkTextUpdated, mkText, mkListStartIndex); + mkIndex = sprintf(mkTextUpdated + mkListStartIndex, "#EXAMPLES_LIST_START\n"); + + for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++) + { + mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i]))); + + int exCount = 0; + rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], true, &exCount); + + for (int x = 0; x < exCount - 1; x++) mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat(" %s/%s \\\n", exCatList[x].category, exCatList[x].name)); + mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat(" %s/%s\n\n", exCatList[exCount - 1].category, exCatList[exCount - 1].name)); + + UnloadExamplesData(exCatList); + } + + // Add the remaining part of the original file + memcpy(mkTextUpdated + mkListStartIndex + mkIndex, mkText + mkListEndIndex, strlen(mkText) - mkListEndIndex); + + // Save updated file + SaveFileText(TextFormat("%s/Makefile", exBasePath), mkTextUpdated); + UnloadFileText(mkText); + RL_FREE(mkTextUpdated); + //------------------------------------------------------------------------------------------------ + + // Edit: raylib/examples/Makefile.Web --> Update from collection + //------------------------------------------------------------------------------------------------ + char *mkwText = LoadFileText(TextFormat("%s/Makefile.Web", exBasePath)); + char *mkwTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated Makefile copy, 2MB + + int mkwListStartIndex = TextFindIndex(mkwText, "#EXAMPLES_LIST_START"); + int mkwListEndIndex = TextFindIndex(mkwText, "#EXAMPLES_LIST_END"); + + int mkwIndex = 0; + memcpy(mkwTextUpdated, mkwText, mkwListStartIndex); + mkwIndex = sprintf(mkwTextUpdated + mkwListStartIndex, "#EXAMPLES_LIST_START\n"); + + for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++) + { + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i]))); + + int exCount = 0; + rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], true, &exCount); + + for (int x = 0; x < exCount - 1; x++) mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat(" %s/%s \\\n", exCatList[x].category, exCatList[x].name)); + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat(" %s/%s\n\n", exCatList[exCount - 1].category, exCatList[exCount - 1].name)); + + UnloadExamplesData(exCatList); + } + + // Add the remaining part of the original file + memcpy(mkwTextUpdated + mkwListStartIndex + mkwIndex, mkwText + mkwListEndIndex, strlen(mkwText) - mkwListEndIndex); + + // TODO: Add new example target, considering resources + + // Save updated file + SaveFileText(TextFormat("%s/Makefile.Web", exBasePath), mkwTextUpdated); + UnloadFileText(mkwText); + RL_FREE(mkwTextUpdated); + //------------------------------------------------------------------------------------------------ + + // Edit: raylib/examples/README.md --> Update from collection + //------------------------------------------------------------------------------------------------ + // NOTE: Using [examples_list.txt] to update/regen README.md + // Lines format: | 01 | [core_basic_window](core/core_basic_window.c) | core_basic_window | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | + char *mdText = LoadFileText(TextFormat("%s/README.md", exBasePath)); + char *mdTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated examples.js copy, 2MB + + int mdListStartIndex = TextFindIndex(mdText, "| 01 | "); + + int mdIndex = 0; + memcpy(mdTextUpdated, mdText, mdListStartIndex); + + // NOTE: We keep a global examples counter + for (int i = 0, catCount = 0, gCount = 0; i < MAX_EXAMPLE_CATEGORIES; i++) + { + // Every category includes some introductory text, as it is quite short, just copying it here + // NOTE: "core" text already placed in the file + if (i == 1) // "shapes" + { + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: shapes\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, + "Examples using raylib shapes drawing functionality, provided by raylib [shapes](../src/rshapes.c) module.\n\n"); + } + else if (i == 2) // "textures" + { + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: textures\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, + "Examples using raylib textures functionality, including image/textures loading/generation and drawing, provided by raylib [textures](../src/rtextures.c) module.\n\n"); + } + else if (i == 3) // "text" + { + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: text\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, + "Examples using raylib text functionality, including sprite fonts loading/generation and text drawing, provided by raylib [text](../src/rtext.c) module.\n\n"); + } + else if (i == 4) // "models" + { + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: models\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, + "Examples using raylib models functionality, including models loading/generation and drawing, provided by raylib [models](../src/rmodels.c) module.\n\n"); + } + else if (i == 5) // "shaders" + { + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: shaders\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, + "Examples using raylib shaders functionality, including shaders loading, parameters configuration and drawing using them (model shaders and postprocessing shaders). This functionality is directly provided by raylib [rlgl](../src/rlgl.c) module.\n\n"); + } + else if (i == 6) // "audio" + { + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: audio\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, + "Examples using raylib audio functionality, including sound/music loading and playing. This functionality is provided by raylib [raudio](../src/raudio.c) module. Note this module can be used standalone independently of raylib.\n\n"); + } + else if (i == 7) // "others" + { + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: others\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, + "Examples showing raylib misc functionality that does not fit in other categories, like standalone modules usage or examples integrating external libraries.\n\n"); + } + + if (i > 0) + { + // Table header required + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "| ## | example | image | difficulty
level | version
created | last version
updated | original
developer |\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "|----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------|\n"); + } + + rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], false, &catCount); + for (int x = 0; x < catCount; x++) + { + char stars[16] = { 0 }; + for (int s = 0; s < 4; s++) + { + if (s < exCatList[x].stars) strcpy(stars + 3*s, "⭐️"); + else strcpy(stars + 3*s, "☆"); + } + + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, + TextFormat("| %02i | [%s](%s/%s.c) | \"%s\" | %s | %.1f | %.1f | [%s](https://github.com/%s) |\n", + gCount + 1, exCatList[x].name, exCatList[x].category, exCatList[x].name, exCatList[x].category, exCatList[x].name, exCatList[x].name, + stars, exCatList[x].verCreated, exCatList[x].verUpdated, exCatList[x].author, exCatList[x].authorGitHub + 1)); + + gCount++; + } + + UnloadExamplesData(exCatList); + } + + // Save updated file + SaveFileText(TextFormat("%s/README.md", exBasePath), mdTextUpdated); + UnloadFileText(mdText); + RL_FREE(mdTextUpdated); + //------------------------------------------------------------------------------------------------ + + // Edit: raylib.com/common/examples.js --> Update from collection + // NOTE: Entries format: exampleEntry('⭐️☆☆☆' , 'core' , 'basic_window'), + //------------------------------------------------------------------------------------------------ + char *jsText = LoadFileText(TextFormat("%s/../common/examples.js", exWebPath)); + char *jsTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated examples.js copy, 2MB + + int jsListStartIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_START"); + int jsListEndIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_END"); + + int jsIndex = 0; + memcpy(jsTextUpdated, jsText, jsListStartIndex); + jsIndex = sprintf(jsTextUpdated + jsListStartIndex, "//EXAMPLE_DATA_LIST_START\n"); + jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, " var exampleData = [\n"); + + // NOTE: We avoid "others" category + for (int i = 0, exCount = 0; i < MAX_EXAMPLE_CATEGORIES - 1; i++) + { + rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], false, &exCount); + for (int x = 0; x < exCount; x++) + { + char stars[16] = { 0 }; + for (int s = 0; s < 4; s++) + { + if (s < exCatList[x].stars) strcpy(stars + 3*s, "⭐️"); + else strcpy(stars + 3*s, "☆"); + } + + if ((i == 6) && (x == (exCount - 1))) + { + // NOTE: Last line to add, special case to consider + jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, + TextFormat(" exampleEntry('%s', '%s', '%s')];\n", stars, exCatList[x].category, exCatList[x].name + strlen(exCatList[x].category) + 1)); + } + else + { + jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, + TextFormat(" exampleEntry('%s', '%s', '%s'),\n", stars, exCatList[x].category, exCatList[x].name + strlen(exCatList[x].category) + 1)); + } + } + + UnloadExamplesData(exCatList); + } + + // Add the remaining part of the original file + memcpy(jsTextUpdated + jsListStartIndex + jsIndex, jsText + jsListEndIndex, strlen(jsText) - jsListEndIndex); + + // Save updated file + SaveFileText(TextFormat("%s/../common/examples.js", exWebPath), jsTextUpdated); + UnloadFileText(jsText); + RL_FREE(jsTextUpdated); + //------------------------------------------------------------------------------------------------ + + return result; +} + + // Load examples collection information static rlExampleInfo *LoadExamplesData(const char *fileName, const char *category, bool sort, int *exCount) { From d182ae783751f7a37cde1a4495419a4678a919a0 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 3 Aug 2025 23:32:54 +0200 Subject: [PATCH 163/242] UPDATED: Support example removal --- examples/rexm.c | 58 +++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/examples/rexm.c b/examples/rexm.c index 88dd22cc8..557bb1b9e 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -251,6 +251,7 @@ int main(int argc, char *argv[]) if (TextFindIndex(exColInfo, argv[2]) != -1) // Example in the collection { strcpy(exName, argv[2]); // Register filename for removal + strncpy(exCategory, exName, TextFindIndex(exName, "_")); opCode = OP_REMOVE; } else LOG("WARNING: REMOVE: Example not available in the collection\n"); @@ -350,7 +351,6 @@ int main(int argc, char *argv[]) RL_FREE(exColInfoUpdated); } else LOG("WARNING: ADD: Example is already on the collection\n"); - UnloadFileText(exColInfo); //------------------------------------------------------------------------------------------------ @@ -465,7 +465,26 @@ int main(int argc, char *argv[]) } break; case 4: // Remove { - // TODO: Remove and update all required files... + // Remove example from collection for files update + //------------------------------------------------------------------------------------------------ + char *exColInfo = LoadFileText(exCollectionListPath); + int exIndex = TextFindIndex(exColInfo, TextFormat("%s;%s", exCategory, exName)); + if (exIndex > 0) // Example found + { + char *exColInfoUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated list copy, 2MB + + memcpy(exColInfoUpdated, exColInfo, exIndex); + int lineLen = 0; + for (int i = exIndex; (exColInfo[i] != '\n') && (exColInfo[i] != '\0'); i++) lineLen++; + // Remove line and copy the rest next + memcpy(exColInfoUpdated + exIndex, exColInfo + exIndex + lineLen + 1, strlen(exColInfo) - exIndex - lineLen); + + SaveFileText(exCollectionListPath, exColInfoUpdated); + RL_FREE(exColInfoUpdated); + } + else LOG("WARNING: REMOVE: Example not found in the collection\n"); + UnloadFileText(exColInfo); + //------------------------------------------------------------------------------------------------ // Remove: raylib/examples//_example_name.c // Remove: raylib/examples//_example_name.png @@ -474,37 +493,15 @@ int main(int argc, char *argv[]) // TODO: Remove: raylib/examples//resources/.. - // Edit: raylib/examples/Makefile - //--------------------------------------------------------------------------- - // - // - //--------------------------------------------------------------------------- - - // Edit: raylib/examples/Makefile.Web - //--------------------------------------------------------------------------- - // - // - //--------------------------------------------------------------------------- - - // Edit: raylib/examples/README.md - //--------------------------------------------------------------------------- - // - // - //--------------------------------------------------------------------------- + UpdateRequiredFiles(); // Remove: raylib/projects/VS2022/examples/_example_name.vcxproj remove(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName)); - // Edit: raylib/projects/VS2022/raylib.sln + // Edit: raylib/projects/VS2022/raylib.sln --> Remove example project //--------------------------------------------------------------------------- - // - // - //--------------------------------------------------------------------------- - - // Edit: raylib.com/common/examples.js - //--------------------------------------------------------------------------- - - + system(TextFormat("dotnet solution %s/../projects/VS2022/raylib.sln remove %s/../projects/VS2022/examples/%s.vcxproj", + exBasePath, exBasePath, exName)); //--------------------------------------------------------------------------- // Remove: raylib.com/examples//_example_name.html @@ -610,7 +607,7 @@ static int UpdateRequiredFiles(void) } // Add the remaining part of the original file - memcpy(mkTextUpdated + mkListStartIndex + mkIndex, mkText + mkListEndIndex, strlen(mkText) - mkListEndIndex); + memcpy(mkTextUpdated + mkListStartIndex + mkIndex - 1, mkText + mkListEndIndex, strlen(mkText) - mkListEndIndex); // Save updated file SaveFileText(TextFormat("%s/Makefile", exBasePath), mkTextUpdated); @@ -644,7 +641,7 @@ static int UpdateRequiredFiles(void) } // Add the remaining part of the original file - memcpy(mkwTextUpdated + mkwListStartIndex + mkwIndex, mkwText + mkwListEndIndex, strlen(mkwText) - mkwListEndIndex); + memcpy(mkwTextUpdated + mkwListStartIndex + mkwIndex - 1, mkwText + mkwListEndIndex, strlen(mkwText) - mkwListEndIndex); // TODO: Add new example target, considering resources @@ -802,7 +799,6 @@ static int UpdateRequiredFiles(void) return result; } - // Load examples collection information static rlExampleInfo *LoadExamplesData(const char *fileName, const char *category, bool sort, int *exCount) From d43470a3d4248044811e2ba0a962337707e04039 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 3 Aug 2025 23:49:52 +0200 Subject: [PATCH 164/242] Minor comment reviews --- examples/rexm.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/examples/rexm.c b/examples/rexm.c index 557bb1b9e..9ee37fa31 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -297,7 +297,7 @@ int main(int argc, char *argv[]) FileCopy(exTemplateScreenshot, TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName)); // WARNING: To be updated manually! // Copy: raylib/examples//resources/... // WARNING: To be updated manually! - // TODO: Example to be added could be provided as a .zip, containing resources! + // IDEA: Example to be added could be provided as a .zip, containing resources! // TODO: Copy provided resources to respective directories // Possible strategy: @@ -308,7 +308,7 @@ int main(int argc, char *argv[]) // 2. Verify paths: resource files exist // 3. Copy files to required resource dir - // Add example to the main collection list, if not already there + // Add example to the collection list, if not already there // NOTE: Required format: shapes;shapes_basic_shapes;⭐️☆☆☆;1.0;4.2;"Ray";@raysan5 //------------------------------------------------------------------------------------------------ char *exColInfo = LoadFileText(exCollectionListPath); @@ -328,19 +328,20 @@ int main(int argc, char *argv[]) else if (strcmp(exCategory, "shaders") == 0) nextCatIndex = 6; else if (strcmp(exCategory, "audio") == 0) nextCatIndex = 7; else if (strcmp(exCategory, "others") == 0) nextCatIndex = -1; // Add to EOF + + // TODO: Get required example info from example file header (if provided) + // NOTE: If no example info is provided (other than category/name), just using some default values if (nextCatIndex == -1) { - // Add example to the end of the list + // Add example to collection at the EOF int endIndex = (int)strlen(exColInfo); memcpy(exColInfoUpdated, exColInfo, endIndex); - sprintf(exColInfoUpdated + endIndex, TextFormat("\n%s/%s\n", exCategory, exName)); + sprintf(exColInfoUpdated + endIndex, TextFormat("%s;%s;⭐️☆☆☆;6.0;6.0;\"Ray\";@raysan5\n", exCategory, exName)); } else { - // Add example to the end of the category list - // TODO: Get required example info from example file header (if provided) - // NOTE: If no example info is provided (other than category/name), just using some default values + // Add example to collection, at the end of the category list int catIndex = TextFindIndex(exColInfo, exCategories[nextCatIndex]); memcpy(exColInfoUpdated, exColInfo, catIndex); int textWritenSize = sprintf(exColInfoUpdated + catIndex, TextFormat("%s;%s;⭐️☆☆☆;6.0;6.0;\"Ray\";@raysan5\n", exCategory, exName)); @@ -379,7 +380,7 @@ int main(int argc, char *argv[]) // Compile to: raylib.com/examples//_example_name.wasm // Compile to: raylib.com/examples//_example_name.js //------------------------------------------------------------------------------------------------ - // TODO: WARNING: This .BAT is not portable and it does not consider RESOURCES for Web properly, + // TODO: Avoid platform-specific .BAT, not portable and it does not consider RESOURCES for Web properly, // Makefile.Web should be used... but it requires proper editing first! system(TextFormat("%s/build_example_web.bat %s/%s", exBasePath, exCategory, exName)); @@ -492,6 +493,7 @@ int main(int argc, char *argv[]) remove(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName)); // TODO: Remove: raylib/examples//resources/.. + // Get list of resources from Makefile.Web or examples ResourcesScan() UpdateRequiredFiles(); @@ -890,9 +892,12 @@ static int FileCopy(const char *srcPath, const char *dstPath) int srcDataSize = 0; unsigned char *srcFileData = LoadFileData(srcPath, &srcDataSize); - // TODO: Create required paths if they do not exist + // Create required paths if they do not exist + if (!DirectoryExists(GetDirectoryPath(dstPath))) + MakeDirectory(GetDirectoryPath(dstPath)); - if ((srcFileData != NULL) && (srcDataSize > 0)) result = SaveFileData(dstPath, srcFileData, srcDataSize); + if ((srcFileData != NULL) && (srcDataSize > 0)) + result = SaveFileData(dstPath, srcFileData, srcDataSize); UnloadFileData(srcFileData); From 72de562542280e442f0eccb0a7019506eda28162 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Aug 2025 13:23:04 +0200 Subject: [PATCH 165/242] REVIEWED: Examples for automation processes --- examples/core/core_vr_simulator.c | 2 +- .../glsl100/distortion.fs} | 0 .../glsl330/distortion.fs} | 0 examples/models/models_mesh_generation.c | 11 +---------- 4 files changed, 2 insertions(+), 11 deletions(-) rename examples/core/resources/{distortion100.fs => shaders/glsl100/distortion.fs} (100%) rename examples/core/resources/{distortion330.fs => shaders/glsl330/distortion.fs} (100%) diff --git a/examples/core/core_vr_simulator.c b/examples/core/core_vr_simulator.c index a793e62f4..43b1ec471 100644 --- a/examples/core/core_vr_simulator.c +++ b/examples/core/core_vr_simulator.c @@ -61,7 +61,7 @@ int main(void) VrStereoConfig config = LoadVrStereoConfig(device); // Distortion shader (uses device lens distortion and chroma) - Shader distortion = LoadShader(0, TextFormat("resources/distortion%i.fs", GLSL_VERSION)); + Shader distortion = LoadShader(0, TextFormat("resources/shaders/glsl%i/distortion.fs", GLSL_VERSION)); // Update distortion shader with lens and distortion-scale parameters SetShaderValue(distortion, GetShaderLocation(distortion, "leftLensCenter"), diff --git a/examples/core/resources/distortion100.fs b/examples/core/resources/shaders/glsl100/distortion.fs similarity index 100% rename from examples/core/resources/distortion100.fs rename to examples/core/resources/shaders/glsl100/distortion.fs diff --git a/examples/core/resources/distortion330.fs b/examples/core/resources/shaders/glsl330/distortion.fs similarity index 100% rename from examples/core/resources/distortion330.fs rename to examples/core/resources/shaders/glsl330/distortion.fs diff --git a/examples/models/models_mesh_generation.c b/examples/models/models_mesh_generation.c index bc92873c2..e268085b0 100644 --- a/examples/models/models_mesh_generation.c +++ b/examples/models/models_mesh_generation.c @@ -48,16 +48,7 @@ int main(void) models[7] = LoadModelFromMesh(GenMeshPoly(5, 2.0f)); models[8] = LoadModelFromMesh(GenMeshCustom()); - // Generated meshes could be exported as .obj files - //ExportMesh(models[0].meshes[0], "plane.obj"); - //ExportMesh(models[1].meshes[0], "cube.obj"); - //ExportMesh(models[2].meshes[0], "sphere.obj"); - //ExportMesh(models[3].meshes[0], "hemisphere.obj"); - //ExportMesh(models[4].meshes[0], "cylinder.obj"); - //ExportMesh(models[5].meshes[0], "torus.obj"); - //ExportMesh(models[6].meshes[0], "knot.obj"); - //ExportMesh(models[7].meshes[0], "poly.obj"); - //ExportMesh(models[8].meshes[0], "custom.obj"); + // NOTE: Generated meshes could be exported using ExportMesh() // Set checked texture as default diffuse component for all models material for (int i = 0; i < NUM_MODELS; i++) models[i].materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture; From d8952958db1c5ede9ba3d1aaa43955e2c62c2e6e Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Aug 2025 13:23:14 +0200 Subject: [PATCH 166/242] Update Makefile --- examples/Makefile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 33a9d1327..39638d371 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -493,6 +493,8 @@ ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_R LDLIBS = $(RAYLIB_RELEASE_PATH)/libraylib.web.a endif +CURRENT_MAKEFILE = $(lastword $(MAKEFILE_LIST)) + # Define source code object files required #------------------------------------------------------------------------------------------------ #EXAMPLES_LIST_START @@ -672,8 +674,6 @@ OTHERS = \ others/rlgl_standalone #EXAMPLES_LIST_END -CURRENT_MAKEFILE = $(lastword $(MAKEFILE_LIST)) - # Define processes to execute #------------------------------------------------------------------------------------------------ # Default target entry @@ -687,7 +687,6 @@ models: $(MODELS) shaders: $(SHADERS) audio: $(AUDIO) - # Generic compilation pattern # NOTE: Examples must be ready for Android compilation! %: %.c From 80dd72ce128d69176645b946321ba58cde895db1 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Aug 2025 13:23:57 +0200 Subject: [PATCH 167/242] REVIEWED: For automation simplification --- examples/Makefile.Web | 51 +++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/examples/Makefile.Web b/examples/Makefile.Web index c0969dfeb..5a5d74eb0 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -493,6 +493,8 @@ ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_R LDLIBS = $(RAYLIB_RELEASE_PATH)/libraylib.web.a endif +CURRENT_MAKEFILE = $(lastword $(MAKEFILE_LIST)) + # Define source code object files required #------------------------------------------------------------------------------------------------ #EXAMPLES_LIST_START @@ -670,12 +672,7 @@ OTHERS = \ others/raymath_vector_angle \ others/rlgl_compute_shader \ others/rlgl_standalone -#EXAMPLES_LIST_END -CURRENT_MAKEFILE = $(lastword $(MAKEFILE_LIST)) - -# Define processes to execute -#------------------------------------------------------------------------------------------------ # Default target entry all: $(CORE) $(SHAPES) $(TEXT) $(TEXTURES) $(MODELS) $(SHADERS) $(AUDIO) @@ -734,7 +731,7 @@ core/core_custom_logging: core/core_custom_logging.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) core/core_drop_files: core/core_drop_files.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 -sFORCE_FILESYSTEM=1 + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) core/core_input_gamepad: core/core_input_gamepad.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -896,7 +893,7 @@ textures/textures_image_drawing: textures/textures_image_drawing.c --preload-file textures/resources/cat.png@resources/cat.png textures/textures_image_generation: textures/textures_image_generation.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) textures/textures_image_kernel: textures/textures_image_kernel.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -915,7 +912,7 @@ textures/textures_image_rotate: textures/textures_image_rotate.c --preload-file textures/resources/raylib_logo.png textures/textures_image_text: textures/textures_image_text.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file textures/resources/parrots.png@resources/parrots.png \ --preload-file textures/resources/KAISG.ttf@resources/KAISG.ttf @@ -978,17 +975,17 @@ text/text_draw_3d: text/text_draw_3d.c --preload-file text/resources/shaders/glsl100/alpha_discard.fs@resources/shaders/glsl100/alpha_discard.fs text/text_font_filters: text/text_font_filters.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file text/resources/KAISG.ttf@resources/KAISG.ttf text/text_font_loading: text/text_font_loading.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file text/resources/pixantiqua.fnt@resources/pixantiqua.fnt \ --preload-file text/resources/pixantiqua.png@resources/pixantiqua.png \ --preload-file text/resources/pixantiqua.ttf@resources/pixantiqua.ttf text/text_font_sdf: text/text_font_sdf.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file text/resources/anonymous_pro_bold.ttf@resources/anonymous_pro_bold.ttf \ --preload-file text/resources/shaders/glsl100/sdf.fs@resources/shaders/glsl100/sdf.fs @@ -1019,7 +1016,7 @@ text/text_rectangle_bounds: text/text_rectangle_bounds.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) text/text_unicode: text/text_unicode.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file text/resources/dejavu.fnt@resources/dejavu.fnt \ --preload-file text/resources/dejavu.png@resources/dejavu.png \ --preload-file text/resources/noto_cjk.fnt@resources/noto_cjk.fnt \ @@ -1033,13 +1030,13 @@ text/text_writing_anim: text/text_writing_anim.c # Compile MODELS examples models/models_animation: models/models_animation.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file models/resources/models/iqm/guy.iqm@resources/models/iqm/guy.iqm \ --preload-file models/resources/models/iqm/guytex.png@resources/models/iqm/guytex.png \ --preload-file models/resources/models/iqm/guyanim.iqm@resources/models/iqm/guyanim.iqm models/models_gpu_skinning: models/models_gpu_skinning.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file models/resources/models/gltf/greenman.glb@resources/models/gltf/greenman.glb \ --preload-file models/resources/shaders/glsl100/skinning.vs@resources/shaders/glsl100/skinning.vs \ --preload-file models/resources/shaders/glsl100/skinning.fs@resources/shaders/glsl100/skinning.fs @@ -1080,20 +1077,20 @@ models/models_heightmap: models/models_heightmap.c --preload-file models/resources/heightmap.png@resources/heightmap.png models/models_loading: models/models_loading.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file models/resources/models/obj/castle.obj@resources/models/obj/castle.obj \ --preload-file models/resources/models/obj/castle_diffuse.png@resources/models/obj/castle_diffuse.png models/models_loading_gltf: models/models_loading_gltf.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file models/resources/models/gltf/robot.glb@resources/models/gltf/robot.glb models/models_loading_m3d: models/models_loading_m3d.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file models/resources/models/m3d/cesium_man.m3d@resources/models/m3d/cesium_man.m3d models/models_loading_vox: models/models_loading_vox.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file models/resources/models/vox/chr_knight.vox@resources/models/vox/chr_knight.vox \ --preload-file models/resources/models/vox/chr_sword.vox@resources/models/vox/chr_sword.vox \ --preload-file models/resources/models/vox/monu9.vox@resources/models/vox/monu9.vox @@ -1116,7 +1113,7 @@ models/models_rlgl_solar_system: models/models_rlgl_solar_system.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) models/models_skybox: models/models_skybox.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 -sFORCE_FILESYSTEM=1 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file models/resources/dresden_square_2k.hdr@resources/dresden_square_2k.hdr \ --preload-file models/resources/skybox.png@resources/skybox.png \ --preload-file models/resources/shaders/glsl100/skybox.vs@resources/shaders/glsl100/skybox.vs \ @@ -1131,7 +1128,7 @@ models/models_waving_cubes: models/models_waving_cubes.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) models/models_yaw_pitch_roll: models/models_yaw_pitch_roll.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file models/resources/models/obj/plane.obj@resources/models/obj/plane.obj \ --preload-file models/resources/models/obj/plane_diffuse.png@resources/models/obj/plane_diffuse.png @@ -1161,7 +1158,7 @@ shaders/shaders_basic_pbr: shaders/shaders_basic_pbr.c --preload-file shaders/resources/road_n.png@resources/road_n.png shaders/shaders_custom_uniform: shaders/shaders_custom_uniform.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/models/barracks.obj@resources/models/barracks.obj \ --preload-file shaders/resources/models/barracks_diffuse.png@resources/models/barracks_diffuse.png \ --preload-file shaders/resources/shaders/glsl100/swirl.fs@resources/shaders/glsl100/swirl.fs @@ -1210,7 +1207,7 @@ shaders/shaders_mesh_instancing: shaders/shaders_mesh_instancing.c --preload-file shaders/resources/shaders/glsl100/lighting.fs@resources/shaders/glsl100/lighting.fs shaders/shaders_model_shader: shaders/shaders_model_shader.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/models/watermill.obj@resources/models/watermill.obj \ --preload-file shaders/resources/models/watermill_diffuse.png@resources/models/watermill_diffuse.png \ --preload-file shaders/resources/shaders/glsl100/grayscale.fs@resources/shaders/glsl100/grayscale.fs @@ -1224,7 +1221,7 @@ shaders/shaders_palette_switch: shaders/shaders_palette_switch.c --preload-file shaders/resources/shaders/glsl100/palette_switch.fs@resources/shaders/glsl100/palette_switch.fs shaders/shaders_postprocessing: shaders/shaders_postprocessing.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/models/church.obj@resources/models/church.obj \ --preload-file shaders/resources/models/church_diffuse.png@resources/models/church_diffuse.png \ --preload-file shaders/resources/shaders/glsl100@resources/shaders/glsl100 @@ -1294,7 +1291,7 @@ shaders/shaders_vertex_displacement: shaders/shaders_vertex_displacement.c # Compile AUDIO examples audio/audio_mixed_processor: audio/audio_mixed_processor.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file audio/resources/country.mp3@resources/country.mp3 \ --preload-file audio/resources/coin.wav@resources/coin.wav @@ -1303,7 +1300,7 @@ audio/audio_module_playing: audio/audio_module_playing.c --preload-file audio/resources/mini1111.xm@resources/mini1111.xm audio/audio_music_stream: audio/audio_music_stream.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file audio/resources/country.mp3@resources/country.mp3 audio/audio_raw_stream: audio/audio_raw_stream.c @@ -1319,7 +1316,7 @@ audio/audio_sound_multi: audio/audio_sound_multi.c --preload-file audio/resources/sound.wav@resources/sound.wav audio/audio_stream_effects: audio/audio_stream_effects.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file audio/resources/country.mp3@resources/country.mp3 # Compile OTHERS examples @@ -1341,6 +1338,8 @@ others/rlgl_compute_shader: others/rlgl_standalone: $(info Skipping_others_rlgl_standalone) +#EXAMPLES_LIST_END + # Clean everything clean: ifeq ($(PLATFORM),PLATFORM_DESKTOP) From e130775e0d6070c26a7a033c28ad9e7918db7dfa Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Aug 2025 14:13:17 +0200 Subject: [PATCH 168/242] Updated for resources scanning system --- examples/text/text_draw_3d.c | 12 +++++++----- examples/textures/textures_mouse_painting.c | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/examples/text/text_draw_3d.c b/examples/text/text_draw_3d.c index 6fc0e6c5c..c06698b9f 100644 --- a/examples/text/text_draw_3d.c +++ b/examples/text/text_draw_3d.c @@ -34,8 +34,11 @@ #include // Required for: NULL #include // Required for: sinf() -// To make it work with the older RLGL module just comment the line below -#define RAYLIB_NEW_RLGL +#if defined(PLATFORM_DESKTOP) + #define GLSL_VERSION 330 +#else // PLATFORM_ANDROID, PLATFORM_WEB + #define GLSL_VERSION 100 +#endif //-------------------------------------------------------------------------------------- // Globals @@ -50,7 +53,6 @@ bool SHOW_TEXT_BOUNDRY = false; //-------------------------------------------------------------------------------------- // Data Types definition //-------------------------------------------------------------------------------------- - // Configuration structure for waving the text typedef struct WaveTextConfig { Vector3 waveRange; @@ -66,7 +68,7 @@ static void DrawTextCodepoint3D(Font font, int codepoint, Vector3 position, floa // Draw a 2D text in 3D space static void DrawText3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, Color tint); -// Draw a 2D text in 3D space and wave the parts that start with `~~` and end with `~~`. +// Draw a 2D text in 3D space and wave the parts that start with '~~' and end with '~~' // This is a modified version of the original code by @Nighten found here https://github.com/NightenDushi/Raylib_DrawTextStyle static void DrawTextWave3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, WaveTextConfig *config, float time, Color tint); // Measure a text in 3D ignoring the `~~` chars. @@ -128,7 +130,7 @@ int main(void) Color dark = RED; // Load the alpha discard shader - Shader alphaDiscard = LoadShader(NULL, "resources/shaders/glsl330/alpha_discard.fs"); + Shader alphaDiscard = LoadShader(NULL, TextFormat("resources/shaders/glsl%i/alpha_discard.fs", GLSL_VERSION)); // Array filled with multiple random colors (when multicolor mode is set) Color multi[TEXT_MAX_LAYERS] = {0}; diff --git a/examples/textures/textures_mouse_painting.c b/examples/textures/textures_mouse_painting.c index c0e426232..e2a2b6081 100644 --- a/examples/textures/textures_mouse_painting.c +++ b/examples/textures/textures_mouse_painting.c @@ -210,7 +210,7 @@ int main(void) { DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Fade(RAYWHITE, 0.8f)); DrawRectangle(0, 150, GetScreenWidth(), 80, BLACK); - DrawText("IMAGE SAVED: my_amazing_texture_painting.png", 150, 180, 20, RAYWHITE); + DrawText("IMAGE SAVED!", 150, 180, 20, RAYWHITE); } EndDrawing(); From bc0f93baea936af33015e52b76d6a0e6429fb55f Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Aug 2025 17:51:48 +0200 Subject: [PATCH 169/242] UPDATE: Added resources scanning from examples for automated copying --- examples/rexm.c | 157 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 146 insertions(+), 11 deletions(-) diff --git a/examples/rexm.c b/examples/rexm.c index 9ee37fa31..a4148b646 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -58,6 +58,10 @@ #define LOG(...) #endif +#define REXM_MAX_BUFFER_SIZE (2*1024*1024) // 2MB + +#define REXM_MAX_RESOURCE_PATHS 256 + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- @@ -125,6 +129,12 @@ static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry); // WARNING: items[] pointers are reorganized static void SortExampleByName(rlExampleInfo *items, int count); +// Scan resource paths in example file +static char **ScanExampleResources(const char *filePath, int *resPathCount); + +// Clear resource paths scanned +static void ClearExampleResources(char **resPaths); + //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -296,17 +306,68 @@ int main(int argc, char *argv[]) // Create: raylib/examples//_example_name.png FileCopy(exTemplateScreenshot, TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName)); // WARNING: To be updated manually! - // Copy: raylib/examples//resources/... // WARNING: To be updated manually! - // IDEA: Example to be added could be provided as a .zip, containing resources! + // Copy: raylib/examples//resources/... + // ----------------------------------------------------------------------------------------- + // Scan resources used in example to copy + int resPathCount = 0; + char **resPaths = ScanExampleResources(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName), &resPathCount); - // TODO: Copy provided resources to respective directories - // Possible strategy: - // 1. Scan code file for resources paths -> Resources list - // Look for specific text: '.png"' - // Look for full path, previous '"' - // Be careful with shaders: '.vs"', '.fs"' -> Reconstruct path manually? - // 2. Verify paths: resource files exist - // 3. Copy files to required resource dir + if (resPathCount > 0) + { + for (int r = 0; r < resPathCount; r++) + { + // WARNING: Special case to consider: shaders, resource paths could use conditions: "glsl%i" + // In this case, multiple resources are required: glsl100, glsl120, glsl330 + if (TextFindIndex(resPaths[r], "glsl%i") > -1) + { + int glslVer[3] = { 100, 120, 330 }; + + for (int v = 0; v < 3; v++) + { + char *resPathUpdated = TextReplace(resPaths[r], "glsl%i", TextFormat("glsl%i", glslVer[v])); + + LOG("INFO: Example resource required: %s\n", resPathUpdated); + + if (FileExists(TextFormat("%s/%s", GetDirectoryPath(inFileName), resPathUpdated))) + { + // Verify the resources are placed in "resources" directory + if (TextFindIndex(resPathUpdated, "resources/") > 0) + { + // NOTE: Look for resources in the path of the provided .c to be added + // To be copied to /resources directory, extra dirs are automatically created if required + FileCopy(TextFormat("%s/%s", GetDirectoryPath(inFileName), resPathUpdated), + TextFormat("%s/%s/%s", exBasePath, exCategory, resPathUpdated)); + } + else LOG("WARNING: Example resource must be placed in 'resources' directory next to .c file\n"); + } + else LOG("WARNING: Example resource can not be found in: %s\n", TextFormat("%s/%s", GetDirectoryPath(inFileName), resPathUpdated)); + + RL_FREE(resPathUpdated); + } + } + else + { + LOG("INFO: Example resource required: %s\n", resPaths[r]); + + if (FileExists(TextFormat("%s/%s", GetDirectoryPath(inFileName), resPaths[r]))) + { + // Verify the resources are placed in "resources" directory + if (TextFindIndex(resPaths[r], "resources/") > 0) + { + // NOTE: Look for resources in the path of the provided .c to be added + // To be copied to /resources directory, extra dirs are automatically created if required + FileCopy(TextFormat("%s/%s", GetDirectoryPath(inFileName), resPaths[r]), + TextFormat("%s/%s/%s", exBasePath, exCategory, resPaths[r])); + } + else LOG("WARNING: Example resource must be placed in 'resources' directory next to .c file\n"); + } + else LOG("WARNING: Example resource can not be found in: %s\n", TextFormat("%s/%s", GetDirectoryPath(inFileName), resPaths[r])); + } + } + } + + ClearExampleResources(resPaths); + // ----------------------------------------------------------------------------------------- // Add example to the collection list, if not already there // NOTE: Required format: shapes;shapes_basic_shapes;⭐️☆☆☆;1.0;4.2;"Ray";@raysan5 @@ -618,6 +679,7 @@ static int UpdateRequiredFiles(void) //------------------------------------------------------------------------------------------------ // Edit: raylib/examples/Makefile.Web --> Update from collection + // NOTE: We avoid the "others" category on web building //------------------------------------------------------------------------------------------------ char *mkwText = LoadFileText(TextFormat("%s/Makefile.Web", exBasePath)); char *mkwTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated Makefile copy, 2MB @@ -629,7 +691,8 @@ static int UpdateRequiredFiles(void) memcpy(mkwTextUpdated, mkwText, mkwListStartIndex); mkwIndex = sprintf(mkwTextUpdated + mkwListStartIndex, "#EXAMPLES_LIST_START\n"); - for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++) + // NOTE: We avoid the "others" category on web building + for (int i = 0; i < MAX_EXAMPLE_CATEGORIES - 1; i++) { mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i]))); @@ -1018,3 +1081,75 @@ static void SortExampleByName(rlExampleInfo *items, int count) { qsort(items, count, sizeof(rlExampleInfo), rlExampleInfoCompare); } + +// Scan resource paths in example file +static char **ScanExampleResources(const char *filePath, int *resPathCount) +{ + #define MAX_RES_PATH_LEN 256 + + char **paths = (char **)RL_CALLOC(REXM_MAX_RESOURCE_PATHS, sizeof(char **)); + for (int i = 0; i < REXM_MAX_RESOURCE_PATHS; i++) paths[i] = (char *)RL_CALLOC(MAX_RES_PATH_LEN, sizeof(char)); + + int resCounter = 0; + char *code = LoadFileText(filePath); + + if (code != NULL) + { + // Resources extensions to check + const char *exts[] = { ".png", ".bmp", ".jpg", ".qoi", ".gif", ".raw", ".hdr", ".ttf", ".fnt", ".wav", ".ogg", ".mp3", ".flac", ".mod", ".qoa", ".qoa", ".obj", ".iqm", ".glb", ".m3d", ".vox", ".vs", ".fs" }; + const int extCount = sizeof(exts)/sizeof(exts[0]); + + char *ptr = code; + while ((ptr = strchr(ptr, '"')) != NULL) + { + char *start = ptr + 1; + char *end = strchr(start, '"'); + if (!end) break; + + int len = end - start; + if ((len > 0) && (len < MAX_RES_PATH_LEN)) + { + char buffer[MAX_RES_PATH_LEN] = { 0 }; + strncpy(buffer, start, len); + buffer[len] = '\0'; + + // Check for known extensions + for (int i = 0; i < extCount; i++) + { + if (IsFileExtension(buffer, exts[i])) + { + // Avoid duplicates + bool found = false; + for (int j = 0; j < resCounter; j++) + { + if (TextIsEqual(paths[j], buffer)) { found = true; break; } + } + + if (!found && (resCounter < REXM_MAX_RESOURCE_PATHS)) + { + strcpy(paths[resCounter], buffer); + resCounter++; + } + + break; + } + } + } + + ptr = end + 1; + } + + UnloadFileText(code); + } + + *resPathCount = resCounter; + return paths; +} + +// Clear resource paths scanned +static void ClearExampleResources(char **resPaths) +{ + for (int i = 0; i < REXM_MAX_RESOURCE_PATHS; i++) RL_FREE(resPaths[i]); + + RL_FREE(resPaths); +} From 1f5fec851dacd0ef85076c73ccaed0be3df6aa0e Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Aug 2025 17:52:22 +0200 Subject: [PATCH 170/242] UPDATE: Added `Makefile.Web` per-example target generation including used resources --- examples/rexm.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/examples/rexm.c b/examples/rexm.c index a4148b646..9e7fbf899 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -705,11 +705,92 @@ static int UpdateRequiredFiles(void) UnloadExamplesData(exCatList); } + // Add examples individual targets, considering every example resources + // Some required makefile code... + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "# Default target entry\n"); + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "all: $(CORE) $(SHAPES) $(TEXT) $(TEXTURES) $(MODELS) $(SHADERS) $(AUDIO)\n\n"); + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "core: $(CORE)\n"); + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "shapes: $(SHAPES)\n"); + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "textures: $(TEXTURES)\n"); + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "text: $(TEXT)\n"); + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "models: $(MODELS)\n"); + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "shaders: $(SHADERS)\n"); + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "audio: $(AUDIO)\n\n"); + + // NOTE: We avoid the "others" category on web building + for (int i = 0; i < MAX_EXAMPLE_CATEGORIES - 1; i++) + { + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("# Compile %s examples\n", TextToUpper(exCategories[i]))); + + int exCount = 0; + rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], true, &exCount); + + for (int x = 0; x < exCount; x++) + { + // Scan resources used in example to list + int resPathCount = 0; + char **resPaths = ScanExampleResources(TextFormat("%s/%s/%s.c", exBasePath, exCatList[x].category, exCatList[x].name), &resPathCount); + + if (resPathCount > 0) + { + /* + // WARNING: Compilation line starts with [TAB] + shaders/shaders_vertex_displacement: shaders/shaders_vertex_displacement.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file shaders/resources/shaders/glsl100/vertex_displacement.vs@resources/shaders/glsl100/vertex_displacement.vs \ + --preload-file shaders/resources/shaders/glsl330/vertex_displacement.vs@resources/shaders/glsl330/vertex_displacement.vs \ + --preload-file shaders/resources/shaders/glsl100/vertex_displacement.fs@resources/shaders/glsl100/vertex_displacement.fs \ + --preload-file shaders/resources/shaders/glsl330/vertex_displacement.fs@resources/shaders/glsl330/vertex_displacement.fs + */ + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, + TextFormat("%s/%s: %s/%s.c\n", exCatList[x].category, exCatList[x].name, exCatList[x].category, exCatList[x].name)); + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, " $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \\\n"); + + for (int r = 0; r < resPathCount; r++) + { + // WARNING: Special case to consider: shaders, resource paths could use conditions: "glsl%i" + // In this case, we focus on web building for: glsl100 + if (TextFindIndex(resPaths[r], "glsl%i") > -1) + { + char *resPathUpdated = TextReplace(resPaths[r], "glsl%i", "glsl100"); + memset(resPaths[r], 0, 256); + strcpy(resPaths[r], resPathUpdated); + RL_FREE(resPathUpdated); + } + + if (r < (resPathCount - 1)) + { + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, + TextFormat(" --preload-file %s/%s@%s \\\n", exCatList[x].category, resPaths[r], resPaths[r])); + } + else + { + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, + TextFormat(" --preload-file %s/%s@%s\n\n", exCatList[x].category, resPaths[r], resPaths[r])); + } + } + } + else // Example does not require resources + { + /* + // WARNING: Compilation line starts with [TAB] + core/core_2d_camera: core/core_2d_camera.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + */ + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, + TextFormat("%s/%s: %s/%s.c\n", exCatList[x].category, exCatList[x].name, exCatList[x].category, exCatList[x].name)); + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, " $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)\n\n"); + } + + ClearExampleResources(resPaths); + } + + UnloadExamplesData(exCatList); + } + // Add the remaining part of the original file memcpy(mkwTextUpdated + mkwListStartIndex + mkwIndex - 1, mkwText + mkwListEndIndex, strlen(mkwText) - mkwListEndIndex); - // TODO: Add new example target, considering resources - // Save updated file SaveFileText(TextFormat("%s/Makefile.Web", exBasePath), mkwTextUpdated); UnloadFileText(mkwText); From a457ab154e7cc7ed675f0dd315216db100f340dd Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Aug 2025 18:11:57 +0200 Subject: [PATCH 171/242] ADDED: Example resources for automatic removal WARNING: Some of those resources could be shared by other examples in the category so for now we leave removed manual by users... --- examples/rexm.c | 75 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 64 insertions(+), 11 deletions(-) diff --git a/examples/rexm.c b/examples/rexm.c index 9e7fbf899..7a0f24dc9 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -310,7 +310,7 @@ int main(int argc, char *argv[]) // ----------------------------------------------------------------------------------------- // Scan resources used in example to copy int resPathCount = 0; - char **resPaths = ScanExampleResources(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName), &resPathCount); + char **resPaths = ScanExampleResources(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), &resPathCount); if (resPathCount > 0) { @@ -467,39 +467,46 @@ int main(int argc, char *argv[]) FileTextReplace(exCollectionListPath, TextFormat("%s;%s", exCategory, exName), TextFormat("%s;%s", exRecategory, exRename)); - // Rename all required files + // Edit: Rename example code and screenshot files .c and .png rename(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), TextFormat("%s/%s/%s.c", exBasePath, exCategory, exRename)); rename(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName), TextFormat("%s/%s/%s.png", exBasePath, exCategory, exRename)); - // Rename example on required files + // NOTE: Example resource files do not need to be changed... + // unless the example is moved from one caegory to another + + // Edit: Rename example on required files FileTextReplace(TextFormat("%s/Makefile", exBasePath), exName, exRename); FileTextReplace(TextFormat("%s/Makefile.Web", exBasePath), exName, exRename); FileTextReplace(TextFormat("%s/README.md", exBasePath), exName, exRename); FileTextReplace(TextFormat("%s/../common/examples.js", exWebPath), exName, exRename); - // Rename example project and solution + // Edit: Rename example project and solution rename(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName), TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exRename)); FileTextReplace(TextFormat("%s/../projects/VS2022/raylib.sln", exBasePath), exName, exRename); } else { - // Rename with change of category - // TODO: Reorder collection as required + // WARNING: Rename with change of category + // TODO: Reorder collection to place renamed example at the end of category FileTextReplace(exCollectionListPath, TextFormat("%s;%s", exCategory, exName), TextFormat("%s;%s", exRecategory, exRename)); - // Rename all required files + // TODO: Move example resources from /resources to /resources + // WARNING: Resources can be shared with other examples in the category + + // Edit: Rename example code file (copy and remove) FileCopy(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), TextFormat("%s/%s/%s.c", exBasePath, exCategory, exRename)); remove(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); - + // Edit: Rename example screenshot file (copy and remove) FileCopy(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName), TextFormat("%s/%s/%s.png", exBasePath, exCategory, exRename)); remove(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName)); + // Edit: Update required files: Makefile, Makefile.Web, README.md, examples.js UpdateRequiredFiles(); } @@ -547,15 +554,47 @@ int main(int argc, char *argv[]) else LOG("WARNING: REMOVE: Example not found in the collection\n"); UnloadFileText(exColInfo); //------------------------------------------------------------------------------------------------ + + // Remove: raylib/examples//resources/.. + // WARNING: Some of those resources could be used by other examples, + // just leave this process to manual update for now! + // ----------------------------------------------------------------------------------------- + /* + // Scan resources used in example to be removed + int resPathCount = 0; + char **resPaths = ScanExampleResources(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), &resPathCount); + + if (resPathCount > 0) + { + for (int r = 0; r < resPathCount; r++) + { + // WARNING: Special case to consider: shaders, resource paths could use conditions: "glsl%i" + // In this case, multiple resources are required: glsl100, glsl120, glsl330 + if (TextFindIndex(resPaths[r], "glsl%i") > -1) + { + int glslVer[3] = { 100, 120, 330 }; + + for (int v = 0; v < 3; v++) + { + char *resPathUpdated = TextReplace(resPaths[r], "glsl%i", TextFormat("glsl%i", glslVer[v])); + remove(TextFormat("%s/%s/%s", exBasePath, exCategory, resPathUpdated)); + RL_FREE(resPathUpdated); + } + } + else remove(TextFormat("%s/%s/%s", exBasePath, exCategory, resPaths[r])); + } + } + + ClearExampleResources(resPaths); + */ + // ----------------------------------------------------------------------------------------- // Remove: raylib/examples//_example_name.c // Remove: raylib/examples//_example_name.png remove(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); remove(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName)); - // TODO: Remove: raylib/examples//resources/.. - // Get list of resources from Makefile.Web or examples ResourcesScan() - + // Edit: Update required files: Makefile, Makefile.Web, README.md, examples.js UpdateRequiredFiles(); // Remove: raylib/projects/VS2022/examples/_example_name.vcxproj @@ -1069,6 +1108,20 @@ static int FileRemove(const char *fileName) return result; } +// Move file from one directory to another +static int FileMove(const char *srcPath, const char *dstPath) +{ + int result = 0; + + if (FileExists(srcPath)) + { + FileCopy(srcPath, dstPath); + remove(srcPath); + } + + return result; +} + // Load text lines static char **LoadTextLines(const char *text, int *count) { From 1293461f5e1ee15bd4e196cb66c6734cc24929b4 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Aug 2025 19:27:48 +0200 Subject: [PATCH 172/242] REXM: Some code reviews, `README.md` generation improvements --- examples/rexm.c | 158 +++++++++++++++++++++++++++--------------------- 1 file changed, 90 insertions(+), 68 deletions(-) diff --git a/examples/rexm.c b/examples/rexm.c index 7a0f24dc9..2f95e4000 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -96,7 +96,10 @@ static const char *exBasePath = "C:/GitHub/raylib/examples"; static const char *exWebPath = "C:/GitHub/raylib.com/examples"; static const char *exTemplateFilePath = "C:/GitHub/raylib/examples/examples_template.c"; static const char *exTemplateScreenshot = "C:/GitHub/raylib/examples/examples_template.png"; -static const char *exCollectionListPath = "C:/GitHub/raylib/examples/examples_list.txt"; +static const char *exCollectionFilePath = "C:/GitHub/raylib/examples/examples_list.txt"; + +//const char *exBasePath = getenv("REXM_EXAMPLES_PATH"); +//if (!exBasePath) exBasePath = "default/path"; //---------------------------------------------------------------------------------- // Module specific functions declaration @@ -105,6 +108,7 @@ static int FileTextReplace(const char *fileName, const char *textLookUp, const c static int FileCopy(const char *srcPath, const char *dstPath); static int FileRename(const char *fileName, const char *fileRename); static int FileRemove(const char *fileName); +static int FileMove(const char *srcPath, const char *dstPath); // Update required files from examples collection // UPDATES: Makefile, Makefile.Web, README.md, examples.js @@ -236,7 +240,7 @@ int main(int argc, char *argv[]) else { // Verify example exists in collection to be removed - char *exColInfo = LoadFileText(exCollectionListPath); + char *exColInfo = LoadFileText(exCollectionFilePath); if (TextFindIndex(exColInfo, argv[2]) != -1) // Example in the collection { strcpy(exName, argv[2]); // Register example name @@ -257,7 +261,7 @@ int main(int argc, char *argv[]) else { // Verify example exists in collection to be removed - char *exColInfo = LoadFileText(exCollectionListPath); + char *exColInfo = LoadFileText(exCollectionFilePath); if (TextFindIndex(exColInfo, argv[2]) != -1) // Example in the collection { strcpy(exName, argv[2]); // Register filename for removal @@ -372,7 +376,7 @@ int main(int argc, char *argv[]) // Add example to the collection list, if not already there // NOTE: Required format: shapes;shapes_basic_shapes;⭐️☆☆☆;1.0;4.2;"Ray";@raysan5 //------------------------------------------------------------------------------------------------ - char *exColInfo = LoadFileText(exCollectionListPath); + char *exColInfo = LoadFileText(exCollectionFilePath); if (TextFindIndex(exColInfo, exName) == -1) // Example not found { char *exColInfoUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated list copy, 2MB @@ -409,7 +413,7 @@ int main(int argc, char *argv[]) memcpy(exColInfoUpdated + catIndex + textWritenSize, exColInfo + catIndex, strlen(exColInfo) - catIndex); } - SaveFileText(exCollectionListPath, exColInfoUpdated); + SaveFileText(exCollectionFilePath, exColInfoUpdated); RL_FREE(exColInfoUpdated); } else LOG("WARNING: ADD: Example is already on the collection\n"); @@ -464,7 +468,7 @@ int main(int argc, char *argv[]) if (strcmp(exCategory, exRecategory) == 0) { // Rename example on collection - FileTextReplace(exCollectionListPath, TextFormat("%s;%s", exCategory, exName), + FileTextReplace(exCollectionFilePath, TextFormat("%s;%s", exCategory, exName), TextFormat("%s;%s", exRecategory, exRename)); // Edit: Rename example code and screenshot files .c and .png @@ -491,7 +495,7 @@ int main(int argc, char *argv[]) { // WARNING: Rename with change of category // TODO: Reorder collection to place renamed example at the end of category - FileTextReplace(exCollectionListPath, TextFormat("%s;%s", exCategory, exName), + FileTextReplace(exCollectionFilePath, TextFormat("%s;%s", exCategory, exName), TextFormat("%s;%s", exRecategory, exRename)); // TODO: Move example resources from /resources to /resources @@ -536,7 +540,7 @@ int main(int argc, char *argv[]) { // Remove example from collection for files update //------------------------------------------------------------------------------------------------ - char *exColInfo = LoadFileText(exCollectionListPath); + char *exColInfo = LoadFileText(exCollectionFilePath); int exIndex = TextFindIndex(exColInfo, TextFormat("%s;%s", exCategory, exName)); if (exIndex > 0) // Example found { @@ -548,7 +552,7 @@ int main(int argc, char *argv[]) // Remove line and copy the rest next memcpy(exColInfoUpdated + exIndex, exColInfo + exIndex + lineLen + 1, strlen(exColInfo) - exIndex - lineLen); - SaveFileText(exCollectionListPath, exColInfoUpdated); + SaveFileText(exCollectionFilePath, exColInfoUpdated); RL_FREE(exColInfoUpdated); } else LOG("WARNING: REMOVE: Example not found in the collection\n"); @@ -699,13 +703,13 @@ static int UpdateRequiredFiles(void) { mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i]))); - int exCount = 0; - rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], true, &exCount); + int exCollectionCount = 0; + rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], true, &exCollectionCount); - for (int x = 0; x < exCount - 1; x++) mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat(" %s/%s \\\n", exCatList[x].category, exCatList[x].name)); - mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat(" %s/%s\n\n", exCatList[exCount - 1].category, exCatList[exCount - 1].name)); + for (int x = 0; x < exCollectionCount - 1; x++) mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat(" %s/%s \\\n", exCollection[x].category, exCollection[x].name)); + mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat(" %s/%s\n\n", exCollection[exCollectionCount - 1].category, exCollection[exCollectionCount - 1].name)); - UnloadExamplesData(exCatList); + UnloadExamplesData(exCollection); } // Add the remaining part of the original file @@ -735,13 +739,13 @@ static int UpdateRequiredFiles(void) { mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i]))); - int exCount = 0; - rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], true, &exCount); + int exCollectionCount = 0; + rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], true, &exCollectionCount); - for (int x = 0; x < exCount - 1; x++) mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat(" %s/%s \\\n", exCatList[x].category, exCatList[x].name)); - mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat(" %s/%s\n\n", exCatList[exCount - 1].category, exCatList[exCount - 1].name)); + for (int x = 0; x < exCollectionCount - 1; x++) mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat(" %s/%s \\\n", exCollection[x].category, exCollection[x].name)); + mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat(" %s/%s\n\n", exCollection[exCollectionCount - 1].category, exCollection[exCollectionCount - 1].name)); - UnloadExamplesData(exCatList); + UnloadExamplesData(exCollection); } // Add examples individual targets, considering every example resources @@ -761,14 +765,14 @@ static int UpdateRequiredFiles(void) { mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("# Compile %s examples\n", TextToUpper(exCategories[i]))); - int exCount = 0; - rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], true, &exCount); + int exCollectionCount = 0; + rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], true, &exCollectionCount); - for (int x = 0; x < exCount; x++) + for (int x = 0; x < exCollectionCount; x++) { // Scan resources used in example to list int resPathCount = 0; - char **resPaths = ScanExampleResources(TextFormat("%s/%s/%s.c", exBasePath, exCatList[x].category, exCatList[x].name), &resPathCount); + char **resPaths = ScanExampleResources(TextFormat("%s/%s/%s.c", exBasePath, exCollection[x].category, exCollection[x].name), &resPathCount); if (resPathCount > 0) { @@ -782,7 +786,7 @@ static int UpdateRequiredFiles(void) --preload-file shaders/resources/shaders/glsl330/vertex_displacement.fs@resources/shaders/glsl330/vertex_displacement.fs */ mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, - TextFormat("%s/%s: %s/%s.c\n", exCatList[x].category, exCatList[x].name, exCatList[x].category, exCatList[x].name)); + TextFormat("%s/%s: %s/%s.c\n", exCollection[x].category, exCollection[x].name, exCollection[x].category, exCollection[x].name)); mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, " $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \\\n"); for (int r = 0; r < resPathCount; r++) @@ -800,12 +804,12 @@ static int UpdateRequiredFiles(void) if (r < (resPathCount - 1)) { mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, - TextFormat(" --preload-file %s/%s@%s \\\n", exCatList[x].category, resPaths[r], resPaths[r])); + TextFormat(" --preload-file %s/%s@%s \\\n", exCollection[x].category, resPaths[r], resPaths[r])); } else { mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, - TextFormat(" --preload-file %s/%s@%s\n\n", exCatList[x].category, resPaths[r], resPaths[r])); + TextFormat(" --preload-file %s/%s@%s\n\n", exCollection[x].category, resPaths[r], resPaths[r])); } } } @@ -817,14 +821,14 @@ static int UpdateRequiredFiles(void) $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) */ mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, - TextFormat("%s/%s: %s/%s.c\n", exCatList[x].category, exCatList[x].name, exCatList[x].category, exCatList[x].name)); + TextFormat("%s/%s: %s/%s.c\n", exCollection[x].category, exCollection[x].name, exCollection[x].category, exCollection[x].name)); mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, " $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)\n\n"); } ClearExampleResources(resPaths); } - UnloadExamplesData(exCatList); + UnloadExamplesData(exCollection); } // Add the remaining part of the original file @@ -843,87 +847,100 @@ static int UpdateRequiredFiles(void) char *mdText = LoadFileText(TextFormat("%s/README.md", exBasePath)); char *mdTextUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated examples.js copy, 2MB - int mdListStartIndex = TextFindIndex(mdText, "| 01 | "); + int mdListStartIndex = TextFindIndex(mdText, "## EXAMPLES COLLECTION"); int mdIndex = 0; memcpy(mdTextUpdated, mdText, mdListStartIndex); + int exCollectionFullCount = 0; + rlExampleInfo *exCollectionFull = LoadExamplesData(exCollectionFilePath, "ALL", false, &exCollectionFullCount); + UnloadExamplesData(exCollectionFull); + + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("## EXAMPLES COLLECTION [TOTAL: %i]\n\n", exCollectionFullCount)); + // NOTE: We keep a global examples counter - for (int i = 0, catCount = 0, gCount = 0; i < MAX_EXAMPLE_CATEGORIES; i++) + for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++) { + int exCollectionCount = 0; + rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], false, &exCollectionCount); + // Every category includes some introductory text, as it is quite short, just copying it here - // NOTE: "core" text already placed in the file - if (i == 1) // "shapes" + if (i == 0) // "core" { - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: shapes\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("\n### category: core [%i]\n\n", exCollectionCount)); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, + "Examples using raylib[core](../src/rcore.c) platform functionality like window creation, inputs, drawing modes and system functionality.\n\n"); + } + else if (i == 1) // "shapes" + { + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("\n### category: shapes [%i]\n\n", exCollectionCount)); mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "Examples using raylib shapes drawing functionality, provided by raylib [shapes](../src/rshapes.c) module.\n\n"); } else if (i == 2) // "textures" { - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: textures\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("\n### category: textures [%i]\n\n", exCollectionCount)); mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "Examples using raylib textures functionality, including image/textures loading/generation and drawing, provided by raylib [textures](../src/rtextures.c) module.\n\n"); } else if (i == 3) // "text" { - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: text\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("\n### category: text [%i]\n\n", exCollectionCount)); mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "Examples using raylib text functionality, including sprite fonts loading/generation and text drawing, provided by raylib [text](../src/rtext.c) module.\n\n"); } else if (i == 4) // "models" { - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: models\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("\n### category: models [%i]\n\n", exCollectionCount)); mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "Examples using raylib models functionality, including models loading/generation and drawing, provided by raylib [models](../src/rmodels.c) module.\n\n"); } else if (i == 5) // "shaders" { - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: shaders\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("\n### category: shaders [%i]\n\n", exCollectionCount)); mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "Examples using raylib shaders functionality, including shaders loading, parameters configuration and drawing using them (model shaders and postprocessing shaders). This functionality is directly provided by raylib [rlgl](../src/rlgl.c) module.\n\n"); } else if (i == 6) // "audio" { - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: audio\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("\n### category: audio [%i]\n\n", exCollectionCount)); mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "Examples using raylib audio functionality, including sound/music loading and playing. This functionality is provided by raylib [raudio](../src/raudio.c) module. Note this module can be used standalone independently of raylib.\n\n"); } else if (i == 7) // "others" { - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "\n### category: others\n\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("\n### category: others [%i]\n\n", exCollectionCount)); mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "Examples showing raylib misc functionality that does not fit in other categories, like standalone modules usage or examples integrating external libraries.\n\n"); } - if (i > 0) - { - // Table header required - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "| ## | example | image | difficulty
level | version
created | last version
updated | original
developer |\n"); - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "|----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------|\n"); - } + // Table header required + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "| example | image | difficulty
level | version
created | last version
updated | original
developer |\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "|-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------|\n"); - rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], false, &catCount); - for (int x = 0; x < catCount; x++) + for (int x = 0; x < exCollectionCount; x++) { char stars[16] = { 0 }; for (int s = 0; s < 4; s++) { - if (s < exCatList[x].stars) strcpy(stars + 3*s, "⭐️"); + if (s < exCollection[x].stars) strcpy(stars + 3*s, "⭐️"); else strcpy(stars + 3*s, "☆"); } mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, - TextFormat("| %02i | [%s](%s/%s.c) | \"%s\" | %s | %.1f | %.1f | [%s](https://github.com/%s) |\n", - gCount + 1, exCatList[x].name, exCatList[x].category, exCatList[x].name, exCatList[x].category, exCatList[x].name, exCatList[x].name, - stars, exCatList[x].verCreated, exCatList[x].verUpdated, exCatList[x].author, exCatList[x].authorGitHub + 1)); - - gCount++; + TextFormat("| [%s](%s/%s.c) | \"%s\" | %s | %.1f | %.1f | [%s](https://github.com/%s) |\n", + exCollection[x].name, exCollection[x].category, exCollection[x].name, exCollection[x].category, exCollection[x].name, exCollection[x].name, + stars, exCollection[x].verCreated, exCollection[x].verUpdated, exCollection[x].author, exCollection[x].authorGitHub + 1)); } - UnloadExamplesData(exCatList); + UnloadExamplesData(exCollection); } + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, + "\nSome example missing? As always, contributions are welcome, feel free to send new examples!\n"); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, + "Here is an[examples template](examples_template.c) with instructions to start with!\n"); + // Save updated file SaveFileText(TextFormat("%s/README.md", exBasePath), mdTextUpdated); UnloadFileText(mdText); @@ -945,32 +962,33 @@ static int UpdateRequiredFiles(void) jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, " var exampleData = [\n"); // NOTE: We avoid "others" category - for (int i = 0, exCount = 0; i < MAX_EXAMPLE_CATEGORIES - 1; i++) + for (int i = 0; i < MAX_EXAMPLE_CATEGORIES - 1; i++) { - rlExampleInfo *exCatList = LoadExamplesData(exCollectionListPath, exCategories[i], false, &exCount); - for (int x = 0; x < exCount; x++) + int exCollectionCount = 0; + rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], false, &exCollectionCount); + for (int x = 0; x < exCollectionCount; x++) { char stars[16] = { 0 }; for (int s = 0; s < 4; s++) { - if (s < exCatList[x].stars) strcpy(stars + 3*s, "⭐️"); + if (s < exCollection[x].stars) strcpy(stars + 3*s, "⭐️"); else strcpy(stars + 3*s, "☆"); } - if ((i == 6) && (x == (exCount - 1))) + if ((i == 6) && (x == (exCollectionCount - 1))) { // NOTE: Last line to add, special case to consider jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, - TextFormat(" exampleEntry('%s', '%s', '%s')];\n", stars, exCatList[x].category, exCatList[x].name + strlen(exCatList[x].category) + 1)); + TextFormat(" exampleEntry('%s', '%s', '%s')];\n", stars, exCollection[x].category, exCollection[x].name + strlen(exCollection[x].category) + 1)); } else { jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, - TextFormat(" exampleEntry('%s', '%s', '%s'),\n", stars, exCatList[x].category, exCatList[x].name + strlen(exCatList[x].category) + 1)); + TextFormat(" exampleEntry('%s', '%s', '%s'),\n", stars, exCollection[x].category, exCollection[x].name + strlen(exCollection[x].category) + 1)); } } - UnloadExamplesData(exCatList); + UnloadExamplesData(exCollection); } // Add the remaining part of the original file @@ -1109,6 +1127,7 @@ static int FileRemove(const char *fileName) } // Move file from one directory to another +// NOTE: If dst directories do not exists they are created static int FileMove(const char *srcPath, const char *dstPath) { int result = 0; @@ -1217,12 +1236,15 @@ static void SortExampleByName(rlExampleInfo *items, int count) } // Scan resource paths in example file +// WARNING: Supported resource file extensions is hardcoded by used file types +// but new examples could require other file extensions to be added, +// maybe it should look for '.xxx")' patterns instead static char **ScanExampleResources(const char *filePath, int *resPathCount) { - #define MAX_RES_PATH_LEN 256 + #define REXM_MAX_RESOURCE_PATH_LEN 256 char **paths = (char **)RL_CALLOC(REXM_MAX_RESOURCE_PATHS, sizeof(char **)); - for (int i = 0; i < REXM_MAX_RESOURCE_PATHS; i++) paths[i] = (char *)RL_CALLOC(MAX_RES_PATH_LEN, sizeof(char)); + for (int i = 0; i < REXM_MAX_RESOURCE_PATHS; i++) paths[i] = (char *)RL_CALLOC(REXM_MAX_RESOURCE_PATH_LEN, sizeof(char)); int resCounter = 0; char *code = LoadFileText(filePath); @@ -1230,8 +1252,8 @@ static char **ScanExampleResources(const char *filePath, int *resPathCount) if (code != NULL) { // Resources extensions to check - const char *exts[] = { ".png", ".bmp", ".jpg", ".qoi", ".gif", ".raw", ".hdr", ".ttf", ".fnt", ".wav", ".ogg", ".mp3", ".flac", ".mod", ".qoa", ".qoa", ".obj", ".iqm", ".glb", ".m3d", ".vox", ".vs", ".fs" }; - const int extCount = sizeof(exts)/sizeof(exts[0]); + const char *exts[] = { ".png", ".bmp", ".jpg", ".qoi", ".gif", ".raw", ".hdr", ".ttf", ".fnt", ".wav", ".ogg", ".mp3", ".flac", ".mod", ".qoa", ".qoa", ".obj", ".iqm", ".glb", ".m3d", ".vox", ".vs", ".fs", ".txt" }; + const int extCount = sizeof(exts)/sizeof(char *); char *ptr = code; while ((ptr = strchr(ptr, '"')) != NULL) @@ -1241,9 +1263,9 @@ static char **ScanExampleResources(const char *filePath, int *resPathCount) if (!end) break; int len = end - start; - if ((len > 0) && (len < MAX_RES_PATH_LEN)) + if ((len > 0) && (len < REXM_MAX_RESOURCE_PATH_LEN)) { - char buffer[MAX_RES_PATH_LEN] = { 0 }; + char buffer[REXM_MAX_RESOURCE_PATH_LEN] = { 0 }; strncpy(buffer, start, len); buffer[len] = '\0'; From a436d935f11bfb04f006590ccc829ec467578f7e Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Aug 2025 19:28:07 +0200 Subject: [PATCH 173/242] Update README.md --- examples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index 42a3a3de0..8a387596a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -16,7 +16,7 @@ You may find it easier to use than other toolchains, especially when it comes to - `zig build [module]` to compile all examples for a module (e.g. `zig build core`) - `zig build [example]` to compile _and run_ a particular example (e.g. `zig build core_basic_window`) -## EXAMPLES LIST +## EXAMPLES COLLECTION [TOTAL: 158] ### category: core From cdfb9d7a0be1b45327b5da2dcbc2d44149222135 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Aug 2025 19:29:28 +0200 Subject: [PATCH 174/242] Added some notes for example collection `validation` --- examples/rexm.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/examples/rexm.c b/examples/rexm.c index 2f95e4000..3ed90f20c 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -622,21 +622,29 @@ int main(int argc, char *argv[]) } break; case 5: // Validate { - // TODO: Validate examples collection against [examples_list.txt] - - // Validate: raylib/examples//_example_name.c - // Validate: raylib/examples//_example_name.png - // Validate: raylib/examples//resources/.. -> Not possible for now... - // Validate: raylib/examples/Makefile - // Validate: raylib/examples/Makefile.Web - // Validate: raylib/examples/README.md - // Validate: raylib/projects/VS2022/examples/_example_name.vcxproj - // Validate: raylib/projects/VS2022/raylib.sln - // Validate: raylib.com/common/examples.js - // Validate: raylib.com/examples//_example_name.html - // Validate: raylib.com/examples//_example_name.data - // Validate: raylib.com/examples//_example_name.wasm - // Validate: raylib.com/examples//_example_name.js + // TODO: Validate examples in collection list [examples_list.txt] -> Source of truth! + // Validate: raylib/examples//_example_name.c -> File exists? + // Validate: raylib/examples//_example_name.png -> File exists? + // Validate: raylib/examples//resources/.. -> Example resources available? + // Validate: raylib/examples/Makefile -> Example listed? + // Validate: raylib/examples/Makefile.Web -> Example listed? + // Validate: raylib/examples/README.md -> Example listed? + // Validate: raylib/projects/VS2022/examples/_example_name.vcxproj -> File exists? + // Validate: raylib/projects/VS2022/raylib.sln -> Example listed? + // Validate: raylib.com/common/examples.js -> Example listed? + // Validate: raylib.com/examples//_example_name.html -> File exists? + // Validate: raylib.com/examples//_example_name.data -> File exists? + // Validate: raylib.com/examples//_example_name.wasm -> File exists? + // Validate: raylib.com/examples//_example_name.js -> File exists? + + // Additional validation elements + // Validate: Example naming conventions: /_example_name + // Validate: Duplicate entries in collection list + // Validate: Example info (stars, author, github) missmatches with example content + + // After validation, update required files for consistency + // Update files: Makefile, Makefile.Web, README.md, examples.js + UpdateRequiredFiles(); } break; default: // Help From 47c186dd87c4c0d361a2f373ecace3e3098df904 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Aug 2025 20:26:19 +0200 Subject: [PATCH 175/242] WARNING: **BREAKING**: Moved `raylib parser` to `tools/parser` directory --- .github/workflows/parse.yml | 4 ++-- {parser => tools/parser}/LICENSE | 0 {parser => tools/parser}/Makefile | 0 {parser => tools/parser}/README.md | 0 {parser => tools/parser}/output/raylib_api.json | 0 {parser => tools/parser}/output/raylib_api.lua | 0 {parser => tools/parser}/output/raylib_api.txt | 0 {parser => tools/parser}/output/raylib_api.xml | 0 {parser => tools/parser}/raylib_parser.c | 0 9 files changed, 2 insertions(+), 2 deletions(-) rename {parser => tools/parser}/LICENSE (100%) rename {parser => tools/parser}/Makefile (100%) rename {parser => tools/parser}/README.md (100%) rename {parser => tools/parser}/output/raylib_api.json (100%) rename {parser => tools/parser}/output/raylib_api.lua (100%) rename {parser => tools/parser}/output/raylib_api.txt (100%) rename {parser => tools/parser}/output/raylib_api.xml (100%) rename {parser => tools/parser}/raylib_parser.c (100%) diff --git a/.github/workflows/parse.yml b/.github/workflows/parse.yml index ee413d643..4491b142e 100644 --- a/.github/workflows/parse.yml +++ b/.github/workflows/parse.yml @@ -1,4 +1,4 @@ -name: Parse raylib_api +name: Parse raylib API on: workflow_dispatch: @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v4 - name: Update parse files - working-directory: parser + working-directory: tools/parser run: | make raylib_api mv raylib_api.* output diff --git a/parser/LICENSE b/tools/parser/LICENSE similarity index 100% rename from parser/LICENSE rename to tools/parser/LICENSE diff --git a/parser/Makefile b/tools/parser/Makefile similarity index 100% rename from parser/Makefile rename to tools/parser/Makefile diff --git a/parser/README.md b/tools/parser/README.md similarity index 100% rename from parser/README.md rename to tools/parser/README.md diff --git a/parser/output/raylib_api.json b/tools/parser/output/raylib_api.json similarity index 100% rename from parser/output/raylib_api.json rename to tools/parser/output/raylib_api.json diff --git a/parser/output/raylib_api.lua b/tools/parser/output/raylib_api.lua similarity index 100% rename from parser/output/raylib_api.lua rename to tools/parser/output/raylib_api.lua diff --git a/parser/output/raylib_api.txt b/tools/parser/output/raylib_api.txt similarity index 100% rename from parser/output/raylib_api.txt rename to tools/parser/output/raylib_api.txt diff --git a/parser/output/raylib_api.xml b/tools/parser/output/raylib_api.xml similarity index 100% rename from parser/output/raylib_api.xml rename to tools/parser/output/raylib_api.xml diff --git a/parser/raylib_parser.c b/tools/parser/raylib_parser.c similarity index 100% rename from parser/raylib_parser.c rename to tools/parser/raylib_parser.c From f5e95029b1d85761040c0073027cb0516e14ab18 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Aug 2025 20:44:22 +0200 Subject: [PATCH 176/242] Update rexm.c --- examples/rexm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/rexm.c b/examples/rexm.c index 3ed90f20c..89c794a8a 100644 --- a/examples/rexm.c +++ b/examples/rexm.c @@ -1025,7 +1025,7 @@ static rlExampleInfo *LoadExamplesData(const char *fileName, const char *categor if (text != NULL) { int lineCount = 0; - const char **lines = LoadTextLines(text, &lineCount); + char **lines = LoadTextLines(text, &lineCount); for (int i = 0; i < lineCount; i++) { @@ -1058,6 +1058,7 @@ static rlExampleInfo *LoadExamplesData(const char *fileName, const char *categor } } + UnloadTextLines(lines); UnloadFileText(text); } From 945f1410d28f2505475300877d83ee0d2123b854 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Aug 2025 23:24:43 +0200 Subject: [PATCH 177/242] MOVED: `rexm` to tools directory --- tools/rexm/LICENSE | 16 ++ tools/rexm/Makefile | 366 ++++++++++++++++++++++++++++++++ tools/rexm/README.md | 20 ++ {examples => tools/rexm}/rexm.c | 0 tools/rexm/rexm.ico | Bin 0 -> 9752 bytes tools/rexm/rexm.rc | 27 +++ 6 files changed, 429 insertions(+) create mode 100644 tools/rexm/LICENSE create mode 100644 tools/rexm/Makefile create mode 100644 tools/rexm/README.md rename {examples => tools/rexm}/rexm.c (100%) create mode 100644 tools/rexm/rexm.ico create mode 100644 tools/rexm/rexm.rc diff --git a/tools/rexm/LICENSE b/tools/rexm/LICENSE new file mode 100644 index 000000000..7d8c7bfc5 --- /dev/null +++ b/tools/rexm/LICENSE @@ -0,0 +1,16 @@ +Copyright (c) 2025 Ramon Santamaria (@raysan5) + +This software is provided "as-is", without any express or implied warranty. In no event +will the authors be held liable for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, including commercial +applications, and to alter it and redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim that you + wrote the original software. If you use this software in a product, an acknowledgment + in the product documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be misrepresented + as being the original software. + + 3. This notice may not be removed or altered from any source distribution. diff --git a/tools/rexm/Makefile b/tools/rexm/Makefile new file mode 100644 index 000000000..4ac96a1ab --- /dev/null +++ b/tools/rexm/Makefile @@ -0,0 +1,366 @@ +#************************************************************************************************** +# +# raylib makefile for Desktop platforms, Web (Wasm), Raspberry Pi (DRM mode) and Android +# +# Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) +# +# This software is provided "as-is", without any express or implied warranty. In no event +# will the authors be held liable for any damages arising from the use of this software. +# +# Permission is granted to anyone to use this software for any purpose, including commercial +# applications, and to alter it and redistribute it freely, subject to the following restrictions: +# +# 1. The origin of this software must not be misrepresented; you must not claim that you +# wrote the original software. If you use this software in a product, an acknowledgment +# in the product documentation would be appreciated but is not required. +# +# 2. Altered source versions must be plainly marked as such, and must not be misrepresented +# as being the original software. +# +# 3. This notice may not be removed or altered from any source distribution. +# +#************************************************************************************************** + +.PHONY: all clean + +# Define required environment variables +#------------------------------------------------------------------------------------------------ +# Define target platform: PLATFORM_DESKTOP, PLATFORM_WEB, PLATFORM_DRM, PLATFORM_ANDROID +PLATFORM ?= PLATFORM_DESKTOP + +# Define project variables +PROJECT_NAME ?= rexm +PROJECT_VERSION ?= 1.0 +PROJECT_BUILD_PATH ?= . +PROJECT_SOURCE_FILES ?= rexm.c + +# raylib library variables +RAYLIB_SRC_PATH ?= C:\raylib\raylib\src +RAYLIB_INCLUDE_PATH ?= $(RAYLIB_SRC_PATH) +RAYLIB_LIB_PATH ?= $(RAYLIB_SRC_PATH) + +# Library type used for raylib: STATIC (.a) or SHARED (.so/.dll) +RAYLIB_LIBTYPE ?= STATIC + +# Define compiler path on Windows +COMPILER_PATH ?= C:\raylib\w64devkit\bin + +# Build mode for project: DEBUG or RELEASE +BUILD_MODE ?= RELEASE + +# PLATFORM_WEB: Default properties +BUILD_WEB_ASYNCIFY ?= FALSE +BUILD_WEB_SHELL ?= minshell.html +BUILD_WEB_HEAP_SIZE ?= 128MB +BUILD_WEB_STACK_SIZE ?= 1MB +BUILD_WEB_ASYNCIFY_STACK_SIZE ?= 1048576 +BUILD_WEB_RESOURCES ?= FALSE +BUILD_WEB_RESOURCES_PATH ?= resources + +# Determine PLATFORM_OS in case PLATFORM_DESKTOP selected +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + # No uname.exe on MinGW!, but OS=Windows_NT on Windows! + # ifeq ($(UNAME),Msys) -> Windows + ifeq ($(OS),Windows_NT) + PLATFORM_OS = WINDOWS + export PATH := $(COMPILER_PATH):$(PATH) + else + UNAMEOS = $(shell uname) + ifeq ($(UNAMEOS),Linux) + PLATFORM_OS = LINUX + endif + ifeq ($(UNAMEOS),FreeBSD) + PLATFORM_OS = BSD + endif + ifeq ($(UNAMEOS),OpenBSD) + PLATFORM_OS = BSD + endif + ifeq ($(UNAMEOS),NetBSD) + PLATFORM_OS = BSD + endif + ifeq ($(UNAMEOS),DragonFly) + PLATFORM_OS = BSD + endif + ifeq ($(UNAMEOS),Darwin) + PLATFORM_OS = OSX + endif + endif +endif +ifeq ($(PLATFORM),PLATFORM_DRM) + UNAMEOS = $(shell uname) + ifeq ($(UNAMEOS),Linux) + PLATFORM_OS = LINUX + endif +endif + +ifeq ($(PLATFORM_OS),WINDOWS) + ifeq ($(PLATFORM),PLATFORM_WEB) + # Emscripten required variables + EMSDK_PATH ?= C:/raylib/emsdk + EMSCRIPTEN_PATH ?= $(EMSDK_PATH)/upstream/emscripten + CLANG_PATH = $(EMSDK_PATH)/upstream/bin + PYTHON_PATH = $(EMSDK_PATH)/python/3.9.2-nuget_64bit + NODE_PATH = $(EMSDK_PATH)/node/20.18.0_64bit/bin + export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH):$$(PATH) + endif +endif + +# Define default C compiler: CC +#------------------------------------------------------------------------------------------------ +CC = gcc + +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(PLATFORM_OS),OSX) + # OSX default compiler + CC = clang + endif + ifeq ($(PLATFORM_OS),BSD) + # FreeBSD, OpenBSD, NetBSD, DragonFly default compiler + CC = clang + endif +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + # HTML5 emscripten compiler + # WARNING: To compile to HTML5, code must be redesigned + # to use emscripten.h and emscripten_set_main_loop() + CC = emcc +endif +ifeq ($(PLATFORM),PLATFORM_DRM) + ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) + # Define RPI cross-compiler + #CC = armv6j-hardfloat-linux-gnueabi-gcc + CC = $(RPI_TOOLCHAIN)/bin/arm-linux-gnueabihf-gcc + endif +endif + + +# Define default make program: MAKE +#------------------------------------------------------------------------------------------------ +MAKE ?= make + +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(PLATFORM_OS),WINDOWS) + MAKE = mingw32-make + endif +endif + +# Define compiler flags: CFLAGS +#------------------------------------------------------------------------------------------------ +# -O1 defines optimization level +# -g include debug information on compilation +# -s strip unnecessary data from build +# -Wall turns on most, but not all, compiler warnings +# -std=c99 defines C language mode (standard C from 1999 revision) +# -std=gnu99 defines C language mode (GNU C from 1999 revision) +# -Wno-missing-braces ignore invalid warning (GCC bug 53119) +# -Wno-unused-value ignore unused return values of some functions (i.e. fread()) +# -D_DEFAULT_SOURCE use with -std=c99 on Linux and PLATFORM_WEB, required for timespec +CFLAGS = -std=c99 -Wall -Wno-missing-braces -Wno-unused-value -Wno-pointer-sign -D_DEFAULT_SOURCE $(PROJECT_CUSTOM_FLAGS) +#CFLAGS += -Wextra -Wmissing-prototypes -Wstrict-prototypes + +ifeq ($(BUILD_MODE),DEBUG) + CFLAGS += -g -D_DEBUG +else + ifeq ($(PLATFORM),PLATFORM_WEB) + ifeq ($(BUILD_WEB_ASYNCIFY),TRUE) + CFLAGS += -O3 + else + CFLAGS += -Os + endif + else + ifeq ($(PLATFORM_OS),OSX) + CFLAGS += -O2 + else + CFLAGS += -s -O2 + endif + endif +endif +ifeq ($(PLATFORM),PLATFORM_DRM) + CFLAGS += -std=gnu99 -DEGL_NO_X11 +endif + +# Define include paths for required headers: INCLUDE_PATHS +#------------------------------------------------------------------------------------------------ +INCLUDE_PATHS += -I. -Iexternal -I$(RAYLIB_INCLUDE_PATH) + +# Define additional directories containing required header files +ifeq ($(PLATFORM),PLATFORM_DRM) + # DRM required libraries + INCLUDE_PATHS += -I/usr/include/libdrm +endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(PLATFORM_OS),BSD) + # Consider -L$(RAYLIB_H_INSTALL_PATH) + INCLUDE_PATHS += -I/usr/local/include + endif +endif + +# Define library paths containing required libs: LDFLAGS +#------------------------------------------------------------------------------------------------ +LDFLAGS = -L. -L$(RAYLIB_LIB_PATH) + +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(PLATFORM_OS),WINDOWS) + # NOTE: The resource .rc file contains windows executable icon and properties + LDFLAGS += $(PROJECT_NAME).rc.data + # -Wl,--subsystem,windows hides the console window + ifeq ($(BUILD_MODE), RELEASE) + LDFLAGS += -Wl,--subsystem,windows + endif + endif + ifeq ($(PLATFORM_OS),BSD) + # Consider -L$(RAYLIB_INSTALL_PATH) + LDFLAGS += -Lsrc -L/usr/local/lib + endif + ifeq ($(PLATFORM_OS),LINUX) + # Reset everything. + # Precedence: immediately local, installed version, raysan5 provided libs + #LDFLAGS += -L$(RAYLIB_RELEASE_PATH) + endif +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + # -Os # size optimization + # -O2 # optimization level 2, if used, also set --memory-init-file 0 + # -sUSE_GLFW=3 # Use glfw3 library (context/input management) + # -sALLOW_MEMORY_GROWTH=1 # to allow memory resizing -> WARNING: Audio buffers could FAIL! + # -sTOTAL_MEMORY=16777216 # to specify heap memory size (default = 16MB) (67108864 = 64MB) + # -sUSE_PTHREADS=1 # multithreading support + # -sWASM=0 # disable Web Assembly, emitted by default + # -sASYNCIFY # lets synchronous C/C++ code interact with asynchronous JS + # -sFORCE_FILESYSTEM=1 # force filesystem to load/save files data + # -sASSERTIONS=1 # enable runtime checks for common memory allocation errors (-O1 and above turn it off) + # -sMINIFY_HTML=0 # minify generated html from shell.html + # --profiling # include information for code profiling + # --memory-init-file 0 # to avoid an external memory initialization code file (.mem) + # --preload-file resources # specify a resources folder for data compilation + # --source-map-base # allow debugging in browser with source map + # --shell-file shell.html # define a custom shell .html and output extension + LDFLAGS += -sUSE_GLFW=3 -sTOTAL_MEMORY=$(BUILD_WEB_HEAP_SIZE) -sSTACK_SIZE=$(BUILD_WEB_STACK_SIZE) -sFORCE_FILESYSTEM=1 -sMINIFY_HTML=0 + + # Build using asyncify + ifeq ($(BUILD_WEB_ASYNCIFY),TRUE) + LDFLAGS += -sASYNCIFY -sASYNCIFY_STACK_SIZE=$(BUILD_WEB_ASYNCIFY_STACK_SIZE) + endif + + # Add resources building if required + ifeq ($(BUILD_WEB_RESOURCES),TRUE) + LDFLAGS += --preload-file $(BUILD_WEB_RESOURCES_PATH) + endif + + # Add debug mode flags if required + ifeq ($(BUILD_MODE),DEBUG) + LDFLAGS += -sASSERTIONS=1 --profiling + endif + + # Define a custom shell .html and output extension + LDFLAGS += --shell-file $(BUILD_WEB_SHELL) + EXT = .html +endif + +# Define libraries required on linking: LDLIBS +# NOTE: To link libraries (lib.so or lib.a), use -l +#------------------------------------------------------------------------------------------------ +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(PLATFORM_OS),WINDOWS) + # Libraries for Windows desktop compilation + # NOTE: WinMM library required to set high-res timer resolution + LDLIBS = -lraylib -lopengl32 -lgdi32 -lwinmm -lcomdlg32 -lole32 + # Required for physac examples + LDLIBS += -static -lpthread + endif + ifeq ($(PLATFORM_OS),LINUX) + # Libraries for Debian GNU/Linux desktop compiling + # NOTE: Required packages: libegl1-mesa-dev + LDLIBS = -lraylib -lGL -lm -lpthread -ldl -lrt + + # On Wayland windowing system, additional libraries requires + ifeq ($(USE_WAYLAND_DISPLAY),TRUE) + LDLIBS += -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon + else + # On X11 requires also below libraries + LDLIBS += -lX11 + # NOTE: It seems additional libraries are not required any more, latest GLFW just dlopen them + #LDLIBS += -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor + endif + # Explicit link to libc + ifeq ($(RAYLIB_LIBTYPE),SHARED) + LDLIBS += -lc + endif + endif + ifeq ($(PLATFORM_OS),OSX) + # Libraries for OSX 10.9 desktop compiling + # NOTE: Required packages: libopenal-dev libegl1-mesa-dev + LDLIBS = -lraylib -framework OpenGL -framework Cocoa -framework IOKit -framework CoreAudio -framework CoreVideo + endif + ifeq ($(PLATFORM_OS),BSD) + # Libraries for FreeBSD, OpenBSD, NetBSD, DragonFly desktop compiling + # NOTE: Required packages: mesa-libs + LDLIBS = -lraylib -lGL -lpthread -lm + + # On XWindow requires also below libraries + LDLIBS += -lX11 -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor + endif +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + # Libraries for web (HTML5) compiling + LDLIBS = $(RAYLIB_LIB_PATH)/libraylib.a +endif +ifeq ($(PLATFORM),PLATFORM_DRM) + # Libraries for DRM compiling + # NOTE: Required packages: libasound2-dev (ALSA) + LDLIBS = -lraylib -lGLESv2 -lEGL -lpthread -lrt -lm -lgbm -ldrm -ldl +endif + + +# Define all object files from source files +#------------------------------------------------------------------------------------------------ +OBJS = $(patsubst %.c, %.o, $(PROJECT_SOURCE_FILES)) + +# Define processes to execute +#------------------------------------------------------------------------------------------------ +# For Android platform we call a custom Makefile.Android +ifeq ($(PLATFORM),PLATFORM_ANDROID) + MAKEFILE_TARGET = -f Makefile.Android + export PROJECT_NAME + export PROJECT_SOURCE_FILES +else + MAKEFILE_TARGET = $(PROJECT_NAME) +endif + +# Default target entry +# NOTE: We call this Makefile target or Makefile.Android target +all: + $(MAKE) $(MAKEFILE_TARGET) + +# Project target defined by PROJECT_NAME +$(PROJECT_NAME): $(OBJS) + $(CC) -o $(PROJECT_BUILD_PATH)/$(PROJECT_NAME)$(EXT) $(OBJS) $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + +# Compile source files +# NOTE: This pattern will compile every module defined on $(OBJS) +%.o: %.c + $(CC) -c $< -o $@ $(CFLAGS) $(INCLUDE_PATHS) -D$(PLATFORM) + +# Clean everything +clean: +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(PLATFORM_OS),WINDOWS) + del *.o *.exe /s + endif + ifeq ($(PLATFORM_OS),LINUX) + find . -type f -executable -delete + rm -fv *.o + endif + ifeq ($(PLATFORM_OS),OSX) + rm -f *.o external/*.o $(PROJECT_NAME) + endif +endif +ifeq ($(PLATFORM),PLATFORM_DRM) + find . -type f -executable -delete + rm -fv *.o +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + del *.o *.html *.js +endif + @echo Cleaning done + diff --git a/tools/rexm/README.md b/tools/rexm/README.md new file mode 100644 index 000000000..101a5b92e --- /dev/null +++ b/tools/rexm/README.md @@ -0,0 +1,20 @@ +## rexm + +### Description + +raylib examples manager + +### Features + + - $(Project Feature 01) + - $(Project Feature 02) + - $(Project Feature 03) + +### Command line + + +### License + +This project sources are licensed under an unmodified zlib/libpng license, which is an OSI-certified, BSD-like license that allows static linking with closed source software. Check [LICENSE](LICENSE) for further details. + +*Copyright (c) 2025 Ramon Santamaria (@raysan5)* diff --git a/examples/rexm.c b/tools/rexm/rexm.c similarity index 100% rename from examples/rexm.c rename to tools/rexm/rexm.c diff --git a/tools/rexm/rexm.ico b/tools/rexm/rexm.ico new file mode 100644 index 0000000000000000000000000000000000000000..0cedcc55cee7da0e5784256078a93f2901e5d734 GIT binary patch literal 9752 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 AR{#J2 literal 0 HcmV?d00001 diff --git a/tools/rexm/rexm.rc b/tools/rexm/rexm.rc new file mode 100644 index 000000000..ad125f796 --- /dev/null +++ b/tools/rexm/rexm.rc @@ -0,0 +1,27 @@ +GLFW_ICON ICON "rexm.ico" + +1 VERSIONINFO +FILEVERSION 1,0,0,0 +PRODUCTVERSION 1,0,0,0 +BEGIN + BLOCK "StringFileInfo" + BEGIN + //BLOCK "080904E4" // English UK + BLOCK "040904E4" // English US + BEGIN + VALUE "CompanyName", "Ramon Santamaria" + VALUE "FileDescription", "rexm | raylib examples manager" + VALUE "FileVersion", "1.0" + VALUE "InternalName", "rexm" + VALUE "LegalCopyright", "(c) 2025 Ramon Santamaria" + //VALUE "OriginalFilename", "rexm.exe" + VALUE "rexm", "rexm" + VALUE "ProductVersion", "1.0" + END + END + BLOCK "VarFileInfo" + BEGIN + //VALUE "Translation", 0x809, 1252 // English UK + VALUE "Translation", 0x409, 1252 // English US + END +END From e09c9ce281caf23e3502bc6faa0ddd1686bef972 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Aug 2025 23:36:07 +0200 Subject: [PATCH 178/242] Minor tweaks --- .gitignore | 5 +++-- src/raudio.c | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index d857bf2d4..b496d83a3 100644 --- a/.gitignore +++ b/.gitignore @@ -111,5 +111,6 @@ build/ build-*/ docgen_tmp/ -# Parser stuff -parser/raylib_parser +# Tools stuff +tools/parser/raylib_parser +tools/rexm/VS2022 diff --git a/src/raudio.c b/src/raudio.c index eb159beeb..93024722f 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -2087,7 +2087,7 @@ float GetMusicTimePlayed(Music music) int framesInFirstBuffer = music.stream.buffer->isSubBufferProcessed[0]? 0 : subBufferSize; int framesInSecondBuffer = music.stream.buffer->isSubBufferProcessed[1]? 0 : subBufferSize; int framesInBuffers = framesInFirstBuffer + framesInSecondBuffer; - if (framesInBuffers > music.frameCount) { + if ((unsigned int)framesInBuffers > music.frameCount) { if (!music.looping) framesInBuffers = music.frameCount; } int framesSentToMix = music.stream.buffer->frameCursorPos%subBufferSize; From 7267c1c8a11c05f7d9b98f2c9d05ef4f13d2d03c Mon Sep 17 00:00:00 2001 From: Nikolas Date: Tue, 5 Aug 2025 00:15:52 +0200 Subject: [PATCH 179/242] Make culling distances consistent across raylib --- src/config.h | 4 ++-- src/rcamera.h | 4 ++-- src/rcore.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/config.h b/src/config.h index bfd1fad13..9c6e334f7 100644 --- a/src/config.h +++ b/src/config.h @@ -135,8 +135,8 @@ #define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported -#define RL_CULL_DISTANCE_NEAR 0.001 // Default projection matrix near cull distance -#define RL_CULL_DISTANCE_FAR 10000.0 // Default projection matrix far cull distance +#define RL_CULL_DISTANCE_NEAR 0.05 // Default projection matrix near cull distance +#define RL_CULL_DISTANCE_FAR 4000.0 // Default projection matrix far cull distance // Default shader vertex attribute locations #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION 0 diff --git a/src/rcamera.h b/src/rcamera.h index a598e1107..62c1225ec 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -65,8 +65,8 @@ #endif #if defined(RCAMERA_STANDALONE) - #define CAMERA_CULL_DISTANCE_NEAR 0.01 - #define CAMERA_CULL_DISTANCE_FAR 1000.0 + #define CAMERA_CULL_DISTANCE_NEAR 0.05 + #define CAMERA_CULL_DISTANCE_FAR 4000.0 #else #define CAMERA_CULL_DISTANCE_NEAR RL_CULL_DISTANCE_NEAR #define CAMERA_CULL_DISTANCE_FAR RL_CULL_DISTANCE_FAR diff --git a/src/rcore.c b/src/rcore.c index 099bbe9c8..46e39991f 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1530,7 +1530,7 @@ Ray GetScreenToWorldRayEx(Vector2 position, Camera camera, int width, int height double right = top*aspect; // Calculate projection matrix from orthographic - matProj = MatrixOrtho(-right, right, -top, top, 0.01, 1000.0); + matProj = MatrixOrtho(-right, right, -top, top, rlGetCullDistanceNear(), rlGetCullDistanceFar()); } // Unproject far/near points From b683af298c3679925864faf8e76601d7ed1aa65a Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 5 Aug 2025 09:40:01 +0200 Subject: [PATCH 180/242] Update examples_template.c --- examples/examples_template.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/examples_template.c b/examples/examples_template.c index 2ee806b54..ac8747e82 100644 --- a/examples/examples_template.c +++ b/examples/examples_template.c @@ -58,9 +58,9 @@ * * raylib [] example - * -* Example complexity rating: [★☆??] ?/4 +* Example complexity rating: [★☆☆☆] 1/4 * -* Example originally created with raylib 5.5, last time updated with raylib 5.6-dev +* Example originally created with raylib 5.5, last time updated with raylib 5.6 * * Example contributed by (@) and reviewed by Ramon Santamaria (@raysan5) * From 02992ce63b4dc05608911d226d99bd1c9c2710c9 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 5 Aug 2025 09:54:17 +0200 Subject: [PATCH 181/242] UPDATE: Get example info from example header when added, to be added to collection --- tools/rexm/rexm.c | 102 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 78 insertions(+), 24 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 89c794a8a..9dc7740fd 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -376,48 +376,102 @@ int main(int argc, char *argv[]) // Add example to the collection list, if not already there // NOTE: Required format: shapes;shapes_basic_shapes;⭐️☆☆☆;1.0;4.2;"Ray";@raysan5 //------------------------------------------------------------------------------------------------ - char *exColInfo = LoadFileText(exCollectionFilePath); - if (TextFindIndex(exColInfo, exName) == -1) // Example not found + char *exCollectionList = LoadFileText(exCollectionFilePath); + if (TextFindIndex(exCollectionList, exName) == -1) // Example not found { - char *exColInfoUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated list copy, 2MB + char *exCollectionListUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated list copy, 2MB // Add example to the main list, by category // by default add it last in the category list // NOTE: When populating to other files, lists are sorted by name - int nextCatIndex = 0; - if (strcmp(exCategory, "core") == 0) nextCatIndex = 1; - else if (strcmp(exCategory, "shapes") == 0) nextCatIndex = 2; - else if (strcmp(exCategory, "textures") == 0) nextCatIndex = 3; - else if (strcmp(exCategory, "text") == 0) nextCatIndex = 4; - else if (strcmp(exCategory, "models") == 0) nextCatIndex = 5; - else if (strcmp(exCategory, "shaders") == 0) nextCatIndex = 6; - else if (strcmp(exCategory, "audio") == 0) nextCatIndex = 7; - else if (strcmp(exCategory, "others") == 0) nextCatIndex = -1; // Add to EOF + int nextCategoryIndex = 0; + if (strcmp(exCategory, "core") == 0) nextCategoryIndex = 1; + else if (strcmp(exCategory, "shapes") == 0) nextCategoryIndex = 2; + else if (strcmp(exCategory, "textures") == 0) nextCategoryIndex = 3; + else if (strcmp(exCategory, "text") == 0) nextCategoryIndex = 4; + else if (strcmp(exCategory, "models") == 0) nextCategoryIndex = 5; + else if (strcmp(exCategory, "shaders") == 0) nextCategoryIndex = 6; + else if (strcmp(exCategory, "audio") == 0) nextCategoryIndex = 7; + else if (strcmp(exCategory, "others") == 0) nextCategoryIndex = -1; // Add to EOF - // TODO: Get required example info from example file header (if provided) + // Get required example info from example file header (if provided) // NOTE: If no example info is provided (other than category/name), just using some default values + char *exText = LoadFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); + + rlExampleInfo exInfo = { 0 }; + strcpy(exInfo.category, exCategory); + strcpy(exInfo.name, exName); - if (nextCatIndex == -1) + // Get example difficulty stars + char starsText[16] = { 0 }; + int starsIndex = TextFindIndex(exText, "★"); + if (starsIndex > 0) strncpy(starsText, exText + starsIndex, 3*4); // NOTE: Every UTF-8 star are 3 bytes + else strcpy(starsText, "★☆☆☆"); + + // Get example create with raylib version + char verCreateText[4] = { 0 }; + int verCreateIndex = TextFindIndex(exText, "created with raylib "); // Version = index + 20 + if (verCreateIndex > 0) strncpy(verCreateText, exText + verCreateIndex + 20, 3); + else strncpy(verCreateText, RAYLIB_VERSION, 3); // Only pick MAJOR.MINOR + + // Get example update with raylib version + char verUpdateText[4] = { 0 }; + int verUpdateIndex = TextFindIndex(exText, "updated with raylib "); // Version = index + 20 + if (verUpdateIndex > 0) strncpy(verUpdateText, exText + verUpdateIndex + 20, 3); + else strncpy(verUpdateText, RAYLIB_VERSION, 3); // Only pick MAJOR.MINOR + + // Get example creator and github user + int authorIndex = TextFindIndex(exText, "Example contributed by "); // Author = index + 23 + int authorGitIndex = TextFindIndex(exText, "(@"); // Author GitHub user = index + 2 + if (authorIndex > 0) + { + int authorNameLen = 0; + if (authorGitIndex > 0) authorNameLen = (authorGitIndex - 1) - (authorIndex + 23); + else + { + int authorNameEndIndex = TextFindIndex(exText + authorIndex, " and reviewed by Ramon Santamaria"); + if (authorNameEndIndex == -1) authorNameEndIndex = TextFindIndex(exText + authorIndex, "\n"); + + authorNameLen = authorNameEndIndex - (authorIndex + 23); + } + strncpy(exInfo.author, exText + authorIndex + 23, authorNameLen); + } + else strcpy(exInfo.author, ""); + if (authorGitIndex > 0) + { + int authorGitEndIndex = TextFindIndex(exText + authorGitIndex, ")"); + if (authorGitEndIndex > 0) strncpy(exInfo.authorGitHub, exText + authorGitIndex + 2, authorGitEndIndex - (authorGitIndex + 2)); + } + else strcpy(exInfo.author, ""); + + // TODO: Verify copyright line + // Copyright (c) - (@) + + UnloadFileText(exText); + + if (nextCategoryIndex == -1) { // Add example to collection at the EOF - int endIndex = (int)strlen(exColInfo); - memcpy(exColInfoUpdated, exColInfo, endIndex); - sprintf(exColInfoUpdated + endIndex, TextFormat("%s;%s;⭐️☆☆☆;6.0;6.0;\"Ray\";@raysan5\n", exCategory, exName)); + int endIndex = (int)strlen(exCollectionList); + memcpy(exCollectionListUpdated, exCollectionList, endIndex); + sprintf(exCollectionListUpdated + endIndex, TextFormat("%s;%s;%s;%s;%s;\"%s\";@%s\n", + exInfo.category, exInfo.name, starsText, verCreateText, verUpdateText, exInfo.author, exInfo.authorGitHub)); } else { // Add example to collection, at the end of the category list - int catIndex = TextFindIndex(exColInfo, exCategories[nextCatIndex]); - memcpy(exColInfoUpdated, exColInfo, catIndex); - int textWritenSize = sprintf(exColInfoUpdated + catIndex, TextFormat("%s;%s;⭐️☆☆☆;6.0;6.0;\"Ray\";@raysan5\n", exCategory, exName)); - memcpy(exColInfoUpdated + catIndex + textWritenSize, exColInfo + catIndex, strlen(exColInfo) - catIndex); + int categoryIndex = TextFindIndex(exCollectionList, exCategories[nextCategoryIndex]); + memcpy(exCollectionListUpdated, exCollectionList, categoryIndex); + int textWritenSize = sprintf(exCollectionListUpdated + categoryIndex, TextFormat("%s;%s;%s;%s;%s;\"%s\";@%s\n", + exInfo.category, exInfo.name, starsText, verCreateText, verUpdateText, exInfo.author, exInfo.authorGitHub)); + memcpy(exCollectionListUpdated + categoryIndex + textWritenSize, exCollectionList + categoryIndex, strlen(exCollectionList) - categoryIndex); } - SaveFileText(exCollectionFilePath, exColInfoUpdated); - RL_FREE(exColInfoUpdated); + SaveFileText(exCollectionFilePath, exCollectionListUpdated); + RL_FREE(exCollectionListUpdated); } else LOG("WARNING: ADD: Example is already on the collection\n"); - UnloadFileText(exColInfo); + UnloadFileText(exCollectionList); //------------------------------------------------------------------------------------------------ // Update: Makefile, Makefile.Web, README.md, examples.js From 7b93591676f70daea9d80231e2f88165cfeadeec Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 5 Aug 2025 09:54:51 +0200 Subject: [PATCH 182/242] Added some notes to avoid platform-dependant .BAT scripts --- tools/rexm/rexm.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 9dc7740fd..9de591ad7 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -499,8 +499,24 @@ int main(int argc, char *argv[]) // Compile to: raylib.com/examples//_example_name.wasm // Compile to: raylib.com/examples//_example_name.js //------------------------------------------------------------------------------------------------ - // TODO: Avoid platform-specific .BAT, not portable and it does not consider RESOURCES for Web properly, - // Makefile.Web should be used... but it requires proper editing first! + // TODO: Avoid platform-specific .BAT file + /* + SET RAYLIB_PATH=C:\GitHub\raylib + SET COMPILER_PATH=C:\raylib\w64devkit\bin + ENV_SET PATH=$(COMPILER_PATH) + SET MAKE=mingw32-make + $(MAKE) -f Makefile.Web shaders/shaders_deferred_render PLATFORM=$(PLATFORM) -B + + //int putenv(char *string); // putenv takes a string of the form NAME=VALUE + //int setenv(const char *envname, const char *envval, int overwrite); + //int unsetenv(const char *name); //unset variable + putenv("RAYLIB_DIR=C:\\GitHub\\raylib"); + putenv("PATH=%PATH%;C:\\raylib\\w64devkit\\bin"); + setenv("RAYLIB_DIR", "C:\\GitHub\\raylib", 1); + unsetenv("RAYLIB_DIR"); + getenv("RAYLIB_DIR"); + system(TextFormat("make -f Makefile.Web %s/%s PLATFORM=PLATFORM_WEB -B", exCategory, exName)); + */ system(TextFormat("%s/build_example_web.bat %s/%s", exBasePath, exCategory, exName)); // Copy results to web side From 1f2b5d6282140a127e31fb737f0bd7beaa86a79a Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 5 Aug 2025 10:04:54 +0200 Subject: [PATCH 183/242] Update examples_template.c --- examples/examples_template.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/examples_template.c b/examples/examples_template.c index ac8747e82..e82bed065 100644 --- a/examples/examples_template.c +++ b/examples/examples_template.c @@ -62,12 +62,12 @@ * * Example originally created with raylib 5.5, last time updated with raylib 5.6 * -* Example contributed by (@) and reviewed by Ramon Santamaria (@raysan5) +* Example contributed by (@) 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) - (@) +* Copyright (c) - (@) * ********************************************************************************************/ From 90cf6a18da923b57736dab4e3c7df979ac16bffe Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 5 Aug 2025 12:37:11 +0200 Subject: [PATCH 184/242] REXM: Renamed some variables --- tools/rexm/rexm.c | 53 +++++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 9de591ad7..e26f27269 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -58,9 +58,12 @@ #define LOG(...) #endif -#define REXM_MAX_BUFFER_SIZE (2*1024*1024) // 2MB +#define REXM_MAX_EXAMPLES 512 +#define REXM_MAX_EXAMPLE_CATEGORIES 8 -#define REXM_MAX_RESOURCE_PATHS 256 +#define REXM_MAX_BUFFER_SIZE (2*1024*1024) // 2MB + +#define REXM_MAX_RESOURCE_PATHS 256 //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -178,7 +181,7 @@ int main(int argc, char *argv[]) char cat[12] = { 0 }; strncpy(cat, argv[2], catIndex); bool catFound = false; - for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++) + for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES; i++) { if (TextIsEqual(cat, exCategories[i])) { catFound = true; break; } } @@ -212,7 +215,7 @@ int main(int argc, char *argv[]) char cat[12] = { 0 }; strncpy(cat, argv[2], catIndex); bool catFound = false; - for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++) + for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES; i++) { if (TextIsEqual(cat, exCategories[i])) { catFound = true; break; } } @@ -274,8 +277,8 @@ int main(int argc, char *argv[]) } else if (strcmp(argv[1], "validate") == 0) { - // Validate examples in collection - // All examples in collection match all requirements on required files + // Validate examples in collection (report results) + // All examples in collection match all files requirements opCode = OP_VALIDATE; } @@ -283,7 +286,7 @@ int main(int argc, char *argv[]) switch (opCode) { - case 1: // Create: New example from template + case OP_CREATE: // Create: New example from template { // Create: raylib/examples//_example_name.c char *exText = LoadFileText(exTemplateFilePath); @@ -302,7 +305,7 @@ int main(int argc, char *argv[]) for (int i = 0; i < 6; i++) { MemFree(exTextUpdated[i]); exTextUpdated[i] = NULL; } UnloadFileText(exText); } - case 2: // Add: Example from command-line input filename + case OP_ADD: // Add: Example from command-line input filename { // Add: raylib/examples//_example_name.c if (opCode != 1) FileCopy(inFileName, TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); @@ -531,7 +534,7 @@ int main(int argc, char *argv[]) //------------------------------------------------------------------------------------------------ } break; - case 3: // Rename + case OP_RENAME: // Rename { // NOTE: At this point provided values have been validated: // exName, exCategory, exRename, exRecategory @@ -606,27 +609,27 @@ int main(int argc, char *argv[]) TextFormat("%s/%s/%s.js", exWebPath, exRecategory, exRename)); } break; - case 4: // Remove + case OP_REMOVE: // Remove { // Remove example from collection for files update //------------------------------------------------------------------------------------------------ - char *exColInfo = LoadFileText(exCollectionFilePath); - int exIndex = TextFindIndex(exColInfo, TextFormat("%s;%s", exCategory, exName)); + char *exCollectionList = LoadFileText(exCollectionFilePath); + int exIndex = TextFindIndex(exCollectionList, TextFormat("%s;%s", exCategory, exName)); if (exIndex > 0) // Example found { - char *exColInfoUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated list copy, 2MB + char *exCollectionListUpdated = (char *)RL_CALLOC(2*1024*1024, 1); // Updated list copy, 2MB - memcpy(exColInfoUpdated, exColInfo, exIndex); + memcpy(exCollectionListUpdated, exCollectionList, exIndex); int lineLen = 0; - for (int i = exIndex; (exColInfo[i] != '\n') && (exColInfo[i] != '\0'); i++) lineLen++; + for (int i = exIndex; (exCollectionList[i] != '\n') && (exCollectionList[i] != '\0'); i++) lineLen++; // Remove line and copy the rest next - memcpy(exColInfoUpdated + exIndex, exColInfo + exIndex + lineLen + 1, strlen(exColInfo) - exIndex - lineLen); + memcpy(exCollectionListUpdated + exIndex, exCollectionList + exIndex + lineLen + 1, strlen(exCollectionList) - exIndex - lineLen); - SaveFileText(exCollectionFilePath, exColInfoUpdated); - RL_FREE(exColInfoUpdated); + SaveFileText(exCollectionFilePath, exCollectionListUpdated); + RL_FREE(exCollectionListUpdated); } else LOG("WARNING: REMOVE: Example not found in the collection\n"); - UnloadFileText(exColInfo); + UnloadFileText(exCollectionList); //------------------------------------------------------------------------------------------------ // Remove: raylib/examples//resources/.. @@ -690,7 +693,7 @@ int main(int argc, char *argv[]) remove(TextFormat("%s/%s/%s.js", exWebPath, exCategory, exName)); } break; - case 5: // Validate + case OP_VALIDATE: // Validate: report and actions { // TODO: Validate examples in collection list [examples_list.txt] -> Source of truth! // Validate: raylib/examples//_example_name.c -> File exists? @@ -777,7 +780,7 @@ static int UpdateRequiredFiles(void) memcpy(mkTextUpdated, mkText, mkListStartIndex); mkIndex = sprintf(mkTextUpdated + mkListStartIndex, "#EXAMPLES_LIST_START\n"); - for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++) + for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES; i++) { mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i]))); @@ -813,7 +816,7 @@ static int UpdateRequiredFiles(void) mkwIndex = sprintf(mkwTextUpdated + mkwListStartIndex, "#EXAMPLES_LIST_START\n"); // NOTE: We avoid the "others" category on web building - for (int i = 0; i < MAX_EXAMPLE_CATEGORIES - 1; i++) + for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES - 1; i++) { mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i]))); @@ -839,7 +842,7 @@ static int UpdateRequiredFiles(void) mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "audio: $(AUDIO)\n\n"); // NOTE: We avoid the "others" category on web building - for (int i = 0; i < MAX_EXAMPLE_CATEGORIES - 1; i++) + for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES - 1; i++) { mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("# Compile %s examples\n", TextToUpper(exCategories[i]))); @@ -937,7 +940,7 @@ static int UpdateRequiredFiles(void) mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("## EXAMPLES COLLECTION [TOTAL: %i]\n\n", exCollectionFullCount)); // NOTE: We keep a global examples counter - for (int i = 0; i < MAX_EXAMPLE_CATEGORIES; i++) + for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES; i++) { int exCollectionCount = 0; rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], false, &exCollectionCount); @@ -1040,7 +1043,7 @@ static int UpdateRequiredFiles(void) jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, " var exampleData = [\n"); // NOTE: We avoid "others" category - for (int i = 0; i < MAX_EXAMPLE_CATEGORIES - 1; i++) + for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES - 1; i++) { int exCollectionCount = 0; rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], false, &exCollectionCount); From 99a43457e893632d7e28b0923208c512a986beef Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 5 Aug 2025 12:38:11 +0200 Subject: [PATCH 185/242] REXM: ADDED: Command; `update` to validate+update required files --- tools/rexm/rexm.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index e26f27269..c213ca1ec 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -8,6 +8,7 @@ * - rename * - remove * - validate +* - update * * Files involved in the processes: * - raylib/examples//_example_name.c @@ -81,12 +82,13 @@ typedef struct { // Example management operations typedef enum { - OP_NONE = 0, // No process to do - OP_CREATE = 1, // Create new example, using default template - OP_ADD = 2, // Add existing examples (hopefully following template) - OP_RENAME = 3, // Rename existing example - OP_REMOVE = 4, // Remove existing example - OP_VALIDATE = 5, // Validate examples, using [examples_list.txt] as main source by default + OP_NONE = 0, // No process to do + OP_CREATE = 1, // Create new example, using default template + OP_ADD = 2, // Add existing examples (hopefully following template) + OP_RENAME = 3, // Rename existing example + OP_REMOVE = 4, // Remove existing example + OP_VALIDATE = 5, // Validate examples, using [examples_list.txt] as main source by default + OP_UPDATE = 6, // Validate and update required examples (as far as possible) } rlExampleOperation; #define MAX_EXAMPLE_CATEGORIES 8 @@ -282,6 +284,13 @@ int main(int argc, char *argv[]) opCode = OP_VALIDATE; } + else if (strcmp(argv[1], "update") == 0) + { + // Validate and update examples in collection + // All examples in collection match all files requirements + + opCode = OP_UPDATE; + } } switch (opCode) @@ -694,6 +703,7 @@ int main(int argc, char *argv[]) } break; case OP_VALIDATE: // Validate: report and actions + case OP_UPDATE: { // TODO: Validate examples in collection list [examples_list.txt] -> Source of truth! // Validate: raylib/examples//_example_name.c -> File exists? From f3066854695f606fac714240fa45d13bc06c3227 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 5 Aug 2025 12:38:30 +0200 Subject: [PATCH 186/242] Update rexm.c --- tools/rexm/rexm.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index c213ca1ec..296ffc552 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -91,9 +91,7 @@ typedef enum { OP_UPDATE = 6, // Validate and update required examples (as far as possible) } rlExampleOperation; -#define MAX_EXAMPLE_CATEGORIES 8 - -static const char *exCategories[MAX_EXAMPLE_CATEGORIES] = { "core", "shapes", "textures", "text", "models", "shaders", "audio", "others" }; +static const char *exCategories[REXM_MAX_EXAMPLE_CATEGORIES] = { "core", "shapes", "textures", "text", "models", "shaders", "audio", "others" }; // Paths required for examples management // TODO: Avoid hardcoding path values... From 81eb48cad8f118385f7e23a2e075da029a55d62b Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Tue, 5 Aug 2025 11:44:41 +0100 Subject: [PATCH 187/242] Add compiled rexm to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b496d83a3..3a9197be3 100644 --- a/.gitignore +++ b/.gitignore @@ -114,3 +114,4 @@ docgen_tmp/ # Tools stuff tools/parser/raylib_parser tools/rexm/VS2022 +tools/rexm/rexm From 68a1190bf2cb1754ee0050ad363ce2a833303858 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 6 Aug 2025 00:56:29 +0200 Subject: [PATCH 188/242] REXM: ADDED: Examples validation --- tools/rexm/rexm.c | 403 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 315 insertions(+), 88 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 296ffc552..d5f7f02ca 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -71,15 +71,36 @@ //---------------------------------------------------------------------------------- // raylib example info struct typedef struct { - char category[16]; - char name[128]; - char stars; - float verCreated; - float verUpdated; - char author[64]; - char authorGitHub[64]; + char category[16]; // Example category: core, shapes, textures, text, models, shaders, audio, others + char name[128]; // Example name: _name_part + int stars; // Example stars count: ★☆☆☆ + float verCreated; // Example raylib creation version + float verUpdated; // Example raylib last update version + char author[64]; // Example author + char authorGitHub[64]; // Example author, GitHub user name + int resCount; // Example resources counter + int status; // Example validation status info } rlExampleInfo; +// Validation status for a single example +typedef enum { + VALID_OK = 0, // All required files and entries are present + VALID_MISSING_C = 1 << 0, // Missing .c source file + VALID_MISSING_PNG = 1 << 1, // Missing screenshot .png + VALID_INVALID_PNG = 1 << 2, // Invalid screenshot .png (using template one) + VALID_MISSING_RESOURCES = 1 << 3, // Missing resources listed in the code + VALID_MISSING_VCXPROJ = 1 << 4, // Missing Visual Studio .vcxproj file + VALID_NOT_IN_VCXSOL = 1 << 5, // Project not included in solution file + VALID_NOT_IN_MAKEFILE = 1 << 6, // Not listed in Makefile + VALID_NOT_IN_MAKEFILE_WEB = 1 << 7, // Not listed in Makefile.Web + VALID_NOT_IN_README = 1 << 8, // Not listed in README.md + VALID_NOT_IN_JS = 1 << 9, // Not listed in examples.js + VALID_INCONSISTENT_INFO = 1 << 10, // Inconsistent info between collection and example header (stars, author...) + VALID_MISSING_WEB_OUTPUT = 1 << 11, // Missing .html/.data/.wasm/.js + VALID_INVALID_CATEGORY = 1 << 12, // Not a recognized category + VALID_UNKNOWN_ERROR = 1 << 13 // Unknown failure case (fallback) +} rlExampleValidationStatus; + // Example management operations typedef enum { OP_NONE = 0, // No process to do @@ -107,11 +128,12 @@ static const char *exCollectionFilePath = "C:/GitHub/raylib/examples/examples_li //---------------------------------------------------------------------------------- // Module specific functions declaration //---------------------------------------------------------------------------------- +static int FileTextFind(const char *fileName, const char *find); static int FileTextReplace(const char *fileName, const char *textLookUp, const char *textReplace); static int FileCopy(const char *srcPath, const char *dstPath); static int FileRename(const char *fileName, const char *fileRename); -static int FileRemove(const char *fileName); static int FileMove(const char *srcPath, const char *dstPath); +static int FileRemove(const char *fileName); // Update required files from examples collection // UPDATES: Makefile, Makefile.Web, README.md, examples.js @@ -128,6 +150,9 @@ static void UnloadExamplesData(rlExampleInfo *exInfo); static char **LoadTextLines(const char *text, int *count); static void UnloadTextLines(char **text); +// Get example info from file header +static rlExampleInfo *GetExampleInfo(const char *exFileName); + // raylib example line info parser // Parses following line format: core/core_basic_window;⭐️☆☆☆;1.0;1.0;"Ray"/@raysan5 static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry); @@ -406,76 +431,36 @@ int main(int argc, char *argv[]) // Get required example info from example file header (if provided) // NOTE: If no example info is provided (other than category/name), just using some default values - char *exText = LoadFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); - - rlExampleInfo exInfo = { 0 }; - strcpy(exInfo.category, exCategory); - strcpy(exInfo.name, exName); + rlExampleInfo *exInfo = GetExampleInfo(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); // Get example difficulty stars char starsText[16] = { 0 }; - int starsIndex = TextFindIndex(exText, "★"); - if (starsIndex > 0) strncpy(starsText, exText + starsIndex, 3*4); // NOTE: Every UTF-8 star are 3 bytes - else strcpy(starsText, "★☆☆☆"); - - // Get example create with raylib version - char verCreateText[4] = { 0 }; - int verCreateIndex = TextFindIndex(exText, "created with raylib "); // Version = index + 20 - if (verCreateIndex > 0) strncpy(verCreateText, exText + verCreateIndex + 20, 3); - else strncpy(verCreateText, RAYLIB_VERSION, 3); // Only pick MAJOR.MINOR - - // Get example update with raylib version - char verUpdateText[4] = { 0 }; - int verUpdateIndex = TextFindIndex(exText, "updated with raylib "); // Version = index + 20 - if (verUpdateIndex > 0) strncpy(verUpdateText, exText + verUpdateIndex + 20, 3); - else strncpy(verUpdateText, RAYLIB_VERSION, 3); // Only pick MAJOR.MINOR - - // Get example creator and github user - int authorIndex = TextFindIndex(exText, "Example contributed by "); // Author = index + 23 - int authorGitIndex = TextFindIndex(exText, "(@"); // Author GitHub user = index + 2 - if (authorIndex > 0) + for (int i = 0; i < 4; i++) { - int authorNameLen = 0; - if (authorGitIndex > 0) authorNameLen = (authorGitIndex - 1) - (authorIndex + 23); - else - { - int authorNameEndIndex = TextFindIndex(exText + authorIndex, " and reviewed by Ramon Santamaria"); - if (authorNameEndIndex == -1) authorNameEndIndex = TextFindIndex(exText + authorIndex, "\n"); - - authorNameLen = authorNameEndIndex - (authorIndex + 23); - } - strncpy(exInfo.author, exText + authorIndex + 23, authorNameLen); + // NOTE: Every UTF-8 star are 3 bytes + if (i < exInfo->stars) strncpy(starsText + 3*i, "★", 3); + else strncpy(starsText + 3*i, "☆", 3); } - else strcpy(exInfo.author, ""); - if (authorGitIndex > 0) - { - int authorGitEndIndex = TextFindIndex(exText + authorGitIndex, ")"); - if (authorGitEndIndex > 0) strncpy(exInfo.authorGitHub, exText + authorGitIndex + 2, authorGitEndIndex - (authorGitIndex + 2)); - } - else strcpy(exInfo.author, ""); - - // TODO: Verify copyright line - // Copyright (c) - (@) - UnloadFileText(exText); - if (nextCategoryIndex == -1) { // Add example to collection at the EOF int endIndex = (int)strlen(exCollectionList); memcpy(exCollectionListUpdated, exCollectionList, endIndex); - sprintf(exCollectionListUpdated + endIndex, TextFormat("%s;%s;%s;%s;%s;\"%s\";@%s\n", - exInfo.category, exInfo.name, starsText, verCreateText, verUpdateText, exInfo.author, exInfo.authorGitHub)); + sprintf(exCollectionListUpdated + endIndex, TextFormat("%s;%s;%s;%.2f;%.2f;\"%s\";@%s\n", + exInfo->category, exInfo->name, starsText, exInfo->verCreated, exInfo->verUpdated, exInfo->author, exInfo->authorGitHub)); } else { // Add example to collection, at the end of the category list int categoryIndex = TextFindIndex(exCollectionList, exCategories[nextCategoryIndex]); memcpy(exCollectionListUpdated, exCollectionList, categoryIndex); - int textWritenSize = sprintf(exCollectionListUpdated + categoryIndex, TextFormat("%s;%s;%s;%s;%s;\"%s\";@%s\n", - exInfo.category, exInfo.name, starsText, verCreateText, verUpdateText, exInfo.author, exInfo.authorGitHub)); + int textWritenSize = sprintf(exCollectionListUpdated + categoryIndex, TextFormat("%s;%s;%s;%.2f;%.2f;\"%s\";@%s\n", + exInfo->category, exInfo->name, starsText, exInfo->verCreated, exInfo->verUpdated, exInfo->author, exInfo->authorGitHub)); memcpy(exCollectionListUpdated + categoryIndex + textWritenSize, exCollectionList + categoryIndex, strlen(exCollectionList) - categoryIndex); } + + RL_FREE(exInfo); SaveFileText(exCollectionFilePath, exCollectionListUpdated); RL_FREE(exCollectionListUpdated); @@ -703,29 +688,178 @@ int main(int argc, char *argv[]) case OP_VALIDATE: // Validate: report and actions case OP_UPDATE: { - // TODO: Validate examples in collection list [examples_list.txt] -> Source of truth! - // Validate: raylib/examples//_example_name.c -> File exists? - // Validate: raylib/examples//_example_name.png -> File exists? - // Validate: raylib/examples//resources/.. -> Example resources available? - // Validate: raylib/examples/Makefile -> Example listed? - // Validate: raylib/examples/Makefile.Web -> Example listed? - // Validate: raylib/examples/README.md -> Example listed? - // Validate: raylib/projects/VS2022/examples/_example_name.vcxproj -> File exists? - // Validate: raylib/projects/VS2022/raylib.sln -> Example listed? - // Validate: raylib.com/common/examples.js -> Example listed? - // Validate: raylib.com/examples//_example_name.html -> File exists? - // Validate: raylib.com/examples//_example_name.data -> File exists? - // Validate: raylib.com/examples//_example_name.wasm -> File exists? - // Validate: raylib.com/examples//_example_name.js -> File exists? + /* + // Validation flags available: + VALID_MISSING_C + VALID_MISSING_PNG + VALID_INVALID_PNG + VALID_MISSING_RESOURCES + VALID_MISSING_VCXPROJ + VALID_NOT_IN_VCXSOL + VALID_NOT_IN_MAKEFILE + VALID_NOT_IN_MAKEFILE_WEB + VALID_NOT_IN_README + VALID_NOT_IN_JS + VALID_INCONSISTENT_INFO + VALID_MISSING_WEB_OUTPUT + VALID_INVALID_CATEGORY + */ - // Additional validation elements - // Validate: Example naming conventions: /_example_name - // Validate: Duplicate entries in collection list - // Validate: Example info (stars, author, github) missmatches with example content + // Check all examples in collection [examples_list.txt] -> Source of truth! + int exCollectionCount = 0; + rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, "ALL", true, &exCollectionCount); - // After validation, update required files for consistency - // Update files: Makefile, Makefile.Web, README.md, examples.js - UpdateRequiredFiles(); + // TODO: Validate: Duplicate entries in collection list? + + // Get status information for all examples, using "status" field in the struct + for (int i = 0; i < exCollectionCount; i++) + { + rlExampleInfo *exInfo = &exCollection[i]; + exInfo->status = 0; + + // Validate: raylib/examples//_example_name.c -> File exists? + if (!FileExists(TextFormat("%s/%s/%s.c", exBasePath, exInfo->category, exInfo->name))) exInfo->status |= VALID_MISSING_C; + + // Validate: raylib/examples//_example_name.png -> File exists? + if (!FileExists(TextFormat("%s/%s/%s.png", exBasePath, exInfo->category, exInfo->name))) exInfo->status |= VALID_MISSING_PNG; + + // Validate: example screenshot is not the template default one + Image imScreenshot = LoadImage(TextFormat("%s/%s/%s.png", exBasePath, exInfo->category, exInfo->name)); + Image imTemplate = LoadImage(TextFormat("%s/examples_template.png", exBasePath)); + if (memcmp(imScreenshot.data, imTemplate.data, GetPixelDataSize(imScreenshot.width, imScreenshot.height, imScreenshot.format)) != 0) exInfo->status |= VALID_INVALID_PNG; + UnloadImage(imTemplate); + UnloadImage(imScreenshot); + + // Validate: raylib/examples/Makefile -> Example listed? + if (FileTextFind(TextFormat("%s/Makefile", exBasePath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_MAKEFILE; + + // Validate: raylib/examples/Makefile.Web -> Example listed? + if (FileTextFind(TextFormat("%s/Makefile.Web", exBasePath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_MAKEFILE_WEB; + + // Validate: raylib/examples/README.md -> Example listed? + if (FileTextFind(TextFormat("%s/README.md", exBasePath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_JS; + + // Validate: raylib.com/common/examples.js -> Example listed? + if (FileTextFind(TextFormat("%s/common/examples.js", exWebPath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_README; + + // Validate: raylib/projects/VS2022/examples/_example_name.vcxproj -> File exists? + if (!FileExists(TextFormat("%s/../projects/VS2022/examples/%s.png", exBasePath, exInfo->name))) exInfo->status |= VALID_MISSING_VCXPROJ; + + // Validate: raylib/projects/VS2022/raylib.sln -> Example listed? + if (FileTextFind(TextFormat("%s/../projects/VS2022/raylib.sln", exBasePath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_VCXSOL; + + // Validate: raylib/examples//resources/.. -> Example resources available? + // Scan resources used in example to check for missing resource files + char **resPaths = ScanExampleResources(TextFormat("%s/%s/%s.c", exBasePath, exInfo->category, exInfo->name), &exInfo->resCount); + if (exInfo->resCount > 0) + { + for (int r = 0; r < exInfo->resCount; r++) + { + // WARNING: Special case to consider: shaders, resource paths could use conditions: "glsl%i" + // In this case, multiple resources are required: glsl100, glsl120, glsl330 + if (TextFindIndex(resPaths[r], "glsl%i") > -1) + { + int glslVer[3] = { 100, 120, 330 }; + + for (int v = 0; v < 3; v++) + { + char *resPathUpdated = TextReplace(resPaths[r], "glsl%i", TextFormat("glsl%i", glslVer[v])); + if (!FileExists(TextFormat("%s/%s/%s", exBasePath, exInfo->category, resPathUpdated))) exInfo->status |= VALID_MISSING_RESOURCES; + RL_FREE(resPathUpdated); + } + } + else + { + if (!FileExists(TextFormat("%s/%s/%s", exBasePath, exInfo->category, resPaths[r]))) exInfo->status |= VALID_MISSING_RESOURCES; + } + } + } + ClearExampleResources(resPaths); + + // Validate: raylib.com/examples//_example_name.html -> File exists? + // Validate: raylib.com/examples//_example_name.data -> File exists? + // Validate: raylib.com/examples//_example_name.wasm -> File exists? + // Validate: raylib.com/examples//_example_name.js -> File exists? + if ((!FileExists(TextFormat("%s/examples/%s/%s.html", exWebPath, exInfo->category, exInfo->name))) || + ((exInfo->resCount > 0) && !FileExists(TextFormat("%s/examples/%s/%s.data", exWebPath, exInfo->category, exInfo->name))) || + (!FileExists(TextFormat("%s/examples/%s/%s.wasm", exWebPath, exInfo->category, exInfo->name))) || + (!FileExists(TextFormat("%s/examples/%s/%s.js", exWebPath, exInfo->category, exInfo->name)))) exInfo->status |= VALID_MISSING_WEB_OUTPUT; + + // NOTE: Additional validation elements + // Validate: Example naming conventions: /_example_name, valid category + if ((TextFindIndex(exInfo->name, exInfo->category) == -1) || + (!TextIsEqual(exInfo->category, "core") || !TextIsEqual(exInfo->category, "shapes") || + !TextIsEqual(exInfo->category, "textures") || !TextIsEqual(exInfo->category, "text") || + !TextIsEqual(exInfo->category, "models") || !TextIsEqual(exInfo->category, "shaders") || + !TextIsEqual(exInfo->category, "audio") || !TextIsEqual(exInfo->category, "others"))) exInfo->status |= VALID_INVALID_CATEGORY; + + // Validate: Example info (stars, author, github) missmatches with example header content + rlExampleInfo *exInfoHeader = GetExampleInfo(TextFormat("%s/%s/%s.c", exBasePath, exInfo->category, exInfo->name)); + + if ((strcmp(exInfo->name, exInfoHeader->name) != 0) || // NOTE: Get it from example, not file + (strcmp(exInfo->category, exInfoHeader->category) != 0) || + (strcmp(exInfo->author, exInfoHeader->author) != 0) || + (strcmp(exInfo->authorGitHub, exInfoHeader->authorGitHub) != 0) || + (exInfo->stars != exInfoHeader->stars) || + (exInfo->verCreated != exInfoHeader->verCreated) || + (exInfo->verUpdated != exInfoHeader->verUpdated)) exInfo->status |= VALID_INCONSISTENT_INFO; + + RL_FREE(exInfoHeader); + + // TODO: Generate validation report/table with results (.md) + /* + //Status Description + //OK Everything found + //MISSING One or more files are missing, some can be solved automatically + //ERROR Serious inconsistencies + + Columns: + [C] VALID_MISSING_C // Missing .c source file + [PNG] VALID_MISSING_PNG // Missing screenshot .png + [WPNG] VALID_INVALID_PNG // Invalid png screenshot (using template one) + [RES] VALID_MISSING_RESOURCES // Missing resources listed in the code + [VCX] VALID_MISSING_VCXPROJ // Missing Visual Studio .vcxproj file + [SOL] VALID_NOT_IN_VCXSOL // Project not included in solution file + [MK] VALID_NOT_IN_MAKEFILE // Not listed in Makefile + [MKWEB] VALID_NOT_IN_MAKEFILE_WEB // Not listed in Makefile.Web + [RDME] VALID_NOT_IN_README // Not listed in README.md + [JS] VALID_NOT_IN_JS // Not listed in examples.js + [WOUT] VALID_MISSING_WEB_OUTPUT // Missing .html/.data/.wasm/.js + [INFO] VALID_INCONSISTENT_INFO // Inconsistent info between collection and example header (stars, author...) + [CAT] VALID_INVALID_CATEGORY // Not a recognized category + + [STATUS] [CATEGORY] [NAME] [C] [PNG] [WPNG] [RES] [VCX] [SOL] [MK] [MKWEB] [RDME] [JS] [WOUT] [INFO] [CAT] + ----------------------------------------------------------------------------------------------------------------------- + OK core basic_window ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ + MISSING shapes colorful_lines ✘ ✔ ✘ ✔ ✘ ✔ ✔ ✘ ✔ ✔ ✔ ✔ ✔ + ERROR text broken_shader ✘ ✘ ✘ ✘ ✘ ✘ ✘ ✘ ✔ ✘ ✔ ✔ ✔ + */ + } + + UnloadExamplesData(exCollection); + //------------------------------------------------------------------------------------------------ + + if (opCode == OP_UPDATE) + { + // Actions to be performed to fix/review anything possible + //------------------------------------------------------------------------------------------------ + + // TODO: Process validation results and take actions to + // correct everything possible to make collection consistent + + // Review: Add/Remove: raylib/projects/VS2022/examples/_example_name.vcxproj + // Review: Add/remove: raylib/projects/VS2022/raylib.sln + + // Update files: Makefile, Makefile.Web, README.md, examples.js + UpdateRequiredFiles(); + + // Review examples + // Review: Add/Remove: raylib.com/examples//_example_name.html + // Review: Add/Remove: raylib.com/examples//_example_name.data + // Review: Add/Remove: raylib.com/examples//_example_name.wasm + // Review: Add/Remove: raylib.com/examples//_example_name.js + //------------------------------------------------------------------------------------------------ + } } break; default: // Help @@ -1156,6 +1290,21 @@ static void UnloadExamplesData(rlExampleInfo *exInfo) RL_FREE(exInfo); } +// Find text in existing file +static int FileTextFind(const char *fileName, const char *find) +{ + int result = -1; + + if (FileExists(fileName)) + { + char *fileText = LoadFileText(fileName); + result = TextFindIndex(fileText, find); + UnloadFileText(fileText); + } + + return result; +} + // Replace text in an existing file static int FileTextReplace(const char *fileName, const char *textLookUp, const char *textReplace) { @@ -1264,6 +1413,84 @@ static void UnloadTextLines(char **lines) RL_FREE(lines); } +// Get example info from file header +rlExampleInfo *GetExampleInfo(const char *exFileName) +{ + rlExampleInfo *exInfo = (rlExampleInfo *)RL_CALLOC(1, sizeof(rlExampleInfo)); + + if (IsFileExtension(exFileName, ".c")) + { + strcpy(exInfo->name, GetFileNameWithoutExt(exFileName)); + strncpy(exInfo->category, exInfo->name, TextFindIndex(exInfo->name, "_")); + + char *exText = LoadFileText(exFileName); + + // Get example difficulty stars + // NOTE: Counting the unicode char occurrences: ⭐️ + int starsIndex = TextFindIndex(exText, "★"); + if (starsIndex > 0) + { + const char *starPtr = exText + starsIndex; + while (*starPtr) + { + if (((unsigned char)starPtr[0] == 0xe2) && + ((unsigned char)starPtr[1] == 0xad) && + ((unsigned char)starPtr[2] == 0x90)) + { + exInfo->stars++; + starPtr += 3; // Advance past multibyte character + } + else starPtr++; + } + } + + // Get example create with raylib version + char verCreateText[4] = { 0 }; + int verCreateIndex = TextFindIndex(exText, "created with raylib "); // Version = index + 20 + if (verCreateIndex > 0) strncpy(verCreateText, exText + verCreateIndex + 20, 3); + else strncpy(verCreateText, RAYLIB_VERSION, 3); // Only pick MAJOR.MINOR + exInfo->verCreated = TextToFloat(verCreateText); + + // Get example update with raylib version + char verUpdateText[4] = { 0 }; + int verUpdateIndex = TextFindIndex(exText, "updated with raylib "); // Version = index + 20 + if (verUpdateIndex > 0) strncpy(verUpdateText, exText + verUpdateIndex + 20, 3); + else strncpy(verUpdateText, RAYLIB_VERSION, 3); // Only pick MAJOR.MINOR + exInfo->verUpdated = TextToFloat(verUpdateText); + + // Get example creator and github user + int authorIndex = TextFindIndex(exText, "Example contributed by "); // Author = index + 23 + int authorGitIndex = TextFindIndex(exText, "(@"); // Author GitHub user = index + 2 + if (authorIndex > 0) + { + int authorNameLen = 0; + if (authorGitIndex > 0) authorNameLen = (authorGitIndex - 1) - (authorIndex + 23); + else + { + int authorNameEndIndex = TextFindIndex(exText + authorIndex, " and reviewed by Ramon Santamaria"); + if (authorNameEndIndex == -1) authorNameEndIndex = TextFindIndex(exText + authorIndex, "\n"); + + authorNameLen = authorNameEndIndex - (authorIndex + 23); + } + strncpy(exInfo->author, exText + authorIndex + 23, authorNameLen); + } + else strcpy(exInfo->author, ""); + if (authorGitIndex > 0) + { + int authorGitEndIndex = TextFindIndex(exText + authorGitIndex, ")"); + if (authorGitEndIndex > 0) strncpy(exInfo->authorGitHub, exText + authorGitIndex + 2, authorGitEndIndex - (authorGitIndex + 2)); + } + else strcpy(exInfo->authorGitHub, ""); + + // TODO: Verify copyright line + // Copyright (c) - (@) + + UnloadFileText(exText); + } + + return exInfo; +} + // raylib example line info parser // Parses following line format: core;core_basic_window;⭐️☆☆☆;1.0;1.0;"Ray";@raysan5 static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry) @@ -1283,17 +1510,17 @@ static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry) // Parsing stars // NOTE: Counting the unicode char occurrences: ⭐️ - const char *ptr = tokens[2]; - while (*ptr) + const char *starPtr = tokens[2]; + while (*starPtr) { - if (((unsigned char)ptr[0] == 0xE2) && - ((unsigned char)ptr[1] == 0xAD) && - ((unsigned char)ptr[2] == 0x90)) + if (((unsigned char)starPtr[0] == 0xe2) && + ((unsigned char)starPtr[1] == 0xad) && + ((unsigned char)starPtr[2] == 0x90)) { entry->stars++; - ptr += 3; // Advance past multibyte character + starPtr += 3; // Advance past multibyte character } - else ptr++; + else starPtr++; } // Get raylib creation/update versions @@ -1352,7 +1579,7 @@ static char **ScanExampleResources(const char *filePath, int *resPathCount) char *end = strchr(start, '"'); if (!end) break; - int len = end - start; + int len = (int)(end - start); if ((len > 0) && (len < REXM_MAX_RESOURCE_PATH_LEN)) { char buffer[REXM_MAX_RESOURCE_PATH_LEN] = { 0 }; From 095319602c26cf5adfcfbae243c8a65692d43035 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 6 Aug 2025 13:09:56 +0200 Subject: [PATCH 189/242] REVIEWED: Compule for OpenGL 1.1 #5088 --- src/rlgl.h | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 2adf1753d..e83cb55a7 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1157,16 +1157,16 @@ static const char *rlGetCompressedFormatName(int format); // Get compressed form static int rlGetPixelDataSize(int width, int height, int format); // Get pixel data size in bytes (image or texture) +static Matrix rlMatrixIdentity(void); // Get identity matrix +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Auxiliar matrix math functions -typedef struct rl_float16 { - float v[16]; -} rl_float16; +typedef struct rl_float16 { float v[16]; } rl_float16; static rl_float16 rlMatrixToFloatV(Matrix mat); // Get float array of matrix data #define rlMatrixToFloat(mat) (rlMatrixToFloatV(mat).v) // Get float vector for Matrix -static Matrix rlMatrixIdentity(void); // Get identity matrix static Matrix rlMatrixMultiply(Matrix left, Matrix right); // Multiply two matrices static Matrix rlMatrixTranspose(Matrix mat); // Transposes provided matrix static Matrix rlMatrixInvert(Matrix mat); // Invert provided matrix +#endif //---------------------------------------------------------------------------------- // Module Functions Definition - Matrix operations @@ -2571,9 +2571,9 @@ void rlLoadExtensions(void *loader) TRACELOG(RL_LOG_INFO, " > Version: %s", glGetString(GL_VERSION)); TRACELOG(RL_LOG_INFO, " > GLSL: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); - RLGL.loader = (rlglLoadProc)loader; - #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + RLGL.loader = (rlglLoadProc)loader; + // NOTE: Anisotropy levels capability is an extension #ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF @@ -2632,8 +2632,13 @@ void rlLoadExtensions(void *loader) } // Get OpenGL procedure address -void *rlGetProcAddress(const char *procName) { - return RLGL.loader(procName); +void *rlGetProcAddress(const char *procName) +{ + void *func = NULL; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + func = RLGL.loader(procName); +#endif + return func; } // Get current OpenGL version @@ -5140,7 +5145,20 @@ static int rlGetPixelDataSize(int width, int height, int format) } // Auxiliar math functions +//------------------------------------------------------------------------------- +// Get identity matrix +static Matrix rlMatrixIdentity(void) +{ + Matrix result = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + return result; +} +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Get float array of matrix data static rl_float16 rlMatrixToFloatV(Matrix mat) { @@ -5166,19 +5184,6 @@ static rl_float16 rlMatrixToFloatV(Matrix mat) return result; } -// Get identity matrix -static Matrix rlMatrixIdentity(void) -{ - Matrix result = { - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - - return result; -} - // Get two matrix multiplication // NOTE: When multiplying matrices... the order matters! static Matrix rlMatrixMultiply(Matrix left, Matrix right) @@ -5276,5 +5281,6 @@ static Matrix rlMatrixInvert(Matrix mat) return result; } +#endif #endif // RLGL_IMPLEMENTATION From c3cad65d307e66307e118814ce695215967d1b12 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 6 Aug 2025 14:02:51 +0200 Subject: [PATCH 190/242] Update examples_list.txt --- examples/examples_list.txt | 174 ++++++++++++++++++------------------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/examples/examples_list.txt b/examples/examples_list.txt index f25097a56..964bae667 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -7,130 +7,130 @@ # # WARNING: List is not ordered by example name but by the display order on web # -core;core_basic_window;⭐️☆☆☆;1.0;1.0;"Ray";@raysan5 -core;core_input_keys;⭐️☆☆☆;1.0;1.0;"Ray";@raysan5 -core;core_input_mouse;⭐️☆☆☆;1.0;5.5;"Ray";@raysan5 -core;core_input_mouse_wheel;⭐️☆☆☆;1.1;1.3;"Ray";@raysan5 -core;core_input_gamepad;⭐️☆☆☆;1.1;4.2;"Ray";@raysan5 +core;core_basic_window;⭐️☆☆☆;1.0;1.0;"Ramon Santamaria";@raysan5 +core;core_input_keys;⭐️☆☆☆;1.0;1.0;"Ramon Santamaria";@raysan5 +core;core_input_mouse;⭐️☆☆☆;1.0;5.5;"Ramon Santamaria";@raysan5 +core;core_input_mouse_wheel;⭐️☆☆☆;1.1;1.3;"Ramon Santamaria";@raysan5 +core;core_input_gamepad;⭐️☆☆☆;1.1;4.2;"Ramon Santamaria";@raysan5 core;core_input_multitouch;⭐️☆☆☆;2.1;2.5;"Berni";@Berni8k -core;core_input_gestures;⭐️⭐️☆☆;1.4;4.2;"Ray";@raysan5 +core;core_input_gestures;⭐️⭐️☆☆;1.4;4.2;"Ramon Santamaria";@raysan5 core;core_input_virtual_controls;⭐️⭐️☆☆;5.0;5.0;"oblerion";@oblerion -core;core_2d_camera;⭐️⭐️☆☆;1.5;3.0;"Ray";@raysan5 +core;core_2d_camera;⭐️⭐️☆☆;1.5;3.0;"Ramon Santamaria";@raysan5 core;core_2d_camera_mouse_zoom;⭐️⭐️☆☆;4.2;4.2;"Jeffery Myers";@JeffM2501 core;core_2d_camera_platformer;⭐️⭐️⭐️☆;2.5;3.0;"arvyy";@arvyy core;core_2d_camera_split_screen;⭐️⭐️⭐️⭐️;4.5;4.5;"Gabriel dos Santos Sanches";@gabrielssanches -core;core_3d_camera_mode;⭐️☆☆☆;1.0;1.0;"Ray";@raysan5 -core;core_3d_camera_free;⭐️☆☆☆;1.3;1.3;"Ray";@raysan5 -core;core_3d_camera_first_person;⭐️⭐️☆☆;1.3;1.3;"Ray";@raysan5 +core;core_3d_camera_mode;⭐️☆☆☆;1.0;1.0;"Ramon Santamaria";@raysan5 +core;core_3d_camera_free;⭐️☆☆☆;1.3;1.3;"Ramon Santamaria";@raysan5 +core;core_3d_camera_first_person;⭐️⭐️☆☆;1.3;1.3;"Ramon Santamaria";@raysan5 core;core_3d_camera_split_screen;⭐️⭐️⭐️☆;3.7;4.0;"Jeffery Myers";@JeffM2501 -core;core_3d_picking;⭐️⭐️☆☆;1.3;4.0;"Ray";@raysan5 -core;core_world_screen;⭐️⭐️☆☆;1.3;1.4;"Ray";@raysan5 +core;core_3d_picking;⭐️⭐️☆☆;1.3;4.0;"Ramon Santamaria";@raysan5 +core;core_world_screen;⭐️⭐️☆☆;1.3;1.4;"Ramon Santamaria";@raysan5 core;core_custom_logging;⭐️⭐️⭐️☆;2.5;2.5;"Pablo Marcos Oltra";@pamarcos -core;core_window_flags;⭐️⭐️⭐️☆;3.5;3.5;"Ray";@raysan5 +core;core_window_flags;⭐️⭐️⭐️☆;3.5;3.5;"Ramon Santamaria";@raysan5 core;core_window_letterbox;⭐️⭐️☆☆;2.5;4.0;"Anata";@anatagawa -core;core_window_should_close;⭐️☆☆☆;4.2;4.2;"Ray";@raysan5 -core;core_drop_files;⭐️⭐️☆☆;1.3;4.2;"Ray";@raysan5 -core;core_random_values;⭐️☆☆☆;1.1;1.1;"Ray";@raysan5 -core;core_storage_values;⭐️⭐️☆☆;1.4;4.2;"Ray";@raysan5 -core;core_vr_simulator;⭐️⭐️⭐️☆;2.5;4.0;"Ray";@raysan5 -core;core_loading_thread;⭐️⭐️⭐️☆;2.5;3.0;"Ray";@raysan5 +core;core_window_should_close;⭐️☆☆☆;4.2;4.2;"Ramon Santamaria";@raysan5 +core;core_drop_files;⭐️⭐️☆☆;1.3;4.2;"Ramon Santamaria";@raysan5 +core;core_random_values;⭐️☆☆☆;1.1;1.1;"Ramon Santamaria";@raysan5 +core;core_storage_values;⭐️⭐️☆☆;1.4;4.2;"Ramon Santamaria";@raysan5 +core;core_vr_simulator;⭐️⭐️⭐️☆;2.5;4.0;"Ramon Santamaria";@raysan5 +core;core_loading_thread;⭐️⭐️⭐️☆;2.5;3.0;"Ramon Santamaria";@raysan5 core;core_scissor_test;⭐️☆☆☆;2.5;3.0;"Chris Dill";@MysteriousSpace -core;core_basic_screen_manager;⭐️☆☆☆;4.0;4.0;"Ray";@raysan5 -core;core_custom_frame_control;⭐️⭐️⭐️⭐️;4.0;4.0;"Ray";@raysan5 +core;core_basic_screen_manager;⭐️☆☆☆;4.0;4.0;"Ramon Santamaria";@raysan5 +core;core_custom_frame_control;⭐️⭐️⭐️⭐️;4.0;4.0;"Ramon Santamaria";@raysan5 core;core_smooth_pixelperfect;⭐️⭐️⭐️☆;3.7;4.0;"Giancamillo Alessandroni";@NotManyIdeasDev core;core_random_sequence;⭐️☆☆☆;5.0;5.0;"Dalton Overmyer";@REDl3east -core;core_basic_window_web;⭐️☆☆☆;1.3;1.3;"Ray";@raysan5 +core;core_basic_window_web;⭐️☆☆☆;1.3;1.3;"Ramon Santamaria";@raysan5 core;core_input_gestures_web;⭐️⭐️☆☆;4.6-dev;4.6-dev;"ubkp";@ubkp -core;core_automation_events;⭐️⭐️⭐️☆;5.0;5.0;"Ray";@raysan5 +core;core_automation_events;⭐️⭐️⭐️☆;5.0;5.0;"Ramon Santamaria";@raysan5 core;core_high_dpi;⭐️☆☆☆;5.0;5.0;"Jonathan Marler";@marler8997 -shapes;shapes_basic_shapes;⭐️☆☆☆;1.0;4.2;"Ray";@raysan5 -shapes;shapes_bouncing_ball;⭐️☆☆☆;2.5;2.5;"Ray";@raysan5 -shapes;shapes_colors_palette;⭐️⭐️☆☆;1.0;2.5;"Ray";@raysan5 -shapes;shapes_logo_raylib;⭐️☆☆☆;1.0;1.0;"Ray";@raysan5 -shapes;shapes_logo_raylib_anim;⭐️⭐️☆☆;2.5;4.0;"Ray";@raysan5 +shapes;shapes_basic_shapes;⭐️☆☆☆;1.0;4.2;"Ramon Santamaria";@raysan5 +shapes;shapes_bouncing_ball;⭐️☆☆☆;2.5;2.5;"Ramon Santamaria";@raysan5 +shapes;shapes_colors_palette;⭐️⭐️☆☆;1.0;2.5;"Ramon Santamaria";@raysan5 +shapes;shapes_logo_raylib;⭐️☆☆☆;1.0;1.0;"Ramon Santamaria";@raysan5 +shapes;shapes_logo_raylib_anim;⭐️⭐️☆☆;2.5;4.0;"Ramon Santamaria";@raysan5 shapes;shapes_rectangle_scaling;⭐️⭐️☆☆;2.5;2.5;"Vlad Adrian";@demizdor -shapes;shapes_lines_bezier;⭐️☆☆☆;1.7;1.7;"Ray";@raysan5 -shapes;shapes_collision_area;⭐️⭐️☆☆;2.5;2.5;"Ray";@raysan5 -shapes;shapes_following_eyes;⭐️⭐️☆☆;2.5;2.5;"Ray";@raysan5 -shapes;shapes_easings_ball_anim;⭐️⭐️☆☆;2.5;2.5;"Ray";@raysan5 -shapes;shapes_easings_box_anim;⭐️⭐️☆☆;2.5;2.5;"Ray";@raysan5 -shapes;shapes_easings_rectangle_array;⭐️⭐️⭐️☆;2.0;2.5;"Ray";@raysan5 +shapes;shapes_lines_bezier;⭐️☆☆☆;1.7;1.7;"Ramon Santamaria";@raysan5 +shapes;shapes_collision_area;⭐️⭐️☆☆;2.5;2.5;"Ramon Santamaria";@raysan5 +shapes;shapes_following_eyes;⭐️⭐️☆☆;2.5;2.5;"Ramon Santamaria";@raysan5 +shapes;shapes_easings_ball_anim;⭐️⭐️☆☆;2.5;2.5;"Ramon Santamaria";@raysan5 +shapes;shapes_easings_box_anim;⭐️⭐️☆☆;2.5;2.5;"Ramon Santamaria";@raysan5 +shapes;shapes_easings_rectangle_array;⭐️⭐️⭐️☆;2.0;2.5;"Ramon Santamaria";@raysan5 shapes;shapes_draw_ring;⭐️⭐️⭐️☆;2.5;2.5;"Vlad Adrian";@demizdor shapes;shapes_draw_circle_sector;⭐️⭐️⭐️☆;2.5;2.5;"Vlad Adrian";@demizdor shapes;shapes_draw_rectangle_rounded;⭐️⭐️⭐️☆;2.5;2.5;"Vlad Adrian";@demizdor shapes;shapes_top_down_lights;⭐️⭐️⭐️⭐️;4.2;4.2;"Jeffery Myers";@JeffM2501 shapes;shapes_rectangle_advanced;⭐️⭐️⭐️⭐️;5.5;5.5;"Everton Jr.";@evertonse -shapes;shapes_splines_drawing;⭐️⭐️⭐️☆;5.0;5.0;"Ray";@raysan5 +shapes;shapes_splines_drawing;⭐️⭐️⭐️☆;5.0;5.0;"Ramon Santamaria";@raysan5 shapes;shapes_digital_clock;⭐️⭐️☆☆;5.5;5.5;"Hamza RAHAL";@rhmz-rhl shapes;shapes_double_pendulum;⭐️⭐️☆☆;5.5;5.5;"JoeCheong";@Joecheong2006 -textures;textures_logo_raylib;⭐️☆☆☆;1.0;1.0;"Ray";@raysan5 -textures;textures_srcrec_dstrec;⭐️⭐️⭐️☆;1.3;1.3;"Ray";@raysan5 -textures;textures_image_drawing;⭐️⭐️☆☆;1.4;1.4;"Ray";@raysan5 +textures;textures_logo_raylib;⭐️☆☆☆;1.0;1.0;"Ramon Santamaria";@raysan5 +textures;textures_srcrec_dstrec;⭐️⭐️⭐️☆;1.3;1.3;"Ramon Santamaria";@raysan5 +textures;textures_image_drawing;⭐️⭐️☆☆;1.4;1.4;"Ramon Santamaria";@raysan5 textures;textures_image_generation;⭐️⭐️☆☆;1.8;1.8;"Wilhem Barbier";@nounoursheureux -textures;textures_image_loading;⭐️☆☆☆;1.3;1.3;"Ray";@raysan5 -textures;textures_image_processing;⭐️⭐️⭐️☆;1.4;3.5;"Ray";@raysan5 -textures;textures_image_text;⭐️⭐️☆☆;1.8;4.0;"Ray";@raysan5 -textures;textures_to_image;⭐️☆☆☆;1.3;4.0;"Ray";@raysan5 -textures;textures_raw_data;⭐️⭐️⭐️☆;1.3;3.5;"Ray";@raysan5 -textures;textures_particles_blending;⭐️☆☆☆;1.7;3.5;"Ray";@raysan5 +textures;textures_image_loading;⭐️☆☆☆;1.3;1.3;"Ramon Santamaria";@raysan5 +textures;textures_image_processing;⭐️⭐️⭐️☆;1.4;3.5;"Ramon Santamaria";@raysan5 +textures;textures_image_text;⭐️⭐️☆☆;1.8;4.0;"Ramon Santamaria";@raysan5 +textures;textures_to_image;⭐️☆☆☆;1.3;4.0;"Ramon Santamaria";@raysan5 +textures;textures_raw_data;⭐️⭐️⭐️☆;1.3;3.5;"Ramon Santamaria";@raysan5 +textures;textures_particles_blending;⭐️☆☆☆;1.7;3.5;"Ramon Santamaria";@raysan5 textures;textures_npatch_drawing;⭐️⭐️⭐️☆;2.0;2.5;"Jorge A. Gomes";@overdev -textures;textures_background_scrolling;⭐️☆☆☆;2.0;2.5;"Ray";@raysan5 -textures;textures_sprite_anim;⭐️⭐️☆☆;1.3;1.3;"Ray";@raysan5 -textures;textures_sprite_button;⭐️⭐️☆☆;2.5;2.5;"Ray";@raysan5 -textures;textures_sprite_explosion;⭐️⭐️☆☆;2.5;3.5;"Ray";@raysan5 -textures;textures_bunnymark;⭐️⭐️⭐️☆;1.6;2.5;"Ray";@raysan5 +textures;textures_background_scrolling;⭐️☆☆☆;2.0;2.5;"Ramon Santamaria";@raysan5 +textures;textures_sprite_anim;⭐️⭐️☆☆;1.3;1.3;"Ramon Santamaria";@raysan5 +textures;textures_sprite_button;⭐️⭐️☆☆;2.5;2.5;"Ramon Santamaria";@raysan5 +textures;textures_sprite_explosion;⭐️⭐️☆☆;2.5;3.5;"Ramon Santamaria";@raysan5 +textures;textures_bunnymark;⭐️⭐️⭐️☆;1.6;2.5;"Ramon Santamaria";@raysan5 textures;textures_mouse_painting;⭐️⭐️⭐️☆;3.0;3.0;"Chris Dill";@MysteriousSpace textures;textures_blend_modes;⭐️☆☆☆;3.5;3.5;"Karlo Licudine";@accidentalrebel textures;textures_draw_tiled;⭐️⭐️⭐️☆;3.0;4.2;"Vlad Adrian";@demizdor textures;textures_polygon;⭐️☆☆☆;3.7;3.7;"Chris Camacho";@chriscamacho -textures;textures_fog_of_war;⭐️⭐️⭐️☆;4.2;4.2;"Ray";@raysan5 -textures;textures_gif_player;⭐️⭐️⭐️☆;4.2;4.2;"Ray";@raysan5 +textures;textures_fog_of_war;⭐️⭐️⭐️☆;4.2;4.2;"Ramon Santamaria";@raysan5 +textures;textures_gif_player;⭐️⭐️⭐️☆;4.2;4.2;"Ramon Santamaria";@raysan5 textures;textures_image_kernel;⭐️⭐️⭐️⭐️;1.3;1.3;"Karim Salem";@kimo-s textures;textures_image_channel;⭐️⭐️☆☆;5.1-dev;5.1-dev;"Bruno Cabral";@brccabral -textures;textures_image_rotate;⭐️⭐️☆☆;1.0;1.0;"Ray";@raysan5 +textures;textures_image_rotate;⭐️⭐️☆☆;1.0;1.0;"Ramon Santamaria";@raysan5 textures;textures_textured_curve;⭐️⭐️⭐️☆;4.5;4.5;"Jeffery Myers";@JeffM2501 -text;text_raylib_fonts;⭐️☆☆☆;1.7;3.7;"Ray";@raysan5 -text;text_font_spritefont;⭐️☆☆☆;1.0;1.0;"Ray";@raysan5 -text;text_font_filters;⭐️⭐️☆☆;1.3;4.2;"Ray";@raysan5 -text;text_font_loading;⭐️☆☆☆;1.4;3.0;"Ray";@raysan5 -text;text_font_sdf;⭐️⭐️⭐️☆;1.3;4.0;"Ray";@raysan5 -text;text_format_text;⭐️☆☆☆;1.1;3.0;"Ray";@raysan5 -text;text_input_box;⭐️⭐️☆☆;1.7;3.5;"Ray";@raysan5 -text;text_writing_anim;⭐️⭐️☆☆;1.4;1.4;"Ray";@raysan5 +text;text_raylib_fonts;⭐️☆☆☆;1.7;3.7;"Ramon Santamaria";@raysan5 +text;text_font_spritefont;⭐️☆☆☆;1.0;1.0;"Ramon Santamaria";@raysan5 +text;text_font_filters;⭐️⭐️☆☆;1.3;4.2;"Ramon Santamaria";@raysan5 +text;text_font_loading;⭐️☆☆☆;1.4;3.0;"Ramon Santamaria";@raysan5 +text;text_font_sdf;⭐️⭐️⭐️☆;1.3;4.0;"Ramon Santamaria";@raysan5 +text;text_format_text;⭐️☆☆☆;1.1;3.0;"Ramon Santamaria";@raysan5 +text;text_input_box;⭐️⭐️☆☆;1.7;3.5;"Ramon Santamaria";@raysan5 +text;text_writing_anim;⭐️⭐️☆☆;1.4;1.4;"Ramon Santamaria";@raysan5 text;text_rectangle_bounds;⭐️⭐️⭐️⭐️;2.5;4.0;"Vlad Adrian";@demizdor text;text_unicode;⭐️⭐️⭐️⭐️;2.5;4.0;"Vlad Adrian";@demizdor text;text_draw_3d;⭐️⭐️⭐️⭐️;3.5;4.0;"Vlad Adrian";@demizdor -text;text_codepoints_loading;⭐️⭐️⭐️☆;4.2;4.2;"Ray";@raysan5 +text;text_codepoints_loading;⭐️⭐️⭐️☆;4.2;4.2;"Ramon Santamaria";@raysan5 models;models_animation;⭐️⭐️☆☆;2.5;3.5;"Culacant";@culacant -models;models_billboard;⭐️⭐️⭐️☆;1.3;3.5;"Ray";@raysan5 -models;models_box_collisions;⭐️☆☆☆;1.3;3.5;"Ray";@raysan5 -models;models_cubicmap;⭐️⭐️☆☆;1.8;3.5;"Ray";@raysan5 -models;models_first_person_maze;⭐️⭐️☆☆;2.5;3.5;"Ray";@raysan5 -models;models_geometric_shapes;⭐️☆☆☆;1.0;3.5;"Ray";@raysan5 -models;models_mesh_generation;⭐️⭐️☆☆;1.8;4.0;"Ray";@raysan5 +models;models_billboard;⭐️⭐️⭐️☆;1.3;3.5;"Ramon Santamaria";@raysan5 +models;models_box_collisions;⭐️☆☆☆;1.3;3.5;"Ramon Santamaria";@raysan5 +models;models_cubicmap;⭐️⭐️☆☆;1.8;3.5;"Ramon Santamaria";@raysan5 +models;models_first_person_maze;⭐️⭐️☆☆;2.5;3.5;"Ramon Santamaria";@raysan5 +models;models_geometric_shapes;⭐️☆☆☆;1.0;3.5;"Ramon Santamaria";@raysan5 +models;models_mesh_generation;⭐️⭐️☆☆;1.8;4.0;"Ramon Santamaria";@raysan5 models;models_mesh_picking;⭐️⭐️⭐️☆;1.7;4.0;"Joel Davis";@joeld42 -models;models_loading;⭐️☆☆☆;2.0;4.2;"Ray";@raysan5 -models;models_loading_gltf;⭐️☆☆☆;3.7;4.2;"Ray";@raysan5 +models;models_loading;⭐️☆☆☆;2.0;4.2;"Ramon Santamaria";@raysan5 +models;models_loading_gltf;⭐️☆☆☆;3.7;4.2;"Ramon Santamaria";@raysan5 models;models_loading_vox;⭐️☆☆☆;4.0;4.0;"Johann Nadalutti";@procfxgen models;models_loading_m3d;⭐️⭐️☆☆;4.5;4.5;"bzt";@model3d models;models_orthographic_projection;⭐️☆☆☆;2.0;3.7;"Max Danielsson";@autious models;models_point_rendering;⭐️⭐️⭐️☆;5.0;5.0;"Reese Gallagher";@satchelfrost -models;models_rlgl_solar_system;⭐️⭐️⭐️⭐️;2.5;4.0;"Ray";@raysan5 +models;models_rlgl_solar_system;⭐️⭐️⭐️⭐️;2.5;4.0;"Ramon Santamaria";@raysan5 models;models_yaw_pitch_roll;⭐️⭐️☆☆;1.8;4.0;"Berni";@Berni8k models;models_waving_cubes;⭐️⭐️⭐️☆;2.5;3.7;"Codecat";@codecat -models;models_heightmap;⭐️☆☆☆;1.8;3.5;"Ray";@raysan5 -models;models_skybox;⭐️⭐️☆☆;1.8;4.0;"Ray";@raysan5 -models;models_draw_cube_texture;⭐️⭐️☆☆;4.5;4.5;"Ray";@raysan5 +models;models_heightmap;⭐️☆☆☆;1.8;3.5;"Ramon Santamaria";@raysan5 +models;models_skybox;⭐️⭐️☆☆;1.8;4.0;"Ramon Santamaria";@raysan5 +models;models_draw_cube_texture;⭐️⭐️☆☆;4.5;4.5;"Ramon Santamaria";@raysan5 models;models_gpu_skinning;⭐️⭐️⭐️☆;4.5;4.5;"Daniel Holden";@orangeduck models;models_bone_socket;⭐️⭐️⭐️⭐️;4.5;4.5;"iP";@ipzaur models;models_tesseract_view;⭐️⭐️☆☆;5.6-dev;5.6-dev;"Timothy van der Valk";@arceryz shaders;shaders_basic_lighting;⭐️⭐️⭐️⭐️;3.0;4.2;"Chris Camacho";@chriscamacho -shaders;shaders_model_shader;⭐️⭐️☆☆;1.3;3.7;"Ray";@raysan5 -shaders;shaders_shapes_textures;⭐️⭐️☆☆;1.7;3.7;"Ray";@raysan5 -shaders;shaders_custom_uniform;⭐️⭐️☆☆;1.3;4.0;"Ray";@raysan5 -shaders;shaders_postprocessing;⭐️⭐️⭐️☆;1.3;4.0;"Ray";@raysan5 +shaders;shaders_model_shader;⭐️⭐️☆☆;1.3;3.7;"Ramon Santamaria";@raysan5 +shaders;shaders_shapes_textures;⭐️⭐️☆☆;1.7;3.7;"Ramon Santamaria";@raysan5 +shaders;shaders_custom_uniform;⭐️⭐️☆☆;1.3;4.0;"Ramon Santamaria";@raysan5 +shaders;shaders_postprocessing;⭐️⭐️⭐️☆;1.3;4.0;"Ramon Santamaria";@raysan5 shaders;shaders_palette_switch;⭐️⭐️⭐️☆;2.5;3.7;"Marco Lizza";@MarcoLizza -shaders;shaders_raymarching;⭐️⭐️⭐️⭐️;2.0;4.2;"Ray";@raysan5 +shaders;shaders_raymarching;⭐️⭐️⭐️⭐️;2.0;4.2;"Ramon Santamaria";@raysan5 shaders;shaders_texture_drawing;⭐️⭐️☆☆;2.0;3.7;"Michał Ciesielski";@ciessielski shaders;shaders_texture_outline;⭐️⭐️⭐️☆;4.0;4.0;"Samuel Skiff";@GoldenThumbs shaders;shaders_texture_waves;⭐️⭐️☆☆;2.5;3.7;"Anata";@anatagawa @@ -138,9 +138,9 @@ shaders;shaders_julia_set;⭐️⭐️⭐️☆;2.5;4.0;"Josh Colclough";@joshco shaders;shaders_eratosthenes;⭐️⭐️⭐️☆;2.5;4.0;"ProfJski";@ProfJski shaders;shaders_fog;⭐️⭐️⭐️☆;2.5;3.7;"Chris Camacho";@chriscamacho shaders;shaders_simple_mask;⭐️⭐️☆☆;2.5;3.7;"Chris Camacho";@chriscamacho -shaders;shaders_hot_reloading;⭐️⭐️⭐️☆;3.0;3.5;"Ray";@raysan5 +shaders;shaders_hot_reloading;⭐️⭐️⭐️☆;3.0;3.5;"Ramon Santamaria";@raysan5 shaders;shaders_mesh_instancing;⭐️⭐️⭐️⭐️;3.7;4.2;"seanpringle";@seanpringle -shaders;shaders_multi_sample2d;⭐️⭐️☆☆;3.5;3.5;"Ray";@raysan5 +shaders;shaders_multi_sample2d;⭐️⭐️☆☆;3.5;3.5;"Ramon Santamaria";@raysan5 shaders;shaders_spotlight;⭐️⭐️☆☆;2.5;3.7;"Chris Camacho";@chriscamacho shaders;shaders_deferred_render;⭐️⭐️⭐️⭐️;4.5;4.5;"Justin Andreas Lacoste";@27justin shaders;shaders_hybrid_render;⭐️⭐️⭐️⭐️;4.2;4.2;"Buğra Alptekin Sarı";@BugraAlptekinSari @@ -152,17 +152,17 @@ shaders;shaders_basic_pbr;⭐️⭐️⭐️⭐️;5.0;5.1-dev;"Afan OLOVCIC";@_ shaders;shaders_lightmap;⭐️⭐️⭐️☆;4.5;4.5;"Jussi Viitala";@nullstare shaders;shaders_rounded_rectangle;⭐️⭐️⭐️☆;5.5;5.5;"Anstro Pleuton";@anstropleuton shaders;shaders_view_depth;⭐️⭐️⭐️☆;5.6-dev;5.6-dev;"Luís Almeida";@luis605 -audio;audio_module_playing;⭐️☆☆☆;1.5;3.5;"Ray";@raysan5 -audio;audio_music_stream;⭐️☆☆☆;1.3;4.2;"Ray";@raysan5 -audio;audio_raw_stream;⭐️⭐️⭐️☆;1.6;4.2;"Ray";@raysan5 -audio;audio_sound_loading;⭐️☆☆☆;1.1;3.5;"Ray";@raysan5 +audio;audio_module_playing;⭐️☆☆☆;1.5;3.5;"Ramon Santamaria";@raysan5 +audio;audio_music_stream;⭐️☆☆☆;1.3;4.2;"Ramon Santamaria";@raysan5 +audio;audio_raw_stream;⭐️⭐️⭐️☆;1.6;4.2;"Ramon Santamaria";@raysan5 +audio;audio_sound_loading;⭐️☆☆☆;1.1;3.5;"Ramon Santamaria";@raysan5 audio;audio_mixed_processor;⭐️⭐️⭐️⭐️;4.2;4.2;"hkc";@hatkidchan -audio;audio_stream_effects;⭐️⭐️⭐️⭐️;4.2;5.0;"Ray";@raysan5 +audio;audio_stream_effects;⭐️⭐️⭐️⭐️;4.2;5.0;"Ramon Santamaria";@raysan5 audio;audio_sound_multi;⭐️⭐️☆☆;4.6;4.6;"Jeffery Myers";@JeffM2501 audio;audio_sound_positioning;⭐️⭐️☆☆;5.5;5.5;"Le Juez Victor";@Bigfoot71 -others;rlgl_standalone;⭐️⭐️⭐️⭐️;1.6;4.0;"Ray";@raysan5 +others;rlgl_standalone;⭐️⭐️⭐️⭐️;1.6;4.0;"Ramon Santamaria";@raysan5 others;rlgl_compute_shader;⭐️⭐️⭐️⭐️;4.0;4.0;"Teddy Astie";@tsnake41 others;easings_testbed;⭐️⭐️⭐️☆;2.5;3.0;"Juan Miguel López";@flashback-fx others;raylib_opengl_interop;⭐️⭐️⭐️⭐️;3.8;4.0;"Stephan Soller";@arkanis others;embedded_files_loading;⭐️⭐️☆☆;3.0;3.5;"Kristian Holmgren";@defutura -others;raymath_vector_angle;⭐️⭐️☆☆;1.0;4.6;"Ray";@raysan5 +others;raymath_vector_angle;⭐️⭐️☆☆;1.0;4.6;"Ramon Santamaria";@raysan5 From c95fd5a8034203c30f4371cd1873dc0a8e9d020e Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 6 Aug 2025 14:03:32 +0200 Subject: [PATCH 191/242] REXM: Support paths customization with environment variables --- tools/rexm/rexm.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index d5f7f02ca..fc6f646d3 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -116,14 +116,11 @@ static const char *exCategories[REXM_MAX_EXAMPLE_CATEGORIES] = { "core", "shapes // Paths required for examples management // TODO: Avoid hardcoding path values... -static const char *exBasePath = "C:/GitHub/raylib/examples"; -static const char *exWebPath = "C:/GitHub/raylib.com/examples"; -static const char *exTemplateFilePath = "C:/GitHub/raylib/examples/examples_template.c"; -static const char *exTemplateScreenshot = "C:/GitHub/raylib/examples/examples_template.png"; -static const char *exCollectionFilePath = "C:/GitHub/raylib/examples/examples_list.txt"; - -//const char *exBasePath = getenv("REXM_EXAMPLES_PATH"); -//if (!exBasePath) exBasePath = "default/path"; +static const char *exBasePath = NULL; //"C:/GitHub/raylib/examples"; +static const char *exWebPath = NULL; //"C:/GitHub/raylib.com/examples"; +static const char *exTemplateFilePath = NULL; //"C:/GitHub/raylib/examples/examples_template.c"; +static const char *exTemplateScreenshot = NULL; //"C:/GitHub/raylib/examples/examples_template.png"; +static const char *exCollectionFilePath = NULL; //"C:/GitHub/raylib/examples/examples_list.txt"; //---------------------------------------------------------------------------------- // Module specific functions declaration @@ -172,6 +169,19 @@ static void ClearExampleResources(char **resPaths); //------------------------------------------------------------------------------------ int main(int argc, char *argv[]) { + // Path values can be configured with environment variables + exBasePath = getenv("REXM_EXAMPLES_BASE_PATH"); + exWebPath = getenv("REXM_EXAMPLES_WEB_PATH"); + exTemplateFilePath = getenv("REXM_EXAMPLES_TEMPLATE_FILE_PATH"); + exTemplateScreenshot = getenv("REXM_EXAMPLES_TEMPLATE_SCREENSHOT_PATH"); + exCollectionFilePath = getenv("REXM_EXAMPLES_COLLECTION_FILE_PATH"); + + if (!exBasePath) exBasePath = "C:/GitHub/raylib/examples"; + if (!exWebPath) exWebPath = "C:/GitHub/raylib.com/examples"; + if (!exTemplateFilePath) exTemplateFilePath = "C:/GitHub/raylib/examples/examples_template.c"; + if (!exTemplateScreenshot) exTemplateScreenshot = "C:/GitHub/raylib/examples/examples_template.png"; + if (!exCollectionFilePath) exCollectionFilePath = "C:/GitHub/raylib/examples/examples_list.txt"; + char inFileName[1024] = { 0 }; // Example input filename (to be added) char exName[64] = { 0 }; // Example name, without extension: core_basic_window From 59546eb54ad950eb882627670c759a595d338acf Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 6 Aug 2025 14:04:30 +0200 Subject: [PATCH 192/242] REXM: Support examples data `validation` and **report generation** --- tools/rexm/rexm.c | 247 +++++++++++++++++++++++++++++----------------- 1 file changed, 154 insertions(+), 93 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index fc6f646d3..702948d4d 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -147,8 +147,9 @@ static void UnloadExamplesData(rlExampleInfo *exInfo); static char **LoadTextLines(const char *text, int *count); static void UnloadTextLines(char **text); -// Get example info from file header -static rlExampleInfo *GetExampleInfo(const char *exFileName); +// Load example info from file header +static rlExampleInfo *LoadExampleInfo(const char *exFileName); +static void UnloadExampleInfo(rlExampleInfo *exInfo); // raylib example line info parser // Parses following line format: core/core_basic_window;⭐️☆☆☆;1.0;1.0;"Ray"/@raysan5 @@ -441,7 +442,7 @@ int main(int argc, char *argv[]) // Get required example info from example file header (if provided) // NOTE: If no example info is provided (other than category/name), just using some default values - rlExampleInfo *exInfo = GetExampleInfo(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); + rlExampleInfo *exInfo = LoadExampleInfo(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName)); // Get example difficulty stars char starsText[16] = { 0 }; @@ -470,7 +471,7 @@ int main(int argc, char *argv[]) memcpy(exCollectionListUpdated + categoryIndex + textWritenSize, exCollectionList + categoryIndex, strlen(exCollectionList) - categoryIndex); } - RL_FREE(exInfo); + UnloadExampleInfo(exInfo); SaveFileText(exCollectionFilePath, exCollectionListUpdated); RL_FREE(exCollectionListUpdated); @@ -721,7 +722,7 @@ int main(int argc, char *argv[]) // TODO: Validate: Duplicate entries in collection list? - // Get status information for all examples, using "status" field in the struct + // Set status information for all examples, using "status" field in the struct for (int i = 0; i < exCollectionCount; i++) { rlExampleInfo *exInfo = &exCollection[i]; @@ -736,7 +737,8 @@ int main(int argc, char *argv[]) // Validate: example screenshot is not the template default one Image imScreenshot = LoadImage(TextFormat("%s/%s/%s.png", exBasePath, exInfo->category, exInfo->name)); Image imTemplate = LoadImage(TextFormat("%s/examples_template.png", exBasePath)); - if (memcmp(imScreenshot.data, imTemplate.data, GetPixelDataSize(imScreenshot.width, imScreenshot.height, imScreenshot.format)) != 0) exInfo->status |= VALID_INVALID_PNG; + if (memcmp(imScreenshot.data, imTemplate.data, GetPixelDataSize(imScreenshot.width, imScreenshot.height, imScreenshot.format)) == 0) + exInfo->status |= VALID_INVALID_PNG; UnloadImage(imTemplate); UnloadImage(imScreenshot); @@ -747,13 +749,13 @@ int main(int argc, char *argv[]) if (FileTextFind(TextFormat("%s/Makefile.Web", exBasePath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_MAKEFILE_WEB; // Validate: raylib/examples/README.md -> Example listed? - if (FileTextFind(TextFormat("%s/README.md", exBasePath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_JS; + if (FileTextFind(TextFormat("%s/README.md", exBasePath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_README; // Validate: raylib.com/common/examples.js -> Example listed? - if (FileTextFind(TextFormat("%s/common/examples.js", exWebPath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_README; + if (FileTextFind(TextFormat("%s/../common/examples.js", exWebPath), exInfo->name + TextFindIndex(exInfo->name, "_") + 1) == -1) exInfo->status |= VALID_NOT_IN_JS; // Validate: raylib/projects/VS2022/examples/_example_name.vcxproj -> File exists? - if (!FileExists(TextFormat("%s/../projects/VS2022/examples/%s.png", exBasePath, exInfo->name))) exInfo->status |= VALID_MISSING_VCXPROJ; + if (!FileExists(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exInfo->name))) exInfo->status |= VALID_MISSING_VCXPROJ; // Validate: raylib/projects/VS2022/raylib.sln -> Example listed? if (FileTextFind(TextFormat("%s/../projects/VS2022/raylib.sln", exBasePath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_VCXSOL; @@ -774,13 +776,22 @@ int main(int argc, char *argv[]) for (int v = 0; v < 3; v++) { char *resPathUpdated = TextReplace(resPaths[r], "glsl%i", TextFormat("glsl%i", glslVer[v])); - if (!FileExists(TextFormat("%s/%s/%s", exBasePath, exInfo->category, resPathUpdated))) exInfo->status |= VALID_MISSING_RESOURCES; + if (!FileExists(TextFormat("%s/%s/%s", exBasePath, exInfo->category, resPathUpdated))) + { + exInfo->status |= VALID_MISSING_RESOURCES; + // Logging missing resources for convenience + LOG("WARNING: [%s] Missing resource: %s\n", exInfo->name, resPathUpdated); + } RL_FREE(resPathUpdated); } } else { - if (!FileExists(TextFormat("%s/%s/%s", exBasePath, exInfo->category, resPaths[r]))) exInfo->status |= VALID_MISSING_RESOURCES; + if (!FileExists(TextFormat("%s/%s/%s", exBasePath, exInfo->category, resPaths[r]))) + { + exInfo->status |= VALID_MISSING_RESOURCES; + LOG("WARNING: [%s] Missing resource: %s\n", exInfo->name, resPaths[r]); + } } } } @@ -790,21 +801,22 @@ int main(int argc, char *argv[]) // Validate: raylib.com/examples//_example_name.data -> File exists? // Validate: raylib.com/examples//_example_name.wasm -> File exists? // Validate: raylib.com/examples//_example_name.js -> File exists? - if ((!FileExists(TextFormat("%s/examples/%s/%s.html", exWebPath, exInfo->category, exInfo->name))) || - ((exInfo->resCount > 0) && !FileExists(TextFormat("%s/examples/%s/%s.data", exWebPath, exInfo->category, exInfo->name))) || - (!FileExists(TextFormat("%s/examples/%s/%s.wasm", exWebPath, exInfo->category, exInfo->name))) || - (!FileExists(TextFormat("%s/examples/%s/%s.js", exWebPath, exInfo->category, exInfo->name)))) exInfo->status |= VALID_MISSING_WEB_OUTPUT; + if (!FileExists(TextFormat("%s/%s/%s.html", exWebPath, exInfo->category, exInfo->name)) || + !FileExists(TextFormat("%s/%s/%s.wasm", exWebPath, exInfo->category, exInfo->name)) || + !FileExists(TextFormat("%s/%s/%s.js", exWebPath, exInfo->category, exInfo->name)) || + ((exInfo->resCount > 0) && !FileExists(TextFormat("%s/%s/%s.data", exWebPath, exInfo->category, exInfo->name)))) + exInfo->status |= VALID_MISSING_WEB_OUTPUT; // NOTE: Additional validation elements // Validate: Example naming conventions: /_example_name, valid category if ((TextFindIndex(exInfo->name, exInfo->category) == -1) || - (!TextIsEqual(exInfo->category, "core") || !TextIsEqual(exInfo->category, "shapes") || - !TextIsEqual(exInfo->category, "textures") || !TextIsEqual(exInfo->category, "text") || - !TextIsEqual(exInfo->category, "models") || !TextIsEqual(exInfo->category, "shaders") || - !TextIsEqual(exInfo->category, "audio") || !TextIsEqual(exInfo->category, "others"))) exInfo->status |= VALID_INVALID_CATEGORY; + (!TextIsEqual(exInfo->category, "core") && !TextIsEqual(exInfo->category, "shapes") && + !TextIsEqual(exInfo->category, "textures") && !TextIsEqual(exInfo->category, "text") && + !TextIsEqual(exInfo->category, "models") && !TextIsEqual(exInfo->category, "shaders") && + !TextIsEqual(exInfo->category, "audio") && !TextIsEqual(exInfo->category, "others"))) exInfo->status |= VALID_INVALID_CATEGORY; // Validate: Example info (stars, author, github) missmatches with example header content - rlExampleInfo *exInfoHeader = GetExampleInfo(TextFormat("%s/%s/%s.c", exBasePath, exInfo->category, exInfo->name)); + rlExampleInfo *exInfoHeader = LoadExampleInfo(TextFormat("%s/%s/%s.c", exBasePath, exInfo->category, exInfo->name)); if ((strcmp(exInfo->name, exInfoHeader->name) != 0) || // NOTE: Get it from example, not file (strcmp(exInfo->category, exInfoHeader->category) != 0) || @@ -814,62 +826,103 @@ int main(int argc, char *argv[]) (exInfo->verCreated != exInfoHeader->verCreated) || (exInfo->verUpdated != exInfoHeader->verUpdated)) exInfo->status |= VALID_INCONSISTENT_INFO; - RL_FREE(exInfoHeader); + UnloadExampleInfo(exInfoHeader); + } - // TODO: Generate validation report/table with results (.md) - /* - //Status Description - //OK Everything found - //MISSING One or more files are missing, some can be solved automatically - //ERROR Serious inconsistencies + // Generate validation report/table with results (.md) + /* + Columns: + [C] VALID_MISSING_C // Missing .c source file + [PNG] VALID_MISSING_PNG // Missing screenshot .png + [WPNG] VALID_INVALID_PNG // Invalid png screenshot (using template one) + [RES] VALID_MISSING_RESOURCES // Missing resources listed in the code + [VCX] VALID_MISSING_VCXPROJ // Missing Visual Studio .vcxproj file + [SOL] VALID_NOT_IN_VCXSOL // Project not included in solution file + [MK] VALID_NOT_IN_MAKEFILE // Not listed in Makefile + [MKWEB] VALID_NOT_IN_MAKEFILE_WEB // Not listed in Makefile.Web + [RDME] VALID_NOT_IN_README // Not listed in README.md + [JS] VALID_NOT_IN_JS // Not listed in examples.js + [WOUT] VALID_MISSING_WEB_OUTPUT // Missing .html/.data/.wasm/.js + [INFO] VALID_INCONSISTENT_INFO // Inconsistent info between collection and example header (stars, author...) + [CAT] VALID_INVALID_CATEGORY // Not a recognized category - Columns: - [C] VALID_MISSING_C // Missing .c source file - [PNG] VALID_MISSING_PNG // Missing screenshot .png - [WPNG] VALID_INVALID_PNG // Invalid png screenshot (using template one) - [RES] VALID_MISSING_RESOURCES // Missing resources listed in the code - [VCX] VALID_MISSING_VCXPROJ // Missing Visual Studio .vcxproj file - [SOL] VALID_NOT_IN_VCXSOL // Project not included in solution file - [MK] VALID_NOT_IN_MAKEFILE // Not listed in Makefile - [MKWEB] VALID_NOT_IN_MAKEFILE_WEB // Not listed in Makefile.Web - [RDME] VALID_NOT_IN_README // Not listed in README.md - [JS] VALID_NOT_IN_JS // Not listed in examples.js - [WOUT] VALID_MISSING_WEB_OUTPUT // Missing .html/.data/.wasm/.js - [INFO] VALID_INCONSISTENT_INFO // Inconsistent info between collection and example header (stars, author...) - [CAT] VALID_INVALID_CATEGORY // Not a recognized category + | [EXAMPLE NAME] | [C] |[CAT]|[INFO]|[PNG]|[WPNG]|[RES]|[MK] |[MKWEB]|[VCX]|[SOL]|[RDME]|[JS] |[WOUT]| + |:-----------------------------|:---:|:---:|:----:|:---:|:----:|:---:|:---:|:-----:|:---:|:---:|:----:|:---:|:----:| + | core_basic_window | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | + | shapes_colors_palette | ✘ | ✔ | ✘ | ✔ | ✘ | ✔ | ✔ | ✘ | ✔ | ✔ | ✔ | ✔ | ✔ | + | text_format_text | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ | ✔ | ✘ | ✔ | ✔ | ✔ | + */ - [STATUS] [CATEGORY] [NAME] [C] [PNG] [WPNG] [RES] [VCX] [SOL] [MK] [MKWEB] [RDME] [JS] [WOUT] [INFO] [CAT] - ----------------------------------------------------------------------------------------------------------------------- - OK core basic_window ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ - MISSING shapes colorful_lines ✘ ✔ ✘ ✔ ✘ ✔ ✔ ✘ ✔ ✔ ✔ ✔ ✔ - ERROR text broken_shader ✘ ✘ ✘ ✘ ✘ ✘ ✘ ✘ ✔ ✘ ✔ ✔ ✔ - */ + char *report = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1); + + int repIndex = 0; + repIndex += sprintf(report + repIndex, "# EXAMPLES COLLECTION REPORT\n\n"); + repIndex += sprintf(report + repIndex, "| **EXAMPLE NAME** | [C] | [CAT]| [INFO]|[PNG]|[WPNG]| [RES]| [MK] |[MKWEB]| [VCX]| [SOL]|[RDME]|[JS] | [WOUT]|\n"); + repIndex += sprintf(report + repIndex, "|:---------------------------------|:---:|:----:|:-----:|:---:|:----:|:----:|:----:|:-----:|:----:|:----:|:----:|:---:|:-----:|\n"); + + for (int i = 0; i < exCollectionCount; i++) + { + repIndex += sprintf(report + repIndex, "| %-32s | %s | %s | %s | %s | %s | %s | %s | %s | %s | %s | %s | %s | %s |\n", + exCollection[i].name, + (exCollection[i].status & VALID_MISSING_C)? "❌" : "✔", + (exCollection[i].status & VALID_INVALID_CATEGORY)? "❌" : "✔", + (exCollection[i].status & VALID_INCONSISTENT_INFO)? "❌" : "✔", + (exCollection[i].status & VALID_MISSING_PNG)? "❌" : "✔", + (exCollection[i].status & VALID_INVALID_PNG)? "❌" : "✔", + (exCollection[i].status & VALID_MISSING_RESOURCES)? "❌" : "✔", + (exCollection[i].status & VALID_NOT_IN_MAKEFILE)? "❌" : "✔", + (exCollection[i].status & VALID_NOT_IN_MAKEFILE_WEB)? "❌" : "✔", + (exCollection[i].status & VALID_MISSING_VCXPROJ)? "❌" : "✔", + (exCollection[i].status & VALID_NOT_IN_VCXSOL)? "❌" : "✔", + (exCollection[i].status & VALID_NOT_IN_README)? "❌" : "✔", + (exCollection[i].status & VALID_NOT_IN_JS)? "❌" : "✔", + (exCollection[i].status & VALID_MISSING_WEB_OUTPUT)? "❌" : "✔"); + } + + SaveFileText(TextFormat("%s/../tools/rexm/%s", exBasePath, "examples_report.md"), report); + RL_FREE(report); + + //UnloadExamplesData(exCollection); // Done at the end, it can be required for fixing + + if (opCode == OP_UPDATE) + { + // Actions to fix/review anything possible from validation results + //------------------------------------------------------------------------------------------------ + // Check examples "status" information + for (int i = 0; i < exCollectionCount; i++) + { + rlExampleInfo *exInfo = &exCollection[i]; + + if (exInfo->status & VALID_MISSING_C) LOG("WARNING: [%s] Missing code file\n", exInfo->name); + else + { + // NOTE: Some issues can not be automatically fixed, only logged + //if (exInfo->status & VALID_MISSING_PNG) LOG("WARNING: [%s] Missing screenshot file\n", exInfo->name); + //if (exInfo->status & VALID_INVALID_PNG) LOG("WARNING: [%s] Invalid screenshot file (using template)\n", exInfo->name); + //if (exInfo->status & VALID_MISSING_RESOURCES) LOG("WARNING: [%s] Missing resources detected\n", exInfo->name); + //if (exInfo->status & VALID_INCONSISTENT_INFO) LOG("WARNING: [%s] Inconsistent example header info\n", exInfo->name); + //if (exInfo->status & VALID_INVALID_CATEGORY) LOG("WARNING: [%s] Invalid example category\n", exInfo->name); + + // Review: Add/Remove: raylib/projects/VS2022/examples/_example_name.vcxproj + // Review: Add/remove: raylib/projects/VS2022/raylib.sln + // Solves: VALID_MISSING_VCXPROJ, VALID_NOT_IN_VCXSOL + + // Review: Add/Remove: raylib.com/examples//_example_name.html + // Review: Add/Remove: raylib.com/examples//_example_name.data + // Review: Add/Remove: raylib.com/examples//_example_name.wasm + // Review: Add/Remove: raylib.com/examples//_example_name.js + // Solves: VALID_MISSING_WEB_OUTPUT + } + } + + // Update files: Makefile, Makefile.Web, README.md, examples.js + // Solves: VALID_NOT_IN_MAKEFILE, VALID_NOT_IN_MAKEFILE_WEB, VALID_NOT_IN_README, VALID_NOT_IN_JS + UpdateRequiredFiles(); + //------------------------------------------------------------------------------------------------ } UnloadExamplesData(exCollection); //------------------------------------------------------------------------------------------------ - - if (opCode == OP_UPDATE) - { - // Actions to be performed to fix/review anything possible - //------------------------------------------------------------------------------------------------ - - // TODO: Process validation results and take actions to - // correct everything possible to make collection consistent - - // Review: Add/Remove: raylib/projects/VS2022/examples/_example_name.vcxproj - // Review: Add/remove: raylib/projects/VS2022/raylib.sln - - // Update files: Makefile, Makefile.Web, README.md, examples.js - UpdateRequiredFiles(); - - // Review examples - // Review: Add/Remove: raylib.com/examples//_example_name.html - // Review: Add/Remove: raylib.com/examples//_example_name.data - // Review: Add/Remove: raylib.com/examples//_example_name.wasm - // Review: Add/Remove: raylib.com/examples//_example_name.js - //------------------------------------------------------------------------------------------------ - } } break; default: // Help @@ -1424,7 +1477,7 @@ static void UnloadTextLines(char **lines) } // Get example info from file header -rlExampleInfo *GetExampleInfo(const char *exFileName) +rlExampleInfo *LoadExampleInfo(const char *exFileName) { rlExampleInfo *exInfo = (rlExampleInfo *)RL_CALLOC(1, sizeof(rlExampleInfo)); @@ -1437,6 +1490,7 @@ rlExampleInfo *GetExampleInfo(const char *exFileName) // Get example difficulty stars // NOTE: Counting the unicode char occurrences: ⭐️ + // WARNING: The stars unicode in examples is not the same than in collection list!!! int starsIndex = TextFindIndex(exText, "★"); if (starsIndex > 0) { @@ -1444,8 +1498,8 @@ rlExampleInfo *GetExampleInfo(const char *exFileName) while (*starPtr) { if (((unsigned char)starPtr[0] == 0xe2) && - ((unsigned char)starPtr[1] == 0xad) && - ((unsigned char)starPtr[2] == 0x90)) + ((unsigned char)starPtr[1] == 0x98) && + ((unsigned char)starPtr[2] == 0x85)) { exInfo->stars++; starPtr += 3; // Advance past multibyte character @@ -1469,31 +1523,32 @@ rlExampleInfo *GetExampleInfo(const char *exFileName) exInfo->verUpdated = TextToFloat(verUpdateText); // Get example creator and github user - int authorIndex = TextFindIndex(exText, "Example contributed by "); // Author = index + 23 - int authorGitIndex = TextFindIndex(exText, "(@"); // Author GitHub user = index + 2 - if (authorIndex > 0) + // NOTE: Using copyright line instead of "Example contributed by " because + // most examples do not contain that line --> TODO: Review examples header formating? + // Expected format: Copyright (c) - (@) + // Alternatives: Copyright (c) (@) and (@) + int copyrightIndex = TextFindIndex(exText, "Copyright (c) "); + int yearStartIndex = copyrightIndex + 14; + int yearEndIndex = TextFindIndex(exText + yearStartIndex, " "); + int authorStartIndex = yearStartIndex + yearEndIndex + 1; + int authorEndIndex = TextFindIndex(exText + authorStartIndex, " (@"); + if (authorEndIndex != -1) // Github user also available { - int authorNameLen = 0; - if (authorGitIndex > 0) authorNameLen = (authorGitIndex - 1) - (authorIndex + 23); - else - { - int authorNameEndIndex = TextFindIndex(exText + authorIndex, " and reviewed by Ramon Santamaria"); - if (authorNameEndIndex == -1) authorNameEndIndex = TextFindIndex(exText + authorIndex, "\n"); + authorEndIndex += authorStartIndex; + strncpy(exInfo->author, exText + authorStartIndex, authorEndIndex - authorStartIndex); - authorNameLen = authorNameEndIndex - (authorIndex + 23); - } - strncpy(exInfo->author, exText + authorIndex + 23, authorNameLen); + // Get GitHub user + int userStartIndex = authorEndIndex + 3; + int userEndIndex = TextFindIndex(exText + userStartIndex, ")"); + userEndIndex += userStartIndex; + strncpy(exInfo->authorGitHub, exText + userStartIndex, userEndIndex - userStartIndex); } - else strcpy(exInfo->author, ""); - if (authorGitIndex > 0) + else // GitHub user not found to set end, using '\n' { - int authorGitEndIndex = TextFindIndex(exText + authorGitIndex, ")"); - if (authorGitEndIndex > 0) strncpy(exInfo->authorGitHub, exText + authorGitIndex + 2, authorGitEndIndex - (authorGitIndex + 2)); + authorEndIndex = TextFindIndex(exText + authorStartIndex, "\n"); + authorEndIndex += authorStartIndex; + strncpy(exInfo->author, exText + authorStartIndex, authorEndIndex - authorStartIndex); } - else strcpy(exInfo->authorGitHub, ""); - - // TODO: Verify copyright line - // Copyright (c) - (@) UnloadFileText(exText); } @@ -1501,6 +1556,12 @@ rlExampleInfo *GetExampleInfo(const char *exFileName) return exInfo; } +// Unload example information +static void UnloadExampleInfo(rlExampleInfo *exInfo) +{ + RL_FREE(exInfo); +} + // raylib example line info parser // Parses following line format: core;core_basic_window;⭐️☆☆☆;1.0;1.0;"Ray";@raysan5 static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry) @@ -1541,7 +1602,7 @@ static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry) if (tokens[5][0] == '"') tokens[5] += 1; if (tokens[5][strlen(tokens[5]) - 1] == '"') tokens[5][strlen(tokens[5]) - 1] = '\0'; strcpy(entry->author, tokens[5]); - strcpy(entry->authorGitHub, tokens[6]); + strcpy(entry->authorGitHub, tokens[6] + 1); // Skip '@' return 1; } From 8bc1d62e88c16c981ecffbfc0042436632cae240 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 6 Aug 2025 15:43:13 +0200 Subject: [PATCH 193/242] REXM: Added legend info to report --- tools/rexm/rexm.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 702948d4d..92554b4ab 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -718,7 +718,7 @@ int main(int argc, char *argv[]) // Check all examples in collection [examples_list.txt] -> Source of truth! int exCollectionCount = 0; - rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, "ALL", true, &exCollectionCount); + rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, "ALL", false, &exCollectionCount); // TODO: Validate: Duplicate entries in collection list? @@ -857,6 +857,22 @@ int main(int argc, char *argv[]) int repIndex = 0; repIndex += sprintf(report + repIndex, "# EXAMPLES COLLECTION REPORT\n\n"); + + repIndex += sprintf(report + repIndex, "```\nExample elements validated:\n\n"); + repIndex += sprintf(report + repIndex, " - [C] : Missing .c source file\n"); + repIndex += sprintf(report + repIndex, " - [CAT] : Not a recognized category\n"); + repIndex += sprintf(report + repIndex, " - [INFO] : Inconsistent example header info (stars, author...)\n"); + repIndex += sprintf(report + repIndex, " - [PNG] : Missing screenshot .png\n"); + repIndex += sprintf(report + repIndex, " - [WPNG] : Invalid png screenshot (using default one)\n"); + repIndex += sprintf(report + repIndex, " - [RES] : Missing resources listed in the code\n"); + repIndex += sprintf(report + repIndex, " - [MK] : Not listed in Makefile\n"); + repIndex += sprintf(report + repIndex, " - [MKWEB] : Not listed in Makefile.Web\n"); + repIndex += sprintf(report + repIndex, " - [VCX] : Missing Visual Studio project file\n"); + repIndex += sprintf(report + repIndex, " - [SOL] : Project not included in solution file\n"); + repIndex += sprintf(report + repIndex, " - [RDME] : Not listed in README.md\n"); + repIndex += sprintf(report + repIndex, " - [JS] : Not listed in Web (examples.js)\n"); + repIndex += sprintf(report + repIndex, " - [WOUT] : Missing Web build (.html/.data/.wasm/.js)\n```\n"); + repIndex += sprintf(report + repIndex, "| **EXAMPLE NAME** | [C] | [CAT]| [INFO]|[PNG]|[WPNG]| [RES]| [MK] |[MKWEB]| [VCX]| [SOL]|[RDME]|[JS] | [WOUT]|\n"); repIndex += sprintf(report + repIndex, "|:---------------------------------|:---:|:----:|:-----:|:---:|:----:|:----:|:----:|:-----:|:----:|:----:|:----:|:---:|:-----:|\n"); From 2878f7db22fba739b1e65592fd2e70b1ff42311f Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 6 Aug 2025 15:43:15 +0200 Subject: [PATCH 194/242] Create examples_report.md --- tools/rexm/examples_report.md | 180 ++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 tools/rexm/examples_report.md diff --git a/tools/rexm/examples_report.md b/tools/rexm/examples_report.md new file mode 100644 index 000000000..0188c4a98 --- /dev/null +++ b/tools/rexm/examples_report.md @@ -0,0 +1,180 @@ +# EXAMPLES COLLECTION REPORT + +``` +Example elements validated: + + - [C] : Missing .c source file + - [CAT] : Not a recognized category + - [INFO] : Inconsistent example header info (stars, author...) + - [PNG] : Missing screenshot .png + - [WPNG] : Invalid png screenshot (using default one) + - [RES] : Missing resources listed in the code + - [MK] : Not listed in Makefile + - [MKWEB] : Not listed in Makefile.Web + - [VCX] : Missing Visual Studio project file + - [SOL] : Project not included in solution file + - [RDME] : Not listed in README.md + - [JS] : Not listed in Web (examples.js) + - [WOUT] : Missing Web build (.html/.data/.wasm/.js) +``` +| **EXAMPLE NAME** | [C] | [CAT]| [INFO]|[PNG]|[WPNG]| [RES]| [MK] |[MKWEB]| [VCX]| [SOL]|[RDME]|[JS] | [WOUT]| +|:---------------------------------|:---:|:----:|:-----:|:---:|:----:|:----:|:----:|:-----:|:----:|:----:|:----:|:---:|:-----:| +| core_basic_window | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_input_keys | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_input_mouse | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_input_mouse_wheel | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_input_gamepad | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_input_multitouch | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_input_gestures | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_input_virtual_controls | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | +| core_2d_camera | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_2d_camera_mouse_zoom | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_2d_camera_platformer | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_2d_camera_split_screen | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_3d_camera_mode | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_3d_camera_free | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_3d_camera_first_person | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_3d_camera_split_screen | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_3d_picking | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_world_screen | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_custom_logging | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_window_flags | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_window_letterbox | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_window_should_close | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_drop_files | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_random_values | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_storage_values | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_vr_simulator | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_loading_thread | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_scissor_test | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_basic_screen_manager | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_custom_frame_control | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_smooth_pixelperfect | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_random_sequence | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | +| core_basic_window_web | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ❌ | ❌ | +| core_input_gestures_web | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ❌ | ✔ | +| core_automation_events | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_high_dpi | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | +| shapes_basic_shapes | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shapes_bouncing_ball | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shapes_colors_palette | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shapes_logo_raylib | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shapes_logo_raylib_anim | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shapes_rectangle_scaling | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shapes_lines_bezier | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shapes_collision_area | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shapes_following_eyes | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shapes_easings_ball_anim | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shapes_easings_box_anim | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shapes_easings_rectangle_array | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shapes_draw_ring | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shapes_draw_circle_sector | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shapes_draw_rectangle_rounded | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shapes_top_down_lights | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shapes_rectangle_advanced | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | +| shapes_splines_drawing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shapes_digital_clock | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ❌ | ❌ | +| shapes_double_pendulum | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ❌ | ❌ | +| textures_logo_raylib | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_srcrec_dstrec | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_image_drawing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_image_generation | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_image_loading | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_image_processing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_image_text | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_to_image | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_raw_data | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_particles_blending | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_npatch_drawing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_background_scrolling | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_sprite_anim | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_sprite_button | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_sprite_explosion | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_bunnymark | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_mouse_painting | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | +| textures_blend_modes | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_draw_tiled | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_polygon | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_fog_of_war | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_gif_player | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_image_kernel | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ❌ | ✔ | +| textures_image_channel | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | +| textures_image_rotate | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ❌ | ✔ | +| textures_textured_curve | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| text_raylib_fonts | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| text_font_spritefont | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| text_font_filters | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| text_font_loading | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| text_font_sdf | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| text_format_text | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| text_input_box | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| text_writing_anim | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| text_rectangle_bounds | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| text_unicode | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| text_draw_3d | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| text_codepoints_loading | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_animation | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_billboard | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_box_collisions | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_cubicmap | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_first_person_maze | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_geometric_shapes | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_mesh_generation | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_mesh_picking | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_loading | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_loading_gltf | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_loading_vox | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_loading_m3d | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_orthographic_projection | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_point_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | +| models_rlgl_solar_system | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_yaw_pitch_roll | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_waving_cubes | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_heightmap | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_skybox | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_draw_cube_texture | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_gpu_skinning | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_bone_socket | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_tesseract_view | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | +| shaders_basic_lighting | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_model_shader | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_shapes_textures | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_custom_uniform | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_postprocessing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_palette_switch | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_raymarching | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_texture_drawing | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_texture_outline | ✔ | ✔ | ❌ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_texture_waves | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_julia_set | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_eratosthenes | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_fog | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_simple_mask | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_hot_reloading | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_mesh_instancing | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_multi_sample2d | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_spotlight | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_deferred_render | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_hybrid_render | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_texture_tiling | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_shadowmap | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | +| shaders_vertex_displacement | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_write_depth | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_basic_pbr | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | +| shaders_lightmap | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | +| shaders_rounded_rectangle | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | +| shaders_view_depth | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | +| audio_module_playing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| audio_music_stream | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| audio_raw_stream | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| audio_sound_loading | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| audio_mixed_processor | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| audio_stream_effects | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| audio_sound_multi | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| audio_sound_positioning | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ❌ | ❌ | +| rlgl_standalone | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | +| rlgl_compute_shader | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ❌ | ❌ | +| easings_testbed | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | +| raylib_opengl_interop | ✔ | ❌ | ❌ | ✔ | ✔ | ❌ | ✔ | ✔ | ❌ | ❌ | ✔ | ❌ | ❌ | +| embedded_files_loading | ✔ | ❌ | ❌ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | +| raymath_vector_angle | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ❌ | ❌ | From c477c684193e4bcb65575bb80f79c452d21ba6c5 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 6 Aug 2025 18:14:52 +0200 Subject: [PATCH 195/242] REXM: Automatically fix not found VS2022 project / solution --- tools/rexm/rexm.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 92554b4ab..8a7ec80cd 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -115,18 +115,18 @@ typedef enum { static const char *exCategories[REXM_MAX_EXAMPLE_CATEGORIES] = { "core", "shapes", "textures", "text", "models", "shaders", "audio", "others" }; // Paths required for examples management -// TODO: Avoid hardcoding path values... -static const char *exBasePath = NULL; //"C:/GitHub/raylib/examples"; -static const char *exWebPath = NULL; //"C:/GitHub/raylib.com/examples"; -static const char *exTemplateFilePath = NULL; //"C:/GitHub/raylib/examples/examples_template.c"; -static const char *exTemplateScreenshot = NULL; //"C:/GitHub/raylib/examples/examples_template.png"; -static const char *exCollectionFilePath = NULL; //"C:/GitHub/raylib/examples/examples_list.txt"; +// NOTE: Paths can be provided with environment variables +static const char *exBasePath = NULL; // Env: REXM_EXAMPLES_BASE_PATH +static const char *exWebPath = NULL; // Env: REXM_EXAMPLES_WEB_PATH +static const char *exTemplateFilePath = NULL; // Env: REXM_EXAMPLES_TEMPLATE_FILE_PATH +static const char *exTemplateScreenshot = NULL; // Env: REXM_EXAMPLES_TEMPLATE_SCREENSHOT_PATH +static const char *exCollectionFilePath = NULL; // Env: REXM_EXAMPLES_COLLECTION_FILE_PATH //---------------------------------------------------------------------------------- // Module specific functions declaration //---------------------------------------------------------------------------------- static int FileTextFind(const char *fileName, const char *find); -static int FileTextReplace(const char *fileName, const char *textLookUp, const char *textReplace); +static int FileTextReplace(const char *fileName, const char *find, const char *replace); static int FileCopy(const char *srcPath, const char *dstPath); static int FileRename(const char *fileName, const char *fileRename); static int FileMove(const char *srcPath, const char *dstPath); @@ -449,8 +449,8 @@ int main(int argc, char *argv[]) for (int i = 0; i < 4; i++) { // NOTE: Every UTF-8 star are 3 bytes - if (i < exInfo->stars) strncpy(starsText + 3*i, "★", 3); - else strncpy(starsText + 3*i, "☆", 3); + if (i < exInfo->stars) strcpy(starsText + 3*i, "★"); + else strcpy(starsText + 3*i, "☆"); } if (nextCategoryIndex == -1) @@ -856,7 +856,7 @@ int main(int argc, char *argv[]) char *report = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1); int repIndex = 0; - repIndex += sprintf(report + repIndex, "# EXAMPLES COLLECTION REPORT\n\n"); + repIndex += sprintf(report + repIndex, "# EXAMPLES COLLECTION - VALIDATION REPORT\n\n"); repIndex += sprintf(report + repIndex, "```\nExample elements validated:\n\n"); repIndex += sprintf(report + repIndex, " - [C] : Missing .c source file\n"); @@ -919,15 +919,29 @@ int main(int argc, char *argv[]) //if (exInfo->status & VALID_INCONSISTENT_INFO) LOG("WARNING: [%s] Inconsistent example header info\n", exInfo->name); //if (exInfo->status & VALID_INVALID_CATEGORY) LOG("WARNING: [%s] Invalid example category\n", exInfo->name); - // Review: Add/Remove: raylib/projects/VS2022/examples/_example_name.vcxproj - // Review: Add/remove: raylib/projects/VS2022/raylib.sln + // Review: Add: raylib/projects/VS2022/examples/_example_name.vcxproj + // Review: Add: raylib/projects/VS2022/raylib.sln // Solves: VALID_MISSING_VCXPROJ, VALID_NOT_IN_VCXSOL + if (exInfo->status & VALID_MISSING_VCXPROJ) + { + FileCopy(TextFormat("%s/../projects/VS2022/examples/core_basic_window.vcxproj", exBasePath), + TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exInfo->name)); + FileTextReplace(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exInfo->name), + "core_basic_window", exInfo->name); + FileTextReplace(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exInfo->name), + "..\\..\\examples\\core", TextFormat("..\\..\\examples\\%s", exInfo->category)); + } + if (exInfo->status & VALID_NOT_IN_VCXSOL) + system(TextFormat("dotnet solution %s/../projects/VS2022/raylib.sln add %s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exBasePath, exInfo->name)); + // Review: Add/Remove: raylib.com/examples//_example_name.html // Review: Add/Remove: raylib.com/examples//_example_name.data // Review: Add/Remove: raylib.com/examples//_example_name.wasm // Review: Add/Remove: raylib.com/examples//_example_name.js // Solves: VALID_MISSING_WEB_OUTPUT + if (exInfo->status & VALID_MISSING_WEB_OUTPUT) + system(TextFormat("%s/build_example_web.bat %s/%s", exBasePath, exInfo->category, exInfo->name)); } } From f64d405ef30309f17494ed83cd9dacca85438d86 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 6 Aug 2025 18:18:39 +0200 Subject: [PATCH 196/242] Update Makefile.Web --- examples/Makefile.Web | 200 +++++++++++++++++++----------------------- 1 file changed, 90 insertions(+), 110 deletions(-) diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 5a5d74eb0..dc8c886dd 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -665,14 +665,6 @@ AUDIO = \ audio/audio_sound_positioning \ audio/audio_stream_effects -OTHERS = \ - others/easings_testbed \ - others/embedded_files_loading \ - others/raylib_opengl_interop \ - others/raymath_vector_angle \ - others/rlgl_compute_shader \ - others/rlgl_standalone - # Default target entry all: $(CORE) $(SHAPES) $(TEXT) $(TEXTURES) $(MODELS) $(SHADERS) $(AUDIO) @@ -712,7 +704,10 @@ core/core_3d_camera_split_screen: core/core_3d_camera_split_screen.c core/core_3d_picking: core/core_3d_picking.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -core/core_automation_events : core/core_automation_events.c +core/core_automation_events: core/core_automation_events.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + +core/core_basic_screen_manager: core/core_basic_screen_manager.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) core/core_basic_window: core/core_basic_window.c @@ -721,9 +716,6 @@ core/core_basic_window: core/core_basic_window.c core/core_basic_window_web: core/core_basic_window_web.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -core/core_basic_screen_manager: core/core_basic_screen_manager.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - core/core_custom_frame_control: core/core_custom_frame_control.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -733,6 +725,9 @@ core/core_custom_logging: core/core_custom_logging.c core/core_drop_files: core/core_drop_files.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +core/core_high_dpi: core/core_high_dpi.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + core/core_input_gamepad: core/core_input_gamepad.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file core/resources/ps3.png@resources/ps3.png \ @@ -759,12 +754,8 @@ core/core_input_multitouch: core/core_input_multitouch.c core/core_input_virtual_controls: core/core_input_virtual_controls.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -# NOTE: To use multi-threading raylib must be compiled with multi-theading support (-sUSE_PTHREADS=1) -# WARNING: For security reasons multi-threading is not supported on browsers, it requires cross-origin isolation (Oct.2021) -# WARNING: It requires raylib to be compiled using -pthread, so atomic operations and thread-local data (if any) -# in its source were transformed to non-atomic operations and non-thread-local data core/core_loading_thread: core/core_loading_thread.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sUSE_PTHREADS=1 + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) core/core_random_sequence: core/core_random_sequence.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -779,11 +770,11 @@ core/core_smooth_pixelperfect: core/core_smooth_pixelperfect.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) core/core_storage_values: core/core_storage_values.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sFORCE_FILESYSTEM=1 + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) core/core_vr_simulator: core/core_vr_simulator.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file core/resources/distortion100.fs@resources/distortion100.fs + --preload-file core/resources/shaders/glsl100/distortion.fs@resources/shaders/glsl100/distortion.fs core/core_window_flags: core/core_window_flags.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -810,6 +801,12 @@ shapes/shapes_collision_area: shapes/shapes_collision_area.c shapes/shapes_colors_palette: shapes/shapes_colors_palette.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +shapes/shapes_digital_clock: shapes/shapes_digital_clock.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + +shapes/shapes_double_pendulum: shapes/shapes_double_pendulum.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + shapes/shapes_draw_circle_sector: shapes/shapes_draw_circle_sector.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -840,6 +837,9 @@ shapes/shapes_logo_raylib: shapes/shapes_logo_raylib.c shapes/shapes_logo_raylib_anim: shapes/shapes_logo_raylib_anim.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +shapes/shapes_rectangle_advanced: shapes/shapes_rectangle_advanced.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + shapes/shapes_rectangle_scaling: shapes/shapes_rectangle_scaling.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -849,12 +849,6 @@ shapes/shapes_splines_drawing: shapes/shapes_splines_drawing.c shapes/shapes_top_down_lights: shapes/shapes_top_down_lights.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -shapes/shapes_rectangle_advanced: shapes/shapes_rectangle_advanced.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - -shapes/shapes_double_pendulum: shapes/shapes_double_pendulum.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - # Compile TEXTURES examples textures/textures_background_scrolling: textures/textures_background_scrolling.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -888,16 +882,16 @@ textures/textures_image_channel: textures/textures_image_channel.c textures/textures_image_drawing: textures/textures_image_drawing.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file textures/resources/custom_jupiter_crash.png@resources/custom_jupiter_crash.png \ + --preload-file textures/resources/cat.png@resources/cat.png \ --preload-file textures/resources/parrots.png@resources/parrots.png \ - --preload-file textures/resources/cat.png@resources/cat.png + --preload-file textures/resources/custom_jupiter_crash.png@resources/custom_jupiter_crash.png textures/textures_image_generation: textures/textures_image_generation.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) textures/textures_image_kernel: textures/textures_image_kernel.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file textures/resources/cat.png@resources/cat.png + --preload-file textures/resources/cat.png@resources/cat.png textures/textures_image_loading: textures/textures_image_loading.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -909,7 +903,7 @@ textures/textures_image_processing: textures/textures_image_processing.c textures/textures_image_rotate: textures/textures_image_rotate.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file textures/resources/raylib_logo.png + --preload-file textures/resources/raylib_logo.png@resources/raylib_logo.png textures/textures_image_text: textures/textures_image_text.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -921,7 +915,8 @@ textures/textures_logo_raylib: textures/textures_logo_raylib.c --preload-file textures/resources/raylib_logo.png@resources/raylib_logo.png textures/textures_mouse_painting: textures/textures_mouse_painting.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file textures/my_amazing_texture_painting.png@my_amazing_texture_painting.png textures/textures_npatch_drawing: textures/textures_npatch_drawing.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -945,13 +940,13 @@ textures/textures_sprite_anim: textures/textures_sprite_anim.c textures/textures_sprite_button: textures/textures_sprite_button.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file textures/resources/button.png@resources/button.png \ - --preload-file textures/resources/buttonfx.wav@resources/buttonfx.wav + --preload-file textures/resources/buttonfx.wav@resources/buttonfx.wav \ + --preload-file textures/resources/button.png@resources/button.png textures/textures_sprite_explosion: textures/textures_sprite_explosion.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file textures/resources/explosion.png@resources/explosion.png \ - --preload-file textures/resources/boom.wav@resources/boom.wav + --preload-file textures/resources/boom.wav@resources/boom.wav \ + --preload-file textures/resources/explosion.png@resources/explosion.png textures/textures_srcrec_dstrec: textures/textures_srcrec_dstrec.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -979,10 +974,7 @@ text/text_font_filters: text/text_font_filters.c --preload-file text/resources/KAISG.ttf@resources/KAISG.ttf text/text_font_loading: text/text_font_loading.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file text/resources/pixantiqua.fnt@resources/pixantiqua.fnt \ - --preload-file text/resources/pixantiqua.png@resources/pixantiqua.png \ - --preload-file text/resources/pixantiqua.ttf@resources/pixantiqua.ttf + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) text/text_font_sdf: text/text_font_sdf.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -1018,16 +1010,12 @@ text/text_rectangle_bounds: text/text_rectangle_bounds.c text/text_unicode: text/text_unicode.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file text/resources/dejavu.fnt@resources/dejavu.fnt \ - --preload-file text/resources/dejavu.png@resources/dejavu.png \ --preload-file text/resources/noto_cjk.fnt@resources/noto_cjk.fnt \ - --preload-file text/resources/noto_cjk.png@resources/noto_cjk.png \ - --preload-file text/resources/symbola.fnt@resources/symbola.fnt \ - --preload-file text/resources/symbola.png@resources/symbola.png + --preload-file text/resources/symbola.fnt@resources/symbola.fnt text/text_writing_anim: text/text_writing_anim.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - # Compile MODELS examples models/models_animation: models/models_animation.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -1035,12 +1023,6 @@ models/models_animation: models/models_animation.c --preload-file models/resources/models/iqm/guytex.png@resources/models/iqm/guytex.png \ --preload-file models/resources/models/iqm/guyanim.iqm@resources/models/iqm/guyanim.iqm -models/models_gpu_skinning: models/models_gpu_skinning.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file models/resources/models/gltf/greenman.glb@resources/models/gltf/greenman.glb \ - --preload-file models/resources/shaders/glsl100/skinning.vs@resources/shaders/glsl100/skinning.vs \ - --preload-file models/resources/shaders/glsl100/skinning.fs@resources/shaders/glsl100/skinning.fs - models/models_billboard: models/models_billboard.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file models/resources/billboard.png@resources/billboard.png @@ -1072,6 +1054,12 @@ models/models_first_person_maze: models/models_first_person_maze.c models/models_geometric_shapes: models/models_geometric_shapes.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +models/models_gpu_skinning: models/models_gpu_skinning.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file models/resources/models/gltf/greenman.glb@resources/models/gltf/greenman.glb \ + --preload-file models/resources/shaders/glsl100/skinning.vs@resources/shaders/glsl100/skinning.vs \ + --preload-file models/resources/shaders/glsl100/skinning.fs@resources/shaders/glsl100/skinning.fs + models/models_heightmap: models/models_heightmap.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file models/resources/heightmap.png@resources/heightmap.png @@ -1093,7 +1081,10 @@ models/models_loading_vox: models/models_loading_vox.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file models/resources/models/vox/chr_knight.vox@resources/models/vox/chr_knight.vox \ --preload-file models/resources/models/vox/chr_sword.vox@resources/models/vox/chr_sword.vox \ - --preload-file models/resources/models/vox/monu9.vox@resources/models/vox/monu9.vox + --preload-file models/resources/models/vox/monu9.vox@resources/models/vox/monu9.vox \ + --preload-file models/resources/models/vox/fez.vox@resources/models/vox/fez.vox \ + --preload-file models/resources/shaders/glsl100/voxel_lighting.vs@resources/shaders/glsl100/voxel_lighting.vs \ + --preload-file models/resources/shaders/glsl100/voxel_lighting.fs@resources/shaders/glsl100/voxel_lighting.fs models/models_mesh_generation: models/models_mesh_generation.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -1114,12 +1105,12 @@ models/models_rlgl_solar_system: models/models_rlgl_solar_system.c models/models_skybox: models/models_skybox.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file models/resources/dresden_square_2k.hdr@resources/dresden_square_2k.hdr \ - --preload-file models/resources/skybox.png@resources/skybox.png \ --preload-file models/resources/shaders/glsl100/skybox.vs@resources/shaders/glsl100/skybox.vs \ --preload-file models/resources/shaders/glsl100/skybox.fs@resources/shaders/glsl100/skybox.fs \ --preload-file models/resources/shaders/glsl100/cubemap.vs@resources/shaders/glsl100/cubemap.vs \ - --preload-file models/resources/shaders/glsl100/cubemap.fs@resources/shaders/glsl100/cubemap.fs + --preload-file models/resources/shaders/glsl100/cubemap.fs@resources/shaders/glsl100/cubemap.fs \ + --preload-file models/resources/dresden_square_2k.hdr@resources/dresden_square_2k.hdr \ + --preload-file models/resources/skybox.png@resources/skybox.png models/models_tesseract_view: models/models_tesseract_view.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -1132,21 +1123,16 @@ models/models_yaw_pitch_roll: models/models_yaw_pitch_roll.c --preload-file models/resources/models/obj/plane.obj@resources/models/obj/plane.obj \ --preload-file models/resources/models/obj/plane_diffuse.png@resources/models/obj/plane_diffuse.png -# Compile SHADER examples +# Compile SHADERS examples shaders/shaders_basic_lighting: shaders/shaders_basic_lighting.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file shaders/resources/texel_checker.png@resources/texel_checker.png \ - --preload-file shaders/resources/shaders/glsl100/lighting.fs@resources/shaders/glsl100/lighting.fs \ - --preload-file shaders/resources/shaders/glsl100/lighting.vs@resources/shaders/glsl100/lighting.vs + --preload-file shaders/resources/shaders/glsl100/lighting.vs@resources/shaders/glsl100/lighting.vs \ + --preload-file shaders/resources/shaders/glsl100/lighting.fs@resources/shaders/glsl100/lighting.fs shaders/shaders_basic_pbr: shaders/shaders_basic_pbr.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/shaders/glsl100/pbr.vs@resources/shaders/glsl100/pbr.vs \ - --preload-file shaders/resources/shaders/glsl120/pbr.vs@resources/shaders/glsl120/pbr.vs \ - --preload-file shaders/resources/shaders/glsl330/pbr.vs@resources/shaders/glsl330/pbr.vs \ --preload-file shaders/resources/shaders/glsl100/pbr.fs@resources/shaders/glsl100/pbr.fs \ - --preload-file shaders/resources/shaders/glsl120/pbr.fs@resources/shaders/glsl120/pbr.fs \ - --preload-file shaders/resources/shaders/glsl330/pbr.fs@resources/shaders/glsl330/pbr.fs \ --preload-file shaders/resources/models/old_car_new.glb@resources/models/old_car_new.glb \ --preload-file shaders/resources/old_car_d.png@resources/old_car_d.png \ --preload-file shaders/resources/old_car_mra.png@resources/old_car_mra.png \ @@ -1165,7 +1151,6 @@ shaders/shaders_custom_uniform: shaders/shaders_custom_uniform.c shaders/shaders_deferred_render: shaders/shaders_deferred_render.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file shaders/resources/fudesumi.png@resources/fudesumi.png \ --preload-file shaders/resources/shaders/glsl100/gbuffer.vs@resources/shaders/glsl100/gbuffer.vs \ --preload-file shaders/resources/shaders/glsl100/gbuffer.fs@resources/shaders/glsl100/gbuffer.fs \ --preload-file shaders/resources/shaders/glsl100/deferred_shading.vs@resources/shaders/glsl100/deferred_shading.vs \ @@ -1178,11 +1163,11 @@ shaders/shaders_eratosthenes: shaders/shaders_eratosthenes.c shaders/shaders_fog: shaders/shaders_fog.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/texel_checker.png@resources/texel_checker.png \ - --preload-file shaders/resources/shaders/glsl100/fog.fs@resources/shaders/glsl100/fog.fs \ - --preload-file shaders/resources/shaders/glsl100/lighting.vs@resources/shaders/glsl100/lighting.vs + --preload-file shaders/resources/shaders/glsl100/lighting.vs@resources/shaders/glsl100/lighting.vs \ + --preload-file shaders/resources/shaders/glsl100/fog.fs@resources/shaders/glsl100/fog.fs shaders/shaders_hot_reloading: shaders/shaders_hot_reloading.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sFORCE_FILESYSTEM=1 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/shaders/glsl100/reload.fs@resources/shaders/glsl100/reload.fs shaders/shaders_hybrid_render: shaders/shaders_hybrid_render.c @@ -1195,7 +1180,7 @@ shaders/shaders_julia_set: shaders/shaders_julia_set.c --preload-file shaders/resources/shaders/glsl100/julia_set.fs@resources/shaders/glsl100/julia_set.fs shaders/shaders_lightmap: shaders/shaders_lightmap.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sFORCE_FILESYSTEM=1 \ + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/shaders/glsl100/lightmap.vs@resources/shaders/glsl100/lightmap.vs \ --preload-file shaders/resources/shaders/glsl100/lightmap.fs@resources/shaders/glsl100/lightmap.fs \ --preload-file shaders/resources/cubicmap_atlas.png@resources/cubicmap_atlas.png \ @@ -1224,31 +1209,45 @@ shaders/shaders_postprocessing: shaders/shaders_postprocessing.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/models/church.obj@resources/models/church.obj \ --preload-file shaders/resources/models/church_diffuse.png@resources/models/church_diffuse.png \ - --preload-file shaders/resources/shaders/glsl100@resources/shaders/glsl100 + --preload-file shaders/resources/shaders/glsl100/grayscale.fs@resources/shaders/glsl100/grayscale.fs \ + --preload-file shaders/resources/shaders/glsl100/posterization.fs@resources/shaders/glsl100/posterization.fs \ + --preload-file shaders/resources/shaders/glsl100/dream_vision.fs@resources/shaders/glsl100/dream_vision.fs \ + --preload-file shaders/resources/shaders/glsl100/pixelizer.fs@resources/shaders/glsl100/pixelizer.fs \ + --preload-file shaders/resources/shaders/glsl100/cross_hatching.fs@resources/shaders/glsl100/cross_hatching.fs \ + --preload-file shaders/resources/shaders/glsl100/cross_stitching.fs@resources/shaders/glsl100/cross_stitching.fs \ + --preload-file shaders/resources/shaders/glsl100/predator.fs@resources/shaders/glsl100/predator.fs \ + --preload-file shaders/resources/shaders/glsl100/scanlines.fs@resources/shaders/glsl100/scanlines.fs \ + --preload-file shaders/resources/shaders/glsl100/fisheye.fs@resources/shaders/glsl100/fisheye.fs \ + --preload-file shaders/resources/shaders/glsl100/sobel.fs@resources/shaders/glsl100/sobel.fs \ + --preload-file shaders/resources/shaders/glsl100/bloom.fs@resources/shaders/glsl100/bloom.fs \ + --preload-file shaders/resources/shaders/glsl100/blur.fs@resources/shaders/glsl100/blur.fs shaders/shaders_raymarching: shaders/shaders_raymarching.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/shaders/glsl100/raymarching.fs@resources/shaders/glsl100/raymarching.fs +shaders/shaders_rounded_rectangle: shaders/shaders_rounded_rectangle.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file shaders/resources/shaders/glsl100/base.vs@resources/shaders/glsl100/base.vs \ + --preload-file shaders/resources/shaders/glsl100/rounded_rectangle.fs@resources/shaders/glsl100/rounded_rectangle.fs + shaders/shaders_shadowmap: shaders/shaders_shadowmap.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file shaders/resources/shaders/glsl120/shadowmap.vs@resources/shaders/glsl120/shadowmap.vs \ - --preload-file shaders/resources/shaders/glsl330/shadowmap.vs@resources/shaders/glsl330/shadowmap.vs \ - --preload-file shaders/resources/shaders/glsl120/shadowmap.fs@resources/shaders/glsl120/shadowmap.fs \ - --preload-file shaders/resources/shaders/glsl330/shadowmap.fs@resources/shaders/glsl330/shadowmap.fs \ - --preload-file shaders/resources/models/robot.glb@resources/models/robot.glb + --preload-file shaders/resources/shaders/glsl100/shadowmap.vs@resources/shaders/glsl100/shadowmap.vs \ + --preload-file shaders/resources/shaders/glsl100/shadowmap.fs@resources/shaders/glsl100/shadowmap.fs \ + --preload-file shaders/resources/models/robot.glb@resources/models/robot.glb \ + --preload-file shaders/shaders_shadowmap.png@shaders_shadowmap.png shaders/shaders_shapes_textures: shaders/shaders_shapes_textures.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/fudesumi.png@resources/fudesumi.png \ - --preload-file shaders/resources/shaders/glsl100/base.vs@resources/shaders/glsl100/base.vs \ --preload-file shaders/resources/shaders/glsl100/grayscale.fs@resources/shaders/glsl100/grayscale.fs shaders/shaders_simple_mask: shaders/shaders_simple_mask.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file shaders/resources/shaders/glsl100/mask.fs@resources/shaders/glsl100/mask.fs \ --preload-file shaders/resources/plasma.png@resources/plasma.png \ - --preload-file shaders/resources/mask.png@resources/mask.png \ - --preload-file shaders/resources/shaders/glsl100/mask.fs@resources/shaders/glsl100/mask.fs + --preload-file shaders/resources/mask.png@resources/mask.png shaders/shaders_spotlight: shaders/shaders_spotlight.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -1261,34 +1260,32 @@ shaders/shaders_texture_drawing: shaders/shaders_texture_drawing.c shaders/shaders_texture_outline: shaders/shaders_texture_outline.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file shaders/resources/shaders/glsl100/outline.fs@resources/shaders/glsl100/outline.fs \ - --preload-file shaders/resources/fudesumi.png@resources/fudesumi.png + --preload-file shaders/resources/fudesumi.png@resources/fudesumi.png \ + --preload-file shaders/resources/shaders/glsl100/outline.fs@resources/shaders/glsl100/outline.fs shaders/shaders_texture_tiling: shaders/shaders_texture_tiling.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file shaders/resources/shaders/glsl100/tiling.fs@resources/shaders/glsl100/tiling.fs \ - --preload-file shaders/resources/cubicmap_atlas.png@resources/cubicmap_atlas.png + --preload-file shaders/resources/cubicmap_atlas.png@resources/cubicmap_atlas.png \ + --preload-file shaders/resources/shaders/glsl100/tiling.fs@resources/shaders/glsl100/tiling.fs shaders/shaders_texture_waves: shaders/shaders_texture_waves.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/space.png@resources/space.png \ --preload-file shaders/resources/shaders/glsl100/wave.fs@resources/shaders/glsl100/wave.fs +shaders/shaders_vertex_displacement: shaders/shaders_vertex_displacement.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file shaders/resources/shaders/glsl100/vertex_displacement.vs@resources/shaders/glsl100/vertex_displacement.vs \ + --preload-file shaders/resources/shaders/glsl100/vertex_displacement.fs@resources/shaders/glsl100/vertex_displacement.fs + shaders/shaders_view_depth: shaders/shaders_view_depth.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file shaders/resources/shaders/glsl100/write_depth.fs@resources/shaders/glsl100/write_depth.fs + --preload-file shaders/resources/shaders/glsl100/depth.fs@resources/shaders/glsl100/depth.fs shaders/shaders_write_depth: shaders/shaders_write_depth.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/shaders/glsl100/write_depth.fs@resources/shaders/glsl100/write_depth.fs -shaders/shaders_vertex_displacement: shaders/shaders_vertex_displacement.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file shaders/resources/shaders/glsl100/vertex_displacement.vs@resources/shaders/glsl100/vertex_displacement.vs \ - --preload-file shaders/resources/shaders/glsl330/vertex_displacement.vs@resources/shaders/glsl330/vertex_displacement.vs \ - --preload-file shaders/resources/shaders/glsl100/vertex_displacement.fs@resources/shaders/glsl100/vertex_displacement.fs \ - --preload-file shaders/resources/shaders/glsl330/vertex_displacement.fs@resources/shaders/glsl330/vertex_displacement.fs - # Compile AUDIO examples audio/audio_mixed_processor: audio/audio_mixed_processor.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -1296,15 +1293,14 @@ audio/audio_mixed_processor: audio/audio_mixed_processor.c --preload-file audio/resources/coin.wav@resources/coin.wav audio/audio_module_playing: audio/audio_module_playing.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file audio/resources/mini1111.xm@resources/mini1111.xm + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) audio/audio_music_stream: audio/audio_music_stream.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file audio/resources/country.mp3@resources/country.mp3 audio/audio_raw_stream: audio/audio_raw_stream.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -sTOTAL_MEMORY=67108864 + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) audio/audio_sound_loading: audio/audio_sound_loading.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -1315,29 +1311,13 @@ audio/audio_sound_multi: audio/audio_sound_multi.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file audio/resources/sound.wav@resources/sound.wav +audio/audio_sound_positioning: audio/audio_sound_positioning.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file audio/resources/coin.wav@resources/coin.wav + audio/audio_stream_effects: audio/audio_stream_effects.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file audio/resources/country.mp3@resources/country.mp3 - -# Compile OTHERS examples -others/easings_testbed: others/easings_testbed.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - -others/embedded_files_loading: others/embedded_files_loading.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - -others/raylib_opengl_interop: - $(info Skipping_others_raylib_opengl_interop) - -others/raymath_vector_angle: others/raymath_vector_angle.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - -others/rlgl_compute_shader: - $(info Skipping_others_rlgl_compute_shader) - -others/rlgl_standalone: - $(info Skipping_others_rlgl_standalone) - #EXAMPLES_LIST_END # Clean everything From c6f8f06f7ec9ee3e83c0ec771f5ff6da07646381 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 6 Aug 2025 18:20:34 +0200 Subject: [PATCH 197/242] Update rexm.c --- tools/rexm/rexm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 8a7ec80cd..2e78d23d0 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -1246,7 +1246,7 @@ static int UpdateRequiredFiles(void) mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("| [%s](%s/%s.c) | \"%s\" | %s | %.1f | %.1f | [%s](https://github.com/%s) |\n", exCollection[x].name, exCollection[x].category, exCollection[x].name, exCollection[x].category, exCollection[x].name, exCollection[x].name, - stars, exCollection[x].verCreated, exCollection[x].verUpdated, exCollection[x].author, exCollection[x].authorGitHub + 1)); + stars, exCollection[x].verCreated, exCollection[x].verUpdated, exCollection[x].author, exCollection[x].authorGitHub)); } UnloadExamplesData(exCollection); From 533f06109de725b70be2ec2e60a7c022abb8ead2 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 6 Aug 2025 18:23:42 +0200 Subject: [PATCH 198/242] Update README.md --- examples/README.md | 374 +++++++++++++++++++++++---------------------- 1 file changed, 188 insertions(+), 186 deletions(-) diff --git a/examples/README.md b/examples/README.md index 8a387596a..59e2f3d34 100644 --- a/examples/README.md +++ b/examples/README.md @@ -16,221 +16,223 @@ You may find it easier to use than other toolchains, especially when it comes to - `zig build [module]` to compile all examples for a module (e.g. `zig build core`) - `zig build [example]` to compile _and run_ a particular example (e.g. `zig build core_basic_window`) -## EXAMPLES COLLECTION [TOTAL: 158] +## EXAMPLES COLLECTION [TOTAL: 159] -### category: core -Examples using raylib [core](../src/rcore.c) platform functionality like window creation, inputs, drawing modes and system functionality. +### category: core [36] -| ## | example | image | difficulty
level | version
created | last version
updated | original
developer | -|----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 01 | [core_basic_window](core/core_basic_window.c) | core_basic_window | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 02 | [core_input_keys](core/core_input_keys.c) | core_input_keys | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 03 | [core_input_mouse](core/core_input_mouse.c) | core_input_mouse | ⭐️☆☆☆ | 1.0 | 5.5 | [Ray](https://github.com/raysan5) | -| 04 | [core_input_mouse_wheel](core/core_input_mouse_wheel.c) | core_input_mouse_wheel | ⭐️☆☆☆ | 1.1 | 1.3 | [Ray](https://github.com/raysan5) | -| 05 | [core_input_gamepad](core/core_input_gamepad.c) | core_input_gamepad | ⭐️☆☆☆ | 1.1 | 4.2 | [Ray](https://github.com/raysan5) | -| 06 | [core_input_multitouch](core/core_input_multitouch.c) | core_input_multitouch | ⭐️☆☆☆ | 2.1 | 2.5 | [Berni](https://github.com/Berni8k) | -| 07 | [core_input_gestures](core/core_input_gestures.c) | core_input_gestures | ⭐️⭐️☆☆ | 1.4 | 4.2 | [Ray](https://github.com/raysan5) | -| 08 | [core_input_virtual_controls](core/core_input_virtual_controls.c) | core_input_virtual_controls | ⭐️⭐️☆☆ | 5.0 | 5.0 | [oblerion](https://github.com/oblerion) | -| 09 | [core_2d_camera](core/core_2d_camera.c) | core_2d_camera | ⭐️⭐️☆☆ | 1.5 | 3.0 | [Ray](https://github.com/raysan5) | -| 10 | [core_2d_camera_mouse_zoom](core/core_2d_camera_mouse_zoom.c) | core_2d_camera_mouse_zoom | ⭐️⭐️☆☆ | 4.2 | 4.2 | [Jeffery Myers](https://github.com/JeffM2501) | -| 11 | [core_2d_camera_platformer](core/core_2d_camera_platformer.c) | core_2d_camera_platformer | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [arvyy](https://github.com/arvyy) | -| 12 | [core_2d_camera_split_screen](core/core_2d_camera_split_screen.c) | core_2d_camera_split_screen | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [Gabriel dos Santos Sanches](https://github.com/gabrielssanches) | -| 13 | [core_3d_camera_mode](core/core_3d_camera_mode.c) | core_3d_camera_mode | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 14 | [core_3d_camera_free](core/core_3d_camera_free.c) | core_3d_camera_free | ⭐️☆☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 15 | [core_3d_camera_first_person](core/core_3d_camera_first_person.c) | core_3d_camera_first_person | ⭐️⭐️☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 16 | [core_3d_camera_split_screen](core/core_3d_camera_split_screen.c) | core_3d_camera_split_screen | ⭐️⭐️⭐️☆ | 3.7 | 4.0 | [Jeffery Myers](https://github.com/JeffM2501) | -| 17 | [core_3d_picking](core/core_3d_picking.c) | core_3d_picking | ⭐️⭐️☆☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | -| 18 | [core_world_screen](core/core_world_screen.c) | core_world_screen | ⭐️⭐️☆☆ | 1.3 | 1.4 | [Ray](https://github.com/raysan5) | -| 19 | [core_custom_logging](core/core_custom_logging.c) | core_custom_logging | ⭐️⭐️⭐️☆ | 2.5 | 2.5 | [Pablo Marcos Oltra](https://github.com/pamarcos) | -| 20 | [core_window_flags](core/core_window_flags.c) | core_window_flags | ⭐️⭐️⭐️☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 21 | [core_window_letterbox](core/core_window_letterbox.c) | core_window_letterbox | ⭐️⭐️☆☆ | 2.5 | 4.0 | [Anata](https://github.com/anatagawa) | -| 22 | [core_window_should_close](core/core_window_should_close.c) | core_window_should_close | ⭐️☆☆☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) | -| 23 | [core_drop_files](core/core_drop_files.c) | core_drop_files | ⭐️⭐️☆☆ | 1.3 | 4.2 | [Ray](https://github.com/raysan5) | -| 24 | [core_random_values](core/core_random_values.c) | core_random_values | ⭐️☆☆☆ | 1.1 | 1.1 | [Ray](https://github.com/raysan5) | -| 25 | [core_storage_values](core/core_storage_values.c) | core_storage_values | ⭐️⭐️☆☆ | 1.4 | 4.2 | [Ray](https://github.com/raysan5) | -| 26 | [core_vr_simulator](core/core_vr_simulator.c) | core_vr_simulator | ⭐️⭐️⭐️☆ | 2.5 | 4.0 | [Ray](https://github.com/raysan5) | -| 27 | [core_loading_thread](core/core_loading_thread.c) | core_loading_thread | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [Ray](https://github.com/raysan5) | -| 28 | [core_scissor_test](core/core_scissor_test.c) | core_scissor_test | ⭐️☆☆☆ | 2.5 | 3.0 | [Chris Dill](https://github.com/MysteriousSpace) | -| 29 | [core_basic_screen_manager](core/core_basic_screen_manager.c) | core_basic_screen_manager | ⭐️☆☆☆ | 4.0 | 4.0 | [Ray](https://github.com/raysan5) | -| 30 | [core_custom_frame_control](core/core_custom_frame_control.c) | core_custom_frame_control | ⭐️⭐️⭐️⭐️ | 4.0 | 4.0 | [Ray](https://github.com/raysan5) | -| 31 | [core_smooth_pixelperfect](core/core_smooth_pixelperfect.c) | core_smooth_pixelperfect | ⭐️⭐️⭐️☆ | 3.7 | 4.0 | [Giancamillo Alessandroni](https://github.com/NotManyIdeasDev) | -| 32 | [core_random_sequence](core/core_random_sequence.c) | core_random_sequence | ⭐️☆☆☆ | 5.0 | 5.0 | [Dalton Overmyer](https://github.com/REDl3east) | -| 33 | [core_basic_window_web](core/core_basic_window_web.c) | core_basic_window_web | ⭐️☆☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 34 | [core_input_gestures_web](core/core_input_gestures_web.c) | core_input_gestures_web | ⭐️⭐️☆☆ | 4.6-dev | 4.6-dev | [ubkp](https://github.com/ubkp) | -| 35 | [core_automation_events](core/core_automation_events.c) | core_automation_events | ⭐️⭐️⭐️☆ | 5.0 | 5.0 | [Ray](https://github.com/raysan5) | -| 36 | [core_high_dpi](core/core_high_dpi.c) | core_high_dpi | ⭐️☆☆☆ | 5.0 | 5.0 | [Jonathan Marler](https://github.com/marler8997) | +Examples using raylib[core](../src/rcore.c) platform functionality like window creation, inputs, drawing modes and system functionality. -### category: shapes +| example | image | difficulty
level | version
created | last version
updated | original
developer | +|-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| +| [core_basic_window](core/core_basic_window.c) | core_basic_window | ⭐☆☆☆ | 1.0 | 1.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_input_keys](core/core_input_keys.c) | core_input_keys | ⭐☆☆☆ | 1.0 | 1.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_input_mouse](core/core_input_mouse.c) | core_input_mouse | ⭐☆☆☆ | 1.0 | 5.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_input_mouse_wheel](core/core_input_mouse_wheel.c) | core_input_mouse_wheel | ⭐☆☆☆ | 1.1 | 1.3 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_input_gamepad](core/core_input_gamepad.c) | core_input_gamepad | ⭐☆☆☆ | 1.1 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_input_multitouch](core/core_input_multitouch.c) | core_input_multitouch | ⭐☆☆☆ | 2.1 | 2.5 | [Berni](https://github.com/Berni8k) | +| [core_input_gestures](core/core_input_gestures.c) | core_input_gestures | ⭐⭐☆☆ | 1.4 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_input_virtual_controls](core/core_input_virtual_controls.c) | core_input_virtual_controls | ⭐⭐☆☆ | 5.0 | 5.0 | [oblerion](https://github.com/oblerion) | +| [core_2d_camera](core/core_2d_camera.c) | core_2d_camera | ⭐⭐☆☆ | 1.5 | 3.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_2d_camera_mouse_zoom](core/core_2d_camera_mouse_zoom.c) | core_2d_camera_mouse_zoom | ⭐⭐☆☆ | 4.2 | 4.2 | [Jeffery Myers](https://github.com/JeffM2501) | +| [core_2d_camera_platformer](core/core_2d_camera_platformer.c) | core_2d_camera_platformer | ⭐⭐⭐☆ | 2.5 | 3.0 | [arvyy](https://github.com/arvyy) | +| [core_2d_camera_split_screen](core/core_2d_camera_split_screen.c) | core_2d_camera_split_screen | ⭐⭐⭐⭐️ | 4.5 | 4.5 | [Gabriel dos Santos Sanches](https://github.com/gabrielssanches) | +| [core_3d_camera_mode](core/core_3d_camera_mode.c) | core_3d_camera_mode | ⭐☆☆☆ | 1.0 | 1.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_3d_camera_free](core/core_3d_camera_free.c) | core_3d_camera_free | ⭐☆☆☆ | 1.3 | 1.3 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_3d_camera_first_person](core/core_3d_camera_first_person.c) | core_3d_camera_first_person | ⭐⭐☆☆ | 1.3 | 1.3 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_3d_camera_split_screen](core/core_3d_camera_split_screen.c) | core_3d_camera_split_screen | ⭐⭐⭐☆ | 3.7 | 4.0 | [Jeffery Myers](https://github.com/JeffM2501) | +| [core_3d_picking](core/core_3d_picking.c) | core_3d_picking | ⭐⭐☆☆ | 1.3 | 4.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_world_screen](core/core_world_screen.c) | core_world_screen | ⭐⭐☆☆ | 1.3 | 1.4 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_custom_logging](core/core_custom_logging.c) | core_custom_logging | ⭐⭐⭐☆ | 2.5 | 2.5 | [Pablo Marcos Oltra](https://github.com/pamarcos) | +| [core_window_flags](core/core_window_flags.c) | core_window_flags | ⭐⭐⭐☆ | 3.5 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_window_letterbox](core/core_window_letterbox.c) | core_window_letterbox | ⭐⭐☆☆ | 2.5 | 4.0 | [Anata](https://github.com/anatagawa) | +| [core_window_should_close](core/core_window_should_close.c) | core_window_should_close | ⭐☆☆☆ | 4.2 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_drop_files](core/core_drop_files.c) | core_drop_files | ⭐⭐☆☆ | 1.3 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_random_values](core/core_random_values.c) | core_random_values | ⭐☆☆☆ | 1.1 | 1.1 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_storage_values](core/core_storage_values.c) | core_storage_values | ⭐⭐☆☆ | 1.4 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_vr_simulator](core/core_vr_simulator.c) | core_vr_simulator | ⭐⭐⭐☆ | 2.5 | 4.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_loading_thread](core/core_loading_thread.c) | core_loading_thread | ⭐⭐⭐☆ | 2.5 | 3.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_scissor_test](core/core_scissor_test.c) | core_scissor_test | ⭐☆☆☆ | 2.5 | 3.0 | [Chris Dill](https://github.com/MysteriousSpace) | +| [core_basic_screen_manager](core/core_basic_screen_manager.c) | core_basic_screen_manager | ⭐☆☆☆ | 4.0 | 4.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_custom_frame_control](core/core_custom_frame_control.c) | core_custom_frame_control | ⭐⭐⭐⭐️ | 4.0 | 4.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_smooth_pixelperfect](core/core_smooth_pixelperfect.c) | core_smooth_pixelperfect | ⭐⭐⭐☆ | 3.7 | 4.0 | [Giancamillo Alessandroni](https://github.com/NotManyIdeasDev) | +| [core_random_sequence](core/core_random_sequence.c) | core_random_sequence | ⭐☆☆☆ | 5.0 | 5.0 | [Dalton Overmyer](https://github.com/REDl3east) | +| [core_basic_window_web](core/core_basic_window_web.c) | core_basic_window_web | ⭐☆☆☆ | 1.3 | 1.3 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_input_gestures_web](core/core_input_gestures_web.c) | core_input_gestures_web | ⭐⭐☆☆ | 4.6 | 4.6 | [ubkp](https://github.com/ubkp) | +| [core_automation_events](core/core_automation_events.c) | core_automation_events | ⭐⭐⭐☆ | 5.0 | 5.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_high_dpi](core/core_high_dpi.c) | core_high_dpi | ⭐☆☆☆ | 5.0 | 5.0 | [Jonathan Marler](https://github.com/marler8997) | + +### category: shapes [20] Examples using raylib shapes drawing functionality, provided by raylib [shapes](../src/rshapes.c) module. -| ## | example | image | difficulty
level | version
created | last version
updated | original
developer | -|----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 36 | [shapes_basic_shapes](shapes/shapes_basic_shapes.c) | shapes_basic_shapes | ⭐️☆☆☆ | 1.0 | 4.2 | [Ray](https://github.com/raysan5) | -| 37 | [shapes_bouncing_ball](shapes/shapes_bouncing_ball.c) | shapes_bouncing_ball | ⭐️☆☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | -| 38 | [shapes_colors_palette](shapes/shapes_colors_palette.c) | shapes_colors_palette | ⭐️⭐️☆☆ | 1.0 | 2.5 | [Ray](https://github.com/raysan5) | -| 39 | [shapes_logo_raylib](shapes/shapes_logo_raylib.c) | shapes_logo_raylib | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 40 | [shapes_logo_raylib_anim](shapes/shapes_logo_raylib_anim.c) | shapes_logo_raylib_anim | ⭐️⭐️☆☆ | 2.5 | 4.0 | [Ray](https://github.com/raysan5) | -| 41 | [shapes_rectangle_scaling](shapes/shapes_rectangle_scaling.c) | shapes_rectangle_scaling | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Vlad Adrian](https://github.com/demizdor) | -| 42 | [shapes_lines_bezier](shapes/shapes_lines_bezier.c) | shapes_lines_bezier | ⭐️☆☆☆ | 1.7 | 1.7 | [Ray](https://github.com/raysan5) | -| 43 | [shapes_collision_area](shapes/shapes_collision_area.c) | shapes_collision_area | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | -| 44 | [shapes_following_eyes](shapes/shapes_following_eyes.c) | shapes_following_eyes | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | -| 45 | [shapes_easings_ball_anim](shapes/shapes_easings_ball_anim.c) | shapes_easings_ball_anim | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | -| 46 | [shapes_easings_box_anim](shapes/shapes_easings_box_anim.c) | shapes_easings_box_anim | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | -| 47 | [shapes_easings_rectangle_array](shapes/shapes_easings_rectangle_array.c) | shapes_easings_rectangle_array | ⭐️⭐️⭐️☆ | 2.0 | 2.5 | [Ray](https://github.com/raysan5) | -| 48 | [shapes_draw_ring](shapes/shapes_draw_ring.c) | shapes_draw_ring | ⭐️⭐️⭐️☆ | 2.5 | 2.5 | [Vlad Adrian](https://github.com/demizdor) | -| 49 | [shapes_draw_circle_sector](shapes/shapes_draw_circle_sector.c) | shapes_draw_circle_sector | ⭐️⭐️⭐️☆ | 2.5 | 2.5 | [Vlad Adrian](https://github.com/demizdor) | -| 50 | [shapes_draw_rectangle_rounded](shapes/shapes_draw_rectangle_rounded.c) | shapes_draw_rectangle_rounded | ⭐️⭐️⭐️☆ | 2.5 | 2.5 | [Vlad Adrian](https://github.com/demizdor) | -| 51 | [shapes_top_down_lights](shapes/shapes_top_down_lights.c) | shapes_top_down_lights | ⭐️⭐️⭐️⭐️ | 4.2 | 4.2 | [Jeffery Myers](https://github.com/JeffM2501) | -| 52 | [shapes_rectangle_advanced](shapes/shapes_rectangle_advanced.c) | shapes_rectangle_advanced | ⭐️⭐️⭐️⭐️ | 5.5 | 5.5 | [Everton Jr.](https://github.com/evertonse) | -| 53 | [shapes_splines_drawing](shapes/shapes_splines_drawing.c) | shapes_splines_drawing | ⭐️⭐️⭐️☆ | 5.0 | 5.0 | [Ray](https://github.com/raysan5) | -| 54 | [shapes_digital_clock](shapes/shapes_digital_clock.c) | shapes_digital_clock | ⭐️⭐️☆☆ | 5.5 | 5.5 | [Hamza RAHAL](https://github.com/rhmz-rhl) | -| 55 | [shapes_double_pendulum](shapes/shapes_double_pendulum.c) | shapes_double_pendulum | ⭐️⭐️☆☆ | 5.5 | 5.5 | [JoeCheong](https://github.com/Joecheong2006) | +| example | image | difficulty
level | version
created | last version
updated | original
developer | +|-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| +| [shapes_basic_shapes](shapes/shapes_basic_shapes.c) | shapes_basic_shapes | ⭐☆☆☆ | 1.0 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) | +| [shapes_bouncing_ball](shapes/shapes_bouncing_ball.c) | shapes_bouncing_ball | ⭐☆☆☆ | 2.5 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [shapes_colors_palette](shapes/shapes_colors_palette.c) | shapes_colors_palette | ⭐⭐☆☆ | 1.0 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [shapes_logo_raylib](shapes/shapes_logo_raylib.c) | shapes_logo_raylib | ⭐☆☆☆ | 1.0 | 1.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [shapes_logo_raylib_anim](shapes/shapes_logo_raylib_anim.c) | shapes_logo_raylib_anim | ⭐⭐☆☆ | 2.5 | 4.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [shapes_rectangle_scaling](shapes/shapes_rectangle_scaling.c) | shapes_rectangle_scaling | ⭐⭐☆☆ | 2.5 | 2.5 | [Vlad Adrian](https://github.com/demizdor) | +| [shapes_lines_bezier](shapes/shapes_lines_bezier.c) | shapes_lines_bezier | ⭐☆☆☆ | 1.7 | 1.7 | [Ramon Santamaria](https://github.com/raysan5) | +| [shapes_collision_area](shapes/shapes_collision_area.c) | shapes_collision_area | ⭐⭐☆☆ | 2.5 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [shapes_following_eyes](shapes/shapes_following_eyes.c) | shapes_following_eyes | ⭐⭐☆☆ | 2.5 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [shapes_easings_ball_anim](shapes/shapes_easings_ball_anim.c) | shapes_easings_ball_anim | ⭐⭐☆☆ | 2.5 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [shapes_easings_box_anim](shapes/shapes_easings_box_anim.c) | shapes_easings_box_anim | ⭐⭐☆☆ | 2.5 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [shapes_easings_rectangle_array](shapes/shapes_easings_rectangle_array.c) | shapes_easings_rectangle_array | ⭐⭐⭐☆ | 2.0 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [shapes_draw_ring](shapes/shapes_draw_ring.c) | shapes_draw_ring | ⭐⭐⭐☆ | 2.5 | 2.5 | [Vlad Adrian](https://github.com/demizdor) | +| [shapes_draw_circle_sector](shapes/shapes_draw_circle_sector.c) | shapes_draw_circle_sector | ⭐⭐⭐☆ | 2.5 | 2.5 | [Vlad Adrian](https://github.com/demizdor) | +| [shapes_draw_rectangle_rounded](shapes/shapes_draw_rectangle_rounded.c) | shapes_draw_rectangle_rounded | ⭐⭐⭐☆ | 2.5 | 2.5 | [Vlad Adrian](https://github.com/demizdor) | +| [shapes_top_down_lights](shapes/shapes_top_down_lights.c) | shapes_top_down_lights | ⭐⭐⭐⭐️ | 4.2 | 4.2 | [Jeffery Myers](https://github.com/JeffM2501) | +| [shapes_rectangle_advanced](shapes/shapes_rectangle_advanced.c) | shapes_rectangle_advanced | ⭐⭐⭐⭐️ | 5.5 | 5.5 | [Everton Jr.](https://github.com/evertonse) | +| [shapes_splines_drawing](shapes/shapes_splines_drawing.c) | shapes_splines_drawing | ⭐⭐⭐☆ | 5.0 | 5.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [shapes_digital_clock](shapes/shapes_digital_clock.c) | shapes_digital_clock | ⭐⭐☆☆ | 5.5 | 5.5 | [Hamza RAHAL](https://github.com/rhmz-rhl) | +| [shapes_double_pendulum](shapes/shapes_double_pendulum.c) | shapes_double_pendulum | ⭐⭐☆☆ | 5.5 | 5.5 | [JoeCheong](https://github.com/Joecheong2006) | -### category: textures +### category: textures [26] Examples using raylib textures functionality, including image/textures loading/generation and drawing, provided by raylib [textures](../src/rtextures.c) module. -| ## | example | image | difficulty
level | version
created | last version
updated | original
developer | -|----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 56 | [textures_logo_raylib](textures/textures_logo_raylib.c) | textures_logo_raylib | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 57 | [textures_srcrec_dstrec](textures/textures_srcrec_dstrec.c) | textures_srcrec_dstrec | ⭐️⭐️⭐️☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 58 | [textures_image_drawing](textures/textures_image_drawing.c) | textures_image_drawing | ⭐️⭐️☆☆ | 1.4 | 1.4 | [Ray](https://github.com/raysan5) | -| 59 | [textures_image_generation](textures/textures_image_generation.c) | textures_image_generation | ⭐️⭐️☆☆ | 1.8 | 1.8 | [Wilhem Barbier](https://github.com/nounoursheureux) | -| 60 | [textures_image_loading](textures/textures_image_loading.c) | textures_image_loading | ⭐️☆☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 61 | [textures_image_processing](textures/textures_image_processing.c) | textures_image_processing | ⭐️⭐️⭐️☆ | 1.4 | 3.5 | [Ray](https://github.com/raysan5) | -| 62 | [textures_image_text](textures/textures_image_text.c) | textures_image_text | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) | -| 63 | [textures_to_image](textures/textures_to_image.c) | textures_to_image | ⭐️☆☆☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | -| 64 | [textures_raw_data](textures/textures_raw_data.c) | textures_raw_data | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | -| 65 | [textures_particles_blending](textures/textures_particles_blending.c) | textures_particles_blending | ⭐️☆☆☆ | 1.7 | 3.5 | [Ray](https://github.com/raysan5) | -| 66 | [textures_npatch_drawing](textures/textures_npatch_drawing.c) | textures_npatch_drawing | ⭐️⭐️⭐️☆ | 2.0 | 2.5 | [Jorge A. Gomes](https://github.com/overdev) | -| 67 | [textures_background_scrolling](textures/textures_background_scrolling.c) | textures_background_scrolling | ⭐️☆☆☆ | 2.0 | 2.5 | [Ray](https://github.com/raysan5) | -| 68 | [textures_sprite_anim](textures/textures_sprite_anim.c) | textures_sprite_anim | ⭐️⭐️☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 69 | [textures_sprite_button](textures/textures_sprite_button.c) | textures_sprite_button | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | -| 70 | [textures_sprite_explosion](textures/textures_sprite_explosion.c) | textures_sprite_explosion | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 71 | [textures_bunnymark](textures/textures_bunnymark.c) | textures_bunnymark | ⭐️⭐️⭐️☆ | 1.6 | 2.5 | [Ray](https://github.com/raysan5) | -| 72 | [textures_mouse_painting](textures/textures_mouse_painting.c) | textures_mouse_painting | ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Chris Dill](https://github.com/MysteriousSpace) | -| 73 | [textures_blend_modes](textures/textures_blend_modes.c) | textures_blend_modes | ⭐️☆☆☆ | 3.5 | 3.5 | [Karlo Licudine](https://github.com/accidentalrebel) | -| 74 | [textures_draw_tiled](textures/textures_draw_tiled.c) | textures_draw_tiled | ⭐️⭐️⭐️☆ | 3.0 | 4.2 | [Vlad Adrian](https://github.com/demizdor) | -| 75 | [textures_polygon](textures/textures_polygon.c) | textures_polygon | ⭐️☆☆☆ | 3.7 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | -| 76 | [textures_fog_of_war](textures/textures_fog_of_war.c) | textures_fog_of_war | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) | -| 77 | [textures_gif_player](textures/textures_gif_player.c) | textures_gif_player | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) | -| 78 | [textures_image_kernel](textures/textures_image_kernel.c) | textures_image_kernel | ⭐️⭐️⭐️⭐️ | 1.3 | 1.3 | [Karim Salem](https://github.com/kimo-s) | -| 79 | [textures_image_channel](textures/textures_image_channel.c) | textures_image_channel | ⭐️⭐️☆☆ | 5.1-dev | 5.1-dev | [Bruno Cabral](https://github.com/brccabral) | -| 80 | [textures_image_rotate](textures/textures_image_rotate.c) | textures_image_rotate | ⭐️⭐️☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 81 | [textures_textured_curve](textures/textures_textured_curve.c) | textures_textured_curve | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Jeffery Myers](https://github.com/JeffM2501) | +| example | image | difficulty
level | version
created | last version
updated | original
developer | +|-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| +| [textures_logo_raylib](textures/textures_logo_raylib.c) | textures_logo_raylib | ⭐☆☆☆ | 1.0 | 1.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [textures_srcrec_dstrec](textures/textures_srcrec_dstrec.c) | textures_srcrec_dstrec | ⭐⭐⭐☆ | 1.3 | 1.3 | [Ramon Santamaria](https://github.com/raysan5) | +| [textures_image_drawing](textures/textures_image_drawing.c) | textures_image_drawing | ⭐⭐☆☆ | 1.4 | 1.4 | [Ramon Santamaria](https://github.com/raysan5) | +| [textures_image_generation](textures/textures_image_generation.c) | textures_image_generation | ⭐⭐☆☆ | 1.8 | 1.8 | [Wilhem Barbier](https://github.com/nounoursheureux) | +| [textures_image_loading](textures/textures_image_loading.c) | textures_image_loading | ⭐☆☆☆ | 1.3 | 1.3 | [Ramon Santamaria](https://github.com/raysan5) | +| [textures_image_processing](textures/textures_image_processing.c) | textures_image_processing | ⭐⭐⭐☆ | 1.4 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [textures_image_text](textures/textures_image_text.c) | textures_image_text | ⭐⭐☆☆ | 1.8 | 4.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [textures_to_image](textures/textures_to_image.c) | textures_to_image | ⭐☆☆☆ | 1.3 | 4.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [textures_raw_data](textures/textures_raw_data.c) | textures_raw_data | ⭐⭐⭐☆ | 1.3 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [textures_particles_blending](textures/textures_particles_blending.c) | textures_particles_blending | ⭐☆☆☆ | 1.7 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [textures_npatch_drawing](textures/textures_npatch_drawing.c) | textures_npatch_drawing | ⭐⭐⭐☆ | 2.0 | 2.5 | [Jorge A. Gomes](https://github.com/overdev) | +| [textures_background_scrolling](textures/textures_background_scrolling.c) | textures_background_scrolling | ⭐☆☆☆ | 2.0 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [textures_sprite_anim](textures/textures_sprite_anim.c) | textures_sprite_anim | ⭐⭐☆☆ | 1.3 | 1.3 | [Ramon Santamaria](https://github.com/raysan5) | +| [textures_sprite_button](textures/textures_sprite_button.c) | textures_sprite_button | ⭐⭐☆☆ | 2.5 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [textures_sprite_explosion](textures/textures_sprite_explosion.c) | textures_sprite_explosion | ⭐⭐☆☆ | 2.5 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [textures_bunnymark](textures/textures_bunnymark.c) | textures_bunnymark | ⭐⭐⭐☆ | 1.6 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [textures_mouse_painting](textures/textures_mouse_painting.c) | textures_mouse_painting | ⭐⭐⭐☆ | 3.0 | 3.0 | [Chris Dill](https://github.com/MysteriousSpace) | +| [textures_blend_modes](textures/textures_blend_modes.c) | textures_blend_modes | ⭐☆☆☆ | 3.5 | 3.5 | [Karlo Licudine](https://github.com/accidentalrebel) | +| [textures_draw_tiled](textures/textures_draw_tiled.c) | textures_draw_tiled | ⭐⭐⭐☆ | 3.0 | 4.2 | [Vlad Adrian](https://github.com/demizdor) | +| [textures_polygon](textures/textures_polygon.c) | textures_polygon | ⭐☆☆☆ | 3.7 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | +| [textures_fog_of_war](textures/textures_fog_of_war.c) | textures_fog_of_war | ⭐⭐⭐☆ | 4.2 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) | +| [textures_gif_player](textures/textures_gif_player.c) | textures_gif_player | ⭐⭐⭐☆ | 4.2 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) | +| [textures_image_kernel](textures/textures_image_kernel.c) | textures_image_kernel | ⭐⭐⭐⭐️ | 1.3 | 1.3 | [Karim Salem](https://github.com/kimo-s) | +| [textures_image_channel](textures/textures_image_channel.c) | textures_image_channel | ⭐⭐☆☆ | 5.1 | 5.1 | [Bruno Cabral](https://github.com/brccabral) | +| [textures_image_rotate](textures/textures_image_rotate.c) | textures_image_rotate | ⭐⭐☆☆ | 1.0 | 1.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [textures_textured_curve](textures/textures_textured_curve.c) | textures_textured_curve | ⭐⭐⭐☆ | 4.5 | 4.5 | [Jeffery Myers](https://github.com/JeffM2501) | -### category: text +### category: text [12] Examples using raylib text functionality, including sprite fonts loading/generation and text drawing, provided by raylib [text](../src/rtext.c) module. -| ## | example | image | difficulty
level | version
created | last version
updated | original
developer | -|----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 82 | [text_raylib_fonts](text/text_raylib_fonts.c) | text_raylib_fonts | ⭐️☆☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) | -| 83 | [text_font_spritefont](text/text_font_spritefont.c) | text_font_spritefont | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 84 | [text_font_filters](text/text_font_filters.c) | text_font_filters | ⭐️⭐️☆☆ | 1.3 | 4.2 | [Ray](https://github.com/raysan5) | -| 85 | [text_font_loading](text/text_font_loading.c) | text_font_loading | ⭐️☆☆☆ | 1.4 | 3.0 | [Ray](https://github.com/raysan5) | -| 86 | [text_font_sdf](text/text_font_sdf.c) | text_font_sdf | ⭐️⭐️⭐️☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | -| 87 | [text_format_text](text/text_format_text.c) | text_format_text | ⭐️☆☆☆ | 1.1 | 3.0 | [Ray](https://github.com/raysan5) | -| 88 | [text_input_box](text/text_input_box.c) | text_input_box | ⭐️⭐️☆☆ | 1.7 | 3.5 | [Ray](https://github.com/raysan5) | -| 89 | [text_writing_anim](text/text_writing_anim.c) | text_writing_anim | ⭐️⭐️☆☆ | 1.4 | 1.4 | [Ray](https://github.com/raysan5) | -| 90 | [text_rectangle_bounds](text/text_rectangle_bounds.c) | text_rectangle_bounds | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) | -| 91 | [text_unicode](text/text_unicode.c) | text_unicode | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) | -| 92 | [text_draw_3d](text/text_draw_3d.c) | text_draw_3d | ⭐️⭐️⭐️⭐️ | 3.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) | -| 93 | [text_codepoints_loading](text/text_codepoints_loading.c) | text_codepoints_loading | ⭐️⭐️⭐️☆ | 4.2 | 4.2 | [Ray](https://github.com/raysan5) | +| example | image | difficulty
level | version
created | last version
updated | original
developer | +|-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| +| [text_raylib_fonts](text/text_raylib_fonts.c) | text_raylib_fonts | ⭐☆☆☆ | 1.7 | 3.7 | [Ramon Santamaria](https://github.com/raysan5) | +| [text_font_spritefont](text/text_font_spritefont.c) | text_font_spritefont | ⭐☆☆☆ | 1.0 | 1.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [text_font_filters](text/text_font_filters.c) | text_font_filters | ⭐⭐☆☆ | 1.3 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) | +| [text_font_loading](text/text_font_loading.c) | text_font_loading | ⭐☆☆☆ | 1.4 | 3.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [text_font_sdf](text/text_font_sdf.c) | text_font_sdf | ⭐⭐⭐☆ | 1.3 | 4.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [text_format_text](text/text_format_text.c) | text_format_text | ⭐☆☆☆ | 1.1 | 3.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [text_input_box](text/text_input_box.c) | text_input_box | ⭐⭐☆☆ | 1.7 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [text_writing_anim](text/text_writing_anim.c) | text_writing_anim | ⭐⭐☆☆ | 1.4 | 1.4 | [Ramon Santamaria](https://github.com/raysan5) | +| [text_rectangle_bounds](text/text_rectangle_bounds.c) | text_rectangle_bounds | ⭐⭐⭐⭐️ | 2.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) | +| [text_unicode](text/text_unicode.c) | text_unicode | ⭐⭐⭐⭐️ | 2.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) | +| [text_draw_3d](text/text_draw_3d.c) | text_draw_3d | ⭐⭐⭐⭐️ | 3.5 | 4.0 | [Vlad Adrian](https://github.com/demizdor) | +| [text_codepoints_loading](text/text_codepoints_loading.c) | text_codepoints_loading | ⭐⭐⭐☆ | 4.2 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) | -### category: models +### category: models [23] Examples using raylib models functionality, including models loading/generation and drawing, provided by raylib [models](../src/rmodels.c) module. -| ## | example | image | difficulty
level | version
created | last version
updated | original
developer | -|----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 94 | [models_animation](models/models_animation.c) | models_animation | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Culacant](https://github.com/culacant) | -| 95 | [models_billboard](models/models_billboard.c) | models_billboard | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | -| 96 | [models_box_collisions](models/models_box_collisions.c) | models_box_collisions | ⭐️☆☆☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | -| 97 | [models_cubicmap](models/models_cubicmap.c) | models_cubicmap | ⭐️⭐️☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) | -| 98 | [models_first_person_maze](models/models_first_person_maze.c) | models_first_person_maze | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 99 | [models_geometric_shapes](models/models_geometric_shapes.c) | models_geometric_shapes | ⭐️☆☆☆ | 1.0 | 3.5 | [Ray](https://github.com/raysan5) | -| 100 | [models_mesh_generation](models/models_mesh_generation.c) | models_mesh_generation | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) | -| 101 | [models_mesh_picking](models/models_mesh_picking.c) | models_mesh_picking | ⭐️⭐️⭐️☆ | 1.7 | 4.0 | [Joel Davis](https://github.com/joeld42) | -| 102 | [models_loading](models/models_loading.c) | models_loading | ⭐️☆☆☆ | 2.0 | 4.2 | [Ray](https://github.com/raysan5) | -| 103 | [models_loading_gltf](models/models_loading_gltf.c) | models_loading_gltf | ⭐️☆☆☆ | 3.7 | 4.2 | [Ray](https://github.com/raysan5) | -| 104 | [models_loading_vox](models/models_loading_vox.c) | models_loading_vox | ⭐️☆☆☆ | 4.0 | 4.0 | [Johann Nadalutti](https://github.com/procfxgen) | -| 105 | [models_loading_m3d](models/models_loading_m3d.c) | models_loading_m3d | ⭐️⭐️☆☆ | 4.5 | 4.5 | [bzt](https://bztsrc.gitlab.io/model3d) | -| 106 | [models_orthographic_projection](models/models_orthographic_projection.c) | models_orthographic_projection | ⭐️☆☆☆ | 2.0 | 3.7 | [Max Danielsson](https://github.com/autious) | -| 107 | [models_point_rendering](models/models_point_rendering.c) | models_point_rendering | ⭐️⭐️⭐️☆ | 5.0 | 5.0 | [Reese Gallagher](https://github.com/satchelfrost) | -| 108 | [models_rlgl_solar_system](models/models_rlgl_solar_system.c) | models_rlgl_solar_system | ⭐️⭐️⭐️⭐️ | 2.5 | 4.0 | [Ray](https://github.com/raysan5) | -| 109 | [models_yaw_pitch_roll](models/models_yaw_pitch_roll.c) | models_yaw_pitch_roll | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Berni](https://github.com/Berni8k) | -| 110 | [models_waving_cubes](models/models_waving_cubes.c) | models_waving_cubes | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Codecat](https://github.com/codecat) | -| 111 | [models_heightmap](models/models_heightmap.c) | models_heightmap | ⭐️☆☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) | -| 112 | [models_skybox](models/models_skybox.c) | models_skybox | ⭐️⭐️☆☆ | 1.8 | 4.0 | [Ray](https://github.com/raysan5) | -| 113 | [models_draw_cube_texture](models/models_draw_cube_texture.c) | models_draw_cube_texture | ⭐️⭐️☆☆ | 4.5 | 4.5 | [Ray](https://github.com/raysan5) | -| 114 | [models_gpu_skinning](models/models_gpu_skinning.c) | models_gpu_skinning | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Daniel Holden](https://github.com/orangeduck) | -| 115 | [models_bone_socket](models/models_bone_socket.c) | models_bone_socket | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [iP](https://github.com/ipzaur) | -| 116 | [models_tesseract_view](models/models_tesseract_view.c) | models_tesseract_view | ⭐️⭐️☆☆ | 5.6-dev | 5.6-dev | [Timothy van der Valk](https://github.com/arceryz) | +| example | image | difficulty
level | version
created | last version
updated | original
developer | +|-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| +| [models_animation](models/models_animation.c) | models_animation | ⭐⭐☆☆ | 2.5 | 3.5 | [Culacant](https://github.com/culacant) | +| [models_billboard](models/models_billboard.c) | models_billboard | ⭐⭐⭐☆ | 1.3 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [models_box_collisions](models/models_box_collisions.c) | models_box_collisions | ⭐☆☆☆ | 1.3 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [models_cubicmap](models/models_cubicmap.c) | models_cubicmap | ⭐⭐☆☆ | 1.8 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [models_first_person_maze](models/models_first_person_maze.c) | models_first_person_maze | ⭐⭐☆☆ | 2.5 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [models_geometric_shapes](models/models_geometric_shapes.c) | models_geometric_shapes | ⭐☆☆☆ | 1.0 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [models_mesh_generation](models/models_mesh_generation.c) | models_mesh_generation | ⭐⭐☆☆ | 1.8 | 4.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [models_mesh_picking](models/models_mesh_picking.c) | models_mesh_picking | ⭐⭐⭐☆ | 1.7 | 4.0 | [Joel Davis](https://github.com/joeld42) | +| [models_loading](models/models_loading.c) | models_loading | ⭐☆☆☆ | 2.0 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) | +| [models_loading_gltf](models/models_loading_gltf.c) | models_loading_gltf | ⭐☆☆☆ | 3.7 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) | +| [models_loading_vox](models/models_loading_vox.c) | models_loading_vox | ⭐☆☆☆ | 4.0 | 4.0 | [Johann Nadalutti](https://github.com/procfxgen) | +| [models_loading_m3d](models/models_loading_m3d.c) | models_loading_m3d | ⭐⭐☆☆ | 4.5 | 4.5 | [bzt](https://github.com/model3d) | +| [models_orthographic_projection](models/models_orthographic_projection.c) | models_orthographic_projection | ⭐☆☆☆ | 2.0 | 3.7 | [Max Danielsson](https://github.com/autious) | +| [models_point_rendering](models/models_point_rendering.c) | models_point_rendering | ⭐⭐⭐☆ | 5.0 | 5.0 | [Reese Gallagher](https://github.com/satchelfrost) | +| [models_rlgl_solar_system](models/models_rlgl_solar_system.c) | models_rlgl_solar_system | ⭐⭐⭐⭐️ | 2.5 | 4.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [models_yaw_pitch_roll](models/models_yaw_pitch_roll.c) | models_yaw_pitch_roll | ⭐⭐☆☆ | 1.8 | 4.0 | [Berni](https://github.com/Berni8k) | +| [models_waving_cubes](models/models_waving_cubes.c) | models_waving_cubes | ⭐⭐⭐☆ | 2.5 | 3.7 | [Codecat](https://github.com/codecat) | +| [models_heightmap](models/models_heightmap.c) | models_heightmap | ⭐☆☆☆ | 1.8 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [models_skybox](models/models_skybox.c) | models_skybox | ⭐⭐☆☆ | 1.8 | 4.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [models_draw_cube_texture](models/models_draw_cube_texture.c) | models_draw_cube_texture | ⭐⭐☆☆ | 4.5 | 4.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [models_gpu_skinning](models/models_gpu_skinning.c) | models_gpu_skinning | ⭐⭐⭐☆ | 4.5 | 4.5 | [Daniel Holden](https://github.com/orangeduck) | +| [models_bone_socket](models/models_bone_socket.c) | models_bone_socket | ⭐⭐⭐⭐️ | 4.5 | 4.5 | [iP](https://github.com/ipzaur) | +| [models_tesseract_view](models/models_tesseract_view.c) | models_tesseract_view | ⭐⭐☆☆ | 5.6 | 5.6 | [Timothy van der Valk](https://github.com/arceryz) | -### category: shaders +### category: shaders [28] Examples using raylib shaders functionality, including shaders loading, parameters configuration and drawing using them (model shaders and postprocessing shaders). This functionality is directly provided by raylib [rlgl](../src/rlgl.c) module. -| ## | example | image | difficulty
level | version
created | last version
updated | original
developer | -|----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 117 | [shaders_basic_lighting](shaders/shaders_basic_lighting.c) | shaders_basic_lighting | ⭐️⭐️⭐️⭐️ | 3.0 | 4.2 | [Chris Camacho](https://github.com/chriscamacho) | -| 118 | [shaders_model_shader](shaders/shaders_model_shader.c) | shaders_model_shader | ⭐️⭐️☆☆ | 1.3 | 3.7 | [Ray](https://github.com/raysan5) | -| 119 | [shaders_shapes_textures](shaders/shaders_shapes_textures.c) | shaders_shapes_textures | ⭐️⭐️☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) | -| 120 | [shaders_custom_uniform](shaders/shaders_custom_uniform.c) | shaders_custom_uniform | ⭐️⭐️☆☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | -| 121 | [shaders_postprocessing](shaders/shaders_postprocessing.c) | shaders_postprocessing | ⭐️⭐️⭐️☆ | 1.3 | 4.0 | [Ray](https://github.com/raysan5) | -| 122 | [shaders_palette_switch](shaders/shaders_palette_switch.c) | shaders_palette_switch | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Marco Lizza](https://github.com/MarcoLizza) | -| 123 | [shaders_raymarching](shaders/shaders_raymarching.c) | shaders_raymarching | ⭐️⭐️⭐️⭐️ | 2.0 | 4.2 | [Ray](https://github.com/raysan5) | -| 124 | [shaders_texture_drawing](shaders/shaders_texture_drawing.c) | shaders_texture_drawing | ⭐️⭐️☆☆ | 2.0 | 3.7 | [Michał Ciesielski](https://github.com/ciessielski) | -| 125 | [shaders_texture_outline](shaders/shaders_texture_outline.c) | shaders_texture_outline | ⭐️⭐️⭐️☆ | 4.0 | 4.0 | [Samuel Skiff](https://github.com/GoldenThumbs) | -| 126 | [shaders_texture_waves](shaders/shaders_texture_waves.c) | shaders_texture_waves | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Anata](https://github.com/anatagawa) | -| 127 | [shaders_julia_set](shaders/shaders_julia_set.c) | shaders_julia_set | ⭐️⭐️⭐️☆ | 2.5 | 4.0 | [Josh Colclough](https://github.com/joshcol9232) | -| 128 | [shaders_eratosthenes](shaders/shaders_eratosthenes.c) | shaders_eratosthenes | ⭐️⭐️⭐️☆ | 2.5 | 4.0 | [ProfJski](https://github.com/ProfJski) | -| 129 | [shaders_fog](shaders/shaders_fog.c) | shaders_fog | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | -| 130 | [shaders_simple_mask](shaders/shaders_simple_mask.c) | shaders_simple_mask | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | -| 131 | [shaders_hot_reloading](shaders/shaders_hot_reloading.c) | shaders_hot_reloading | ⭐️⭐️⭐️☆ | 3.0 | 3.5 | [Ray](https://github.com/raysan5) | -| 132 | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | shaders_mesh_instancing | ⭐️⭐️⭐️⭐️ | 3.7 | 4.2 | [seanpringle](https://github.com/seanpringle) | -| 133 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | shaders_multi_sample2d | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 134 | [shaders_spotlight](shaders/shaders_spotlight.c) | shaders_spotlight | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | -| 135 | [shaders_deferred_render](shaders/shaders_deferred_render.c) | shaders_deferred_render | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [Justin Andreas Lacoste](https://github.com/27justin) | -| 136 | [shaders_hybrid_render](shaders/shaders_hybrid_render.c) | shaders_hybrid_render | ⭐️⭐️⭐️⭐️ | 4.2 | 4.2 | [Buğra Alptekin Sarı](https://github.com/BugraAlptekinSari) | -| 137 | [shaders_texture_tiling](shaders/shaders_texture_tiling.c) | shaders_texture_tiling | ⭐️⭐️☆☆ | 4.5 | 4.5 | [Luis Almeida](https://github.com/luis605) | -| 138 | [shaders_shadowmap](shaders/shaders_shadowmap.c) | shaders_shadowmap | ⭐️⭐️⭐️⭐️ | 5.0 | 5.0 | [TheManTheMythTheGameDev](https://github.com/TheManTheMythTheGameDev) | -| 139 | [shaders_vertex_displacement](shaders/shaders_vertex_displacement.c) | shaders_vertex_displacement | ⭐️⭐️⭐️☆ | 5.0 | 4.5 | [Alex ZH](https://github.com/ZzzhHe) | -| 140 | [shaders_write_depth](shaders/shaders_write_depth.c) | shaders_write_depth | ⭐️⭐️☆☆ | 4.2 | 4.2 | [Buğra Alptekin Sarı](https://github.com/BugraAlptekinSari) | -| 141 | [shaders_basic_pbr](shaders/shaders_basic_pbr.c) | shaders_basic_pbr | ⭐️⭐️⭐️⭐️ | 5.0 | 5.1-dev | [Afan OLOVCIC](https://github.com/_DevDad) | -| 142 | [shaders_lightmap](shaders/shaders_lightmap.c) | shaders_lightmap | ⭐️⭐️⭐️☆ | 4.5 | 4.5 | [Jussi Viitala](https://github.com/nullstare) | -| 143 | [shaders_rounded_rectangle](shaders/shaders_rounded_rectangle.c) | shaders_rounded_rectangle | ⭐️⭐️⭐️☆ | 5.5 | 5.5 | [Anstro Pleuton](https://github.com/anstropleuton) | -| 144 | [shaders_view_depth](shaders/shaders_view_depth.c) | shaders_view_depth | ⭐️⭐️⭐️☆ | 5.6-dev | 5.6-dev | [Luís Almeida](https://github.com/luis605) | +| example | image | difficulty
level | version
created | last version
updated | original
developer | +|-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| +| [shaders_basic_lighting](shaders/shaders_basic_lighting.c) | shaders_basic_lighting | ⭐⭐⭐⭐️ | 3.0 | 4.2 | [Chris Camacho](https://github.com/chriscamacho) | +| [shaders_model_shader](shaders/shaders_model_shader.c) | shaders_model_shader | ⭐⭐☆☆ | 1.3 | 3.7 | [Ramon Santamaria](https://github.com/raysan5) | +| [shaders_shapes_textures](shaders/shaders_shapes_textures.c) | shaders_shapes_textures | ⭐⭐☆☆ | 1.7 | 3.7 | [Ramon Santamaria](https://github.com/raysan5) | +| [shaders_custom_uniform](shaders/shaders_custom_uniform.c) | shaders_custom_uniform | ⭐⭐☆☆ | 1.3 | 4.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [shaders_postprocessing](shaders/shaders_postprocessing.c) | shaders_postprocessing | ⭐⭐⭐☆ | 1.3 | 4.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [shaders_palette_switch](shaders/shaders_palette_switch.c) | shaders_palette_switch | ⭐⭐⭐☆ | 2.5 | 3.7 | [Marco Lizza](https://github.com/MarcoLizza) | +| [shaders_raymarching](shaders/shaders_raymarching.c) | shaders_raymarching | ⭐⭐⭐⭐️ | 2.0 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) | +| [shaders_texture_drawing](shaders/shaders_texture_drawing.c) | shaders_texture_drawing | ⭐⭐☆☆ | 2.0 | 3.7 | [Michał Ciesielski](https://github.com/ciessielski) | +| [shaders_texture_outline](shaders/shaders_texture_outline.c) | shaders_texture_outline | ⭐⭐⭐☆ | 4.0 | 4.0 | [Samuel Skiff](https://github.com/GoldenThumbs) | +| [shaders_texture_waves](shaders/shaders_texture_waves.c) | shaders_texture_waves | ⭐⭐☆☆ | 2.5 | 3.7 | [Anata](https://github.com/anatagawa) | +| [shaders_julia_set](shaders/shaders_julia_set.c) | shaders_julia_set | ⭐⭐⭐☆ | 2.5 | 4.0 | [Josh Colclough](https://github.com/joshcol9232) | +| [shaders_eratosthenes](shaders/shaders_eratosthenes.c) | shaders_eratosthenes | ⭐⭐⭐☆ | 2.5 | 4.0 | [ProfJski](https://github.com/ProfJski) | +| [shaders_fog](shaders/shaders_fog.c) | shaders_fog | ⭐⭐⭐☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | +| [shaders_simple_mask](shaders/shaders_simple_mask.c) | shaders_simple_mask | ⭐⭐☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | +| [shaders_hot_reloading](shaders/shaders_hot_reloading.c) | shaders_hot_reloading | ⭐⭐⭐☆ | 3.0 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | shaders_mesh_instancing | ⭐⭐⭐⭐️ | 3.7 | 4.2 | [seanpringle](https://github.com/seanpringle) | +| [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | shaders_multi_sample2d | ⭐⭐☆☆ | 3.5 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [shaders_spotlight](shaders/shaders_spotlight.c) | shaders_spotlight | ⭐⭐☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | +| [shaders_deferred_render](shaders/shaders_deferred_render.c) | shaders_deferred_render | ⭐⭐⭐⭐️ | 4.5 | 4.5 | [Justin Andreas Lacoste](https://github.com/27justin) | +| [shaders_hybrid_render](shaders/shaders_hybrid_render.c) | shaders_hybrid_render | ⭐⭐⭐⭐️ | 4.2 | 4.2 | [Buğra Alptekin Sarı](https://github.com/BugraAlptekinSari) | +| [shaders_texture_tiling](shaders/shaders_texture_tiling.c) | shaders_texture_tiling | ⭐⭐☆☆ | 4.5 | 4.5 | [Luis Almeida](https://github.com/luis605) | +| [shaders_shadowmap](shaders/shaders_shadowmap.c) | shaders_shadowmap | ⭐⭐⭐⭐️ | 5.0 | 5.0 | [TheManTheMythTheGameDev](https://github.com/TheManTheMythTheGameDev) | +| [shaders_vertex_displacement](shaders/shaders_vertex_displacement.c) | shaders_vertex_displacement | ⭐⭐⭐☆ | 5.0 | 4.5 | [Alex ZH](https://github.com/ZzzhHe) | +| [shaders_write_depth](shaders/shaders_write_depth.c) | shaders_write_depth | ⭐⭐☆☆ | 4.2 | 4.2 | [Buğra Alptekin Sarı](https://github.com/BugraAlptekinSari) | +| [shaders_basic_pbr](shaders/shaders_basic_pbr.c) | shaders_basic_pbr | ⭐⭐⭐⭐️ | 5.0 | 5.1 | [Afan OLOVCIC](https://github.com/_DevDad) | +| [shaders_lightmap](shaders/shaders_lightmap.c) | shaders_lightmap | ⭐⭐⭐☆ | 4.5 | 4.5 | [Jussi Viitala](https://github.com/nullstare) | +| [shaders_rounded_rectangle](shaders/shaders_rounded_rectangle.c) | shaders_rounded_rectangle | ⭐⭐⭐☆ | 5.5 | 5.5 | [Anstro Pleuton](https://github.com/anstropleuton) | +| [shaders_view_depth](shaders/shaders_view_depth.c) | shaders_view_depth | ⭐⭐⭐☆ | 5.6 | 5.6 | [Luís Almeida](https://github.com/luis605) | -### category: audio +### category: audio [8] Examples using raylib audio functionality, including sound/music loading and playing. This functionality is provided by raylib [raudio](../src/raudio.c) module. Note this module can be used standalone independently of raylib. -| ## | example | image | difficulty
level | version
created | last version
updated | original
developer | -|----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 145 | [audio_module_playing](audio/audio_module_playing.c) | audio_module_playing | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 146 | [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐️☆☆☆ | 1.3 | 4.2 | [Ray](https://github.com/raysan5) | -| 147 | [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐️⭐️⭐️☆ | 1.6 | 4.2 | [Ray](https://github.com/raysan5) | -| 148 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | -| 149 | [audio_mixed_processor](audio/audio_mixed_processor.c) | audio_mixed_processor | ⭐️⭐️⭐️⭐️ | 4.2 | 4.2 | [hkc](https://github.com/hatkidchan) | -| 150 | [audio_stream_effects](audio/audio_stream_effects.c) | audio_stream_effects | ⭐️⭐️⭐️⭐️ | 4.2 | 5.0 | [Ray](https://github.com/raysan5) | -| 151 | [audio_sound_multi](audio/audio_sound_multi.c) | audio_sound_multi | ⭐️⭐️☆☆ | 4.6 | 4.6 | [Jeffery Myers](https://github.com/JeffM2501) | -| 152 | [audio_sound_positioning](audio/audio_sound_positioning.c) | audio_sound_positioning | ⭐️⭐️☆☆ | 5.5 | 5.5 | [Le Juez Victor](https://github.com/Bigfoot71) | +| example | image | difficulty
level | version
created | last version
updated | original
developer | +|-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| +| [audio_module_playing](audio/audio_module_playing.c) | audio_module_playing | ⭐☆☆☆ | 1.5 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐☆☆☆ | 1.3 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) | +| [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐⭐⭐☆ | 1.6 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) | +| [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐☆☆☆ | 1.1 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [audio_mixed_processor](audio/audio_mixed_processor.c) | audio_mixed_processor | ⭐⭐⭐⭐️ | 4.2 | 4.2 | [hkc](https://github.com/hatkidchan) | +| [audio_stream_effects](audio/audio_stream_effects.c) | audio_stream_effects | ⭐⭐⭐⭐️ | 4.2 | 5.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [audio_sound_multi](audio/audio_sound_multi.c) | audio_sound_multi | ⭐⭐☆☆ | 4.6 | 4.6 | [Jeffery Myers](https://github.com/JeffM2501) | +| [audio_sound_positioning](audio/audio_sound_positioning.c) | audio_sound_positioning | ⭐⭐☆☆ | 5.5 | 5.5 | [Le Juez Victor](https://github.com/Bigfoot71) | -### category: others +### category: others [6] Examples showing raylib misc functionality that does not fit in other categories, like standalone modules usage or examples integrating external libraries. -| ## | example | image | difficulty
level | version
created | last version
updated | original
developer | -|----|----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| 153 | [rlgl_standalone](others/rlgl_standalone.c) | rlgl_standalone | ⭐️⭐️⭐️⭐️ | 1.6 | 4.0 | [Ray](https://github.com/raysan5) | -| 154 | [rlgl_compute_shader](others/rlgl_compute_shader.c) | rlgl_compute_shader | ⭐️⭐️⭐️⭐️ | 4.0 | 4.0 | [Teddy Astie](https://github.com/tsnake41) | -| 155 | [easings_testbed](others/easings_testbed.c) | easings_testbed | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) | -| 156 | [raylib_opengl_interop](others/raylib_opengl_interop.c) | raylib_opengl_interop | ⭐️⭐️⭐️⭐️ | 3.8 | 4.0 | [Stephan Soller](https://github.com/arkanis) | -| 157 | [embedded_files_loading](others/embedded_files_loading.c) | embedded_files_loading | ⭐️⭐️☆☆ | 3.0 | 3.5 | [Kristian Holmgren](https://github.com/defutura) | -| 158 | [raymath_vector_angle](others/raymath_vector_angle.c) | raymath_vector_angle | ⭐️⭐️☆☆ | 1.0 | 4.6 | [Ray](https://github.com/raysan5) | +| example | image | difficulty
level | version
created | last version
updated | original
developer | +|-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| +| [rlgl_standalone](others/rlgl_standalone.c) | rlgl_standalone | ⭐⭐⭐⭐️ | 1.6 | 4.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [rlgl_compute_shader](others/rlgl_compute_shader.c) | rlgl_compute_shader | ⭐⭐⭐⭐️ | 4.0 | 4.0 | [Teddy Astie](https://github.com/tsnake41) | +| [easings_testbed](others/easings_testbed.c) | easings_testbed | ⭐⭐⭐☆ | 2.5 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) | +| [raylib_opengl_interop](others/raylib_opengl_interop.c) | raylib_opengl_interop | ⭐⭐⭐⭐️ | 3.8 | 4.0 | [Stephan Soller](https://github.com/arkanis) | +| [embedded_files_loading](others/embedded_files_loading.c) | embedded_files_loading | ⭐⭐☆☆ | 3.0 | 3.5 | [Kristian Holmgren](https://github.com/defutura) | +| [raymath_vector_angle](others/raymath_vector_angle.c) | raymath_vector_angle | ⭐⭐☆☆ | 1.0 | 4.6 | [Ramon Santamaria](https://github.com/raysan5) | -As always contributions are welcome, feel free to send new examples! Here is an [examples template](examples_template.c) to start with! +Some example missing? As always, contributions are welcome, feel free to send new examples! +Here is an[examples template](examples_template.c) with instructions to start with! From c3a4217148fb2eef0af42302e476e9ef80da09bc Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 6 Aug 2025 18:25:58 +0200 Subject: [PATCH 199/242] Update rexm.c --- tools/rexm/rexm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 2e78d23d0..a7facdaf9 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -1172,7 +1172,7 @@ static int UpdateRequiredFiles(void) rlExampleInfo *exCollectionFull = LoadExamplesData(exCollectionFilePath, "ALL", false, &exCollectionFullCount); UnloadExamplesData(exCollectionFull); - mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("## EXAMPLES COLLECTION [TOTAL: %i]\n\n", exCollectionFullCount)); + mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("## EXAMPLES COLLECTION [TOTAL: %i]\n", exCollectionFullCount)); // NOTE: We keep a global examples counter for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES; i++) From b3513e4719e874c8df661b7dcda64cc2ea246b1b Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 6 Aug 2025 18:42:27 +0200 Subject: [PATCH 200/242] ADDED: Missing example VS2022 projects --- .../examples/audio_sound_positioning.vcxproj | 569 ++++++++++++++++++ .../examples/core_basic_window_web.vcxproj | 569 ++++++++++++++++++ .../examples/core_input_gestures_web.vcxproj | 569 ++++++++++++++++++ .../examples/models_point_rendering.vcxproj | 569 ++++++++++++++++++ .../examples/models_tesseract_view.vcxproj | 569 ++++++++++++++++++ .../examples/raylib_opengl_interop.vcxproj | 569 ++++++++++++++++++ .../examples/raymath_vector_angle.vcxproj | 569 ++++++++++++++++++ .../examples/rlgl_compute_shader.vcxproj | 569 ++++++++++++++++++ .../VS2022/examples/shaders_basic_pbr.vcxproj | 569 ++++++++++++++++++ .../VS2022/examples/shaders_lightmap.vcxproj | 569 ++++++++++++++++++ .../examples/shapes_digital_clock.vcxproj | 569 ++++++++++++++++++ .../examples/shapes_double_pendulum.vcxproj | 569 ++++++++++++++++++ .../examples/textures_image_channel.vcxproj | 569 ++++++++++++++++++ .../examples/textures_image_kernel.vcxproj | 569 ++++++++++++++++++ .../examples/textures_image_rotate.vcxproj | 569 ++++++++++++++++++ 15 files changed, 8535 insertions(+) create mode 100644 projects/VS2022/examples/audio_sound_positioning.vcxproj create mode 100644 projects/VS2022/examples/core_basic_window_web.vcxproj create mode 100644 projects/VS2022/examples/core_input_gestures_web.vcxproj create mode 100644 projects/VS2022/examples/models_point_rendering.vcxproj create mode 100644 projects/VS2022/examples/models_tesseract_view.vcxproj create mode 100644 projects/VS2022/examples/raylib_opengl_interop.vcxproj create mode 100644 projects/VS2022/examples/raymath_vector_angle.vcxproj create mode 100644 projects/VS2022/examples/rlgl_compute_shader.vcxproj create mode 100644 projects/VS2022/examples/shaders_basic_pbr.vcxproj create mode 100644 projects/VS2022/examples/shaders_lightmap.vcxproj create mode 100644 projects/VS2022/examples/shapes_digital_clock.vcxproj create mode 100644 projects/VS2022/examples/shapes_double_pendulum.vcxproj create mode 100644 projects/VS2022/examples/textures_image_channel.vcxproj create mode 100644 projects/VS2022/examples/textures_image_kernel.vcxproj create mode 100644 projects/VS2022/examples/textures_image_rotate.vcxproj diff --git a/projects/VS2022/examples/audio_sound_positioning.vcxproj b/projects/VS2022/examples/audio_sound_positioning.vcxproj new file mode 100644 index 000000000..96c8fef5e --- /dev/null +++ b/projects/VS2022/examples/audio_sound_positioning.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + Win32Proj + audio_sound_positioning + 10.0 + audio_sound_positioning + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/examples/core_basic_window_web.vcxproj b/projects/VS2022/examples/core_basic_window_web.vcxproj new file mode 100644 index 000000000..7d9d0efb4 --- /dev/null +++ b/projects/VS2022/examples/core_basic_window_web.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + Win32Proj + core_basic_window_web + 10.0 + core_basic_window_web + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/examples/core_input_gestures_web.vcxproj b/projects/VS2022/examples/core_input_gestures_web.vcxproj new file mode 100644 index 000000000..bcccc57d0 --- /dev/null +++ b/projects/VS2022/examples/core_input_gestures_web.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + Win32Proj + core_input_gestures_web + 10.0 + core_input_gestures_web + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/examples/models_point_rendering.vcxproj b/projects/VS2022/examples/models_point_rendering.vcxproj new file mode 100644 index 000000000..ba66e9baa --- /dev/null +++ b/projects/VS2022/examples/models_point_rendering.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + Win32Proj + models_point_rendering + 10.0 + models_point_rendering + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/examples/models_tesseract_view.vcxproj b/projects/VS2022/examples/models_tesseract_view.vcxproj new file mode 100644 index 000000000..b49094ac7 --- /dev/null +++ b/projects/VS2022/examples/models_tesseract_view.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + Win32Proj + models_tesseract_view + 10.0 + models_tesseract_view + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/examples/raylib_opengl_interop.vcxproj b/projects/VS2022/examples/raylib_opengl_interop.vcxproj new file mode 100644 index 000000000..a1b05153a --- /dev/null +++ b/projects/VS2022/examples/raylib_opengl_interop.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + Win32Proj + raylib_opengl_interop + 10.0 + raylib_opengl_interop + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/examples/raymath_vector_angle.vcxproj b/projects/VS2022/examples/raymath_vector_angle.vcxproj new file mode 100644 index 000000000..fd650d93c --- /dev/null +++ b/projects/VS2022/examples/raymath_vector_angle.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + Win32Proj + raymath_vector_angle + 10.0 + raymath_vector_angle + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/examples/rlgl_compute_shader.vcxproj b/projects/VS2022/examples/rlgl_compute_shader.vcxproj new file mode 100644 index 000000000..84e267623 --- /dev/null +++ b/projects/VS2022/examples/rlgl_compute_shader.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + Win32Proj + rlgl_compute_shader + 10.0 + rlgl_compute_shader + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\others + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/examples/shaders_basic_pbr.vcxproj b/projects/VS2022/examples/shaders_basic_pbr.vcxproj new file mode 100644 index 000000000..49744ed0e --- /dev/null +++ b/projects/VS2022/examples/shaders_basic_pbr.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + Win32Proj + shaders_basic_pbr + 10.0 + shaders_basic_pbr + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/examples/shaders_lightmap.vcxproj b/projects/VS2022/examples/shaders_lightmap.vcxproj new file mode 100644 index 000000000..392f35b71 --- /dev/null +++ b/projects/VS2022/examples/shaders_lightmap.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + Win32Proj + shaders_lightmap + 10.0 + shaders_lightmap + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/examples/shapes_digital_clock.vcxproj b/projects/VS2022/examples/shapes_digital_clock.vcxproj new file mode 100644 index 000000000..d14448393 --- /dev/null +++ b/projects/VS2022/examples/shapes_digital_clock.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + Win32Proj + shapes_digital_clock + 10.0 + shapes_digital_clock + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/examples/shapes_double_pendulum.vcxproj b/projects/VS2022/examples/shapes_double_pendulum.vcxproj new file mode 100644 index 000000000..57f7d458b --- /dev/null +++ b/projects/VS2022/examples/shapes_double_pendulum.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + Win32Proj + shapes_double_pendulum + 10.0 + shapes_double_pendulum + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/examples/textures_image_channel.vcxproj b/projects/VS2022/examples/textures_image_channel.vcxproj new file mode 100644 index 000000000..c480f869d --- /dev/null +++ b/projects/VS2022/examples/textures_image_channel.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + Win32Proj + textures_image_channel + 10.0 + textures_image_channel + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/examples/textures_image_kernel.vcxproj b/projects/VS2022/examples/textures_image_kernel.vcxproj new file mode 100644 index 000000000..20cb60d6a --- /dev/null +++ b/projects/VS2022/examples/textures_image_kernel.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + Win32Proj + textures_image_kernel + 10.0 + textures_image_kernel + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/examples/textures_image_rotate.vcxproj b/projects/VS2022/examples/textures_image_rotate.vcxproj new file mode 100644 index 000000000..98179299a --- /dev/null +++ b/projects/VS2022/examples/textures_image_rotate.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + Win32Proj + textures_image_rotate + 10.0 + textures_image_rotate + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file From a6f29e4f1901b54406dfdaabdff979ff31c14c57 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 6 Aug 2025 18:42:33 +0200 Subject: [PATCH 201/242] Update examples_report.md --- tools/rexm/examples_report.md | 52 +++++++++++++++++------------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/tools/rexm/examples_report.md b/tools/rexm/examples_report.md index 0188c4a98..97bc7c52a 100644 --- a/tools/rexm/examples_report.md +++ b/tools/rexm/examples_report.md @@ -1,4 +1,4 @@ -# EXAMPLES COLLECTION REPORT +# EXAMPLES COLLECTION - VALIDATION REPORT ``` Example elements validated: @@ -26,7 +26,7 @@ Example elements validated: | core_input_gamepad | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_input_multitouch | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_input_gestures | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| core_input_virtual_controls | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | +| core_input_virtual_controls | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_2d_camera | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_2d_camera_mouse_zoom | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_2d_camera_platformer | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -50,11 +50,11 @@ Example elements validated: | core_basic_screen_manager | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_custom_frame_control | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_smooth_pixelperfect | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| core_random_sequence | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | -| core_basic_window_web | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ❌ | ❌ | -| core_input_gestures_web | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ❌ | ✔ | +| core_random_sequence | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_basic_window_web | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ❌ | +| core_input_gestures_web | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | | core_automation_events | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| core_high_dpi | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | +| core_high_dpi | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | | shapes_basic_shapes | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_bouncing_ball | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_colors_palette | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -71,10 +71,10 @@ Example elements validated: | shapes_draw_circle_sector | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_draw_rectangle_rounded | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_top_down_lights | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| shapes_rectangle_advanced | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | +| shapes_rectangle_advanced | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_splines_drawing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| shapes_digital_clock | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ❌ | ❌ | -| shapes_double_pendulum | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ❌ | ❌ | +| shapes_digital_clock | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ❌ | +| shapes_double_pendulum | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ❌ | | textures_logo_raylib | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | textures_srcrec_dstrec | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | textures_image_drawing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -97,9 +97,9 @@ Example elements validated: | textures_polygon | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | textures_fog_of_war | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | textures_gif_player | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| textures_image_kernel | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ❌ | ✔ | -| textures_image_channel | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | -| textures_image_rotate | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ❌ | ✔ | +| textures_image_kernel | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | +| textures_image_channel | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | +| textures_image_rotate | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | | textures_textured_curve | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | text_raylib_fonts | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | text_font_spritefont | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -126,7 +126,7 @@ Example elements validated: | models_loading_vox | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_loading_m3d | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_orthographic_projection | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| models_point_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | +| models_point_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | | models_rlgl_solar_system | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_yaw_pitch_roll | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_waving_cubes | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -135,7 +135,7 @@ Example elements validated: | models_draw_cube_texture | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_gpu_skinning | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_bone_socket | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| models_tesseract_view | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | +| models_tesseract_view | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | | shaders_basic_lighting | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_model_shader | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_shapes_textures | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -157,13 +157,13 @@ Example elements validated: | shaders_deferred_render | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_hybrid_render | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_texture_tiling | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| shaders_shadowmap | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | +| shaders_shadowmap | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_vertex_displacement | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_write_depth | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| shaders_basic_pbr | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | -| shaders_lightmap | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | -| shaders_rounded_rectangle | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | -| shaders_view_depth | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | +| shaders_basic_pbr | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | +| shaders_lightmap | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | +| shaders_rounded_rectangle | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | +| shaders_view_depth | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | | audio_module_playing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_music_stream | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_raw_stream | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -171,10 +171,10 @@ Example elements validated: | audio_mixed_processor | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_stream_effects | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_sound_multi | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| audio_sound_positioning | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ❌ | ❌ | -| rlgl_standalone | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | -| rlgl_compute_shader | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ❌ | ❌ | -| easings_testbed | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | -| raylib_opengl_interop | ✔ | ❌ | ❌ | ✔ | ✔ | ❌ | ✔ | ✔ | ❌ | ❌ | ✔ | ❌ | ❌ | -| embedded_files_loading | ✔ | ❌ | ❌ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | -| raymath_vector_angle | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ❌ | ❌ | +| audio_sound_positioning | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ❌ | +| rlgl_standalone | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ❌ | +| rlgl_compute_shader | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ❌ | +| easings_testbed | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ❌ | +| raylib_opengl_interop | ✔ | ❌ | ❌ | ✔ | ✔ | ❌ | ✔ | ❌ | ✔ | ❌ | ✔ | ❌ | ❌ | +| embedded_files_loading | ✔ | ❌ | ❌ | ✔ | ✔ | ❌ | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ❌ | +| raymath_vector_angle | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ❌ | ✔ | ❌ | ❌ | From 5dc304b5a7e8fbe717a2056661d94e31773e9ce8 Mon Sep 17 00:00:00 2001 From: Connor O'Connor Date: Wed, 6 Aug 2025 22:41:37 -0400 Subject: [PATCH 202/242] Remove binding link for https://github.com/Rabios/raylib-v7 This repo (and user) does not exist. Remove it. --- BINDINGS.md | 1 - 1 file changed, 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 719a902a0..e60638ac1 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -145,7 +145,6 @@ These are older raylib bindings that are more than 2 versions old or have not be | [clj-raylib](https://github.com/lsevero/clj-raylib) | 3.0 | [Clojure](https://clojure.org) | | [QuickJS-raylib](https://github.com/sntg-p/QuickJS-raylib) | 3.0 | [QuickJS](https://bellard.org/quickjs) | | [raylib-duktape](https://github.com/RobLoach/raylib-duktape) | 2.6 | [JavaScript (Duktape)](https://en.wikipedia.org/wiki/JavaScript) | -| [raylib-v7](https://github.com/Rabios/raylib-v7) | 3.5 | [JavaScript (v7)](https://en.wikipedia.org/wiki/JavaScript) | | [raylib-chaiscript](https://github.com/RobLoach/raylib-chaiscript) | 2.6 | [ChaiScript](http://chaiscript.com) | | [raylib-squirrel](https://github.com/RobLoach/raylib-squirrel) | 2.5 | [Squirrel](http://www.squirrel-lang.org) | | [racket-raylib-2d](https://github.com/arvyy/racket-raylib-2d) | 2.5 | [Racket](https://racket-lang.org) | From 1c2ecfd6ab914b8aa077ae347d99c0e7156427a7 Mon Sep 17 00:00:00 2001 From: Connor O'Connor Date: Wed, 6 Aug 2025 23:13:05 -0400 Subject: [PATCH 203/242] fix: buffer overflow in jar_mod.h memcopy --- src/external/jar_mod.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/external/jar_mod.h b/src/external/jar_mod.h index eacd3b7d6..2a220f8af 100644 --- a/src/external/jar_mod.h +++ b/src/external/jar_mod.h @@ -1130,7 +1130,7 @@ static bool jar_mod_load( jar_mod_context_t * modctx, void * mod_data, int mod_d { if( modctx ) { - memcopy(&(modctx->song.title),modmemory,1084); + memcopy(&(modctx->song), modmemory, 1084); i = 0; modctx->number_of_channels = 0; @@ -1593,4 +1593,4 @@ void jar_mod_seek_start(jar_mod_context_t * ctx) //------------------------------------------------------------------------------- -#endif //end of header file \ No newline at end of file +#endif //end of header file From 36dc70443ae9c084c4f83ebe25a0dafe1f1ac319 Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Thu, 7 Aug 2025 12:01:21 +0100 Subject: [PATCH 204/242] Add warning for missing new name to avoid segmentation fault --- tools/rexm/rexm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index a7facdaf9..874859f61 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -275,6 +275,7 @@ int main(int argc, char *argv[]) else if (strcmp(argv[1], "rename") == 0) { if (argc == 2) LOG("WARNING: No filename provided to be renamed\n"); + else if (argc == 3) LOG("WARNING: No new filename provided to be renamed\n"); else if (argc > 4) LOG("WARNING: Too many arguments provided\n"); else { From fa5560881e98cce2b00828caa84650b2b37a5d88 Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Thu, 7 Aug 2025 12:39:12 +0100 Subject: [PATCH 205/242] Check for category on new name --- tools/rexm/rexm.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index a7facdaf9..9eebccaf3 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -282,11 +282,29 @@ int main(int argc, char *argv[]) char *exColInfo = LoadFileText(exCollectionFilePath); if (TextFindIndex(exColInfo, argv[2]) != -1) // Example in the collection { - strcpy(exName, argv[2]); // Register example name - strncpy(exCategory, exName, TextFindIndex(exName, "_")); - strcpy(exRename, argv[3]); - strncpy(exRecategory, exRename, TextFindIndex(exRename, "_")); - opCode = OP_RENAME; + // Security checks for new file name to verify category is included + int newCatIndex = TextFindIndex(argv[3], "_"); + if (newCatIndex > 3) + { + char cat[12] = { 0 }; + strncpy(cat, argv[3], newCatIndex); + bool newCatFound = false; + for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES; i++) + { + if (TextIsEqual(cat, exCategories[i])) { newCatFound = true; break; } + } + + if (newCatFound) + { + strcpy(exName, argv[2]); // Register example name + strncpy(exCategory, exName, TextFindIndex(exName, "_")); + strcpy(exRename, argv[3]); + strncpy(exRecategory, exRename, TextFindIndex(exRename, "_")); + opCode = OP_RENAME; + } + else LOG("WARNING: Example new category is not valid\n"); + } + else LOG("WARNING: Example new name does not include category\n"); } else LOG("WARNING: RENAME: Example not available in the collection\n"); UnloadFileText(exColInfo); From 89e6ed269713ffb262b3a9e379c667f7eb3d8d71 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 17:03:54 +0200 Subject: [PATCH 206/242] REXM: Reviewed VS project adding to solution, no tool to do it! -WIP- --- tools/rexm/rexm.c | 82 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 74 insertions(+), 8 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index a7facdaf9..07606da86 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -165,6 +165,9 @@ static char **ScanExampleResources(const char *filePath, int *resPathCount); // Clear resource paths scanned static void ClearExampleResources(char **resPaths); +// Add VS project (.vcxproj) to existing VS solution (.sol) +static int AddVSProjectToSolution(const char *projFile, const char *solFile); + //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -487,6 +490,7 @@ int main(int argc, char *argv[]) // Create: raylib/projects/VS2022/examples/_example_name.vcxproj //------------------------------------------------------------------------------------------------ + // WARNING: When adding new project a unique UUID should be assigned! FileCopy(TextFormat("%s/../projects/VS2022/examples/core_basic_window.vcxproj", exBasePath), TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName)); FileTextReplace(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName), @@ -495,7 +499,8 @@ int main(int argc, char *argv[]) "..\\..\\examples\\core", TextFormat("..\\..\\examples\\%s", exCategory)); // Edit: raylib/projects/VS2022/raylib.sln --> Add new example project - system(TextFormat("dotnet solution %s/../projects/VS2022/raylib.sln add %s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exBasePath, exName)); + AddVSProjectToSolution(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName), + TextFormat("%s/../projects/VS2022/raylib.sln", exBasePath)); //------------------------------------------------------------------------------------------------ // Recompile example (on raylib side) @@ -680,10 +685,10 @@ int main(int argc, char *argv[]) // Remove: raylib/projects/VS2022/examples/_example_name.vcxproj remove(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName)); - // Edit: raylib/projects/VS2022/raylib.sln --> Remove example project + // TODO: Edit: raylib/projects/VS2022/raylib.sln --> Remove example project //--------------------------------------------------------------------------- - system(TextFormat("dotnet solution %s/../projects/VS2022/raylib.sln remove %s/../projects/VS2022/examples/%s.vcxproj", - exBasePath, exBasePath, exName)); + //RemoveVSProjectFromSolution(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName), + // TextFormat("%s/../projects/VS2022/raylib.sln", exBasePath)); //--------------------------------------------------------------------------- // Remove: raylib.com/examples//_example_name.html @@ -932,16 +937,23 @@ int main(int argc, char *argv[]) "..\\..\\examples\\core", TextFormat("..\\..\\examples\\%s", exInfo->category)); } + // WARNING: Adding a .vcxproj to .sln can not be automated with + // "dotnet" tool (C# projects only) + // "devenv" tool (no adding support, only building) + // It must be done manually editing the .sln file if (exInfo->status & VALID_NOT_IN_VCXSOL) - system(TextFormat("dotnet solution %s/../projects/VS2022/raylib.sln add %s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exBasePath, exInfo->name)); - + { + AddVSProjectToSolution(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName), + TextFormat("%s/../projects/VS2022/raylib.sln", exBasePath)); + } + // Review: Add/Remove: raylib.com/examples//_example_name.html // Review: Add/Remove: raylib.com/examples//_example_name.data // Review: Add/Remove: raylib.com/examples//_example_name.wasm // Review: Add/Remove: raylib.com/examples//_example_name.js // Solves: VALID_MISSING_WEB_OUTPUT - if (exInfo->status & VALID_MISSING_WEB_OUTPUT) - system(TextFormat("%s/build_example_web.bat %s/%s", exBasePath, exInfo->category, exInfo->name)); + //if (exInfo->status & VALID_MISSING_WEB_OUTPUT) + // system(TextFormat("%s/build_example_web.bat %s/%s", exBasePath, exInfo->category, exInfo->name)); } } @@ -1727,3 +1739,57 @@ static void ClearExampleResources(char **resPaths) RL_FREE(resPaths); } + +// TODO: Add VS project (.vcxproj) to existing VS solution (.sol) +static int AddVSProjectToSolution(const char *projFile, const char *solFile) +{ + int result = 0; + + // Generate unique UUID: + // C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\uuidgen.exe > saved_uuid.txt + + // Add project to the list: + // Format: Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "", "examples\.vcxproj", "{}" + /* + Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_basic_window", "examples\core_basic_window.vcxproj", "{0981CA98-E4A5-4DF1-987F-A41D09131EFC}" + EndProject + */ + + // Update project config: + /* + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Debug|ARM64.Build.0 = Debug|ARM64 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Debug|x64.ActiveCfg = Debug|x64 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Debug|x64.Build.0 = Debug|x64 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Debug|x86.ActiveCfg = Debug|Win32 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Debug|x86.Build.0 = Debug|Win32 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Release|ARM64.ActiveCfg = Release|ARM64 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Release|ARM64.Build.0 = Release|ARM64 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Release|x64.ActiveCfg = Release|x64 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Release|x64.Build.0 = Release|x64 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Release|x86.ActiveCfg = Release|Win32 + {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Release|x86.Build.0 = Release|Win32 + */ + + // Update projects to folders: + // {project_uuid} = {solution_folder_uuid} + /* + GlobalSection(NestedProjects) = preSolution + {0981CA98-E4A5-4DF1-987F-A41D09131EFC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + */ + + return result; +} From c91c185221847407f1b3c54597e9b337bc640bb7 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 17:04:22 +0200 Subject: [PATCH 207/242] Update examples_report.md --- tools/rexm/examples_report.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tools/rexm/examples_report.md b/tools/rexm/examples_report.md index 97bc7c52a..ef1aca64b 100644 --- a/tools/rexm/examples_report.md +++ b/tools/rexm/examples_report.md @@ -44,7 +44,7 @@ Example elements validated: | core_drop_files | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_random_values | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_storage_values | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| core_vr_simulator | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_vr_simulator | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_loading_thread | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_scissor_test | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_basic_screen_manager | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -73,8 +73,8 @@ Example elements validated: | shapes_top_down_lights | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_rectangle_advanced | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_splines_drawing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| shapes_digital_clock | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ❌ | -| shapes_double_pendulum | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ❌ | +| shapes_digital_clock | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | +| shapes_double_pendulum | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | | textures_logo_raylib | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | textures_srcrec_dstrec | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | textures_image_drawing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -97,9 +97,9 @@ Example elements validated: | textures_polygon | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | textures_fog_of_war | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | textures_gif_player | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| textures_image_kernel | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | -| textures_image_channel | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | -| textures_image_rotate | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | +| textures_image_kernel | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_image_channel | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_image_rotate | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | textures_textured_curve | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | text_raylib_fonts | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | text_font_spritefont | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -126,16 +126,16 @@ Example elements validated: | models_loading_vox | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_loading_m3d | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_orthographic_projection | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| models_point_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | +| models_point_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_rlgl_solar_system | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_yaw_pitch_roll | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_waving_cubes | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_heightmap | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| models_skybox | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_skybox | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_draw_cube_texture | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| models_gpu_skinning | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_gpu_skinning | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_bone_socket | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| models_tesseract_view | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | +| models_tesseract_view | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_basic_lighting | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_model_shader | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_shapes_textures | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -160,10 +160,10 @@ Example elements validated: | shaders_shadowmap | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_vertex_displacement | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_write_depth | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| shaders_basic_pbr | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | -| shaders_lightmap | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | +| shaders_basic_pbr | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_lightmap | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_rounded_rectangle | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | -| shaders_view_depth | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | +| shaders_view_depth | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | | audio_module_playing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_music_stream | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_raw_stream | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -171,7 +171,7 @@ Example elements validated: | audio_mixed_processor | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_stream_effects | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_sound_multi | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| audio_sound_positioning | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ❌ | +| audio_sound_positioning | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | | rlgl_standalone | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ❌ | | rlgl_compute_shader | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ❌ | | easings_testbed | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ❌ | From ff1087480cbf0d613494eb567cc0f926fdfa9d76 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 17:04:49 +0200 Subject: [PATCH 208/242] ADDED: New projects to VS2022 solution --- .../examples/audio_sound_positioning.vcxproj | 2 +- .../examples/models_point_rendering.vcxproj | 2 +- .../examples/models_tesseract_view.vcxproj | 2 +- .../VS2022/examples/shaders_basic_pbr.vcxproj | 2 +- .../VS2022/examples/shaders_lightmap.vcxproj | 2 +- .../examples/shapes_digital_clock.vcxproj | 2 +- .../examples/shapes_double_pendulum.vcxproj | 2 +- .../examples/textures_image_channel.vcxproj | 2 +- .../examples/textures_image_kernel.vcxproj | 2 +- .../examples/textures_image_rotate.vcxproj | 2 +- projects/VS2022/raylib.sln | 320 ++++++++++++++++-- 11 files changed, 306 insertions(+), 34 deletions(-) diff --git a/projects/VS2022/examples/audio_sound_positioning.vcxproj b/projects/VS2022/examples/audio_sound_positioning.vcxproj index 96c8fef5e..7a241ea30 100644 --- a/projects/VS2022/examples/audio_sound_positioning.vcxproj +++ b/projects/VS2022/examples/audio_sound_positioning.vcxproj @@ -51,7 +51,7 @@ - {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + {9CD8BCAD-F212-4BCC-BA98-899743CE3279} Win32Proj audio_sound_positioning 10.0 diff --git a/projects/VS2022/examples/models_point_rendering.vcxproj b/projects/VS2022/examples/models_point_rendering.vcxproj index ba66e9baa..ce32a3415 100644 --- a/projects/VS2022/examples/models_point_rendering.vcxproj +++ b/projects/VS2022/examples/models_point_rendering.vcxproj @@ -51,7 +51,7 @@ - {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + {921391C6-7626-4212-9928-BC82BC785461} Win32Proj models_point_rendering 10.0 diff --git a/projects/VS2022/examples/models_tesseract_view.vcxproj b/projects/VS2022/examples/models_tesseract_view.vcxproj index b49094ac7..f64344044 100644 --- a/projects/VS2022/examples/models_tesseract_view.vcxproj +++ b/projects/VS2022/examples/models_tesseract_view.vcxproj @@ -51,7 +51,7 @@ - {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F} Win32Proj models_tesseract_view 10.0 diff --git a/projects/VS2022/examples/shaders_basic_pbr.vcxproj b/projects/VS2022/examples/shaders_basic_pbr.vcxproj index 49744ed0e..a17b0de3b 100644 --- a/projects/VS2022/examples/shaders_basic_pbr.vcxproj +++ b/projects/VS2022/examples/shaders_basic_pbr.vcxproj @@ -51,7 +51,7 @@ - {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8} Win32Proj shaders_basic_pbr 10.0 diff --git a/projects/VS2022/examples/shaders_lightmap.vcxproj b/projects/VS2022/examples/shaders_lightmap.vcxproj index 392f35b71..d1be94f1c 100644 --- a/projects/VS2022/examples/shaders_lightmap.vcxproj +++ b/projects/VS2022/examples/shaders_lightmap.vcxproj @@ -51,7 +51,7 @@ - {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + {C54703BF-D68A-480D-BE27-49B62E45D582} Win32Proj shaders_lightmap 10.0 diff --git a/projects/VS2022/examples/shapes_digital_clock.vcxproj b/projects/VS2022/examples/shapes_digital_clock.vcxproj index d14448393..e361eb190 100644 --- a/projects/VS2022/examples/shapes_digital_clock.vcxproj +++ b/projects/VS2022/examples/shapes_digital_clock.vcxproj @@ -51,7 +51,7 @@ - {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + {3384C257-3CFE-4A8F-838C-19DAC5C955DA} Win32Proj shapes_digital_clock 10.0 diff --git a/projects/VS2022/examples/shapes_double_pendulum.vcxproj b/projects/VS2022/examples/shapes_double_pendulum.vcxproj index 57f7d458b..775b25335 100644 --- a/projects/VS2022/examples/shapes_double_pendulum.vcxproj +++ b/projects/VS2022/examples/shapes_double_pendulum.vcxproj @@ -51,7 +51,7 @@ - {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + {2B140378-125F-4DE9-AC37-2CC1B73D7254} Win32Proj shapes_double_pendulum 10.0 diff --git a/projects/VS2022/examples/textures_image_channel.vcxproj b/projects/VS2022/examples/textures_image_channel.vcxproj index c480f869d..90c09fc54 100644 --- a/projects/VS2022/examples/textures_image_channel.vcxproj +++ b/projects/VS2022/examples/textures_image_channel.vcxproj @@ -51,7 +51,7 @@ - {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F} Win32Proj textures_image_channel 10.0 diff --git a/projects/VS2022/examples/textures_image_kernel.vcxproj b/projects/VS2022/examples/textures_image_kernel.vcxproj index 20cb60d6a..b0012e4d6 100644 --- a/projects/VS2022/examples/textures_image_kernel.vcxproj +++ b/projects/VS2022/examples/textures_image_kernel.vcxproj @@ -51,7 +51,7 @@ - {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6} Win32Proj textures_image_kernel 10.0 diff --git a/projects/VS2022/examples/textures_image_rotate.vcxproj b/projects/VS2022/examples/textures_image_rotate.vcxproj index 98179299a..8345b42a7 100644 --- a/projects/VS2022/examples/textures_image_rotate.vcxproj +++ b/projects/VS2022/examples/textures_image_rotate.vcxproj @@ -51,7 +51,7 @@ - {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + {2AA91EED-2D32-4B09-84A3-53D41EED1005} Win32Proj textures_image_rotate 10.0 diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 4f8c3beea..452ba39c5 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -7,6 +7,22 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "raylib", "raylib\raylib.vcx EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{6C82BAAE-BDDF-457D-8FA8-7E2490B07035}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shapes", "shapes", "{278D8859-20B1-428F-8448-064F46E1F021}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "textures", "textures", "{DA049009-21FF-4AC0-84E4-830DD1BCD0CE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "text", "text", "{8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "models", "models", "{AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shaders", "shaders", "{5317807F-61D4-4E0F-B6DC-2D9F12621ED9}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "audio", "audio", "{CC132A4D-D081-4C26-BFB9-AB11984054F8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "other", "other", "{E9D708A5-9C1F-4B84-A795-C5F191801762}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_basic_window", "examples\core_basic_window.vcxproj", "{0981CA98-E4A5-4DF1-987F-A41D09131EFC}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_sprite_anim", "examples\textures_sprite_anim.vcxproj", "{C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}" @@ -79,18 +95,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_box_collisions", "ex EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_cubicmap", "examples\models_cubicmap.vcxproj", "{82F3D34B-8DB2-4C6A-98B1-132245DD9D99}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{6C82BAAE-BDDF-457D-8FA8-7E2490B07035}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "audio", "audio", "{CC132A4D-D081-4C26-BFB9-AB11984054F8}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "models", "models", "{AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "textures", "textures", "{DA049009-21FF-4AC0-84E4-830DD1BCD0CE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shapes", "shapes", "{278D8859-20B1-428F-8448-064F46E1F021}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shaders", "shaders", "{5317807F-61D4-4E0F-B6DC-2D9F12621ED9}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_first_person_maze", "examples\models_first_person_maze.vcxproj", "{CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_geometric_shapes", "examples\models_geometric_shapes.vcxproj", "{14BA7F98-02CC-4648-9236-676BFF9458AF}" @@ -145,10 +149,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_blend_modes", "exa EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_background_scrolling", "examples\textures_background_scrolling.vcxproj", "{8DD0EB7E-668E-452D-91D7-906C64A9C8AC}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "text", "text", "{8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "other", "other", "{E9D708A5-9C1F-4B84-A795-C5F191801762}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_writing_anim", "examples\text_writing_anim.vcxproj", "{F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_unicode", "examples\text_unicode.vcxproj", "{1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}" @@ -313,6 +313,26 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_vertex_displacement EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_rounded_rectangle", "examples\shaders_rounded_rectangle.vcxproj", "{D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_digital_clock", "examples\shapes_digital_clock.vcxproj", "{3384C257-3CFE-4A8F-838C-19DAC5C955DA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_double_pendulum", "examples\shapes_double_pendulum.vcxproj", "{2B140378-125F-4DE9-AC37-2CC1B73D7254}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_image_kernel", "examples\textures_image_kernel.vcxproj", "{F4C55B99-E1C5-496A-8AC2-40188C38F4F6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_image_rotate", "examples\textures_image_rotate.vcxproj", "{2AA91EED-2D32-4B09-84A3-53D41EED1005}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_image_channel", "examples\textures_image_channel.vcxproj", "{EC0910F6-8D66-4509-BF57-A5EE7AE9485F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_point_rendering", "examples\models_point_rendering.vcxproj", "{921391C6-7626-4212-9928-BC82BC785461}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_tesseract_view", "examples\models_tesseract_view.vcxproj", "{6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_basic_pbr", "examples\shaders_basic_pbr.vcxproj", "{4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_lightmap", "examples\shaders_lightmap.vcxproj", "{C54703BF-D68A-480D-BE27-49B62E45D582}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_sound_positioning", "examples\audio_sound_positioning.vcxproj", "{9CD8BCAD-F212-4BCC-BA98-899743CE3279}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|ARM64 = Debug.DLL|ARM64 @@ -3821,11 +3841,259 @@ Global {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Release|x64.Build.0 = Release|x64 {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Release|x86.ActiveCfg = Release|Win32 {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4}.Release|x86.Build.0 = Release|Win32 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Debug|ARM64.Build.0 = Debug|ARM64 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Debug|x64.ActiveCfg = Debug|x64 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Debug|x64.Build.0 = Debug|x64 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Debug|x86.ActiveCfg = Debug|Win32 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Debug|x86.Build.0 = Debug|Win32 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Release|ARM64.ActiveCfg = Release|ARM64 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Release|ARM64.Build.0 = Release|ARM64 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Release|x64.ActiveCfg = Release|x64 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Release|x64.Build.0 = Release|x64 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Release|x86.ActiveCfg = Release|Win32 + {3384C257-3CFE-4A8F-838C-19DAC5C955DA}.Release|x86.Build.0 = Release|Win32 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Debug|ARM64.Build.0 = Debug|ARM64 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Debug|x64.ActiveCfg = Debug|x64 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Debug|x64.Build.0 = Debug|x64 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Debug|x86.ActiveCfg = Debug|Win32 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Debug|x86.Build.0 = Debug|Win32 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Release|ARM64.ActiveCfg = Release|ARM64 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Release|ARM64.Build.0 = Release|ARM64 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Release|x64.ActiveCfg = Release|x64 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Release|x64.Build.0 = Release|x64 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Release|x86.ActiveCfg = Release|Win32 + {2B140378-125F-4DE9-AC37-2CC1B73D7254}.Release|x86.Build.0 = Release|Win32 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Debug|ARM64.Build.0 = Debug|ARM64 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Debug|x64.ActiveCfg = Debug|x64 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Debug|x64.Build.0 = Debug|x64 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Debug|x86.ActiveCfg = Debug|Win32 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Debug|x86.Build.0 = Debug|Win32 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Release|ARM64.ActiveCfg = Release|ARM64 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Release|ARM64.Build.0 = Release|ARM64 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Release|x64.ActiveCfg = Release|x64 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Release|x64.Build.0 = Release|x64 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Release|x86.ActiveCfg = Release|Win32 + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6}.Release|x86.Build.0 = Release|Win32 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Debug|ARM64.Build.0 = Debug|ARM64 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Debug|x64.ActiveCfg = Debug|x64 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Debug|x64.Build.0 = Debug|x64 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Debug|x86.ActiveCfg = Debug|Win32 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Debug|x86.Build.0 = Debug|Win32 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Release|ARM64.ActiveCfg = Release|ARM64 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Release|ARM64.Build.0 = Release|ARM64 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Release|x64.ActiveCfg = Release|x64 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Release|x64.Build.0 = Release|x64 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Release|x86.ActiveCfg = Release|Win32 + {2AA91EED-2D32-4B09-84A3-53D41EED1005}.Release|x86.Build.0 = Release|Win32 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Debug|ARM64.Build.0 = Debug|ARM64 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Debug|x64.ActiveCfg = Debug|x64 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Debug|x64.Build.0 = Debug|x64 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Debug|x86.ActiveCfg = Debug|Win32 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Debug|x86.Build.0 = Debug|Win32 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Release|ARM64.ActiveCfg = Release|ARM64 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Release|ARM64.Build.0 = Release|ARM64 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Release|x64.ActiveCfg = Release|x64 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Release|x64.Build.0 = Release|x64 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Release|x86.ActiveCfg = Release|Win32 + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F}.Release|x86.Build.0 = Release|Win32 + {921391C6-7626-4212-9928-BC82BC785461}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {921391C6-7626-4212-9928-BC82BC785461}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {921391C6-7626-4212-9928-BC82BC785461}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {921391C6-7626-4212-9928-BC82BC785461}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {921391C6-7626-4212-9928-BC82BC785461}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {921391C6-7626-4212-9928-BC82BC785461}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {921391C6-7626-4212-9928-BC82BC785461}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {921391C6-7626-4212-9928-BC82BC785461}.Debug|ARM64.Build.0 = Debug|ARM64 + {921391C6-7626-4212-9928-BC82BC785461}.Debug|x64.ActiveCfg = Debug|x64 + {921391C6-7626-4212-9928-BC82BC785461}.Debug|x64.Build.0 = Debug|x64 + {921391C6-7626-4212-9928-BC82BC785461}.Debug|x86.ActiveCfg = Debug|Win32 + {921391C6-7626-4212-9928-BC82BC785461}.Debug|x86.Build.0 = Debug|Win32 + {921391C6-7626-4212-9928-BC82BC785461}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {921391C6-7626-4212-9928-BC82BC785461}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {921391C6-7626-4212-9928-BC82BC785461}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {921391C6-7626-4212-9928-BC82BC785461}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {921391C6-7626-4212-9928-BC82BC785461}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {921391C6-7626-4212-9928-BC82BC785461}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {921391C6-7626-4212-9928-BC82BC785461}.Release|ARM64.ActiveCfg = Release|ARM64 + {921391C6-7626-4212-9928-BC82BC785461}.Release|ARM64.Build.0 = Release|ARM64 + {921391C6-7626-4212-9928-BC82BC785461}.Release|x64.ActiveCfg = Release|x64 + {921391C6-7626-4212-9928-BC82BC785461}.Release|x64.Build.0 = Release|x64 + {921391C6-7626-4212-9928-BC82BC785461}.Release|x86.ActiveCfg = Release|Win32 + {921391C6-7626-4212-9928-BC82BC785461}.Release|x86.Build.0 = Release|Win32 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Debug|ARM64.Build.0 = Debug|ARM64 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Debug|x64.ActiveCfg = Debug|x64 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Debug|x64.Build.0 = Debug|x64 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Debug|x86.ActiveCfg = Debug|Win32 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Debug|x86.Build.0 = Debug|Win32 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Release|ARM64.ActiveCfg = Release|ARM64 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Release|ARM64.Build.0 = Release|ARM64 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Release|x64.ActiveCfg = Release|x64 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Release|x64.Build.0 = Release|x64 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Release|x86.ActiveCfg = Release|Win32 + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F}.Release|x86.Build.0 = Release|Win32 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Debug|ARM64.Build.0 = Debug|ARM64 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Debug|x64.ActiveCfg = Debug|x64 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Debug|x64.Build.0 = Debug|x64 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Debug|x86.ActiveCfg = Debug|Win32 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Debug|x86.Build.0 = Debug|Win32 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Release|ARM64.ActiveCfg = Release|ARM64 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Release|ARM64.Build.0 = Release|ARM64 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Release|x64.ActiveCfg = Release|x64 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Release|x64.Build.0 = Release|x64 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Release|x86.ActiveCfg = Release|Win32 + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8}.Release|x86.Build.0 = Release|Win32 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Debug|ARM64.Build.0 = Debug|ARM64 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Debug|x64.ActiveCfg = Debug|x64 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Debug|x64.Build.0 = Debug|x64 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Debug|x86.ActiveCfg = Debug|Win32 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Debug|x86.Build.0 = Debug|Win32 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Release|ARM64.ActiveCfg = Release|ARM64 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Release|ARM64.Build.0 = Release|ARM64 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Release|x64.ActiveCfg = Release|x64 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Release|x64.Build.0 = Release|x64 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Release|x86.ActiveCfg = Release|Win32 + {C54703BF-D68A-480D-BE27-49B62E45D582}.Release|x86.Build.0 = Release|Win32 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Debug|ARM64.Build.0 = Debug|ARM64 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Debug|x64.ActiveCfg = Debug|x64 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Debug|x64.Build.0 = Debug|x64 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Debug|x86.ActiveCfg = Debug|Win32 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Debug|x86.Build.0 = Debug|Win32 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Release|ARM64.ActiveCfg = Release|ARM64 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Release|ARM64.Build.0 = Release|ARM64 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Release|x64.ActiveCfg = Release|x64 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Release|x64.Build.0 = Release|x64 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Release|x86.ActiveCfg = Release|Win32 + {9CD8BCAD-F212-4BCC-BA98-899743CE3279}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution + {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1} + {278D8859-20B1-428F-8448-064F46E1F021} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1} + {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1} + {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1} + {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1} + {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1} + {CC132A4D-D081-4C26-BFB9-AB11984054F8} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1} + {E9D708A5-9C1F-4B84-A795-C5F191801762} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1} {0981CA98-E4A5-4DF1-987F-A41D09131EFC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} {103B292B-049B-4B15-85A1-9F902840DB2C} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} @@ -3843,6 +4111,7 @@ Global {FD193822-3D5C-4161-A147-884C2ABDE483} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {20AD0AC9-9159-4744-99CC-6AC5779D6B87} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {0199E349-0701-40BC-8A7F-06A54FFA3E7C} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {8F19E3DA-8929-4000-87B5-3CA6929636CC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {51A00565-5787-4911-9CC0-28403AA4909D} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {92B64AE7-D773-4F05-89F1-CE59BBF4F053} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} @@ -3861,12 +4130,6 @@ Global {B7812167-50FB-4934-996F-DF6FE4CBBFDF} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {39DB56C7-05F8-492C-A8D4-F19E40FECB59} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {82F3D34B-8DB2-4C6A-98B1-132245DD9D99} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1} - {CC132A4D-D081-4C26-BFB9-AB11984054F8} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1} - {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1} - {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1} - {278D8859-20B1-428F-8448-064F46E1F021} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1} - {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1} {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {14BA7F98-02CC-4648-9236-676BFF9458AF} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {0859A973-E4FE-4688-8D16-0253163FDE24} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} @@ -3894,8 +4157,6 @@ Global {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} {6D75CD88-1A03-4955-B8C7-ACFC3742154F} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} {8DD0EB7E-668E-452D-91D7-906C64A9C8AC} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1} - {E9D708A5-9C1F-4B84-A795-C5F191801762} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1} {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} {25BCB876-B60A-499B-9046-E9801CFD7780} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} @@ -3957,6 +4218,7 @@ Global {769FF0C1-4424-4FA3-BC44-D7A7DA312A06} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} {6D9E00D8-2893-45E4-9363-3F7F61D416BD} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {70B35F59-AFC2-4D8F-8833-5314D2047A81} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {3755E9F4-CB48-4EC3-B561-3B85964EBDEF} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {F81C5819-85B4-4D2E-B6DC-104A7634461B} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} {CC62F7DB-D089-4677-8575-CAB7A7815C43} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} @@ -3977,6 +4239,16 @@ Global {3A7FE53D-35F7-49DC-9C9A-A5204A53523F} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {CCA63A76-D9FC-4130-9F67-4D97F9770D53} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {D3493FFE-8873-4C53-8F6C-74DEF78EA3C4} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {3384C257-3CFE-4A8F-838C-19DAC5C955DA} = {278D8859-20B1-428F-8448-064F46E1F021} + {2B140378-125F-4DE9-AC37-2CC1B73D7254} = {278D8859-20B1-428F-8448-064F46E1F021} + {F4C55B99-E1C5-496A-8AC2-40188C38F4F6} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {2AA91EED-2D32-4B09-84A3-53D41EED1005} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {EC0910F6-8D66-4509-BF57-A5EE7AE9485F} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {921391C6-7626-4212-9928-BC82BC785461} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {6B8C5711-6AB4-4023-9FDD-E9D976E8D18F} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {4DF6D5E4-6796-4257-B466-BCD62DEBBCF8} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {C54703BF-D68A-480D-BE27-49B62E45D582} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {9CD8BCAD-F212-4BCC-BA98-899743CE3279} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} From 1fadc67fb36a5959be05ee4f33c21bcb12937313 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 17:05:25 +0200 Subject: [PATCH 209/242] ADDED: Missing resources on some examples --- .../resources/shaders/glsl120/cubemap.fs | 27 +++++++++ .../resources/shaders/glsl120/cubemap.vs | 20 +++++++ .../resources/shaders/glsl120/skinning.fs | 18 ++++++ .../resources/shaders/glsl120/skinning.vs | 59 +++++++++++++++++++ .../resources/shaders/glsl120/skybox.fs | 29 +++++++++ .../resources/shaders/glsl120/skybox.vs | 24 ++++++++ .../resources/shaders/glsl120/depth.fs | 28 +++++++++ 7 files changed, 205 insertions(+) create mode 100644 examples/models/resources/shaders/glsl120/cubemap.fs create mode 100644 examples/models/resources/shaders/glsl120/cubemap.vs create mode 100644 examples/models/resources/shaders/glsl120/skinning.fs create mode 100644 examples/models/resources/shaders/glsl120/skinning.vs create mode 100644 examples/models/resources/shaders/glsl120/skybox.fs create mode 100644 examples/models/resources/shaders/glsl120/skybox.vs create mode 100644 examples/shaders/resources/shaders/glsl120/depth.fs diff --git a/examples/models/resources/shaders/glsl120/cubemap.fs b/examples/models/resources/shaders/glsl120/cubemap.fs new file mode 100644 index 000000000..529ee4ab9 --- /dev/null +++ b/examples/models/resources/shaders/glsl120/cubemap.fs @@ -0,0 +1,27 @@ +#version 120 + +// Input vertex attributes (from vertex shader) +varying vec3 fragPosition; + +// Input uniform values +uniform sampler2D equirectangularMap; + +vec2 SampleSphericalMap(vec3 v) +{ + vec2 uv = vec2(atan(v.z, v.x), asin(v.y)); + uv *= vec2(0.1591, 0.3183); + uv += 0.5; + return uv; +} + +void main() +{ + // Normalize local position + vec2 uv = SampleSphericalMap(normalize(fragPosition)); + + // Fetch color from texture map + vec3 color = texture2D(equirectangularMap, uv).rgb; + + // Calculate final fragment color + gl_FragColor = vec4(color, 1.0); +} diff --git a/examples/models/resources/shaders/glsl120/cubemap.vs b/examples/models/resources/shaders/glsl120/cubemap.vs new file mode 100644 index 000000000..8ce230503 --- /dev/null +++ b/examples/models/resources/shaders/glsl120/cubemap.vs @@ -0,0 +1,20 @@ +#version 120 + +// Input vertex attributes +attribute vec3 vertexPosition; + +// Input uniform values +uniform mat4 matProjection; +uniform mat4 matView; + +// Output vertex attributes (to fragment shader) +varying vec3 fragPosition; + +void main() +{ + // Calculate fragment position based on model transformations + fragPosition = vertexPosition; + + // Calculate final vertex position + gl_Position = matProjection*matView*vec4(vertexPosition, 1.0); +} diff --git a/examples/models/resources/shaders/glsl120/skinning.fs b/examples/models/resources/shaders/glsl120/skinning.fs new file mode 100644 index 000000000..44c6314d1 --- /dev/null +++ b/examples/models/resources/shaders/glsl120/skinning.fs @@ -0,0 +1,18 @@ +#version 120 + +// Input vertex attributes (from vertex shader) +varying vec2 fragTexCoord; +varying vec4 fragColor; + +// Input uniform values +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +void main() +{ + // Fetch color from texture sampler + vec4 texelColor = texture2D(texture0, fragTexCoord); + + // Calculate final fragment color + gl_FragColor = texelColor*colDiffuse*fragColor; +} diff --git a/examples/models/resources/shaders/glsl120/skinning.vs b/examples/models/resources/shaders/glsl120/skinning.vs new file mode 100644 index 000000000..c08840b54 --- /dev/null +++ b/examples/models/resources/shaders/glsl120/skinning.vs @@ -0,0 +1,59 @@ +#version 120 + +#define MAX_BONE_NUM 64 + +// Input vertex attributes +attribute vec3 vertexPosition; +attribute vec2 vertexTexCoord; +attribute vec4 vertexColor; +attribute vec4 vertexBoneIds; +attribute vec4 vertexBoneWeights; + +// Input uniform values +uniform mat4 mvp; +uniform mat4 boneMatrices[MAX_BONE_NUM]; + +// Output vertex attributes (to fragment shader) +varying vec2 fragTexCoord; +varying vec4 fragColor; + +void main() +{ + int boneIndex0 = int(vertexBoneIds.x); + int boneIndex1 = int(vertexBoneIds.y); + int boneIndex2 = int(vertexBoneIds.z); + int boneIndex3 = int(vertexBoneIds.w); + + // WARNING: OpenGL ES 2.0 does not support automatic matrix transposing, neither transpose() function + mat4 boneMatrixTransposed0 = mat4( + vec4(boneMatrices[boneIndex0][0].x, boneMatrices[boneIndex0][1].x, boneMatrices[boneIndex0][2].x, boneMatrices[boneIndex0][3].x), + vec4(boneMatrices[boneIndex0][0].y, boneMatrices[boneIndex0][1].y, boneMatrices[boneIndex0][2].y, boneMatrices[boneIndex0][3].y), + vec4(boneMatrices[boneIndex0][0].z, boneMatrices[boneIndex0][1].z, boneMatrices[boneIndex0][2].z, boneMatrices[boneIndex0][3].z), + vec4(boneMatrices[boneIndex0][0].w, boneMatrices[boneIndex0][1].w, boneMatrices[boneIndex0][2].w, boneMatrices[boneIndex0][3].w)); + mat4 boneMatrixTransposed1 = mat4( + vec4(boneMatrices[boneIndex1][0].x, boneMatrices[boneIndex1][1].x, boneMatrices[boneIndex1][2].x, boneMatrices[boneIndex1][3].x), + vec4(boneMatrices[boneIndex1][0].y, boneMatrices[boneIndex1][1].y, boneMatrices[boneIndex1][2].y, boneMatrices[boneIndex1][3].y), + vec4(boneMatrices[boneIndex1][0].z, boneMatrices[boneIndex1][1].z, boneMatrices[boneIndex1][2].z, boneMatrices[boneIndex1][3].z), + vec4(boneMatrices[boneIndex1][0].w, boneMatrices[boneIndex1][1].w, boneMatrices[boneIndex1][2].w, boneMatrices[boneIndex1][3].w)); + mat4 boneMatrixTransposed2 = mat4( + vec4(boneMatrices[boneIndex2][0].x, boneMatrices[boneIndex2][1].x, boneMatrices[boneIndex2][2].x, boneMatrices[boneIndex2][3].x), + vec4(boneMatrices[boneIndex2][0].y, boneMatrices[boneIndex2][1].y, boneMatrices[boneIndex2][2].y, boneMatrices[boneIndex2][3].y), + vec4(boneMatrices[boneIndex2][0].z, boneMatrices[boneIndex2][1].z, boneMatrices[boneIndex2][2].z, boneMatrices[boneIndex2][3].z), + vec4(boneMatrices[boneIndex2][0].w, boneMatrices[boneIndex2][1].w, boneMatrices[boneIndex2][2].w, boneMatrices[boneIndex2][3].w)); + mat4 boneMatrixTransposed3 = mat4( + vec4(boneMatrices[boneIndex3][0].x, boneMatrices[boneIndex3][1].x, boneMatrices[boneIndex3][2].x, boneMatrices[boneIndex3][3].x), + vec4(boneMatrices[boneIndex3][0].y, boneMatrices[boneIndex3][1].y, boneMatrices[boneIndex3][2].y, boneMatrices[boneIndex3][3].y), + vec4(boneMatrices[boneIndex3][0].z, boneMatrices[boneIndex3][1].z, boneMatrices[boneIndex3][2].z, boneMatrices[boneIndex3][3].z), + vec4(boneMatrices[boneIndex3][0].w, boneMatrices[boneIndex3][1].w, boneMatrices[boneIndex3][2].w, boneMatrices[boneIndex3][3].w)); + + vec4 skinnedPosition = + vertexBoneWeights.x*(boneMatrixTransposed0*vec4(vertexPosition, 1.0)) + + vertexBoneWeights.y*(boneMatrixTransposed1*vec4(vertexPosition, 1.0)) + + vertexBoneWeights.z*(boneMatrixTransposed2*vec4(vertexPosition, 1.0)) + + vertexBoneWeights.w*(boneMatrixTransposed3*vec4(vertexPosition, 1.0)); + + fragTexCoord = vertexTexCoord; + fragColor = vertexColor; + + gl_Position = mvp*skinnedPosition; +} \ No newline at end of file diff --git a/examples/models/resources/shaders/glsl120/skybox.fs b/examples/models/resources/shaders/glsl120/skybox.fs new file mode 100644 index 000000000..ec9a3c274 --- /dev/null +++ b/examples/models/resources/shaders/glsl120/skybox.fs @@ -0,0 +1,29 @@ +#version 120 + +// Input vertex attributes (from vertex shader) +varying vec3 fragPosition; + +// Input uniform values +uniform samplerCube environmentMap; +uniform bool vflipped; +uniform bool doGamma; + +void main() +{ + // Fetch color from texture map + vec4 texelColor = vec4(0.0); + + if (vflipped) texelColor = textureCube(environmentMap, vec3(fragPosition.x, -fragPosition.y, fragPosition.z)); + else texelColor = textureCube(environmentMap, fragPosition); + + vec3 color = vec3(texelColor.x, texelColor.y, texelColor.z); + + if (doGamma) // Apply gamma correction + { + color = color/(color + vec3(1.0)); + color = pow(color, vec3(1.0/2.2)); + } + + // Calculate final fragment color + gl_FragColor = vec4(color, 1.0); +} diff --git a/examples/models/resources/shaders/glsl120/skybox.vs b/examples/models/resources/shaders/glsl120/skybox.vs new file mode 100644 index 000000000..71c933ea5 --- /dev/null +++ b/examples/models/resources/shaders/glsl120/skybox.vs @@ -0,0 +1,24 @@ +#version 120 + +// Input vertex attributes +attribute vec3 vertexPosition; + +// Input uniform values +uniform mat4 matProjection; +uniform mat4 matView; + +// Output vertex attributes (to fragment shader) +varying vec3 fragPosition; + +void main() +{ + // Calculate fragment position based on model transformations + fragPosition = vertexPosition; + + // Remove translation from the view matrix + mat4 rotView = mat4(mat3(matView)); + vec4 clipPos = matProjection*rotView*vec4(vertexPosition, 1.0); + + // Calculate final vertex position + gl_Position = clipPos; +} diff --git a/examples/shaders/resources/shaders/glsl120/depth.fs b/examples/shaders/resources/shaders/glsl120/depth.fs new file mode 100644 index 000000000..5cf6ef71b --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/depth.fs @@ -0,0 +1,28 @@ +#version 120 + +// Input vertex attributes (from vertex shader) +varying vec2 fragTexCoord; + +// Input uniform values +uniform sampler2D depthTexture; +uniform bool flipY; + +float nearPlane = 0.1; +float farPlane = 100.0; + +void main() +{ + // Handle potential Y-flipping + vec2 texCoord = fragTexCoord; + if (flipY) + texCoord.y = 1.0 - texCoord.y; + + // Sample depth texture + float depth = texture2D(depthTexture, texCoord).r; + + // Linearize depth + float linearDepth = (2.0*nearPlane)/(farPlane + nearPlane - depth*(farPlane - nearPlane)); + + // Output final color + gl_FragColor = vec4(vec3(linearDepth), 1.0); +} \ No newline at end of file From 5eacc872c77032f5679b051807d8498a12dce5a8 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 17:05:45 +0200 Subject: [PATCH 210/242] Update README.md --- examples/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index 59e2f3d34..1c169d25a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -18,7 +18,6 @@ You may find it easier to use than other toolchains, especially when it comes to ## EXAMPLES COLLECTION [TOTAL: 159] - ### category: core [36] Examples using raylib[core](../src/rcore.c) platform functionality like window creation, inputs, drawing modes and system functionality. From fd79b44920e817f5c0e3f31be0e0bb2450138c65 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 17:05:50 +0200 Subject: [PATCH 211/242] Create distortion.fs --- .../resources/shaders/glsl120/distortion.fs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 examples/core/resources/shaders/glsl120/distortion.fs diff --git a/examples/core/resources/shaders/glsl120/distortion.fs b/examples/core/resources/shaders/glsl120/distortion.fs new file mode 100644 index 000000000..496557b07 --- /dev/null +++ b/examples/core/resources/shaders/glsl120/distortion.fs @@ -0,0 +1,50 @@ +#version 120 + +// Input vertex attributes (from vertex shader) +varying vec2 fragTexCoord; +varying vec4 fragColor; + +// Input uniform values +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +// NOTE: Add your custom variables here +uniform vec2 leftLensCenter; +uniform vec2 rightLensCenter; +uniform vec2 leftScreenCenter; +uniform vec2 rightScreenCenter; +uniform vec2 scale; +uniform vec2 scaleIn; +uniform vec4 deviceWarpParam; +uniform vec4 chromaAbParam; + +void main() +{ + // Compute lens distortion + vec2 lensCenter = fragTexCoord.x < 0.5? leftLensCenter : rightLensCenter; + vec2 screenCenter = fragTexCoord.x < 0.5? leftScreenCenter : rightScreenCenter; + vec2 theta = (fragTexCoord - lensCenter)*scaleIn; + float rSq = theta.x*theta.x + theta.y*theta.y; + vec2 theta1 = theta*(deviceWarpParam.x + deviceWarpParam.y*rSq + deviceWarpParam.z*rSq*rSq + deviceWarpParam.w*rSq*rSq*rSq); + vec2 thetaBlue = theta1*(chromaAbParam.z + chromaAbParam.w*rSq); + vec2 tcBlue = lensCenter + scale*thetaBlue; + + if (any(bvec2(clamp(tcBlue, screenCenter - vec2(0.25, 0.5), screenCenter + vec2(0.25, 0.5)) - tcBlue))) + { + // Set black fragment for everything outside the lens border + gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); + } + else + { + // Compute color chroma aberration + float blue = texture2D(texture0, tcBlue).b; + vec2 tcGreen = lensCenter + scale*theta1; + float green = texture2D(texture0, tcGreen).g; + + vec2 thetaRed = theta1*(chromaAbParam.x + chromaAbParam.y*rSq); + vec2 tcRed = lensCenter + scale*thetaRed; + + float red = texture2D(texture0, tcRed).r; + gl_FragColor = vec4(red, green, blue, 1.0); + } +} From 5d4a233f525207d64e4aa877fd6bd5d8d5df3ac7 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 17:05:57 +0200 Subject: [PATCH 212/242] Update examples_template.c --- examples/examples_template.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/examples_template.c b/examples/examples_template.c index e82bed065..433fe57bd 100644 --- a/examples/examples_template.c +++ b/examples/examples_template.c @@ -56,7 +56,7 @@ /******************************************************************************************* * -* raylib [] example - +* raylib [] example - * * Example complexity rating: [★☆☆☆] 1/4 * From 64fbf07e7b1b3d16a6d47067f3a53f22d6f0a882 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 17:06:11 +0200 Subject: [PATCH 213/242] Update audio_sound_multi.c --- examples/audio/audio_sound_multi.c | 38 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/examples/audio/audio_sound_multi.c b/examples/audio/audio_sound_multi.c index 243ca7bd8..6f9aea3f9 100644 --- a/examples/audio/audio_sound_multi.c +++ b/examples/audio/audio_sound_multi.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [audio] example - Playing sound multiple times +* raylib [audio] example - sound alias * * Example complexity rating: [★★☆☆] 2/4 * @@ -31,18 +31,18 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [audio] example - playing sound multiple times"); + InitWindow(screenWidth, screenHeight, "raylib [audio] example - sound alias"); InitAudioDevice(); // Initialize audio device - // load the sound list - soundArray[0] = LoadSound("resources/sound.wav"); // Load WAV audio file into the first slot as the 'source' sound - // this sound owns the sample data - for (int i = 1; i < MAX_SOUNDS; i++) - { - soundArray[i] = LoadSoundAlias(soundArray[0]); // Load an alias of the sound into slots 1-9. These do not own the sound data, but can be played - } - currentSound = 0; // set the sound list to the start + // Load audio file into the first slot as the 'source' sound, + // this sound owns the sample data + soundArray[0] = LoadSound("resources/sound.wav"); + + // Load an alias of the sound into slots 1-9. These do not own the sound data, but can be played + for (int i = 1; i < MAX_SOUNDS; i++) soundArray[i] = LoadSoundAlias(soundArray[0]); + + currentSound = 0; // Set the sound list to the start SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -54,14 +54,15 @@ int main(void) //---------------------------------------------------------------------------------- if (IsKeyPressed(KEY_SPACE)) { - PlaySound(soundArray[currentSound]); // play the next open sound slot - currentSound++; // increment the sound slot - if (currentSound >= MAX_SOUNDS) // if the sound slot is out of bounds, go back to 0 - currentSound = 0; + PlaySound(soundArray[currentSound]); // Play the next open sound slot + currentSound++; // Increment the sound slot - // Note: a better way would be to look at the list for the first sound that is not playing and use that slot + // If the sound slot is out of bounds, go back to 0 + if (currentSound >= MAX_SOUNDS) currentSound = 0; + + // NOTE: Another approach would be to look at the list for the first sound + // that is not playing and use that slot } - //---------------------------------------------------------------------------------- // Draw @@ -78,9 +79,8 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - for (int i = 1; i < MAX_SOUNDS; i++) - UnloadSoundAlias(soundArray[i]); // Unload sound aliases - UnloadSound(soundArray[0]); // Unload source sound data + for (int i = 1; i < MAX_SOUNDS; i++) UnloadSoundAlias(soundArray[i]); // Unload sound aliases + UnloadSound(soundArray[0]); // Unload source sound data CloseAudioDevice(); // Close audio device From 366887b863f49e8e6b67e1cdb115861890c231e6 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 17:06:39 +0200 Subject: [PATCH 214/242] Update models_loading_vox.c --- examples/models/models_loading_vox.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/examples/models/models_loading_vox.c b/examples/models/models_loading_vox.c index 75b975f94..f8917ecf2 100644 --- a/examples/models/models_loading_vox.c +++ b/examples/models/models_loading_vox.c @@ -40,7 +40,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - const char* voxFileNames[] = { + const char *voxFileNames[] = { "resources/models/vox/chr_knight.vox", "resources/models/vox/chr_sword.vox", "resources/models/vox/monu9.vox", @@ -57,24 +57,23 @@ int main(void) camera.fovy = 45.0f; // Camera field-of-view Y camera.projection = CAMERA_PERSPECTIVE; // Camera projection type - //-------------------------------------------------------------------------------------- // Load MagicaVoxel files Model models[MAX_VOX_FILES] = { 0 }; for (int i = 0; i < MAX_VOX_FILES; i++) { // Load VOX file and measure time - double t0 = GetTime() * 1000.0; + double t0 = GetTime()*1000.0; models[i] = LoadModel(voxFileNames[i]); - double t1 = GetTime() * 1000.0; + double t1 = GetTime()*1000.0; TraceLog(LOG_WARNING, TextFormat("[%s] File loaded in %.3f ms", voxFileNames[i], t1 - t0)); // Compute model translation matrix to center model on draw position (0, 0 , 0) BoundingBox bb = GetModelBoundingBox(models[i]); Vector3 center = { 0 }; - center.x = bb.min.x + (((bb.max.x - bb.min.x) / 2)); - center.z = bb.min.z + (((bb.max.z - bb.min.z) / 2)); + center.x = bb.min.x + (((bb.max.x - bb.min.x)/2)); + center.z = bb.min.z + (((bb.max.z - bb.min.z)/2)); Matrix matTranslate = MatrixTranslate(-center.x, 0, -center.z); models[i].transform = matTranslate; @@ -82,14 +81,13 @@ int main(void) int currentModel = 0; - //-------------------------------------------------------------------------------------- // Load voxel shader Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/voxel_lighting.vs", GLSL_VERSION), TextFormat("resources/shaders/glsl%i/voxel_lighting.fs", GLSL_VERSION)); // Get some required shader locations shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos"); - // NOTE: "matModel" location name is automatically assigned on shader loading, + // NOTE: "matModel" location name is automatically assigned on shader loading, // no need to get the location again if using that uniform name //shader.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocation(shader, "matModel"); From 9003cd32ba41834612986a69a092031e347b1e09 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 17:06:50 +0200 Subject: [PATCH 215/242] Update models_rlgl_solar_system.c --- examples/models/models_rlgl_solar_system.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/models/models_rlgl_solar_system.c b/examples/models/models_rlgl_solar_system.c index 81f3e0f75..ee1e713c2 100644 --- a/examples/models/models_rlgl_solar_system.c +++ b/examples/models/models_rlgl_solar_system.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [models] example - rlgl module usage with push/pop matrix transformations +* raylib [models] example - rlgl solar system * * Example complexity rating: [★★★★] 4/4 * @@ -41,7 +41,7 @@ int main(void) const float moonRadius = 0.16f; const float moonOrbitRadius = 1.5f; - InitWindow(screenWidth, screenHeight, "raylib [models] example - rlgl module usage with push/pop matrix transformations"); + InitWindow(screenWidth, screenHeight, "raylib [models] example - rlgl solar system"); // Define the camera to look into our 3d world Camera camera = { 0 }; From 7e597dd574d29e6e466ceaa8c6a2296bb6a9855b Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 17:07:10 +0200 Subject: [PATCH 216/242] Update rlgl_compute_shader.c --- examples/others/rlgl_compute_shader.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/others/rlgl_compute_shader.c b/examples/others/rlgl_compute_shader.c index b17ba711a..74a776d29 100644 --- a/examples/others/rlgl_compute_shader.c +++ b/examples/others/rlgl_compute_shader.c @@ -51,9 +51,12 @@ int main(void) { // Initialization //-------------------------------------------------------------------------------------- - InitWindow(GOL_WIDTH, GOL_WIDTH, "raylib [rlgl] example - compute shader - game of life"); + const int screenWidth = GOL_WIDTH; + const int screenHeight = GOL_WIDTH; - const Vector2 resolution = { GOL_WIDTH, GOL_WIDTH }; + InitWindow(screenWidth, screenHeight, "raylib [rlgl] example - compute shader - game of life"); + + const Vector2 resolution = { screenWidth, screenHeight }; unsigned int brushSize = 8; // Game of Life logic compute shader From d45b00b191662c40fb8df2977a7f50d72301ebe1 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 17:07:26 +0200 Subject: [PATCH 217/242] Update shaders_rounded_rectangle.c --- examples/shaders/shaders_rounded_rectangle.c | 22 +++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/examples/shaders/shaders_rounded_rectangle.c b/examples/shaders/shaders_rounded_rectangle.c index 2256db6c3..654fba49b 100644 --- a/examples/shaders/shaders_rounded_rectangle.c +++ b/examples/shaders/shaders_rounded_rectangle.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [shaders] example - Rounded Rectangle +* raylib [shaders] example - rounded rectangle * * Example complexity rating: [★★★☆] 3/4 * @@ -24,9 +24,8 @@ #endif //------------------------------------------------------------------------------------ -// Declare custom Structs +// Structs definition //------------------------------------------------------------------------------------ - // Rounded rectangle data typedef struct { Vector4 cornerRadius; // Individual corner radius (top-left, top-right, bottom-left, bottom-right) @@ -54,7 +53,6 @@ typedef struct { //------------------------------------------------------------------------------------ // Module Functions Declaration //------------------------------------------------------------------------------------ - // Create a rounded rectangle and set uniform locations static RoundedRectangle CreateRoundedRectangle(Vector4 cornerRadius, float shadowRadius, Vector2 shadowOffset, float shadowScale, float borderThickness, Shader shader); @@ -71,11 +69,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - const Color rectangleColor = BLUE; - const Color shadowColor = DARKBLUE; - const Color borderColor = SKYBLUE; - - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - Rounded Rectangle"); + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - rounded rectangle"); // Load the shader Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/base.vs", GLSL_VERSION), @@ -94,6 +88,10 @@ int main(void) // Update shader uniforms UpdateRoundedRectangle(roundedRectangle, shader); + const Color rectangleColor = BLUE; + const Color shadowColor = DARKBLUE; + const Color borderColor = SKYBLUE; + SetTargetFPS(60); //-------------------------------------------------------------------------------------- @@ -124,8 +122,6 @@ int main(void) DrawRectangle(0, 0, screenWidth, screenHeight, WHITE); EndShaderMode(); - - // Draw rectangle shadow using shader rec = (Rectangle){ 50, 200, 110, 60 }; DrawRectangleLines((int)rec.x - 20, (int)rec.y - 20, (int)rec.width + 40, (int)rec.height + 40, DARKGRAY); @@ -143,8 +139,6 @@ int main(void) DrawRectangle(0, 0, screenWidth, screenHeight, WHITE); EndShaderMode(); - - // Draw rectangle's border using shader rec = (Rectangle){ 50, 330, 110, 60 }; DrawRectangleLines((int)rec.x - 20, (int)rec.y - 20, (int)rec.width + 40, (int)rec.height + 40, DARKGRAY); @@ -162,8 +156,6 @@ int main(void) DrawRectangle(0, 0, screenWidth, screenHeight, WHITE); EndShaderMode(); - - // Draw one more rectangle with all three colors rec = (Rectangle){ 240, 80, 500, 300 }; DrawRectangleLines((int)rec.x - 30, (int)rec.y - 30, (int)rec.width + 60, (int)rec.height + 60, DARKGRAY); From f02c7fca8a75c55eaee7e82cb7d560df30f3146b Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 17:07:45 +0200 Subject: [PATCH 218/242] Update shapes_double_pendulum.c --- examples/shapes/shapes_double_pendulum.c | 85 +++++++++++------------- 1 file changed, 37 insertions(+), 48 deletions(-) diff --git a/examples/shapes/shapes_double_pendulum.c b/examples/shapes/shapes_double_pendulum.c index 85ca4af66..8cf0c5c68 100644 --- a/examples/shapes/shapes_double_pendulum.c +++ b/examples/shapes/shapes_double_pendulum.c @@ -22,12 +22,6 @@ //---------------------------------------------------------------------------------- // Macro Helpers //---------------------------------------------------------------------------------- -#define SCREEN_WIDTH 800 -#define SCREEN_HEIGHT 450 - -#define CENTER_X SCREEN_WIDTH * 0.5 -#define CENTER_Y SCREEN_HEIGHT * 0.5 - 100 - // Constant for Simulation #define SIMULATION_STEPS 30 #define G 9.81 @@ -45,32 +39,32 @@ int main(void) { // Initialization //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + SetConfigFlags(FLAG_WINDOW_HIGHDPI); - InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [shapes] example - Double Pendulum"); + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - double pendulum"); // Simulation Paramters - //-------------------------------------------------------------------------------------- float l1 = 15, m1 = 0.2, theta1 = DEG2RAD * 170, w1 = 0; float l2 = 15, m2 = 0.1, theta2 = DEG2RAD * 0, w2 = 0; float lengthScaler = 0.1; float totalM = m1 + m2; Vector2 previousPosition = CalculateDoublePendulumEndPoint(l1, theta1, l2, theta2); - previousPosition.x += CENTER_X; - previousPosition.y += CENTER_Y; + previousPosition.x += (screenWidth/2); + previousPosition.y += (screenHeight/2 - 100); // Scale length float L1 = l1 * lengthScaler; float L2 = l2 * lengthScaler; - // Draw Parameters - //-------------------------------------------------------------------------------------- + // Draw parameters int lineThick = 20, trailThick = 2; float fateAlpha = 0.01; - - // Create Framebuffer - //-------------------------------------------------------------------------------------- - RenderTexture2D target = LoadRenderTexture(SCREEN_WIDTH, SCREEN_HEIGHT); + + // Create framebuffer + RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight); SetTextureFilter(target.texture, TEXTURE_FILTER_BILINEAR); SetTargetFPS(60); @@ -80,60 +74,56 @@ int main(void) while (!WindowShouldClose()) // Detect window close button or ESC key { // Update + //---------------------------------------------------------------------------------- float dt = GetFrameTime(); float step = dt / SIMULATION_STEPS, step2 = step * step; // Update Physics - larger steps = better approximation - //---------------------------------------------------------------------------------- for (int i = 0; i < SIMULATION_STEPS; ++i) { float delta = theta1 - theta2; - float sinD = sin(delta), cosD = cos(delta), cos2D = cos(2 * delta); + float sinD = sinf(delta), cosD = cosf(delta), cos2D = cosf(2*delta); float ww1 = w1 * w1, ww2 = w2 * w2; // Calculate a1 - float a1 = (-G * (2 * m1 + m2) * sin(theta1) - - m2 * G * sin(theta1 - 2 * theta2) - - 2 * sinD * m2 * (ww2 * L2 + ww1 * L1 * cosD)) - / (L1 * (2 * m1 + m2 - m2 * cos2D)); + float a1 = (-G*(2*m1 + m2)*sinf(theta1) + - m2*G*sinf(theta1 - 2*theta2) + - 2*sinD*m2*(ww2*L2 + ww1*L1*cosD)) + / (L1*(2*m1 + m2 - m2*cos2D)); // Calculate a2 - float a2 = (2 * sinD * (ww1 * L1 * totalM - + G * totalM * cos(theta1) - + ww2 * L2 * m2 * cosD)) - / (L2 * (2 * m1 + m2 - m2 * cos2D)); + float a2 = (2*sinD*(ww1*L1*totalM + + G*totalM*cosf(theta1) + + ww2*L2*m2*cosD)) + / (L2*(2*m1 + m2 - m2*cos2D)); // Update thetas - theta1 += w1 * step + 0.5 * a1 * step2; - theta2 += w2 * step + 0.5 * a2 * step2; + theta1 += w1*step + 0.5f*a1*step2; + theta2 += w2*step + 0.5f*a2*step2; // Update omegas - w1 += a1 * step; - w2 += a2 * step; + w1 += a1*step; + w2 += a2*step; } - //---------------------------------------------------------------------------------- // Calculate position Vector2 currentPosition = CalculateDoublePendulumEndPoint(l1, theta1, l2, theta2); - currentPosition.x += CENTER_X; - currentPosition.y += CENTER_Y; + currentPosition.x += screenWidth/2; + currentPosition.y += screenHeight/2 - 100; - // Draw to framebuffer - //---------------------------------------------------------------------------------- + // Draw to render texture BeginTextureMode(target); - // Draw a transparent rectangle - smaller alpha = longer trails - DrawRectangle(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, Fade(BLACK, fateAlpha)); - + DrawRectangle(0, 0, screenWidth, screenHeight, Fade(BLACK, fateAlpha)); + // Draw trail DrawCircleV(previousPosition, trailThick, RED); DrawLineEx(previousPosition, currentPosition, trailThick * 2, RED); - EndTextureMode(); - //---------------------------------------------------------------------------------- // Update previous position previousPosition = currentPosition; + //---------------------------------------------------------------------------------- // Draw //---------------------------------------------------------------------------------- @@ -141,17 +131,16 @@ int main(void) ClearBackground(BLACK); - // Draw Trails Texture - DrawTextureRec(target.texture, (Rectangle){ 0, 0, target.texture.width, -target.texture.height }, - (Vector2){ 0, 0 }, WHITE); + // Draw trails texture + DrawTextureRec(target.texture, (Rectangle){ 0, 0, target.texture.width, -target.texture.height }, (Vector2){ 0, 0 }, WHITE); - // Draw Double Pendulum - DrawRectanglePro((Rectangle){ CENTER_X, CENTER_Y, 10 * l1, lineThick }, - (Vector2){0, lineThick * 0.5}, 90 - RAD2DEG * theta1, RAYWHITE); + // Draw double pendulum + DrawRectanglePro((Rectangle){ screenWidth/2, screenHeight/2 - 100, 10 * l1, lineThick }, + (Vector2){0, lineThick * 0.5}, 90 - RAD2DEG * theta1, RAYWHITE); Vector2 endpoint1 = CalculatePendulumEndPoint(l1, theta1); - DrawRectanglePro((Rectangle){ CENTER_X + endpoint1.x, CENTER_Y + endpoint1.y, 10 * l2, lineThick }, - (Vector2){0, lineThick * 0.5}, 90 - RAD2DEG * theta2, RAYWHITE); + DrawRectanglePro((Rectangle){ screenWidth/2 + endpoint1.x, screenHeight/2 - 100 + endpoint1.y, 10 * l2, lineThick }, + (Vector2){0, lineThick * 0.5}, 90 - RAD2DEG * theta2, RAYWHITE); EndDrawing(); //---------------------------------------------------------------------------------- From 9f07cfe0b7e152b3c30e435e9c82426230dccb71 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 17:08:02 +0200 Subject: [PATCH 219/242] Update textures_image_kernel.c --- examples/textures/textures_image_kernel.c | 57 ++++++++++++++--------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/examples/textures/textures_image_kernel.c b/examples/textures/textures_image_kernel.c index 456cd1c18..fa1bc92c7 100644 --- a/examples/textures/textures_image_kernel.c +++ b/examples/textures/textures_image_kernel.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [textures] example - Image loading and texture creation +* raylib [textures] example - image kernel convolution * * Example complexity rating: [★★★★] 4/4 * @@ -19,20 +19,14 @@ #include "raylib.h" +//------------------------------------------------------------------------------------ +// Module functions declaration +//------------------------------------------------------------------------------------ +void NormalizeKernel(float *kernel, int size); + //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ -void NormalizeKernel(float *kernel, int size) -{ - float sum = 0.0f; - for (int i = 0; i < size; i++) sum += kernel[i]; - - if (sum != 0.0f) - { - for (int i = 0; i < size; i++) kernel[i] /= sum; - } -} - int main(void) { // Initialization @@ -41,23 +35,26 @@ int main(void) const int screenHeight = 450; InitWindow(screenWidth, screenHeight, "raylib [textures] example - image convolution"); - - Image image = LoadImage("resources/cat.png"); // Loaded in CPU memory (RAM) - float gaussiankernel[] = { + Image image = LoadImage("resources/cat.png"); // Loaded in CPU memory (RAM) + + float gaussiankernel[] = { 1.0f, 2.0f, 1.0f, 2.0f, 4.0f, 2.0f, - 1.0f, 2.0f, 1.0f }; + 1.0f, 2.0f, 1.0f + }; float sobelkernel[] = { 1.0f, 0.0f, -1.0f, 2.0f, 0.0f, -2.0f, - 1.0f, 0.0f, -1.0f }; + 1.0f, 0.0f, -1.0f + }; float sharpenkernel[] = { 0.0f, -1.0f, 0.0f, -1.0f, 5.0f, -1.0f, - 0.0f, -1.0f, 0.0f }; + 0.0f, -1.0f, 0.0f + }; NormalizeKernel(gaussiankernel, 9); NormalizeKernel(sharpenkernel, 9); @@ -65,12 +62,12 @@ int main(void) Image catSharpend = ImageCopy(image); ImageKernelConvolution(&catSharpend, sharpenkernel, 9); - + Image catSobel = ImageCopy(image); ImageKernelConvolution(&catSobel, sobelkernel, 9); Image catGaussian = ImageCopy(image); - + for (int i = 0; i < 6; i++) { ImageKernelConvolution(&catGaussian, gaussiankernel, 9); @@ -80,14 +77,14 @@ int main(void) ImageCrop(&catGaussian, (Rectangle){ 0, 0, (float)200, (float)450 }); ImageCrop(&catSobel, (Rectangle){ 0, 0, (float)200, (float)450 }); ImageCrop(&catSharpend, (Rectangle){ 0, 0, (float)200, (float)450 }); - + // Images converted to texture, GPU memory (VRAM) Texture2D texture = LoadTextureFromImage(image); Texture2D catSharpendTexture = LoadTextureFromImage(catSharpend); Texture2D catSobelTexture = LoadTextureFromImage(catSobel); Texture2D catGaussianTexture = LoadTextureFromImage(catGaussian); - - // Once images have been converted to texture and uploaded to VRAM, + + // Once images have been converted to texture and uploaded to VRAM, // they can be unloaded from RAM UnloadImage(image); UnloadImage(catGaussian); @@ -132,3 +129,17 @@ int main(void) return 0; } + +//------------------------------------------------------------------------------------ +// Module functions definition +//------------------------------------------------------------------------------------ +static void NormalizeKernel(float *kernel, int size) +{ + float sum = 0.0f; + for (int i = 0; i < size; i++) sum += kernel[i]; + + if (sum != 0.0f) + { + for (int i = 0; i < size; i++) kernel[i] /= sum; + } +} From f0889a74fe23c44d869d2876dd19ed580d142e53 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 17:08:22 +0200 Subject: [PATCH 220/242] EXAMPLES: Format tweaks --- examples/audio/audio_mixed_processor.c | 2 +- examples/audio/audio_music_stream.c | 2 +- examples/audio/audio_sound_positioning.c | 24 ++--- examples/audio/audio_stream_effects.c | 6 +- examples/core/core_2d_camera_mouse_zoom.c | 16 +-- examples/core/core_2d_camera_platformer.c | 2 +- examples/core/core_2d_camera_split_screen.c | 24 ++--- examples/core/core_3d_camera_first_person.c | 2 +- examples/core/core_3d_camera_split_screen.c | 26 ++--- examples/core/core_automation_events.c | 28 ++--- examples/core/core_custom_frame_control.c | 26 ++--- examples/core/core_high_dpi.c | 101 ++++++++++-------- examples/core/core_input_mouse.c | 2 +- examples/core/core_input_multitouch.c | 2 +- examples/core/core_input_virtual_controls.c | 2 +- examples/core/core_loading_thread.c | 2 +- examples/core/core_random_sequence.c | 26 ++--- examples/core/core_random_values.c | 4 +- examples/core/core_smooth_pixelperfect.c | 2 +- examples/core/core_vr_simulator.c | 2 +- examples/core/core_window_letterbox.c | 2 +- examples/core/core_window_should_close.c | 8 +- examples/core/core_world_screen.c | 2 +- examples/models/models_animation.c | 4 +- examples/models/models_billboard.c | 6 +- examples/models/models_bone_socket.c | 20 ++-- examples/models/models_draw_cube_texture.c | 8 +- examples/models/models_gpu_skinning.c | 18 ++-- examples/models/models_loading_gltf.c | 2 +- examples/models/models_mesh_generation.c | 2 +- examples/models/models_mesh_picking.c | 4 +- examples/models/models_point_rendering.c | 26 ++--- examples/models/models_skybox.c | 10 +- examples/models/models_tesseract_view.c | 20 ++-- examples/models/models_waving_cubes.c | 2 +- examples/others/raylib_opengl_interop.c | 26 ++--- examples/others/raymath_vector_angle.c | 18 ++-- examples/others/rlgl_standalone.c | 2 +- examples/shaders/shaders_basic_lighting.c | 8 +- examples/shaders/shaders_basic_pbr.c | 46 ++++---- examples/shaders/shaders_custom_uniform.c | 2 +- examples/shaders/shaders_deferred_render.c | 26 ++--- examples/shaders/shaders_eratosthenes.c | 4 +- examples/shaders/shaders_hot_reloading.c | 2 +- examples/shaders/shaders_hybrid_render.c | 18 ++-- examples/shaders/shaders_julia_set.c | 2 +- examples/shaders/shaders_lightmap.c | 6 +- examples/shaders/shaders_mesh_instancing.c | 2 +- examples/shaders/shaders_multi_sample2d.c | 6 +- examples/shaders/shaders_postprocessing.c | 2 +- examples/shaders/shaders_shadowmap.c | 2 +- examples/shaders/shaders_simple_mask.c | 2 +- examples/shaders/shaders_texture_outline.c | 4 +- examples/shaders/shaders_texture_tiling.c | 10 +- .../shaders/shaders_vertex_displacement.c | 4 +- examples/shaders/shaders_view_depth.c | 6 +- examples/shaders/shaders_write_depth.c | 10 +- examples/shapes/shapes_lines_bezier.c | 2 +- examples/shapes/shapes_logo_raylib.c | 2 +- examples/shapes/shapes_rectangle_advanced.c | 14 +-- examples/shapes/shapes_rectangle_scaling.c | 2 +- examples/shapes/shapes_splines_drawing.c | 40 +++---- examples/shapes/shapes_top_down_lights.c | 16 +-- examples/text/text_codepoints_loading.c | 2 +- examples/text/text_draw_3d.c | 8 +- examples/text/text_font_filters.c | 4 +- examples/text/text_font_sdf.c | 2 +- examples/text/text_font_spritefont.c | 2 +- examples/text/text_format_text.c | 2 +- examples/text/text_input_box.c | 2 +- examples/text/text_rectangle_bounds.c | 2 +- examples/text/text_unicode.c | 4 +- examples/text/text_writing_anim.c | 2 +- .../textures/textures_background_scrolling.c | 2 +- examples/textures/textures_bunnymark.c | 2 +- examples/textures/textures_draw_tiled.c | 4 +- examples/textures/textures_fog_of_war.c | 2 +- examples/textures/textures_image_channel.c | 5 +- examples/textures/textures_image_drawing.c | 2 +- examples/textures/textures_image_generation.c | 2 +- examples/textures/textures_image_loading.c | 2 +- examples/textures/textures_image_rotate.c | 4 +- examples/textures/textures_image_text.c | 2 +- examples/textures/textures_logo_raylib.c | 2 +- examples/textures/textures_mouse_painting.c | 2 +- examples/textures/textures_polygon.c | 8 +- examples/textures/textures_raw_data.c | 2 +- examples/textures/textures_sprite_anim.c | 2 +- examples/textures/textures_srcrec_dstrec.c | 2 +- examples/textures/textures_textured_curve.c | 10 +- examples/textures/textures_to_image.c | 2 +- 91 files changed, 409 insertions(+), 397 deletions(-) diff --git a/examples/audio/audio_mixed_processor.c b/examples/audio/audio_mixed_processor.c index fc9785bc8..e761993ff 100644 --- a/examples/audio/audio_mixed_processor.c +++ b/examples/audio/audio_mixed_processor.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [audio] example - Mixed audio processing +* raylib [audio] example - mixed audio processing * * Example complexity rating: [★★★★] 4/4 * diff --git a/examples/audio/audio_music_stream.c b/examples/audio/audio_music_stream.c index 609b12333..b6efb3d2e 100644 --- a/examples/audio/audio_music_stream.c +++ b/examples/audio/audio_music_stream.c @@ -45,7 +45,7 @@ int main(void) // Update //---------------------------------------------------------------------------------- UpdateMusicStream(music); // Update music buffer with new stream data - + // Restart music playing (stop and play) if (IsKeyPressed(KEY_SPACE)) { diff --git a/examples/audio/audio_sound_positioning.c b/examples/audio/audio_sound_positioning.c index aad558672..4102a97f6 100644 --- a/examples/audio/audio_sound_positioning.c +++ b/examples/audio/audio_sound_positioning.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [audio] example - Playing spatialized 3D sound +* raylib [audio] example - spatialized 3D sound * * Example complexity rating: [★★☆☆] 2/4 * @@ -31,9 +31,9 @@ int main(void) //-------------------------------------------------------------------------------------- const int screenWidth = 800; const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [audio] example - Playing spatialized 3D sound"); - + + InitWindow(screenWidth, screenHeight, "raylib [audio] example - spatialized 3D sound"); + InitAudioDevice(); Sound sound = LoadSound("resources/coin.wav"); @@ -45,9 +45,9 @@ int main(void) .fovy = 60, .projection = CAMERA_PERSPECTIVE }; - + DisableCursor(); - + SetTargetFPS(60); //-------------------------------------------------------------------------------------- @@ -89,7 +89,7 @@ int main(void) //-------------------------------------------------------------------------------------- UnloadSound(sound); CloseAudioDevice(); // Close audio device - + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- } @@ -100,23 +100,23 @@ static void SetSoundPosition(Camera listener, Sound sound, Vector3 position, flo // Calculate direction vector and distance between listener and sound source Vector3 direction = Vector3Subtract(position, listener.position); float distance = Vector3Length(direction); - + // Apply logarithmic distance attenuation and clamp between 0-1 float attenuation = 1.0f/(1.0f + (distance/maxDist)); attenuation = Clamp(attenuation, 0.0f, 1.0f); - + // Calculate normalized vectors for spatial positioning Vector3 normalizedDirection = Vector3Normalize(direction); Vector3 forward = Vector3Normalize(Vector3Subtract(listener.target, listener.position)); Vector3 right = Vector3Normalize(Vector3CrossProduct(listener.up, forward)); - + // Reduce volume for sounds behind the listener float dotProduct = Vector3DotProduct(forward, normalizedDirection); if (dotProduct < 0.0f) attenuation *= (1.0f + dotProduct*0.5f); - + // Set stereo panning based on sound position relative to listener float pan = 0.5f + 0.5f*Vector3DotProduct(normalizedDirection, right); - + // Apply final sound properties SetSoundVolume(sound, attenuation); SetSoundPan(sound, pan); diff --git a/examples/audio/audio_stream_effects.c b/examples/audio/audio_stream_effects.c index 4a34d3030..435e7b878 100644 --- a/examples/audio/audio_stream_effects.c +++ b/examples/audio/audio_stream_effects.c @@ -53,7 +53,7 @@ int main(void) float timePlayed = 0.0f; // Time played normalized [0.0f..1.0f] bool pause = false; // Music playing paused - + bool enableEffectLPF = false; // Enable effect low-pass-filter bool enableEffectDelay = false; // Enable effect delay (1 second) @@ -98,7 +98,7 @@ int main(void) if (enableEffectDelay) AttachAudioStreamProcessor(music.stream, AudioProcessEffectDelay); else DetachAudioStreamProcessor(music.stream, AudioProcessEffectDelay); } - + // Get normalized time played for current music stream timePlayed = GetMusicTimePlayed(music)/GetMusicTimeLength(music); @@ -119,7 +119,7 @@ int main(void) DrawText("PRESS SPACE TO RESTART MUSIC", 215, 230, 20, LIGHTGRAY); DrawText("PRESS P TO PAUSE/RESUME MUSIC", 208, 260, 20, LIGHTGRAY); - + DrawText(TextFormat("PRESS F TO TOGGLE LPF EFFECT: %s", enableEffectLPF? "ON" : "OFF"), 200, 320, 20, GRAY); DrawText(TextFormat("PRESS D TO TOGGLE DELAY EFFECT: %s", enableEffectDelay? "ON" : "OFF"), 180, 350, 20, GRAY); diff --git a/examples/core/core_2d_camera_mouse_zoom.c b/examples/core/core_2d_camera_mouse_zoom.c index 31aa7bc6b..6cb6dcfa2 100644 --- a/examples/core/core_2d_camera_mouse_zoom.c +++ b/examples/core/core_2d_camera_mouse_zoom.c @@ -47,7 +47,7 @@ int main () //---------------------------------------------------------------------------------- if (IsKeyPressed(KEY_ONE)) zoomMode = 0; else if (IsKeyPressed(KEY_TWO)) zoomMode = 1; - + // Translate based on mouse right click if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { @@ -68,7 +68,7 @@ int main () // Set the offset to where the mouse is camera.offset = GetMousePosition(); - // Set the target to match, so that the camera maps the world space point + // Set the target to match, so that the camera maps the world space point // under the cursor to the screen space point under the cursor at any zoom camera.target = mouseWorldPos; @@ -89,7 +89,7 @@ int main () // Set the offset to where the mouse is camera.offset = GetMousePosition(); - // Set the target to match, so that the camera maps the world space point + // Set the target to match, so that the camera maps the world space point // under the cursor to the screen space point under the cursor at any zoom camera.target = mouseWorldPos; } @@ -111,7 +111,7 @@ int main () BeginMode2D(camera); - // Draw the 3d grid, rotated 90 degrees and centered around 0,0 + // Draw the 3d grid, rotated 90 degrees and centered around 0,0 // just so we have something in the XY plane rlPushMatrix(); rlTranslatef(0, 25*50, 0); @@ -121,19 +121,19 @@ int main () // Draw a reference circle DrawCircle(GetScreenWidth()/2, GetScreenHeight()/2, 50, MAROON); - + EndMode2D(); - + // Draw mouse reference //Vector2 mousePos = GetWorldToScreen2D(GetMousePosition(), camera) DrawCircleV(GetMousePosition(), 4, DARKGRAY); - DrawTextEx(GetFontDefault(), TextFormat("[%i, %i]", GetMouseX(), GetMouseY()), + DrawTextEx(GetFontDefault(), TextFormat("[%i, %i]", GetMouseX(), GetMouseY()), Vector2Add(GetMousePosition(), (Vector2){ -44, -24 }), 20, 2, BLACK); DrawText("[1][2] Select mouse zoom mode (Wheel or Move)", 20, 20, 20, DARKGRAY); if (zoomMode == 0) DrawText("Mouse left button drag to move, mouse wheel to zoom", 20, 50, 20, DARKGRAY); else DrawText("Mouse left button drag to move, mouse press and move to zoom", 20, 50, 20, DARKGRAY); - + EndDrawing(); //---------------------------------------------------------------------------------- } diff --git a/examples/core/core_2d_camera_platformer.c b/examples/core/core_2d_camera_platformer.c index 1ccb35167..d4d46e295 100644 --- a/examples/core/core_2d_camera_platformer.c +++ b/examples/core/core_2d_camera_platformer.c @@ -137,7 +137,7 @@ int main(void) Rectangle playerRect = { player.position.x - 20, player.position.y - 40, 40.0f, 40.0f }; DrawRectangleRec(playerRect, RED); - + DrawCircleV(player.position, 5.0f, GOLD); EndMode2D(); diff --git a/examples/core/core_2d_camera_split_screen.c b/examples/core/core_2d_camera_split_screen.c index 900db450e..169465f9d 100644 --- a/examples/core/core_2d_camera_split_screen.c +++ b/examples/core/core_2d_camera_split_screen.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★★★] 4/4 * -* Addapted from the core_3d_camera_split_screen example: +* Addapted from the core_3d_camera_split_screen example: * https://github.com/raysan5/raylib/blob/master/examples/core/core_3d_camera_split_screen.c * * Example originally created with raylib 4.5, last time updated with raylib 4.5 @@ -81,9 +81,9 @@ int main(void) //---------------------------------------------------------------------------------- BeginTextureMode(screenCamera1); ClearBackground(RAYWHITE); - + BeginMode2D(camera1); - + // Draw full scene with first camera for (int i = 0; i < screenWidth/PLAYER_SIZE + 1; i++) { @@ -106,17 +106,17 @@ int main(void) DrawRectangleRec(player1, RED); DrawRectangleRec(player2, BLUE); EndMode2D(); - + DrawRectangle(0, 0, GetScreenWidth()/2, 30, Fade(RAYWHITE, 0.6f)); DrawText("PLAYER1: W/S/A/D to move", 10, 10, 10, MAROON); - + EndTextureMode(); BeginTextureMode(screenCamera2); ClearBackground(RAYWHITE); - + BeginMode2D(camera2); - + // Draw full scene with second camera for (int i = 0; i < screenWidth/PLAYER_SIZE + 1; i++) { @@ -138,21 +138,21 @@ int main(void) DrawRectangleRec(player1, RED); DrawRectangleRec(player2, BLUE); - + EndMode2D(); - + DrawRectangle(0, 0, GetScreenWidth()/2, 30, Fade(RAYWHITE, 0.6f)); DrawText("PLAYER2: UP/DOWN/LEFT/RIGHT to move", 10, 10, 10, DARKBLUE); - + EndTextureMode(); // Draw both views render textures to the screen side by side BeginDrawing(); ClearBackground(BLACK); - + DrawTextureRec(screenCamera1.texture, splitScreenRect, (Vector2){ 0, 0 }, WHITE); DrawTextureRec(screenCamera2.texture, splitScreenRect, (Vector2){ screenWidth/2.0f, 0 }, WHITE); - + DrawRectangle(GetScreenWidth()/2 - 2, 0, 4, GetScreenHeight(), LIGHTGRAY); EndDrawing(); } diff --git a/examples/core/core_3d_camera_first_person.c b/examples/core/core_3d_camera_first_person.c index c26fe827c..42e85d0d8 100644 --- a/examples/core/core_3d_camera_first_person.c +++ b/examples/core/core_3d_camera_first_person.c @@ -127,7 +127,7 @@ int main(void) UpdateCameraPro(&camera, (Vector3){ (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP))*0.1f - // Move forward-backward - (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN))*0.1f, + (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN))*0.1f, (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT))*0.1f - // Move right-left (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT))*0.1f, 0.0f // Move up-down diff --git a/examples/core/core_3d_camera_split_screen.c b/examples/core/core_3d_camera_split_screen.c index f825a52f9..b0cb0a18d 100644 --- a/examples/core/core_3d_camera_split_screen.c +++ b/examples/core/core_3d_camera_split_screen.c @@ -51,7 +51,7 @@ int main(void) // Build a flipped rectangle the size of the split view to use for drawing later Rectangle splitScreenRect = { 0.0f, 0.0f, (float)screenPlayer1.texture.width, (float)-screenPlayer1.texture.height }; - + // Grid data int count = 5; float spacing = 4; @@ -98,9 +98,9 @@ int main(void) // Draw Player1 view to the render texture BeginTextureMode(screenPlayer1); ClearBackground(SKYBLUE); - + BeginMode3D(cameraPlayer1); - + // Draw scene: grid of cube trees on a plane to make a "world" DrawPlane((Vector3){ 0, 0, 0 }, (Vector2){ 50, 50 }, BEIGE); // Simple world plane @@ -116,20 +116,20 @@ int main(void) // Draw a cube at each player's position DrawCube(cameraPlayer1.position, 1, 1, 1, RED); DrawCube(cameraPlayer2.position, 1, 1, 1, BLUE); - + EndMode3D(); - + DrawRectangle(0, 0, GetScreenWidth()/2, 40, Fade(RAYWHITE, 0.8f)); DrawText("PLAYER1: W/S to move", 10, 10, 20, MAROON); - + EndTextureMode(); // Draw Player2 view to the render texture BeginTextureMode(screenPlayer2); ClearBackground(SKYBLUE); - + BeginMode3D(cameraPlayer2); - + // Draw scene: grid of cube trees on a plane to make a "world" DrawPlane((Vector3){ 0, 0, 0 }, (Vector2){ 50, 50 }, BEIGE); // Simple world plane @@ -145,21 +145,21 @@ int main(void) // Draw a cube at each player's position DrawCube(cameraPlayer1.position, 1, 1, 1, RED); DrawCube(cameraPlayer2.position, 1, 1, 1, BLUE); - + EndMode3D(); - + DrawRectangle(0, 0, GetScreenWidth()/2, 40, Fade(RAYWHITE, 0.8f)); DrawText("PLAYER2: UP/DOWN to move", 10, 10, 20, DARKBLUE); - + EndTextureMode(); // Draw both views render textures to the screen side by side BeginDrawing(); ClearBackground(BLACK); - + DrawTextureRec(screenPlayer1.texture, splitScreenRect, (Vector2){ 0, 0 }, WHITE); DrawTextureRec(screenPlayer2.texture, splitScreenRect, (Vector2){ screenWidth/2.0f, 0 }, WHITE); - + DrawRectangle(GetScreenWidth()/2 - 2, 0, 4, GetScreenHeight(), LIGHTGRAY); EndDrawing(); } diff --git a/examples/core/core_automation_events.c b/examples/core/core_automation_events.c index fab363d58..6ae0a4336 100644 --- a/examples/core/core_automation_events.c +++ b/examples/core/core_automation_events.c @@ -54,7 +54,7 @@ int main(void) player.position = (Vector2){ 400, 280 }; player.speed = 0; player.canJump = false; - + // Define environment elements (platforms) EnvElement envElements[MAX_ENVIRONMENT_ELEMENTS] = { {{ 0, 0, 1000, 400 }, 0, LIGHTGRAY }, @@ -70,13 +70,13 @@ int main(void) camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f }; camera.rotation = 0.0f; camera.zoom = 1.0f; - + // Automation events AutomationEventList aelist = LoadAutomationEventList(0); // Initialize list of automation events to record new events SetAutomationEventList(&aelist); bool eventRecording = false; bool eventPlaying = false; - + unsigned int frameCounter = 0; unsigned int playFrameCounter = 0; unsigned int currentPlayFrame = 0; @@ -90,7 +90,7 @@ int main(void) // Update //---------------------------------------------------------------------------------- float deltaTime = 0.015f;//GetFrameTime(); - + // Dropped files logic //---------------------------------------------------------------------------------- if (IsFileDropped()) @@ -102,14 +102,14 @@ int main(void) { UnloadAutomationEventList(aelist); aelist = LoadAutomationEventList(droppedFiles.paths[0]); - + eventRecording = false; - + // Reset scene state to play eventPlaying = true; playFrameCounter = 0; currentPlayFrame = 0; - + player.position = (Vector2){ 400, 280 }; player.speed = 0; player.canJump = false; @@ -174,7 +174,7 @@ int main(void) //---------------------------------------------------------------------------------- // Events playing - // NOTE: Logic must be before Camera update because it depends on mouse-wheel value, + // 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) @@ -228,7 +228,7 @@ int main(void) if (min.x > 0) camera.offset.x = screenWidth/2 - min.x; if (min.y > 0) camera.offset.y = screenHeight/2 - min.y; //---------------------------------------------------------------------------------- - + // Events management if (IsKeyPressed(KEY_S)) // Toggle events recording { @@ -238,12 +238,12 @@ int main(void) { StopAutomationEventRecording(); eventRecording = false; - + ExportAutomationEventList(aelist, "automation.rae"); - + TraceLog(LOG_INFO, "RECORDED FRAMES: %i", aelist.count); } - else + else { SetAutomationEventBaseFrame(180); StartAutomationEventRecording(); @@ -293,7 +293,7 @@ int main(void) DrawRectangleRec((Rectangle){ player.position.x - 20, player.position.y - 40, 40, 40 }, RED); EndMode2D(); - + // Draw game controls DrawRectangle(10, 10, 290, 145, Fade(SKYBLUE, 0.5f)); DrawRectangleLines(10, 10, 290, 145, Fade(BLUE, 0.8f)); @@ -323,7 +323,7 @@ int main(void) if (((frameCounter/15)%2) == 1) DrawText(TextFormat("PLAYING RECORDED EVENTS... [%i]", currentPlayFrame), 50, 170, 10, DARKGREEN); } - + EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/examples/core/core_custom_frame_control.c b/examples/core/core_custom_frame_control.c index 2e9595e98..ed08f8e87 100644 --- a/examples/core/core_custom_frame_control.c +++ b/examples/core/core_custom_frame_control.c @@ -39,7 +39,7 @@ int main(void) //-------------------------------------------------------------------------------------- const int screenWidth = 800; const int screenHeight = 450; - + InitWindow(screenWidth, screenHeight, "raylib [core] example - custom frame control"); // Custom timming variables @@ -48,11 +48,11 @@ int main(void) double updateDrawTime = 0.0; // Update + Draw time double waitTime = 0.0; // Wait time (if target fps required) float deltaTime = 0.0f; // Frame time (Update + Draw + Wait time) - + float timeCounter = 0.0f; // Accumulative time counter (seconds) float position = 0.0f; // Circle position bool pause = false; // Pause control flag - + int targetFPS = 60; // Our initial target fps //-------------------------------------------------------------------------------------- @@ -64,12 +64,12 @@ int main(void) #ifndef PLATFORM_WEB // NOTE: On non web platforms the PollInputEvents just works before the inputs checks PollInputEvents(); // Poll input events (SUPPORT_CUSTOM_FRAME_CONTROL) #endif - + if (IsKeyPressed(KEY_SPACE)) pause = !pause; - + if (IsKeyPressed(KEY_UP)) targetFPS += 20; else if (IsKeyPressed(KEY_DOWN)) targetFPS -= 20; - + if (targetFPS < 0) targetFPS = 0; if (!pause) @@ -91,12 +91,12 @@ int main(void) ClearBackground(RAYWHITE); for (int i = 0; i < GetScreenWidth()/200; i++) DrawRectangle(200*i, 0, 1, GetScreenHeight(), SKYBLUE); - + DrawCircle((int)position, GetScreenHeight()/2 - 25, 50, RED); - + DrawText(TextFormat("%03.0f ms", timeCounter*1000.0f), (int)position - 40, GetScreenHeight()/2 - 100, 20, MAROON); DrawText(TextFormat("PosX: %03.0f", position), (int)position - 50, GetScreenHeight()/2 + 40, 20, BLACK); - + DrawText("Circle is moving at a constant 200 pixels/sec,\nindependently of the frame rate.", 10, 10, 20, DARKGRAY); DrawText("PRESS SPACE to PAUSE MOVEMENT", 10, GetScreenHeight() - 60, 20, GRAY); DrawText("PRESS UP | DOWN to CHANGE TARGET FPS", 10, GetScreenHeight() - 30, 20, GRAY); @@ -108,18 +108,18 @@ int main(void) EndDrawing(); - // NOTE: In case raylib is configured to SUPPORT_CUSTOM_FRAME_CONTROL, + // NOTE: In case raylib is configured to SUPPORT_CUSTOM_FRAME_CONTROL, // Events polling, screen buffer swap and frame time control must be managed by the user SwapScreenBuffer(); // Flip the back buffer to screen (front buffer) - + currentTime = GetTime(); updateDrawTime = currentTime - previousTime; - + if (targetFPS > 0) // We want a fixed frame rate { waitTime = (1.0f/(float)targetFPS) - updateDrawTime; - if (waitTime > 0.0) + if (waitTime > 0.0) { WaitTime((float)waitTime); currentTime = GetTime(); diff --git a/examples/core/core_high_dpi.c b/examples/core/core_high_dpi.c index b9417bd63..05661f50e 100644 --- a/examples/core/core_high_dpi.c +++ b/examples/core/core_high_dpi.c @@ -13,12 +13,10 @@ #include "raylib.h" -static void DrawTextCenter(const char *text, int x, int y, int fontSize, Color color) -{ - Vector2 size = MeasureTextEx(GetFontDefault(), text, (float)fontSize, 3); - Vector2 pos = (Vector2){x - size.x/2, y - size.y/2 }; - DrawTextEx(GetFontDefault(), text, pos, (float)fontSize, 3, color); -} +//------------------------------------------------------------------------------------ +// Module functions declaration +//------------------------------------------------------------------------------------ +static void DrawTextCenter(const char *text, int x, int y, int fontSize, Color color); //------------------------------------------------------------------------------------ // Program main entry point @@ -31,10 +29,20 @@ int main(void) const int screenHeight = 450; SetConfigFlags(FLAG_WINDOW_HIGHDPI | FLAG_WINDOW_RESIZABLE); - InitWindow(screenWidth, screenHeight, "raylib [core] example - highdpi"); SetWindowMinSize(450, 450); + int logicalGridDescY = 120; + int logicalGridLabelY = logicalGridDescY + 30; + int logicalGridTop = logicalGridLabelY + 30; + int logicalGridBottom = logicalGridTop + 80; + int pixelGridTop = logicalGridBottom - 20; + int pixelGridBottom = pixelGridTop + 80; + int pixelGridLabelY = pixelGridBottom + 30; + int pixelGridDescY = pixelGridLabelY + 30; + int cellSize = 50; + float cellSizePx = (float)cellSize; + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -44,67 +52,60 @@ int main(void) // Update //---------------------------------------------------------------------------------- int monitorCount = GetMonitorCount(); - if (monitorCount > 1 && IsKeyPressed(KEY_N)) { - SetWindowMonitor((GetCurrentMonitor() + 1) % monitorCount); + + if ((monitorCount > 1) && IsKeyPressed(KEY_N)) + { + SetWindowMonitor((GetCurrentMonitor() + 1)%monitorCount); } + int currentMonitor = GetCurrentMonitor(); + Vector2 dpiScale = GetWindowScaleDPI(); + cellSizePx = ((float)cellSize)/dpiScale.x; + //---------------------------------------------------------------------------------- // Draw //---------------------------------------------------------------------------------- BeginDrawing(); - Vector2 dpiScale = GetWindowScaleDPI(); ClearBackground(RAYWHITE); - int windowCenter = GetScreenWidth() / 2; + int windowCenter = GetScreenWidth()/2; DrawTextCenter(TextFormat("Dpi Scale: %f", dpiScale.x), windowCenter, 30, 40, DARKGRAY); - DrawTextCenter(TextFormat("Monitor: %d/%d ([N] next monitor)", currentMonitor+1, monitorCount), windowCenter, 70, 16, LIGHTGRAY); - - const int logicalGridDescY = 120; - const int logicalGridLabelY = logicalGridDescY + 30; - const int logicalGridTop = logicalGridLabelY + 30; - const int logicalGridBottom = logicalGridTop + 80; - const int pixelGridTop = logicalGridBottom - 20; - const int pixelGridBottom = pixelGridTop + 80; - const int pixelGridLabelY = pixelGridBottom + 30; - const int pixelGridDescY = pixelGridLabelY + 30; - - const int cellSize = 50; - const float cellSizePx = ((float)cellSize) / dpiScale.x; - + DrawTextCenter(TextFormat("Monitor: %d/%d ([N] next monitor)", currentMonitor+1, monitorCount), windowCenter, 70, 20, LIGHTGRAY); DrawTextCenter(TextFormat("Window is %d \"logical points\" wide", GetScreenWidth()), windowCenter, logicalGridDescY, 20, ORANGE); + bool odd = true; - for (int i = cellSize; i < GetScreenWidth(); i += cellSize, odd = !odd) { - if (odd) { - DrawRectangle(i, logicalGridTop, cellSize, logicalGridBottom-logicalGridTop, ORANGE); - } - DrawTextCenter(TextFormat("%d", i), i, logicalGridLabelY, 12, LIGHTGRAY); + for (int i = cellSize; i < GetScreenWidth(); i += cellSize, odd = !odd) + { + if (odd) DrawRectangle(i, logicalGridTop, cellSize, logicalGridBottom-logicalGridTop, ORANGE); + + DrawTextCenter(TextFormat("%d", i), i, logicalGridLabelY, 10, LIGHTGRAY); DrawLine(i, logicalGridLabelY + 10, i, logicalGridBottom, GRAY); } odd = true; const int minTextSpace = 30; - int last_text_x = -minTextSpace; - for (int i = cellSize; i < GetRenderWidth(); i += cellSize, odd = !odd) { - int x = (int)(((float)i) / dpiScale.x); - if (odd) { - DrawRectangle(x, pixelGridTop, (int)cellSizePx, pixelGridBottom-pixelGridTop, CLITERAL(Color){ 0, 121, 241, 100 }); - } + int lastTextX = -minTextSpace; + for (int i = cellSize; i < GetRenderWidth(); i += cellSize, odd = !odd) + { + int x = (int)(((float)i)/dpiScale.x); + if (odd) DrawRectangle(x, pixelGridTop, (int)cellSizePx, pixelGridBottom - pixelGridTop, CLITERAL(Color){ 0, 121, 241, 100 }); + DrawLine(x, pixelGridTop, (int)(((float)i) / dpiScale.x), pixelGridLabelY - 10, GRAY); - if (x - last_text_x >= minTextSpace) { - DrawTextCenter(TextFormat("%d", i), x, pixelGridLabelY, 12, LIGHTGRAY); - last_text_x = x; + + if ((x - lastTextX) >= minTextSpace) + { + DrawTextCenter(TextFormat("%d", i), x, pixelGridLabelY, 10, LIGHTGRAY); + lastTextX = x; } } DrawTextCenter(TextFormat("Window is %d \"physical pixels\" wide", GetRenderWidth()), windowCenter, pixelGridDescY, 20, BLUE); - { - const char *text = "Can you see this?"; - Vector2 size = MeasureTextEx(GetFontDefault(), text, 16, 3); - Vector2 pos = (Vector2){GetScreenWidth() - size.x - 5, GetScreenHeight() - size.y - 5}; - DrawTextEx(GetFontDefault(), text, pos, 16, 3, LIGHTGRAY); - } + const char *text = "Can you see this?"; + Vector2 size = MeasureTextEx(GetFontDefault(), text, 20, 3); + Vector2 pos = (Vector2){ GetScreenWidth() - size.x - 5, GetScreenHeight() - size.y - 5 }; + DrawTextEx(GetFontDefault(), text, pos, 20, 3, LIGHTGRAY); EndDrawing(); //---------------------------------------------------------------------------------- @@ -117,3 +118,13 @@ int main(void) return 0; } + +//------------------------------------------------------------------------------------ +// Module functions definition +//------------------------------------------------------------------------------------ +static void DrawTextCenter(const char *text, int x, int y, int fontSize, Color color) +{ + Vector2 size = MeasureTextEx(GetFontDefault(), text, (float)fontSize, 3); + Vector2 pos = (Vector2){ x - size.x/2, y - size.y/2 }; + DrawTextEx(GetFontDefault(), text, pos, (float)fontSize, 3, color); +} diff --git a/examples/core/core_input_mouse.c b/examples/core/core_input_mouse.c index 6091ded45..58c1c4035 100644 --- a/examples/core/core_input_mouse.c +++ b/examples/core/core_input_mouse.c @@ -52,7 +52,7 @@ int main(void) isCursorHidden = 0; } } - + ballPosition = GetMousePosition(); if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) ballColor = MAROON; diff --git a/examples/core/core_input_multitouch.c b/examples/core/core_input_multitouch.c index 55015235c..136c603cc 100644 --- a/examples/core/core_input_multitouch.c +++ b/examples/core/core_input_multitouch.c @@ -54,7 +54,7 @@ int main(void) BeginDrawing(); ClearBackground(RAYWHITE); - + for (int i = 0; i < tCount; ++i) { // Make sure point is not (0, 0) as this means there is no touch for it diff --git a/examples/core/core_input_virtual_controls.c b/examples/core/core_input_virtual_controls.c index 80d9f0b3b..6a4946655 100644 --- a/examples/core/core_input_virtual_controls.c +++ b/examples/core/core_input_virtual_controls.c @@ -7,7 +7,7 @@ * Example originally created with raylib 5.0, last time updated with raylib 5.0 * * Example create by GreenSnakeLinux (@GreenSnakeLinux), -* lighter by oblerion (@oblerion) and +* lighter by oblerion (@oblerion) and * reviewed by Ramon Santamaria (@raysan5) and * improved by danilwhale (@danilwhale) * diff --git a/examples/core/core_loading_thread.c b/examples/core/core_loading_thread.c index cd3d5a744..1fa84640d 100644 --- a/examples/core/core_loading_thread.c +++ b/examples/core/core_loading_thread.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★★☆] 3/4 * -* NOTE: This example requires linking with pthreads library on MinGW, +* NOTE: This example requires linking with pthreads library on MinGW, * it can be accomplished passing -static parameter to compiler * * Example originally created with raylib 2.5, last time updated with raylib 3.0 diff --git a/examples/core/core_random_sequence.c b/examples/core/core_random_sequence.c index 1aa0a068c..b92eaba88 100644 --- a/examples/core/core_random_sequence.c +++ b/examples/core/core_random_sequence.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [core] example - Generates a random sequence +* raylib [core] example - generate random sequence * * Example complexity rating: [★☆☆☆] 1/4 * @@ -43,7 +43,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - Generates a random sequence"); + InitWindow(screenWidth, screenHeight, "raylib [core] example - generate random sequence"); int rectCount = 20; float rectSize = (float)screenWidth/rectCount; @@ -118,8 +118,8 @@ int main(void) //------------------------------------------------------------------------------------ static Color GenerateRandomColor() { - Color color = { - GetRandomValue(0, 255), + Color color = { + GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 @@ -138,20 +138,20 @@ static ColorRect *GenerateRandomColorRectSequence(float rectCount, float rectWid for (int i = 0; i < rectCount; i++) { int rectHeight = (int)Remap((float)seq[i], 0, rectCount - 1, 0, screenHeight); - + rectangles[i].c = GenerateRandomColor(); rectangles[i].r = CLITERAL(Rectangle){ startX + i*rectWidth, screenHeight - rectHeight, rectWidth, (float)rectHeight }; } - + UnloadRandomSequence(seq); - + return rectangles; } static void ShuffleColorRectSequence(ColorRect *rectangles, int rectCount) { int *seq = LoadRandomSequence(rectCount, 0, rectCount - 1); - + for (int i1 = 0; i1 < rectCount; i1++) { ColorRect *r1 = &rectangles[i1]; @@ -166,16 +166,16 @@ static void ShuffleColorRectSequence(ColorRect *rectangles, int rectCount) r2->r.height = tmp.r.height; r2->r.y = tmp.r.y; } - + UnloadRandomSequence(seq); } static void DrawTextCenterKeyHelp(const char *key, const char *text, int posX, int posY, int fontSize, Color color) { - int spaceSize = MeasureText(" ", fontSize); - int pressSize = MeasureText("Press", fontSize); - int keySize = MeasureText(key, fontSize); - int textSize = MeasureText(text, fontSize); + int spaceSize = MeasureText(" ", fontSize); + int pressSize = MeasureText("Press", fontSize); + int keySize = MeasureText(key, fontSize); + int textSize = MeasureText(text, fontSize); int textSizeCurrent = 0; DrawText("Press", posX, posY, fontSize, color); diff --git a/examples/core/core_random_values.c b/examples/core/core_random_values.c index 4abc87694..16b89e700 100644 --- a/examples/core/core_random_values.c +++ b/examples/core/core_random_values.c @@ -30,9 +30,9 @@ int main(void) // SetRandomSeed(0xaabbccff); // Set a custom random seed if desired, by default: "time(NULL)" int randValue = GetRandomValue(-8, 5); // Get a random integer number between -8 and 5 (both included) - + unsigned int framesCounter = 0; // Variable used to count frames - + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/core/core_smooth_pixelperfect.c b/examples/core/core_smooth_pixelperfect.c index 964bacdb3..0bd82db43 100644 --- a/examples/core/core_smooth_pixelperfect.c +++ b/examples/core/core_smooth_pixelperfect.c @@ -5,7 +5,7 @@ * Example complexity rating: [★★★☆] 3/4 * * Example originally created with raylib 3.7, last time updated with raylib 4.0 -* +* * Example contributed by Giancamillo Alessandroni (@NotManyIdeasDev) and * reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/core/core_vr_simulator.c b/examples/core/core_vr_simulator.c index 43b1ec471..34b556e99 100644 --- a/examples/core/core_vr_simulator.c +++ b/examples/core/core_vr_simulator.c @@ -127,7 +127,7 @@ int main(void) EndMode3D(); EndVrStereoMode(); EndTextureMode(); - + BeginDrawing(); ClearBackground(RAYWHITE); BeginShaderMode(distortion); diff --git a/examples/core/core_window_letterbox.c b/examples/core/core_window_letterbox.c index b3622c8b9..a46e9eec6 100644 --- a/examples/core/core_window_letterbox.c +++ b/examples/core/core_window_letterbox.c @@ -86,7 +86,7 @@ int main(void) DrawText(TextFormat("Default Mouse: [%i , %i]", (int)mouse.x, (int)mouse.y), 350, 25, 20, GREEN); DrawText(TextFormat("Virtual Mouse: [%i , %i]", (int)virtualMouse.x, (int)virtualMouse.y), 350, 55, 20, YELLOW); EndTextureMode(); - + BeginDrawing(); ClearBackground(BLACK); // Clear screen background diff --git a/examples/core/core_window_should_close.c b/examples/core/core_window_should_close.c index 56561795d..274891ef3 100644 --- a/examples/core/core_window_should_close.c +++ b/examples/core/core_window_should_close.c @@ -26,9 +26,9 @@ int main() const int screenHeight = 450; InitWindow(screenWidth, screenHeight, "raylib [core] example - window should close"); - + SetExitKey(KEY_NULL); // Disable KEY_ESCAPE to close window, X-button still works - + bool exitWindowRequested = false; // Flag to request window to exit bool exitWindow = false; // Flag to set window to exit @@ -42,12 +42,12 @@ int main() //---------------------------------------------------------------------------------- // Detect if X-button or KEY_ESCAPE have been pressed to close window if (WindowShouldClose() || IsKeyPressed(KEY_ESCAPE)) exitWindowRequested = true; - + if (exitWindowRequested) { // A request for close window has been issued, we can save data before closing // or just show a message asking for confirmation - + if (IsKeyPressed(KEY_Y)) exitWindow = true; else if (IsKeyPressed(KEY_N)) exitWindowRequested = false; } diff --git a/examples/core/core_world_screen.c b/examples/core/core_world_screen.c index f302c3b27..6e065ca34 100644 --- a/examples/core/core_world_screen.c +++ b/examples/core/core_world_screen.c @@ -70,7 +70,7 @@ int main(void) EndMode3D(); DrawText("Enemy: 100 / 100", (int)cubeScreenPosition.x - MeasureText("Enemy: 100/100", 20)/2, (int)cubeScreenPosition.y, 20, BLACK); - + DrawText(TextFormat("Cube position in screen space coordinates: [%i, %i]", (int)cubeScreenPosition.x, (int)cubeScreenPosition.y), 10, 10, 20, LIME); DrawText("Text 2d should be always on top of the cube", 10, 40, 20, GRAY); diff --git a/examples/models/models_animation.c b/examples/models/models_animation.c index b334e17ea..a0f514ad2 100644 --- a/examples/models/models_animation.c +++ b/examples/models/models_animation.c @@ -15,8 +15,8 @@ * ******************************************************************************************** * -* NOTE: To export a model from blender, make sure it is not posed, the vertices need to be -* in the same position as they would be in edit mode and the scale of your models is +* NOTE: To export a model from blender, make sure it is not posed, the vertices need to be +* in the same position as they would be in edit mode and the scale of your models is * set to 0. Scaling can be done from the export menu. * ********************************************************************************************/ diff --git a/examples/models/models_billboard.c b/examples/models/models_billboard.c index 1d49ab89e..84c02ea23 100644 --- a/examples/models/models_billboard.c +++ b/examples/models/models_billboard.c @@ -85,17 +85,17 @@ int main(void) DrawGrid(10, 1.0f); // Draw a grid // Draw order matters! - if (distanceStatic > distanceRotating) + if (distanceStatic > distanceRotating) { DrawBillboard(camera, bill, billPositionStatic, 2.0f, WHITE); DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, size, origin, rotation, WHITE); - } + } else { DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, size, origin, rotation, WHITE); DrawBillboard(camera, bill, billPositionStatic, 2.0f, WHITE); } - + EndMode3D(); DrawFPS(10, 10); diff --git a/examples/models/models_bone_socket.c b/examples/models/models_bone_socket.c index ceea21b8c..cdad46738 100644 --- a/examples/models/models_bone_socket.c +++ b/examples/models/models_bone_socket.c @@ -3,7 +3,7 @@ * raylib [core] example - Using bones as socket for calculating the positioning of something * * Example complexity rating: [★★★★] 4/4 -* +* * Example originally created with raylib 4.5, last time updated with raylib 4.5 * * Example contributed by iP (@ipzaur) and reviewed by Ramon Santamaria (@raysan5) @@ -51,7 +51,7 @@ int main(void) LoadModel("resources/models/gltf/greenman_sword.glb"), // Index for the sword model is the same as BONE_SOCKET_HAND_R LoadModel("resources/models/gltf/greenman_shield.glb") // Index for the shield model is the same as BONE_SOCKET_HAND_L }; - + bool showEquip[3] = { true, true, true }; // Toggle on/off equip // Load gltf model animations @@ -63,7 +63,7 @@ int main(void) // indices of bones for sockets int boneSocketIndex[BONE_SOCKETS] = { -1, -1, -1 }; - // search bones for sockets + // search bones for sockets for (int i = 0; i < characterModel.boneCount; i++) { if (TextIsEqual(characterModel.bones[i].name, "socket_hat")) @@ -71,13 +71,13 @@ int main(void) boneSocketIndex[BONE_SOCKET_HAT] = i; continue; } - + if (TextIsEqual(characterModel.bones[i].name, "socket_hand_R")) { boneSocketIndex[BONE_SOCKET_HAND_R] = i; continue; } - + if (TextIsEqual(characterModel.bones[i].name, "socket_hand_L")) { boneSocketIndex[BONE_SOCKET_HAND_L] = i; @@ -99,7 +99,7 @@ int main(void) // Update //---------------------------------------------------------------------------------- UpdateCamera(&camera, CAMERA_THIRD_PERSON); - + // Rotate character if (IsKeyDown(KEY_F)) angle = (angle + 1)%360; else if (IsKeyDown(KEY_H)) angle = (360 + angle - 1)%360; @@ -112,7 +112,7 @@ int main(void) if (IsKeyPressed(KEY_ONE)) showEquip[BONE_SOCKET_HAT] = !showEquip[BONE_SOCKET_HAT]; if (IsKeyPressed(KEY_TWO)) showEquip[BONE_SOCKET_HAND_R] = !showEquip[BONE_SOCKET_HAND_R]; if (IsKeyPressed(KEY_THREE)) showEquip[BONE_SOCKET_HAND_L] = !showEquip[BONE_SOCKET_HAND_L]; - + // Update model animation ModelAnimation anim = modelAnimations[animIndex]; animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount; @@ -140,7 +140,7 @@ int main(void) Transform *transform = &anim.framePoses[animCurrentFrame][boneSocketIndex[i]]; Quaternion inRotation = characterModel.bindPose[boneSocketIndex[i]].rotation; Quaternion outRotation = transform->rotation; - + // Calculate socket rotation (angle between bone in initial pose and same bone in current animation frame) Quaternion rotate = QuaternionMultiply(outRotation, QuaternionInvert(inRotation)); Matrix matrixTransform = QuaternionToMatrix(rotate); @@ -148,7 +148,7 @@ int main(void) matrixTransform = MatrixMultiply(matrixTransform, MatrixTranslate(transform->translation.x, transform->translation.y, transform->translation.z)); // Transform the socket using the transform of the character (angle and translate) matrixTransform = MatrixMultiply(matrixTransform, characterModel.transform); - + // Draw mesh at socket position with socket angle rotation DrawMesh(equipModel[i].meshes[0], equipModel[i].materials[1], matrixTransform); } @@ -168,7 +168,7 @@ int main(void) //-------------------------------------------------------------------------------------- UnloadModelAnimations(modelAnimations, animsCount); UnloadModel(characterModel); // Unload character model and meshes/material - + // Unload equipment model and meshes/material for (int i = 0; i < BONE_SOCKETS; i++) UnloadModel(equipModel[i]); diff --git a/examples/models/models_draw_cube_texture.c b/examples/models/models_draw_cube_texture.c index 172a50394..650f80aee 100644 --- a/examples/models/models_draw_cube_texture.c +++ b/examples/models/models_draw_cube_texture.c @@ -42,7 +42,7 @@ int main(void) camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; camera.fovy = 45.0f; camera.projection = CAMERA_PERSPECTIVE; - + // Load texture to be applied to the cubes sides Texture2D texture = LoadTexture("resources/cubicmap_atlas.png"); @@ -69,7 +69,7 @@ int main(void) DrawCubeTexture(texture, (Vector3){ -2.0f, 2.0f, 0.0f }, 2.0f, 4.0f, 2.0f, WHITE); // Draw cube with an applied texture, but only a defined rectangle piece of the texture - DrawCubeTextureRec(texture, (Rectangle){ 0.0f, texture.height/2.0f, texture.width/2.0f, texture.height/2.0f }, + DrawCubeTextureRec(texture, (Rectangle){ 0.0f, texture.height/2.0f, texture.width/2.0f, texture.height/2.0f }, (Vector3){ 2.0f, 1.0f, 0.0f }, 2.0f, 2.0f, 2.0f, WHITE); DrawGrid(10, 1.0f); // Draw a grid @@ -85,7 +85,7 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- UnloadTexture(texture); // Unload texture - + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- @@ -171,7 +171,7 @@ void DrawCubeTextureRec(Texture2D texture, Rectangle source, Vector3 position, f rlSetTexture(texture.id); // We calculate the normalized texture coordinates for the desired texture-source-rectangle - // It means converting from (tex.width, tex.height) coordinates to [0.0f, 1.0f] equivalent + // It means converting from (tex.width, tex.height) coordinates to [0.0f, 1.0f] equivalent rlBegin(RL_QUADS); rlColor4ub(color.r, color.g, color.b, color.a); diff --git a/examples/models/models_gpu_skinning.c b/examples/models/models_gpu_skinning.c index c268d25d1..d1b8c097a 100644 --- a/examples/models/models_gpu_skinning.c +++ b/examples/models/models_gpu_skinning.c @@ -3,7 +3,7 @@ * raylib [core] example - Doing skinning on the gpu using a vertex shader * * Example complexity rating: [★★★☆] 3/4 -* +* * Example originally created with raylib 4.5, last time updated with raylib 4.5 * * Example contributed by Daniel Holden (@orangeduck) and reviewed by Ramon Santamaria (@raysan5) @@ -12,7 +12,7 @@ * BSD-like license that allows static linking with closed source software * * Copyright (c) 2024-2025 Daniel Holden (@orangeduck) -* +* * Note: Due to limitations in the Apple OpenGL driver, this feature does not work on MacOS * ********************************************************************************************/ @@ -49,13 +49,13 @@ int main(void) // Load gltf model Model characterModel = LoadModel("resources/models/gltf/greenman.glb"); // Load character model - + // Load skinning shader Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION), TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION)); - + characterModel.materials[1].shader = skinningShader; - + // Load gltf model animations int animsCount = 0; unsigned int animIndex = 0; @@ -75,7 +75,7 @@ int main(void) // Update //---------------------------------------------------------------------------------- UpdateCamera(&camera, CAMERA_THIRD_PERSON); - + // Select current animation if (IsKeyPressed(KEY_T)) animIndex = (animIndex + 1)%animsCount; else if (IsKeyPressed(KEY_G)) animIndex = (animIndex + animsCount - 1)%animsCount; @@ -94,12 +94,12 @@ int main(void) ClearBackground(RAYWHITE); BeginMode3D(camera); - + // Draw character mesh, pose calculation is done in shader (GPU skinning) DrawMesh(characterModel.meshes[0], characterModel.materials[1], characterModel.transform); DrawGrid(10, 1.0f); - + EndMode3D(); DrawText("Use the T/G to switch animation", 10, 10, 20, GRAY); @@ -113,7 +113,7 @@ int main(void) UnloadModelAnimations(modelAnimations, animsCount); // Unload model animation UnloadModel(characterModel); // Unload model and meshes/material UnloadShader(skinningShader); // Unload GPU skinning shader - + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_loading_gltf.c b/examples/models/models_loading_gltf.c index 6309af84c..e3a5b5bcb 100644 --- a/examples/models/models_loading_gltf.c +++ b/examples/models/models_loading_gltf.c @@ -45,7 +45,7 @@ int main(void) // Load gltf model Model model = LoadModel("resources/models/gltf/robot.glb"); Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position - + // Load gltf model animations int animsCount = 0; unsigned int animIndex = 0; diff --git a/examples/models/models_mesh_generation.c b/examples/models/models_mesh_generation.c index e268085b0..828c9809d 100644 --- a/examples/models/models_mesh_generation.c +++ b/examples/models/models_mesh_generation.c @@ -47,7 +47,7 @@ int main(void) models[6] = LoadModelFromMesh(GenMeshKnot(1.0f, 2.0f, 16, 128)); models[7] = LoadModelFromMesh(GenMeshPoly(5, 2.0f)); models[8] = LoadModelFromMesh(GenMeshCustom()); - + // NOTE: Generated meshes could be exported using ExportMesh() // Set checked texture as default diffuse component for all models material diff --git a/examples/models/models_mesh_picking.c b/examples/models/models_mesh_picking.c index f6fffd907..6e2c03473 100644 --- a/examples/models/models_mesh_picking.c +++ b/examples/models/models_mesh_picking.c @@ -137,7 +137,7 @@ int main(void) RayCollision meshHitInfo = { 0 }; for (int m = 0; m < tower.meshCount; m++) { - // NOTE: We consider the model.transform for the collision check but + // NOTE: We consider the model.transform for the collision check but // it can be checked against any transform Matrix, used when checking against same // model drawn multiple times with multiple transforms meshHitInfo = GetRayCollisionMesh(ray, tower.meshes[m], tower.transform); @@ -145,7 +145,7 @@ int main(void) { // Save the closest hit mesh if ((!collision.hit) || (collision.distance > meshHitInfo.distance)) collision = meshHitInfo; - + break; // Stop once one mesh collision is detected, the colliding mesh is m } } diff --git a/examples/models/models_point_rendering.c b/examples/models/models_point_rendering.c index b7bf96742..bbe335bb2 100644 --- a/examples/models/models_point_rendering.c +++ b/examples/models/models_point_rendering.c @@ -35,7 +35,7 @@ int main() //-------------------------------------------------------------------------------------- const int screenWidth = 800; const int screenHeight = 450; - + InitWindow(screenWidth, screenHeight, "raylib [models] example - point rendering"); Camera camera = { @@ -50,10 +50,10 @@ int main() bool useDrawModelPoints = true; bool numPointsChanged = false; int numPoints = 1000; - + Mesh mesh = GenMeshPoints(numPoints); Model model = LoadModelFromMesh(mesh); - + //SetTargetFPS(60); //-------------------------------------------------------------------------------------- @@ -114,14 +114,14 @@ int main() .b = mesh.colors[i*4 + 2], .a = mesh.colors[i*4 + 3], }; - + DrawPoint3D(pos, color); } } // Draw a unit sphere for reference DrawSphereWires(position, 1.0f, 10, 10, YELLOW); - + EndMode3D(); // Draw UI text @@ -129,12 +129,12 @@ int main() DrawText("Up - increase points", 20, 70, 20, WHITE); DrawText("Down - decrease points", 20, 100, 20, WHITE); DrawText("Space - drawing function", 20, 130, 20, WHITE); - + if (useDrawModelPoints) DrawText("Using: DrawModelPoints()", 20, 160, 20, GREEN); else DrawText("Using: DrawPoint3D()", 20, 160, 20, RED); - + DrawFPS(10, 10); - + EndDrawing(); //---------------------------------------------------------------------------------- } @@ -151,7 +151,7 @@ int main() // Generate a spherical point cloud static Mesh GenMeshPoints(int numPoints) { - Mesh mesh = { + Mesh mesh = { .triangleCount = 1, .vertexCount = numPoints, .vertices = (float *)MemAlloc(numPoints*3*sizeof(float)), @@ -164,13 +164,13 @@ static Mesh GenMeshPoints(int numPoints) float theta = ((float)PI*rand())/RAND_MAX; float phi = (2.0f*PI*rand())/RAND_MAX; float r = (10.0f*rand())/RAND_MAX; - + mesh.vertices[i*3 + 0] = r*sinf(theta)*cosf(phi); mesh.vertices[i*3 + 1] = r*sinf(theta)*sinf(phi); mesh.vertices[i*3 + 2] = r*cosf(theta); - + Color color = ColorFromHSV(r*360.0f, 1.0f, 1.0f); - + mesh.colors[i*4 + 0] = color.r; mesh.colors[i*4 + 1] = color.g; mesh.colors[i*4 + 2] = color.b; @@ -179,6 +179,6 @@ static Mesh GenMeshPoints(int numPoints) // Upload mesh data from CPU (RAM) to GPU (VRAM) memory UploadMesh(&mesh, false); - + return mesh; } diff --git a/examples/models/models_skybox.c b/examples/models/models_skybox.c index e816b27e1..a5304a93d 100644 --- a/examples/models/models_skybox.c +++ b/examples/models/models_skybox.c @@ -70,7 +70,7 @@ int main(void) SetShaderValue(shdrCubemap, GetShaderLocation(shdrCubemap, "equirectangularMap"), (int[1]){ 0 }, SHADER_UNIFORM_INT); char skyboxFileName[256] = { 0 }; - + if (useHDR) { TextCopy(skyboxFileName, "resources/dresden_square_2k.hdr"); @@ -116,7 +116,7 @@ int main(void) { // Unload current cubemap texture to load new one UnloadTexture(skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture); - + if (useHDR) { // Load HDR panorama (sphere) texture @@ -124,7 +124,7 @@ int main(void) // Generate cubemap from panorama texture skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = GenTextureCubemap(shdrCubemap, panorama, 1024, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8); - + UnloadTexture(panorama); // Texture not required anymore, cubemap already generated } else @@ -223,7 +223,7 @@ static TextureCubemap GenTextureCubemap(Shader shader, Texture2D panorama, int s }; rlViewport(0, 0, size, size); // Set viewport to current fbo dimensions - + // Activate and enable texture for drawing to cubemap faces rlActiveTextureSlot(0); rlEnableTexture(panorama.id); @@ -232,7 +232,7 @@ static TextureCubemap GenTextureCubemap(Shader shader, Texture2D panorama, int s { // Set the view matrix for the current cube face rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_VIEW], fboViews[i]); - + // Select the current cubemap face attachment for the fbo // WARNING: This function by default enables->attach->disables fbo!!! rlFramebufferAttach(fbo, cubemap.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X + i, 0); diff --git a/examples/models/models_tesseract_view.c b/examples/models/models_tesseract_view.c index 44484ca5b..661d078b4 100644 --- a/examples/models/models_tesseract_view.c +++ b/examples/models/models_tesseract_view.c @@ -32,7 +32,7 @@ int main(void) const int screenHeight = 450; InitWindow(screenWidth, screenHeight, "raylib [models] example - tesseract view"); - + // Define the camera to look into our 3d world Camera camera = { 0 }; camera.position = (Vector3){ 4.0f, 4.0f, 4.0f }; // Camera position @@ -43,16 +43,16 @@ int main(void) // Find the coordinates by setting XYZW to +-1 Vector4 tesseract[16] = { - { 1, 1, 1, 1 }, { 1, 1, 1, -1 }, + { 1, 1, 1, 1 }, { 1, 1, 1, -1 }, { 1, 1, -1, 1 }, { 1, 1, -1, -1 }, - { 1, -1, 1, 1 }, { 1, -1, 1, -1 }, + { 1, -1, 1, 1 }, { 1, -1, 1, -1 }, { 1, -1, -1, 1 }, { 1, -1, -1, -1 }, - { -1, 1, 1, 1 }, { -1, 1, 1, -1 }, + { -1, 1, 1, 1 }, { -1, 1, 1, -1 }, { -1, 1, -1, 1 }, { -1, 1, -1, -1 }, - { -1, -1, 1, 1 }, { -1, -1, 1, -1 }, + { -1, -1, 1, 1 }, { -1, -1, 1, -1 }, { -1, -1, -1, 1 }, { -1, -1, -1, -1 }, }; - + float rotation = 0.0f; Vector3 transformed[16] = { 0 }; float wValues[16] = { 0 }; @@ -66,7 +66,7 @@ int main(void) // Update //---------------------------------------------------------------------------------- rotation = DEG2RAD*45.0f*GetTime(); - + for (int i = 0; i < 16; i++) { Vector4 p = tesseract[i]; @@ -92,9 +92,9 @@ int main(void) // Draw //---------------------------------------------------------------------------------- BeginDrawing(); - + ClearBackground(RAYWHITE); - + BeginMode3D(camera); for (int i = 0; i < 16; i++) { @@ -114,7 +114,7 @@ int main(void) } } EndMode3D(); - + EndDrawing(); //---------------------------------------------------------------------------------- } diff --git a/examples/models/models_waving_cubes.c b/examples/models/models_waving_cubes.c index 6608eba48..455d85e18 100644 --- a/examples/models/models_waving_cubes.c +++ b/examples/models/models_waving_cubes.c @@ -91,7 +91,7 @@ int main() }; // Pick a color with a hue depending on cube position for the rainbow color effect - // NOTE: This function is quite costly to be done per cube and frame, + // NOTE: This function is quite costly to be done per cube and frame, // pre-catching the results into a separate array could improve performance Color cubeColor = ColorFromHSV((float)(((x + y + z)*18)%360), 0.75f, 0.9f); diff --git a/examples/others/raylib_opengl_interop.c b/examples/others/raylib_opengl_interop.c index 9ab2bb220..53fc744db 100644 --- a/examples/others/raylib_opengl_interop.c +++ b/examples/others/raylib_opengl_interop.c @@ -30,18 +30,18 @@ #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL) #if defined(GRAPHICS_API_OPENGL_ES2) - #include "glad_gles2.h" // Required for: OpenGL functionality + #include "glad_gles2.h" // Required for: OpenGL functionality #define glGenVertexArrays glGenVertexArraysOES #define glBindVertexArray glBindVertexArrayOES #define glDeleteVertexArrays glDeleteVertexArraysOES #define GLSL_VERSION 100 #else #if defined(__APPLE__) - #define GL_SILENCE_DEPRECATION // Silence Opengl API deprecation warnings + #define GL_SILENCE_DEPRECATION // Silence Opengl API deprecation warnings #include // OpenGL 3 library for OSX #include // OpenGL 3 extensions library for OSX #else - #include "glad.h" // Required for: OpenGL functionality + #include "glad.h" // Required for: OpenGL functionality #endif #define GLSL_VERSION 330 #endif @@ -71,7 +71,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib - point particles"); + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - point particles"); Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/point_particle.vs", GLSL_VERSION), TextFormat("resources/shaders/glsl%i/point_particle.fs", GLSL_VERSION)); @@ -86,13 +86,13 @@ int main(void) { particles[i].x = (float)GetRandomValue(20, screenWidth - 20); particles[i].y = (float)GetRandomValue(50, screenHeight - 20); - - // Give each particle a slightly different period. But don't spread it to much. + + // Give each particle a slightly different period. But don't spread it to much. // This way the particles line up every so often and you get a glimps of what is going on. particles[i].period = (float)GetRandomValue(10, 30)/10.0f; } - // Create a plain OpenGL vertex buffer with the data and an vertex array object + // Create a plain OpenGL vertex buffer with the data and an vertex array object // that feeds the data from the buffer into the vertexPosition shader attribute. GLuint vao = 0; GLuint vbo = 0; @@ -125,13 +125,13 @@ int main(void) DrawRectangle(10, 10, 210, 30, MAROON); DrawText(TextFormat("%zu particles in one vertex buffer", MAX_PARTICLES), 20, 20, 10, RAYWHITE); - + rlDrawRenderBatchActive(); // Draw iternal buffers data (previous draw calls) // Switch to plain OpenGL //------------------------------------------------------------------------------ glUseProgram(shader.id); - + glUniform1f(currentTimeLoc, GetTime()); Vector4 color = ColorNormalize((Color){ 255, 0, 0, 128 }); @@ -139,18 +139,18 @@ int main(void) // Get the current modelview and projection matrix so the particle system is displayed and transformed Matrix modelViewProjection = MatrixMultiply(rlGetMatrixModelview(), rlGetMatrixProjection()); - + glUniformMatrix4fv(shader.locs[SHADER_LOC_MATRIX_MVP], 1, false, MatrixToFloat(modelViewProjection)); glBindVertexArray(vao); glDrawArrays(GL_POINTS, 0, MAX_PARTICLES); glBindVertexArray(0); - + glUseProgram(0); //------------------------------------------------------------------------------ - + DrawFPS(screenWidth - 100, 10); - + EndDrawing(); //---------------------------------------------------------------------------------- } diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index 414546ac6..1d49cec66 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -12,7 +12,7 @@ * Copyright (c) 2023-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ - + #include "raylib.h" #include "raymath.h" @@ -32,7 +32,7 @@ int main(void) Vector2 v0 = { screenWidth/2, screenHeight/2 }; Vector2 v1 = Vector2Add(v0, (Vector2){ 100.0f, 80.0f }); Vector2 v2 = { 0 }; // Updated with mouse position - + float angle = 0.0f; // Angle in degrees int angleMode = 0; // 0-Vector2Angle(), 1-Vector2LineAngle() @@ -47,12 +47,12 @@ int main(void) float startangle = 0.0f; if (angleMode == 0) startangle = -Vector2LineAngle(v0, v1)*RAD2DEG; - if (angleMode == 1) startangle = 0.0f; + if (angleMode == 1) startangle = 0.0f; v2 = GetMousePosition(); if (IsKeyPressed(KEY_SPACE)) angleMode = !angleMode; - + if ((angleMode == 0) && IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) v1 = GetMousePosition(); if (angleMode == 0) @@ -75,12 +75,12 @@ int main(void) BeginDrawing(); ClearBackground(RAYWHITE); - + if (angleMode == 0) { DrawText("MODE 0: Angle between V1 and V2", 10, 10, 20, BLACK); DrawText("Right Click to Move V2", 10, 30, 20, DARKGRAY); - + DrawLineEx(v0, v1, 2.0f, BLACK); DrawLineEx(v0, v2, 2.0f, RED); @@ -89,13 +89,13 @@ int main(void) else if (angleMode == 1) { DrawText("MODE 1: Angle formed by line V1 to V2", 10, 10, 20, BLACK); - + DrawLine(0, screenHeight/2, screenWidth, screenHeight/2, LIGHTGRAY); DrawLineEx(v0, v2, 2.0f, RED); DrawCircleSector(v0, 40.0f, startangle, startangle - angle, 32, Fade(GREEN, 0.6f)); } - + DrawText("v0", v0.x, v0.y, 10, DARKGRAY); // If the line from v0 to v1 would overlap the text, move it's position up 10 @@ -110,7 +110,7 @@ int main(void) DrawText("Press SPACE to change MODE", 460, 10, 20, DARKGRAY); DrawText(TextFormat("ANGLE: %2.2f", angle), 10, 70, 20, LIME); - + EndDrawing(); //---------------------------------------------------------------------------------- } diff --git a/examples/others/rlgl_standalone.c b/examples/others/rlgl_standalone.c index 713d1b908..e74ca0f6c 100644 --- a/examples/others/rlgl_standalone.c +++ b/examples/others/rlgl_standalone.c @@ -137,7 +137,7 @@ int main(void) glfwWindowHint(GLFW_SAMPLES, 4); glfwWindowHint(GLFW_DEPTH_BITS, 16); - + // WARNING: OpenGL 3.3 Core profile only glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); diff --git a/examples/shaders/shaders_basic_lighting.c b/examples/shaders/shaders_basic_lighting.c index f115338a5..913e4aeeb 100644 --- a/examples/shaders/shaders_basic_lighting.c +++ b/examples/shaders/shaders_basic_lighting.c @@ -59,10 +59,10 @@ int main(void) TextFormat("resources/shaders/glsl%i/lighting.fs", GLSL_VERSION)); // Get some required shader locations shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos"); - // NOTE: "matModel" location name is automatically assigned on shader loading, + // NOTE: "matModel" location name is automatically assigned on shader loading, // no need to get the location again if using that uniform name //shader.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocation(shader, "matModel"); - + // Ambient light level (some basic lighting) int ambientLoc = GetShaderLocation(shader, "ambient"); SetShaderValue(shader, ambientLoc, (float[4]){ 0.1f, 0.1f, 0.1f, 1.0f }, SHADER_UNIFORM_VEC4); @@ -87,13 +87,13 @@ int main(void) // Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f }) float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z }; SetShaderValue(shader, shader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3); - + // Check key inputs to enable/disable lights if (IsKeyPressed(KEY_Y)) { lights[0].enabled = !lights[0].enabled; } if (IsKeyPressed(KEY_R)) { lights[1].enabled = !lights[1].enabled; } if (IsKeyPressed(KEY_G)) { lights[2].enabled = !lights[2].enabled; } if (IsKeyPressed(KEY_B)) { lights[3].enabled = !lights[3].enabled; } - + // Update light values (actually, only enable/disable them) for (int i = 0; i < MAX_LIGHTS; i++) UpdateLightValues(shader, lights[i]); //---------------------------------------------------------------------------------- diff --git a/examples/shaders/shaders_basic_pbr.c b/examples/shaders/shaders_basic_pbr.c index b375c4fa1..39db430ab 100644 --- a/examples/shaders/shaders_basic_pbr.c +++ b/examples/shaders/shaders_basic_pbr.c @@ -13,8 +13,8 @@ * * Copyright (c) 2023-2025 Afan OLOVCIC (@_DevDad) * -* Model: "Old Rusty Car" (https://skfb.ly/LxRy) by Renafox, -* licensed under Creative Commons Attribution-NonCommercial +* Model: "Old Rusty Car" (https://skfb.ly/LxRy) by Renafox, +* licensed under Creative Commons Attribution-NonCommercial * (http://creativecommons.org/licenses/by-nc/4.0/) * ********************************************************************************************/ @@ -105,7 +105,7 @@ int main() // shader already takes care of it accordingly shader.locs[SHADER_LOC_MAP_METALNESS] = GetShaderLocation(shader, "mraMap"); shader.locs[SHADER_LOC_MAP_NORMAL] = GetShaderLocation(shader, "normalMap"); - // WARNING: Similar to the MRA map, the emissive map packs different information + // WARNING: Similar to the MRA map, the emissive map packs different information // into a single texture: it stores height and emission data // It is binded to SHADER_LOC_MAP_EMISSION location an properly processed on shader shader.locs[SHADER_LOC_MAP_EMISSION] = GetShaderLocation(shader, "emissiveMap"); @@ -153,7 +153,7 @@ int main() car.materials[0].maps[MATERIAL_MAP_METALNESS].texture = LoadTexture("resources/old_car_mra.png"); car.materials[0].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture("resources/old_car_n.png"); car.materials[0].maps[MATERIAL_MAP_EMISSION].texture = LoadTexture("resources/old_car_e.png"); - + // Load floor model mesh and assign material parameters // NOTE: A basic plane shape can be generated instead of being loaded from a model file Model floor = LoadModel("resources/models/plane.glb"); @@ -161,9 +161,9 @@ int main() //GenMeshTangents(&floorMesh); // TODO: Review tangents generation //Model floor = LoadModelFromMesh(floorMesh); - // Assign material shader for our floor model, same PBR shader + // Assign material shader for our floor model, same PBR shader floor.materials[0].shader = shader; - + floor.materials[0].maps[MATERIAL_MAP_ALBEDO].color = WHITE; floor.materials[0].maps[MATERIAL_MAP_METALNESS].value = 0.8f; floor.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value = 0.1f; @@ -193,7 +193,7 @@ int main() SetShaderValue(shader, GetShaderLocation(shader, "useTexNormal"), &usage, SHADER_UNIFORM_INT); SetShaderValue(shader, GetShaderLocation(shader, "useTexMRA"), &usage, SHADER_UNIFORM_INT); SetShaderValue(shader, GetShaderLocation(shader, "useTexEmissive"), &usage, SHADER_UNIFORM_INT); - + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //--------------------------------------------------------------------------------------- @@ -221,11 +221,11 @@ int main() // Draw //---------------------------------------------------------------------------------- BeginDrawing(); - + ClearBackground(BLACK); - + BeginMode3D(camera); - + // Set floor model texture tiling and emissive color parameters on shader SetShaderValue(shader, textureTilingLoc, &floorTextureTiling, SHADER_UNIFORM_VEC2); Vector4 floorEmissiveColor = ColorNormalize(floor.materials[0].maps[MATERIAL_MAP_EMISSION].color); @@ -234,7 +234,7 @@ int main() // Set floor metallic and roughness values SetShaderValue(shader, metallicValueLoc, &floor.materials[0].maps[MATERIAL_MAP_METALNESS].value, SHADER_UNIFORM_FLOAT); SetShaderValue(shader, roughnessValueLoc, &floor.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value, SHADER_UNIFORM_FLOAT); - + DrawModel(floor, (Vector3){ 0.0f, 0.0f, 0.0f }, 5.0f, WHITE); // Draw floor model // Set old car model texture tiling, emissive color and emissive intensity parameters on shader @@ -247,24 +247,24 @@ int main() // Set old car metallic and roughness values SetShaderValue(shader, metallicValueLoc, &car.materials[0].maps[MATERIAL_MAP_METALNESS].value, SHADER_UNIFORM_FLOAT); SetShaderValue(shader, roughnessValueLoc, &car.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value, SHADER_UNIFORM_FLOAT); - + 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++) { Color lightColor = (Color){ lights[i].color[0]*255, lights[i].color[1]*255, lights[i].color[2]*255, lights[i].color[3]*255 }; - + if (lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, lightColor); else DrawSphereWires(lights[i].position, 0.2f, 8, 8, ColorAlpha(lightColor, 0.3f)); } - + EndMode3D(); - + DrawText("Toggle lights: [1][2][3][4]", 10, 40, 20, LIGHTGRAY); DrawText("(c) Old Rusty Car model by Renafox (https://skfb.ly/LxRy)", screenWidth - 320, screenHeight - 20, 10, LIGHTGRAY); - + DrawFPS(10, 10); EndDrawing(); @@ -273,20 +273,20 @@ int main() // De-Initialization //-------------------------------------------------------------------------------------- - // Unbind (disconnect) shader from car.material[0] + // Unbind (disconnect) shader from car.material[0] // to avoid UnloadMaterial() trying to unload it automatically car.materials[0].shader = (Shader){ 0 }; UnloadMaterial(car.materials[0]); car.materials[0].maps = NULL; UnloadModel(car); - + floor.materials[0].shader = (Shader){ 0 }; UnloadMaterial(floor.materials[0]); floor.materials[0].maps = NULL; UnloadModel(floor); - + UnloadShader(shader); // Unload Shader - + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- @@ -310,7 +310,7 @@ static Light CreateLight(int type, Vector3 position, Vector3 target, Color color light.color[2] = (float)color.b/255.0f; light.color[3] = (float)color.a/255.0f; light.intensity = intensity; - + // NOTE: Shader parameters names for lights must match the requested ones light.enabledLoc = GetShaderLocation(shader, TextFormat("lights[%i].enabled", lightCount)); light.typeLoc = GetShaderLocation(shader, TextFormat("lights[%i].type", lightCount)); @@ -318,7 +318,7 @@ static Light CreateLight(int type, Vector3 position, Vector3 target, Color color light.targetLoc = GetShaderLocation(shader, TextFormat("lights[%i].target", lightCount)); light.colorLoc = GetShaderLocation(shader, TextFormat("lights[%i].color", lightCount)); light.intensityLoc = GetShaderLocation(shader, TextFormat("lights[%i].intensity", lightCount)); - + UpdateLight(shader, light); lightCount++; @@ -333,7 +333,7 @@ static void UpdateLight(Shader shader, Light light) { SetShaderValue(shader, light.enabledLoc, &light.enabled, SHADER_UNIFORM_INT); SetShaderValue(shader, light.typeLoc, &light.type, SHADER_UNIFORM_INT); - + // Send to shader light position values float position[3] = { light.position.x, light.position.y, light.position.z }; SetShaderValue(shader, light.positionLoc, position, SHADER_UNIFORM_VEC3); diff --git a/examples/shaders/shaders_custom_uniform.c b/examples/shaders/shaders_custom_uniform.c index 432b240ee..951759079 100644 --- a/examples/shaders/shaders_custom_uniform.c +++ b/examples/shaders/shaders_custom_uniform.c @@ -78,7 +78,7 @@ int main(void) // Update //---------------------------------------------------------------------------------- UpdateCamera(&camera, CAMERA_ORBITAL); - + Vector2 mousePosition = GetMousePosition(); swirlCenter[0] = mousePosition.x; diff --git a/examples/shaders/shaders_deferred_render.c b/examples/shaders/shaders_deferred_render.c index 879f9a68b..1fe6d3a0c 100644 --- a/examples/shaders/shaders_deferred_render.c +++ b/examples/shaders/shaders_deferred_render.c @@ -42,7 +42,7 @@ typedef struct GBuffer { unsigned int positionTexture; unsigned int normalTexture; unsigned int albedoSpecTexture; - + unsigned int depthRenderbuffer; } GBuffer; @@ -94,14 +94,14 @@ int main(void) TraceLog(LOG_WARNING, "Failed to create framebuffer"); exit(1); } - + rlEnableFramebuffer(gBuffer.framebuffer); // NOTE: Vertex positions are stored in a texture for simplicity. A better approach would use a depth texture - // (instead of a detph renderbuffer) to reconstruct world positions in the final render shader via clip-space position, + // (instead of a detph renderbuffer) to reconstruct world positions in the final render shader via clip-space position, // depth, and the inverse view/projection matrices. - // 16-bit precision ensures OpenGL ES 3 compatibility, though it may lack precision for real scenarios. + // 16-bit precision ensures OpenGL ES 3 compatibility, though it may lack precision for real scenarios. // But as mentioned above, the positions could be reconstructed instead of stored. If not targeting OpenGL ES // and you wish to maintain this approach, consider using `RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32`. gBuffer.positionTexture = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16, 1); @@ -161,7 +161,7 @@ int main(void) const float CUBE_SCALE = 0.25; Vector3 cubePositions[MAX_CUBES] = { 0 }; float cubeRotations[MAX_CUBES] = { 0 }; - + for (int i = 0; i < MAX_CUBES; i++) { cubePositions[i] = (Vector3){ @@ -169,7 +169,7 @@ int main(void) .y = (float)(rand()%5), .z = (float)(rand()%10) - 5, }; - + cubeRotations[i] = (float)(rand()%360); } @@ -190,7 +190,7 @@ int main(void) // Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f }) float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z }; SetShaderValue(deferredShader, deferredShader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3); - + // Check key inputs to enable/disable lights if (IsKeyPressed(KEY_Y)) { lights[0].enabled = !lights[0].enabled; } if (IsKeyPressed(KEY_R)) { lights[1].enabled = !lights[1].enabled; } @@ -215,7 +215,7 @@ int main(void) rlEnableFramebuffer(gBuffer.framebuffer); rlClearColor(0, 0, 0, 0); rlClearScreenBuffers(); // Clear color and depth buffer - + rlDisableColorBlend(); BeginMode3D(camera); // NOTE: We have to use rlEnableShader here. `BeginShaderMode` or thus `rlSetShader` @@ -281,7 +281,7 @@ int main(void) } rlDisableShader(); EndMode3D(); - + DrawText("FINAL RESULT", 10, screenHeight - 30, 20, DARKGREEN); } break; case DEFERRED_POSITION: @@ -291,7 +291,7 @@ int main(void) .width = screenWidth, .height = screenHeight, }, (Rectangle) { 0, 0, (float)screenWidth, (float)-screenHeight }, Vector2Zero(), RAYWHITE); - + DrawText("POSITION TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); } break; case DEFERRED_NORMAL: @@ -301,7 +301,7 @@ int main(void) .width = screenWidth, .height = screenHeight, }, (Rectangle) { 0, 0, (float)screenWidth, (float)-screenHeight }, Vector2Zero(), RAYWHITE); - + DrawText("NORMAL TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); } break; case DEFERRED_ALBEDO: @@ -311,7 +311,7 @@ int main(void) .width = screenWidth, .height = screenHeight, }, (Rectangle) { 0, 0, (float)screenWidth, (float)-screenHeight }, Vector2Zero(), RAYWHITE); - + DrawText("ALBEDO TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); } break; default: break; @@ -321,7 +321,7 @@ int main(void) DrawText("Switch G-buffer textures: [1][2][3][4]", 10, 70, 20, DARKGRAY); DrawFPS(10, 10); - + EndDrawing(); // ----------------------------------------------------------------------------- } diff --git a/examples/shaders/shaders_eratosthenes.c b/examples/shaders/shaders_eratosthenes.c index 26075706e..03dfc2125 100644 --- a/examples/shaders/shaders_eratosthenes.c +++ b/examples/shaders/shaders_eratosthenes.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [shaders] example - Sieve of Eratosthenes +* raylib [shaders] example - sieve of Eratosthenes * * Example complexity rating: [★★★☆] 3/4 * @@ -45,7 +45,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - Sieve of Eratosthenes"); + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - sieve of Eratosthenes"); RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight); diff --git a/examples/shaders/shaders_hot_reloading.c b/examples/shaders/shaders_hot_reloading.c index 1005c0913..b11245dcc 100644 --- a/examples/shaders/shaders_hot_reloading.c +++ b/examples/shaders/shaders_hot_reloading.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [shaders] example - Hot reloading +* raylib [shaders] example - hot reloading * * Example complexity rating: [★★★☆] 3/4 * diff --git a/examples/shaders/shaders_hybrid_render.c b/examples/shaders/shaders_hybrid_render.c index 4c9f5c978..0c7e789fd 100644 --- a/examples/shaders/shaders_hybrid_render.c +++ b/examples/shaders/shaders_hybrid_render.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [shaders] example - Hybrid Rendering +* raylib [shaders] example - hybrid rendering * * Example complexity rating: [★★★★] 4/4 * @@ -69,7 +69,7 @@ int main(void) marchLocs.camDir = GetShaderLocation(shdrRaymarch, "camDir"); marchLocs.screenCenter = GetShaderLocation(shdrRaymarch, "screenCenter"); - // Transfer screenCenter position to shader. Which is used to calculate ray direction. + // Transfer screenCenter position to shader. Which is used to calculate ray direction. Vector2 screenCenter = {.x = screenWidth/2.0f, .y = screenHeight/2.0f}; SetShaderValue(shdrRaymarch, marchLocs.screenCenter , &screenCenter , SHADER_UNIFORM_VEC2); @@ -84,10 +84,10 @@ int main(void) .fovy = 45.0f, // Camera field-of-view Y .projection = CAMERA_PERSPECTIVE // Camera projection type }; - + // Camera FOV is pre-calculated in the camera Distance. float camDist = 1.0f/(tanf(camera.fovy*0.5f*DEG2RAD)); - + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -100,12 +100,12 @@ int main(void) // Update Camera Postion in the ray march shader. SetShaderValue(shdrRaymarch, marchLocs.camPos, &(camera.position), RL_SHADER_UNIFORM_VEC3); - + // Update Camera Looking Vector. Vector length determines FOV. Vector3 camDir = Vector3Scale( Vector3Normalize( Vector3Subtract(camera.target, camera.position)) , camDist); SetShaderValue(shdrRaymarch, marchLocs.camDir, &(camDir), RL_SHADER_UNIFORM_VEC3); //---------------------------------------------------------------------------------- - + // Draw //---------------------------------------------------------------------------------- // Draw into our custom render texture (framebuffer) @@ -117,7 +117,7 @@ int main(void) BeginShaderMode(shdrRaymarch); DrawRectangleRec((Rectangle){0,0, (float)screenWidth, (float)screenHeight},WHITE); EndShaderMode(); - + // Rasterize Scene BeginMode3D(camera); BeginShaderMode(shdrRaster); @@ -130,10 +130,10 @@ int main(void) EndMode3D(); EndTextureMode(); - // Draw into screen our custom render texture + // Draw into screen our custom render texture BeginDrawing(); ClearBackground(RAYWHITE); - + DrawTextureRec(target.texture, (Rectangle) { 0, 0, (float)screenWidth, (float)-screenHeight }, (Vector2) { 0, 0 }, WHITE); DrawFPS(10, 10); EndDrawing(); diff --git a/examples/shaders/shaders_julia_set.c b/examples/shaders/shaders_julia_set.c index c1be3f325..72ff31ad7 100644 --- a/examples/shaders/shaders_julia_set.c +++ b/examples/shaders/shaders_julia_set.c @@ -167,7 +167,7 @@ int main(void) // do not represent full screen coordinates (space where want to apply shader) DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), BLACK); EndTextureMode(); - + BeginDrawing(); ClearBackground(BLACK); // Clear screen background diff --git a/examples/shaders/shaders_lightmap.c b/examples/shaders/shaders_lightmap.c index 0d26e54c8..a393c1afe 100644 --- a/examples/shaders/shaders_lightmap.c +++ b/examples/shaders/shaders_lightmap.c @@ -70,7 +70,7 @@ int main(void) // Load a new texcoords2 attributes buffer mesh.vboId[SHADER_LOC_VERTEX_TEXCOORD02] = rlLoadVertexBuffer(mesh.texcoords2, mesh.vertexCount*2*sizeof(float), false); rlEnableVertexArray(mesh.vaoId); - + // Index 5 is for texcoords2 rlSetVertexAttribute(5, 2, RL_FLOAT, 0, 0, 0); rlEnableVertexAttribute(5); @@ -156,10 +156,10 @@ int main(void) (Vector2){ 0.0, 0.0 }, 0.0, WHITE); - + DrawText("lightmap", GetRenderWidth() - 66, 16 + MAP_SIZE*8, 10, GRAY); DrawText("10x10 pixels", GetRenderWidth() - 76, 30 + MAP_SIZE*8, 10, GRAY); - + EndDrawing(); //---------------------------------------------------------------------------------- } diff --git a/examples/shaders/shaders_mesh_instancing.c b/examples/shaders/shaders_mesh_instancing.c index 17b2da33d..6142a825e 100644 --- a/examples/shaders/shaders_mesh_instancing.c +++ b/examples/shaders/shaders_mesh_instancing.c @@ -64,7 +64,7 @@ int main(void) Vector3 axis = Vector3Normalize((Vector3){ (float)GetRandomValue(0, 360), (float)GetRandomValue(0, 360), (float)GetRandomValue(0, 360) }); float angle = (float)GetRandomValue(0, 180)*DEG2RAD; Matrix rotation = MatrixRotate(axis, angle); - + transforms[i] = MatrixMultiply(rotation, translation); } diff --git a/examples/shaders/shaders_multi_sample2d.c b/examples/shaders/shaders_multi_sample2d.c index c13864607..67cfb2058 100644 --- a/examples/shaders/shaders_multi_sample2d.c +++ b/examples/shaders/shaders_multi_sample2d.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [shaders] example - Multiple sample2D with default batch system +* raylib [shaders] example - multi sample2D * * Example complexity rating: [★★☆☆] 2/4 * @@ -38,7 +38,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib - multiple sample2D"); + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - multi sample2D"); Image imRed = GenImageColor(800, 450, (Color){ 255, 0, 0, 255 }); Texture texRed = LoadTextureFromImage(imRed); @@ -93,7 +93,7 @@ int main(void) // an additional texture units is enabled for texBlue [sampler2D texture1] DrawTexture(texRed, 0, 0, WHITE); - EndShaderMode(); // Texture sampler2D is reseted, needs to be set again for next frame + EndShaderMode(); // Texture sampler2D is reseted, needs to be set again for next frame DrawText("Use KEY_LEFT/KEY_RIGHT to move texture mixing in shader!", 80, GetScreenHeight() - 40, 20, RAYWHITE); diff --git a/examples/shaders/shaders_postprocessing.c b/examples/shaders/shaders_postprocessing.c index ea55c9ca4..156474e60 100644 --- a/examples/shaders/shaders_postprocessing.c +++ b/examples/shaders/shaders_postprocessing.c @@ -141,7 +141,7 @@ int main(void) DrawGrid(10, 1.0f); // Draw a grid EndMode3D(); // End 3d mode drawing, returns to orthographic 2d mode EndTextureMode(); // End drawing to texture (now we have a texture available for next passes) - + BeginDrawing(); ClearBackground(RAYWHITE); // Clear screen background diff --git a/examples/shaders/shaders_shadowmap.c b/examples/shaders/shaders_shadowmap.c index 4910b0908..7f98a25fe 100644 --- a/examples/shaders/shaders_shadowmap.c +++ b/examples/shaders/shaders_shadowmap.c @@ -173,7 +173,7 @@ int main(void) // Draw the same exact things as we drew in the shadowmap! DrawScene(cube, robot); - + EndMode3D(); DrawText("Shadows in raylib using the shadowmapping algorithm!", screenWidth - 320, screenHeight - 20, 10, GRAY); diff --git a/examples/shaders/shaders_simple_mask.c b/examples/shaders/shaders_simple_mask.c index f12230373..2e6e83a61 100644 --- a/examples/shaders/shaders_simple_mask.c +++ b/examples/shaders/shaders_simple_mask.c @@ -97,7 +97,7 @@ int main(void) // Update //---------------------------------------------------------------------------------- UpdateCamera(&camera, CAMERA_FIRST_PERSON); - + framesCounter++; rotation.x += 0.01f; rotation.y += 0.005f; diff --git a/examples/shaders/shaders_texture_outline.c b/examples/shaders/shaders_texture_outline.c index b2172aef9..68db65188 100644 --- a/examples/shaders/shaders_texture_outline.c +++ b/examples/shaders/shaders_texture_outline.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [shaders] example - Apply an shdrOutline to a texture +* raylib [shaders] example - texture outline * * Example complexity rating: [★★★☆] 3/4 * @@ -36,7 +36,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - Apply an outline to a texture"); + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - texture outline"); Texture2D texture = LoadTexture("resources/fudesumi.png"); diff --git a/examples/shaders/shaders_texture_tiling.c b/examples/shaders/shaders_texture_tiling.c index d811e30ec..9752b871f 100644 --- a/examples/shaders/shaders_texture_tiling.c +++ b/examples/shaders/shaders_texture_tiling.c @@ -48,7 +48,7 @@ int main(void) // Load a cube model Mesh cube = GenMeshCube(1.0f, 1.0f, 1.0f); Model model = LoadModelFromMesh(cube); - + // Load a texture and assign to cube model Texture2D texture = LoadTexture("resources/cubicmap_atlas.png"); model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture; @@ -77,17 +77,17 @@ int main(void) // Draw //---------------------------------------------------------------------------------- BeginDrawing(); - + ClearBackground(RAYWHITE); BeginMode3D(camera); - + BeginShaderMode(shader); DrawModel(model, (Vector3){ 0.0f, 0.0f, 0.0f }, 2.0f, WHITE); EndShaderMode(); DrawGrid(10, 1.0f); - + EndMode3D(); DrawText("Use mouse to rotate the camera", 10, 10, 20, DARKGRAY); @@ -104,6 +104,6 @@ int main(void) CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- - + return 0; } diff --git a/examples/shaders/shaders_vertex_displacement.c b/examples/shaders/shaders_vertex_displacement.c index 8008bc004..1a163f9b7 100644 --- a/examples/shaders/shaders_vertex_displacement.c +++ b/examples/shaders/shaders_vertex_displacement.c @@ -52,7 +52,7 @@ int main(void) 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); @@ -64,7 +64,7 @@ int main(void) 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); diff --git a/examples/shaders/shaders_view_depth.c b/examples/shaders/shaders_view_depth.c index 4f141acb9..f4eecf7e2 100644 --- a/examples/shaders/shaders_view_depth.c +++ b/examples/shaders/shaders_view_depth.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [shader] example - render depth texture +* raylib [shaders] example - render depth texture * * Example complexity rating: [★★★☆] 3/4 * @@ -36,7 +36,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [shader] example - render depth texture"); + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - render depth texture"); // Init camera Camera camera = { 0 }; @@ -76,7 +76,7 @@ int main(void) //---------------------------------------------------------------------------------- BeginTextureMode(target); ClearBackground(WHITE); - + BeginMode3D(camera); DrawModel(cube, (Vector3){ 0.0f, 0.0f, 0.0f }, 3.0f, YELLOW); DrawModel(floor, (Vector3){ 10.0f, 0.0f, 2.0f }, 2.0f, RED); diff --git a/examples/shaders/shaders_write_depth.c b/examples/shaders/shaders_write_depth.c index 2d24dcaed..7a0e01968 100644 --- a/examples/shaders/shaders_write_depth.c +++ b/examples/shaders/shaders_write_depth.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [shaders] example - Depth buffer writing +* raylib [shaders] example - depth buffer writing * * Example complexity rating: [★★☆☆] 2/4 * @@ -60,7 +60,7 @@ int main(void) .fovy = 45.0f, // Camera field-of-view Y .projection = CAMERA_PERSPECTIVE // Camera projection type }; - + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -71,13 +71,13 @@ int main(void) //---------------------------------------------------------------------------------- UpdateCamera(&camera, CAMERA_ORBITAL); //---------------------------------------------------------------------------------- - + // Draw //---------------------------------------------------------------------------------- // Draw into our custom render texture (framebuffer) BeginTextureMode(target); ClearBackground(WHITE); - + BeginMode3D(camera); BeginShaderMode(shader); DrawCubeWiresV((Vector3){ 0.0f, 0.5f, 1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, RED); @@ -89,7 +89,7 @@ int main(void) EndMode3D(); EndTextureMode(); - // Draw into screen our custom render texture + // Draw into screen our custom render texture BeginDrawing(); ClearBackground(RAYWHITE); DrawTextureRec(target.texture, (Rectangle) { 0, 0, (float)screenWidth, (float)-screenHeight }, (Vector2) { 0, 0 }, WHITE); diff --git a/examples/shapes/shapes_lines_bezier.c b/examples/shapes/shapes_lines_bezier.c index f963cd4de..3e8f68a7f 100644 --- a/examples/shapes/shapes_lines_bezier.c +++ b/examples/shapes/shapes_lines_bezier.c @@ -69,7 +69,7 @@ int main(void) // Draw line Cubic Bezier, in-out interpolation (easing), no control points DrawLineBezier(startPoint, endPoint, 4.0f, BLUE); - + // Draw start-end spline circles with some details DrawCircleV(startPoint, CheckCollisionPointCircle(mouse, startPoint, 10.0f)? 14.0f : 8.0f, moveStartPoint? RED : BLUE); DrawCircleV(endPoint, CheckCollisionPointCircle(mouse, endPoint, 10.0f)? 14.0f : 8.0f, moveEndPoint? RED : BLUE); diff --git a/examples/shapes/shapes_logo_raylib.c b/examples/shapes/shapes_logo_raylib.c index d26db5ce6..f2974ddb4 100644 --- a/examples/shapes/shapes_logo_raylib.c +++ b/examples/shapes/shapes_logo_raylib.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [shapes] example - Draw raylib logo using basic shapes +* raylib [shapes] example - draw raylib logo using basic shapes * * Example complexity rating: [★☆☆☆] 1/4 * diff --git a/examples/shapes/shapes_rectangle_advanced.c b/examples/shapes/shapes_rectangle_advanced.c index 0c20c9e8b..d0366f616 100644 --- a/examples/shapes/shapes_rectangle_advanced.c +++ b/examples/shapes/shapes_rectangle_advanced.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [shapes] example - Rectangle advanced +* raylib [shapes] example - advanced rectangle drawing * * Example complexity rating: [★★★★] 4/4 * @@ -33,9 +33,9 @@ int main(void) //-------------------------------------------------------------------------------------- const int screenWidth = 800; const int screenHeight = 450; - + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - rectangle avanced"); - + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -195,7 +195,7 @@ static void DrawRectangleRoundedGradientH(Rectangle rec, float roundnessLeft, fl } // Here we use the 'Diagram' to guide ourselves to which point receives what color - // By choosing the color correctly associated with a pointe the gradient effect + // By choosing the color correctly associated with a pointe the gradient effect // will naturally come from OpenGL interpolation // [2] Upper Rectangle @@ -266,7 +266,7 @@ static void DrawRectangleRoundedGradientH(Rectangle rec, float roundnessLeft, fl #else // Here we use the 'Diagram' to guide ourselves to which point receives what color. - // By choosing the color correctly associated with a pointe the gradient effect + // By choosing the color correctly associated with a pointe the gradient effect // will naturally come from OpenGL interpolation. // But this time instead of Quad, we think in triangles. @@ -280,10 +280,10 @@ static void DrawRectangleRoundedGradientH(Rectangle rec, float roundnessLeft, fl if (k == 1) color = right, radius = radiusRight; // [3] Upper Right Corner if (k == 2) color = right, radius = radiusRight; // [5] Lower Right Corner if (k == 3) color = left, radius = radiusLeft; // [7] Lower Left Corner - + float angle = angles[k]; const Vector2 center = centers[k]; - + for (int i = 0; i < segments; i++) { rlColor4ub(color.r, color.g, color.b, color.a); diff --git a/examples/shapes/shapes_rectangle_scaling.c b/examples/shapes/shapes_rectangle_scaling.c index b5c778317..7426e54d5 100644 --- a/examples/shapes/shapes_rectangle_scaling.c +++ b/examples/shapes/shapes_rectangle_scaling.c @@ -65,7 +65,7 @@ int main(void) // Check minimum rec size if (rec.width < MOUSE_SCALE_MARK_SIZE) rec.width = MOUSE_SCALE_MARK_SIZE; if (rec.height < MOUSE_SCALE_MARK_SIZE) rec.height = MOUSE_SCALE_MARK_SIZE; - + // Check maximum rec size if (rec.width > (GetScreenWidth() - rec.x)) rec.width = GetScreenWidth() - rec.x; if (rec.height > (GetScreenHeight() - rec.y)) rec.height = GetScreenHeight() - rec.y; diff --git a/examples/shapes/shapes_splines_drawing.c b/examples/shapes/shapes_splines_drawing.c index 3b4c9c284..88cfe688b 100644 --- a/examples/shapes/shapes_splines_drawing.c +++ b/examples/shapes/shapes_splines_drawing.c @@ -23,7 +23,7 @@ #define MAX_SPLINE_POINTS 32 // Cubic Bezier spline control points -// NOTE: Every segment has two control points +// NOTE: Every segment has two control points typedef struct { Vector2 start; Vector2 end; @@ -57,17 +57,17 @@ int main(void) { 520.0f, 60.0f }, { 710.0f, 260.0f }, }; - - // Array required for spline bezier-cubic, + + // Array required for spline bezier-cubic, // including control points interleaved with start-end segment points Vector2 pointsInterleaved[3*(MAX_SPLINE_POINTS - 1) + 1] = { 0 }; - + int pointCount = 5; int selectedPoint = -1; int focusedPoint = -1; Vector2 *selectedControlPoint = NULL; Vector2 *focusedControlPoint = NULL; - + // Cubic Bezier control points initialization ControlPoint control[MAX_SPLINE_POINTS-1] = { 0 }; for (int i = 0; i < pointCount - 1; i++) @@ -79,9 +79,9 @@ int main(void) // Spline config variables float splineThickness = 8.0f; int splineTypeActive = SPLINE_LINEAR; // 0-Linear, 1-BSpline, 2-CatmullRom, 3-Bezier - bool splineTypeEditMode = false; + bool splineTypeEditMode = false; bool splineHelpersActive = true; - + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -114,14 +114,14 @@ int main(void) } if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) selectedPoint = focusedPoint; } - + // Spline point movement logic if (selectedPoint >= 0) { points[selectedPoint] = GetMousePosition(); if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) selectedPoint = -1; } - + // Cubic Bezier spline control points logic if ((splineTypeActive == SPLINE_BEZIER) && (focusedPoint == -1)) { @@ -144,7 +144,7 @@ int main(void) } if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) selectedControlPoint = focusedControlPoint; } - + // Spline control point movement logic if (selectedControlPoint != NULL) { @@ -152,7 +152,7 @@ int main(void) if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) selectedControlPoint = NULL; } } - + // Spline selection logic if (IsKeyPressed(KEY_ONE)) splineTypeActive = 0; else if (IsKeyPressed(KEY_TWO)) splineTypeActive = 1; @@ -168,7 +168,7 @@ int main(void) BeginDrawing(); ClearBackground(RAYWHITE); - + if (splineTypeActive == SPLINE_LINEAR) { // Draw spline: linear @@ -191,7 +191,7 @@ int main(void) { // Draw spline: catmull-rom DrawSplineCatmullRom(points, pointCount, splineThickness, RED); // Provide connected points array - + /* for (int i = 0; i < (pointCount - 3); i++) { @@ -202,20 +202,20 @@ int main(void) } else if (splineTypeActive == SPLINE_BEZIER) { - // NOTE: Cubic-bezier spline requires the 2 control points of each segnment to be + // NOTE: Cubic-bezier spline requires the 2 control points of each segnment to be // provided interleaved with the start and end point of every segment - for (int i = 0; i < (pointCount - 1); i++) + for (int i = 0; i < (pointCount - 1); i++) { pointsInterleaved[3*i] = points[i]; pointsInterleaved[3*i + 1] = control[i].start; pointsInterleaved[3*i + 2] = control[i].end; } - + pointsInterleaved[3*(pointCount - 1)] = points[pointCount - 1]; // Draw spline: cubic-bezier (with control points) DrawSplineBezierCubic(pointsInterleaved, 3*(pointCount - 1) + 1, splineThickness, RED); - + /* for (int i = 0; i < 3*(pointCount - 1); i += 3) { @@ -234,7 +234,7 @@ int main(void) else if (focusedControlPoint == &control[i].end) DrawCircleV(control[i].end, 8, GREEN); DrawLineEx(points[i], control[i].start, 1.0f, LIGHTGRAY); DrawLineEx(points[i + 1], control[i].end, 1.0f, LIGHTGRAY); - + // Draw spline control lines DrawLineV(points[i], control[i].start, GRAY); //DrawLineV(control[i].start, control[i].end, LIGHTGRAY); @@ -258,7 +258,7 @@ int main(void) // Check all possible UI states that require controls lock if (splineTypeEditMode || (selectedPoint != -1) || (selectedControlPoint != NULL)) GuiLock(); - + // Draw spline config GuiLabel((Rectangle){ 12, 62, 140, 24 }, TextFormat("Spline thickness: %i", (int)splineThickness)); GuiSliderBar((Rectangle){ 12, 60 + 24, 140, 16 }, NULL, NULL, &splineThickness, 1.0f, 40.0f); @@ -269,7 +269,7 @@ int main(void) GuiLabel((Rectangle){ 12, 10, 140, 24 }, "Spline type:"); if (GuiDropdownBox((Rectangle){ 12, 8 + 24, 140, 28 }, "LINEAR;BSPLINE;CATMULLROM;BEZIER", &splineTypeActive, splineTypeEditMode)) splineTypeEditMode = !splineTypeEditMode; - + GuiUnlock(); EndDrawing(); diff --git a/examples/shapes/shapes_top_down_lights.c b/examples/shapes/shapes_top_down_lights.c index 7e2640117..3c62b602a 100644 --- a/examples/shapes/shapes_top_down_lights.c +++ b/examples/shapes/shapes_top_down_lights.c @@ -55,7 +55,7 @@ LightInfo lights[MAX_LIGHTS] = { 0 }; void MoveLight(int slot, float x, float y) { lights[slot].dirty = true; - lights[slot].position.x = x; + lights[slot].position.x = x; lights[slot].position.y = y; // update the cached bounds @@ -99,7 +99,7 @@ void DrawLightMask(int slot) // If we are valid, then draw the light radius to the alpha mask if (lights[slot].valid) DrawCircleGradient((int)lights[slot].position.x, (int)lights[slot].position.y, lights[slot].outerRadius, ColorAlpha(WHITE, 0), WHITE); - + rlDrawRenderBatchActive(); // Cut out the shadows from the light radius by forcing the alpha to maximum @@ -114,7 +114,7 @@ void DrawLightMask(int slot) } rlDrawRenderBatchActive(); - + // Go back to normal blend mode rlSetBlendMode(BLEND_ALPHA); @@ -156,7 +156,7 @@ bool UpdateLight(int slot, Rectangle* boxes, int count) if (!CheckCollisionRecs(lights[slot].bounds, boxes[i])) continue; // Check the edges that are on the same side we are, and cast shadow volumes out from them - + // Top Vector2 sp = (Vector2){ boxes[i].x, boxes[i].y }; Vector2 ep = (Vector2){ boxes[i].x + boxes[i].width, boxes[i].y }; @@ -219,7 +219,7 @@ int main(void) //-------------------------------------------------------------------------------------- const int screenWidth = 800; const int screenHeight = 450; - + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - top down lights"); // Initialize our 'world' of boxes @@ -274,7 +274,7 @@ int main(void) { // Build up the light mask BeginTextureMode(lightMask); - + ClearBackground(BLACK); // Force the blend mode to only set the alpha of the destination @@ -300,10 +300,10 @@ int main(void) BeginDrawing(); ClearBackground(BLACK); - + // Draw the tile background DrawTextureRec(backgroundTexture, (Rectangle){ 0, 0, (float)GetScreenWidth(), (float)GetScreenHeight() }, Vector2Zero(), WHITE); - + // Overlay the shadows from all the lights DrawTextureRec(lightMask.texture, (Rectangle){ 0, 0, (float)GetScreenWidth(), -(float)GetScreenHeight() }, Vector2Zero(), ColorAlpha(WHITE, showLines? 0.75f : 1.0f)); diff --git a/examples/text/text_codepoints_loading.c b/examples/text/text_codepoints_loading.c index a176148ea..1cbd80ac9 100644 --- a/examples/text/text_codepoints_loading.c +++ b/examples/text/text_codepoints_loading.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [text] example - Codepoints loading +* raylib [text] example - text codepoints loading * * Example complexity rating: [★★★☆] 3/4 * diff --git a/examples/text/text_draw_3d.c b/examples/text/text_draw_3d.c index c06698b9f..734d9d744 100644 --- a/examples/text/text_draw_3d.c +++ b/examples/text/text_draw_3d.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [text] example - Draw 3d +* raylib [text] example - drawing 3d text * * Example complexity rating: [★★★★] 4/4 * @@ -12,7 +12,7 @@ * map that texture to a plane and render that, or maybe a shader but my method allows more * flexibility...for example to change position of each letter individually to make somethink * like a wavy text effect. -* +* * Special thanks to: * @Nighten for the DrawTextStyle() code https://github.com/NightenDushi/Raylib_DrawTextStyle * Chris Camacho (codifies - http://bedroomcoders.co.uk/) for the alpha discard shader @@ -146,7 +146,7 @@ int main(void) // Update //---------------------------------------------------------------------------------- UpdateCamera(&camera, camera_mode); - + // Handle font files dropped if (IsFileDropped()) { @@ -164,7 +164,7 @@ int main(void) font = LoadFont(droppedFiles.paths[0]); fontSize = (float)font.baseSize; } - + UnloadDroppedFiles(droppedFiles); // Unload filepaths from memory } diff --git a/examples/text/text_font_filters.c b/examples/text/text_font_filters.c index f3293e6be..f75ae26b2 100644 --- a/examples/text/text_font_filters.c +++ b/examples/text/text_font_filters.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [text] example - Font filters +* raylib [text] example - font texture filters * * Example complexity rating: [★★☆☆] 2/4 * @@ -94,7 +94,7 @@ int main(void) UnloadFont(font); font = LoadFontEx(droppedFiles.paths[0], (int)fontSize, 0, 0); } - + UnloadDroppedFiles(droppedFiles); // Unload filepaths from memory } //---------------------------------------------------------------------------------- diff --git a/examples/text/text_font_sdf.c b/examples/text/text_font_sdf.c index 7a051710b..fd3b2f776 100644 --- a/examples/text/text_font_sdf.c +++ b/examples/text/text_font_sdf.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [text] example - Font SDF loading +* raylib [text] example - font SDF loading * * Example complexity rating: [★★★☆] 3/4 * diff --git a/examples/text/text_font_spritefont.c b/examples/text/text_font_spritefont.c index 95b5cfee5..d3200a9ae 100644 --- a/examples/text/text_font_spritefont.c +++ b/examples/text/text_font_spritefont.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [text] example - Sprite font loading +* raylib [text] example - sprite font loading * * Example complexity rating: [★☆☆☆] 1/4 * diff --git a/examples/text/text_format_text.c b/examples/text/text_format_text.c index eca6500e3..a5c2a2331 100644 --- a/examples/text/text_format_text.c +++ b/examples/text/text_format_text.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [text] example - Text formatting +* raylib [text] example - text formating * * Example complexity rating: [★☆☆☆] 1/4 * diff --git a/examples/text/text_input_box.c b/examples/text/text_input_box.c index 24672b2ad..2e55e8b74 100644 --- a/examples/text/text_input_box.c +++ b/examples/text/text_input_box.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [text] example - Input Box +* raylib [text] example - text input box * * Example complexity rating: [★★☆☆] 2/4 * diff --git a/examples/text/text_rectangle_bounds.c b/examples/text/text_rectangle_bounds.c index c9dad5f9e..a05dfbd17 100644 --- a/examples/text/text_rectangle_bounds.c +++ b/examples/text/text_rectangle_bounds.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [text] example - Rectangle bounds +* raylib [text] example - rectangle bounds * * Example complexity rating: [★★★★] 4/4 * diff --git a/examples/text/text_unicode.c b/examples/text/text_unicode.c index 31faafade..c3ca39d69 100644 --- a/examples/text/text_unicode.c +++ b/examples/text/text_unicode.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [text] example - Unicode +* raylib [text] example - unicode text drawing * * Example complexity rating: [★★★★] 4/4 * @@ -284,7 +284,7 @@ int main(void) int length = GetCodepointCount(messages[message].text); const char *info = TextFormat("%s %u characters %i bytes", messages[message].language, length, size); sz = MeasureTextEx(GetFontDefault(), info, 10, 1.0f); - + DrawText(info, (int)(textRect.x + textRect.width - sz.x), (int)(msgRect.y + msgRect.height - sz.y - 2), 10, RAYWHITE); } //------------------------------------------------------------------------------ diff --git a/examples/text/text_writing_anim.c b/examples/text/text_writing_anim.c index 3455cec33..5d5b8ea7d 100644 --- a/examples/text/text_writing_anim.c +++ b/examples/text/text_writing_anim.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [text] example - Text Writing Animation +* raylib [text] example - text writing animation * * Example complexity rating: [★★☆☆] 2/4 * diff --git a/examples/textures/textures_background_scrolling.c b/examples/textures/textures_background_scrolling.c index 48ba314d4..d942caa54 100644 --- a/examples/textures/textures_background_scrolling.c +++ b/examples/textures/textures_background_scrolling.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [textures] example - Background scrolling +* raylib [textures] example - background scrolling * * Example complexity rating: [★☆☆☆] 1/4 * diff --git a/examples/textures/textures_bunnymark.c b/examples/textures/textures_bunnymark.c index 6f58e8281..a26f175ca 100644 --- a/examples/textures/textures_bunnymark.c +++ b/examples/textures/textures_bunnymark.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [textures] example - Bunnymark +* raylib [textures] example - bunnymark * * Example complexity rating: [★★★☆] 3/4 * diff --git a/examples/textures/textures_draw_tiled.c b/examples/textures/textures_draw_tiled.c index 801a7c15a..3c5cb7199 100644 --- a/examples/textures/textures_draw_tiled.c +++ b/examples/textures/textures_draw_tiled.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [textures] example - Draw part of the texture tiled +* raylib [textures] example - draw texture tiled * * Example complexity rating: [★★★☆] 3/4 * @@ -36,7 +36,7 @@ int main(void) const int screenHeight = 450; SetConfigFlags(FLAG_WINDOW_RESIZABLE); // Make the window resizable - InitWindow(screenWidth, screenHeight, "raylib [textures] example - Draw part of a texture tiled"); + InitWindow(screenWidth, screenHeight, "raylib [textures] example - draw texture tiled"); // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) Texture texPattern = LoadTexture("resources/patterns.png"); diff --git a/examples/textures/textures_fog_of_war.c b/examples/textures/textures_fog_of_war.c index e9296f3f4..41a332272 100644 --- a/examples/textures/textures_fog_of_war.c +++ b/examples/textures/textures_fog_of_war.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [textures] example - Fog of war +* raylib [textures] example - fog of war * * Example complexity rating: [★★★☆] 3/4 * diff --git a/examples/textures/textures_image_channel.c b/examples/textures/textures_image_channel.c index 5e159b984..35ef0c7f5 100644 --- a/examples/textures/textures_image_channel.c +++ b/examples/textures/textures_image_channel.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [textures] example - Retrive image channel (mask) +* raylib [textures] example - extract image channel * * NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM) * @@ -79,7 +79,7 @@ int main(void) //---------------------------------------------------------------------------------- // TODO... //---------------------------------------------------------------------------------- - + // Draw //---------------------------------------------------------------------------------- BeginDrawing(); @@ -104,6 +104,7 @@ int main(void) UnloadTexture(textureGreen); UnloadTexture(textureBlue); UnloadTexture(textureAlpha); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/examples/textures/textures_image_drawing.c b/examples/textures/textures_image_drawing.c index 05f40df9b..cc2340d82 100644 --- a/examples/textures/textures_image_drawing.c +++ b/examples/textures/textures_image_drawing.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [textures] example - Image loading and drawing on it +* raylib [textures] example - image loading and drawing * * Example complexity rating: [★★☆☆] 2/4 * diff --git a/examples/textures/textures_image_generation.c b/examples/textures/textures_image_generation.c index 2372af9c3..f616c6bfe 100644 --- a/examples/textures/textures_image_generation.c +++ b/examples/textures/textures_image_generation.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [textures] example - Procedural images generation +* raylib [textures] example - procedural images generation * * Example complexity rating: [★★☆☆] 2/4 * diff --git a/examples/textures/textures_image_loading.c b/examples/textures/textures_image_loading.c index 267216332..9ed864db5 100644 --- a/examples/textures/textures_image_loading.c +++ b/examples/textures/textures_image_loading.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [textures] example - Image loading and texture creation +* raylib [textures] example - image loading and texture creation * * Example complexity rating: [★☆☆☆] 1/4 * diff --git a/examples/textures/textures_image_rotate.c b/examples/textures/textures_image_rotate.c index 4921dc8ac..cf3278d22 100644 --- a/examples/textures/textures_image_rotate.c +++ b/examples/textures/textures_image_rotate.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [textures] example - Image Rotation +* raylib [textures] example - image rotation * * Example complexity rating: [★★☆☆] 2/4 * @@ -27,7 +27,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture rotation"); + InitWindow(screenWidth, screenHeight, "raylib [textures] example - image rotation"); // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) Image image45 = LoadImage("resources/raylib_logo.png"); diff --git a/examples/textures/textures_image_text.c b/examples/textures/textures_image_text.c index a0a41404a..6c8720315 100644 --- a/examples/textures/textures_image_text.c +++ b/examples/textures/textures_image_text.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [textures] example - Image text drawing using TTF generated font +* raylib [textures] example - image text drawing using TTF generated font * * Example complexity rating: [★★☆☆] 2/4 * diff --git a/examples/textures/textures_logo_raylib.c b/examples/textures/textures_logo_raylib.c index bd402baec..4fb176217 100644 --- a/examples/textures/textures_logo_raylib.c +++ b/examples/textures/textures_logo_raylib.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [textures] example - Texture loading and drawing +* raylib [textures] example - texture loading and drawing * * Example complexity rating: [★☆☆☆] 1/4 * diff --git a/examples/textures/textures_mouse_painting.c b/examples/textures/textures_mouse_painting.c index e2a2b6081..2575c4450 100644 --- a/examples/textures/textures_mouse_painting.c +++ b/examples/textures/textures_mouse_painting.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [textures] example - Mouse painting +* raylib [textures] example - mouse painting * * Example complexity rating: [★★★☆] 3/4 * diff --git a/examples/textures/textures_polygon.c b/examples/textures/textures_polygon.c index 1cde29a4b..414b91350 100644 --- a/examples/textures/textures_polygon.c +++ b/examples/textures/textures_polygon.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [textures] example - Draw Textured Polygon +* raylib [textures] example - draw textured polygon * * Example complexity rating: [★☆☆☆] 1/4 * @@ -34,7 +34,7 @@ int main(void) //-------------------------------------------------------------------------------------- const int screenWidth = 800; const int screenHeight = 450; - + InitWindow(screenWidth, screenHeight, "raylib [textures] example - textured polygon"); // Define texture coordinates to map our texture to poly @@ -60,7 +60,7 @@ int main(void) points[i].x = (texcoords[i].x - 0.5f)*256.0f; points[i].y = (texcoords[i].y - 0.5f)*256.0f; } - + // Define the vertices drawing position // NOTE: Initially same as points but updated every frame Vector2 positions[MAX_POINTS] = { 0 }; @@ -116,7 +116,7 @@ int main(void) void DrawTexturePoly(Texture2D texture, Vector2 center, Vector2 *points, Vector2 *texcoords, int pointCount, Color tint) { rlBegin(RL_TRIANGLES); - + rlSetTexture(texture.id); rlColor4ub(tint.r, tint.g, tint.b, tint.a); diff --git a/examples/textures/textures_raw_data.c b/examples/textures/textures_raw_data.c index 01248e979..76be8593a 100644 --- a/examples/textures/textures_raw_data.c +++ b/examples/textures/textures_raw_data.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [textures] example - Load textures from raw data +* raylib [textures] example - load textures from raw data * * Example complexity rating: [★★★☆] 3/4 * diff --git a/examples/textures/textures_sprite_anim.c b/examples/textures/textures_sprite_anim.c index c8b401522..032b1f621 100644 --- a/examples/textures/textures_sprite_anim.c +++ b/examples/textures/textures_sprite_anim.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [textures] example - Sprite animation +* raylib [textures] example - sprite animation * * Example complexity rating: [★★☆☆] 2/4 * diff --git a/examples/textures/textures_srcrec_dstrec.c b/examples/textures/textures_srcrec_dstrec.c index cf3686d15..6c5ba1b36 100644 --- a/examples/textures/textures_srcrec_dstrec.c +++ b/examples/textures/textures_srcrec_dstrec.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [textures] example - Texture source and destination rectangles +* raylib [textures] example - texture source and destination rectangles * * Example complexity rating: [★★★☆] 3/4 * diff --git a/examples/textures/textures_textured_curve.c b/examples/textures/textures_textured_curve.c index 00417b038..0102e8d5c 100644 --- a/examples/textures/textures_textured_curve.c +++ b/examples/textures/textures_textured_curve.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [textures] example - Draw a texture along a segmented curve +* raylib [textures] example - draw texture along segmented curve * * Example complexity rating: [★★★☆] 3/4 * @@ -112,7 +112,7 @@ int main() ClearBackground(RAYWHITE); DrawTexturedCurve(); // Draw a textured Spline Cubic Bezier - + // Draw spline for reference if (showCurve) DrawSplineSegmentBezierCubic(curveStartPosition, curveEndPosition, curveStartPositionTangent, curveEndPositionTangent, 2, BLUE); @@ -120,7 +120,7 @@ int main() DrawLineV(curveStartPosition, curveStartPositionTangent, SKYBLUE); DrawLineV(curveStartPositionTangent, curveEndPositionTangent, Fade(LIGHTGRAY, 0.4f)); DrawLineV(curveEndPosition, curveEndPositionTangent, PURPLE); - + if (CheckCollisionPointCircle(mouse, curveStartPosition, 6)) DrawCircleV(curveStartPosition, 7, YELLOW); DrawCircleV(curveStartPosition, 5, RED); @@ -137,7 +137,7 @@ int main() DrawText("Drag points to move curve, press SPACE to show/hide base curve", 10, 10, 10, DARKGRAY); DrawText(TextFormat("Curve width: %2.0f (Use + and - to adjust)", curveWidth), 10, 30, 10, DARKGRAY); DrawText(TextFormat("Curve segments: %d (Use LEFT and RIGHT to adjust)", curveSegments), 10, 50, 10, DARKGRAY); - + EndDrawing(); //---------------------------------------------------------------------------------- } @@ -145,7 +145,7 @@ int main() // De-Initialization //-------------------------------------------------------------------------------------- UnloadTexture(texRoad); - + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/examples/textures/textures_to_image.c b/examples/textures/textures_to_image.c index 84b4a72be..16d55f84c 100644 --- a/examples/textures/textures_to_image.c +++ b/examples/textures/textures_to_image.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [textures] example - Retrieve image data from texture: LoadImageFromTexture() +* raylib [textures] example - texture to image * * Example complexity rating: [★☆☆☆] 1/4 * From 8da4887c2dd7171d57556bed6c7401d4cc7d8d5a Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 17:19:27 +0200 Subject: [PATCH 221/242] Update examples_list.txt --- examples/examples_list.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/examples_list.txt b/examples/examples_list.txt index 964bae667..230656000 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -141,6 +141,7 @@ shaders;shaders_simple_mask;⭐️⭐️☆☆;2.5;3.7;"Chris Camacho";@chriscam shaders;shaders_hot_reloading;⭐️⭐️⭐️☆;3.0;3.5;"Ramon Santamaria";@raysan5 shaders;shaders_mesh_instancing;⭐️⭐️⭐️⭐️;3.7;4.2;"seanpringle";@seanpringle shaders;shaders_multi_sample2d;⭐️⭐️☆☆;3.5;3.5;"Ramon Santamaria";@raysan5 +shaders;shaders_normalmap;⭐️⭐️⭐️⭐️;5.6;5.6;"Jeremy Montgomery";@Sir_Irk shaders;shaders_spotlight;⭐️⭐️☆☆;2.5;3.7;"Chris Camacho";@chriscamacho shaders;shaders_deferred_render;⭐️⭐️⭐️⭐️;4.5;4.5;"Justin Andreas Lacoste";@27justin shaders;shaders_hybrid_render;⭐️⭐️⭐️⭐️;4.2;4.2;"Buğra Alptekin Sarı";@BugraAlptekinSari From 54473e2c2b6162d2a5446b853b816e51da3b8f5b Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 17:28:25 +0200 Subject: [PATCH 222/242] REVIEWED: Example `shaders_normalmap`, it crashes #5032 --- examples/shaders/shaders_normalmap.c | 53 ++++++++++++----------- examples/textures/textures_image_kernel.c | 2 +- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/examples/shaders/shaders_normalmap.c b/examples/shaders/shaders_normalmap.c index 96cbf414a..58c7deb48 100644 --- a/examples/shaders/shaders_normalmap.c +++ b/examples/shaders/shaders_normalmap.c @@ -19,12 +19,13 @@ ********************************************************************************************/ #include + #include #if defined(PLATFORM_DESKTOP) -#define GLSL_VERSION 330 -#else // PLATFORM_ANDROID, PLATFORM_WEB -#define GLSL_VERSION 100 + #define GLSL_VERSION 330 +#else // PLATFORM_ANDROID, PLATFORM_WEB + #define GLSL_VERSION 100 #endif //------------------------------------------------------------------------------------ @@ -34,14 +35,16 @@ int main(void) { // Initialization //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; SetConfigFlags(FLAG_MSAA_4X_HINT); - InitWindow(800, 450, "Normal Map"); + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - normal map"); - Camera camera = {0}; - camera.position = (Vector3){0.0f, 2.0f, -4.0f}; - camera.target = (Vector3){0.0f, 0.0f, 0.0f}; - camera.up = (Vector3){0.0f, 1.0f, 0.0f}; + Camera camera = { 0 }; + camera.position = (Vector3){ 0.0f, 2.0f, -4.0f }; + camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; camera.fovy = 45.0f; camera.projection = CAMERA_PERSPECTIVE; @@ -52,12 +55,13 @@ int main(void) // Get some required shader locations shader.locs[SHADER_LOC_MAP_NORMAL] = GetShaderLocation(shader, "normalMap"); shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos"); + // NOTE: "matModel" location name is automatically assigned on shader loading, // no need to get the location again if using that uniform name // shader.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocation(shader, "matModel"); - // This example uses just 1 point light. - Vector3 lightPosition = {0.0f, 1.0f, 0.0f}; + // This example uses just 1 point light + Vector3 lightPosition = { 0.0f, 1.0f, 0.0f }; int lightPosLoc = GetShaderLocation(shader, "lightPos"); // Load a plane model that has proper normals and tangents @@ -91,26 +95,25 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - // Move the light around on the X and Z axis using WASD keys - Vector3 direction = {0}; - if (IsKeyDown(KEY_W)) direction = Vector3Add(direction, (Vector3){0.0f, 0.0f, 1.0f}); - if (IsKeyDown(KEY_S)) direction = Vector3Add(direction, (Vector3){0.0f, 0.0f, -1.0f}); - if (IsKeyDown(KEY_D)) direction = Vector3Add(direction, (Vector3){-1.0f, 0.0f, 0.0f}); - if (IsKeyDown(KEY_A)) direction = Vector3Add(direction, (Vector3){1.0f, 0.0f, 0.0f}); + Vector3 direction = { 0 }; + if (IsKeyDown(KEY_W)) direction = Vector3Add(direction, (Vector3){ 0.0f, 0.0f, 1.0f }); + if (IsKeyDown(KEY_S)) direction = Vector3Add(direction, (Vector3){ 0.0f, 0.0f, -1.0f }); + if (IsKeyDown(KEY_D)) direction = Vector3Add(direction, (Vector3){ -1.0f, 0.0f, 0.0f }); + if (IsKeyDown(KEY_A)) direction = Vector3Add(direction, (Vector3){ 1.0f, 0.0f, 0.0f }); direction = Vector3Normalize(direction); - lightPosition = Vector3Add(lightPosition, Vector3Scale(direction, GetFrameTime() * 3.0f)); + lightPosition = Vector3Add(lightPosition, Vector3Scale(direction, GetFrameTime()*3.0f)); // Increase/Decrease the specular exponent(shininess) - if (IsKeyDown(KEY_UP)) specularExponent = Clamp(specularExponent + 40.0f * GetFrameTime(), 2.0f, 128.0f); - if (IsKeyDown(KEY_DOWN)) specularExponent = Clamp(specularExponent - 40.0f * GetFrameTime(), 2.0f, 128.0f); + if (IsKeyDown(KEY_UP)) specularExponent = Clamp(specularExponent + 40.0f*GetFrameTime(), 2.0f, 128.0f); + if (IsKeyDown(KEY_DOWN)) specularExponent = Clamp(specularExponent - 40.0f*GetFrameTime(), 2.0f, 128.0f); // Toggle normal map on and off if (IsKeyPressed(KEY_N)) useNormalMap = !useNormalMap; // Spin plane model at a constant rate - plane.transform = MatrixRotateY(GetTime() * 0.5f); + plane.transform = MatrixRotateY(GetTime()*0.5f); // Update shader values float lightPos[3] = {lightPosition.x, lightPosition.y, lightPosition.z}; @@ -138,19 +141,19 @@ int main(void) EndShaderMode(); - //Draw sphere to show light position + // Draw sphere to show light position DrawSphereWires(lightPosition, 0.2f, 8, 8, ORANGE); EndMode3D(); Color textColor = (useNormalMap) ? DARKGREEN : RED; const char *toggleStr = (useNormalMap) ? "On" : "Off"; - DrawText(TextFormat("Use key [N] to toggle normal map: %s", toggleStr), 10, 30, 20, textColor); + DrawText(TextFormat("Use key [N] to toggle normal map: %s", toggleStr), 10, 30, 10, textColor); int yOffset = 24; - DrawText("Use keys [W][A][S][D] to move the light", 10, 30 + yOffset * 1, 20, BLACK); - DrawText("Use keys [Up][Down] to change specular exponent", 10, 30 + yOffset * 2, 20, BLACK); - DrawText(TextFormat("Specular Exponent: %.2f", specularExponent), 10, 30 + yOffset * 3, 20, BLUE); + DrawText("Use keys [W][A][S][D] to move the light", 10, 30 + yOffset*1, 10, BLACK); + DrawText("Use keys [Up][Down] to change specular exponent", 10, 30 + yOffset*2, 10, BLACK); + DrawText(TextFormat("Specular Exponent: %.2f", specularExponent), 10, 30 + yOffset*3, 10, BLUE); DrawFPS(10, 10); diff --git a/examples/textures/textures_image_kernel.c b/examples/textures/textures_image_kernel.c index fa1bc92c7..cfd05092a 100644 --- a/examples/textures/textures_image_kernel.c +++ b/examples/textures/textures_image_kernel.c @@ -22,7 +22,7 @@ //------------------------------------------------------------------------------------ // Module functions declaration //------------------------------------------------------------------------------------ -void NormalizeKernel(float *kernel, int size); +static void NormalizeKernel(float *kernel, int size); //------------------------------------------------------------------------------------ // Program main entry point From 2365ac8defd2bc82b93ab6399e6513ef5e253e8d Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 17:31:11 +0200 Subject: [PATCH 223/242] REVIEWED: Example `shaders_normalmap` #5032 --- examples/shaders/shaders_normalmap.c | 10 +++++----- examples/shaders/shaders_normalmap.png | Bin 267835 -> 382358 bytes 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/shaders/shaders_normalmap.c b/examples/shaders/shaders_normalmap.c index 58c7deb48..fcbf6135d 100644 --- a/examples/shaders/shaders_normalmap.c +++ b/examples/shaders/shaders_normalmap.c @@ -148,14 +148,14 @@ int main(void) Color textColor = (useNormalMap) ? DARKGREEN : RED; const char *toggleStr = (useNormalMap) ? "On" : "Off"; - DrawText(TextFormat("Use key [N] to toggle normal map: %s", toggleStr), 10, 30, 10, textColor); + DrawText(TextFormat("Use key [N] to toggle normal map: %s", toggleStr), 10, 10, 10, textColor); int yOffset = 24; - DrawText("Use keys [W][A][S][D] to move the light", 10, 30 + yOffset*1, 10, BLACK); - DrawText("Use keys [Up][Down] to change specular exponent", 10, 30 + yOffset*2, 10, BLACK); - DrawText(TextFormat("Specular Exponent: %.2f", specularExponent), 10, 30 + yOffset*3, 10, BLUE); + DrawText("Use keys [W][A][S][D] to move the light", 10, 10 + yOffset*1, 10, BLACK); + DrawText("Use keys [Up][Down] to change specular exponent", 10, 10 + yOffset*2, 10, BLACK); + DrawText(TextFormat("Specular Exponent: %.2f", specularExponent), 10, 10 + yOffset*3, 10, BLUE); - DrawFPS(10, 10); + DrawFPS(screenWidth - 80, 10); EndDrawing(); //-------------------------------------------------------------------------------------- diff --git a/examples/shaders/shaders_normalmap.png b/examples/shaders/shaders_normalmap.png index b2db75eeceb02020ad430ad7c017540d445f5b81..f6057479ddfbb2689e3bccdd6c58fb7d4006df67 100644 GIT binary patch literal 382358 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

W{G@`1927g({@bKa+FS|b2j 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≦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

6ah>nQJbzR@fh- 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%0AHf}Jt;7rA>8Ppiz?fT?{IObtC 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

(^g^(7+DP21t1zYI=Ng_4bWg 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 U=I*2-CIA2c07*qoM6N<$g3RZuEC2ui From 6792e6e2dd8b4a3f056e50e30d9ac2ed9fd440f7 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 17:50:18 +0200 Subject: [PATCH 224/242] Updated required files for new example: `shaders_normalmap` --- examples/Makefile.Web | 8 + examples/README.md | 5 +- .../VS2022/examples/shaders_normalmap.vcxproj | 569 ++++++++++++++++++ tools/rexm/examples_report.md | 1 + 4 files changed, 581 insertions(+), 2 deletions(-) create mode 100644 projects/VS2022/examples/shaders_normalmap.vcxproj diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 20483e4e6..ef10840ee 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -1202,6 +1202,14 @@ shaders/shaders_multi_sample2d: shaders/shaders_multi_sample2d.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/shaders/glsl100/color_mix.fs@resources/shaders/glsl100/color_mix.fs +shaders/shaders_normalmap: shaders/shaders_normalmap.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file shaders/resources/shaders/glsl100/normalmap.vs@resources/shaders/glsl100/normalmap.vs \ + --preload-file shaders/resources/shaders/glsl100/normalmap.fs@resources/shaders/glsl100/normalmap.fs \ + --preload-file shaders/resources/models/plane.glb@resources/models/plane.glb \ + --preload-file shaders/resources/tiles_diffuse.png@resources/tiles_diffuse.png \ + --preload-file shaders/resources/tiles_normal.png@resources/tiles_normal.png + shaders/shaders_palette_switch: shaders/shaders_palette_switch.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/shaders/glsl100/palette_switch.fs@resources/shaders/glsl100/palette_switch.fs diff --git a/examples/README.md b/examples/README.md index 1c169d25a..cb17f8e84 100644 --- a/examples/README.md +++ b/examples/README.md @@ -16,7 +16,7 @@ You may find it easier to use than other toolchains, especially when it comes to - `zig build [module]` to compile all examples for a module (e.g. `zig build core`) - `zig build [example]` to compile _and run_ a particular example (e.g. `zig build core_basic_window`) -## EXAMPLES COLLECTION [TOTAL: 159] +## EXAMPLES COLLECTION [TOTAL: 160] ### category: core [36] @@ -170,7 +170,7 @@ Examples using raylib models functionality, including models loading/generation | [models_bone_socket](models/models_bone_socket.c) | models_bone_socket | ⭐⭐⭐⭐️ | 4.5 | 4.5 | [iP](https://github.com/ipzaur) | | [models_tesseract_view](models/models_tesseract_view.c) | models_tesseract_view | ⭐⭐☆☆ | 5.6 | 5.6 | [Timothy van der Valk](https://github.com/arceryz) | -### category: shaders [28] +### category: shaders [29] Examples using raylib shaders functionality, including shaders loading, parameters configuration and drawing using them (model shaders and postprocessing shaders). This functionality is directly provided by raylib [rlgl](../src/rlgl.c) module. @@ -193,6 +193,7 @@ Examples using raylib shaders functionality, including shaders loading, paramete | [shaders_hot_reloading](shaders/shaders_hot_reloading.c) | shaders_hot_reloading | ⭐⭐⭐☆ | 3.0 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) | | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | shaders_mesh_instancing | ⭐⭐⭐⭐️ | 3.7 | 4.2 | [seanpringle](https://github.com/seanpringle) | | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | shaders_multi_sample2d | ⭐⭐☆☆ | 3.5 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [shaders_normalmap](shaders/shaders_normalmap.c) | shaders_normalmap | ⭐⭐⭐⭐️ | 5.6 | 5.6 | [Jeremy Montgomery](https://github.com/Sir_Irk) | | [shaders_spotlight](shaders/shaders_spotlight.c) | shaders_spotlight | ⭐⭐☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | | [shaders_deferred_render](shaders/shaders_deferred_render.c) | shaders_deferred_render | ⭐⭐⭐⭐️ | 4.5 | 4.5 | [Justin Andreas Lacoste](https://github.com/27justin) | | [shaders_hybrid_render](shaders/shaders_hybrid_render.c) | shaders_hybrid_render | ⭐⭐⭐⭐️ | 4.2 | 4.2 | [Buğra Alptekin Sarı](https://github.com/BugraAlptekinSari) | diff --git a/projects/VS2022/examples/shaders_normalmap.vcxproj b/projects/VS2022/examples/shaders_normalmap.vcxproj new file mode 100644 index 000000000..79f976102 --- /dev/null +++ b/projects/VS2022/examples/shaders_normalmap.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {0981CA98-E4A5-4DF1-987F-A41D09131EFC} + Win32Proj + shaders_normalmap + 10.0 + shaders_normalmap + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/tools/rexm/examples_report.md b/tools/rexm/examples_report.md index ef1aca64b..6fa79bd50 100644 --- a/tools/rexm/examples_report.md +++ b/tools/rexm/examples_report.md @@ -153,6 +153,7 @@ Example elements validated: | shaders_hot_reloading | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_mesh_instancing | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_multi_sample2d | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_normalmap | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ❌ | ❌ | ❌ | ❌ | | shaders_spotlight | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_deferred_render | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_hybrid_render | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | From 570082deba05e7d9f87041481f2b7ca0ea3d4baf Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 18:23:20 +0200 Subject: [PATCH 225/242] `WARNING`: **NEW** raylib code CONVENTION: Comments do not end with '.' --- CONVENTIONS.md | 4 ++ examples/audio/audio_raw_stream.c | 4 +- examples/core/core_custom_frame_control.c | 2 +- examples/core/core_smooth_pixelperfect.c | 3 +- examples/examples_template.c | 24 ++++---- examples/models/models_animation.c | 2 +- examples/models/models_billboard.c | 4 +- examples/models/models_loading.c | 6 +- examples/models/models_loading_m3d.c | 2 +- examples/others/easings_testbed.c | 6 +- examples/others/raylib_opengl_interop.c | 12 ++-- examples/others/rlgl_compute_shader.c | 8 +-- examples/others/rlgl_standalone.c | 4 +- examples/shaders/shaders_basic_lighting.c | 4 +- examples/shaders/shaders_custom_uniform.c | 2 +- examples/shaders/shaders_deferred_render.c | 34 ++++++------ examples/shaders/shaders_eratosthenes.c | 6 +- examples/shaders/shaders_fog.c | 4 +- examples/shaders/shaders_hot_reloading.c | 2 +- examples/shaders/shaders_hybrid_render.c | 14 ++--- examples/shaders/shaders_julia_set.c | 13 ++--- examples/shaders/shaders_lightmap.c | 4 +- examples/shaders/shaders_model_shader.c | 2 +- examples/shaders/shaders_multi_sample2d.c | 2 +- examples/shaders/shaders_normalmap.c | 4 +- examples/shaders/shaders_palette_switch.c | 2 +- examples/shaders/shaders_postprocessing.c | 2 +- examples/shaders/shaders_raymarching.c | 2 +- examples/shaders/shaders_shadowmap.c | 2 +- examples/shaders/shaders_shapes_textures.c | 2 +- examples/shaders/shaders_spotlight.c | 8 +-- examples/shaders/shaders_texture_outline.c | 2 +- examples/shaders/shaders_texture_tiling.c | 2 +- examples/shaders/shaders_texture_waves.c | 2 +- .../shapes/shapes_easings_rectangle_array.c | 2 +- examples/shapes/shapes_rectangle_advanced.c | 6 +- examples/text/text_codepoints_loading.c | 2 +- examples/text/text_draw_3d.c | 10 ++-- examples/text/text_font_spritefont.c | 2 +- examples/text/text_input_box.c | 2 +- examples/text/text_rectangle_bounds.c | 4 +- examples/text/text_unicode.c | 4 +- examples/textures/textures_draw_tiled.c | 4 +- examples/textures/textures_image_channel.c | 4 +- .../textures/textures_particles_blending.c | 2 +- src/external/rl_gputex.h | 6 +- src/platforms/rcore_desktop_glfw.c | 55 ++++++++++--------- src/platforms/rcore_desktop_sdl.c | 35 ++++++------ src/platforms/rcore_drm.c | 32 +++++------ src/raudio.c | 14 ++--- src/raylib.h | 2 +- src/rmodels.c | 22 ++++---- src/rshapes.c | 6 +- src/rtext.c | 6 +- src/rtextures.c | 2 +- 55 files changed, 210 insertions(+), 208 deletions(-) diff --git a/CONVENTIONS.md b/CONVENTIONS.md index b7ace0275..17c02e216 100644 --- a/CONVENTIONS.md +++ b/CONVENTIONS.md @@ -28,6 +28,10 @@ Some other conventions to follow: - **ALWAYS** initialize all defined variables. - **Do not use TABS**, use 4 spaces instead. - Avoid trailing spaces, please, avoid them + - Comments always start with space + capital letter and never end with a '.', place them **before** the line(s) they refer to +```c +// This is a comment in raylib or raylib examples +``` - Control flow statements always are followed **by a space**: ```c if (condition) value = 0; diff --git a/examples/audio/audio_raw_stream.c b/examples/audio/audio_raw_stream.c index fb36830ce..84c8963e3 100644 --- a/examples/audio/audio_raw_stream.c +++ b/examples/audio/audio_raw_stream.c @@ -108,8 +108,6 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - - // Sample mouse input. mousePosition = GetMousePosition(); if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) @@ -125,7 +123,7 @@ int main(void) // Compute two cycles to allow the buffer padding, simplifying any modulation, resampling, etc. if (frequency != oldFrequency) { - // Compute wavelength. Limit size in both directions. + // Compute wavelength. Limit size in both directions //int oldWavelength = waveLength; waveLength = (int)(22050/frequency); if (waveLength > MAX_SAMPLES/2) waveLength = MAX_SAMPLES/2; diff --git a/examples/core/core_custom_frame_control.c b/examples/core/core_custom_frame_control.c index ed08f8e87..40af52f41 100644 --- a/examples/core/core_custom_frame_control.c +++ b/examples/core/core_custom_frame_control.c @@ -12,7 +12,7 @@ * 4. PollInputEvents() * * To avoid steps 2, 3 and 4, flag SUPPORT_CUSTOM_FRAME_CONTROL can be enabled in -* config.h (it requires recompiling raylib). This way those steps are up to the user. +* config.h (it requires recompiling raylib). This way those steps are up to the user * * Note that enabling this flag invalidates some functions: * - GetFrameTime() diff --git a/examples/core/core_smooth_pixelperfect.c b/examples/core/core_smooth_pixelperfect.c index 0bd82db43..697b2ff09 100644 --- a/examples/core/core_smooth_pixelperfect.c +++ b/examples/core/core_smooth_pixelperfect.c @@ -43,7 +43,8 @@ int main(void) Camera2D screenSpaceCamera = { 0 }; // Smoothing camera screenSpaceCamera.zoom = 1.0f; - RenderTexture2D target = LoadRenderTexture(virtualScreenWidth, virtualScreenHeight); // This is where we'll draw all our objects. + // Load render texture to draw all our objects + RenderTexture2D target = LoadRenderTexture(virtualScreenWidth, virtualScreenHeight); Rectangle rec01 = { 70.0f, 35.0f, 20.0f, 20.0f }; Rectangle rec02 = { 90.0f, 55.0f, 30.0f, 10.0f }; diff --git a/examples/examples_template.c b/examples/examples_template.c index 433fe57bd..2bb223a9c 100644 --- a/examples/examples_template.c +++ b/examples/examples_template.c @@ -6,30 +6,30 @@ 1. File naming: _ - Lower case filename, words separated by underscore, no more than 3-4 words in total to describe the example. referes to the primary - raylib module the example is more related with (code, shapes, textures, models, shaders, raudio). + raylib module the example is more related with (code, shapes, textures, models, shaders, raudio) i.e: core_input_multitouch, shapes_lines_bezier, shaders_palette_switch 2. Follow below template structure, example info should list the module, the short description - and the author of the example, twitter or github info could be also provided for the author. - Short description should also be used on the title of the window. + and the author of the example, twitter or github info could be also provided for the author + Short description should also be used on the title of the window 3. Code should be organized by sections:[Initialization]- [Update] - [Draw] - [De-Initialization] Place your code between the dotted lines for every section, please don't mix update logic with drawing - and remember to unload all loaded resources. + and remember to unload all loaded resources 4. Code should follow raylib conventions: https://github.com/raysan5/raylib/wiki/raylib-coding-conventions - Try to be very organized, using line-breaks appropiately. + Try to be very organized, using line-breaks appropiately - 5. Add comments to the specific parts of code the example is focus on. - Don't abuse with comments, try to be clear and impersonal on the comments. + 5. Add comments to the specific parts of code the example is focus on + Don't abuse with comments, try to be clear and impersonal on the comments - 6. Try to keep the example simple, under 300 code lines if possible. Try to avoid external dependencies. - Try to avoid defining functions outside the main(). Example should be as self-contained as possible. + 6. Try to keep the example simple, under 300 code lines if possible. Try to avoid external dependencies + Try to avoid defining functions outside the main(). Example should be as self-contained as possible 7. About external resources, they should be placed in a [resources] folder and those resources - should be open and free for use and distribution. Avoid propietary content. + should be open and free for use and distribution. Avoid propietary content - 8. Try to keep the example simple but with a creative touch. + 8. Try to keep the example simple but with a creative touch Simple but beautiful examples are more appealing to users! 9. In case of additional information is required, just come to raylib Discord channel: example-contributions @@ -37,7 +37,7 @@ 10. Have fun! The following files should be updated when adding a new example, it's planned to create some - script to automatize this process but not available yet. + script to automatize this process but not available yet - raylib/examples//_example_name.c - raylib/examples//_example_name.png diff --git a/examples/models/models_animation.c b/examples/models/models_animation.c index a0f514ad2..2d98cb5ed 100644 --- a/examples/models/models_animation.c +++ b/examples/models/models_animation.c @@ -17,7 +17,7 @@ * * NOTE: To export a model from blender, make sure it is not posed, the vertices need to be * in the same position as they would be in edit mode and the scale of your models is -* set to 0. Scaling can be done from the export menu. +* set to 0. Scaling can be done from the export menu * ********************************************************************************************/ diff --git a/examples/models/models_billboard.c b/examples/models/models_billboard.c index 84c02ea23..82610a4df 100644 --- a/examples/models/models_billboard.c +++ b/examples/models/models_billboard.c @@ -40,7 +40,7 @@ int main(void) Vector3 billPositionStatic = { 0.0f, 2.0f, 0.0f }; // Position of static billboard Vector3 billPositionRotating = { 1.0f, 2.0f, 1.0f }; // Position of rotating billboard - // Entire billboard texture, source is used to take a segment from a larger texture. + // Entire billboard texture, source is used to take a segment from a larger texture Rectangle source = { 0.0f, 0.0f, (float)bill.width, (float)bill.height }; // NOTE: Billboard locked on axis-Y @@ -54,7 +54,7 @@ int main(void) Vector2 origin = Vector2Scale(size, 0.5f); // Distance is needed for the correct billboard draw order - // Larger distance (further away from the camera) should be drawn prior to smaller distance. + // Larger distance (further away from the camera) should be drawn prior to smaller distance float distanceStatic; float distanceRotating; float rotation = 0.0f; diff --git a/examples/models/models_loading.c b/examples/models/models_loading.c index 2a674eeaa..82ae40dc4 100644 --- a/examples/models/models_loading.c +++ b/examples/models/models_loading.c @@ -7,11 +7,11 @@ * NOTE: raylib supports multiple models file formats: * * - OBJ > Text file format. Must include vertex position-texcoords-normals information, -* if files references some .mtl materials file, it will be loaded (or try to). +* if files references some .mtl materials file, it will be loaded (or try to) * - GLTF > Text/binary file format. Includes lot of information and it could -* also reference external files, raylib will try loading mesh and materials data. +* also reference external files, raylib will try loading mesh and materials data * - IQM > Binary file format. Includes mesh vertex data but also animation data, -* raylib can load .iqm animations. +* raylib can load .iqm animations * - VOX > Binary file format. MagikaVoxel mesh format: * https://github.com/ephtracy/voxel-model/blob/master/MagicaVoxel-file-format-vox.txt * - M3D > Binary file format. Model 3D format: diff --git a/examples/models/models_loading_m3d.c b/examples/models/models_loading_m3d.c index 38dfbd51e..871bb54dd 100644 --- a/examples/models/models_loading_m3d.c +++ b/examples/models/models_loading_m3d.c @@ -119,7 +119,7 @@ int main(void) // without a -1, we would always draw a cube at the origin for (int i = 0; i < model.boneCount - 1; i++) { - // By default the model is loaded in bind-pose by LoadModel(). + // By default the model is loaded in bind-pose by LoadModel() // But if UpdateModelAnimation() has been called at least once // then the model is already in animation pose, so we need the animated skeleton if (!animPlaying || !animsCount) diff --git a/examples/others/easings_testbed.c b/examples/others/easings_testbed.c index e3fa0e84b..31daeecee 100644 --- a/examples/others/easings_testbed.c +++ b/examples/others/easings_testbed.c @@ -221,10 +221,12 @@ int main(void) } -// NoEase function, used when "no easing" is selected for any axis. It just ignores all parameters besides b. +// NoEase function, used when "no easing" is selected for any axis +// It just ignores all parameters besides b static float NoEase(float t, float b, float c, float d) { - float burn = t + b + c + d; // Hack to avoid compiler warning (about unused variables) + // Hack to avoid compiler warning (about unused variables) + float burn = t + b + c + d; d += burn; return b; diff --git a/examples/others/raylib_opengl_interop.c b/examples/others/raylib_opengl_interop.c index 53fc744db..4dc534d12 100644 --- a/examples/others/raylib_opengl_interop.c +++ b/examples/others/raylib_opengl_interop.c @@ -16,13 +16,13 @@ ******************************************************************************************** * * Mixes raylib and plain OpenGL code to draw a GL_POINTS based particle system. The -* primary point is to demonstrate raylib and OpenGL interop. +* primary point is to demonstrate raylib and OpenGL interop * * rlgl batched draw operations internally so we have to flush the current batch before -* doing our own OpenGL work (rlDrawRenderBatchActive()). +* doing our own OpenGL work (rlDrawRenderBatchActive()) * * The example also demonstrates how to get the current model view projection matrix of -* raylib. That way raylib cameras and so on work as expected. +* raylib. That way raylib cameras and so on work as expected * ********************************************************************************************/ @@ -87,13 +87,13 @@ int main(void) particles[i].x = (float)GetRandomValue(20, screenWidth - 20); particles[i].y = (float)GetRandomValue(50, screenHeight - 20); - // Give each particle a slightly different period. But don't spread it to much. - // This way the particles line up every so often and you get a glimps of what is going on. + // Give each particle a slightly different period. But don't spread it to much + // This way the particles line up every so often and you get a glimps of what is going on particles[i].period = (float)GetRandomValue(10, 30)/10.0f; } // Create a plain OpenGL vertex buffer with the data and an vertex array object - // that feeds the data from the buffer into the vertexPosition shader attribute. + // that feeds the data from the buffer into the vertexPosition shader attribute GLuint vao = 0; GLuint vbo = 0; glGenVertexArrays(1, &vao); diff --git a/examples/others/rlgl_compute_shader.c b/examples/others/rlgl_compute_shader.c index 74a776d29..07a10b1d5 100644 --- a/examples/others/rlgl_compute_shader.c +++ b/examples/others/rlgl_compute_shader.c @@ -23,11 +23,11 @@ #include -// IMPORTANT: This must match gol*.glsl GOL_WIDTH constant. -// This must be a multiple of 16 (check golLogic compute dispatch). +// IMPORTANT: This must match gol*.glsl GOL_WIDTH constant +// This must be a multiple of 16 (check golLogic compute dispatch) #define GOL_WIDTH 768 -// Maximum amount of queued draw commands (squares draw from mouse down events). +// Maximum amount of queued draw commands (squares draw from mouse down events) #define MAX_BUFFERED_TRANSFERTS 48 // Game Of Life Update Command @@ -160,7 +160,7 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - // Unload shader buffers objects. + // Unload shader buffers objects rlUnloadShaderBuffer(ssboA); rlUnloadShaderBuffer(ssboB); rlUnloadShaderBuffer(ssboTransfert); diff --git a/examples/others/rlgl_standalone.c b/examples/others/rlgl_standalone.c index e74ca0f6c..631699a5d 100644 --- a/examples/others/rlgl_standalone.c +++ b/examples/others/rlgl_standalone.c @@ -9,7 +9,7 @@ * * Example originally created with raylib 1.6, last time updated with raylib 4.0 * -* WARNING: This example is intended only for PLATFORM_DESKTOP and OpenGL 3.3 Core profile. +* WARNING: This example is intended only for PLATFORM_DESKTOP and OpenGL 3.3 Core profile * It could work on other platforms if redesigned for those platforms (out-of-scope) * * DEPENDENCIES: @@ -48,7 +48,7 @@ * 2. Altered source versions must be plainly marked as such, and must not be misrepresented * as being the original software. * -* 3. This notice may not be removed or altered from any source distribution. +* 3. This notice may not be removed or altered from any source distribution * ********************************************************************************************/ diff --git a/examples/shaders/shaders_basic_lighting.c b/examples/shaders/shaders_basic_lighting.c index 913e4aeeb..a2a392126 100644 --- a/examples/shaders/shaders_basic_lighting.c +++ b/examples/shaders/shaders_basic_lighting.c @@ -5,9 +5,9 @@ * Example complexity rating: [★★★★] 4/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * -* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3). +* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3) * * Example originally created with raylib 3.0, last time updated with raylib 4.2 * diff --git a/examples/shaders/shaders_custom_uniform.c b/examples/shaders/shaders_custom_uniform.c index 951759079..d6738cf57 100644 --- a/examples/shaders/shaders_custom_uniform.c +++ b/examples/shaders/shaders_custom_uniform.c @@ -5,7 +5,7 @@ * Example complexity rating: [★★☆☆] 2/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * * NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example * on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders diff --git a/examples/shaders/shaders_deferred_render.c b/examples/shaders/shaders_deferred_render.c index 1fe6d3a0c..96c118c61 100644 --- a/examples/shaders/shaders_deferred_render.c +++ b/examples/shaders/shaders_deferred_render.c @@ -99,44 +99,44 @@ int main(void) // NOTE: Vertex positions are stored in a texture for simplicity. A better approach would use a depth texture // (instead of a detph renderbuffer) to reconstruct world positions in the final render shader via clip-space position, - // depth, and the inverse view/projection matrices. + // depth, and the inverse view/projection matrices - // 16-bit precision ensures OpenGL ES 3 compatibility, though it may lack precision for real scenarios. + // 16-bit precision ensures OpenGL ES 3 compatibility, though it may lack precision for real scenarios // But as mentioned above, the positions could be reconstructed instead of stored. If not targeting OpenGL ES - // and you wish to maintain this approach, consider using `RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32`. + // and you wish to maintain this approach, consider using `RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32` gBuffer.positionTexture = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16, 1); - // Similarly, 16-bit precision is used for normals ensures OpenGL ES 3 compatibility. - // This is generally sufficient, but a 16-bit fixed-point format offer a better uniform precision in all orientations. + // Similarly, 16-bit precision is used for normals ensures OpenGL ES 3 compatibility + // This is generally sufficient, but a 16-bit fixed-point format offer a better uniform precision in all orientations gBuffer.normalTexture = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16, 1); - // Albedo (diffuse color) and specular strength can be combined into one texture. - // The color in RGB, and the specular strength in the alpha channel. + // Albedo (diffuse color) and specular strength can be combined into one texture + // The color in RGB, and the specular strength in the alpha channel gBuffer.albedoSpecTexture = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1); // Activate the draw buffers for our framebuffer rlActiveDrawBuffers(3); - // Now we attach our textures to the framebuffer. + // Now we attach our textures to the framebuffer rlFramebufferAttach(gBuffer.framebuffer, gBuffer.positionTexture, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D, 0); rlFramebufferAttach(gBuffer.framebuffer, gBuffer.normalTexture, RL_ATTACHMENT_COLOR_CHANNEL1, RL_ATTACHMENT_TEXTURE2D, 0); rlFramebufferAttach(gBuffer.framebuffer, gBuffer.albedoSpecTexture, RL_ATTACHMENT_COLOR_CHANNEL2, RL_ATTACHMENT_TEXTURE2D, 0); - // Finally we attach the depth buffer. + // Finally we attach the depth buffer gBuffer.depthRenderbuffer = rlLoadTextureDepth(screenWidth, screenHeight, true); rlFramebufferAttach(gBuffer.framebuffer, gBuffer.depthRenderbuffer, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0); - // Make sure our framebuffer is complete. + // Make sure our framebuffer is complete // NOTE: rlFramebufferComplete() automatically unbinds the framebuffer, so we don't have - // to rlDisableFramebuffer() here. + // to rlDisableFramebuffer() here if (!rlFramebufferComplete(gBuffer.framebuffer)) { TraceLog(LOG_WARNING, "Framebuffer is not complete"); } - // Now we initialize the sampler2D uniform's in the deferred shader. + // Now we initialize the sampler2D uniform's in the deferred shader // We do this by setting the uniform's values to the texture units that - // we later bind our g-buffer textures to. + // we later bind our g-buffer textures to rlEnableShader(deferredShader.id); int texUnitPosition = 0; int texUnitNormal = 1; @@ -219,7 +219,7 @@ int main(void) rlDisableColorBlend(); BeginMode3D(camera); // NOTE: We have to use rlEnableShader here. `BeginShaderMode` or thus `rlSetShader` - // will not work, as they won't immediately load the shader program. + // will not work, as they won't immediately load the shader program rlEnableShader(gbufferShader.id); // When drawing a model here, make sure that the material's shaders // are set to the gbuffer shader! @@ -236,7 +236,7 @@ int main(void) EndMode3D(); rlEnableColorBlend(); - // Go back to the default framebuffer (0) and draw our deferred shading. + // Go back to the default framebuffer (0) and draw our deferred shading rlDisableFramebuffer(); rlClearScreenBuffers(); // Clear color & depth buffer @@ -264,10 +264,10 @@ int main(void) rlEnableColorBlend(); EndMode3D(); - // As a last step, we now copy over the depth buffer from our g-buffer to the default framebuffer. + // As a last step, we now copy over the depth buffer from our g-buffer to the default framebuffer rlBindFramebuffer(RL_READ_FRAMEBUFFER, gBuffer.framebuffer); rlBindFramebuffer(RL_DRAW_FRAMEBUFFER, 0); - rlBlitFramebuffer(0, 0, screenWidth, screenHeight, 0, 0, screenWidth, screenHeight, 0x00000100); // GL_DEPTH_BUFFER_BIT + rlBlitFramebuffer(0, 0, screenWidth, screenHeight, 0, 0, screenWidth, screenHeight, 0x00000100); // GL_DEPTH_BUFFER_BIT rlDisableFramebuffer(); // Since our shader is now done and disabled, we can draw spheres diff --git a/examples/shaders/shaders_eratosthenes.c b/examples/shaders/shaders_eratosthenes.c index 03dfc2125..4a974c9c8 100644 --- a/examples/shaders/shaders_eratosthenes.c +++ b/examples/shaders/shaders_eratosthenes.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★★☆] 3/4 * -* NOTE: Sieve of Eratosthenes, the earliest known (ancient Greek) prime number sieve. +* NOTE: Sieve of Eratosthenes, the earliest known (ancient Greek) prime number sieve * * "Sift the twos and sift the threes, * The Sieve of Eratosthenes. @@ -12,9 +12,9 @@ * the numbers that are left are prime." * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * -* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3). +* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3) * * Example originally created with raylib 2.5, last time updated with raylib 4.0 * diff --git a/examples/shaders/shaders_fog.c b/examples/shaders/shaders_fog.c index 2e589eec4..3307f88f4 100644 --- a/examples/shaders/shaders_fog.c +++ b/examples/shaders/shaders_fog.c @@ -5,9 +5,9 @@ * Example complexity rating: [★★★☆] 3/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * -* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3). +* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3) * * Example originally created with raylib 2.5, last time updated with raylib 3.7 * diff --git a/examples/shaders/shaders_hot_reloading.c b/examples/shaders/shaders_hot_reloading.c index b11245dcc..ef46e9ecf 100644 --- a/examples/shaders/shaders_hot_reloading.c +++ b/examples/shaders/shaders_hot_reloading.c @@ -5,7 +5,7 @@ * Example complexity rating: [★★★☆] 3/4 * * NOTE: This example requires raylib OpenGL 3.3 for shaders support and only #version 330 -* is currently supported. OpenGL ES 2.0 platforms are not supported at the moment. +* is currently supported. OpenGL ES 2.0 platforms are not supported at the moment * * Example originally created with raylib 3.0, last time updated with raylib 3.5 * diff --git a/examples/shaders/shaders_hybrid_render.c b/examples/shaders/shaders_hybrid_render.c index 0c7e789fd..5d4f8aba1 100644 --- a/examples/shaders/shaders_hybrid_render.c +++ b/examples/shaders/shaders_hybrid_render.c @@ -61,15 +61,15 @@ int main(void) // You are required to write depth for all shaders if one shader does it Shader shdrRaster = LoadShader(0, TextFormat("resources/shaders/glsl%i/hybrid_raster.fs", GLSL_VERSION)); - // Declare Struct used to store camera locs. + // Declare Struct used to store camera locs RayLocs marchLocs = {0}; - // Fill the struct with shader locs. + // Fill the struct with shader locs marchLocs.camPos = GetShaderLocation(shdrRaymarch, "camPos"); marchLocs.camDir = GetShaderLocation(shdrRaymarch, "camDir"); marchLocs.screenCenter = GetShaderLocation(shdrRaymarch, "screenCenter"); - // Transfer screenCenter position to shader. Which is used to calculate ray direction. + // Transfer screenCenter position to shader. Which is used to calculate ray direction Vector2 screenCenter = {.x = screenWidth/2.0f, .y = screenHeight/2.0f}; SetShaderValue(shdrRaymarch, marchLocs.screenCenter , &screenCenter , SHADER_UNIFORM_VEC2); @@ -85,7 +85,7 @@ int main(void) .projection = CAMERA_PERSPECTIVE // Camera projection type }; - // Camera FOV is pre-calculated in the camera Distance. + // Camera FOV is pre-calculated in the camera distance float camDist = 1.0f/(tanf(camera.fovy*0.5f*DEG2RAD)); SetTargetFPS(60); // Set our game to run at 60 frames-per-second @@ -98,10 +98,10 @@ int main(void) //---------------------------------------------------------------------------------- UpdateCamera(&camera, CAMERA_ORBITAL); - // Update Camera Postion in the ray march shader. + // Update Camera Postion in the ray march shader SetShaderValue(shdrRaymarch, marchLocs.camPos, &(camera.position), RL_SHADER_UNIFORM_VEC3); - // Update Camera Looking Vector. Vector length determines FOV. + // Update Camera Looking Vector. Vector length determines FOV Vector3 camDir = Vector3Scale( Vector3Normalize( Vector3Subtract(camera.target, camera.position)) , camDist); SetShaderValue(shdrRaymarch, marchLocs.camDir, &(camDir), RL_SHADER_UNIFORM_VEC3); //---------------------------------------------------------------------------------- @@ -113,7 +113,7 @@ int main(void) ClearBackground(WHITE); // Raymarch Scene - rlEnableDepthTest(); //Manually enable Depth Test to handle multiple rendering methods. + rlEnableDepthTest(); // Manually enable Depth Test to handle multiple rendering methods BeginShaderMode(shdrRaymarch); DrawRectangleRec((Rectangle){0,0, (float)screenWidth, (float)screenHeight},WHITE); EndShaderMode(); diff --git a/examples/shaders/shaders_julia_set.c b/examples/shaders/shaders_julia_set.c index 72ff31ad7..da5564321 100644 --- a/examples/shaders/shaders_julia_set.c +++ b/examples/shaders/shaders_julia_set.c @@ -5,9 +5,9 @@ * Example complexity rating: [★★★☆] 3/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * -* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3). +* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3) * * Example originally created with raylib 2.5, last time updated with raylib 4.0 * @@ -109,7 +109,7 @@ int main(void) SetShaderValue(shader, cLoc, c, SHADER_UNIFORM_VEC2); } - // If "R" is pressed, reset zoom and offset. + // If "R" is pressed, reset zoom and offset if (IsKeyPressed(KEY_R)) { zoom = startingZoom; @@ -125,17 +125,16 @@ int main(void) if (IsKeyPressed(KEY_RIGHT)) incrementSpeed++; else if (IsKeyPressed(KEY_LEFT)) incrementSpeed--; - // If either left or right button is pressed, zoom in/out. + // If either left or right button is pressed, zoom in/out if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) || IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) { - // Change zoom. If Mouse left -> zoom in. Mouse right -> zoom out. + // Change zoom. If Mouse left -> zoom in. Mouse right -> zoom out zoom *= IsMouseButtonDown(MOUSE_BUTTON_LEFT)? zoomSpeed : 1.0f/zoomSpeed; const Vector2 mousePos = GetMousePosition(); Vector2 offsetVelocity; // Find the velocity at which to change the camera. Take the distance of the mouse - // from the center of the screen as the direction, and adjust magnitude based on - // the current zoom. + // from the center of the screen as the direction, and adjust magnitude based on the current zoom offsetVelocity.x = (mousePos.x/(float)screenWidth - 0.5f)*offsetSpeedMul/zoom; offsetVelocity.y = (mousePos.y/(float)screenHeight - 0.5f)*offsetSpeedMul/zoom; diff --git a/examples/shaders/shaders_lightmap.c b/examples/shaders/shaders_lightmap.c index a393c1afe..3da900921 100644 --- a/examples/shaders/shaders_lightmap.c +++ b/examples/shaders/shaders_lightmap.c @@ -5,9 +5,9 @@ * Example complexity rating: [★★★☆] 3/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * -* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3). +* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3) * * Example originally created with raylib 4.5, last time updated with raylib 4.5 * diff --git a/examples/shaders/shaders_model_shader.c b/examples/shaders/shaders_model_shader.c index b448bece0..42cfff4aa 100644 --- a/examples/shaders/shaders_model_shader.c +++ b/examples/shaders/shaders_model_shader.c @@ -5,7 +5,7 @@ * Example complexity rating: [★★☆☆] 2/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * * NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example * on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders diff --git a/examples/shaders/shaders_multi_sample2d.c b/examples/shaders/shaders_multi_sample2d.c index 67cfb2058..4e732ce1a 100644 --- a/examples/shaders/shaders_multi_sample2d.c +++ b/examples/shaders/shaders_multi_sample2d.c @@ -5,7 +5,7 @@ * Example complexity rating: [★★☆☆] 2/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * * NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example * on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders diff --git a/examples/shaders/shaders_normalmap.c b/examples/shaders/shaders_normalmap.c index fcbf6135d..656c8597e 100644 --- a/examples/shaders/shaders_normalmap.c +++ b/examples/shaders/shaders_normalmap.c @@ -5,7 +5,7 @@ * Example complexity rating: [★★★★] 4/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, - * OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. + * OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * * Example originally created with raylib 5.6, last time updated with raylib 5.6 * @@ -79,7 +79,7 @@ int main(void) SetTextureFilter(plane.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture, TEXTURE_FILTER_TRILINEAR); SetTextureFilter(plane.materials[0].maps[MATERIAL_MAP_NORMAL].texture, TEXTURE_FILTER_TRILINEAR); - // Specular exponent AKA shininess of the material. + // Specular exponent AKA shininess of the material float specularExponent = 8.0f; int specularExponentLoc = GetShaderLocation(shader, "specularExponent"); diff --git a/examples/shaders/shaders_palette_switch.c b/examples/shaders/shaders_palette_switch.c index 50f80b8f0..df35db447 100644 --- a/examples/shaders/shaders_palette_switch.c +++ b/examples/shaders/shaders_palette_switch.c @@ -5,7 +5,7 @@ * Example complexity rating: [★★★☆] 3/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * * NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example * on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders diff --git a/examples/shaders/shaders_postprocessing.c b/examples/shaders/shaders_postprocessing.c index 156474e60..fade145ff 100644 --- a/examples/shaders/shaders_postprocessing.c +++ b/examples/shaders/shaders_postprocessing.c @@ -5,7 +5,7 @@ * Example complexity rating: [★★★☆] 3/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * * NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example * on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders diff --git a/examples/shaders/shaders_raymarching.c b/examples/shaders/shaders_raymarching.c index ab69fe69a..cc1badb9f 100644 --- a/examples/shaders/shaders_raymarching.c +++ b/examples/shaders/shaders_raymarching.c @@ -5,7 +5,7 @@ * Example complexity rating: [★★★★] 4/4 * * NOTE: This example requires raylib OpenGL 3.3 for shaders support and only #version 330 -* is currently supported. OpenGL ES 2.0 platforms are not supported at the moment. +* is currently supported. OpenGL ES 2.0 platforms are not supported at the moment * * Example originally created with raylib 2.0, last time updated with raylib 4.2 * diff --git a/examples/shaders/shaders_shadowmap.c b/examples/shaders/shaders_shadowmap.c index 7f98a25fe..9c7111a8b 100644 --- a/examples/shaders/shaders_shadowmap.c +++ b/examples/shaders/shaders_shadowmap.c @@ -44,7 +44,7 @@ int main(void) SetConfigFlags(FLAG_MSAA_4X_HINT); // Shadows are a HUGE topic, and this example shows an extremely simple implementation of the shadowmapping algorithm, // which is the industry standard for shadows. This algorithm can be extended in a ridiculous number of ways to improve - // realism and also adapt it for different scenes. This is pretty much the simplest possible implementation. + // realism and also adapt it for different scenes. This is pretty much the simplest possible implementation InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shadowmap"); Camera3D cam = (Camera3D){ 0 }; diff --git a/examples/shaders/shaders_shapes_textures.c b/examples/shaders/shaders_shapes_textures.c index 107b09c03..b0f7752ac 100644 --- a/examples/shaders/shaders_shapes_textures.c +++ b/examples/shaders/shaders_shapes_textures.c @@ -5,7 +5,7 @@ * Example complexity rating: [★★☆☆] 2/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * * NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example * on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders diff --git a/examples/shaders/shaders_spotlight.c b/examples/shaders/shaders_spotlight.c index 288292cb6..a0ee7a421 100644 --- a/examples/shaders/shaders_spotlight.c +++ b/examples/shaders/shaders_spotlight.c @@ -20,13 +20,13 @@ * * The right hand side of the screen there is just enough light to see whats * going on without the spot light, great for a stealth type game where you -* have to avoid the spotlights. +* have to avoid the spotlights * -* The left hand side of the screen is in pitch dark except for where the spotlights are. +* The left hand side of the screen is in pitch dark except for where the spotlights are * * Although this example doesn't scale like the letterbox example, you could integrate * the two techniques, but by scaling the actual colour of the render texture rather -* than using alpha as a mask. +* than using alpha as a mask * ********************************************************************************************/ @@ -115,7 +115,7 @@ int main(void) } // Tell the shader how wide the screen is so we can have - // a pitch black half and a dimly lit half. + // a pitch black half and a dimly lit half unsigned int wLoc = GetShaderLocation(shdrSpot, "screenWidth"); float sw = (float)GetScreenWidth(); SetShaderValue(shdrSpot, wLoc, &sw, SHADER_UNIFORM_FLOAT); diff --git a/examples/shaders/shaders_texture_outline.c b/examples/shaders/shaders_texture_outline.c index 68db65188..d7a3a1a07 100644 --- a/examples/shaders/shaders_texture_outline.c +++ b/examples/shaders/shaders_texture_outline.c @@ -5,7 +5,7 @@ * Example complexity rating: [★★★☆] 3/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * * Example originally created with raylib 4.0, last time updated with raylib 4.0 * diff --git a/examples/shaders/shaders_texture_tiling.c b/examples/shaders/shaders_texture_tiling.c index 9752b871f..14e15b8b2 100644 --- a/examples/shaders/shaders_texture_tiling.c +++ b/examples/shaders/shaders_texture_tiling.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★☆☆] 2/4 * -* Example demonstrates how to tile a texture on a 3D model using raylib. +* Example demonstrates how to tile a texture on a 3D model using raylib * * Example originally created with raylib 4.5, last time updated with raylib 4.5 * diff --git a/examples/shaders/shaders_texture_waves.c b/examples/shaders/shaders_texture_waves.c index 73ba738c6..c957635c4 100644 --- a/examples/shaders/shaders_texture_waves.c +++ b/examples/shaders/shaders_texture_waves.c @@ -5,7 +5,7 @@ * Example complexity rating: [★★☆☆] 2/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * * NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example * on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders diff --git a/examples/shapes/shapes_easings_rectangle_array.c b/examples/shapes/shapes_easings_rectangle_array.c index 912926ea4..c55c07979 100644 --- a/examples/shapes/shapes_easings_rectangle_array.c +++ b/examples/shapes/shapes_easings_rectangle_array.c @@ -5,7 +5,7 @@ * Example complexity rating: [★★★☆] 3/4 * * NOTE: This example requires 'easings.h' library, provided on raylib/src. Just copy -* the library to same directory as example or make sure it's available on include path. +* the library to same directory as example or make sure it's available on include path * * Example originally created with raylib 2.0, last time updated with raylib 2.5 * diff --git a/examples/shapes/shapes_rectangle_advanced.c b/examples/shapes/shapes_rectangle_advanced.c index d0366f616..a3f8adf84 100644 --- a/examples/shapes/shapes_rectangle_advanced.c +++ b/examples/shapes/shapes_rectangle_advanced.c @@ -265,10 +265,10 @@ static void DrawRectangleRoundedGradientH(Rectangle rec, float roundnessLeft, fl rlSetTexture(0); #else - // Here we use the 'Diagram' to guide ourselves to which point receives what color. + // Here we use the 'Diagram' to guide ourselves to which point receives what color // By choosing the color correctly associated with a pointe the gradient effect - // will naturally come from OpenGL interpolation. - // But this time instead of Quad, we think in triangles. + // will naturally come from OpenGL interpolation + // But this time instead of Quad, we think in triangles rlBegin(RL_TRIANGLES); // Draw all of the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner diff --git a/examples/text/text_codepoints_loading.c b/examples/text/text_codepoints_loading.c index 1cbd80ac9..28d15c33f 100644 --- a/examples/text/text_codepoints_loading.c +++ b/examples/text/text_codepoints_loading.c @@ -39,7 +39,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [text] example - codepoints loading"); // Convert each utf-8 character into its - // corresponding codepoint in the font file. + // corresponding codepoint in the font file int codepointCount = 0; int *codepoints = LoadCodepoints(text, &codepointCount); diff --git a/examples/text/text_draw_3d.c b/examples/text/text_draw_3d.c index 734d9d744..d87fb2b86 100644 --- a/examples/text/text_draw_3d.c +++ b/examples/text/text_draw_3d.c @@ -6,12 +6,12 @@ * * NOTE: Draw a 2D text in 3D space, each letter is drawn in a quad (or 2 quads if backface is set) * where the texture coodinates of each quad map to the texture coordinates of the glyphs -* inside the font texture. +* inside the font texture * * A more efficient approach, i believe, would be to render the text in a render texture and * map that texture to a plane and render that, or maybe a shader but my method allows more * flexibility...for example to change position of each letter individually to make somethink -* like a wavy text effect. +* like a wavy text effect * * Special thanks to: * @Nighten for the DrawTextStyle() code https://github.com/NightenDushi/Raylib_DrawTextStyle @@ -71,7 +71,7 @@ static void DrawText3D(Font font, const char *text, Vector3 position, float font // Draw a 2D text in 3D space and wave the parts that start with '~~' and end with '~~' // This is a modified version of the original code by @Nighten found here https://github.com/NightenDushi/Raylib_DrawTextStyle static void DrawTextWave3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, WaveTextConfig *config, float time, Color tint); -// Measure a text in 3D ignoring the `~~` chars. +// Measure a text in 3D ignoring the `~~` chars static Vector3 MeasureTextWave3D(Font font, const char *text, float fontSize, float fontSpacing, float lineSpacing); // Generates a nice color with a random hue static Color GenerateRandomColor(float s, float v); @@ -562,7 +562,7 @@ static void DrawText3D(Font font, const char *text, Vector3 position, float font } } -// Draw a 2D text in 3D space and wave the parts that start with `~~` and end with `~~`. +// Draw a 2D text in 3D space and wave the parts that start with `~~` and end with `~~` // This is a modified version of the original code by @Nighten found here https://github.com/NightenDushi/Raylib_DrawTextStyle static void DrawTextWave3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, WaveTextConfig* config, float time, Color tint) { @@ -625,7 +625,7 @@ static void DrawTextWave3D(Font font, const char *text, Vector3 position, float } } -// Measure a text in 3D ignoring the `~~` chars. +// Measure a text in 3D ignoring the `~~` chars static Vector3 MeasureTextWave3D(Font font, const char* text, float fontSize, float fontSpacing, float lineSpacing) { int len = TextLength(text); diff --git a/examples/text/text_font_spritefont.c b/examples/text/text_font_spritefont.c index d3200a9ae..8c1a3b102 100644 --- a/examples/text/text_font_spritefont.c +++ b/examples/text/text_font_spritefont.c @@ -12,7 +12,7 @@ * - Rectangles must be defined by a MAGENTA color background * * Following those constraints, a font can be provided just by an image, -* this is quite handy to avoid additional font descriptor files (like BMFonts use). +* this is quite handy to avoid additional font descriptor files (like BMFonts use) * * Example originally created with raylib 1.0, last time updated with raylib 1.0 * diff --git a/examples/text/text_input_box.c b/examples/text/text_input_box.c index 2e55e8b74..77c04d55a 100644 --- a/examples/text/text_input_box.c +++ b/examples/text/text_input_box.c @@ -63,7 +63,7 @@ int main(void) if ((key >= 32) && (key <= 125) && (letterCount < MAX_INPUT_CHARS)) { name[letterCount] = (char)key; - name[letterCount+1] = '\0'; // Add null terminator at the end of the string. + name[letterCount+1] = '\0'; // Add null terminator at the end of the string letterCount++; } diff --git a/examples/text/text_rectangle_bounds.c b/examples/text/text_rectangle_bounds.c index a05dfbd17..75ac65b49 100644 --- a/examples/text/text_rectangle_bounds.c +++ b/examples/text/text_rectangle_bounds.c @@ -179,9 +179,9 @@ static void DrawTextBoxedSelectable(Font font, const char *text, Rectangle rec, // NOTE: When wordWrap is ON we first measure how much of the text we can draw before going outside of the rec container // We store this info in startLine and endLine, then we change states, draw the text between those two variables - // and change states again and again recursively until the end of the text (or until we get outside of the container). + // and change states again and again recursively until the end of the text (or until we get outside of the container) // When wordWrap is OFF we don't need the measure state so we go to the drawing state immediately - // and begin drawing on the next line before we can get outside the container. + // and begin drawing on the next line before we can get outside the container if (state == MEASURE_STATE) { // TODO: There are multiple types of spaces in UNICODE, maybe it's a good idea to add support for more diff --git a/examples/text/text_unicode.c b/examples/text/text_unicode.c index c3ca39d69..fbf579772 100644 --- a/examples/text/text_unicode.c +++ b/examples/text/text_unicode.c @@ -378,9 +378,9 @@ static void DrawTextBoxedSelectable(Font font, const char *text, Rectangle rec, // NOTE: When wordWrap is ON we first measure how much of the text we can draw before going outside of the rec container // We store this info in startLine and endLine, then we change states, draw the text between those two variables - // and change states again and again recursively until the end of the text (or until we get outside of the container). + // and change states again and again recursively until the end of the text (or until we get outside of the container) // When wordWrap is OFF we don't need the measure state so we go to the drawing state immediately - // and begin drawing on the next line before we can get outside the container. + // and begin drawing on the next line before we can get outside the container if (state == MEASURE_STATE) { // TODO: There are multiple types of spaces in UNICODE, maybe it's a good idea to add support for more diff --git a/examples/textures/textures_draw_tiled.c b/examples/textures/textures_draw_tiled.c index 3c5cb7199..c6bf3f835 100644 --- a/examples/textures/textures_draw_tiled.c +++ b/examples/textures/textures_draw_tiled.c @@ -22,7 +22,7 @@ #define MARGIN_SIZE 8 // Size for the margins #define COLOR_SIZE 16 // Size of the color select buttons -// Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest. +// Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest void DrawTextureTiled(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, float scale, Color tint); //------------------------------------------------------------------------------------ @@ -173,7 +173,7 @@ int main(void) return 0; } -// Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest. +// Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest void DrawTextureTiled(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, float scale, Color tint) { if ((texture.id <= 0) || (scale <= 0.0f)) return; // Wanna see a infinite loop?!...just delete this line! diff --git a/examples/textures/textures_image_channel.c b/examples/textures/textures_image_channel.c index 35ef0c7f5..b11fb5cd6 100644 --- a/examples/textures/textures_image_channel.c +++ b/examples/textures/textures_image_channel.c @@ -75,9 +75,9 @@ int main(void) // Main game loop while (!WindowShouldClose()) // Detect window close button or ESC key { - // Draw + // Update //---------------------------------------------------------------------------------- - // TODO... + // Nothing to update... //---------------------------------------------------------------------------------- // Draw diff --git a/examples/textures/textures_particles_blending.c b/examples/textures/textures_particles_blending.c index 65a55f30a..537d7f312 100644 --- a/examples/textures/textures_particles_blending.c +++ b/examples/textures/textures_particles_blending.c @@ -71,7 +71,7 @@ int main(void) // Activate one particle every frame and Update active particles // NOTE: Particles initial position should be mouse position when activated // NOTE: Particles fall down with gravity and rotation... and disappear after 2 seconds (alpha = 0) - // NOTE: When a particle disappears, active = false and it can be reused. + // NOTE: When a particle disappears, active = false and it can be reused for (int i = 0; i < MAX_PARTICLES; i++) { if (!mouseTail[i].active) diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index 906a863b1..9773268f5 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -5,10 +5,10 @@ * DESCRIPTION: * * Load GPU compressed image data from image files provided as memory data arrays, -* data is loaded compressed, ready to be loaded into GPU. +* data is loaded compressed, ready to be loaded into GPU * * Note that some file formats (DDS, PVR, KTX) also support uncompressed data storage. -* In those cases data is loaded uncompressed and format is returned. +* In those cases data is loaded uncompressed and format is returned * * FIXME: This library still depends on Raylib due to the following reasons: * - rl_save_ktx_to_memory() requires rlGetGlTextureFormats() from rlgl.h @@ -436,7 +436,7 @@ void *rl_load_pkm_from_memory(const unsigned char *file_data, unsigned int file_ // version 10: format: 0=ETC1_RGB, [1=ETC1_RGBA, 2=ETC1_RGB_MIP, 3=ETC1_RGBA_MIP] (not used) // version 20: format: 0=ETC1_RGB, 1=ETC2_RGB, 2=ETC2_RGBA_OLD, 3=ETC2_RGBA, 4=ETC2_RGBA1, 5=ETC2_R, 6=ETC2_RG, 7=ETC2_SIGNED_R, 8=ETC2_SIGNED_R - // NOTE: The extended width and height are the widths rounded up to a multiple of 4. + // NOTE: The extended width and height are the widths rounded up to a multiple of 4 // NOTE: ETC is always 4bit per pixel (64 bit for each 4x4 block of pixels) if (file_data_ptr != RL_GPUTEX_NULL) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index d701691a1..c23b570d3 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -724,7 +724,7 @@ void *GetWindowHandle(void) return glfwGetWin32Window(platform.handle); #endif #if defined(__linux__) - // Store the window handle localy and return a pointer to the variable instead. + // Store the window handle localy and return a pointer to the variable instead // Reasoning detailed in the declaration of X11WindowHandle X11WindowHandle = glfwGetX11Window(platform.handle); return &X11WindowHandle; @@ -1066,9 +1066,9 @@ double GetTime(void) } // Open URL with default system browser (if available) -// NOTE: This function is only safe to use if you control the URL given. -// A user could craft a malicious string performing another action. -// Only call this function yourself not with user input or make sure to check the string yourself. +// NOTE: This function is only safe to use if you control the URL given +// A user could craft a malicious string performing another action +// Only call this function yourself not with user input or make sure to check the string yourself // Ref: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { @@ -1130,7 +1130,7 @@ void SetMouseCursor(int cursor) } } -// Get physical key name. +// Get physical key name const char *GetKeyName(int key) { return glfwGetKeyName(key, glfwGetKeyScancode(key)); @@ -1306,8 +1306,8 @@ static void SetDimensionsFromMonitor(GLFWmonitor *monitor) } // Function wrappers around RL_*alloc macros, used by glfwInitAllocator() inside of InitPlatform() -// We need to provide these because GLFWallocator expects function pointers with specific signatures. -// Similar wrappers exist in utils.c but we cannot reuse them here due to declaration mismatch. +// We need to provide these because GLFWallocator expects function pointers with specific signatures +// Similar wrappers exist in utils.c but we cannot reuse them here due to declaration mismatch // https://www.glfw.org/docs/latest/intro_guide.html#init_allocator static void *AllocateWrapper(size_t size, void *user) { @@ -1394,15 +1394,15 @@ int InitPlatform(void) // HACK: Most of this was written before GLFW_SCALE_FRAMEBUFFER existed and // was enabled by default. Disabling it gets back the old behavior. A // complete fix will require removing a lot of CORE.Window.render - // manipulation code. + // manipulation code glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE); if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) { - // Resize window content area based on the monitor content scale. - // NOTE: This hint only has an effect on platforms where screen coordinates and pixels always map 1:1 such as Windows and X11. - // On platforms like macOS the resolution of the framebuffer is changed independently of the window size. - glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); // Scale content area based on the monitor content scale where window is placed on + // Resize window content area based on the monitor content scale + // NOTE: This hint only has an effect on platforms where screen coordinates and pixels always map 1:1 such as Windows and X11 + // On platforms like macOS the resolution of the framebuffer is changed independently of the window size + glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); // Scale content area based on the monitor content scale where window is placed on #if defined(__APPLE__) glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE); #endif @@ -1421,8 +1421,8 @@ int InitPlatform(void) } // NOTE: When asking for an OpenGL context version, most drivers provide the highest supported version - // with backward compatibility to older OpenGL versions. - // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context. + // with backward compatibility to older OpenGL versions + // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context // Check selection OpenGL version if (rlGetVersion() == RL_OPENGL_21) @@ -1468,9 +1468,9 @@ int InitPlatform(void) glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); } - // NOTE: GLFW 3.4+ defers initialization of the Joystick subsystem on the first call to any Joystick related functions. - // Forcing this initialization here avoids doing it on PollInputEvents() called by EndDrawing() after first frame has been just drawn. - // The initialization will still happen and possible delays still occur, but before the window is shown, which is a nicer experience. + // NOTE: GLFW 3.4+ defers initialization of the Joystick subsystem on the first call to any Joystick related functions + // Forcing this initialization here avoids doing it on PollInputEvents() called by EndDrawing() after first frame has been just drawn + // The initialization will still happen and possible delays still occur, but before the window is shown, which is a nicer experience // REF: https://github.com/raysan5/raylib/issues/1554 glfwSetJoystickCallback(NULL); @@ -1478,7 +1478,7 @@ int InitPlatform(void) if (CORE.Window.fullscreen) { // According to glfwCreateWindow(), if the user does not have a choice, fullscreen applications - // should default to the primary monitor. + // should default to the primary monitor monitor = glfwGetPrimaryMonitor(); if (!monitor) @@ -1492,8 +1492,8 @@ int InitPlatform(void) // Remember center for switching from fullscreen to window if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) { - // If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed. - // Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11. + // If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed + // Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11 CORE.Window.position.x = CORE.Window.display.width/4; CORE.Window.position.y = CORE.Window.display.height/4; } @@ -1554,7 +1554,7 @@ int InitPlatform(void) // No-fullscreen window creation bool requestWindowedFullscreen = (CORE.Window.screen.height == 0) && (CORE.Window.screen.width == 0); - // Default to at least one pixel in size, as creation with a zero dimension is not allowed. + // 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; @@ -1566,8 +1566,8 @@ int InitPlatform(void) return -1; } - // After the window was created, determine the monitor that the window manager assigned. - // Derive display sizes, and, if possible, window size in case it was zero at beginning. + // After the window was created, determine the monitor that the window manager assigned + // Derive display sizes, and, if possible, window size in case it was zero at beginning int monitorCount = 0; int monitorIndex = GetCurrentMonitor(); @@ -1582,7 +1582,7 @@ int InitPlatform(void) } else { - // The monitor for the window-manager-created window can not be determined, so it can not be centered. + // The monitor for the window-manager-created window can not be determined, so it can not be centered glfwTerminate(); TRACELOG(LOG_WARNING, "GLFW: Failed to determine Monitor to center Window"); return -1; @@ -1604,7 +1604,7 @@ int InitPlatform(void) // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need - // to be activated on web platforms since VSync is enforced there. + // to be activated on web platforms since VSync is enforced there if (CORE.Window.flags & FLAG_VSYNC_HINT) { // WARNING: It seems to hit a critical render path in Intel HD Graphics @@ -1617,7 +1617,7 @@ int InitPlatform(void) if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) { - // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling. + // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE); #if !defined(__APPLE__) glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight); @@ -1660,7 +1660,8 @@ int InitPlatform(void) int monitorHeight = 0; glfwGetMonitorWorkarea(monitor, &monitorX, &monitorY, &monitorWidth, &monitorHeight); - // Here CORE.Window.render.width/height should be used instead of CORE.Window.screen.width/height to center the window correctly when the high dpi flag is enabled. + // Here CORE.Window.render.width/height should be used instead of + // CORE.Window.screen.width/height to center the window correctly when the high dpi flag is enabled int posX = monitorX + (monitorWidth - (int)CORE.Window.render.width)/2; int posY = monitorY + (monitorHeight - (int)CORE.Window.render.height)/2; if (posX < monitorX) posX = monitorX; diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index bc7889140..5a62fa629 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -252,7 +252,7 @@ static const int CursorsLUT[] = { //SDL_SYSTEM_CURSOR_WAITARROW, // No equivalent implemented on MouseCursor enum on raylib.h }; -// SDL3 Migration Layer made to avoid 'ifdefs' inside functions when we can. +// SDL3 Migration Layer made to avoid 'ifdefs' inside functions when we can #if defined(PLATFORM_DESKTOP_SDL3) // SDL3 Migration: @@ -269,7 +269,7 @@ static const int CursorsLUT[] = { // SDL3 Migration: SDL_INIT_TIMER - no longer needed before calling SDL_AddTimer() #define SDL_INIT_TIMER 0x0 // It's a flag, so no problem in setting it to zero if we use in a bitor (|) -// SDL3 Migration: The SDL_WINDOW_SHOWN flag has been removed. Windows are shown by default and can be created hidden by using the SDL_WINDOW_HIDDEN flag. +// SDL3 Migration: The SDL_WINDOW_SHOWN flag has been removed. Windows are shown by default and can be created hidden by using the SDL_WINDOW_HIDDEN flag #define SDL_WINDOW_SHOWN 0x0 // It's a flag, so no problem in setting it to zero if we use in a bitor (|) // SDL3 Migration: Renamed @@ -344,7 +344,7 @@ SDL_Surface *SDL_CreateRGBSurface(Uint32 flags, int width, int height, int depth // SDL3 Migration: // SDL_GetDisplayDPI() - // not reliable across platforms, approximately replaced by multiplying -// SDL_GetWindowDisplayScale() times 160 on iPhone and Android, and 96 on other platforms. +// SDL_GetWindowDisplayScale() times 160 on iPhone and Android, and 96 on other platforms // returns 0 on success or a negative error code on failure int SDL_GetDisplayDPI(int displayIndex, float *ddpi, float *hdpi, float *vdpi) { @@ -382,7 +382,7 @@ int SDL_NumJoysticks(void) // SDL_SetRelativeMouseMode // returns 0 on success or a negative error code on failure -// If relative mode is not supported, this returns -1. +// If relative mode is not supported, this returns -1 int SDL_SetRelativeMouseMode_Adapter(SDL_bool enabled) { // SDL_SetWindowRelativeMouseMode(SDL_Window *window, bool enabled) @@ -841,7 +841,7 @@ void SetWindowMonitor(int monitor) // NOTE: // 1. SDL started supporting moving exclusive fullscreen windows between displays on SDL3, // see commit https://github.com/libsdl-org/SDL/commit/3f5ef7dd422057edbcf3e736107e34be4b75d9ba - // 2. A workaround for SDL2 is leaving fullscreen, moving the window, then entering full screen again. + // 2. A workaround for SDL2 is leaving fullscreen, moving the window, then entering full screen again const bool wasFullscreen = ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0)? true : false; const int screenWidth = CORE.Window.screen.width; @@ -854,7 +854,7 @@ void SetWindowMonitor(int monitor) if (SDL_GetDisplayUsableBounds(monitor, &usableBounds) == 0) #endif { - if (wasFullscreen == 1) ToggleFullscreen(); // Leave fullscreen. + if (wasFullscreen == 1) ToggleFullscreen(); // Leave fullscreen // If the screen size is larger than the monitor usable area, anchor it on the top left corner, otherwise, center it if ((screenWidth >= usableBounds.w) || (screenHeight >= usableBounds.h)) @@ -862,11 +862,11 @@ void SetWindowMonitor(int monitor) // NOTE: // 1. There's a known issue where if the window larger than the target display bounds, // when moving the windows to that display, the window could be clipped back - // ending up positioned partly outside the target display. + // ending up positioned partly outside the target display // 2. The workaround for that is, previously to moving the window, - // setting the window size to the target display size, so they match. + // setting the window size to the target display size, so they match // 3. It wasn't done here because we can't assume changing the window size automatically - // is acceptable behavior by the user. + // is acceptable behavior by the user SDL_SetWindowPosition(platform.window, usableBounds.x, usableBounds.y); CORE.Window.position.x = usableBounds.x; CORE.Window.position.y = usableBounds.y; @@ -1099,7 +1099,7 @@ Vector2 GetWindowScaleDPI(void) #ifndef PLATFORM_DESKTOP_SDL3 // NOTE: SDL_GetWindowDisplayScale was only added on SDL3 // see https://wiki.libsdl.org/SDL3/SDL_GetWindowDisplayScale - // TODO: Implement the window scale factor calculation manually. + // TODO: Implement the window scale factor calculation manually TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform"); #else scale.x = SDL_GetWindowDisplayScale(platform.window); @@ -1245,9 +1245,9 @@ double GetTime(void) } // Open URL with default system browser (if available) -// NOTE: This function is only safe to use if you control the URL given. -// A user could craft a malicious string performing another action. -// Only call this function yourself not with user input or make sure to check the string yourself. +// NOTE: This function is only safe to use if you control the URL given +// A user could craft a malicious string performing another action +// Only call this function yourself not with user input or make sure to check the string yourself // Ref: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { @@ -1299,7 +1299,7 @@ void SetMouseCursor(int cursor) CORE.Input.Mouse.cursor = cursor; } -// Get physical key name. +// Get physical key name const char *GetKeyName(int key) { return SDL_GetKeyName(key); @@ -1466,10 +1466,9 @@ void PollInputEvents(void) #ifndef PLATFORM_DESKTOP_SDL3 // SDL3 states: - // The SDL_WINDOWEVENT_* events have been moved to top level events, - // and SDL_WINDOWEVENT has been removed. - // In general, handling this change just means checking for the individual events instead of first checking for SDL_WINDOWEVENT - // and then checking for window events. You can compare the event >= SDL_EVENT_WINDOW_FIRST and <= SDL_EVENT_WINDOW_LAST if you need to see whether it's a window event. + // The SDL_WINDOWEVENT_* events have been moved to top level events, and SDL_WINDOWEVENT has been removed + // In general, handling this change just means checking for the individual events instead of first checking for SDL_WINDOWEVENT + // and then checking for window events. You can compare the event >= SDL_EVENT_WINDOW_FIRST and <= SDL_EVENT_WINDOW_LAST if you need to see whether it's a window event. case SDL_WINDOWEVENT: { switch (event.window.event) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index a3051f485..79769eee7 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -18,9 +18,9 @@ * * CONFIGURATION: * #define SUPPORT_SSH_KEYBOARD_RPI (Raspberry Pi only) -* Reconfigure standard input to receive key inputs, works with SSH connection. +* Reconfigure standard input to receive key inputs, works with SSH connection * WARNING: Reconfiguring standard input could lead to undesired effects, like breaking other -* running processes orblocking the device if not restored properly. Use with care. +* running processes orblocking the device if not restored properly. Use with care * * DEPENDENCIES: * - DRM and GLM: System libraries for display initialization and configuration @@ -744,11 +744,11 @@ void SwapScreenBuffer() } // Attempt page flip - // NOTE: rmModePageFlip() schedules a buffer-flip for the next vblank and then notifies us about it. - // It takes a CRTC-id, fb-id and an arbitrary data-pointer and then schedules the page-flip. - // This is fully asynchronous and when the page-flip happens, the DRM-fd will become readable and we can call drmHandleEvent(). - // This will read all vblank/page-flip events and call our modeset_page_flip_event() callback with the data-pointer that we passed to drmModePageFlip(). - // We simply call modeset_draw_dev() then so the next frame is rendered... returns immediately. + // NOTE: rmModePageFlip() schedules a buffer-flip for the next vblank and then notifies us about it + // It takes a CRTC-id, fb-id and an arbitrary data-pointer and then schedules the page-flip + // This is fully asynchronous and when the page-flip happens, the DRM-fd will become readable and we can call drmHandleEvent() + // This will read all vblank/page-flip events and call our modeset_page_flip_event() callback with the data-pointer that we passed to drmModePageFlip() + // We simply call modeset_draw_dev() then so the next frame is rendered... returns immediately if (drmModePageFlip(platform.fd, platform.crtc->crtc_id, fbId, DRM_MODE_PAGE_FLIP_EVENT, platform.prevBO)) { if (errno == EBUSY) errCnt[3]++; // Display busy - skip flip @@ -824,9 +824,9 @@ double GetTime(void) } // Open URL with default system browser (if available) -// NOTE: This function is only safe to use if you control the URL given. -// A user could craft a malicious string performing another action. -// Only call this function yourself not with user input or make sure to check the string yourself. +// NOTE: This function is only safe to use if you control the URL given +// A user could craft a malicious string performing another action +// Only call this function yourself not with user input or make sure to check the string yourself // Ref: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { @@ -863,7 +863,7 @@ void SetMouseCursor(int cursor) TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); } -// Get physical key name. +// Get physical key name const char *GetKeyName(int key) { TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform"); @@ -897,7 +897,7 @@ void PollInputEvents(void) PollKeyboardEvents(); #if defined(SUPPORT_SSH_KEYBOARD_RPI) - // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here. + // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here // stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console if (!platform.eventKeyboardMode) ProcessKeyboard(); #endif @@ -1003,8 +1003,8 @@ int InitPlatform(void) drmModeConnector *con = drmModeGetConnector(platform.fd, res->connectors[i]); TRACELOG(LOG_TRACE, "DISPLAY: Connector modes detected: %i", con->count_modes); - // In certain cases the status of the conneciton is reported as UKNOWN, but it is still connected. - // This might be a hardware or software limitation like on Raspberry Pi Zero with composite output. + // In certain cases the status of the conneciton is reported as UKNOWN, but it is still connected + // This might be a hardware or software limitation like on Raspberry Pi Zero with composite output if (((con->connection == DRM_MODE_CONNECTED) || (con->connection == DRM_MODE_UNKNOWNCONNECTION)) && (con->encoder_id)) { TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected"); @@ -1160,7 +1160,7 @@ int InitPlatform(void) // Initialize the EGL device connection if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE) { - // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred. + // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); return -1; } @@ -1644,7 +1644,7 @@ static void ConfigureEvdevDevice(char *device) return; } - // At this point we have a connection to the device, but we don't yet know what the device is. + // At this point we have a connection to the device, but we don't yet know what the device is // It could be many things, even as simple as a power button... //------------------------------------------------------------------------------------------------------- diff --git a/src/raudio.c b/src/raudio.c index 93024722f..76fee2333 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -15,8 +15,8 @@ * raudio module is included in the build * * #define RAUDIO_STANDALONE -* Define to use the module as standalone library (independently of raylib). -* Required types and functions are defined in the same module. +* Define to use the module as standalone library (independently of raylib) +* Required types and functions are defined in the same module * * #define SUPPORT_FILEFORMAT_WAV * #define SUPPORT_FILEFORMAT_OGG @@ -89,7 +89,7 @@ // by user at some point and won't be included... //------------------------------------------------------------------------------------- -// If defined, the following flags inhibit definition of the indicated items. +// If defined, the following flags inhibit definition of the indicated items #define NOGDICAPMASKS // CC_*, LC_*, PC_*, CP_*, TC_*, RC_ #define NOVIRTUALKEYCODES // VK_* #define NOWINMESSAGES // WM_*, EM_*, LB_*, CB_* @@ -124,9 +124,9 @@ #define NOWH // SetWindowsHook and WH_* #define NOWINOFFSETS // GWL_*, GCL_*, associated routines #define NOCOMM // COMM driver routines -#define NOKANJI // Kanji support stuff. -#define NOHELP // Help engine interface. -#define NOPROFILER // Profiler interface. +#define NOKANJI // Kanji support stuff +#define NOHELP // Help engine interface +#define NOPROFILER // Profiler interface #define NODEFERWINDOWPOS // DeferWindowPos routines #define NOMCX // Modem Configuration Extensions @@ -1118,7 +1118,7 @@ bool ExportWaveAsCode(Wave wave, const char *fileName) // NOTE: Text data buffer size is estimated considering wave data size in bytes // and requiring 12 char bytes for every byte; the actual size varies, but - // the longest possible char being appended is "%.4ff,\n ", which is 12 bytes. + // the longest possible char being appended is "%.4ff,\n ", which is 12 bytes char *txtData = (char *)RL_CALLOC(waveDataSize*12 + 2000, sizeof(char)); int byteCount = 0; diff --git a/src/raylib.h b/src/raylib.h index 4517cbcac..0851905c1 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -5,7 +5,7 @@ * FEATURES: * - NO external dependencies, all required libraries included with raylib * - Multiplatform: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly, -* MacOS, Haiku, Android, Raspberry Pi, DRM native, HTML5. +* MacOS, Haiku, Android, Raspberry Pi, DRM native, HTML5 * - Written in plain C code (C99) in PascalCase/camelCase notation * - Hardware accelerated with OpenGL (1.1, 2.1, 3.3, 4.3, ES2, ES3 - choose at compile) * - Unique OpenGL abstraction layer (usable as standalone module): [rlgl] diff --git a/src/rmodels.c b/src/rmodels.c index 1a181c843..c1a32058a 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -12,7 +12,7 @@ * #define SUPPORT_FILEFORMAT_GLTF * #define SUPPORT_FILEFORMAT_VOX * #define SUPPORT_FILEFORMAT_M3D -* Selected desired fileformats to be supported for model data loading. +* Selected desired fileformats to be supported for model data loading * * #define SUPPORT_MESH_GENERATION * Support procedural mesh generation functions, uses external par_shapes.h library @@ -2298,7 +2298,7 @@ void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame) if (firstMeshWithBones != -1) { - // Update all bones and boneMatrices of first mesh with bones. + // Update all bones and boneMatrices of first mesh with bones for (int boneId = 0; boneId < anim.boneCount; boneId++) { Transform *bindTransform = &model.bindPose[boneId]; @@ -5291,8 +5291,7 @@ static Model LoadGLTF(const char *fileName) > Texcoords: vec2: float > Colors: vec4: u8, u16, f32 (normalized) > Indices: u16, u32 (truncated to u16) - - Scenes defined in the glTF file are ignored. All nodes in the file - are used. + - Scenes defined in the glTF file are ignored. All nodes in the file are used ***********************************************************************************************/ @@ -5347,8 +5346,8 @@ static Model LoadGLTF(const char *fileName) int primitivesCount = 0; - // NOTE: We will load every primitive in the glTF as a separate raylib Mesh. - // Determine total number of meshes needed from the node hierarchy. + // NOTE: We will load every primitive in the glTF as a separate raylib Mesh + // Determine total number of meshes needed from the node hierarchy for (unsigned int i = 0; i < data->nodes_count; i++) { cgltf_node *node = &(data->nodes[i]); @@ -5490,14 +5489,13 @@ static Model LoadGLTF(const char *fileName) // has_clearcoat, has_transmission, has_volume, has_ior, has specular, has_sheen } - // Visit each node in the hierarchy and process any mesh linked from it. - // Each primitive within a glTF node becomes a Raylib Mesh. + // Visit each node in the hierarchy and process any mesh linked from it + // Each primitive within a glTF node becomes a Raylib Mesh // The local-to-world transform of each node is used to transform the - // points/normals/tangents of the created Mesh(es). + // points/normals/tangents of the created Mesh(es) // Any glTF mesh linked from more than one Node (i.e. instancing) - // is turned into multiple Mesh's, as each Node will have its own - // transform applied. - // NOTE: The code below disregards the scenes defined in the file, all nodes are used. + // is turned into multiple Mesh's, as each Node will have its own transform applied + // NOTE: The code below disregards the scenes defined in the file, all nodes are used //---------------------------------------------------------------------------------------------------- int meshIndex = 0; for (unsigned int i = 0; i < data->nodes_count; i++) diff --git a/src/rshapes.c b/src/rshapes.c index be28e6cad..38ad75c7d 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -3,17 +3,17 @@ * rshapes - Basic functions to draw 2d shapes and check collisions * * ADDITIONAL NOTES: -* Shapes can be draw using 3 types of primitives: LINES, TRIANGLES and QUADS. +* Shapes can be draw using 3 types of primitives: LINES, TRIANGLES and QUADS * Some functions implement two drawing options: TRIANGLES and QUADS, by default TRIANGLES * are used but QUADS implementation can be selected with SUPPORT_QUADS_DRAW_MODE define * * Some functions define texture coordinates (rlTexCoord2f()) for the shapes and use a * user-provided texture with SetShapesTexture(), the pourpouse of this implementation -* is allowing to reduce draw calls when combined with a texture-atlas. +* is allowing to reduce draw calls when combined with a texture-atlas * * By default, raylib sets the default texture and rectangle at InitWindow()[rcore] to one * white character of default font [rtext], this way, raylib text and shapes can be draw with -* a single draw call and it also allows users to configure it the same way with their own fonts. +* a single draw call and it also allows users to configure it the same way with their own fonts * * CONFIGURATION: * #define SUPPORT_MODULE_RSHAPES diff --git a/src/rtext.c b/src/rtext.c index 1330c5906..50ea6c883 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -7,8 +7,8 @@ * rtext module is included in the build * * #define SUPPORT_DEFAULT_FONT -* Load default raylib font on initialization to be used by DrawText() and MeasureText(). -* If no default font loaded, DrawTextEx() and MeasureTextEx() are required. +* Load default raylib font on initialization to be used by DrawText() and MeasureText() +* If no default font loaded, DrawTextEx() and MeasureTextEx() are required * * #define SUPPORT_FILEFORMAT_FNT * #define SUPPORT_FILEFORMAT_TTF @@ -19,7 +19,7 @@ * #define SUPPORT_FONT_ATLAS_WHITE_REC * On font atlas image generation [GenImageFontAtlas()], add a 3x3 pixels white rectangle * at the bottom-right corner of the atlas. It can be useful to for shapes drawing, to allow -* drawing text and shapes with a single draw call [SetShapesTexture()]. +* drawing text and shapes with a single draw call [SetShapesTexture()] * * #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH * TextSplit() function static buffer max size diff --git a/src/rtextures.c b/src/rtextures.c index b798caed6..81d615c38 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -36,7 +36,7 @@ * * DEPENDENCIES: * stb_image - Multiple image formats loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC) -* NOTE: stb_image has been slightly modified to support Android platform. +* NOTE: stb_image has been slightly modified to support Android platform * stb_image_resize - Multiple image resize algorithms * * From df40357f190caf2e3c370a141540c7f805773579 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Aug 2025 18:37:11 +0200 Subject: [PATCH 226/242] Update linux.yml --- .github/workflows/linux.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 222e2b289..d31acc854 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -30,17 +30,17 @@ jobs: - bits: 32 ARCH: "i386" ARCH_NAME: "i386" - COMPILER_PATH: "/user/bin" + COMPILER_PATH: "/usr/bin" runner: "ubuntu-latest" - bits: 64 ARCH: "x86_64" ARCH_NAME: "amd64" - COMPILER_PATH: "/user/bin" + COMPILER_PATH: "/usr/bin" runner: "ubuntu-latest" - bits: 64 ARCH: "aarch64" ARCH_NAME: "arm64" - COMPILER_PATH: "/user/bin" + COMPILER_PATH: "/usr/bin" runner: "ubuntu-24.04-arm" runs-on: ${{ matrix.runner }} From c6171d339ccdf611583b2a2b24f15f6266984a30 Mon Sep 17 00:00:00 2001 From: wileyanderssen Date: Thu, 7 Aug 2025 19:55:46 +0300 Subject: [PATCH 227/242] removed unneeded comma hope this gets merged --- src/raymath.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raymath.h b/src/raymath.h index a531b3a14..f246f26d9 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -329,7 +329,7 @@ RMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2) } // Calculate the signed angle from v1 to v2, relative to the origin (0, 0) -// NOTE: Coordinate system convention: positive X right, positive Y down, +// NOTE: Coordinate system convention: positive X right, positive Y down // positive angles appear clockwise, and negative angles appear counterclockwise RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) { From c25e9f641d3811ff8ae13d3fcac085a82d666857 Mon Sep 17 00:00:00 2001 From: JohnnyCena123 Date: Fri, 8 Aug 2025 03:04:33 +0300 Subject: [PATCH 228/242] fix typo in 'Ramon Santamaria' --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 4517cbcac..2bdbc4fa0 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -36,7 +36,7 @@ * [rcore] msf_gif (Miles Fogle) for GIF recording * [rcore] sinfl (Micha Mettke) for DEFLATE decompression algorithm * [rcore] sdefl (Micha Mettke) for DEFLATE compression algorithm -* [rcore] rprand (Ramon Snatamaria) for pseudo-random numbers generation +* [rcore] rprand (Ramon Santamaria) for pseudo-random numbers generation * [rtextures] qoi (Dominic Szablewski - https://phoboslab.org) for QOI image manage * [rtextures] stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...) * [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG) From d6f9988e8291da51858445dd493484e87798315e Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 8 Aug 2025 13:52:56 +0200 Subject: [PATCH 229/242] Update Makefile --- tools/parser/Makefile | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/tools/parser/Makefile b/tools/parser/Makefile index 165192463..3ff035d87 100644 --- a/tools/parser/Makefile +++ b/tools/parser/Makefile @@ -12,28 +12,22 @@ raylib_api: ../src/raylib.h raylib_parser FORMAT=XML EXTENSION=xml $(MAKE) raylib_api.xml FORMAT=LUA EXTENSION=lua $(MAKE) raylib_api.lua -raylib_api.$(EXTENSION): ../src/raylib.h raylib_parser - ./raylib_parser -i ../src/raylib.h -o raylib_api.$(EXTENSION) -f $(FORMAT) -d RLAPI +raylib_api.$(EXTENSION): ../../src/raylib.h raylib_parser + ./raylib_parser -i ../../src/raylib.h -o raylib_api.$(EXTENSION) -f $(FORMAT) -d RLAPI -raymath_api.$(EXTENSION): ../src/raymath.h raylib_parser - ./raylib_parser -i ../src/raymath.h -o raymath_api.$(EXTENSION) -f $(FORMAT) -d RMAPI +raymath_api.$(EXTENSION): ../../src/raymath.h raylib_parser + ./raylib_parser -i ../../src/raymath.h -o raymath_api.$(EXTENSION) -f $(FORMAT) -d RMAPI -rlgl_api.$(EXTENSION): ../src/rlgl.h raylib_parser - ./raylib_parser -i ../src/rlgl.h -o rlgl_api.$(EXTENSION) -f $(FORMAT) -d RLAPI -t "RLGL IMPLEMENTATION" +rlgl_api.$(EXTENSION): ../../src/rlgl.h raylib_parser + ./raylib_parser -i ../../src/rlgl.h -o rlgl_api.$(EXTENSION) -f $(FORMAT) -d RLAPI -t "RLGL IMPLEMENTATION" -reasings_api.$(EXTENSION): ../examples/others/reasings.h raylib_parser - ./raylib_parser -i ../examples/others/reasings.h -o reasings_api.$(EXTENSION) -f $(FORMAT) -d EASEDEF - -rmem_api.$(EXTENSION): ../rmem.h raylib_parser - ./raylib_parser -i ../rmem.h -o rmem_api.$(EXTENSION) -f $(FORMAT) -d RMEMAPI -t "RMEM IMPLEMENTATION" - -physac_api.$(EXTENSION): ../physac.h raylib_parser - ./raylib_parser -i ../physac.h -o physac_api.$(EXTENSION) -f $(FORMAT) -d PHYSACDEF -t "PHYSAC IMPLEMENTATION" +reasings_api.$(EXTENSION): ../../examples/others/reasings.h raylib_parser + ./raylib_parser -i ../../examples/others/reasings.h -o reasings_api.$(EXTENSION) -f $(FORMAT) -d EASEDEF raygui_api.$(EXTENSION): ../raygui.h raylib_parser ./raylib_parser -i ../raygui.h -o raygui_api.$(EXTENSION) -f $(FORMAT) -d RAYGUIAPI -t "RAYGUI IMPLEMENTATION" -parse: raylib_api.$(EXTENSION) raymath_api.$(EXTENSION) rlgl_api.$(EXTENSION) rmem_api.$(EXTENSION) physac_api.$(EXTENSION) raygui_api.$(EXTENSION) +parse: raylib_api.$(EXTENSION) raymath_api.$(EXTENSION) rlgl_api.$(EXTENSION) raygui_api.$(EXTENSION) # `make parse` (and therefore `make all) requires From f73b04c42c6376ce46e5bc70e4328354aa1ad69b Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 8 Aug 2025 21:54:15 +0200 Subject: [PATCH 230/242] REXM: Copy examples web-build files to `raylib.com` --- tools/rexm/rexm.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 7cdd6175e..adf3cb2c1 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -740,6 +740,8 @@ int main(int argc, char *argv[]) VALID_INVALID_CATEGORY */ + // TODO: Log more details about the validation process + // Check all examples in collection [examples_list.txt] -> Source of truth! int exCollectionCount = 0; rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, "ALL", false, &exCollectionCount); @@ -971,8 +973,20 @@ int main(int argc, char *argv[]) // Review: Add/Remove: raylib.com/examples//_example_name.wasm // Review: Add/Remove: raylib.com/examples//_example_name.js // Solves: VALID_MISSING_WEB_OUTPUT - //if (exInfo->status & VALID_MISSING_WEB_OUTPUT) - // system(TextFormat("%s/build_example_web.bat %s/%s", exBasePath, exInfo->category, exInfo->name)); + if (exInfo->status & VALID_MISSING_WEB_OUTPUT) + { + system(TextFormat("%s/build_example_web.bat %s/%s", exBasePath, exInfo->category, exInfo->name)); + + // Copy results to web side + FileCopy(TextFormat("%s/%s/%s.html", exBasePath, exInfo->category, exInfo->name), + TextFormat("%s/%s/%s.html", exWebPath, exInfo->category, exInfo->name)); + FileCopy(TextFormat("%s/%s/%s.data", exBasePath, exInfo->category, exInfo->name), + TextFormat("%s/%s/%s.data", exWebPath, exInfo->category, exInfo->name)); + FileCopy(TextFormat("%s/%s/%s.wasm", exBasePath, exInfo->category, exInfo->name), + TextFormat("%s/%s/%s.wasm", exWebPath, exInfo->category, exInfo->name)); + FileCopy(TextFormat("%s/%s/%s.js", exBasePath, exInfo->category, exInfo->name), + TextFormat("%s/%s/%s.js", exWebPath, exInfo->category, exInfo->name)); + } } } From c6241b91a26b1a9918f3b3086001256ac916330d Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 8 Aug 2025 23:01:04 +0200 Subject: [PATCH 231/242] REVIEWED: `IsFileExtension()` to avoid other modules dependency #5071 --- src/raylib.h | 2 +- src/rcore.c | 53 +++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index d77fdaf4c..c9fdab553 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1129,7 +1129,7 @@ RLAPI bool SaveFileText(const char *fileName, const char *text); // Save text d // File system functions RLAPI bool FileExists(const char *fileName); // Check if file exists RLAPI bool DirectoryExists(const char *dirPath); // Check if a directory path exists -RLAPI bool IsFileExtension(const char *fileName, const char *ext); // Check file extension (including point: .png, .wav) +RLAPI bool IsFileExtension(const char *fileName, const char *ext); // Check file extension (recommended include point: .png, .wav) RLAPI int GetFileLength(const char *fileName); // Get file length in bytes (NOTE: GetFileSize() conflicts with windows.h) RLAPI const char *GetFileExtension(const char *fileName); // Get pointer to extension for a filename string (includes dot: '.png') RLAPI const char *GetFileName(const char *filePath); // Get pointer to filename for a path string diff --git a/src/rcore.c b/src/rcore.c index 46e39991f..7eef877e2 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -113,7 +113,7 @@ #include // Required for: srand(), rand(), atexit() #include // Required for: sprintf() [Used in OpenURL()] -#include // Required for: strrchr(), strcmp(), strlen(), memset() +#include // Required for: strlen(), strcpy(), strcmp(), strrchr(), memset() #include // Required for: time() [Used in InitTimer()] #include // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()] @@ -1940,34 +1940,61 @@ bool FileExists(const char *fileName) } // Check file extension -// TODO: Avoid [rtext] module dependency bool IsFileExtension(const char *fileName, const char *ext) { - #define MAX_FILE_EXTENSION_LENGTH 16 + #define MAX_FILE_EXTENSIONS 32 bool result = false; const char *fileExt = GetFileExtension(fileName); if (fileExt != NULL) { -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_TEXT_MANIPULATION) - int extCount = 0; - char **checkExts = TextSplit(ext, ';', &extCount); // WARNING: Module required: rtext - - char fileExtLower[MAX_FILE_EXTENSION_LENGTH + 1] = { 0 }; - strncpy(fileExtLower, TextToLower(fileExt), MAX_FILE_EXTENSION_LENGTH); // WARNING: Module required: rtext + int fileExtLen = strlen(fileExt); + char fileExtLower[8] = { 0 }; + char *fileExtLowerPtr = fileExtLower; + for (int i = 0; i < fileExtLen; i++) + { + // Copy and convert to lower-case + if ((fileExt[i] >= 'A') && (fileExt[i] <= 'Z')) fileExtLower[i] = fileExt[i] + 32; + else fileExtLower[i] = fileExt[i]; + } + + int extCount = 1; + int extLen = strlen(ext); + char *extList = (char *)RL_CALLOC(extLen + 1, 1); + char *extListPtrs[MAX_FILE_EXTENSIONS] = { 0 }; + strcpy(extList, ext); + extListPtrs[0] = extList; + + for (int i = 0; i < extLen; i++) + { + // Convert to lower-case if extension is upper-case + if ((extList[i] >= 'A') && (extList[i] <= 'Z')) extList[i] += 32; + + // Get pointer to next extension and add null-terminator + if ((extList[i] == ';') && (extCount < (MAX_FILE_EXTENSIONS - 1))) + { + extList[i] = '\0'; + extListPtrs[extCount] = extList + i + 1; + extCount++; + } + } for (int i = 0; i < extCount; i++) { - if (strcmp(fileExtLower, TextToLower(checkExts[i])) == 0) + // Consider the case where extension provided + // does not start with the '.' + fileExtLowerPtr = fileExtLower; + if (extListPtrs[i][0] != '.') fileExtLowerPtr++; + + if (strcmp(fileExtLowerPtr, extListPtrs[i]) == 0) { result = true; break; } } -#else - if (strcmp(fileExt, ext) == 0) result = true; -#endif + + RL_FREE(extList); } return result; From e11bf1c9789d3f6ba3f18a02dcda3cefa2d46c4e Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 8 Aug 2025 23:07:08 +0200 Subject: [PATCH 232/242] Update Makefile --- tools/parser/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/parser/Makefile b/tools/parser/Makefile index 3ff035d87..ecfdb44ba 100644 --- a/tools/parser/Makefile +++ b/tools/parser/Makefile @@ -6,7 +6,7 @@ FORMAT?=DEFAULT raylib_parser: raylib_parser.c cc raylib_parser.c -o raylib_parser -raylib_api: ../src/raylib.h raylib_parser +raylib_api: ../../src/raylib.h raylib_parser FORMAT=DEFAULT EXTENSION=txt $(MAKE) raylib_api.txt FORMAT=JSON EXTENSION=json $(MAKE) raylib_api.json FORMAT=XML EXTENSION=xml $(MAKE) raylib_api.xml From 5daeffd91b3a6c46674bb510f5f2bd5d0a775c43 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 8 Aug 2025 23:08:42 +0200 Subject: [PATCH 233/242] Update parse.yml --- .github/workflows/parse.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/parse.yml b/.github/workflows/parse.yml index 4491b142e..4d5e25998 100644 --- a/.github/workflows/parse.yml +++ b/.github/workflows/parse.yml @@ -32,6 +32,7 @@ jobs: set -x git config user.email "github-actions[bot]@users.noreply.github.com" git config user.name "github-actions[bot]" - git add parser + git add tools/parser git commit -m "Update raylib_api.* by CI" git push + From 60826855c4df2f5fb3f1737531d1bd5fb25cdec4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 8 Aug 2025 21:09:03 +0000 Subject: [PATCH 234/242] Update raylib_api.* by CI --- tools/parser/output/raylib_api.json | 2 +- tools/parser/output/raylib_api.lua | 2 +- tools/parser/output/raylib_api.txt | 2 +- tools/parser/output/raylib_api.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/parser/output/raylib_api.json b/tools/parser/output/raylib_api.json index c2a4a1827..e0c732e5b 100644 --- a/tools/parser/output/raylib_api.json +++ b/tools/parser/output/raylib_api.json @@ -4438,7 +4438,7 @@ }, { "name": "IsFileExtension", - "description": "Check file extension (including point: .png, .wav)", + "description": "Check file extension (recommended include point: .png, .wav)", "returnType": "bool", "params": [ { diff --git a/tools/parser/output/raylib_api.lua b/tools/parser/output/raylib_api.lua index 23547d9f1..e62609b33 100644 --- a/tools/parser/output/raylib_api.lua +++ b/tools/parser/output/raylib_api.lua @@ -4027,7 +4027,7 @@ return { }, { name = "IsFileExtension", - description = "Check file extension (including point: .png, .wav)", + description = "Check file extension (recommended include point: .png, .wav)", returnType = "bool", params = { {type = "const char *", name = "fileName"}, diff --git a/tools/parser/output/raylib_api.txt b/tools/parser/output/raylib_api.txt index 9539d72a5..4bc67acd1 100644 --- a/tools/parser/output/raylib_api.txt +++ b/tools/parser/output/raylib_api.txt @@ -1670,7 +1670,7 @@ Function 125: DirectoryExists() (1 input parameters) Function 126: IsFileExtension() (2 input parameters) Name: IsFileExtension Return type: bool - Description: Check file extension (including point: .png, .wav) + Description: Check file extension (recommended include point: .png, .wav) Param[1]: fileName (type: const char *) Param[2]: ext (type: const char *) Function 127: GetFileLength() (1 input parameters) diff --git a/tools/parser/output/raylib_api.xml b/tools/parser/output/raylib_api.xml index d99187adf..f6fd1e2a1 100644 --- a/tools/parser/output/raylib_api.xml +++ b/tools/parser/output/raylib_api.xml @@ -1054,7 +1054,7 @@ - + From 7bc22f71bd5dc268ae69434196f2ab29bdab2b19 Mon Sep 17 00:00:00 2001 From: Nikolas Date: Sat, 9 Aug 2025 19:04:00 +0200 Subject: [PATCH 235/242] Update raylib-zig in BINDINGS.md --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index e60638ac1..9d0fd5c5c 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -82,7 +82,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers | [raylib.v](https://github.com/irishgreencitrus/raylib.v) | 4.2 | [V](https://vlang.io) | Zlib | | [raylib-vapi](https://github.com/lxmcf/raylib-vapi) | **5.0** | [Vala](https://vala.dev) | Zlib | | [raylib-wren](https://github.com/TSnake41/raylib-wren) | 4.5 | [Wren](http://wren.io) | ISC | -| [raylib-zig](https://github.com/Not-Nik/raylib-zig) | **5.5** | [Zig](https://ziglang.org) | MIT | +| [raylib-zig](https://github.com/raylib-zig/raylib-zig) | **5.6-dev** | [Zig](https://ziglang.org) | MIT | | [raylib.zig](https://github.com/ryupold/raylib.zig) | **5.1-dev** | [Zig](https://ziglang.org) | MIT | | [raylib-zig-bindings](https://github.com/L-Briand/raylib-zig-bindings) | **5.0** | [Zig](https://ziglang.org) | Zlib | | [hare-raylib](https://git.sr.ht/~evantj/hare-raylib) | **auto** | [Hare](https://harelang.org) | Zlib | From 9582d2521744a44a2bcd7fbe94d5ead8f078e7d5 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Sat, 9 Aug 2025 12:39:42 +0200 Subject: [PATCH 236/242] Fix backspace key in Rpi SSH keyboard There was a typo in the generated keycode. --- src/platforms/rcore_drm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 79769eee7..930624fd2 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -1556,7 +1556,7 @@ static void ProcessKeyboard(void) { CORE.Input.Keyboard.currentKeyState[259] = 1; - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257; // Add keys pressed into queue + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 259; // Add keys pressed into queue CORE.Input.Keyboard.keyPressedQueueCount++; } else From 27d7e61f2829ee8be1364ab672b96dd4c009f99b Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 10 Aug 2025 09:54:45 +0200 Subject: [PATCH 237/242] Update rexm.c --- tools/rexm/rexm.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index adf3cb2c1..0e69d8f9c 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -958,10 +958,7 @@ int main(int argc, char *argv[]) "..\\..\\examples\\core", TextFormat("..\\..\\examples\\%s", exInfo->category)); } - // WARNING: Adding a .vcxproj to .sln can not be automated with - // "dotnet" tool (C# projects only) - // "devenv" tool (no adding support, only building) - // It must be done manually editing the .sln file + // Add project (.vcxproj) to raylib solution (.sln) if (exInfo->status & VALID_NOT_IN_VCXSOL) { AddVSProjectToSolution(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName), @@ -1774,11 +1771,16 @@ static void ClearExampleResources(char **resPaths) } // TODO: Add VS project (.vcxproj) to existing VS solution (.sol) +// WARNING: Adding a .vcxproj to .sln can not be automated with: +// - "dotnet" tool (C# projects only) +// - "devenv" tool (no adding support, only building) +// It must be done manually editing the .sln file static int AddVSProjectToSolution(const char *projFile, const char *solFile) { int result = 0; // Generate unique UUID: + // Make sure the file is found and the UUID generated is valid // C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\uuidgen.exe > saved_uuid.txt // Add project to the list: From 71b7734ce403444c1b409ab3639272c888db0ebc Mon Sep 17 00:00:00 2001 From: Felipe Jorge Date: Sun, 10 Aug 2025 10:05:03 -0300 Subject: [PATCH 238/242] fix: QOA when using with LoadMusicStreamFromMemory, reduced the number of allocs and memcpy --- src/external/qoaplay.c | 46 ++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/external/qoaplay.c b/src/external/qoaplay.c index 039e27974..4378e5ebd 100644 --- a/src/external/qoaplay.c +++ b/src/external/qoaplay.c @@ -46,7 +46,6 @@ typedef struct { unsigned int sample_position; // Current streaming sample position unsigned char *buffer; // Buffer used to read samples from file/memory (used on decoding) - unsigned int buffer_len; // Buffer length to read samples for streaming short *sample_data; // Sample data decoded unsigned int sample_data_len; // Sample data decoded length @@ -111,7 +110,7 @@ qoaplay_desc *qoaplay_open(const char *path) qoa_ctx->file = file; qoa_ctx->file_data = NULL; qoa_ctx->file_data_size = 0; - qoa_ctx->file_data_offset = 0; + qoa_ctx->file_data_offset = first_frame_pos; qoa_ctx->first_frame_pos = first_frame_pos; // Setup data pointers to previously allocated data @@ -128,34 +127,30 @@ qoaplay_desc *qoaplay_open(const char *path) // Open QOA file from memory, no FILE pointer required qoaplay_desc *qoaplay_open_memory(const unsigned char *data, int data_size) { - // Read and decode the file header - unsigned char header[QOA_MIN_FILESIZE]; - memcpy(header, data, QOA_MIN_FILESIZE); - qoa_desc qoa; - unsigned int first_frame_pos = qoa_decode_header(header, QOA_MIN_FILESIZE, &qoa); + if (data_size < QOA_MIN_FILESIZE) return NULL; + unsigned int first_frame_pos = qoa_decode_header(data, QOA_MIN_FILESIZE, &qoa); if (!first_frame_pos) return NULL; // Allocate one chunk of memory for the qoaplay_desc struct // + the sample data for one frame // + a buffer to hold one frame of encoded data - unsigned int buffer_size = qoa_max_frame_size(&qoa); unsigned int sample_data_size = qoa.channels*QOA_FRAME_LEN*sizeof(short)*2; - qoaplay_desc *qoa_ctx = QOA_MALLOC(sizeof(qoaplay_desc) + buffer_size + sample_data_size); + qoaplay_desc *qoa_ctx = QOA_MALLOC(sizeof(qoaplay_desc) + sample_data_size + data_size); memset(qoa_ctx, 0, sizeof(qoaplay_desc)); qoa_ctx->file = NULL; // Keep a copy of file data provided to be managed internally - qoa_ctx->file_data = (unsigned char *)QOA_MALLOC(data_size); + qoa_ctx->file_data = (((unsigned char *)qoa_ctx) + sizeof(qoaplay_desc) + sample_data_size); memcpy(qoa_ctx->file_data, data, data_size); qoa_ctx->file_data_size = data_size; - qoa_ctx->file_data_offset = 0; + qoa_ctx->file_data_offset = first_frame_pos; qoa_ctx->first_frame_pos = first_frame_pos; // Setup data pointers to previously allocated data - qoa_ctx->buffer = ((unsigned char *)qoa_ctx) + sizeof(qoaplay_desc); - qoa_ctx->sample_data = (short *)(((unsigned char *)qoa_ctx) + sizeof(qoaplay_desc) + buffer_size); + qoa_ctx->buffer = NULL; + qoa_ctx->sample_data = (short *)(((unsigned char *)qoa_ctx) + sizeof(qoaplay_desc)); qoa_ctx->info.channels = qoa.channels; qoa_ctx->info.samplerate = qoa.samplerate; @@ -169,11 +164,7 @@ void qoaplay_close(qoaplay_desc *qoa_ctx) { if (qoa_ctx->file) fclose(qoa_ctx->file); - if ((qoa_ctx->file_data) && (qoa_ctx->file_data_size > 0)) - { - QOA_FREE(qoa_ctx->file_data); - qoa_ctx->file_data_size = 0; - } + qoa_ctx->file_data_size = 0; QOA_FREE(qoa_ctx); } @@ -181,16 +172,23 @@ void qoaplay_close(qoaplay_desc *qoa_ctx) // Decode one frame from QOA data unsigned int qoaplay_decode_frame(qoaplay_desc *qoa_ctx) { - if (qoa_ctx->file) qoa_ctx->buffer_len = fread(qoa_ctx->buffer, 1, qoa_max_frame_size(&qoa_ctx->info), qoa_ctx->file); + unsigned char *buffer; + unsigned int buffer_len; + + if (qoa_ctx->file) + { + buffer = qoa_ctx->buffer; + buffer_len = fread(buffer, 1, qoa_max_frame_size(&qoa_ctx->info), qoa_ctx->file); + } else { - qoa_ctx->buffer_len = qoa_max_frame_size(&qoa_ctx->info); - memcpy(qoa_ctx->buffer, qoa_ctx->file_data + qoa_ctx->file_data_offset, qoa_ctx->buffer_len); - qoa_ctx->file_data_offset += qoa_ctx->buffer_len; + buffer = qoa_ctx->file_data + qoa_ctx->file_data_offset; + buffer_len = qoa_max_frame_size(&qoa_ctx->info); + qoa_ctx->file_data_offset += buffer_len; } unsigned int frame_len; - qoa_decode_frame(qoa_ctx->buffer, qoa_ctx->buffer_len, &qoa_ctx->info, qoa_ctx->sample_data, &frame_len); + qoa_decode_frame(buffer, buffer_len, &qoa_ctx->info, qoa_ctx->sample_data, &frame_len); qoa_ctx->sample_data_pos = 0; qoa_ctx->sample_data_len = frame_len; @@ -201,7 +199,7 @@ unsigned int qoaplay_decode_frame(qoaplay_desc *qoa_ctx) void qoaplay_rewind(qoaplay_desc *qoa_ctx) { if (qoa_ctx->file) fseek(qoa_ctx->file, qoa_ctx->first_frame_pos, SEEK_SET); - else qoa_ctx->file_data_offset = 0; + else qoa_ctx->file_data_offset = qoa_ctx->first_frame_pos; qoa_ctx->sample_position = 0; qoa_ctx->sample_data_len = 0; From 705ce45a33007019fe5432d5833496468a6b79cd Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 10 Aug 2025 19:59:33 +0200 Subject: [PATCH 239/242] FIX: Added callback broke keyboard input on web... #5107 --- src/platforms/rcore_web.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 3107c30e4..9156f1599 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -137,7 +137,7 @@ static EM_BOOL EmscriptenFocusCallback(int eventType, const EmscriptenFocusEvent static EM_BOOL EmscriptenVisibilityChangeCallback(int eventType, const EmscriptenVisibilityChangeEvent *visibilityChangeEvent, void *userData); // Emscripten input callback events -static EM_BOOL EmscriptenKeyboardCallback(int eventType, const EmscriptenKeyboardEvent *keyboardEvent, void *userData); +//static EM_BOOL EmscriptenKeyboardCallback(int eventType, const EmscriptenKeyboardEvent *keyboardEvent, void *userData); static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); static EM_BOOL EmscriptenPointerlockCallback(int eventType, const EmscriptenPointerlockChangeEvent *pointerlockChangeEvent, void *userData); @@ -1369,8 +1369,8 @@ int InitPlatform(void) // Setup input events // NOTE: Keyboard callbacks only used to consume some events, libglfw.js takes care of the actual input - emscripten_set_keypress_callback(GetCanvasId(), NULL, 1, EmscriptenKeyboardCallback); - emscripten_set_keydown_callback(GetCanvasId(), NULL, 1, EmscriptenKeyboardCallback); + //emscripten_set_keypress_callback(GetCanvasId(), NULL, 1, EmscriptenKeyboardCallback); // WRNING: Breaks input + //emscripten_set_keydown_callback(GetCanvasId(), NULL, 1, EmscriptenKeyboardCallback); emscripten_set_click_callback(GetCanvasId(), NULL, 1, EmscriptenMouseCallback); emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenPointerlockCallback); emscripten_set_mousemove_callback(GetCanvasId(), NULL, 1, EmscriptenMouseMoveCallback); @@ -1620,13 +1620,15 @@ static void MouseEnterCallback(GLFWwindow *window, int enter) // Emscripten callback functions, called on specific browser events //------------------------------------------------------------------------------------------------------- +/* // Emscripten: Called on key events static EM_BOOL EmscriptenKeyboardCallback(int eventType, const EmscriptenKeyboardEvent *keyboardEvent, void *userData) { - // NOTE: Only used to consume some keyboard events without triggering browser functions - + // WARNING: Keyboard inputs already processed through GLFW callback + return 1; // The event was consumed by the callback handler } +*/ // Emscripten: Called on mouse input events static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) From 00f7121b1ceea4b9b1b6a34bf6d9829cb64ed2c1 Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Mon, 11 Aug 2025 18:16:00 +0100 Subject: [PATCH 240/242] Increasing maxInterations glsl100 to 255 to match with glsl330 and add glsl120 --- .../resources/shaders/glsl100/julia_set.fs | 4 +- .../resources/shaders/glsl120/julia_set.fs | 80 +++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 examples/shaders/resources/shaders/glsl120/julia_set.fs diff --git a/examples/shaders/resources/shaders/glsl100/julia_set.fs b/examples/shaders/resources/shaders/glsl100/julia_set.fs index 9ee8c6f4d..3a61ff5c5 100644 --- a/examples/shaders/resources/shaders/glsl100/julia_set.fs +++ b/examples/shaders/resources/shaders/glsl100/julia_set.fs @@ -12,7 +12,7 @@ uniform float zoom; // Zoom of the scale. // NOTE: Maximum number of shader for-loop iterations depend on GPU, // for example, on RasperryPi for this examply only supports up to 60 -const int maxIterations = 48; // Max iterations to do. +const int maxIterations = 255; // Max iterations to do. const float colorCycles = 1.0; // Number of times the color palette repeats. // Square a complex number @@ -57,7 +57,7 @@ void main() z.y += offset.y; int iter = 0; - for (int iterations = 0; iterations < 60; iterations++) + for (int iterations = 0; iterations < maxIterations; iterations++) { z = ComplexSquare(z) + c; // Iterate function if (dot(z, z) > 4.0) break; diff --git a/examples/shaders/resources/shaders/glsl120/julia_set.fs b/examples/shaders/resources/shaders/glsl120/julia_set.fs new file mode 100644 index 000000000..2f5361db6 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/julia_set.fs @@ -0,0 +1,80 @@ +#version 120 + +// Input vertex attributes (from vertex shader) +varying vec2 fragTexCoord; +varying vec4 fragColor; + +uniform vec2 c; // c.x = real, c.y = imaginary component. Equation done is z^2 + c +uniform vec2 offset; // Offset of the scale. +uniform float zoom; // Zoom of the scale. + +// NOTE: Maximum number of shader for-loop iterations depend on GPU, +// for example, on RasperryPi for this examply only supports up to 60 +const int maxIterations = 255; // Max iterations to do. +const float colorCycles = 1.0; // Number of times the color palette repeats. + +// Square a complex number +vec2 ComplexSquare(vec2 z) +{ + return vec2(z.x*z.x - z.y*z.y, z.x*z.y*2.0); +} + +// Convert Hue Saturation Value (HSV) color into RGB +vec3 Hsv2rgb(vec3 c) +{ + vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz)*6.0 - K.www); + return c.z*mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +void main() +{ + /********************************************************************************************** + Julia sets use a function z^2 + c, where c is a constant. + This function is iterated until the nature of the point is determined. + + If the magnitude of the number becomes greater than 2, then from that point onward + the number will get bigger and bigger, and will never get smaller (tends towards infinity). + 2^2 = 4, 4^2 = 8 and so on. + So at 2 we stop iterating. + + If the number is below 2, we keep iterating. + But when do we stop iterating if the number is always below 2 (it converges)? + That is what maxIterations is for. + Then we can divide the iterations by the maxIterations value to get a normalized value that we can + then map to a color. + + We use dot product (z.x * z.x + z.y * z.y) to determine the magnitude (length) squared. + And once the magnitude squared is > 4, then magnitude > 2 is also true (saves computational power). + *************************************************************************************************/ + + // The pixel coordinates are scaled so they are on the mandelbrot scale + // NOTE: fragTexCoord already comes as normalized screen coordinates but offset must be normalized before scaling and zoom + vec2 z = vec2((fragTexCoord.x - 0.5)*2.5, (fragTexCoord.y - 0.5)*1.5)/zoom; + z.x += offset.x; + z.y += offset.y; + + int iter = 0; + for (int iterations = 0; iterations < maxIterations; iterations++) + { + z = ComplexSquare(z) + c; // Iterate function + if (dot(z, z) > 4.0) break; + + iter = iterations; + } + + // Another few iterations decreases errors in the smoothing calculation. + // See http://linas.org/art-gallery/escape/escape.html for more information. + z = ComplexSquare(z) + c; + z = ComplexSquare(z) + c; + + // This last part smooths the color (again see link above). + float smoothVal = float(iter) + 1.0 - (log(log(length(z)))/log(2.0)); + + // Normalize the value so it is between 0 and 1. + float norm = smoothVal/float(maxIterations); + + // If in set, color black. 0.999 allows for some float accuracy error. + if (norm > 0.999) gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); + else gl_FragColor = vec4(Hsv2rgb(vec3(norm*colorCycles, 1.0, 1.0)), 1.0); +} From 8dae39fbda7077e0aa913fa85fe003bedc095bea Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 11 Aug 2025 20:22:02 +0200 Subject: [PATCH 241/242] ADDED: Missing GLSL120 shaders --- .../resources/shaders/glsl100/shadowmap.fs | 86 ++++++ .../resources/shaders/glsl100/shadowmap.vs | 32 ++ .../resources/shaders/glsl120/color_mix.fs | 24 ++ .../shaders/glsl120/cubes_panning.fs | 58 ++++ .../shaders/glsl120/deferred_shading.fs | 57 ++++ .../shaders/glsl120/deferred_shading.vs | 16 + .../resources/shaders/glsl120/eratosthenes.fs | 58 ++++ .../resources/shaders/glsl120/gbuffer.fs | 34 ++ .../resources/shaders/glsl120/gbuffer.vs | 60 ++++ .../shaders/glsl120/hybrid_raster.fs | 17 + .../shaders/glsl120/hybrid_raymarch.fs | 291 ++++++++++++++++++ .../resources/shaders/glsl120/julia_set.fs | 80 +++++ .../shaders/glsl120/lighting_instancing.vs | 36 +++ .../shaders/resources/shaders/glsl120/mask.fs | 22 ++ .../resources/shaders/glsl120/outline.fs | 32 ++ .../resources/shaders/glsl120/reload.fs | 37 +++ .../resources/shaders/glsl120/spotlight.fs | 75 +++++ .../resources/shaders/glsl120/tiling.fs | 19 ++ .../shaders/glsl120/vertex_displacement.fs | 15 + .../shaders/glsl120/vertex_displacement.vs | 43 +++ .../shaders/resources/shaders/glsl120/wave.fs | 32 ++ .../resources/shaders/glsl120/write_depth.fs | 17 + 22 files changed, 1141 insertions(+) create mode 100644 examples/shaders/resources/shaders/glsl100/shadowmap.fs create mode 100644 examples/shaders/resources/shaders/glsl100/shadowmap.vs create mode 100644 examples/shaders/resources/shaders/glsl120/color_mix.fs create mode 100644 examples/shaders/resources/shaders/glsl120/cubes_panning.fs create mode 100644 examples/shaders/resources/shaders/glsl120/deferred_shading.fs create mode 100644 examples/shaders/resources/shaders/glsl120/deferred_shading.vs create mode 100644 examples/shaders/resources/shaders/glsl120/eratosthenes.fs create mode 100644 examples/shaders/resources/shaders/glsl120/gbuffer.fs create mode 100644 examples/shaders/resources/shaders/glsl120/gbuffer.vs create mode 100644 examples/shaders/resources/shaders/glsl120/hybrid_raster.fs create mode 100644 examples/shaders/resources/shaders/glsl120/hybrid_raymarch.fs create mode 100644 examples/shaders/resources/shaders/glsl120/julia_set.fs create mode 100644 examples/shaders/resources/shaders/glsl120/lighting_instancing.vs create mode 100644 examples/shaders/resources/shaders/glsl120/mask.fs create mode 100644 examples/shaders/resources/shaders/glsl120/outline.fs create mode 100644 examples/shaders/resources/shaders/glsl120/reload.fs create mode 100644 examples/shaders/resources/shaders/glsl120/spotlight.fs create mode 100644 examples/shaders/resources/shaders/glsl120/tiling.fs create mode 100644 examples/shaders/resources/shaders/glsl120/vertex_displacement.fs create mode 100644 examples/shaders/resources/shaders/glsl120/vertex_displacement.vs create mode 100644 examples/shaders/resources/shaders/glsl120/wave.fs create mode 100644 examples/shaders/resources/shaders/glsl120/write_depth.fs diff --git a/examples/shaders/resources/shaders/glsl100/shadowmap.fs b/examples/shaders/resources/shaders/glsl100/shadowmap.fs new file mode 100644 index 000000000..7a0e806a5 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl100/shadowmap.fs @@ -0,0 +1,86 @@ +#version 100 + +precision mediump float; + +// This shader is based on the basic lighting shader +// This only supports one light, which is directional, and it (of course) supports shadows + +// Input vertex attributes (from vertex shader) +varying vec3 fragPosition; +varying vec2 fragTexCoord; +//varying in vec4 fragColor; +varying vec3 fragNormal; + +// Input uniform values +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +// Input lighting values +uniform vec3 lightDir; +uniform vec4 lightColor; +uniform vec4 ambient; +uniform vec3 viewPos; + +// Input shadowmapping values +uniform mat4 lightVP; // Light source view-projection matrix +uniform sampler2D shadowMap; + +uniform int shadowMapResolution; + +void main() +{ + // Texel color fetching from texture sampler + vec4 texelColor = texture2D(texture0, fragTexCoord); + vec3 lightDot = vec3(0.0); + vec3 normal = normalize(fragNormal); + vec3 viewD = normalize(viewPos - fragPosition); + vec3 specular = vec3(0.0); + + vec3 l = -lightDir; + + float NdotL = max(dot(normal, l), 0.0); + lightDot += lightColor.rgb*NdotL; + + float specCo = 0.0; + if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewD, reflect(-(l), normal))), 16.0); // 16 refers to shine + specular += specCo; + + vec4 finalColor = (texelColor*((colDiffuse + vec4(specular, 1.0))*vec4(lightDot, 1.0))); + + // Shadow calculations + vec4 fragPosLightSpace = lightVP*vec4(fragPosition, 1); + fragPosLightSpace.xyz /= fragPosLightSpace.w; // Perform the perspective division + fragPosLightSpace.xyz = (fragPosLightSpace.xyz + 1.0)/2.0; // Transform from [-1, 1] range to [0, 1] range + vec2 sampleCoords = fragPosLightSpace.xy; + float curDepth = fragPosLightSpace.z; + + // Slope-scale depth bias: depth biasing reduces "shadow acne" artifacts, where dark stripes appear all over the scene + // The solution is adding a small bias to the depth + // In this case, the bias is proportional to the slope of the surface, relative to the light + float bias = max(0.0008*(1.0 - dot(normal, l)), 0.00008); + int shadowCounter = 0; + const int numSamples = 9; + + // PCF (percentage-closer filtering) algorithm: + // Instead of testing if just one point is closer to the current point, + // we test the surrounding points as well + // This blurs shadow edges, hiding aliasing artifacts + vec2 texelSize = vec2(1.0/float(shadowMapResolution)); + for (int x = -1; x <= 1; x++) + { + for (int y = -1; y <= 1; y++) + { + float sampleDepth = texture2D(shadowMap, sampleCoords + texelSize*vec2(x, y)).r; + if (curDepth - bias > sampleDepth) shadowCounter++; + } + } + + finalColor = mix(finalColor, vec4(0, 0, 0, 1), float(shadowCounter)/float(numSamples)); + + // Add ambient lighting whether in shadow or not + finalColor += texelColor*(ambient/10.0)*colDiffuse; + + // Gamma correction + finalColor = pow(finalColor, vec4(1.0/2.2)); + gl_FragColor = finalColor; +} diff --git a/examples/shaders/resources/shaders/glsl100/shadowmap.vs b/examples/shaders/resources/shaders/glsl100/shadowmap.vs new file mode 100644 index 000000000..0fb4d476f --- /dev/null +++ b/examples/shaders/resources/shaders/glsl100/shadowmap.vs @@ -0,0 +1,32 @@ +#version 100 + +// Input vertex attributes +attribute vec3 vertexPosition; +attribute vec2 vertexTexCoord; +attribute vec3 vertexNormal; +attribute vec4 vertexColor; + +// Input uniform values +uniform mat4 mvp; +uniform mat4 matModel; +uniform mat4 matNormal; + +// Output vertex attributes (to fragment shader) +varying vec3 fragPosition; +varying vec2 fragTexCoord; +varying vec4 fragColor; +varying vec3 fragNormal; + +// NOTE: Add your custom variables here + +void main() +{ + // Send vertex attributes to fragment shader + fragPosition = vec3(matModel*vec4(vertexPosition, 1.0)); + fragTexCoord = vertexTexCoord; + fragColor = vertexColor; + fragNormal = normalize(vec3(matNormal*vec4(vertexNormal, 1.0))); + + // Calculate final vertex position + gl_Position = mvp*vec4(vertexPosition, 1.0); +} diff --git a/examples/shaders/resources/shaders/glsl120/color_mix.fs b/examples/shaders/resources/shaders/glsl120/color_mix.fs new file mode 100644 index 000000000..999f61afc --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/color_mix.fs @@ -0,0 +1,24 @@ +#version 120 + +// Input vertex attributes (from vertex shader) +varying vec2 fragTexCoord; +varying vec4 fragColor; + +// Input uniform values +uniform sampler2D texture0; +uniform sampler2D texture1; +uniform vec4 colDiffuse; + +uniform float divider; + +void main() +{ + // Texel color fetching from texture sampler + vec4 texelColor0 = texture2D(texture0, fragTexCoord); + vec4 texelColor1 = texture2D(texture1, fragTexCoord); + + float x = fract(fragTexCoord.s); + float final = smoothstep(divider - 0.1, divider + 0.1, x); + + gl_FragColor = mix(texelColor0, texelColor1, final); +} diff --git a/examples/shaders/resources/shaders/glsl120/cubes_panning.fs b/examples/shaders/resources/shaders/glsl120/cubes_panning.fs new file mode 100644 index 000000000..24c53d2ff --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/cubes_panning.fs @@ -0,0 +1,58 @@ +#version 120 + +// Input vertex attributes (from vertex shader) +varying vec2 fragTexCoord; +varying vec4 fragColor; + +// Custom variables +const float PI = 3.14159265358979323846; +uniform float uTime; + +float divisions = 5.0; +float angle = 0.0; + +vec2 VectorRotateTime(vec2 v, float speed) +{ + float time = uTime*speed; + float localTime = fract(time); // The time domain this works on is 1 sec + + if ((localTime >= 0.0) && (localTime < 0.25)) angle = 0.0; + else if ((localTime >= 0.25) && (localTime < 0.50)) angle = PI/4.0*sin(2.0*PI*localTime - PI/2.0); + else if ((localTime >= 0.50) && (localTime < 0.75)) angle = PI*0.25; + else if ((localTime >= 0.75) && (localTime < 1.00)) angle = PI/4.0*sin(2.0*PI*localTime); + + // Rotate vector by angle + v -= 0.5; + v = mat2(cos(angle), -sin(angle), sin(angle), cos(angle))*v; + v += 0.5; + + return v; +} + +float Rectangle(in vec2 st, in float size, in float fill) +{ + float roundSize = 0.5 - size/2.0; + float left = step(roundSize, st.x); + float top = step(roundSize, st.y); + float bottom = step(roundSize, 1.0 - st.y); + float right = step(roundSize, 1.0 - st.x); + + return (left*bottom*right*top)*fill; +} + +void main() +{ + vec2 fragPos = fragTexCoord; + fragPos.xy += uTime/9.0; + + fragPos *= divisions; + vec2 ipos = floor(fragPos); // Get the integer coords + vec2 fpos = fract(fragPos); // Get the fractional coords + + fpos = VectorRotateTime(fpos, 0.2); + + float alpha = Rectangle(fpos, 0.216, 1.0); + vec3 color = vec3(0.3, 0.3, 0.3); + + gl_FragColor = vec4(color, alpha); +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl120/deferred_shading.fs b/examples/shaders/resources/shaders/glsl120/deferred_shading.fs new file mode 100644 index 000000000..f52454d8c --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/deferred_shading.fs @@ -0,0 +1,57 @@ +#version 120 + +// Input vertex attributes (from vertex shader) +varying vec2 fragTexCoord; +varying vec4 fragColor; + +// Input uniform values +uniform sampler2D gPosition; +uniform sampler2D gNormal; +uniform sampler2D gAlbedoSpec; + +struct Light { + int enabled; + int type; // Unused in this demo + vec3 position; + vec3 target; // Unused in this demo + vec4 color; +}; + +const int NR_LIGHTS = 4; +uniform Light lights[NR_LIGHTS]; +uniform vec3 viewPosition; + +const float QUADRATIC = 0.032; +const float LINEAR = 0.09; + +void main() +{ + vec3 fragPosition = texture2D(gPosition, fragTexCoord).rgb; + vec3 normal = texture2D(gNormal, fragTexCoord).rgb; + vec3 albedo = texture2D(gAlbedoSpec, fragTexCoord).rgb; + float specular = texture2D(gAlbedoSpec, fragTexCoord).a; + + vec3 ambient = albedo*vec3(0.1); + vec3 viewDirection = normalize(viewPosition - fragPosition); + + for (int i = 0; i < NR_LIGHTS; ++i) + { + if (lights[i].enabled == 0) continue; + vec3 lightDirection = lights[i].position - fragPosition; + vec3 diffuse = max(dot(normal, lightDirection), 0.0)*albedo*lights[i].color.xyz; + + vec3 halfwayDirection = normalize(lightDirection + viewDirection); + float spec = pow(max(dot(normal, halfwayDirection), 0.0), 32.0); + vec3 specular = specular*spec*lights[i].color.xyz; + + // Attenuation + float distance = length(lights[i].position - fragPosition); + float attenuation = 1.0/(1.0 + LINEAR*distance + QUADRATIC*distance*distance); + diffuse *= attenuation; + specular *= attenuation; + ambient += diffuse + specular; + } + + gl_FragColor = vec4(ambient, 1.0); +} + diff --git a/examples/shaders/resources/shaders/glsl120/deferred_shading.vs b/examples/shaders/resources/shaders/glsl120/deferred_shading.vs new file mode 100644 index 000000000..daece19e5 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/deferred_shading.vs @@ -0,0 +1,16 @@ +#version 120 + +// Input vertex attributes +attribute vec3 vertexPosition; +attribute vec2 vertexTexCoord; + +// Output vertex attributes (to fragment shader) +varying vec2 fragTexCoord; + +void main() +{ + fragTexCoord = vertexTexCoord; + + // Calculate final vertex position + gl_Position = vec4(vertexPosition, 1.0); +} diff --git a/examples/shaders/resources/shaders/glsl120/eratosthenes.fs b/examples/shaders/resources/shaders/glsl120/eratosthenes.fs new file mode 100644 index 000000000..d1623a67c --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/eratosthenes.fs @@ -0,0 +1,58 @@ +#version 120 + +/************************************************************************************* + + The Sieve of Eratosthenes -- a simple shader by ProfJski + An early prime number sieve: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes + + The screen is divided into a square grid of boxes, each representing an integer value + Each integer is tested to see if it is a prime number. Primes are colored white + Non-primes are colored with a color that indicates the smallest factor which evenly divdes our integer + + You can change the scale variable to make a larger or smaller grid + Total number of integers displayed = scale squared, so scale = 100 tests the first 10,000 integers + + WARNING: If you make scale too large, your GPU may bog down! + +***************************************************************************************/ + +// Input vertex attributes (from vertex shader) +varying vec2 fragTexCoord; +varying vec4 fragColor; + +// Make a nice spectrum of colors based on counter and maxSize +vec4 Colorizer(float counter, float maxSize) +{ + float red = 0.0, green = 0.0, blue = 0.0; + float normsize = counter/maxSize; + + red = smoothstep(0.3, 0.7, normsize); + green = sin(3.14159*normsize); + blue = 1.0 - smoothstep(0.0, 0.4, normsize); + + return vec4(0.8*red, 0.8*green, 0.8*blue, 1.0); +} + +void main() +{ + vec4 color = vec4(1.0); + float scale = 1000.0; // Makes 100x100 square grid. Change this variable to make a smaller or larger grid + float value = scale*floor(fragTexCoord.y*scale) + floor(fragTexCoord.x*scale); // Group pixels into boxes representing integer values + int valuei = int(value); + + //if ((valuei == 0) || (valuei == 1) || (valuei == 2)) gl_FragColor = vec4(1.0); + //else + { + //for (int i = 2; (i < int(max(2.0, sqrt(value) + 1.0))); i++) + // NOTE: On GLSL 100 for loops are restricted and loop condition must be a constant + // Tested on RPI, it seems loops are limited around 60 iteractions + for (int i = 2; i < 48; i++) + { + if ((value - float(i)*floor(value/float(i))) <= 0.0) + { + gl_FragColor = Colorizer(float(i), scale); + //break; // Uncomment to color by the largest factor instead + } + } + } +} diff --git a/examples/shaders/resources/shaders/glsl120/gbuffer.fs b/examples/shaders/resources/shaders/glsl120/gbuffer.fs new file mode 100644 index 000000000..a826d7916 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/gbuffer.fs @@ -0,0 +1,34 @@ +#version 120 + +// Input vertex attributes (from vertex shader) +varying vec3 fragPosition; +varying vec2 fragTexCoord; +varying vec3 fragNormal; +varying vec4 fragColor; + +// TODO: Is there some alternative for GLSL100 +//layout (location = 0) out vec3 gPosition; +//layout (location = 1) out vec3 gNormal; +//layout (location = 2) out vec4 gAlbedoSpec; +//uniform vec3 gPosition; +//uniform vec3 gNormal; +//uniform vec4 gAlbedoSpec; + +// Input uniform values +uniform sampler2D texture0; // Diffuse texture +uniform sampler2D specularTexture; + +void main() +{ + // Store the fragment position vector in the first gbuffer texture + //gPosition = fragPosition; + + // Store the per-fragment normals into the gbuffer + //gNormal = normalize(fragNormal); + + // Store the diffuse per-fragment color + gl_FragColor.rgb = texture2D(texture0, fragTexCoord).rgb; + + // Store specular intensity in gAlbedoSpec's alpha component + gl_FragColor.a = texture2D(specularTexture, fragTexCoord).r; +} diff --git a/examples/shaders/resources/shaders/glsl120/gbuffer.vs b/examples/shaders/resources/shaders/glsl120/gbuffer.vs new file mode 100644 index 000000000..adc1dcd1a --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/gbuffer.vs @@ -0,0 +1,60 @@ +#version 120 + +// Input vertex attributes +attribute vec3 vertexPosition; +attribute vec2 vertexTexCoord; +attribute vec3 vertexNormal; +attribute vec4 vertexColor; + +// Input uniform values +uniform mat4 matModel; +uniform mat4 matView; +uniform mat4 matProjection; + +// Output vertex attributes (to fragment shader) +varying vec3 fragPosition; +varying vec2 fragTexCoord; +varying vec3 fragNormal; +varying vec4 fragColor; + + +// https://github.com/glslify/glsl-inverse +mat3 inverse(mat3 m) +{ + float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2]; + float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2]; + float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2]; + + float b01 = a22*a11 - a12*a21; + float b11 = -a22*a10 + a12*a20; + float b21 = a21*a10 - a11*a20; + + float det = a00*b01 + a01*b11 + a02*b21; + + return mat3(b01, (-a22*a01 + a02*a21), (a12*a01 - a02*a11), + b11, (a22*a00 - a02*a20), (-a12*a00 + a02*a10), + b21, (-a21*a00 + a01*a20), (a11*a00 - a01*a10))/det; +} + +// https://github.com/glslify/glsl-transpose +mat3 transpose(mat3 m) +{ + return mat3(m[0][0], m[1][0], m[2][0], + m[0][1], m[1][1], m[2][1], + m[0][2], m[1][2], m[2][2]); +} + +void main() +{ + // Calculate vertex attributes for fragment shader + vec4 worldPos = matModel*vec4(vertexPosition, 1.0); + fragPosition = worldPos.xyz; + fragTexCoord = vertexTexCoord; + fragColor = vertexColor; + + mat3 normalMatrix = transpose(inverse(mat3(matModel))); + fragNormal = normalMatrix*vertexNormal; + + // Calculate final vertex position + gl_Position = matProjection*matView*worldPos; +} diff --git a/examples/shaders/resources/shaders/glsl120/hybrid_raster.fs b/examples/shaders/resources/shaders/glsl120/hybrid_raster.fs new file mode 100644 index 000000000..e4c7e2eca --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/hybrid_raster.fs @@ -0,0 +1,17 @@ +#version 120 + +#extension GL_EXT_frag_depth : enable // Extension required for writing depth + +varying vec2 fragTexCoord; +varying vec4 fragColor; + +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +void main() +{ + vec4 texelColor = texture2D(texture0, fragTexCoord); + + gl_FragColor = texelColor*colDiffuse*fragColor; + gl_FragDepthEXT = gl_FragCoord.z; +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl120/hybrid_raymarch.fs b/examples/shaders/resources/shaders/glsl120/hybrid_raymarch.fs new file mode 100644 index 000000000..3118e1861 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/hybrid_raymarch.fs @@ -0,0 +1,291 @@ +#version 120 + +#extension GL_EXT_frag_depth : enable //Extension required for writing depth +#extension GL_OES_standard_derivatives : enable //Extension used for fwidth() + +// Input vertex attributes (from vertex shader) +varying vec2 fragTexCoord; +varying vec4 fragColor; + +// Input uniform values +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +// Custom Input Uniform +uniform vec3 camPos; +uniform vec3 camDir; +uniform vec2 screenCenter; + +#define ZERO 0 + +// SRC: https://learnopengl.com/Advanced-OpenGL/Depth-testing +float CalcDepth(in vec3 rd, in float Idist) +{ + float local_z = dot(normalize(camDir),rd)*Idist; + return (1.0/(local_z) - 1.0/0.01)/(1.0/1000.0 -1.0/0.01); +} + +// SRC: https://iquilezles.org/articles/distfunctions/ +float sdHorseshoe(in vec3 p, in vec2 c, in float r, in float le, vec2 w) +{ + p.x = abs(p.x); + float l = length(p.xy); + p.xy = mat2(-c.x, c.y, + c.y, c.x)*p.xy; + p.xy = vec2((p.y>0.0 || p.x>0.0)?p.x:l*sign(-c.x), + (p.x>0.0)?p.y:l); + p.xy = vec2(p.x,abs(p.y-r))-vec2(le,0.0); + + vec2 q = vec2(length(max(p.xy,0.0)) + min(0.0,max(p.x,p.y)),p.z); + vec2 d = abs(q) - w; + return min(max(d.x,d.y),0.0) + length(max(d,0.0)); +} + +// r = sphere's radius +// h = cutting's plane's position +// t = thickness +float sdSixWayCutHollowSphere(vec3 p, float r, float h, float t) +{ + // Six way symetry Transformation + vec3 ap = abs(p); + if (ap.x < max(ap.y, ap.z)){ + if (ap.y < ap.z) ap.xz = ap.zx; + else ap.xy = ap.yx; + } + + vec2 q = vec2(length(ap.yz), ap.x); + + float w = sqrt(r*r-h*h); + + return ((h*q.x0.0) + { + tmax = min(tmax, tp1); + res = vec2(tp1, 1.0); + } + + float t = tmin; + for (int i=0; i<70 ; i++) + { + if (t>tmax) break; + vec2 h = map(ro+rd*t); + if (abs(h.x) < (0.0001*t)) + { + res = vec2(t,h.y); + break; + } + t += h.x; + } + + return res; +} + + +// https://iquilezles.org/articles/rmshadows +float calcSoftshadow(in vec3 ro, in vec3 rd, in float mint, in float tmax) +{ + // bounding volume + float tp = (0.8-ro.y)/rd.y; if (tp>0.0) tmax = min(tmax, tp); + + float res = 1.0; + float t = mint; + for (int i=ZERO; i<24; i++) + { + float h = map(ro + rd*t).x; + float s = clamp(8.0*h/t,0.0,1.0); + res = min(res, s); + t += clamp(h, 0.01, 0.2); + if (res<0.004 || t>tmax) break; + } + res = clamp(res, 0.0, 1.0); + return res*res*(3.0-2.0*res); +} + + +// https://iquilezles.org/articles/normalsSDF +vec3 calcNormal(in vec3 pos) +{ + 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); +} + +// https://iquilezles.org/articles/nvscene2008/rwwtt.pdf +float calcAO(in vec3 pos, in vec3 nor) +{ + float occ = 0.0; + float sca = 1.0; + for (int i=ZERO; i<5; i++) + { + float h = 0.01 + 0.12*float(i)/4.0; + float d = map(pos + h*nor).x; + occ += (h-d)*sca; + sca *= 0.95; + if (occ>0.35) break; + } + return clamp(1.0 - 3.0*occ, 0.0, 1.0)*(0.5+0.5*nor.y); +} + +// https://iquilezles.org/articles/checkerfiltering +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; +} + +// https://www.shadertoy.com/view/tdS3DG +vec4 render(in vec3 ro, in vec3 rd) +{ + // background + vec3 col = vec3(0.7, 0.7, 0.9) - max(rd.y,0.0)*0.3; + + // raycast scene + vec2 res = raycast(ro,rd); + float t = res.x; + float m = res.y; + if (m>-0.5) + { + vec3 pos = ro + t*rd; + vec3 nor = (m<1.5) ? vec3(0.0,1.0,0.0) : calcNormal(pos); + vec3 ref = reflect(rd, nor); + + // material + col = 0.2 + 0.2*sin(m*2.0 + vec3(0.0,1.0,2.0)); + float ks = 1.0; + + if (m<1.5) + { + float f = checkersGradBox(3.0*pos.xz); + col = 0.15 + f*vec3(0.05); + ks = 0.4; + } + + // lighting + float occ = calcAO(pos, nor); + + vec3 lin = vec3(0.0); + + // sun + { + vec3 lig = normalize(vec3(-0.5, 0.4, -0.6)); + vec3 hal = normalize(lig-rd); + float dif = clamp(dot(nor, lig), 0.0, 1.0); + //if (dif>0.0001) + dif *= calcSoftshadow(pos, lig, 0.02, 2.5); + float spe = pow(clamp(dot(nor, hal), 0.0, 1.0),16.0); + spe *= dif; + spe *= 0.04+0.96*pow(clamp(1.0-dot(hal,lig),0.0,1.0),5.0); + //spe *= 0.04+0.96*pow(clamp(1.0-sqrt(0.5*(1.0-dot(rd,lig))),0.0,1.0),5.0); + lin += col*2.20*dif*vec3(1.30,1.00,0.70); + lin += 5.00*spe*vec3(1.30,1.00,0.70)*ks; + } + // sky + { + float dif = sqrt(clamp(0.5+0.5*nor.y, 0.0, 1.0)); + dif *= occ; + float spe = smoothstep(-0.2, 0.2, ref.y); + spe *= dif; + spe *= 0.04+0.96*pow(clamp(1.0+dot(nor,rd),0.0,1.0), 5.0); + //if (spe>0.001) + spe *= calcSoftshadow(pos, ref, 0.02, 2.5); + lin += col*0.60*dif*vec3(0.40,0.60,1.15); + lin += 2.00*spe*vec3(0.40,0.60,1.30)*ks; + } + // back + { + float dif = clamp(dot(nor, normalize(vec3(0.5,0.0,0.6))), 0.0, 1.0)*clamp(1.0-pos.y,0.0,1.0); + dif *= occ; + lin += col*0.55*dif*vec3(0.25,0.25,0.25); + } + // sss + { + float dif = pow(clamp(1.0+dot(nor,rd),0.0,1.0),2.0); + dif *= occ; + lin += col*0.25*dif*vec3(1.00,1.00,1.00); + } + + col = lin; + + col = mix(col, vec3(0.7,0.7,0.9), 1.0-exp(-0.0001*t*t*t)); + } + + return vec4(vec3(clamp(col,0.0,1.0)),t); +} + +vec3 CalcRayDir(vec2 nCoord){ + vec3 horizontal = normalize(cross(camDir,vec3(.0 , 1.0, .0))); + vec3 vertical = normalize(cross(horizontal,camDir)); + return normalize(camDir + horizontal*nCoord.x + vertical*nCoord.y); +} + +mat3 setCamera() +{ + vec3 cw = normalize(camDir); + vec3 cp = vec3(0.0, 1.0 ,0.0); + vec3 cu = normalize(cross(cw,cp)); + vec3 cv = (cross(cu,cw)); + return mat3(cu, cv, cw); +} + +void main() +{ + vec2 nCoord = (gl_FragCoord.xy - screenCenter.xy)/screenCenter.y; + mat3 ca = setCamera(); + + // focal length + float fl = length(camDir); + vec3 rd = ca*normalize(vec3(nCoord,fl)); + vec3 color = vec3(nCoord/2.0 + 0.5, 0.0); + float depth = gl_FragCoord.z; + { + vec4 res = render(camPos - vec3(0.0, 0.0, 0.0) , rd); + color = res.xyz; + depth = CalcDepth(rd,res.w); + } + gl_FragColor = vec4(color , 1.0); + gl_FragDepthEXT = depth; +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl120/julia_set.fs b/examples/shaders/resources/shaders/glsl120/julia_set.fs new file mode 100644 index 000000000..d5749c0f0 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/julia_set.fs @@ -0,0 +1,80 @@ +#version 120 + +// Input vertex attributes (from vertex shader) +varying vec2 fragTexCoord; +varying vec4 fragColor; + +uniform vec2 c; // c.x = real, c.y = imaginary component. Equation done is z^2 + c +uniform vec2 offset; // Offset of the scale +uniform float zoom; // Zoom of the scale + +// NOTE: Maximum number of shader for-loop iterations depend on GPU, +// for example, on RasperryPi for this examply only supports up to 60 +const int maxIterations = 48; // Max iterations to do +const float colorCycles = 1.0; // Number of times the color palette repeats + +// Square a complex number +vec2 ComplexSquare(vec2 z) +{ + return vec2(z.x*z.x - z.y*z.y, z.x*z.y*2.0); +} + +// Convert Hue Saturation Value (HSV) color into RGB +vec3 Hsv2rgb(vec3 c) +{ + vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz)*6.0 - K.www); + return c.z*mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +void main() +{ + /********************************************************************************************** + Julia sets use a function z^2 + c, where c is a constant + This function is iterated until the nature of the point is determined + + If the magnitude of the number becomes greater than 2, then from that point onward + the number will get bigger and bigger, and will never get smaller (tends towards infinity) + 2^2 = 4, 4^2 = 8 and so on + So at 2 we stop iterating + + If the number is below 2, we keep iterating + But when do we stop iterating if the number is always below 2 (it converges)? + That is what maxIterations is for + Then we can divide the iterations by the maxIterations value to get a normalized value + that we can then map to a color + + We use dot product (z.x*z.x + z.y*z.y) to determine the magnitude (length) squared + And once the magnitude squared is > 4, then magnitude > 2 is also true (saves computational power) + *************************************************************************************************/ + + // The pixel coordinates are scaled so they are on the mandelbrot scale + // NOTE: fragTexCoord already comes as normalized screen coordinates but offset must be normalized before scaling and zoom + vec2 z = vec2((fragTexCoord.x - 0.5)*2.5, (fragTexCoord.y - 0.5)*1.5)/zoom; + z.x += offset.x; + z.y += offset.y; + + int iter = 0; + for (int iterations = 0; iterations < 60; iterations++) + { + z = ComplexSquare(z) + c; // Iterate function + if (dot(z, z) > 4.0) break; + + iter = iterations; + } + + // Another few iterations decreases errors in the smoothing calculation + // See http://linas.org/art-gallery/escape/escape.html for more information + z = ComplexSquare(z) + c; + z = ComplexSquare(z) + c; + + // This last part smooths the color (again see link above) + float smoothVal = float(iter) + 1.0 - (log(log(length(z)))/log(2.0)); + + // Normalize the value so it is between 0 and 1 + float norm = smoothVal/float(maxIterations); + + // If in set, color black. 0.999 allows for some float accuracy error + if (norm > 0.999) gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); + else gl_FragColor = vec4(Hsv2rgb(vec3(norm*colorCycles, 1.0, 1.0)), 1.0); +} diff --git a/examples/shaders/resources/shaders/glsl120/lighting_instancing.vs b/examples/shaders/resources/shaders/glsl120/lighting_instancing.vs new file mode 100644 index 000000000..4787bc57a --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/lighting_instancing.vs @@ -0,0 +1,36 @@ +#version 120 + +// Input vertex attributes +attribute vec3 vertexPosition; +attribute vec2 vertexTexCoord; +attribute vec3 vertexNormal; +attribute vec4 vertexColor; + +attribute mat4 instanceTransform; + +// Input uniform values +uniform mat4 mvp; +uniform mat4 matNormal; + +// Output vertex attributes (to fragment shader) +varying vec3 fragPosition; +varying vec2 fragTexCoord; +varying vec4 fragColor; +varying vec3 fragNormal; + +// NOTE: Add your custom variables here + +void main() +{ + // Compute MVP for current instance + mat4 mvpi = mvp*instanceTransform; + + // Send vertex attributes to fragment shader + fragPosition = vec3(mvpi*vec4(vertexPosition, 1.0)); + fragTexCoord = vertexTexCoord; + fragColor = vertexColor; + fragNormal = normalize(vec3(matNormal*vec4(vertexNormal, 1.0))); + + // Calculate final vertex position + gl_Position = mvpi*vec4(vertexPosition, 1.0); +} diff --git a/examples/shaders/resources/shaders/glsl120/mask.fs b/examples/shaders/resources/shaders/glsl120/mask.fs new file mode 100644 index 000000000..8203a7008 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/mask.fs @@ -0,0 +1,22 @@ +#version 120 + +// Input vertex attributes (from vertex shader) +varying vec2 fragTexCoord; +varying vec4 fragColor; + +// Input uniform values +uniform sampler2D texture0; +uniform sampler2D mask; +uniform vec4 colDiffuse; +uniform int frame; + +// NOTE: Add your custom variables here + +void main() +{ + vec4 maskColour = texture2D(mask, fragTexCoord + vec2(sin(-float(frame)/150.0)/10.0, cos(-float(frame)/170.0)/10.0)); + if (maskColour.r < 0.25) discard; + vec4 texelColor = texture2D(texture0, fragTexCoord + vec2(sin(float(frame)/90.0)/8.0, cos(float(frame)/60.0)/8.0)); + + gl_FragColor = texelColor*maskColour; +} diff --git a/examples/shaders/resources/shaders/glsl120/outline.fs b/examples/shaders/resources/shaders/glsl120/outline.fs new file mode 100644 index 000000000..1d632caf6 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/outline.fs @@ -0,0 +1,32 @@ +#version 120 + +// Input vertex attributes (from vertex shader) +varying vec2 fragTexCoord; +varying vec4 fragColor; + +// Input uniform values +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +uniform vec2 textureSize; +uniform float outlineSize; +uniform vec4 outlineColor; + +void main() +{ + vec4 texel = texture2D(texture0, fragTexCoord); // Get texel color + vec2 texelScale = vec2(0.0); + texelScale.x = outlineSize/textureSize.x; + texelScale.y = outlineSize/textureSize.y; + + // We sample four corner texels, but only for the alpha channel (this is for the outline) + vec4 corners = vec4(0.0); + corners.x = texture2D(texture0, fragTexCoord + vec2(texelScale.x, texelScale.y)).a; + corners.y = texture2D(texture0, fragTexCoord + vec2(texelScale.x, -texelScale.y)).a; + corners.z = texture2D(texture0, fragTexCoord + vec2(-texelScale.x, texelScale.y)).a; + corners.w = texture2D(texture0, fragTexCoord + vec2(-texelScale.x, -texelScale.y)).a; + + float outline = min(dot(corners, vec4(1.0)), 1.0); + vec4 color = mix(vec4(0.0), outlineColor, outline); + gl_FragColor = mix(color, texel, texel.a); +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl120/reload.fs b/examples/shaders/resources/shaders/glsl120/reload.fs new file mode 100644 index 000000000..9b5e12746 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/reload.fs @@ -0,0 +1,37 @@ +#version 120 + +// Input vertex attributes (from vertex shader) +varying vec2 fragTexCoord; // Texture coordinates (sampler2D) +varying vec4 fragColor; // Tint color + +// Uniform inputs +uniform vec2 resolution; // Viewport resolution (in pixels) +uniform vec2 mouse; // Mouse pixel xy coordinates +uniform float time; // Total run time (in secods) + +// Draw circle +vec4 DrawCircle(vec2 fragCoord, vec2 position, float radius, vec3 color) +{ + float d = length(position - fragCoord) - radius; + float t = clamp(d, 0.0, 1.0); + return vec4(color, 1.0 - t); +} + +void main() +{ + vec2 fragCoord = gl_FragCoord.xy; + vec2 position = vec2(mouse.x, resolution.y - mouse.y); + float radius = 40.0; + + // Draw background layer + vec4 colorA = vec4(0.2,0.2,0.8, 1.0); + vec4 colorB = vec4(1.0,0.7,0.2, 1.0); + vec4 layer1 = mix(colorA, colorB, abs(sin(time*0.1))); + + // Draw circle layer + vec3 color = vec3(0.9, 0.16, 0.21); + vec4 layer2 = DrawCircle(fragCoord, position, radius, color); + + // Blend the two layers + gl_FragColor = mix(layer1, layer2, layer2.a); +} diff --git a/examples/shaders/resources/shaders/glsl120/spotlight.fs b/examples/shaders/resources/shaders/glsl120/spotlight.fs new file mode 100644 index 000000000..da15a8b39 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/spotlight.fs @@ -0,0 +1,75 @@ +#version 120 + +#define MAX_SPOTS 3 + +struct Spot { + vec2 pos; // window coords of spot + float inner; // inner fully transparent centre radius + float radius; // alpha fades out to this radius +}; + +uniform Spot spots[MAX_SPOTS]; // Spotlight positions array +uniform float screenWidth; // Width of the screen + +void main() +{ + float alpha = 1.0; + + // Get the position of the current fragment (screen coordinates!) + vec2 pos = vec2(gl_FragCoord.x, gl_FragCoord.y); + + // Find out which spotlight is nearest + float d = 65000.0; // some high value + int fi = -1; // found index + + for (int i = 0; i < MAX_SPOTS; i++) + { + for (int j = 0; j < MAX_SPOTS; j++) + { + float dj = distance(pos, spots[j].pos) - spots[j].radius + spots[i].radius; + + if (d > dj) + { + d = dj; + fi = i; + } + } + } + + // d now equals distance to nearest spot... + // allowing for the different radii of all spotlights + if (fi == 0) + { + if (d > spots[0].radius) alpha = 1.0; + else + { + if (d < spots[0].inner) alpha = 0.0; + else alpha = (d - spots[0].inner)/(spots[0].radius - spots[0].inner); + } + } + else if (fi == 1) + { + if (d > spots[1].radius) alpha = 1.0; + else + { + if (d < spots[1].inner) alpha = 0.0; + else alpha = (d - spots[1].inner)/(spots[1].radius - spots[1].inner); + } + } + else if (fi == 2) + { + if (d > spots[2].radius) alpha = 1.0; + else + { + if (d < spots[2].inner) alpha = 0.0; + else alpha = (d - spots[2].inner)/(spots[2].radius - spots[2].inner); + } + } + + // Right hand side of screen is dimly lit, + // could make the threshold value user definable + if ((pos.x > screenWidth/2.0) && (alpha > 0.9)) alpha = 0.9; + + // could make the black out colour user definable... + gl_FragColor = vec4(0, 0, 0, alpha); +} diff --git a/examples/shaders/resources/shaders/glsl120/tiling.fs b/examples/shaders/resources/shaders/glsl120/tiling.fs new file mode 100644 index 000000000..78902cf87 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/tiling.fs @@ -0,0 +1,19 @@ +#version 120 + +// Input vertex attributes (from vertex shader) +varying vec2 fragTexCoord; +varying vec4 fragColor; + +// Input uniform values +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +// NOTE: Add your custom variables here +uniform vec2 tiling; + +void main() +{ + vec2 texCoord = fragTexCoord*tiling; + + gl_FragColor = texture2D(texture0, texCoord)*colDiffuse; +} diff --git a/examples/shaders/resources/shaders/glsl120/vertex_displacement.fs b/examples/shaders/resources/shaders/glsl120/vertex_displacement.fs new file mode 100644 index 000000000..ac485050d --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/vertex_displacement.fs @@ -0,0 +1,15 @@ +#version 120 + +// 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/glsl120/vertex_displacement.vs b/examples/shaders/resources/shaders/glsl120/vertex_displacement.vs new file mode 100644 index 000000000..db3c496d5 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/vertex_displacement.vs @@ -0,0 +1,43 @@ +#version 120 + +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/glsl120/wave.fs b/examples/shaders/resources/shaders/glsl120/wave.fs new file mode 100644 index 000000000..dd6bb2e22 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/wave.fs @@ -0,0 +1,32 @@ +#version 120 + +// Input vertex attributes (from vertex shader) +varying vec2 fragTexCoord; +varying vec4 fragColor; + +// Input uniform values +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +uniform float seconds; +uniform vec2 size; +uniform float freqX; +uniform float freqY; +uniform float ampX; +uniform float ampY; +uniform float speedX; +uniform float speedY; + +void main() { + float pixelWidth = 1.0/size.x; + float pixelHeight = 1.0/size.y; + float aspect = pixelHeight/pixelWidth; + float boxLeft = 0.0; + float boxTop = 0.0; + + vec2 p = fragTexCoord; + p.x += cos((fragTexCoord.y - boxTop)*freqX/(pixelWidth*750.0) + (seconds*speedX))*ampX*pixelWidth; + p.y += sin((fragTexCoord.x - boxLeft)*freqY*aspect/(pixelHeight*750.0) + (seconds*speedY))*ampY*pixelHeight; + + gl_FragColor = texture2D(texture0, p)*colDiffuse*fragColor; +} diff --git a/examples/shaders/resources/shaders/glsl120/write_depth.fs b/examples/shaders/resources/shaders/glsl120/write_depth.fs new file mode 100644 index 000000000..0d0e19da7 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/write_depth.fs @@ -0,0 +1,17 @@ +#version 100 + +#extension GL_EXT_frag_depth : enable + +varying vec2 fragTexCoord; +varying vec4 fragColor; + +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +void main() +{ + vec4 texelColor = texture2D(texture0, fragTexCoord); + + gl_FragColor = texelColor*colDiffuse*fragColor; + gl_FragDepthEXT = 1.0 - gl_FragCoord.z; +} \ No newline at end of file From 9b598f6bcf9b62935af42348d752cd8c0d423492 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 11 Aug 2025 20:22:31 +0200 Subject: [PATCH 242/242] REVIEWED: Shaders formating to follow raylib code conventions --- .../shaders/glsl100/point_particle.fs | 6 +- .../shaders/glsl100/point_particle.vs | 6 +- .../shaders/glsl330/point_particle.fs | 6 +- .../shaders/glsl330/point_particle.vs | 6 +- .../resources/shaders/glsl100/bloom.fs | 6 +- .../shaders/glsl100/cross_stitching.fs | 8 +- .../shaders/glsl100/cubes_panning.fs | 2 +- .../shaders/glsl100/deferred_shading.fs | 8 +- .../resources/shaders/glsl100/eratosthenes.fs | 12 +- .../resources/shaders/glsl100/fisheye.fs | 14 +- .../shaders/glsl100/hybrid_raster.fs | 6 +- .../shaders/glsl100/hybrid_raymarch.fs | 178 ++++++------ .../resources/shaders/glsl100/julia_set.fs | 44 ++- .../resources/shaders/glsl100/lighting.fs | 2 +- .../resources/shaders/glsl100/lightmap.fs | 2 +- .../shaders/resources/shaders/glsl100/mask.fs | 4 +- .../resources/shaders/glsl100/normalmap.fs | 14 +- .../resources/shaders/glsl100/normalmap.vs | 28 +- .../shaders/resources/shaders/glsl100/pbr.vs | 2 +- .../resources/shaders/glsl100/raymarching.fs | 258 +++++++++--------- .../shaders/glsl100/rounded_rectangle.fs | 4 +- .../resources/shaders/glsl100/scanlines.fs | 2 +- .../resources/shaders/glsl100/sobel.fs | 4 +- .../shaders/glsl100/vertex_displacement.fs | 1 - .../shaders/glsl100/vertex_displacement.vs | 14 +- .../shaders/resources/shaders/glsl100/wave.fs | 12 +- .../resources/shaders/glsl100/write_depth.fs | 5 +- .../shaders/glsl120/cross_stitching.fs | 8 +- .../resources/shaders/glsl120/fisheye.fs | 14 +- .../resources/shaders/glsl120/lighting.fs | 2 +- .../resources/shaders/glsl120/lightmap.fs | 2 +- .../resources/shaders/glsl120/normalmap.fs | 14 +- .../resources/shaders/glsl120/normalmap.vs | 28 +- .../shaders/glsl120/palette_switch.fs | 10 +- .../shaders/resources/shaders/glsl120/pbr.vs | 2 +- .../resources/shaders/glsl120/raymarching.fs | 258 +++++++++--------- .../shaders/glsl120/rounded_rectangle.fs | 4 +- .../resources/shaders/glsl120/scanlines.fs | 2 +- .../resources/shaders/glsl120/shadowmap.fs | 6 +- .../resources/shaders/glsl120/sobel.fs | 4 +- .../shaders/glsl330/cross_stitching.fs | 8 +- .../shaders/glsl330/cubes_panning.fs | 2 +- .../shaders/glsl330/deferred_shading.fs | 16 +- .../resources/shaders/glsl330/eratosthenes.fs | 12 +- .../resources/shaders/glsl330/fisheye.fs | 14 +- .../resources/shaders/glsl330/gbuffer.vs | 6 +- .../shaders/glsl330/hybrid_raster.fs | 2 +- .../shaders/glsl330/hybrid_raymarch.fs | 158 +++++------ .../resources/shaders/glsl330/julia_set.fs | 40 +-- .../resources/shaders/glsl330/lighting.fs | 2 +- .../resources/shaders/glsl330/lightmap.fs | 2 +- .../resources/shaders/glsl330/normalmap.fs | 14 +- .../resources/shaders/glsl330/normalmap.vs | 14 +- .../shaders/resources/shaders/glsl330/pbr.vs | 2 +- .../resources/shaders/glsl330/raymarching.fs | 258 +++++++++--------- .../resources/shaders/glsl330/scanlines.fs | 2 +- .../resources/shaders/glsl330/shadowmap.fs | 8 +- .../resources/shaders/glsl330/sobel.fs | 4 +- .../resources/shaders/glsl330/spotlight.fs | 2 +- .../shaders/glsl330/vertex_displacement.vs | 8 +- .../shaders/resources/shaders/glsl330/wave.fs | 10 +- .../shaders/glsl330/alpha_discard.fs | 2 +- 62 files changed, 797 insertions(+), 797 deletions(-) diff --git a/examples/others/resources/shaders/glsl100/point_particle.fs b/examples/others/resources/shaders/glsl100/point_particle.fs index b1c5103e6..d74d457a7 100644 --- a/examples/others/resources/shaders/glsl100/point_particle.fs +++ b/examples/others/resources/shaders/glsl100/point_particle.fs @@ -10,7 +10,7 @@ uniform vec4 color; void main() { // Each point is drawn as a screen space square of gl_PointSize size. gl_PointCoord contains where we are inside of - // it. (0, 0) is the top left, (1, 1) the bottom right corner. - // Draw each point as a colored circle with alpha 1.0 in the center and 0.0 at the outer edges. - gl_FragColor = vec4(color.rgb, color.a * (1.0 - length(gl_PointCoord.xy - vec2(0.5))*2.0)); + // it. (0, 0) is the top left, (1, 1) the bottom right corner + // Draw each point as a colored circle with alpha 1.0 in the center and 0.0 at the outer edges + gl_FragColor = vec4(color.rgb, color.a*(1.0 - length(gl_PointCoord.xy - vec2(0.5))*2.0)); } \ No newline at end of file diff --git a/examples/others/resources/shaders/glsl100/point_particle.vs b/examples/others/resources/shaders/glsl100/point_particle.vs index e1c4381ad..281ef6599 100644 --- a/examples/others/resources/shaders/glsl100/point_particle.vs +++ b/examples/others/resources/shaders/glsl100/point_particle.vs @@ -16,9 +16,9 @@ void main() float period = vertexPosition.z; // Calculate final vertex position (jiggle it around a bit horizontally) - pos += vec2(100.0, 0.0) * sin(period * currentTime); - gl_Position = mvp * vec4(pos.x, pos.y, 0.0, 1.0); + pos += vec2(100.0, 0.0)*sin(period*currentTime); + gl_Position = mvp*vec4(pos.x, pos.y, 0.0, 1.0); // Calculate the screen space size of this particle (also vary it over time) - gl_PointSize = 10.0 - 5.0 * abs(sin(period * currentTime)); + gl_PointSize = 10.0 - 5.0*abs(sin(period*currentTime)); } \ No newline at end of file diff --git a/examples/others/resources/shaders/glsl330/point_particle.fs b/examples/others/resources/shaders/glsl330/point_particle.fs index c8a834f9e..456345e4b 100644 --- a/examples/others/resources/shaders/glsl330/point_particle.fs +++ b/examples/others/resources/shaders/glsl330/point_particle.fs @@ -11,7 +11,7 @@ out vec4 finalColor; void main() { // Each point is drawn as a screen space square of gl_PointSize size. gl_PointCoord contains where we are inside of - // it. (0, 0) is the top left, (1, 1) the bottom right corner. - // Draw each point as a colored circle with alpha 1.0 in the center and 0.0 at the outer edges. - finalColor = vec4(color.rgb, color.a * (1 - length(gl_PointCoord.xy - vec2(0.5))*2)); + // it. (0, 0) is the top left, (1, 1) the bottom right corner + // Draw each point as a colored circle with alpha 1.0 in the center and 0.0 at the outer edges + finalColor = vec4(color.rgb, color.a*(1 - length(gl_PointCoord.xy - vec2(0.5))*2)); } \ No newline at end of file diff --git a/examples/others/resources/shaders/glsl330/point_particle.vs b/examples/others/resources/shaders/glsl330/point_particle.vs index 1d835a9bc..90ea53836 100644 --- a/examples/others/resources/shaders/glsl330/point_particle.vs +++ b/examples/others/resources/shaders/glsl330/point_particle.vs @@ -16,9 +16,9 @@ void main() float period = vertexPosition.z; // Calculate final vertex position (jiggle it around a bit horizontally) - pos += vec2(100, 0) * sin(period * currentTime); - gl_Position = mvp * vec4(pos, 0.0, 1.0); + pos += vec2(100, 0)*sin(period*currentTime); + gl_Position = mvp*vec4(pos, 0.0, 1.0); // Calculate the screen space size of this particle (also vary it over time) - gl_PointSize = 10 - 5 * abs(sin(period * currentTime)); + gl_PointSize = 10 - 5*abs(sin(period*currentTime)); } \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl100/bloom.fs b/examples/shaders/resources/shaders/glsl100/bloom.fs index 65254a860..e0c2ecf3c 100644 --- a/examples/shaders/resources/shaders/glsl100/bloom.fs +++ b/examples/shaders/resources/shaders/glsl100/bloom.fs @@ -12,9 +12,9 @@ uniform vec4 colDiffuse; // NOTE: Add your custom variables here -const vec2 size = vec2(800, 450); // render size -const float samples = 5.0; // pixels per axis; higher = bigger glow, worse performance -const float quality = 2.5; // lower = smaller glow, better quality +const vec2 size = vec2(800, 450); // Framebuffer size +const float samples = 5.0; // Pixels per axis; higher = bigger glow, worse performance +const float quality = 2.5; // Defines size factor: Lower = smaller glow, better quality void main() { diff --git a/examples/shaders/resources/shaders/glsl100/cross_stitching.fs b/examples/shaders/resources/shaders/glsl100/cross_stitching.fs index f43f09c84..a098794d4 100644 --- a/examples/shaders/resources/shaders/glsl100/cross_stitching.fs +++ b/examples/shaders/resources/shaders/glsl100/cross_stitching.fs @@ -23,8 +23,8 @@ vec4 PostFX(sampler2D tex, vec2 uv) { vec4 c = vec4(0.0); float size = stitchingSize; - vec2 cPos = uv * vec2(renderWidth, renderHeight); - vec2 tlPos = floor(cPos / vec2(size, size)); + vec2 cPos = uv*vec2(renderWidth, renderHeight); + vec2 tlPos = floor(cPos/vec2(size, size)); tlPos *= size; int remX = int(mod(cPos.x, size)); @@ -38,11 +38,11 @@ vec4 PostFX(sampler2D tex, vec2 uv) if ((remX == remY) || (((int(cPos.x) - int(blPos.x)) == (int(blPos.y) - int(cPos.y))))) { if (invert == 1) c = vec4(0.2, 0.15, 0.05, 1.0); - else c = texture2D(tex, tlPos * vec2(1.0/renderWidth, 1.0/renderHeight)) * 1.4; + else c = texture2D(tex, tlPos*vec2(1.0/renderWidth, 1.0/renderHeight))*1.4; } else { - if (invert == 1) c = texture2D(tex, tlPos * vec2(1.0/renderWidth, 1.0/renderHeight)) * 1.4; + if (invert == 1) c = texture2D(tex, tlPos*vec2(1.0/renderWidth, 1.0/renderHeight))*1.4; else c = vec4(0.0, 0.0, 0.0, 1.0); } diff --git a/examples/shaders/resources/shaders/glsl100/cubes_panning.fs b/examples/shaders/resources/shaders/glsl100/cubes_panning.fs index 9e5eab0fe..ea05cadff 100644 --- a/examples/shaders/resources/shaders/glsl100/cubes_panning.fs +++ b/examples/shaders/resources/shaders/glsl100/cubes_panning.fs @@ -16,7 +16,7 @@ float angle = 0.0; vec2 VectorRotateTime(vec2 v, float speed) { float time = uTime*speed; - float localTime = fract(time); // The time domain this works on is 1 sec. + float localTime = fract(time); // The time domain this works on is 1 sec if ((localTime >= 0.0) && (localTime < 0.25)) angle = 0.0; else if ((localTime >= 0.25) && (localTime < 0.50)) angle = PI/4.0*sin(2.0*PI*localTime - PI/2.0); diff --git a/examples/shaders/resources/shaders/glsl100/deferred_shading.fs b/examples/shaders/resources/shaders/glsl100/deferred_shading.fs index d782792b9..63e9c5bea 100644 --- a/examples/shaders/resources/shaders/glsl100/deferred_shading.fs +++ b/examples/shaders/resources/shaders/glsl100/deferred_shading.fs @@ -13,9 +13,9 @@ uniform sampler2D gAlbedoSpec; struct Light { int enabled; - int type; // Unused in this demo. + int type; // Unused in this demo vec3 position; - vec3 target; // Unused in this demo. + vec3 target; // Unused in this demo vec4 color; }; @@ -38,7 +38,7 @@ void main() for (int i = 0; i < NR_LIGHTS; ++i) { - if(lights[i].enabled == 0) continue; + if (lights[i].enabled == 0) continue; vec3 lightDirection = lights[i].position - fragPosition; vec3 diffuse = max(dot(normal, lightDirection), 0.0)*albedo*lights[i].color.xyz; @@ -48,7 +48,7 @@ void main() // Attenuation float distance = length(lights[i].position - fragPosition); - float attenuation = 1.0/(1.0 + LINEAR * distance + QUADRATIC*distance*distance); + float attenuation = 1.0/(1.0 + LINEAR*distance + QUADRATIC*distance*distance); diffuse *= attenuation; specular *= attenuation; ambient += diffuse + specular; diff --git a/examples/shaders/resources/shaders/glsl100/eratosthenes.fs b/examples/shaders/resources/shaders/glsl100/eratosthenes.fs index 0d5fcc5a4..739d7b991 100644 --- a/examples/shaders/resources/shaders/glsl100/eratosthenes.fs +++ b/examples/shaders/resources/shaders/glsl100/eratosthenes.fs @@ -7,12 +7,12 @@ precision mediump float; The Sieve of Eratosthenes -- a simple shader by ProfJski An early prime number sieve: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes - The screen is divided into a square grid of boxes, each representing an integer value. - Each integer is tested to see if it is a prime number. Primes are colored white. - Non-primes are colored with a color that indicates the smallest factor which evenly divdes our integer. + The screen is divided into a square grid of boxes, each representing an integer value + Each integer is tested to see if it is a prime number. Primes are colored white + Non-primes are colored with a color that indicates the smallest factor which evenly divdes our integer - You can change the scale variable to make a larger or smaller grid. - Total number of integers displayed = scale squared, so scale = 100 tests the first 10,000 integers. + You can change the scale variable to make a larger or smaller grid + Total number of integers displayed = scale squared, so scale = 100 tests the first 10,000 integers WARNING: If you make scale too large, your GPU may bog down! @@ -38,7 +38,7 @@ vec4 Colorizer(float counter, float maxSize) void main() { vec4 color = vec4(1.0); - float scale = 1000.0; // Makes 100x100 square grid. Change this variable to make a smaller or larger grid. + float scale = 1000.0; // Makes 100x100 square grid. Change this variable to make a smaller or larger grid float value = scale*floor(fragTexCoord.y*scale) + floor(fragTexCoord.x*scale); // Group pixels into boxes representing integer values int valuei = int(value); diff --git a/examples/shaders/resources/shaders/glsl100/fisheye.fs b/examples/shaders/resources/shaders/glsl100/fisheye.fs index 161c417c1..9c136a978 100644 --- a/examples/shaders/resources/shaders/glsl100/fisheye.fs +++ b/examples/shaders/resources/shaders/glsl100/fisheye.fs @@ -17,22 +17,22 @@ const float PI = 3.1415926535; void main() { float aperture = 178.0; - float apertureHalf = 0.5 * aperture * (PI / 180.0); + float apertureHalf = 0.5*aperture*(PI/180.0); float maxFactor = sin(apertureHalf); vec2 uv = vec2(0.0); - vec2 xy = 2.0 * fragTexCoord.xy - 1.0; + vec2 xy = 2.0*fragTexCoord.xy - 1.0; float d = length(xy); if (d < (2.0 - maxFactor)) { - d = length(xy * maxFactor); - float z = sqrt(1.0 - d * d); - float r = atan(d, z) / PI; + d = length(xy*maxFactor); + float z = sqrt(1.0 - d*d); + float r = atan(d, z)/PI; float phi = atan(xy.y, xy.x); - uv.x = r * cos(phi) + 0.5; - uv.y = r * sin(phi) + 0.5; + uv.x = r*cos(phi) + 0.5; + uv.y = r*sin(phi) + 0.5; } else { diff --git a/examples/shaders/resources/shaders/glsl100/hybrid_raster.fs b/examples/shaders/resources/shaders/glsl100/hybrid_raster.fs index 28188a454..35bc75d30 100644 --- a/examples/shaders/resources/shaders/glsl100/hybrid_raster.fs +++ b/examples/shaders/resources/shaders/glsl100/hybrid_raster.fs @@ -1,4 +1,5 @@ -#version 100 +#version 100 + #extension GL_EXT_frag_depth : enable // Extension required for writing depth precision mediump float; // Precision required for OpenGL ES2 (WebGL) @@ -11,6 +12,7 @@ uniform vec4 colDiffuse; void main() { vec4 texelColor = texture2D(texture0, fragTexCoord); + gl_FragColor = texelColor*colDiffuse*fragColor; - gl_FragDepthEXT = gl_FragCoord.z; + gl_FragDepthEXT = gl_FragCoord.z; } \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl100/hybrid_raymarch.fs b/examples/shaders/resources/shaders/glsl100/hybrid_raymarch.fs index 6241186de..44233b3bb 100644 --- a/examples/shaders/resources/shaders/glsl100/hybrid_raymarch.fs +++ b/examples/shaders/resources/shaders/glsl100/hybrid_raymarch.fs @@ -1,9 +1,9 @@ -#version 100 +#version 100 + #extension GL_EXT_frag_depth : enable //Extension required for writing depth #extension GL_OES_standard_derivatives : enable //Extension used for fwidth() precision mediump float; // Precision required for OpenGL ES2 (WebGL) - // Input vertex attributes (from vertex shader) varying vec2 fragTexCoord; varying vec4 fragColor; @@ -19,21 +19,22 @@ uniform vec2 screenCenter; #define ZERO 0 -// https://learnopengl.com/Advanced-OpenGL/Depth-testing -float CalcDepth(in vec3 rd, in float Idist){ +// SRC: https://learnopengl.com/Advanced-OpenGL/Depth-testing +float CalcDepth(in vec3 rd, in float Idist) +{ float local_z = dot(normalize(camDir),rd)*Idist; return (1.0/(local_z) - 1.0/0.01)/(1.0/1000.0 -1.0/0.01); } -// https://iquilezles.org/articles/distfunctions/ -float sdHorseshoe( in vec3 p, in vec2 c, in float r, in float le, vec2 w ) +// SRC: https://iquilezles.org/articles/distfunctions/ +float sdHorseshoe(in vec3 p, in vec2 c, in float r, in float le, vec2 w) { p.x = abs(p.x); float l = length(p.xy); p.xy = mat2(-c.x, c.y, c.y, c.x)*p.xy; p.xy = vec2((p.y>0.0 || p.x>0.0)?p.x:l*sign(-c.x), - (p.x>0.0)?p.y:l ); + (p.x>0.0)?p.y:l); p.xy = vec2(p.x,abs(p.y-r))-vec2(le,0.0); vec2 q = vec2(length(max(p.xy,0.0)) + min(0.0,max(p.x,p.y)),p.z); @@ -44,67 +45,70 @@ float sdHorseshoe( in vec3 p, in vec2 c, in float r, in float le, vec2 w ) // r = sphere's radius // h = cutting's plane's position // t = thickness -float sdSixWayCutHollowSphere( vec3 p, float r, float h, float t ) +float sdSixWayCutHollowSphere(vec3 p, float r, float h, float t) { // Six way symetry Transformation vec3 ap = abs(p); - if(ap.x < max(ap.y, ap.z)){ - if(ap.y < ap.z) ap.xz = ap.zx; + if (ap.x < max(ap.y, ap.z)){ + if (ap.y < ap.z) ap.xz = ap.zx; else ap.xy = ap.yx; } - vec2 q = vec2( length(ap.yz), ap.x ); + vec2 q = vec2(length(ap.yz), ap.x); float w = sqrt(r*r-h*h); - return ((h*q.x0.0 ) + if (tp1>0.0) { - tmax = min( tmax, tp1 ); - res = vec2( tp1, 1.0 ); + tmax = min(tmax, tp1); + res = vec2(tp1, 1.0); } float t = tmin; - for( int i=0; i<70 ; i++ ) + for (int i=0; i<70 ; i++) { - if(t>tmax) break; - vec2 h = map( ro+rd*t ); - if( abs(h.x)<(0.0001*t) ) + if (t>tmax) break; + vec2 h = map(ro+rd*t); + if (abs(h.x) < (0.0001*t)) { res = vec2(t,h.y); break; @@ -117,54 +121,54 @@ vec2 raycast( in vec3 ro, in vec3 rd ){ // https://iquilezles.org/articles/rmshadows -float calcSoftshadow( in vec3 ro, in vec3 rd, in float mint, in float tmax ) +float calcSoftshadow(in vec3 ro, in vec3 rd, in float mint, in float tmax) { // bounding volume - float tp = (0.8-ro.y)/rd.y; if( tp>0.0 ) tmax = min( tmax, tp ); + float tp = (0.8-ro.y)/rd.y; if (tp>0.0) tmax = min(tmax, tp); float res = 1.0; float t = mint; - for( int i=ZERO; i<24; i++ ) + for (int i=ZERO; i<24; i++) { - float h = map( ro + rd*t ).x; + float h = map(ro + rd*t).x; float s = clamp(8.0*h/t,0.0,1.0); - res = min( res, s ); - t += clamp( h, 0.01, 0.2 ); - if( res<0.004 || t>tmax ) break; + res = min(res, s); + t += clamp(h, 0.01, 0.2); + if (res<0.004 || t>tmax) break; } - res = clamp( res, 0.0, 1.0 ); + res = clamp(res, 0.0, 1.0); return res*res*(3.0-2.0*res); } // https://iquilezles.org/articles/normalsSDF -vec3 calcNormal( in vec3 pos ) +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 ); + 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); } // https://iquilezles.org/articles/nvscene2008/rwwtt.pdf -float calcAO( in vec3 pos, in vec3 nor ) +float calcAO(in vec3 pos, in vec3 nor) { - float occ = 0.0; + float occ = 0.0; float sca = 1.0; - for( int i=ZERO; i<5; i++ ) + for (int i=ZERO; i<5; i++) { float h = 0.01 + 0.12*float(i)/4.0; - float d = map( pos + h*nor ).x; + float d = map(pos + h*nor).x; occ += (h-d)*sca; sca *= 0.95; - if( occ>0.35 ) break; + if (occ>0.35) break; } - return clamp( 1.0 - 3.0*occ, 0.0, 1.0 ) * (0.5+0.5*nor.y); + return clamp(1.0 - 3.0*occ, 0.0, 1.0)*(0.5+0.5*nor.y); } // https://iquilezles.org/articles/checkerfiltering -float checkersGradBox( in vec2 p ) +float checkersGradBox(in vec2 p) { // filter kernel vec2 w = fwidth(p) + 0.001; @@ -175,7 +179,7 @@ float checkersGradBox( in vec2 p ) } // https://www.shadertoy.com/view/tdS3DG -vec4 render( in vec3 ro, in vec3 rd) +vec4 render(in vec3 ro, in vec3 rd) { // background vec3 col = vec3(0.7, 0.7, 0.9) - max(rd.y,0.0)*0.3; @@ -183,37 +187,37 @@ vec4 render( in vec3 ro, in vec3 rd) // raycast scene vec2 res = raycast(ro,rd); float t = res.x; - float m = res.y; - if( m>-0.5 ) + float m = res.y; + if (m>-0.5) { vec3 pos = ro + t*rd; - vec3 nor = (m<1.5) ? vec3(0.0,1.0,0.0) : calcNormal( pos ); - vec3 ref = reflect( rd, nor ); + vec3 nor = (m<1.5) ? vec3(0.0,1.0,0.0) : calcNormal(pos); + vec3 ref = reflect(rd, nor); // material - col = 0.2 + 0.2*sin( m*2.0 + vec3(0.0,1.0,2.0) ); + col = 0.2 + 0.2*sin(m*2.0 + vec3(0.0,1.0,2.0)); float ks = 1.0; - if( m<1.5 ) + if (m<1.5) { - float f = checkersGradBox( 3.0*pos.xz); + float f = checkersGradBox(3.0*pos.xz); col = 0.15 + f*vec3(0.05); ks = 0.4; } // lighting - float occ = calcAO( pos, nor ); + float occ = calcAO(pos, nor); - vec3 lin = vec3(0.0); + vec3 lin = vec3(0.0); // sun { - vec3 lig = normalize( vec3(-0.5, 0.4, -0.6) ); - vec3 hal = normalize( lig-rd ); - float dif = clamp( dot( nor, lig ), 0.0, 1.0 ); - //if( dif>0.0001 ) - dif *= calcSoftshadow( pos, lig, 0.02, 2.5 ); - float spe = pow( clamp( dot( nor, hal ), 0.0, 1.0 ),16.0); + vec3 lig = normalize(vec3(-0.5, 0.4, -0.6)); + vec3 hal = normalize(lig-rd); + float dif = clamp(dot(nor, lig), 0.0, 1.0); + //if (dif>0.0001) + dif *= calcSoftshadow(pos, lig, 0.02, 2.5); + float spe = pow(clamp(dot(nor, hal), 0.0, 1.0),16.0); spe *= dif; spe *= 0.04+0.96*pow(clamp(1.0-dot(hal,lig),0.0,1.0),5.0); //spe *= 0.04+0.96*pow(clamp(1.0-sqrt(0.5*(1.0-dot(rd,lig))),0.0,1.0),5.0); @@ -222,35 +226,35 @@ vec4 render( in vec3 ro, in vec3 rd) } // sky { - float dif = sqrt(clamp( 0.5+0.5*nor.y, 0.0, 1.0 )); + float dif = sqrt(clamp(0.5+0.5*nor.y, 0.0, 1.0)); dif *= occ; - float spe = smoothstep( -0.2, 0.2, ref.y ); + float spe = smoothstep(-0.2, 0.2, ref.y); spe *= dif; - spe *= 0.04+0.96*pow(clamp(1.0+dot(nor,rd),0.0,1.0), 5.0 ); - //if( spe>0.001 ) - spe *= calcSoftshadow( pos, ref, 0.02, 2.5 ); + spe *= 0.04+0.96*pow(clamp(1.0+dot(nor,rd),0.0,1.0), 5.0); + //if (spe>0.001) + spe *= calcSoftshadow(pos, ref, 0.02, 2.5); lin += col*0.60*dif*vec3(0.40,0.60,1.15); lin += 2.00*spe*vec3(0.40,0.60,1.30)*ks; } // back { - float dif = clamp( dot( nor, normalize(vec3(0.5,0.0,0.6))), 0.0, 1.0 )*clamp( 1.0-pos.y,0.0,1.0); + float dif = clamp(dot(nor, normalize(vec3(0.5,0.0,0.6))), 0.0, 1.0)*clamp(1.0-pos.y,0.0,1.0); dif *= occ; - lin += col*0.55*dif*vec3(0.25,0.25,0.25); + lin += col*0.55*dif*vec3(0.25,0.25,0.25); } // sss { float dif = pow(clamp(1.0+dot(nor,rd),0.0,1.0),2.0); dif *= occ; - lin += col*0.25*dif*vec3(1.00,1.00,1.00); + lin += col*0.25*dif*vec3(1.00,1.00,1.00); } - col = lin; + col = lin; - col = mix( col, vec3(0.7,0.7,0.9), 1.0-exp( -0.0001*t*t*t ) ); + col = mix(col, vec3(0.7,0.7,0.9), 1.0-exp(-0.0001*t*t*t)); } - return vec4(vec3( clamp(col,0.0,1.0) ),t); + return vec4(vec3(clamp(col,0.0,1.0)),t); } vec3 CalcRayDir(vec2 nCoord){ @@ -261,11 +265,11 @@ vec3 CalcRayDir(vec2 nCoord){ mat3 setCamera() { - vec3 cw = normalize(camDir); - vec3 cp = vec3(0.0, 1.0 ,0.0); - vec3 cu = normalize( cross(cw,cp) ); - vec3 cv = ( cross(cu,cw) ); - return mat3( cu, cv, cw ); + vec3 cw = normalize(camDir); + vec3 cp = vec3(0.0, 1.0 ,0.0); + vec3 cu = normalize(cross(cw,cp)); + vec3 cv = (cross(cu,cw)); + return mat3(cu, cv, cw); } void main() @@ -275,14 +279,14 @@ void main() // focal length float fl = length(camDir); - vec3 rd = ca * normalize( vec3(nCoord,fl) ); + vec3 rd = ca*normalize(vec3(nCoord,fl)); vec3 color = vec3(nCoord/2.0 + 0.5, 0.0); float depth = gl_FragCoord.z; { - vec4 res = render( camPos - vec3(0.0, 0.0, 0.0) , rd ); + vec4 res = render(camPos - vec3(0.0, 0.0, 0.0) , rd); color = res.xyz; depth = CalcDepth(rd,res.w); } gl_FragColor = vec4(color , 1.0); - gl_FragDepthEXT = depth; + gl_FragDepthEXT = depth; } \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl100/julia_set.fs b/examples/shaders/resources/shaders/glsl100/julia_set.fs index 9ee8c6f4d..d5749c0f0 100644 --- a/examples/shaders/resources/shaders/glsl100/julia_set.fs +++ b/examples/shaders/resources/shaders/glsl100/julia_set.fs @@ -1,19 +1,17 @@ -#version 100 - -precision mediump float; +#version 120 // Input vertex attributes (from vertex shader) varying vec2 fragTexCoord; varying vec4 fragColor; uniform vec2 c; // c.x = real, c.y = imaginary component. Equation done is z^2 + c -uniform vec2 offset; // Offset of the scale. -uniform float zoom; // Zoom of the scale. +uniform vec2 offset; // Offset of the scale +uniform float zoom; // Zoom of the scale // NOTE: Maximum number of shader for-loop iterations depend on GPU, // for example, on RasperryPi for this examply only supports up to 60 -const int maxIterations = 48; // Max iterations to do. -const float colorCycles = 1.0; // Number of times the color palette repeats. +const int maxIterations = 48; // Max iterations to do +const float colorCycles = 1.0; // Number of times the color palette repeats // Square a complex number vec2 ComplexSquare(vec2 z) @@ -32,22 +30,22 @@ vec3 Hsv2rgb(vec3 c) void main() { /********************************************************************************************** - Julia sets use a function z^2 + c, where c is a constant. - This function is iterated until the nature of the point is determined. + Julia sets use a function z^2 + c, where c is a constant + This function is iterated until the nature of the point is determined If the magnitude of the number becomes greater than 2, then from that point onward - the number will get bigger and bigger, and will never get smaller (tends towards infinity). - 2^2 = 4, 4^2 = 8 and so on. - So at 2 we stop iterating. + the number will get bigger and bigger, and will never get smaller (tends towards infinity) + 2^2 = 4, 4^2 = 8 and so on + So at 2 we stop iterating - If the number is below 2, we keep iterating. + If the number is below 2, we keep iterating But when do we stop iterating if the number is always below 2 (it converges)? - That is what maxIterations is for. - Then we can divide the iterations by the maxIterations value to get a normalized value that we can - then map to a color. + That is what maxIterations is for + Then we can divide the iterations by the maxIterations value to get a normalized value + that we can then map to a color - We use dot product (z.x * z.x + z.y * z.y) to determine the magnitude (length) squared. - And once the magnitude squared is > 4, then magnitude > 2 is also true (saves computational power). + We use dot product (z.x*z.x + z.y*z.y) to determine the magnitude (length) squared + And once the magnitude squared is > 4, then magnitude > 2 is also true (saves computational power) *************************************************************************************************/ // The pixel coordinates are scaled so they are on the mandelbrot scale @@ -65,18 +63,18 @@ void main() iter = iterations; } - // Another few iterations decreases errors in the smoothing calculation. - // See http://linas.org/art-gallery/escape/escape.html for more information. + // Another few iterations decreases errors in the smoothing calculation + // See http://linas.org/art-gallery/escape/escape.html for more information z = ComplexSquare(z) + c; z = ComplexSquare(z) + c; - // This last part smooths the color (again see link above). + // This last part smooths the color (again see link above) float smoothVal = float(iter) + 1.0 - (log(log(length(z)))/log(2.0)); - // Normalize the value so it is between 0 and 1. + // Normalize the value so it is between 0 and 1 float norm = smoothVal/float(maxIterations); - // If in set, color black. 0.999 allows for some float accuracy error. + // If in set, color black. 0.999 allows for some float accuracy error if (norm > 0.999) gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); else gl_FragColor = vec4(Hsv2rgb(vec3(norm*colorCycles, 1.0, 1.0)), 1.0); } diff --git a/examples/shaders/resources/shaders/glsl100/lighting.fs b/examples/shaders/resources/shaders/glsl100/lighting.fs index 596da0f6f..e36eb7ac0 100644 --- a/examples/shaders/resources/shaders/glsl100/lighting.fs +++ b/examples/shaders/resources/shaders/glsl100/lighting.fs @@ -40,7 +40,7 @@ void main() vec3 viewD = normalize(viewPos - fragPosition); vec3 specular = vec3(0.0); - vec4 tint = colDiffuse * fragColor; + vec4 tint = colDiffuse*fragColor; // NOTE: Implement here your fragment shader code diff --git a/examples/shaders/resources/shaders/glsl100/lightmap.fs b/examples/shaders/resources/shaders/glsl100/lightmap.fs index 9f0bcd27b..70ecad61b 100644 --- a/examples/shaders/resources/shaders/glsl100/lightmap.fs +++ b/examples/shaders/resources/shaders/glsl100/lightmap.fs @@ -18,5 +18,5 @@ void main() vec4 texelColor = texture2D(texture0, fragTexCoord); vec4 texelColor2 = texture2D(texture1, fragTexCoord2); - gl_FragColor = texelColor * texelColor2; + gl_FragColor = texelColor*texelColor2; } diff --git a/examples/shaders/resources/shaders/glsl100/mask.fs b/examples/shaders/resources/shaders/glsl100/mask.fs index 2136ec1d3..8203a7008 100644 --- a/examples/shaders/resources/shaders/glsl100/mask.fs +++ b/examples/shaders/resources/shaders/glsl100/mask.fs @@ -1,6 +1,4 @@ -#version 100 - -precision mediump float; +#version 120 // Input vertex attributes (from vertex shader) varying vec2 fragTexCoord; diff --git a/examples/shaders/resources/shaders/glsl100/normalmap.fs b/examples/shaders/resources/shaders/glsl100/normalmap.fs index 02902a61f..197e81ed1 100644 --- a/examples/shaders/resources/shaders/glsl100/normalmap.fs +++ b/examples/shaders/resources/shaders/glsl100/normalmap.fs @@ -34,21 +34,21 @@ void main() normal = texture(normalMap, vec2(fragTexCoord.x, fragTexCoord.y)).rgb; //Transform normal values to the range -1.0 ... 1.0 - normal = normalize(normal * 2.0 - 1.0); + normal = normalize(normal*2.0 - 1.0); //Transform the normal from tangent-space to world-space for lighting calculation - normal = normalize(normal * TBN); + normal = normalize(normal*TBN); } else { normal = normalize(fragNormal); } - vec4 tint = colDiffuse * fragColor; + vec4 tint = colDiffuse*fragColor; vec3 lightColor = vec3(1.0, 1.0, 1.0); float NdotL = max(dot(normal, lightDir), 0.0); - vec3 lightDot = lightColor * NdotL; + vec3 lightDot = lightColor*NdotL; float specCo = 0.0; @@ -56,9 +56,9 @@ void main() specular += specCo; - finalColor = (texelColor * ((tint + vec4(specular, 1.0)) * vec4(lightDot, 1.0))); - finalColor += texelColor * (vec4(1.0, 1.0, 1.0, 1.0) / 40.0) * tint; + finalColor = (texelColor*((tint + vec4(specular, 1.0))*vec4(lightDot, 1.0))); + finalColor += texelColor*(vec4(1.0, 1.0, 1.0, 1.0)/40.0)*tint; // Gamma correction - gl_FragColor = pow(finalColor, vec4(1.0 / 2.2)); + gl_FragColor = pow(finalColor, vec4(1.0/2.2)); } diff --git a/examples/shaders/resources/shaders/glsl100/normalmap.vs b/examples/shaders/resources/shaders/glsl100/normalmap.vs index 072e0a6c2..0426653ef 100644 --- a/examples/shaders/resources/shaders/glsl100/normalmap.vs +++ b/examples/shaders/resources/shaders/glsl100/normalmap.vs @@ -27,15 +27,15 @@ mat3 inverse(mat3 m) float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2]; float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2]; - float b01 = a22 * a11 - a12 * a21; - float b11 = -a22 * a10 + a12 * a20; - float b21 = a21 * a10 - a11 * a20; + float b01 = a22*a11 - a12*a21; + float b11 = -a22*a10 + a12*a20; + float b21 = a21*a10 - a11*a20; - float det = a00 * b01 + a01 * b11 + a02 * b21; + float det = a00*b01 + a01*b11 + a02*b21; - return mat3(b01, (-a22 * a01 + a02 * a21), (a12 * a01 - a02 * a11), - b11, (a22 * a00 - a02 * a20), (-a12 * a00 + a02 * a10), - b21, (-a21 * a00 + a01 * a20), (a11 * a00 - a01 * a10)) / det; + return mat3(b01, (-a22*a01 + a02*a21), (a12*a01 - a02*a11), + b11, (a22*a00 - a02*a20), (-a12*a00 + a02*a10), + b21, (-a21*a00 + a01*a20), (a11*a00 - a01*a10))/det; } // https://github.com/glslify/glsl-transpose @@ -49,21 +49,21 @@ mat3 transpose(mat3 m) void main() { // Compute binormal from vertex normal and tangent. W component is the tangent handedness - vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz) * vertexTangent.w; + vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz)*vertexTangent.w; // Compute fragment normal based on normal transformations mat3 normalMatrix = transpose(inverse(mat3(matModel))); // Compute fragment position based on model transformations - fragPosition = vec3(matModel * vec4(vertexPosition, 1.0)); + fragPosition = vec3(matModel*vec4(vertexPosition, 1.0)); //Create TBN matrix for transforming the normal map values from tangent-space to world-space - fragNormal = normalize(normalMatrix * vertexNormal); + fragNormal = normalize(normalMatrix*vertexNormal); - vec3 fragTangent = normalize(normalMatrix * vertexTangent.xyz); - fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal) * fragNormal); + vec3 fragTangent = normalize(normalMatrix*vertexTangent.xyz); + fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal); - vec3 fragBinormal = normalize(normalMatrix * vertexBinormal); + vec3 fragBinormal = normalize(normalMatrix*vertexBinormal); fragBinormal = cross(fragNormal, fragTangent); TBN = transpose(mat3(fragTangent, fragBinormal, fragNormal)); @@ -72,5 +72,5 @@ void main() fragTexCoord = vertexTexCoord; - gl_Position = mvp * vec4(vertexPosition, 1.0); + gl_Position = mvp*vec4(vertexPosition, 1.0); } diff --git a/examples/shaders/resources/shaders/glsl100/pbr.vs b/examples/shaders/resources/shaders/glsl100/pbr.vs index baf003842..1bd6e0e9f 100644 --- a/examples/shaders/resources/shaders/glsl100/pbr.vs +++ b/examples/shaders/resources/shaders/glsl100/pbr.vs @@ -52,7 +52,7 @@ mat3 transpose(mat3 m) void main() { // Compute binormal from vertex normal and tangent - vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz) * vertexTangent.w; + vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz)*vertexTangent.w; // Compute fragment normal based on normal transformations mat3 normalMatrix = transpose(inverse(mat3(matModel))); diff --git a/examples/shaders/resources/shaders/glsl100/raymarching.fs b/examples/shaders/resources/shaders/glsl100/raymarching.fs index a7339d216..ce9a2faca 100644 --- a/examples/shaders/resources/shaders/glsl100/raymarching.fs +++ b/examples/shaders/resources/shaders/glsl100/raymarching.fs @@ -34,7 +34,7 @@ uniform vec2 resolution; // SOFTWARE. // A list of useful distance function to simple primitives, and an example on how to -// do some interesting boolean operations, repetition and displacement. +// do some interesting boolean operations, repetition and displacement // // More info here: http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm @@ -42,38 +42,38 @@ uniform vec2 resolution; //------------------------------------------------------------------ -float sdPlane( vec3 p ) +float sdPlane(vec3 p) { return p.y; } -float sdSphere( vec3 p, float s ) +float sdSphere(vec3 p, float s) { return length(p)-s; } -float sdBox( vec3 p, vec3 b ) +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 ) +float sdEllipsoid(in vec3 p, in vec3 r) { - return (length( p/r ) - 1.0) * min(min(r.x,r.y),r.z); + return (length(p/r) - 1.0)*min(min(r.x,r.y),r.z); } -float udRoundBox( vec3 p, vec3 b, float r ) +float udRoundBox(vec3 p, vec3 b, float r) { return length(max(abs(p)-b,0.0))-r; } -float sdTorus( vec3 p, vec2 t ) +float sdTorus(vec3 p, vec2 t) { - return length( vec2(length(p.xz)-t.x,p.y) )-t.y; + return length(vec2(length(p.xz)-t.x,p.y))-t.y; } -float sdHexPrism( vec3 p, vec2 h ) +float sdHexPrism(vec3 p, vec2 h) { vec3 q = abs(p); #if 0 @@ -85,24 +85,24 @@ float sdHexPrism( vec3 p, vec2 h ) #endif } -float sdCapsule( vec3 p, vec3 a, vec3 b, float r ) +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 h = clamp(dot(pa,ba)/dot(ba,ba), 0.0, 1.0); + return length(pa - ba*h) - r; } -float sdEquilateralTriangle( in vec2 p ) +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 ); + 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 ) +float sdTriPrism(vec3 p, vec2 h) { vec3 q = abs(p); float d1 = q.z-h.y; @@ -117,95 +117,95 @@ float sdTriPrism( vec3 p, vec2 h ) return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); } -float sdCylinder( vec3 p, vec2 h ) +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 ) +float sdCone(in vec3 p, in vec3 c) { - vec2 q = vec2( length(p.xz), p.y ); + vec2 q = vec2(length(p.xz), p.y); float d1 = -q.y-c.z; - float d2 = max( dot(q,c.xy), q.y); + 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 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 ); + 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 } +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 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 )) )); + 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 ) +float length2(vec2 p) { - return sqrt( p.x*p.x + p.y*p.y ); + return sqrt(p.x*p.x + p.y*p.y); } -float length6( vec2 p ) +float length6(vec2 p) { p = p*p*p; p = p*p; - return pow( p.x + p.y, 1.0/6.0 ); + return pow(p.x + p.y, 1.0/6.0); } -float length8( vec2 p ) +float length8(vec2 p) { p = p*p; p = p*p; p = p*p; - return pow( p.x + p.y, 1.0/8.0 ); + return pow(p.x + p.y, 1.0/8.0); } -float sdTorus82( vec3 p, vec2 t ) +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 ) +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 ) +float sdCylinder6(vec3 p, vec2 h) { - return max( length6(p.xz)-h.x, abs(p.y)-h.y ); + return max(length6(p.xz)-h.x, abs(p.y)-h.y); } //------------------------------------------------------------------ -float opS( float d1, float d2 ) +float opS(float d1, float d2) { return max(-d2,d1); } -vec2 opU( vec2 d1, vec2 d2 ) +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 ); } + float tp1 = (0.0-ro.y)/rd.y; if (tp1>0.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++ ) + for (int i=0; i<64; i++) { float precis = 0.0005*t; - vec2 res = map( ro+rd*t ); - if( res.xtmax ) break; + 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 ); + 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 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++ ) + 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; + 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 ); + return clamp(res, 0.0, 1.0); } -vec3 calcNormal( in vec3 pos ) +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 ); + 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 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 ); + map(pos+eps.yyx).x - map(pos-eps.yyx).x); return normalize(nor); */ } -float calcAO( in vec3 pos, in vec3 nor ) +float calcAO(in vec3 pos, in vec3 nor) { float occ = 0.0; float sca = 1.0; - for( int i=0; i<5; i++ ) + 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; + 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 ); + return clamp(1.0 - 3.0*occ, 0.0, 1.0); } // http://iquilezles.org/www/articles/checkerfiltering/checkerfiltering.htm -float checkersGradBox( in vec2 p ) +float checkersGradBox(in vec2 p) { // filter kernel vec2 w = fwidth(p) + 0.001; @@ -328,43 +328,43 @@ float checkersGradBox( in vec2 p ) return 0.5 - 0.5*i.x*i.y; } -vec3 render( in vec3 ro, in vec3 rd ) +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 ) + if (m>-0.5) { vec3 pos = ro + t*rd; - vec3 nor = calcNormal( pos ); - vec3 ref = reflect( rd, nor ); + 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 ) + 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 ); + 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 ); + 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 ); + 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)* + 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 )); + (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); @@ -375,51 +375,51 @@ vec3 render( in vec3 ro, in vec3 rd ) 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 ) ); + 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) ); + return vec3(clamp(col,0.0,1.0)); } -mat3 setCamera( in vec3 ro, in vec3 ta, float cr ) +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 ); + 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 @@ -427,5 +427,5 @@ void main() tot /= float(AA*AA); #endif - gl_FragColor = vec4( tot, 1.0 ); + gl_FragColor = vec4(tot, 1.0); } diff --git a/examples/shaders/resources/shaders/glsl100/rounded_rectangle.fs b/examples/shaders/resources/shaders/glsl100/rounded_rectangle.fs index 3c8d07978..f714859de 100644 --- a/examples/shaders/resources/shaders/glsl100/rounded_rectangle.fs +++ b/examples/shaders/resources/shaders/glsl100/rounded_rectangle.fs @@ -1,9 +1,9 @@ -// Note: SDF by Iñigo Quilez is licensed under MIT License - #version 100 precision mediump float; +// NOTE: SDF by Iñigo Quilez, licensed under MIT License + // Input vertex attributes (from vertex shader) varying vec2 fragTexCoord; varying vec4 fragColor; diff --git a/examples/shaders/resources/shaders/glsl100/scanlines.fs b/examples/shaders/resources/shaders/glsl100/scanlines.fs index debfc7623..3d55b3fe5 100644 --- a/examples/shaders/resources/shaders/glsl100/scanlines.fs +++ b/examples/shaders/resources/shaders/glsl100/scanlines.fs @@ -35,7 +35,7 @@ void main() fragColor = color; */ // Scanlines method 2 - float globalPos = (fragTexCoord.y + offset) * frequency; + float globalPos = (fragTexCoord.y + offset)*frequency; float wavePos = cos((fract(globalPos) - 0.5)*3.14); vec4 color = texture2D(texture0, fragTexCoord); diff --git a/examples/shaders/resources/shaders/glsl100/sobel.fs b/examples/shaders/resources/shaders/glsl100/sobel.fs index f8f5b95bc..e0eb06129 100644 --- a/examples/shaders/resources/shaders/glsl100/sobel.fs +++ b/examples/shaders/resources/shaders/glsl100/sobel.fs @@ -20,10 +20,10 @@ void main() vec4 horizEdge = vec4(0.0); horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y - y))*1.0; - horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y ))*2.0; + horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y ))*2.0; horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y + y))*1.0; horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y - y))*1.0; - horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y ))*2.0; + horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y ))*2.0; horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y + y))*1.0; vec4 vertEdge = vec4(0.0); diff --git a/examples/shaders/resources/shaders/glsl100/vertex_displacement.fs b/examples/shaders/resources/shaders/glsl100/vertex_displacement.fs index 3328a91e7..c479e02a4 100644 --- a/examples/shaders/resources/shaders/glsl100/vertex_displacement.fs +++ b/examples/shaders/resources/shaders/glsl100/vertex_displacement.fs @@ -6,7 +6,6 @@ precision mediump float; varying vec2 fragTexCoord; varying float height; - void main() { vec4 darkblue = vec4(0.0, 0.13, 0.18, 1.0); diff --git a/examples/shaders/resources/shaders/glsl100/vertex_displacement.vs b/examples/shaders/resources/shaders/glsl100/vertex_displacement.vs index c7b926d4b..b83b6e966 100644 --- a/examples/shaders/resources/shaders/glsl100/vertex_displacement.vs +++ b/examples/shaders/resources/shaders/glsl100/vertex_displacement.vs @@ -23,23 +23,23 @@ 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); + 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; + animatedTexCoord = animatedTexCoord*0.5 + 0.5; // Fetch displacement from the perlin noise map - float displacement = texture2D(perlinNoiseMap, animatedTexCoord).r * 7.0; // Amplified displacement + 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)); + 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 + 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); + gl_Position = mvp*vec4(displacedPosition, 1.0); } diff --git a/examples/shaders/resources/shaders/glsl100/wave.fs b/examples/shaders/resources/shaders/glsl100/wave.fs index cd4ba9de4..df12df9ba 100644 --- a/examples/shaders/resources/shaders/glsl100/wave.fs +++ b/examples/shaders/resources/shaders/glsl100/wave.fs @@ -11,9 +11,7 @@ uniform sampler2D texture0; uniform vec4 colDiffuse; uniform float seconds; - uniform vec2 size; - uniform float freqX; uniform float freqY; uniform float ampX; @@ -22,15 +20,15 @@ uniform float speedX; uniform float speedY; void main() { - float pixelWidth = 1.0 / size.x; - float pixelHeight = 1.0 / size.y; - float aspect = pixelHeight / pixelWidth; + float pixelWidth = 1.0/size.x; + float pixelHeight = 1.0/size.y; + float aspect = pixelHeight/pixelWidth; float boxLeft = 0.0; float boxTop = 0.0; vec2 p = fragTexCoord; - p.x += cos((fragTexCoord.y - boxTop) * freqX / ( pixelWidth * 750.0) + (seconds * speedX)) * ampX * pixelWidth; - p.y += sin((fragTexCoord.x - boxLeft) * freqY * aspect / ( pixelHeight * 750.0) + (seconds * speedY)) * ampY * pixelHeight; + p.x += cos((fragTexCoord.y - boxTop)*freqX/(pixelWidth*750.0) + (seconds*speedX))*ampX*pixelWidth; + p.y += sin((fragTexCoord.x - boxLeft)*freqY*aspect/(pixelHeight*750.0) + (seconds*speedY))*ampY*pixelHeight; gl_FragColor = texture2D(texture0, p)*colDiffuse*fragColor; } diff --git a/examples/shaders/resources/shaders/glsl100/write_depth.fs b/examples/shaders/resources/shaders/glsl100/write_depth.fs index 341c6115f..6200bd23a 100644 --- a/examples/shaders/resources/shaders/glsl100/write_depth.fs +++ b/examples/shaders/resources/shaders/glsl100/write_depth.fs @@ -1,6 +1,7 @@ -#version 100 +#version 100 + #extension GL_EXT_frag_depth : enable -precision mediump float; // Precision required for OpenGL ES2 (WebGL) +precision mediump float; varying vec2 fragTexCoord; varying vec4 fragColor; diff --git a/examples/shaders/resources/shaders/glsl120/cross_stitching.fs b/examples/shaders/resources/shaders/glsl120/cross_stitching.fs index 532724635..e3ad3417e 100644 --- a/examples/shaders/resources/shaders/glsl120/cross_stitching.fs +++ b/examples/shaders/resources/shaders/glsl120/cross_stitching.fs @@ -21,8 +21,8 @@ vec4 PostFX(sampler2D tex, vec2 uv) { vec4 c = vec4(0.0); float size = stitchingSize; - vec2 cPos = uv * vec2(renderWidth, renderHeight); - vec2 tlPos = floor(cPos / vec2(size, size)); + vec2 cPos = uv*vec2(renderWidth, renderHeight); + vec2 tlPos = floor(cPos/vec2(size, size)); tlPos *= size; int remX = int(mod(cPos.x, size)); @@ -36,11 +36,11 @@ vec4 PostFX(sampler2D tex, vec2 uv) if ((remX == remY) || (((int(cPos.x) - int(blPos.x)) == (int(blPos.y) - int(cPos.y))))) { if (invert == 1) c = vec4(0.2, 0.15, 0.05, 1.0); - else c = texture2D(tex, tlPos * vec2(1.0/renderWidth, 1.0/renderHeight)) * 1.4; + else c = texture2D(tex, tlPos*vec2(1.0/renderWidth, 1.0/renderHeight))*1.4; } else { - if (invert == 1) c = texture2D(tex, tlPos * vec2(1.0/renderWidth, 1.0/renderHeight)) * 1.4; + if (invert == 1) c = texture2D(tex, tlPos*vec2(1.0/renderWidth, 1.0/renderHeight))*1.4; else c = vec4(0.0, 0.0, 0.0, 1.0); } diff --git a/examples/shaders/resources/shaders/glsl120/fisheye.fs b/examples/shaders/resources/shaders/glsl120/fisheye.fs index 5cc86b811..b88c5b790 100644 --- a/examples/shaders/resources/shaders/glsl120/fisheye.fs +++ b/examples/shaders/resources/shaders/glsl120/fisheye.fs @@ -15,22 +15,22 @@ const float PI = 3.1415926535; void main() { float aperture = 178.0; - float apertureHalf = 0.5 * aperture * (PI / 180.0); + float apertureHalf = 0.5*aperture*(PI/180.0); float maxFactor = sin(apertureHalf); vec2 uv = vec2(0.0); - vec2 xy = 2.0 * fragTexCoord.xy - 1.0; + vec2 xy = 2.0*fragTexCoord.xy - 1.0; float d = length(xy); if (d < (2.0 - maxFactor)) { - d = length(xy * maxFactor); - float z = sqrt(1.0 - d * d); - float r = atan(d, z) / PI; + d = length(xy*maxFactor); + float z = sqrt(1.0 - d*d); + float r = atan(d, z)/PI; float phi = atan(xy.y, xy.x); - uv.x = r * cos(phi) + 0.5; - uv.y = r * sin(phi) + 0.5; + uv.x = r*cos(phi) + 0.5; + uv.y = r*sin(phi) + 0.5; } else { diff --git a/examples/shaders/resources/shaders/glsl120/lighting.fs b/examples/shaders/resources/shaders/glsl120/lighting.fs index 508d0eb14..ef0dd65ac 100644 --- a/examples/shaders/resources/shaders/glsl120/lighting.fs +++ b/examples/shaders/resources/shaders/glsl120/lighting.fs @@ -38,7 +38,7 @@ void main() vec3 viewD = normalize(viewPos - fragPosition); vec3 specular = vec3(0.0); - vec4 tint = colDiffuse * fragColor; + vec4 tint = colDiffuse*fragColor; // NOTE: Implement here your fragment shader code diff --git a/examples/shaders/resources/shaders/glsl120/lightmap.fs b/examples/shaders/resources/shaders/glsl120/lightmap.fs index 93a0609e2..85fc6f7fe 100644 --- a/examples/shaders/resources/shaders/glsl120/lightmap.fs +++ b/examples/shaders/resources/shaders/glsl120/lightmap.fs @@ -16,5 +16,5 @@ void main() vec4 texelColor = texture2D(texture0, fragTexCoord); vec4 texelColor2 = texture2D(texture1, fragTexCoord2); - gl_FragColor = texelColor * texelColor2; + gl_FragColor = texelColor*texelColor2; } diff --git a/examples/shaders/resources/shaders/glsl120/normalmap.fs b/examples/shaders/resources/shaders/glsl120/normalmap.fs index caeb4d5c1..9e7ba5e19 100644 --- a/examples/shaders/resources/shaders/glsl120/normalmap.fs +++ b/examples/shaders/resources/shaders/glsl120/normalmap.fs @@ -32,21 +32,21 @@ void main() normal = texture(normalMap, vec2(fragTexCoord.x, fragTexCoord.y)).rgb; //Transform normal values to the range -1.0 ... 1.0 - normal = normalize(normal * 2.0 - 1.0); + normal = normalize(normal*2.0 - 1.0); //Transform the normal from tangent-space to world-space for lighting calculation - normal = normalize(normal * TBN); + normal = normalize(normal*TBN); } else { normal = normalize(fragNormal); } - vec4 tint = colDiffuse * fragColor; + vec4 tint = colDiffuse*fragColor; vec3 lightColor = vec3(1.0, 1.0, 1.0); float NdotL = max(dot(normal, lightDir), 0.0); - vec3 lightDot = lightColor * NdotL; + vec3 lightDot = lightColor*NdotL; float specCo = 0.0; @@ -54,9 +54,9 @@ void main() specular += specCo; - finalColor = (texelColor * ((tint + vec4(specular, 1.0)) * vec4(lightDot, 1.0))); - finalColor += texelColor * (vec4(1.0, 1.0, 1.0, 1.0) / 40.0) * tint; + finalColor = (texelColor*((tint + vec4(specular, 1.0))*vec4(lightDot, 1.0))); + finalColor += texelColor*(vec4(1.0, 1.0, 1.0, 1.0)/40.0)*tint; // Gamma correction - gl_FragColor = pow(finalColor, vec4(1.0 / 2.2)); + gl_FragColor = pow(finalColor, vec4(1.0/2.2)); } diff --git a/examples/shaders/resources/shaders/glsl120/normalmap.vs b/examples/shaders/resources/shaders/glsl120/normalmap.vs index d8921a68f..69fdc7e0f 100644 --- a/examples/shaders/resources/shaders/glsl120/normalmap.vs +++ b/examples/shaders/resources/shaders/glsl120/normalmap.vs @@ -27,15 +27,15 @@ mat3 inverse(mat3 m) float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2]; float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2]; - float b01 = a22 * a11 - a12 * a21; - float b11 = -a22 * a10 + a12 * a20; - float b21 = a21 * a10 - a11 * a20; + float b01 = a22*a11 - a12*a21; + float b11 = -a22*a10 + a12*a20; + float b21 = a21*a10 - a11*a20; - float det = a00 * b01 + a01 * b11 + a02 * b21; + float det = a00*b01 + a01*b11 + a02*b21; - return mat3(b01, (-a22 * a01 + a02 * a21), (a12 * a01 - a02 * a11), - b11, (a22 * a00 - a02 * a20), (-a12 * a00 + a02 * a10), - b21, (-a21 * a00 + a01 * a20), (a11 * a00 - a01 * a10)) / det; + return mat3(b01, (-a22*a01 + a02*a21), (a12*a01 - a02*a11), + b11, (a22*a00 - a02*a20), (-a12*a00 + a02*a10), + b21, (-a21*a00 + a01*a20), (a11*a00 - a01*a10))/det; } // https://github.com/glslify/glsl-transpose @@ -49,21 +49,21 @@ mat3 transpose(mat3 m) void main() { // Compute binormal from vertex normal and tangent. W component is the tangent handedness - vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz) * vertexTangent.w; + vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz)*vertexTangent.w; // Compute fragment normal based on normal transformations mat3 normalMatrix = transpose(inverse(mat3(matModel))); // Compute fragment position based on model transformations - fragPosition = vec3(matModel * vec4(vertexPosition, 1.0)); + fragPosition = vec3(matModel*vec4(vertexPosition, 1.0)); //Create TBN matrix for transforming the normal map values from tangent-space to world-space - fragNormal = normalize(normalMatrix * vertexNormal); + fragNormal = normalize(normalMatrix*vertexNormal); - vec3 fragTangent = normalize(normalMatrix * vertexTangent.xyz); - fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal) * fragNormal); + vec3 fragTangent = normalize(normalMatrix*vertexTangent.xyz); + fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal); - vec3 fragBinormal = normalize(normalMatrix * vertexBinormal); + vec3 fragBinormal = normalize(normalMatrix*vertexBinormal); fragBinormal = cross(fragNormal, fragTangent); TBN = transpose(mat3(fragTangent, fragBinormal, fragNormal)); @@ -72,5 +72,5 @@ void main() fragTexCoord = vertexTexCoord; - gl_Position = mvp * vec4(vertexPosition, 1.0); + gl_Position = mvp*vec4(vertexPosition, 1.0); } diff --git a/examples/shaders/resources/shaders/glsl120/palette_switch.fs b/examples/shaders/resources/shaders/glsl120/palette_switch.fs index ab3f79c78..9c60379bc 100644 --- a/examples/shaders/resources/shaders/glsl120/palette_switch.fs +++ b/examples/shaders/resources/shaders/glsl120/palette_switch.fs @@ -13,15 +13,15 @@ uniform ivec3 palette[colors]; void main() { // Texel color fetching from texture sampler - vec4 texelColor = texture(texture0, fragTexCoord) * fragColor; + vec4 texelColor = texture(texture0, fragTexCoord)*fragColor; // Convert the (normalized) texel color RED component (GB would work, too) - // to the palette index by scaling up from [0, 1] to [0, 255]. - int index = int(texelColor.r * 255.0); + // to the palette index by scaling up from [0, 1] to [0, 255] + int index = int(texelColor.r*255.0); ivec3 color = palette[index]; // Calculate final fragment color. Note that the palette color components // are defined in the range [0, 255] and need to be normalized to [0, 1] - // for OpenGL to work. - gl_FragColor = vec4(color / 255.0, texelColor.a); + // for OpenGL to work + gl_FragColor = vec4(color/255.0, texelColor.a); } diff --git a/examples/shaders/resources/shaders/glsl120/pbr.vs b/examples/shaders/resources/shaders/glsl120/pbr.vs index e9750a60a..4c61012cc 100644 --- a/examples/shaders/resources/shaders/glsl120/pbr.vs +++ b/examples/shaders/resources/shaders/glsl120/pbr.vs @@ -52,7 +52,7 @@ mat3 transpose(mat3 m) void main() { // Compute binormal from vertex normal and tangent - vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz) * vertexTangent.w; + vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz)*vertexTangent.w; // Compute fragment normal based on normal transformations mat3 normalMatrix = transpose(inverse(mat3(matModel))); diff --git a/examples/shaders/resources/shaders/glsl120/raymarching.fs b/examples/shaders/resources/shaders/glsl120/raymarching.fs index efe4fa108..6cc27e9d7 100644 --- a/examples/shaders/resources/shaders/glsl120/raymarching.fs +++ b/examples/shaders/resources/shaders/glsl120/raymarching.fs @@ -30,7 +30,7 @@ uniform vec2 resolution; // SOFTWARE. // A list of useful distance function to simple primitives, and an example on how to -// do some interesting boolean operations, repetition and displacement. +// do some interesting boolean operations, repetition and displacement // // More info here: http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm @@ -38,38 +38,38 @@ uniform vec2 resolution; //------------------------------------------------------------------ -float sdPlane( vec3 p ) +float sdPlane(vec3 p) { return p.y; } -float sdSphere( vec3 p, float s ) +float sdSphere(vec3 p, float s) { return length(p)-s; } -float sdBox( vec3 p, vec3 b ) +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 ) +float sdEllipsoid(in vec3 p, in vec3 r) { - return (length( p/r ) - 1.0) * min(min(r.x,r.y),r.z); + return (length(p/r) - 1.0)*min(min(r.x,r.y),r.z); } -float udRoundBox( vec3 p, vec3 b, float r ) +float udRoundBox(vec3 p, vec3 b, float r) { return length(max(abs(p)-b,0.0))-r; } -float sdTorus( vec3 p, vec2 t ) +float sdTorus(vec3 p, vec2 t) { - return length( vec2(length(p.xz)-t.x,p.y) )-t.y; + return length(vec2(length(p.xz)-t.x,p.y))-t.y; } -float sdHexPrism( vec3 p, vec2 h ) +float sdHexPrism(vec3 p, vec2 h) { vec3 q = abs(p); #if 0 @@ -81,24 +81,24 @@ float sdHexPrism( vec3 p, vec2 h ) #endif } -float sdCapsule( vec3 p, vec3 a, vec3 b, float r ) +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 h = clamp(dot(pa,ba)/dot(ba,ba), 0.0, 1.0); + return length(pa - ba*h) - r; } -float sdEquilateralTriangle( in vec2 p ) +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 ); + 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 ) +float sdTriPrism(vec3 p, vec2 h) { vec3 q = abs(p); float d1 = q.z-h.y; @@ -113,95 +113,95 @@ float sdTriPrism( vec3 p, vec2 h ) return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); } -float sdCylinder( vec3 p, vec2 h ) +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 ) +float sdCone(in vec3 p, in vec3 c) { - vec2 q = vec2( length(p.xz), p.y ); + vec2 q = vec2(length(p.xz), p.y); float d1 = -q.y-c.z; - float d2 = max( dot(q,c.xy), q.y); + 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 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 ); + 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 } +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 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 )) )); + 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 ) +float length2(vec2 p) { - return sqrt( p.x*p.x + p.y*p.y ); + return sqrt(p.x*p.x + p.y*p.y); } -float length6( vec2 p ) +float length6(vec2 p) { p = p*p*p; p = p*p; - return pow( p.x + p.y, 1.0/6.0 ); + return pow(p.x + p.y, 1.0/6.0); } -float length8( vec2 p ) +float length8(vec2 p) { p = p*p; p = p*p; p = p*p; - return pow( p.x + p.y, 1.0/8.0 ); + return pow(p.x + p.y, 1.0/8.0); } -float sdTorus82( vec3 p, vec2 t ) +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 ) +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 ) +float sdCylinder6(vec3 p, vec2 h) { - return max( length6(p.xz)-h.x, abs(p.y)-h.y ); + return max(length6(p.xz)-h.x, abs(p.y)-h.y); } //------------------------------------------------------------------ -float opS( float d1, float d2 ) +float opS(float d1, float d2) { return max(-d2,d1); } -vec2 opU( vec2 d1, vec2 d2 ) +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 ); } + float tp1 = (0.0-ro.y)/rd.y; if (tp1>0.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++ ) + for (int i=0; i<64; i++) { float precis = 0.0005*t; - vec2 res = map( ro+rd*t ); - if( res.xtmax ) break; + 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 ); + 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 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++ ) + 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; + 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 ); + return clamp(res, 0.0, 1.0); } -vec3 calcNormal( in vec3 pos ) +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 ); + 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 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 ); + map(pos+eps.yyx).x - map(pos-eps.yyx).x); return normalize(nor); */ } -float calcAO( in vec3 pos, in vec3 nor ) +float calcAO(in vec3 pos, in vec3 nor) { float occ = 0.0; float sca = 1.0; - for( int i=0; i<5; i++ ) + 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; + 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 ); + return clamp(1.0 - 3.0*occ, 0.0, 1.0); } // http://iquilezles.org/www/articles/checkerfiltering/checkerfiltering.htm -float checkersGradBox( in vec2 p ) +float checkersGradBox(in vec2 p) { // filter kernel vec2 w = fwidth(p) + 0.001; @@ -324,43 +324,43 @@ float checkersGradBox( in vec2 p ) return 0.5 - 0.5*i.x*i.y; } -vec3 render( in vec3 ro, in vec3 rd ) +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 ) + if (m>-0.5) { vec3 pos = ro + t*rd; - vec3 nor = calcNormal( pos ); - vec3 ref = reflect( rd, nor ); + 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 ) + 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 ); + 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 ); + 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 ); + 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)* + 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 )); + (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); @@ -371,51 +371,51 @@ vec3 render( in vec3 ro, in vec3 rd ) 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 ) ); + 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) ); + return vec3(clamp(col,0.0,1.0)); } -mat3 setCamera( in vec3 ro, in vec3 ta, float cr ) +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 ); + 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 @@ -423,5 +423,5 @@ void main() tot /= float(AA*AA); #endif - gl_FragColor = vec4( tot, 1.0 ); + gl_FragColor = vec4(tot, 1.0); } diff --git a/examples/shaders/resources/shaders/glsl120/rounded_rectangle.fs b/examples/shaders/resources/shaders/glsl120/rounded_rectangle.fs index eb96c28d3..627e7b1b6 100644 --- a/examples/shaders/resources/shaders/glsl120/rounded_rectangle.fs +++ b/examples/shaders/resources/shaders/glsl120/rounded_rectangle.fs @@ -1,7 +1,7 @@ -// Note: SDF by Iñigo Quilez is licensed under MIT License - #version 120 +// NOTE: SDF by Iñigo Quilez, licensed under MIT License + // Input vertex attributes (from vertex shader) varying vec2 fragTexCoord; varying vec4 fragColor; diff --git a/examples/shaders/resources/shaders/glsl120/scanlines.fs b/examples/shaders/resources/shaders/glsl120/scanlines.fs index 4125ed734..c9abc7856 100644 --- a/examples/shaders/resources/shaders/glsl120/scanlines.fs +++ b/examples/shaders/resources/shaders/glsl120/scanlines.fs @@ -33,7 +33,7 @@ void main() fragColor = color; */ // Scanlines method 2 - float globalPos = (fragTexCoord.y + offset) * frequency; + float globalPos = (fragTexCoord.y + offset)*frequency; float wavePos = cos((fract(globalPos) - 0.5)*3.14); vec4 color = texture2D(texture0, fragTexCoord); diff --git a/examples/shaders/resources/shaders/glsl120/shadowmap.fs b/examples/shaders/resources/shaders/glsl120/shadowmap.fs index 84f318591..354c4a2c5 100644 --- a/examples/shaders/resources/shaders/glsl120/shadowmap.fs +++ b/examples/shaders/resources/shaders/glsl120/shadowmap.fs @@ -52,7 +52,7 @@ void main() vec2 sampleCoords = fragPosLightSpace.xy; float curDepth = fragPosLightSpace.z; - // Slope-scale depth bias: depth biasing reduces "shadow acne" artifacts, where dark stripes appear all over the scene. + // Slope-scale depth bias: depth biasing reduces "shadow acne" artifacts, where dark stripes appear all over the scene // The solution is adding a small bias to the depth // In this case, the bias is proportional to the slope of the surface, relative to the light float bias = max(0.0008*(1.0 - dot(normal, l)), 0.00008); @@ -61,8 +61,8 @@ void main() // PCF (percentage-closer filtering) algorithm: // Instead of testing if just one point is closer to the current point, - // we test the surrounding points as well. - // This blurs shadow edges, hiding aliasing artifacts. + // we test the surrounding points as well + // This blurs shadow edges, hiding aliasing artifacts vec2 texelSize = vec2(1.0/float(shadowMapResolution)); for (int x = -1; x <= 1; x++) { diff --git a/examples/shaders/resources/shaders/glsl120/sobel.fs b/examples/shaders/resources/shaders/glsl120/sobel.fs index bfa19062b..c17cfd8a8 100644 --- a/examples/shaders/resources/shaders/glsl120/sobel.fs +++ b/examples/shaders/resources/shaders/glsl120/sobel.fs @@ -18,10 +18,10 @@ void main() vec4 horizEdge = vec4(0.0); horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y - y))*1.0; - horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y ))*2.0; + horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y ))*2.0; horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y + y))*1.0; horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y - y))*1.0; - horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y ))*2.0; + horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y ))*2.0; horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y + y))*1.0; vec4 vertEdge = vec4(0.0); diff --git a/examples/shaders/resources/shaders/glsl330/cross_stitching.fs b/examples/shaders/resources/shaders/glsl330/cross_stitching.fs index 7c76cf758..7c4bc9b53 100644 --- a/examples/shaders/resources/shaders/glsl330/cross_stitching.fs +++ b/examples/shaders/resources/shaders/glsl330/cross_stitching.fs @@ -25,8 +25,8 @@ vec4 PostFX(sampler2D tex, vec2 uv) { vec4 c = vec4(0.0); float size = stitchingSize; - vec2 cPos = uv * vec2(renderWidth, renderHeight); - vec2 tlPos = floor(cPos / vec2(size, size)); + vec2 cPos = uv*vec2(renderWidth, renderHeight); + vec2 tlPos = floor(cPos/vec2(size, size)); tlPos *= size; int remX = int(mod(cPos.x, size)); @@ -40,11 +40,11 @@ vec4 PostFX(sampler2D tex, vec2 uv) if ((remX == remY) || (((int(cPos.x) - int(blPos.x)) == (int(blPos.y) - int(cPos.y))))) { if (invert == 1) c = vec4(0.2, 0.15, 0.05, 1.0); - else c = texture(tex, tlPos * vec2(1.0/renderWidth, 1.0/renderHeight)) * 1.4; + else c = texture(tex, tlPos*vec2(1.0/renderWidth, 1.0/renderHeight))*1.4; } else { - if (invert == 1) c = texture(tex, tlPos * vec2(1.0/renderWidth, 1.0/renderHeight)) * 1.4; + if (invert == 1) c = texture(tex, tlPos*vec2(1.0/renderWidth, 1.0/renderHeight))*1.4; else c = vec4(0.0, 0.0, 0.0, 1.0); } diff --git a/examples/shaders/resources/shaders/glsl330/cubes_panning.fs b/examples/shaders/resources/shaders/glsl330/cubes_panning.fs index 1d75e4e8c..d58b7b196 100644 --- a/examples/shaders/resources/shaders/glsl330/cubes_panning.fs +++ b/examples/shaders/resources/shaders/glsl330/cubes_panning.fs @@ -17,7 +17,7 @@ float angle = 0.0; vec2 VectorRotateTime(vec2 v, float speed) { float time = uTime*speed; - float localTime = fract(time); // The time domain this works on is 1 sec. + float localTime = fract(time); // The time domain this works on is 1 sec if ((localTime >= 0.0) && (localTime < 0.25)) angle = 0.0; else if ((localTime >= 0.25) && (localTime < 0.50)) angle = PI/4*sin(2*PI*localTime - PI/2); diff --git a/examples/shaders/resources/shaders/glsl330/deferred_shading.fs b/examples/shaders/resources/shaders/glsl330/deferred_shading.fs index c9c6a313f..660db3244 100644 --- a/examples/shaders/resources/shaders/glsl330/deferred_shading.fs +++ b/examples/shaders/resources/shaders/glsl330/deferred_shading.fs @@ -10,9 +10,9 @@ uniform sampler2D gAlbedoSpec; struct Light { int enabled; - int type; // Unused in this demo. + int type; // Unused in this demo vec3 position; - vec3 target; // Unused in this demo. + vec3 target; // Unused in this demo vec4 color; }; @@ -29,22 +29,22 @@ void main() { vec3 albedo = texture(gAlbedoSpec, texCoord).rgb; float specular = texture(gAlbedoSpec, texCoord).a; - vec3 ambient = albedo * vec3(0.1f); + vec3 ambient = albedo*vec3(0.1f); vec3 viewDirection = normalize(viewPosition - fragPosition); - for(int i = 0; i < NR_LIGHTS; ++i) + for (int i = 0; i < NR_LIGHTS; ++i) { - if(lights[i].enabled == 0) continue; + if (lights[i].enabled == 0) continue; vec3 lightDirection = lights[i].position - fragPosition; - vec3 diffuse = max(dot(normal, lightDirection), 0.0) * albedo * lights[i].color.xyz; + vec3 diffuse = max(dot(normal, lightDirection), 0.0)*albedo*lights[i].color.xyz; vec3 halfwayDirection = normalize(lightDirection + viewDirection); float spec = pow(max(dot(normal, halfwayDirection), 0.0), 32.0); - vec3 specular = specular * spec * lights[i].color.xyz; + vec3 specular = specular*spec*lights[i].color.xyz; // Attenuation float distance = length(lights[i].position - fragPosition); - float attenuation = 1.0 / (1.0 + LINEAR * distance + QUADRATIC * distance * distance); + float attenuation = 1.0/(1.0 + LINEAR*distance + QUADRATIC*distance*distance); diffuse *= attenuation; specular *= attenuation; ambient += diffuse + specular; diff --git a/examples/shaders/resources/shaders/glsl330/eratosthenes.fs b/examples/shaders/resources/shaders/glsl330/eratosthenes.fs index 644e38d8c..3a5b7fe47 100644 --- a/examples/shaders/resources/shaders/glsl330/eratosthenes.fs +++ b/examples/shaders/resources/shaders/glsl330/eratosthenes.fs @@ -5,12 +5,12 @@ The Sieve of Eratosthenes -- a simple shader by ProfJski An early prime number sieve: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes - The screen is divided into a square grid of boxes, each representing an integer value. - Each integer is tested to see if it is a prime number. Primes are colored white. - Non-primes are colored with a color that indicates the smallest factor which evenly divdes our integer. + The screen is divided into a square grid of boxes, each representing an integer value + Each integer is tested to see if it is a prime number. Primes are colored white + Non-primes are colored with a color that indicates the smallest factor which evenly divides our integer - You can change the scale variable to make a larger or smaller grid. - Total number of integers displayed = scale squared, so scale = 100 tests the first 10,000 integers. + You can change the scale variable to make a larger or smaller grid + Total number of integers displayed = scale squared, so scale = 100 tests the first 10,000 integers WARNING: If you make scale too large, your GPU may bog down! @@ -39,7 +39,7 @@ vec4 Colorizer(float counter, float maxSize) void main() { vec4 color = vec4(1.0); - float scale = 1000.0; // Makes 100x100 square grid. Change this variable to make a smaller or larger grid. + float scale = 1000.0; // Makes 100x100 square grid, change this variable to make a smaller or larger grid int value = int(scale*floor(fragTexCoord.y*scale)+floor(fragTexCoord.x*scale)); // Group pixels into boxes representing integer values if ((value == 0) || (value == 1) || (value == 2)) finalColor = vec4(1.0); diff --git a/examples/shaders/resources/shaders/glsl330/fisheye.fs b/examples/shaders/resources/shaders/glsl330/fisheye.fs index 51af4ed88..be81aeb23 100644 --- a/examples/shaders/resources/shaders/glsl330/fisheye.fs +++ b/examples/shaders/resources/shaders/glsl330/fisheye.fs @@ -14,22 +14,22 @@ const float PI = 3.1415926535; void main() { float aperture = 178.0; - float apertureHalf = 0.5 * aperture * (PI / 180.0); + float apertureHalf = 0.5*aperture*(PI/180.0); float maxFactor = sin(apertureHalf); vec2 uv = vec2(0); - vec2 xy = 2.0 * fragTexCoord.xy - 1.0; + vec2 xy = 2.0*fragTexCoord.xy - 1.0; float d = length(xy); if (d < (2.0 - maxFactor)) { - d = length(xy * maxFactor); - float z = sqrt(1.0 - d * d); - float r = atan(d, z) / PI; + d = length(xy*maxFactor); + float z = sqrt(1.0 - d*d); + float r = atan(d, z)/PI; float phi = atan(xy.y, xy.x); - uv.x = r * cos(phi) + 0.5; - uv.y = r * sin(phi) + 0.5; + uv.x = r*cos(phi) + 0.5; + uv.y = r*sin(phi) + 0.5; } else { diff --git a/examples/shaders/resources/shaders/glsl330/gbuffer.vs b/examples/shaders/resources/shaders/glsl330/gbuffer.vs index 7d264ba64..5af4448e6 100644 --- a/examples/shaders/resources/shaders/glsl330/gbuffer.vs +++ b/examples/shaders/resources/shaders/glsl330/gbuffer.vs @@ -13,12 +13,12 @@ uniform mat4 matProjection; void main() { - vec4 worldPos = matModel * vec4(vertexPosition, 1.0); + vec4 worldPos = matModel*vec4(vertexPosition, 1.0); fragPosition = worldPos.xyz; fragTexCoord = vertexTexCoord; mat3 normalMatrix = transpose(inverse(mat3(matModel))); - fragNormal = normalMatrix * vertexNormal; + fragNormal = normalMatrix*vertexNormal; - gl_Position = matProjection * matView * worldPos; + gl_Position = matProjection*matView*worldPos; } diff --git a/examples/shaders/resources/shaders/glsl330/hybrid_raster.fs b/examples/shaders/resources/shaders/glsl330/hybrid_raster.fs index ffd1b3648..12409d137 100644 --- a/examples/shaders/resources/shaders/glsl330/hybrid_raster.fs +++ b/examples/shaders/resources/shaders/glsl330/hybrid_raster.fs @@ -18,5 +18,5 @@ void main() vec4 texelColor = texture(texture0, fragTexCoord); gl_FragColor = texelColor*colDiffuse*fragColor; - gl_FragDepth = gl_FragCoord.z; + gl_FragDepth = gl_FragCoord.z; } \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl330/hybrid_raymarch.fs b/examples/shaders/resources/shaders/glsl330/hybrid_raymarch.fs index 5b66dd71c..2edb625ad 100644 --- a/examples/shaders/resources/shaders/glsl330/hybrid_raymarch.fs +++ b/examples/shaders/resources/shaders/glsl330/hybrid_raymarch.fs @@ -22,14 +22,14 @@ float CalcDepth(in vec3 rd, in float Idist){ } // https://iquilezles.org/articles/distfunctions/ -float sdHorseshoe( in vec3 p, in vec2 c, in float r, in float le, vec2 w ) +float sdHorseshoe(in vec3 p, in vec2 c, in float r, in float le, vec2 w) { p.x = abs(p.x); float l = length(p.xy); p.xy = mat2(-c.x, c.y, c.y, c.x)*p.xy; p.xy = vec2((p.y>0.0 || p.x>0.0)?p.x:l*sign(-c.x), - (p.x>0.0)?p.y:l ); + (p.x>0.0)?p.y:l); p.xy = vec2(p.x,abs(p.y-r))-vec2(le,0.0); vec2 q = vec2(length(max(p.xy,0.0)) + min(0.0,max(p.x,p.y)),p.z); @@ -40,48 +40,48 @@ float sdHorseshoe( in vec3 p, in vec2 c, in float r, in float le, vec2 w ) // r = sphere's radius // h = cutting's plane's position // t = thickness -float sdSixWayCutHollowSphere( vec3 p, float r, float h, float t ) +float sdSixWayCutHollowSphere(vec3 p, float r, float h, float t) { // Six way symetry Transformation vec3 ap = abs(p); - if(ap.x < max(ap.y, ap.z)){ - if(ap.y < ap.z) ap.xz = ap.zx; + if (ap.x < max(ap.y, ap.z)){ + if (ap.y < ap.z) ap.xz = ap.zx; else ap.xy = ap.yx; } - vec2 q = vec2( length(ap.yz), ap.x ); + vec2 q = vec2(length(ap.yz), ap.x); float w = sqrt(r*r-h*h); return ((h*q.x0.0 ) + if (tp1>0.0) { - tmax = min( tmax, tp1 ); - res = vec2( tp1, 1.0 ); + tmax = min(tmax, tp1); + res = vec2(tp1, 1.0); } float t = tmin; - for( int i=0; i<70 ; i++ ) + for (int i=0; i<70 ; i++) { - if(t>tmax) break; - vec2 h = map( ro+rd*t ); - if( abs(h.x)<(0.0001*t) ) + if (t>tmax) break; + vec2 h = map(ro+rd*t); + if (abs(h.x)<(0.0001*t)) { res = vec2(t,h.y); break; @@ -113,54 +113,54 @@ vec2 raycast( in vec3 ro, in vec3 rd ){ // https://iquilezles.org/articles/rmshadows -float calcSoftshadow( in vec3 ro, in vec3 rd, in float mint, in float tmax ) +float calcSoftshadow(in vec3 ro, in vec3 rd, in float mint, in float tmax) { // bounding volume - float tp = (0.8-ro.y)/rd.y; if( tp>0.0 ) tmax = min( tmax, tp ); + float tp = (0.8-ro.y)/rd.y; if (tp>0.0) tmax = min(tmax, tp); float res = 1.0; float t = mint; - for( int i=ZERO; i<24; i++ ) + for (int i=ZERO; i<24; i++) { - float h = map( ro + rd*t ).x; + float h = map(ro + rd*t).x; float s = clamp(8.0*h/t,0.0,1.0); - res = min( res, s ); - t += clamp( h, 0.01, 0.2 ); - if( res<0.004 || t>tmax ) break; + res = min(res, s); + t += clamp(h, 0.01, 0.2); + if (res<0.004 || t>tmax) break; } - res = clamp( res, 0.0, 1.0 ); + res = clamp(res, 0.0, 1.0); return res*res*(3.0-2.0*res); } // https://iquilezles.org/articles/normalsSDF -vec3 calcNormal( in vec3 pos ) +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 ); + 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); } // https://iquilezles.org/articles/nvscene2008/rwwtt.pdf -float calcAO( in vec3 pos, in vec3 nor ) +float calcAO(in vec3 pos, in vec3 nor) { - float occ = 0.0; + float occ = 0.0; float sca = 1.0; - for( int i=ZERO; i<5; i++ ) + for (int i=ZERO; i<5; i++) { float h = 0.01 + 0.12*float(i)/4.0; - float d = map( pos + h*nor ).x; + float d = map(pos + h*nor).x; occ += (h-d)*sca; sca *= 0.95; - if( occ>0.35 ) break; + if (occ>0.35) break; } - return clamp( 1.0 - 3.0*occ, 0.0, 1.0 ) * (0.5+0.5*nor.y); + return clamp(1.0 - 3.0*occ, 0.0, 1.0)*(0.5+0.5*nor.y); } // https://iquilezles.org/articles/checkerfiltering -float checkersGradBox( in vec2 p ) +float checkersGradBox(in vec2 p) { // filter kernel vec2 w = fwidth(p) + 0.001; @@ -171,7 +171,7 @@ float checkersGradBox( in vec2 p ) } // https://www.shadertoy.com/view/tdS3DG -vec4 render( in vec3 ro, in vec3 rd) +vec4 render(in vec3 ro, in vec3 rd) { // background vec3 col = vec3(0.7, 0.7, 0.9) - max(rd.y,0.0)*0.3; @@ -179,37 +179,37 @@ vec4 render( in vec3 ro, in vec3 rd) // raycast scene vec2 res = raycast(ro,rd); float t = res.x; - float m = res.y; - if( m>-0.5 ) + float m = res.y; + if (m>-0.5) { vec3 pos = ro + t*rd; - vec3 nor = (m<1.5) ? vec3(0.0,1.0,0.0) : calcNormal( pos ); - vec3 ref = reflect( rd, nor ); + vec3 nor = (m<1.5) ? vec3(0.0,1.0,0.0) : calcNormal(pos); + vec3 ref = reflect(rd, nor); // material - col = 0.2 + 0.2*sin( m*2.0 + vec3(0.0,1.0,2.0) ); + col = 0.2 + 0.2*sin(m*2.0 + vec3(0.0,1.0,2.0)); float ks = 1.0; - if( m<1.5 ) + if (m<1.5) { - float f = checkersGradBox( 3.0*pos.xz); + float f = checkersGradBox(3.0*pos.xz); col = 0.15 + f*vec3(0.05); ks = 0.4; } // lighting - float occ = calcAO( pos, nor ); + float occ = calcAO(pos, nor); - vec3 lin = vec3(0.0); + vec3 lin = vec3(0.0); // sun { - vec3 lig = normalize( vec3(-0.5, 0.4, -0.6) ); - vec3 hal = normalize( lig-rd ); - float dif = clamp( dot( nor, lig ), 0.0, 1.0 ); - //if( dif>0.0001 ) - dif *= calcSoftshadow( pos, lig, 0.02, 2.5 ); - float spe = pow( clamp( dot( nor, hal ), 0.0, 1.0 ),16.0); + vec3 lig = normalize(vec3(-0.5, 0.4, -0.6)); + vec3 hal = normalize(lig-rd); + float dif = clamp(dot(nor, lig), 0.0, 1.0); + //if (dif>0.0001) + dif *= calcSoftshadow(pos, lig, 0.02, 2.5); + float spe = pow(clamp(dot(nor, hal), 0.0, 1.0),16.0); spe *= dif; spe *= 0.04+0.96*pow(clamp(1.0-dot(hal,lig),0.0,1.0),5.0); //spe *= 0.04+0.96*pow(clamp(1.0-sqrt(0.5*(1.0-dot(rd,lig))),0.0,1.0),5.0); @@ -218,35 +218,35 @@ vec4 render( in vec3 ro, in vec3 rd) } // sky { - float dif = sqrt(clamp( 0.5+0.5*nor.y, 0.0, 1.0 )); + float dif = sqrt(clamp(0.5+0.5*nor.y, 0.0, 1.0)); dif *= occ; - float spe = smoothstep( -0.2, 0.2, ref.y ); + float spe = smoothstep(-0.2, 0.2, ref.y); spe *= dif; - spe *= 0.04+0.96*pow(clamp(1.0+dot(nor,rd),0.0,1.0), 5.0 ); - //if( spe>0.001 ) - spe *= calcSoftshadow( pos, ref, 0.02, 2.5 ); + spe *= 0.04+0.96*pow(clamp(1.0+dot(nor,rd),0.0,1.0), 5.0); + //if (spe>0.001) + spe *= calcSoftshadow(pos, ref, 0.02, 2.5); lin += col*0.60*dif*vec3(0.40,0.60,1.15); lin += 2.00*spe*vec3(0.40,0.60,1.30)*ks; } // back { - float dif = clamp( dot( nor, normalize(vec3(0.5,0.0,0.6))), 0.0, 1.0 )*clamp( 1.0-pos.y,0.0,1.0); + float dif = clamp(dot(nor, normalize(vec3(0.5,0.0,0.6))), 0.0, 1.0)*clamp(1.0-pos.y,0.0,1.0); dif *= occ; - lin += col*0.55*dif*vec3(0.25,0.25,0.25); + lin += col*0.55*dif*vec3(0.25,0.25,0.25); } // sss { float dif = pow(clamp(1.0+dot(nor,rd),0.0,1.0),2.0); dif *= occ; - lin += col*0.25*dif*vec3(1.00,1.00,1.00); + lin += col*0.25*dif*vec3(1.00,1.00,1.00); } - col = lin; + col = lin; - col = mix( col, vec3(0.7,0.7,0.9), 1.0-exp( -0.0001*t*t*t ) ); + col = mix(col, vec3(0.7,0.7,0.9), 1.0-exp(-0.0001*t*t*t)); } - return vec4(vec3( clamp(col,0.0,1.0) ),t); + return vec4(vec3(clamp(col,0.0,1.0)),t); } vec3 CalcRayDir(vec2 nCoord){ @@ -257,11 +257,11 @@ vec3 CalcRayDir(vec2 nCoord){ mat3 setCamera() { - vec3 cw = normalize(camDir); - vec3 cp = vec3(0.0, 1.0 ,0.0); - vec3 cu = normalize( cross(cw,cp) ); - vec3 cv = ( cross(cu,cw) ); - return mat3( cu, cv, cw ); + vec3 cw = normalize(camDir); + vec3 cp = vec3(0.0, 1.0 ,0.0); + vec3 cu = normalize(cross(cw,cp)); + vec3 cv = (cross(cu,cw)); + return mat3(cu, cv, cw); } void main() @@ -271,14 +271,14 @@ void main() // focal length float fl = length(camDir); - vec3 rd = ca * normalize( vec3(nCoord,fl) ); + vec3 rd = ca*normalize(vec3(nCoord,fl)); vec3 color = vec3(nCoord/2.0 + 0.5, 0.0); float depth = gl_FragCoord.z; { - vec4 res = render( camPos - vec3(0.0, 0.0, 0.0) , rd ); + vec4 res = render(camPos - vec3(0.0, 0.0, 0.0) , rd); color = res.xyz; depth = CalcDepth(rd,res.w); } gl_FragColor = vec4(color , 1.0); - gl_FragDepth = depth; + gl_FragDepth = depth; } \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl330/julia_set.fs b/examples/shaders/resources/shaders/glsl330/julia_set.fs index 3a8e25deb..a3e5bec56 100644 --- a/examples/shaders/resources/shaders/glsl330/julia_set.fs +++ b/examples/shaders/resources/shaders/glsl330/julia_set.fs @@ -8,11 +8,11 @@ in vec4 fragColor; out vec4 finalColor; uniform vec2 c; // c.x = real, c.y = imaginary component. Equation done is z^2 + c -uniform vec2 offset; // Offset of the scale. -uniform float zoom; // Zoom of the scale. +uniform vec2 offset; // Offset of the scale +uniform float zoom; // Zoom of the scale -const int maxIterations = 255; // Max iterations to do. -const float colorCycles = 2.0; // Number of times the color palette repeats. Can show higher detail for higher iteration numbers. +const int maxIterations = 255; // Max iterations to do +const float colorCycles = 2.0; // Number of times the color palette repeats. Can show higher detail for higher iteration numbers // Square a complex number vec2 ComplexSquare(vec2 z) @@ -31,22 +31,22 @@ vec3 Hsv2rgb(vec3 c) void main() { /********************************************************************************************** - Julia sets use a function z^2 + c, where c is a constant. - This function is iterated until the nature of the point is determined. + Julia sets use a function z^2 + c, where c is a constant + This function is iterated until the nature of the point is determined If the magnitude of the number becomes greater than 2, then from that point onward - the number will get bigger and bigger, and will never get smaller (tends towards infinity). - 2^2 = 4, 4^2 = 8 and so on. - So at 2 we stop iterating. + the number will get bigger and bigger, and will never get smaller (tends towards infinity) + 2^2 = 4, 4^2 = 8 and so on + So at 2 we stop iterating - If the number is below 2, we keep iterating. + If the number is below 2, we keep iterating But when do we stop iterating if the number is always below 2 (it converges)? - That is what maxIterations is for. - Then we can divide the iterations by the maxIterations value to get a normalized value that we can - then map to a color. + That is what maxIterations is for + Then we can divide the iterations by the maxIterations value to get a normalized value + that we can then map to a color - We use dot product (z.x * z.x + z.y * z.y) to determine the magnitude (length) squared. - And once the magnitude squared is > 4, then magnitude > 2 is also true (saves computational power). + We use dot product (z.x*z.x + z.y*z.y) to determine the magnitude (length) squared + And once the magnitude squared is > 4, then magnitude > 2 is also true (saves computational power) *************************************************************************************************/ // The pixel coordinates are scaled so they are on the mandelbrot scale @@ -63,18 +63,18 @@ void main() if (dot(z, z) > 4.0) break; } - // Another few iterations decreases errors in the smoothing calculation. - // See http://linas.org/art-gallery/escape/escape.html for more information. + // Another few iterations decreases errors in the smoothing calculation + // See http://linas.org/art-gallery/escape/escape.html for more information z = ComplexSquare(z) + c; z = ComplexSquare(z) + c; - // This last part smooths the color (again see link above). + // This last part smooths the color (again see link above) float smoothVal = float(iterations) + 1.0 - (log(log(length(z)))/log(2.0)); - // Normalize the value so it is between 0 and 1. + // Normalize the value so it is between 0 and 1 float norm = smoothVal/float(maxIterations); - // If in set, color black. 0.999 allows for some float accuracy error. + // If in set, color black. 0.999 allows for some float accuracy error if (norm > 0.999) finalColor = vec4(0.0, 0.0, 0.0, 1.0); else finalColor = vec4(Hsv2rgb(vec3(norm*colorCycles, 1.0, 1.0)), 1.0); } diff --git a/examples/shaders/resources/shaders/glsl330/lighting.fs b/examples/shaders/resources/shaders/glsl330/lighting.fs index 8f4a51e54..b9abfed77 100644 --- a/examples/shaders/resources/shaders/glsl330/lighting.fs +++ b/examples/shaders/resources/shaders/glsl330/lighting.fs @@ -41,7 +41,7 @@ void main() vec3 viewD = normalize(viewPos - fragPosition); vec3 specular = vec3(0.0); - vec4 tint = colDiffuse * fragColor; + vec4 tint = colDiffuse*fragColor; // NOTE: Implement here your fragment shader code diff --git a/examples/shaders/resources/shaders/glsl330/lightmap.fs b/examples/shaders/resources/shaders/glsl330/lightmap.fs index 827473d25..0685e84b1 100644 --- a/examples/shaders/resources/shaders/glsl330/lightmap.fs +++ b/examples/shaders/resources/shaders/glsl330/lightmap.fs @@ -19,5 +19,5 @@ void main() vec4 texelColor = texture(texture0, fragTexCoord); vec4 texelColor2 = texture(texture1, fragTexCoord2); - finalColor = texelColor * texelColor2; + finalColor = texelColor*texelColor2; } diff --git a/examples/shaders/resources/shaders/glsl330/normalmap.fs b/examples/shaders/resources/shaders/glsl330/normalmap.fs index 644130ab6..697423701 100644 --- a/examples/shaders/resources/shaders/glsl330/normalmap.fs +++ b/examples/shaders/resources/shaders/glsl330/normalmap.fs @@ -36,21 +36,21 @@ void main() normal = texture(normalMap, vec2(fragTexCoord.x, fragTexCoord.y)).rgb; //Transform normal values to the range -1.0 ... 1.0 - normal = normalize(normal * 2.0 - 1.0); + normal = normalize(normal*2.0 - 1.0); //Transform the normal from tangent-space to world-space for lighting calculation - normal = normalize(normal * TBN); + normal = normalize(normal*TBN); } else { normal = normalize(fragNormal); } - vec4 tint = colDiffuse * fragColor; + vec4 tint = colDiffuse*fragColor; vec3 lightColor = vec3(1.0, 1.0, 1.0); float NdotL = max(dot(normal, lightDir), 0.0); - vec3 lightDot = lightColor * NdotL; + vec3 lightDot = lightColor*NdotL; float specCo = 0.0; @@ -58,10 +58,10 @@ void main() specular += specCo; - finalColor = (texelColor * ((tint + vec4(specular, 1.0)) * vec4(lightDot, 1.0))); - finalColor += texelColor * (vec4(1.0, 1.0, 1.0, 1.0) / 40.0) * tint; + finalColor = (texelColor*((tint + vec4(specular, 1.0))*vec4(lightDot, 1.0))); + finalColor += texelColor*(vec4(1.0, 1.0, 1.0, 1.0)/40.0)*tint; // Gamma correction - finalColor = pow(finalColor, vec4(1.0 / 2.2)); + finalColor = pow(finalColor, vec4(1.0/2.2)); //finalColor = vec4(normal, 1.0); } diff --git a/examples/shaders/resources/shaders/glsl330/normalmap.vs b/examples/shaders/resources/shaders/glsl330/normalmap.vs index 1e0a161b8..58fff39b9 100644 --- a/examples/shaders/resources/shaders/glsl330/normalmap.vs +++ b/examples/shaders/resources/shaders/glsl330/normalmap.vs @@ -21,21 +21,21 @@ out mat3 TBN; void main() { // Compute binormal from vertex normal and tangent. W component is the tangent handedness - vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz) * vertexTangent.w; + vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz)*vertexTangent.w; // Compute fragment normal based on normal transformations mat3 normalMatrix = transpose(inverse(mat3(matModel))); // Compute fragment position based on model transformations - fragPosition = vec3(matModel * vec4(vertexPosition, 1.0)); + fragPosition = vec3(matModel*vec4(vertexPosition, 1.0)); //Create TBN matrix for transforming the normal map values from tangent-space to world-space - fragNormal = normalize(normalMatrix * vertexNormal); + fragNormal = normalize(normalMatrix*vertexNormal); - vec3 fragTangent = normalize(normalMatrix * vertexTangent.xyz); - fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal) * fragNormal); + vec3 fragTangent = normalize(normalMatrix*vertexTangent.xyz); + fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal); - vec3 fragBinormal = normalize(normalMatrix * vertexBinormal); + vec3 fragBinormal = normalize(normalMatrix*vertexBinormal); fragBinormal = cross(fragNormal, fragTangent); TBN = transpose(mat3(fragTangent, fragBinormal, fragNormal)); @@ -44,5 +44,5 @@ void main() fragTexCoord = vertexTexCoord; - gl_Position = mvp * vec4(vertexPosition, 1.0); + gl_Position = mvp*vec4(vertexPosition, 1.0); } diff --git a/examples/shaders/resources/shaders/glsl330/pbr.vs b/examples/shaders/resources/shaders/glsl330/pbr.vs index 8aabb6baf..0fc7888d3 100644 --- a/examples/shaders/resources/shaders/glsl330/pbr.vs +++ b/examples/shaders/resources/shaders/glsl330/pbr.vs @@ -26,7 +26,7 @@ const float normalOffset = 0.1; void main() { // Compute binormal from vertex normal and tangent - vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz) * vertexTangent.w; + vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz)*vertexTangent.w; // Compute fragment normal based on normal transformations mat3 normalMatrix = transpose(inverse(mat3(matModel))); diff --git a/examples/shaders/resources/shaders/glsl330/raymarching.fs b/examples/shaders/resources/shaders/glsl330/raymarching.fs index 6a9eb4516..67446cdc8 100644 --- a/examples/shaders/resources/shaders/glsl330/raymarching.fs +++ b/examples/shaders/resources/shaders/glsl330/raymarching.fs @@ -33,7 +33,7 @@ uniform vec2 resolution; // SOFTWARE. // A list of useful distance function to simple primitives, and an example on how to -// do some interesting boolean operations, repetition and displacement. +// do some interesting boolean operations, repetition and displacement // // More info here: http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm @@ -41,38 +41,38 @@ uniform vec2 resolution; //------------------------------------------------------------------ -float sdPlane( vec3 p ) +float sdPlane(vec3 p) { return p.y; } -float sdSphere( vec3 p, float s ) +float sdSphere(vec3 p, float s) { return length(p)-s; } -float sdBox( vec3 p, vec3 b ) +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 ) +float sdEllipsoid(in vec3 p, in vec3 r) { - return (length( p/r ) - 1.0) * min(min(r.x,r.y),r.z); + return (length(p/r) - 1.0)*min(min(r.x,r.y),r.z); } -float udRoundBox( vec3 p, vec3 b, float r ) +float udRoundBox(vec3 p, vec3 b, float r) { return length(max(abs(p)-b,0.0))-r; } -float sdTorus( vec3 p, vec2 t ) +float sdTorus(vec3 p, vec2 t) { - return length( vec2(length(p.xz)-t.x,p.y) )-t.y; + return length(vec2(length(p.xz)-t.x,p.y))-t.y; } -float sdHexPrism( vec3 p, vec2 h ) +float sdHexPrism(vec3 p, vec2 h) { vec3 q = abs(p); #if 0 @@ -84,24 +84,24 @@ float sdHexPrism( vec3 p, vec2 h ) #endif } -float sdCapsule( vec3 p, vec3 a, vec3 b, float r ) +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 h = clamp(dot(pa,ba)/dot(ba,ba), 0.0, 1.0); + return length(pa - ba*h) - r; } -float sdEquilateralTriangle( in vec2 p ) +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 ); + 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 ) +float sdTriPrism(vec3 p, vec2 h) { vec3 q = abs(p); float d1 = q.z-h.y; @@ -116,95 +116,95 @@ float sdTriPrism( vec3 p, vec2 h ) return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); } -float sdCylinder( vec3 p, vec2 h ) +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 ) +float sdCone(in vec3 p, in vec3 c) { - vec2 q = vec2( length(p.xz), p.y ); + vec2 q = vec2(length(p.xz), p.y); float d1 = -q.y-c.z; - float d2 = max( dot(q,c.xy), q.y); + 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 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 ); + 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 } +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 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 )) )); + 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 ) +float length2(vec2 p) { - return sqrt( p.x*p.x + p.y*p.y ); + return sqrt(p.x*p.x + p.y*p.y); } -float length6( vec2 p ) +float length6(vec2 p) { p = p*p*p; p = p*p; - return pow( p.x + p.y, 1.0/6.0 ); + return pow(p.x + p.y, 1.0/6.0); } -float length8( vec2 p ) +float length8(vec2 p) { p = p*p; p = p*p; p = p*p; - return pow( p.x + p.y, 1.0/8.0 ); + return pow(p.x + p.y, 1.0/8.0); } -float sdTorus82( vec3 p, vec2 t ) +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 ) +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 ) +float sdCylinder6(vec3 p, vec2 h) { - return max( length6(p.xz)-h.x, abs(p.y)-h.y ); + return max(length6(p.xz)-h.x, abs(p.y)-h.y); } //------------------------------------------------------------------ -float opS( float d1, float d2 ) +float opS(float d1, float d2) { return max(-d2,d1); } -vec2 opU( vec2 d1, vec2 d2 ) +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 ); } + float tp1 = (0.0-ro.y)/rd.y; if (tp1>0.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++ ) + for (int i=0; i<64; i++) { float precis = 0.0005*t; - vec2 res = map( ro+rd*t ); - if( res.xtmax ) break; + 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 ); + 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 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++ ) + 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; + 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 ); + return clamp(res, 0.0, 1.0); } -vec3 calcNormal( in vec3 pos ) +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 ); + 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 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 ); + map(pos+eps.yyx).x - map(pos-eps.yyx).x); return normalize(nor); */ } -float calcAO( in vec3 pos, in vec3 nor ) +float calcAO(in vec3 pos, in vec3 nor) { float occ = 0.0; float sca = 1.0; - for( int i=0; i<5; i++ ) + 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; + 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 ); + return clamp(1.0 - 3.0*occ, 0.0, 1.0); } // http://iquilezles.org/www/articles/checkerfiltering/checkerfiltering.htm -float checkersGradBox( in vec2 p ) +float checkersGradBox(in vec2 p) { // filter kernel vec2 w = fwidth(p) + 0.001; @@ -327,43 +327,43 @@ float checkersGradBox( in vec2 p ) return 0.5 - 0.5*i.x*i.y; } -vec3 render( in vec3 ro, in vec3 rd ) +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 ) + if (m>-0.5) { vec3 pos = ro + t*rd; - vec3 nor = calcNormal( pos ); - vec3 ref = reflect( rd, nor ); + 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 ) + 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 ); + 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 ); + 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 ); + 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)* + 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 )); + (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); @@ -374,51 +374,51 @@ vec3 render( in vec3 ro, in vec3 rd ) 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 ) ); + 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) ); + return vec3(clamp(col,0.0,1.0)); } -mat3 setCamera( in vec3 ro, in vec3 ta, float cr ) +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 ); + 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 @@ -426,5 +426,5 @@ void main() tot /= float(AA*AA); #endif - finalColor = vec4( tot, 1.0 ); + finalColor = vec4(tot, 1.0); } diff --git a/examples/shaders/resources/shaders/glsl330/scanlines.fs b/examples/shaders/resources/shaders/glsl330/scanlines.fs index 4de19cf11..77d8f63bc 100644 --- a/examples/shaders/resources/shaders/glsl330/scanlines.fs +++ b/examples/shaders/resources/shaders/glsl330/scanlines.fs @@ -39,7 +39,7 @@ void main() fragColor = color; */ // Scanlines method 2 - float globalPos = (fragTexCoord.y + offset) * frequency; + float globalPos = (fragTexCoord.y + offset)*frequency; float wavePos = cos((fract(globalPos) - 0.5)*3.14); // Texel color fetching from texture sampler diff --git a/examples/shaders/resources/shaders/glsl330/shadowmap.fs b/examples/shaders/resources/shaders/glsl330/shadowmap.fs index d535de2bc..6ad41461c 100644 --- a/examples/shaders/resources/shaders/glsl330/shadowmap.fs +++ b/examples/shaders/resources/shaders/glsl330/shadowmap.fs @@ -49,13 +49,13 @@ void main() finalColor = (texelColor*((colDiffuse + vec4(specular, 1.0))*vec4(lightDot, 1.0))); // Shadow calculations - vec4 fragPosLightSpace = lightVP * vec4(fragPosition, 1); + vec4 fragPosLightSpace = lightVP*vec4(fragPosition, 1); fragPosLightSpace.xyz /= fragPosLightSpace.w; // Perform the perspective division fragPosLightSpace.xyz = (fragPosLightSpace.xyz + 1.0)/2.0; // Transform from [-1, 1] range to [0, 1] range vec2 sampleCoords = fragPosLightSpace.xy; float curDepth = fragPosLightSpace.z; - // Slope-scale depth bias: depth biasing reduces "shadow acne" artifacts, where dark stripes appear all over the scene. + // Slope-scale depth bias: depth biasing reduces "shadow acne" artifacts, where dark stripes appear all over the scene // The solution is adding a small bias to the depth // In this case, the bias is proportional to the slope of the surface, relative to the light float bias = max(0.0002*(1.0 - dot(normal, l)), 0.00002) + 0.00001; @@ -64,8 +64,8 @@ void main() // PCF (percentage-closer filtering) algorithm: // Instead of testing if just one point is closer to the current point, - // we test the surrounding points as well. - // This blurs shadow edges, hiding aliasing artifacts. + // we test the surrounding points as well + // This blurs shadow edges, hiding aliasing artifacts vec2 texelSize = vec2(1.0/float(shadowMapResolution)); for (int x = -1; x <= 1; x++) { diff --git a/examples/shaders/resources/shaders/glsl330/sobel.fs b/examples/shaders/resources/shaders/glsl330/sobel.fs index 590c84d70..a8005cb56 100644 --- a/examples/shaders/resources/shaders/glsl330/sobel.fs +++ b/examples/shaders/resources/shaders/glsl330/sobel.fs @@ -21,10 +21,10 @@ void main() vec4 horizEdge = vec4(0.0); horizEdge -= texture(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y - y))*1.0; - horizEdge -= texture(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y ))*2.0; + horizEdge -= texture(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y ))*2.0; horizEdge -= texture(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y + y))*1.0; horizEdge += texture(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y - y))*1.0; - horizEdge += texture(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y ))*2.0; + horizEdge += texture(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y ))*2.0; horizEdge += texture(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y + y))*1.0; vec4 vertEdge = vec4(0.0); diff --git a/examples/shaders/resources/shaders/glsl330/spotlight.fs b/examples/shaders/resources/shaders/glsl330/spotlight.fs index 9c0b70363..b3e89ebdf 100644 --- a/examples/shaders/resources/shaders/glsl330/spotlight.fs +++ b/examples/shaders/resources/shaders/glsl330/spotlight.fs @@ -53,7 +53,7 @@ void main() else { if (d < spots[fi].inner) alpha = 0.0; - else alpha = (d - spots[fi].inner) / (spots[fi].radius - spots[fi].inner); + else alpha = (d - spots[fi].inner)/(spots[fi].radius - spots[fi].inner); } } diff --git a/examples/shaders/resources/shaders/glsl330/vertex_displacement.vs b/examples/shaders/resources/shaders/glsl330/vertex_displacement.vs index 73cf16109..080ad473a 100644 --- a/examples/shaders/resources/shaders/glsl330/vertex_displacement.vs +++ b/examples/shaders/resources/shaders/glsl330/vertex_displacement.vs @@ -24,13 +24,13 @@ 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); + 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; + animatedTexCoord = animatedTexCoord*0.5 + 0.5; // Fetch displacement from the perlin noise map - float displacement = texture(perlinNoiseMap, animatedTexCoord).r * 7; // Amplified displacement + float displacement = texture(perlinNoiseMap, animatedTexCoord).r*7; // Amplified displacement // Displace vertex position vec3 displacedPosition = vertexPosition + vec3(0.0, displacement, 0.0); @@ -39,7 +39,7 @@ void main() 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 + 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/wave.fs b/examples/shaders/resources/shaders/glsl330/wave.fs index 1f22bee09..393f1bde2 100644 --- a/examples/shaders/resources/shaders/glsl330/wave.fs +++ b/examples/shaders/resources/shaders/glsl330/wave.fs @@ -23,15 +23,15 @@ uniform float speedX; uniform float speedY; void main() { - float pixelWidth = 1.0 / size.x; - float pixelHeight = 1.0 / size.y; - float aspect = pixelHeight / pixelWidth; + float pixelWidth = 1.0/size.x; + float pixelHeight = 1.0/size.y; + float aspect = pixelHeight/pixelWidth; float boxLeft = 0.0; float boxTop = 0.0; vec2 p = fragTexCoord; - p.x += cos((fragTexCoord.y - boxTop) * freqX / ( pixelWidth * 750.0) + (seconds * speedX)) * ampX * pixelWidth; - p.y += sin((fragTexCoord.x - boxLeft) * freqY * aspect / ( pixelHeight * 750.0) + (seconds * speedY)) * ampY * pixelHeight; + p.x += cos((fragTexCoord.y - boxTop)*freqX/(pixelWidth*750.0) + (seconds*speedX))*ampX*pixelWidth; + p.y += sin((fragTexCoord.x - boxLeft)*freqY*aspect/(pixelHeight*750.0) + (seconds*speedY))*ampY*pixelHeight; finalColor = texture(texture0, p)*colDiffuse*fragColor; } diff --git a/examples/text/resources/shaders/glsl330/alpha_discard.fs b/examples/text/resources/shaders/glsl330/alpha_discard.fs index d2134a688..3e2673d0d 100644 --- a/examples/text/resources/shaders/glsl330/alpha_discard.fs +++ b/examples/text/resources/shaders/glsl330/alpha_discard.fs @@ -15,5 +15,5 @@ void main() { vec4 texelColor = texture(texture0, fragTexCoord); if (texelColor.a == 0.0) discard; - finalColor = texelColor * fragColor * colDiffuse; + finalColor = texelColor*fragColor*colDiffuse; }