From 64848bbd4cbf7ef7f8b0a95336e9cbcc308ce3aa Mon Sep 17 00:00:00 2001 From: Vadim Gunko Date: Wed, 11 Feb 2026 23:12:15 +0500 Subject: [PATCH 001/319] Update BINDINGS.md (#5546) added support for Delphi --- BINDINGS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BINDINGS.md b/BINDINGS.md index 5ef67c98f..6c405ab66 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -63,7 +63,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers | [raylib_odin_bindings](https://github.com/Deathbat2190/raylib_odin_bindings) | 4.0-dev | [Odin](https://odin-lang.org) | MIT | | [raylib-ocaml](https://github.com/tjammer/raylib-ocaml) | **5.0** | [OCaml](https://ocaml.org) | MIT | | [TurboRaylib](https://github.com/turborium/TurboRaylib) | 4.5 | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT | -| [Ray4Laz](https://github.com/GuvaCode/Ray4Laz) | **5.5** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal) | Zlib | +| [Ray4Laz](https://github.com/GuvaCode/Ray4Laz) | **5.5** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)/[Delphi](https://en.wikipedia.org/wiki/Delphi_(software)) | Zlib | | [Raylib.4.0.Pascal](https://github.com/sysrpl/Raylib.4.0.Pascal) | 4.0 | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal) | Zlib | | [pyraylib](https://github.com/Ho011/pyraylib) | 3.7 | [Python](https://www.python.org) | Zlib | | [raylib-python-cffi](https://github.com/electronstudio/raylib-python-cffi) | **5.5** | [Python](https://www.python.org) | EPL-2.0 | @@ -185,4 +185,4 @@ Missing some language or wrapper? Feel free to create a new one! :) Usually, raylib bindings follow the convention: `raylib-{language}` -Let me know if you're writing a new binding for raylib, I will list it here! \ No newline at end of file +Let me know if you're writing a new binding for raylib, I will list it here! From 85de580527e96f327d474cd865afba5b68ed4f72 Mon Sep 17 00:00:00 2001 From: Max Coplan Date: Thu, 12 Feb 2026 07:15:47 -0800 Subject: [PATCH 002/319] fix(examples): don't bleed fog when on edge (#5547) Steps to reproduce: 1. play textures_fog_of_war example 2. Move player to edge of screen 3. Note the light bleeds to the other side of the screen --- examples/textures/textures_fog_of_war.c | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/textures/textures_fog_of_war.c b/examples/textures/textures_fog_of_war.c index ea98b03ce..1354d7124 100644 --- a/examples/textures/textures_fog_of_war.c +++ b/examples/textures/textures_fog_of_war.c @@ -68,6 +68,7 @@ int main(void) // at a smaller size (one pixel per tile) and scale it on drawing with bilinear filtering RenderTexture2D fogOfWar = LoadRenderTexture(map.tilesX, map.tilesY); SetTextureFilter(fogOfWar.texture, TEXTURE_FILTER_BILINEAR); + SetTextureWrap(fogOfWar.texture, TEXTURE_WRAP_CLAMP); SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- From 4e7c38ac43a23a96aec7f96ed4fef2b0cf12e981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yui=20Kinomoto=20/=20=E3=81=8D=E3=81=AE=E3=82=82=E3=81=A8?= =?UTF-8?q?=20=E7=B5=90=E8=A1=A3?= Date: Fri, 13 Feb 2026 01:04:22 +0900 Subject: [PATCH 003/319] fix SDL SetGamepadMappings (#5548) --- src/platforms/rcore_desktop_sdl.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 66a917da2..ae44728c4 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1287,7 +1287,25 @@ void OpenURL(const char *url) // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { - return SDL_GameControllerAddMapping(mappings); + const int mappingsLength = strlen(mappings); + char *buffer = (char *)RL_CALLOC(mappingsLength + 1, sizeof(char)); + memcpy(buffer, mappings, mappingsLength); + char *p = strtok(buffer, "\n"); + bool succeed = true; + + while (p != NULL) + { + if (SDL_GameControllerAddMapping(p) == -1) + { + succeed = false; + } + p = strtok(NULL, "\n"); + } + + RL_FREE(buffer); + + // To make return value is consistent with the GLFW version. + return (succeed)? 1 : 0; } // Set gamepad vibration From 070082f8c950ef83708253a64d5a5e89fdbed705 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 12 Feb 2026 18:55:40 +0100 Subject: [PATCH 004/319] REVIEWED: Comments to impersonal format --- src/platforms/rcore_android.c | 32 ++++++++++++++-------------- src/platforms/rcore_desktop_sdl.c | 18 ++++++++-------- src/platforms/rcore_desktop_win32.c | 18 ++++++++-------- src/platforms/rcore_drm.c | 14 +++++------- src/platforms/rcore_web.c | 24 ++++++++++----------- src/platforms/rcore_web_emscripten.c | 4 ++-- src/raudio.c | 4 ++-- src/rtext.c | 2 +- src/rtextures.c | 31 ++++++++++++--------------- 9 files changed, 70 insertions(+), 77 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 65236be0f..74c7b3792 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -290,8 +290,8 @@ FILE *funopen(const void *cookie, int (*readfn)(void *, char *, int), int (*writ // Module Functions Definition: Application //---------------------------------------------------------------------------------- -// To allow easier porting to android, we allow the user to define a -// main function which we call from android_main, defined by ourselves +// To allow easier porting to android, allow the user to define a +// custom main function which is called from android_main extern int main(int argc, char *argv[]); // Android main function @@ -313,7 +313,7 @@ void android_main(struct android_app *app) // Waiting for application events before complete finishing while (!app->destroyRequested) { - // Poll all events until we reach return value TIMEOUT, meaning no events left to process + // Poll all events until return value TIMEOUT is reached, meaning no events left to process while ((pollResult = ALooper_pollOnce(0, NULL, &pollEvents, (void **)&platform.source)) > ALOOPER_POLL_TIMEOUT) { if (platform.source != NULL) platform.source->process(app, platform.source); @@ -640,9 +640,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 +// NOTE: This function is only safe to use if the provided URL is safe // 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 +// Avoid calling this function with user input non-validated strings void OpenURL(const char *url) { // Security check to (partially) avoid malicious code @@ -757,7 +757,7 @@ void PollInputEvents(void) int pollResult = 0; int pollEvents = 0; - // Poll Events (registered events) until we reach TIMEOUT which indicates there are no events left to poll + // Poll Events (registered events) until TIMEOUT is reached which indicates there are no events left to poll // NOTE: Activity is paused if not enabled (platform.appEnabled) and always run flag is not set (FLAG_WINDOW_ALWAYS_RUN) while ((pollResult = ALooper_pollOnce((platform.appEnabled || FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN))? 0 : -1, NULL, &pollEvents, ((void **)&platform.source)) > ALOOPER_POLL_TIMEOUT)) { @@ -843,7 +843,7 @@ int InitPlatform(void) // Wait for window to be initialized (display and context) while (!CORE.Window.ready) { - // Process events until we reach TIMEOUT, which indicates no more events queued + // Process events until TIMEOUT is reached, which indicates no more events queued while ((pollResult = ALooper_pollOnce(0, NULL, &pollEvents, ((void **)&platform.source)) > ALOOPER_POLL_TIMEOUT)) { // Process this event @@ -964,10 +964,10 @@ static int InitGraphicsDevice(void) EGLint displayFormat = 0; // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry() - // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID + // As soon as an EGLConfig is picked, it's safe to reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat); - // At this point we need to manage render size vs screen size + // At this point render size vs screen size needs to be managed // NOTE: This function use and modify global module variables: // -> CORE.Window.screen.width/CORE.Window.screen.height // -> CORE.Window.render.width/CORE.Window.render.height @@ -1075,12 +1075,12 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) Rectangle rec = GetFontDefault().recs[95]; if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) { - // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering + // NOTE: Trying to maxime rec padding to avoid pixel bleeding on MSAA filtering SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); } else { - // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding + // NOTE: Setting up a 1px padding on char rectangle to avoid pixel bleeding SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); } #endif @@ -1450,7 +1450,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) // NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified static void SetupFramebuffer(int width, int height) { - // Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var) + // Calculate CORE.Window.render.width and CORE.Window.render.height, having the display size (input params) and the desired screen size (global var) if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height)) { TRACELOG(LOG_WARNING, "DISPLAY: Downscaling required: Screen size (%ix%i) is bigger than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height); @@ -1478,8 +1478,8 @@ static void SetupFramebuffer(int width, int height) float scaleRatio = (float)CORE.Window.render.width/(float)CORE.Window.screen.width; CORE.Window.screenScale = MatrixScale(scaleRatio, scaleRatio, 1.0f); - // NOTE: We render to full display resolution! - // We just need to calculate above parameters for downscale matrix and offsets + // NOTE: Rendering to full display resolution + // Above parameters need to be calculate for downscale matrix and offsets CORE.Window.render.width = CORE.Window.display.width; CORE.Window.render.height = CORE.Window.display.height; @@ -1533,8 +1533,8 @@ FILE *android_fopen(const char *fileName, const char *mode) if (mode[0] == 'w') { // NOTE: fopen() is mapped to android_fopen() that only grants read access to - // assets directory through AAssetManager but we want to also be able to - // write data when required using the standard stdio FILE access functions + // assets directory through AAssetManager but it could be required to write data + // using the standard stdio FILE access functions // REF: https://stackoverflow.com/questions/11294487/android-writing-saving-files-from-native-code-only #undef fopen file = fopen(TextFormat("%s/%s", platform.app->activity->internalDataPath, fileName), mode); diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index ae44728c4..ca49e6a8e 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -255,9 +255,9 @@ static const int CursorsLUT[] = { // SDL3 Migration: // SDL_WINDOW_FULLSCREEN_DESKTOP has been removed, -// and you can call SDL_GetWindowFullscreenMode() +// SDL_GetWindowFullscreenMode() can be called // to see whether an exclusive fullscreen mode will be used -// or the borderless fullscreen desktop mode will be used +// or the borderless fullscreen desktop mode #define SDL_WINDOW_FULLSCREEN_DESKTOP SDL_WINDOW_FULLSCREEN #define SDL_IGNORE false @@ -1269,9 +1269,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 +// NOTE: This function is only safe to use if the provided URL is safe // 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 +// Avoid calling this function with user input non-validated strings // REF: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { @@ -1437,9 +1437,9 @@ void PollInputEvents(void) #if defined(USING_VERSION_SDL3) // const char *data; // The text for SDL_EVENT_DROP_TEXT and the file name for SDL_EVENT_DROP_FILE, NULL for other events - // Event memory is now managed by SDL, so you should not free the data in SDL_EVENT_DROP_FILE, - // and if you want to hold onto the text in SDL_EVENT_TEXT_EDITING and SDL_EVENT_TEXT_INPUT events, - // you should make a copy of it. SDL_TEXTINPUTEVENT_TEXT_SIZE is no longer necessary and has been removed + // Event memory is now managed by SDL, so it should not be freed in SDL_EVENT_DROP_FILE, + // in case data needs to be hold onto the text in SDL_EVENT_TEXT_EDITING and SDL_EVENT_TEXT_INPUT events, + // a copy is required, SDL_TEXTINPUTEVENT_TEXT_SIZE is no longer necessary and has been removed strncpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.data, MAX_FILEPATH_LENGTH - 1); #else strncpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file, MAX_FILEPATH_LENGTH - 1); @@ -1468,10 +1468,10 @@ void PollInputEvents(void) // Window events are also polled (minimized, maximized, close...) #ifndef USING_VERSION_SDL3 - // SDL3 states: // The SDL_WINDOWEVENT_* events have been moved to top level events, and SDL_WINDOWEVENT has been removed // In general, handling this change just means checking for the individual events instead of first checking for SDL_WINDOWEVENT - // and then checking for window events. You can compare the event >= SDL_EVENT_WINDOW_FIRST and <= SDL_EVENT_WINDOW_LAST if you need to see whether it's a window event + // and then checking for window events; Events >= SDL_EVENT_WINDOW_FIRST and <= SDL_EVENT_WINDOW_LAST can be compared + // to see whether it's a window event case SDL_WINDOWEVENT: { switch (event.window.event) diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index 17d4fc530..8cb9645d2 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -1244,9 +1244,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 +// NOTE: This function is only safe to use if the provided URL is safe // 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 +// Avoid calling this function with user input non-validated strings // REF: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { @@ -1864,9 +1864,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara } break; case WM_SIZE: { - // WARNING: Don't trust the docs, they say you won't get this message if you don't call DefWindowProc - // in response to WM_WINDOWPOSCHANGED but looks like when a window is created you'll get this - // message without getting WM_WINDOWPOSCHANGED + // WARNING: Don't trust the docs, they say this message can not be obtained if not calling DefWindowProc() + // in response to WM_WINDOWPOSCHANGED but looks like when a window is created, + // this message can be obtained without getting WM_WINDOWPOSCHANGED HandleWindowResize(hwnd, &platform.appScreenWidth, &platform.appScreenHeight); } break; //case WM_MOVE @@ -2187,10 +2187,10 @@ static unsigned SanitizeFlags(int mode, unsigned flags) // window. This function will continue to perform these update operations so long as // the state continues to change // -// This design takes care of many odd corner cases. For example, if you want to restore -// a window that was previously maximized AND minimized and you want to remove both these -// flags, you actually need to call ShowWindow with SW_RESTORE twice. Another example is -// if you have a maximized window, if the undecorated flag is modified then the window style +// This design takes care of many odd corner cases. For example, in case of restoring +// a window that was previously maximized AND minimized and those two flags need to be removed, +// ShowWindow with SW_RESTORE twice need to bee actually calleed. Another example is +// wheen having a maximized window, if the undecorated flag is modified then the window style // needs to be updated, but updating the style would mean the window size would change // causing the window to lose its Maximized state which would mean the window size // needs to be updated, followed by the update of window style, a second time, to restore that maximized diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 6d3394f69..85db9b4e1 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -1017,10 +1017,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 +// NOTE: This function is only safe to use if the provided URL is safe // 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 +// Avoid calling this function with user input non-validated strings void OpenURL(const char *url) { TRACELOG(LOG_WARNING, "OpenURL() not implemented on target platform"); @@ -2149,14 +2148,11 @@ static void ConfigureEvdevDevice(char *device) if (absAxisCount > 0) { - // TODO / NOTE + // TODO: Review GamepadAxis enum matching // 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 - // If these are now enumerated you get LJOY_X, LJOY_Y, LEFT_SHOULDERB, RJOY_X, ... - // That means they don't match the GamepadAxis enum - // This could be fixed + // ABS_X, ABX_Y for one joystick ABS_RX, ABS_RY for the other and the Z axes for the shoulder buttons + // If these are now enumerated, it results to LJOY_X, LJOY_Y, LEFT_SHOULDERB, RJOY_X, ... int axisIndex = 0; for (int axis = ABS_X; axis < ABS_PRESSURE; axis++) { diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 3dd3eb9df..5430cf5a9 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -173,7 +173,7 @@ bool WindowShouldClose(void) // 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, + // Optionally, time to give-control-back-to-browser can be managed here, // but it seems below line could generate stuttering on some browsers emscripten_sleep(12); @@ -921,9 +921,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 +// NOTE: This function is only safe to use if the provided URL is safe // 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 +// Avoid calling this function with user input non-validated strings void OpenURL(const char *url) { // Security check to (partially) avoid malicious code on target platform @@ -1252,11 +1252,11 @@ int InitPlatform(void) #else if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) { - // remember center for switchinging from fullscreen to window + // Remember center for switchinging from fullscreen to window if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) { - // If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed - // Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11 + // If screen width/height equal to the display, it's not possible to + // calculate the window position for toggling full-screened/windowed CORE.Window.position.x = CORE.Window.display.width/4; CORE.Window.position.y = CORE.Window.display.height/4; } @@ -1367,7 +1367,7 @@ int InitPlatform(void) if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) MinimizeWindow(); - // If graphic device is no properly initialized, we end program + // If graphic device is no properly initialized, end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } // Load OpenGL extensions @@ -1491,7 +1491,7 @@ static void WindowDropCallback(GLFWwindow *window, int count, const char **paths { if (count > 0) { - // In case previous dropped filepaths have not been freed, we free them + // In case previous dropped filepaths have not been freed, free them if (CORE.Window.dropFileCount > 0) { for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilepaths[i]); @@ -1502,7 +1502,7 @@ static void WindowDropCallback(GLFWwindow *window, int count, const char **paths CORE.Window.dropFilepaths = NULL; } - // WARNING: Paths are freed by GLFW when the callback returns, we must keep an internal copy + // WARNING: Paths are freed by GLFW when the callback returns, an internal copy must freed CORE.Window.dropFileCount = count; CORE.Window.dropFilepaths = (char **)RL_CALLOC(CORE.Window.dropFileCount, sizeof(char *)); @@ -1519,7 +1519,7 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i { if (key < 0) return; // Security check, macOS fn key generates -1 - // WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1 + // WARNING: GLFW could return GLFW_REPEAT, it needs to be considered 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; @@ -1721,7 +1721,7 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent double canvasWidth = 0.0; double canvasHeight = 0.0; // NOTE: emscripten_get_canvas_element_size() returns canvas.width and canvas.height but - // we are looking for actual CSS size: canvas.style.width and canvas.style.height + // actual CSS size needs to be considered: canvas.style.width and canvas.style.height // EMSCRIPTEN_RESULT res = emscripten_get_canvas_element_size("#canvas", &canvasWidth, &canvasHeight); emscripten_get_element_css_size(platform.canvasId, &canvasWidth, &canvasHeight); @@ -1741,7 +1741,7 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) CORE.Input.Touch.currentTouchState[i] = 0; } - // Update mouse position if we detect a single touch + // Update mouse position when single touch detected if (CORE.Input.Touch.pointCount == 1) { CORE.Input.Mouse.currentPosition.x = CORE.Input.Touch.position[0].x; diff --git a/src/platforms/rcore_web_emscripten.c b/src/platforms/rcore_web_emscripten.c index 92caae99f..13e685939 100644 --- a/src/platforms/rcore_web_emscripten.c +++ b/src/platforms/rcore_web_emscripten.c @@ -908,9 +908,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 +// NOTE: This function is only safe to use if the provided URL is safe // 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 +// Avoid calling this function with user input non-validated strings void OpenURL(const char *url) { // Security check to (partially) avoid malicious code on target platform diff --git a/src/raudio.c b/src/raudio.c index 2e087205e..75547d285 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -2015,7 +2015,7 @@ void UpdateMusicStream(Music music) #if defined(SUPPORT_FILEFORMAT_MOD) case MUSIC_MODULE_MOD: { - // NOTE: 3rd parameter (nbsample) specify the number of stereo 16bits samples you want, so sampleCount/2 + // NOTE: 3rd parameter (nbsample) specify the number of stereo 16bits samples desired, so sampleCount/2 jar_mod_fillbuffer((jar_mod_context_t *)music.ctxData, (short *)AUDIO.System.pcmBuffer, framesToStream, 0); //jar_mod_seek_start((jar_mod_context_t *)music.ctxData); @@ -2504,7 +2504,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const memset(pFramesOut, 0, frameCount*pDevice->playback.channels*ma_get_bytes_per_sample(pDevice->playback.format)); // Using a mutex here for thread-safety which makes things not real-time - // This is unlikely to be necessary for this project, but may want to consider how you might want to avoid this + // This is unlikely to be necessary for this project, but it can be reconsidered ma_mutex_lock(&AUDIO.System.lock); { for (AudioBuffer *audioBuffer = AUDIO.Buffer.first; audioBuffer != NULL; audioBuffer = audioBuffer->next) diff --git a/src/rtext.c b/src/rtext.c index 2c871cc49..8e7fafec2 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -682,7 +682,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz // Render a unicode codepoint to a bitmap // stbtt_GetCodepointBitmap() -- allocates and returns a bitmap // stbtt_GetCodepointBitmapBox() -- how big the bitmap must be - // stbtt_MakeCodepointBitmap() -- renders into bitmap you provide + // stbtt_MakeCodepointBitmap() -- renders into a provided bitmap // Check if a glyph is available in the font // WARNING: if (index == 0), glyph not found, it could fallback to default .notdef glyph (if defined in font) diff --git a/src/rtextures.c b/src/rtextures.c index 1aced0533..c49ca1359 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -883,7 +883,7 @@ Image GenImageGradientRadial(int width, int height, float density, Color inner, float factor = (dist - radius*density)/(radius*(1.0f - density)); factor = (float)fmax(factor, 0.0f); - factor = (float)fmin(factor, 1.f); // dist can be bigger than radius, so we have to check + factor = (float)fmin(factor, 1.f); // Distance can be bigger than radius, so it needs to be checked pixels[y*width + x].r = (int)((float)outer.r*factor + (float)inner.r*(1.0f - factor)); pixels[y*width + x].g = (int)((float)outer.g*factor + (float)inner.g*(1.0f - factor)); @@ -1032,7 +1032,7 @@ Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float if (p < -1.0f) p = -1.0f; if (p > 1.0f) p = 1.0f; - // We need to normalize the data from [-1..1] to [0..1] + // Data needs to be normalized from [-1..1] to [0..1] float np = (p + 1.0f)/2.0f; unsigned char intensity = (unsigned char)(np*255.0f); @@ -1264,7 +1264,7 @@ void ImageFormat(Image *image, int newFormat) { Vector4 *pixels = LoadImageDataNormalized(*image); // Supports 8 to 32 bit per channel - RL_FREE(image->data); // WARNING! We loose mipmaps data --> Regenerated at the end... + RL_FREE(image->data); // WARNING! Loosing mipmaps data --> Regenerated at the end image->data = NULL; image->format = newFormat; @@ -1759,7 +1759,7 @@ void ImageResize(Image *image, int newWidth, int newHeight) // Security check to avoid program crash if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; - // Check if we can use a fast path on image scaling + // Check if a fast path can be used on image scaling // It can be for 8 bit per channel images with 1 to 4 channels per pixel if ((image->format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) || (image->format == PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) || @@ -2026,7 +2026,7 @@ void ImageAlphaMask(Image *image, Image alphaMask) Image mask = ImageCopy(alphaMask); if (mask.format != PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) ImageFormat(&mask, PIXELFORMAT_UNCOMPRESSED_GRAYSCALE); - // In case image is only grayscale, we just add alpha channel + // In case image is only grayscale, just add alpha channel if (image->format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) { unsigned char *data = (unsigned char *)RL_MALLOC(image->width*image->height*2); @@ -2479,7 +2479,7 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp) TRACELOG(LOG_WARNING, "IMAGE: Unsupported dithered OpenGL internal format: %ibpp (R%iG%iB%iA%i)", (rBpp+gBpp+bBpp+aBpp), rBpp, gBpp, bBpp, aBpp); } - // NOTE: We will store the dithered data as unsigned short (16bpp) + // NOTE: Storing the dithered data as unsigned short (16bpp) image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short)); Color oldPixel = WHITE; @@ -2507,8 +2507,8 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp) newPixel.b = oldPixel.b >> (8 - bBpp); // B bits newPixel.a = oldPixel.a >> (8 - aBpp); // A bits (not used on dithering) - // NOTE: Error must be computed between new and old pixel but using same number of bits! - // We want to know how much color precision we have lost... + // NOTE: Error must be computed between new and old pixel but using same number of bits, + // to know how much color precision has been lost rError = (int)oldPixel.r - (int)(newPixel.r << (8 - rBpp)); gError = (int)oldPixel.g - (int)(newPixel.g << (8 - gBpp)); bError = (int)oldPixel.b - (int)(newPixel.b << (8 - bBpp)); @@ -3134,7 +3134,7 @@ Color *LoadImagePalette(Image image, int maxPaletteSize, int *colorCount) palette[palCount] = pixels[i]; // Add pixels[i] to palette palCount++; - // We reached the limit of colors supported by palette + // Reached the limit of colors supported by palette if (palCount >= maxPaletteSize) { i = image.width*image.height; // Finish palette get @@ -3806,7 +3806,7 @@ void ImageDrawTriangle(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color col for (int x = xMin; x <= xMax; x++) { // Check if the pixel is inside the triangle using barycentric coordinates - // If it is then we can draw the pixel with the given color + // If it is, the pixel can be drawn with the given color if ((w1 | w2 | w3) >= 0) ImageDrawPixel(dst, x, y, color); // Increment the barycentric coordinates for the next pixel @@ -3863,9 +3863,6 @@ void ImageDrawTriangleEx(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color c int w3Row = (int)((xMin - v1.x)*w3XStep + w3YStep*(yMin - v1.y)); // Calculate the inverse of the sum of the barycentric coordinates for normalization - // NOTE 1: Here, we act as if we multiply by 255 the reciprocal, which avoids additional - // calculations in the loop. This is acceptable because we are only interpolating colors - // NOTE 2: This sum remains constant throughout the triangle float wInvSum = 255.0f/(w1Row + w2Row + w3Row); // Rasterization loop @@ -3965,11 +3962,11 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color if ((srcRec.y + srcRec.height) > src.height) srcRec.height = src.height - srcRec.y; // Check if source rectangle needs to be resized to destination rectangle - // In that case, we make a copy of source, and we apply all required transform + // In that case, make a copy of source, and apply all required transform if (((int)srcRec.width != (int)dstRec.width) || ((int)srcRec.height != (int)dstRec.height)) { - srcMod = ImageFromImage(src, srcRec); // Create image from another image - ImageResize(&srcMod, (int)dstRec.width, (int)dstRec.height); // Resize to destination rectangle + srcMod = ImageFromImage(src, srcRec); // Create image from another image + ImageResize(&srcMod, (int)dstRec.width, (int)dstRec.height); // Resize to destination rectangle srcRec = (Rectangle){ 0, 0, (float)srcMod.width, (float)srcMod.height }; srcPtr = &srcMod; @@ -5126,7 +5123,7 @@ Color ColorAlphaBlend(Color dst, Color src, Color tint) else if (src.a == 255) out = src; else { - unsigned int alpha = (unsigned int)src.a + 1; // We are shifting by 8 (dividing by 256), so we need to take that excess into account + unsigned int alpha = (unsigned int)src.a + 1; // Shifting by 8 (dividing by 256), so need to take that excess into account out.a = (unsigned char)(((unsigned int)alpha*256 + (unsigned int)dst.a*(256 - alpha)) >> 8); if (out.a > 0) From dcd813068b832ae7536eea523fec9c980440098f Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 12 Feb 2026 18:55:42 +0100 Subject: [PATCH 005/319] Update raylib.h --- src/raylib.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index b4b175919..f9c504680 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -350,8 +350,8 @@ typedef struct Mesh { float *texcoords2; // Vertex texture second coordinates (UV - 2 components per vertex) (shader-location = 5) float *normals; // Vertex normals (XYZ - 3 components per vertex) (shader-location = 2) float *tangents; // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4) - unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) - unsigned short *indices; // Vertex indices (in case vertex data comes indexed) + unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) + unsigned short *indices; // Vertex indices (in case vertex data comes indexed) // Animation vertex data float *animVertices; // Animated vertex positions (after bones transformations) From 8e81ca0e60e2ce5f76b8fc3ddee4b4212f032ec2 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 12 Feb 2026 19:08:48 +0100 Subject: [PATCH 006/319] Update win32_clipboard.h --- src/external/win32_clipboard.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/external/win32_clipboard.h b/src/external/win32_clipboard.h index 6845b0c2e..1cb05457d 100644 --- a/src/external/win32_clipboard.h +++ b/src/external/win32_clipboard.h @@ -13,7 +13,7 @@ unsigned char *Win32GetClipboardImageData(int *width, int *height, unsigned long #include #include -// NOTE: These search for architecture is taken from "Windows.h", and it's necessary if we really don't wanna import windows.h +// NOTE: These search for architecture is taken from "windows.h", and it's necessary to avoid including windows.h // and still make it compile on msvc, because import indirectly importing "winnt.h" (e.g. ) can cause problems is these are not defined. #if !defined(_X86_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_IX86) #define _X86_ @@ -306,16 +306,15 @@ static int GetPixelDataOffset(BITMAPINFOHEADER bih) const unsigned int rgbaSize = sizeof(RGBQUAD); // NOTE: biSize specifies the number of bytes required by the structure - // We expect to always be 40 because it should be packed + // It's expected to be always 40 because it should be packed if ((bih.biSize == 40) && (sizeof(BITMAPINFOHEADER) == 40)) { // NOTE: biBitCount specifies the number of bits per pixel // Might exist some bit masks *after* the header and *before* the pixel offset - // we're looking, but only if we have more than - // 8 bits per pixel, so we need to ajust for that + // we're looking, but only if more than 8 bits per pixel, so it needs to be ajusted for that if (bih.biBitCount > 8) { - // If (bih.biCompression == BI_RGB) we should NOT offset more + // If (bih.biCompression == BI_RGB) no need to be offset more if (bih.biCompression == BI_BITFIELDS) offset += 3*rgbaSize; else if (bih.biCompression == BI_ALPHABITFIELDS) offset += 4*rgbaSize; // Not widely supported, but valid From debbb90479d70335c2999df82c8fbd61fcf33e0c Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Sat, 14 Feb 2026 15:09:52 -0600 Subject: [PATCH 007/319] fix extra drawtext() (#5551) --- examples/core/core_random_sequence.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/core/core_random_sequence.c b/examples/core/core_random_sequence.c index 580c2bb26..19d44461e 100644 --- a/examples/core/core_random_sequence.c +++ b/examples/core/core_random_sequence.c @@ -96,8 +96,6 @@ int main(void) { DrawRectangleRec(rectangles[i].rect, rectangles[i].color); - DrawText("Press SPACE to shuffle the sequence", 10, screenHeight - 96, 20, BLACK); - DrawText("Press SPACE to shuffle the current sequence", 10, screenHeight - 96, 20, BLACK); DrawText("Press UP to add a rectangle and generate a new sequence", 10, screenHeight - 64, 20, BLACK); DrawText("Press DOWN to remove a rectangle and generate a new sequence", 10, screenHeight - 32, 20, BLACK); From 8f1421ee5dec1b9e7ad7ea081ac3ed6b36102510 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Sat, 14 Feb 2026 15:10:31 -0600 Subject: [PATCH 008/319] fix wrong name (#5552) --- examples/core/core_input_actions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/core/core_input_actions.c b/examples/core/core_input_actions.c index f4b1156d8..d5fd74e84 100644 --- a/examples/core/core_input_actions.c +++ b/examples/core/core_input_actions.c @@ -117,7 +117,7 @@ int main(void) DrawRectangleV(position, size, releaseAction? BLUE : RED); - DrawText((actionSet == 0)? "Current input set: WASD (default)" : "Current input set: Cursor", 10, 10, 20, WHITE); + DrawText((actionSet == 0)? "Current input set: WASD (default)" : "Current input set: Arrow keys", 10, 10, 20, WHITE); DrawText("Use TAB key to toggles Actions keyset", 10, 50, 20, GREEN); EndDrawing(); From a78d575f752bc26338fce9fffc243e5983da8a8c Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Sat, 14 Feb 2026 15:11:18 -0600 Subject: [PATCH 009/319] change attenuation distance (#5555) --- examples/audio/audio_sound_positioning.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/audio/audio_sound_positioning.c b/examples/audio/audio_sound_positioning.c index 34b15c07b..41ea4cb74 100644 --- a/examples/audio/audio_sound_positioning.c +++ b/examples/audio/audio_sound_positioning.c @@ -68,7 +68,7 @@ int main(void) .z = 5.0f*sinf(th) }; - SetSoundPosition(camera, sound, spherePos, 20.0f); + SetSoundPosition(camera, sound, spherePos, 1.0f); if (!IsSoundPlaying(sound)) PlaySound(sound); //---------------------------------------------------------------------------------- From fd40d2b374c29feaa1aca956d68460fd28ef7cb9 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Sat, 14 Feb 2026 15:11:47 -0600 Subject: [PATCH 010/319] fix missing alias for PS (#5559) --- examples/core/core_input_gamepad.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/core/core_input_gamepad.c b/examples/core/core_input_gamepad.c index a9e0660e0..7291c6d78 100644 --- a/examples/core/core_input_gamepad.c +++ b/examples/core/core_input_gamepad.c @@ -24,7 +24,8 @@ // NOTE: Gamepad name ID depends on drivers and OS #define XBOX_ALIAS_1 "xbox" #define XBOX_ALIAS_2 "x-box" -#define PS_ALIAS "playstation" +#define PS_ALIAS_1 "playstation" +#define PS_ALIAS_2 "sony" //------------------------------------------------------------------------------------ // Program main entry point @@ -148,7 +149,8 @@ int main(void) //DrawText(TextFormat("Xbox axis LT: %02.02f", GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_TRIGGER)), 10, 40, 10, BLACK); //DrawText(TextFormat("Xbox axis RT: %02.02f", GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_TRIGGER)), 10, 60, 10, BLACK); } - else if (TextFindIndex(TextToLower(GetGamepadName(gamepad)), PS_ALIAS) > -1) + else if ((TextFindIndex(TextToLower(GetGamepadName(gamepad)), PS_ALIAS_1) > -1) || + (TextFindIndex(TextToLower(GetGamepadName(gamepad)), PS_ALIAS_2) > -1)) { DrawTexture(texPs3Pad, 0, 0, DARKGRAY); From 4d6ef19fcc53cd2459ce53cdcf791454c7c201d1 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Sat, 14 Feb 2026 15:12:11 -0600 Subject: [PATCH 011/319] change on-screen text (#5553) --- examples/core/core_2d_camera_platformer.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/core/core_2d_camera_platformer.c b/examples/core/core_2d_camera_platformer.c index 49d0a940c..8f443acba 100644 --- a/examples/core/core_2d_camera_platformer.c +++ b/examples/core/core_2d_camera_platformer.c @@ -148,10 +148,11 @@ int main(void) DrawText("Controls:", 20, 20, 10, BLACK); DrawText("- Right/Left to move", 40, 40, 10, DARKGRAY); DrawText("- Space to jump", 40, 60, 10, DARKGRAY); - DrawText("- Mouse Wheel to Zoom in-out, R to reset zoom", 40, 80, 10, DARKGRAY); - DrawText("- C to change camera mode", 40, 100, 10, DARKGRAY); - DrawText("Current camera mode:", 20, 120, 10, BLACK); - DrawText(cameraDescriptions[cameraOption], 40, 140, 10, DARKGRAY); + DrawText("- Mouse Wheel to Zoom in-out", 40, 80, 10, DARKGRAY); + DrawText("- R to reset position + zoom", 40, 100, 10, DARKGRAY); + DrawText("- C to change camera mode", 40, 120, 10, DARKGRAY); + DrawText("Current camera mode:", 20, 140, 10, BLACK); + DrawText(cameraDescriptions[cameraOption], 40, 160, 10, DARKGRAY); EndDrawing(); //---------------------------------------------------------------------------------- From b210d165978b9b29651f99aa2770c323d21cfe44 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Sat, 14 Feb 2026 15:12:44 -0600 Subject: [PATCH 012/319] fix y-offset casting (#5556) --- examples/shapes/shapes_colors_palette.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shapes/shapes_colors_palette.c b/examples/shapes/shapes_colors_palette.c index 44da323eb..aa287df53 100644 --- a/examples/shapes/shapes_colors_palette.c +++ b/examples/shapes/shapes_colors_palette.c @@ -45,7 +45,7 @@ int main(void) for (int i = 0; i < MAX_COLORS_COUNT; i++) { colorsRecs[i].x = 20.0f + 100.0f *(i%7) + 10.0f *(i%7); - colorsRecs[i].y = 80.0f + 100.0f *((float)i/7) + 10.0f *((float)i/7); + colorsRecs[i].y = 80.0f + 100.0f *((int)i/7) + 10.0f *((float)i/7); colorsRecs[i].width = 100.0f; colorsRecs[i].height = 100.0f; } From b04d2a22689cbd53baabac7919462b46104b3791 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Sat, 14 Feb 2026 15:13:22 -0600 Subject: [PATCH 013/319] change d-pad text to shapes (#5557) --- examples/core/core_input_virtual_controls.c | 39 ++++++++++++++++----- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/examples/core/core_input_virtual_controls.c b/examples/core/core_input_virtual_controls.c index db293b993..de4ac7ead 100644 --- a/examples/core/core_input_virtual_controls.c +++ b/examples/core/core_input_virtual_controls.c @@ -51,11 +51,31 @@ int main(void) { padPosition.x, padPosition.y + buttonRadius*1.5f } // Down }; - const char *buttonLabels[BUTTON_MAX] = { - "Y", // Up - "X", // Left - "B", // Right - "A" // Down + Vector2 arrowTris[4][3] = { + // Up + { + { buttonPositions[0].x, buttonPositions[0].y - 12 }, + { buttonPositions[0].x - 9, buttonPositions[0].y + 9 }, + { buttonPositions[0].x + 9, buttonPositions[0].y + 9 } + }, + // Left + { + { buttonPositions[1].x + 9, buttonPositions[1].y - 9 }, + { buttonPositions[1].x - 12, buttonPositions[1].y }, + { buttonPositions[1].x + 9, buttonPositions[1].y + 9 } + }, + // Right + { + { buttonPositions[2].x + 12, buttonPositions[2].y }, + { buttonPositions[2].x - 9, buttonPositions[2].y - 9 }, + { buttonPositions[2].x - 9, buttonPositions[2].y + 9 } + }, + // Down + { + { buttonPositions[3].x - 9, buttonPositions[3].y - 9 }, + { buttonPositions[3].x, buttonPositions[3].y + 12 }, + { buttonPositions[3].x + 9, buttonPositions[3].y - 9 } + } }; Color buttonLabelColors[BUTTON_MAX] = { @@ -128,9 +148,12 @@ int main(void) { DrawCircleV(buttonPositions[i], buttonRadius, (i == pressedButton)? DARKGRAY : BLACK); - DrawText(buttonLabels[i], - (int)buttonPositions[i].x - 7, (int)buttonPositions[i].y - 8, - 20, buttonLabelColors[i]); + DrawTriangle( + arrowTris[i][0], + arrowTris[i][1], + arrowTris[i][2], + buttonLabelColors[i] + ); } DrawText("move the player with D-Pad buttons", 10, 10, 20, DARKGRAY); From fb5bc42190cceb38e51066a399f10fc5217d4773 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Sat, 14 Feb 2026 15:16:09 -0600 Subject: [PATCH 014/319] update camera pan speed (#5554) --- src/rcamera.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcamera.h b/src/rcamera.h index 82f14fecd..3c880bab7 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -199,7 +199,7 @@ RLAPI Matrix GetCameraProjectionMatrix(Camera *camera, float aspect); //---------------------------------------------------------------------------------- #define CAMERA_MOVE_SPEED 5.4f // Units per second #define CAMERA_ROTATION_SPEED 0.03f -#define CAMERA_PAN_SPEED 0.2f +#define CAMERA_PAN_SPEED 2.0f // Camera mouse movement sensitivity #define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f From 1061daf197d1984c913af22ba57c419d5e604d49 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 14 Feb 2026 22:17:49 +0100 Subject: [PATCH 015/319] REVIEWED: Installed libraries #5550 --- src/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Makefile b/src/Makefile index 459b79f83..b4ea8f0a9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -841,6 +841,7 @@ ifeq ($(ROOT),root) # Copying raylib development files to $(RAYLIB_H_INSTALL_PATH). cp --update raylib.h $(RAYLIB_H_INSTALL_PATH)/raylib.h cp --update raymath.h $(RAYLIB_H_INSTALL_PATH)/raymath.h + cp --update rcamera.h $(RAYLIB_H_INSTALL_PATH)/rcamera.h cp --update rlgl.h $(RAYLIB_H_INSTALL_PATH)/rlgl.h @echo "raylib development files installed/updated!" else From 180c3c13ba3ab6ca10b43ced8464aed0db7df566 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 14 Feb 2026 22:22:31 +0100 Subject: [PATCH 016/319] REVIEWED: `GetImageColor()` #5560 --- src/rtextures.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index c49ca1359..1b147796e 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3295,9 +3295,9 @@ Color GetImageColor(Image image, int x, int y) case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: { color.r = (unsigned char)(((float *)image.data)[(y*image.width + x)*4]*255.0f); - color.g = (unsigned char)(((float *)image.data)[(y*image.width + x)*4]*255.0f); - color.b = (unsigned char)(((float *)image.data)[(y*image.width + x)*4]*255.0f); - color.a = (unsigned char)(((float *)image.data)[(y*image.width + x)*4]*255.0f); + color.g = (unsigned char)(((float *)image.data)[(y*image.width + x)*4 + 1]*255.0f); + color.b = (unsigned char)(((float *)image.data)[(y*image.width + x)*4 + 2]*255.0f); + color.a = (unsigned char)(((float *)image.data)[(y*image.width + x)*4 + 3]*255.0f); } break; case PIXELFORMAT_UNCOMPRESSED_R16: @@ -3319,9 +3319,9 @@ Color GetImageColor(Image image, int x, int y) case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: { color.r = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*4])*255.0f); - color.g = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*4])*255.0f); - color.b = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*4])*255.0f); - color.a = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*4])*255.0f); + color.g = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*4 + 1])*255.0f); + color.b = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*4 + 2])*255.0f); + color.a = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*4 + 3])*255.0f); } break; default: TRACELOG(LOG_WARNING, "Compressed image format does not support color reading"); break; From d01f158bd54d70458ee6e40e78154f04d6d45502 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 15 Feb 2026 13:21:08 +0100 Subject: [PATCH 017/319] REVIEWED: Window initialization on HighDPI monitor (Windows) #5549 --- examples/core/core_highdpi_testbed.c | 2 +- src/platforms/rcore_desktop_glfw.c | 29 +++++++++++++++------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/examples/core/core_highdpi_testbed.c b/examples/core/core_highdpi_testbed.c index a925527db..1c39a9d2c 100644 --- a/examples/core/core_highdpi_testbed.c +++ b/examples/core/core_highdpi_testbed.c @@ -78,7 +78,7 @@ int main(void) DrawText(TextFormat("WINDOW POSITION: %ix%i", (int)windowPos.x, (int)windowPos.y), 50, 90, 20, DARKGRAY); DrawText(TextFormat("SCREEN SIZE: %ix%i", GetScreenWidth(), GetScreenHeight()), 50, 130, 20, DARKGRAY); DrawText(TextFormat("RENDER SIZE: %ix%i", GetRenderWidth(), GetRenderHeight()), 50, 170, 20, DARKGRAY); - DrawText(TextFormat("SCALE FACTOR: %.1fx%.1f", scaleDpi.x, scaleDpi.y), 50, 210, 20, GRAY); + DrawText(TextFormat("SCALE FACTOR: %.2fx%.2f", scaleDpi.x, scaleDpi.y), 50, 210, 20, GRAY); // Draw reference rectangles, top-left and bottom-right corners DrawRectangle(0, 0, 30, 60, RED); diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index b13018576..42b5a002f 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1649,31 +1649,34 @@ int InitPlatform(void) TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC"); } - int fbWidth = CORE.Window.screen.width; - int fbHeight = CORE.Window.screen.height; - if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) { + // Set screen size to logical pixel size, considering content scaling + Vector2 scaleDpi = GetWindowScaleDPI(); + CORE.Window.render.width = (int)(CORE.Window.screen.width*scaleDpi.x); + CORE.Window.render.height = (int)(CORE.Window.screen.height*scaleDpi.y); + // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling // Framebuffer scaling is activated with: glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE); - // Get current framebuffer size, on high-dpi it could be bigger than screen size - glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight); + //glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight); // Screen scaling matrix is required in case desired screen area is different from display area - CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f); + CORE.Window.screenScale = MatrixScale(scaleDpi.x, scaleDpi.y, 1.0f); #if !defined(__APPLE__) // Mouse input scaling for the new screen size - SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight); + SetMouseScale(1.0f/scaleDpi.x, 1.0f/scaleDpi.y); #endif + glfwSetWindowSize(platform.handle, CORE.Window.render.width, CORE.Window.render.height); + } + else + { + CORE.Window.render = CORE.Window.screen; + CORE.Window.currentFbo = CORE.Window.render; } - CORE.Window.render.width = fbWidth; - CORE.Window.render.height = fbHeight; - CORE.Window.currentFbo.width = fbWidth; - CORE.Window.currentFbo.height = fbHeight; - - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully %s", + FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)? "(HighDPI)" : ""); TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); From 059ebaa6ada3228e2334c68b9b7adee3aa8f4a10 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 15 Feb 2026 15:02:04 +0100 Subject: [PATCH 018/319] REVIEWED: HIghDPI content scaling on macOS --- src/platforms/rcore_desktop_glfw.c | 57 +++++++++++++++++------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 42b5a002f..68a321ffd 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1655,25 +1655,29 @@ int InitPlatform(void) Vector2 scaleDpi = GetWindowScaleDPI(); CORE.Window.render.width = (int)(CORE.Window.screen.width*scaleDpi.x); CORE.Window.render.height = (int)(CORE.Window.screen.height*scaleDpi.y); - - // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling - // Framebuffer scaling is activated with: glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE); - // Get current framebuffer size, on high-dpi it could be bigger than screen size - //glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight); + //TRACELOG(LOG_INFO, "DPI SCALING: %.2f, %.2f", scaleDpi.x, scaleDpi.y); // Screen scaling matrix is required in case desired screen area is different from display area CORE.Window.screenScale = MatrixScale(scaleDpi.x, scaleDpi.y, 1.0f); + + // NOTE: On APPLE platforms system manage window and input scaling + // Framebuffer scaling is activated with: glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE); + + // Screen scaling matrix is required in case desired screen area is different from display area + CORE.Window.screenScale = MatrixScale(scaleDpi.x, scaleDpi.y, 1.0f); + #if !defined(__APPLE__) // Mouse input scaling for the new screen size SetMouseScale(1.0f/scaleDpi.x, 1.0f/scaleDpi.y); -#endif + + // Force window size (and framebuffer) refresh glfwSetWindowSize(platform.handle, CORE.Window.render.width, CORE.Window.render.height); +#endif } - else - { - CORE.Window.render = CORE.Window.screen; - CORE.Window.currentFbo = CORE.Window.render; - } + else CORE.Window.render = CORE.Window.screen; + + // Current active framebuffer size is main framebuffer size + CORE.Window.currentFbo = CORE.Window.render; TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully %s", FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)? "(HighDPI)" : ""); @@ -1681,6 +1685,7 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + //TRACELOG(LOG_INFO, " > Content Scaling: %.2f, %.2f", scaleDpi.x, scaleDpi.y); // Try to center window on screen but avoiding window-bar outside of screen int monitorCount = 0; @@ -1694,13 +1699,17 @@ int InitPlatform(void) int monitorHeight = 0; glfwGetMonitorWorkarea(monitor, &monitorX, &monitorY, &monitorWidth, &monitorHeight); - // TODO: Here CORE.Window.render.width/height should be used instead of - // CORE.Window.screen.width/height to center the window correctly when the high dpi flag is enabled - CORE.Window.position.x = monitorX + (monitorWidth - (int)CORE.Window.screen.width)/2; - CORE.Window.position.y = monitorY + (monitorHeight - (int)CORE.Window.screen.height)/2; - //if (CORE.Window.position.x < monitorX) CORE.Window.position.x = monitorX; - //if (CORE.Window.position.y < monitorY) CORE.Window.position.y = monitorY; + // NOTE: It seems on macOS monitor size is not correct + //TRACELOG(LOG_WARNING, "Monitor info: [%i, %i, %i, %i]", monitorX, monitorY, monitorWidth, monitorHeight); + // Center window into current monitor + #if defined(__APPLE__) + CORE.Window.position.x = monitorX + (monitorWidth - CORE.Window.screen.width)/2; + CORE.Window.position.y = monitorY + (monitorHeight - CORE.Window.screen.height)/2; + #else + CORE.Window.position.x = monitorX + (monitorWidth - CORE.Window.render.width)/2; + CORE.Window.position.y = monitorY + (monitorHeight - CORE.Window.render.height)/2; + #endif SetWindowPosition(CORE.Window.position.x, CORE.Window.position.y); if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) MinimizeWindow(); @@ -1831,8 +1840,9 @@ static void FramebufferSizeCallback(GLFWwindow *window, int width, int height) SetupViewport(width, height); // Set render size - CORE.Window.currentFbo.width = width; - CORE.Window.currentFbo.height = height; + CORE.Window.render.width = width; + CORE.Window.render.height = height; + CORE.Window.currentFbo = CORE.Window.render; CORE.Window.resizedLastFrame = true; if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) @@ -1878,8 +1888,9 @@ static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float s { //TRACELOG(LOG_INFO, "GLFW3: Window content scale changed, scale: [%.2f,%.2f]", scalex, scaley); - float fbWidth = (float)CORE.Window.screen.width*scalex; - float fbHeight = (float)CORE.Window.screen.height*scaley; + CORE.Window.render.width = (int)((float)CORE.Window.screen.width*scalex); + CORE.Window.render.height = (int)((float)CORE.Window.screen.height*scaley); + CORE.Window.currentFbo = CORE.Window.render; // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling // Framebuffer scaling is activated with: glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE); @@ -1889,10 +1900,6 @@ static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float s // Mouse input scaling for the new screen size SetMouseScale(1.0f/scalex, 1.0f/scaley); #endif - - CORE.Window.render.width = (int)fbWidth; - CORE.Window.render.height = (int)fbHeight; - CORE.Window.currentFbo = CORE.Window.render; } // GLFW3: Window position callback, runs when window position changes From 6564cea6a31109e9f2fe1a6349c8dd12840098cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yui=20Kinomoto=20/=20=E3=81=8D=E3=81=AE=E3=82=82=E3=81=A8?= =?UTF-8?q?=20=E7=B5=90=E8=A1=A3?= Date: Mon, 16 Feb 2026 04:15:25 +0900 Subject: [PATCH 019/319] Fixed doesn't property load when 1 frame animation. (#5561) --- src/rmodels.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index 755d0437d..eadc16f07 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -6244,7 +6244,10 @@ static bool GetPoseAtTimeGLTF(cgltf_interpolation_type interpolationType, cgltf_ } // Constant animation, no need to interpolate - if (FloatEquals(tend, tstart)) return true; + if (FloatEquals(tend, tstart)) + { + interpolationType = cgltf_interpolation_type_step; + } float duration = fmaxf((tend - tstart), EPSILON); float t = (time - tstart)/duration; From 7c48fa9ac9d6cacfa8a1fe5e8a5107380eccc7dd Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 15 Feb 2026 20:20:53 +0100 Subject: [PATCH 020/319] Update Makefile --- examples/Makefile | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index acfcb0857..4f86ad5c0 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -393,14 +393,14 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) # NOTE: Required packages: libegl1-mesa-dev LDLIBS = -lraylib -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 + # On Wayland, additional libraries requires ifeq ($(USE_WAYLAND_DISPLAY),TRUE) LDLIBS += -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon + else + # On X11, additional libraries required + 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) @@ -439,15 +439,16 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL) # NOTE: Required packages: libegl1-mesa-dev LDLIBS = -lraylib $(SDL_LIBRARIES) -lGL -lm -lpthread -ldl -lrt - # On X11 requires also below libraries + # On X11, addition libraries required 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 + # On Wayland, 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 From b871a556d7a2b3086e2e70ca254bb742e4f99021 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Feb 2026 12:13:04 +0100 Subject: [PATCH 021/319] Init framebuffer using render size (should be same as currentFbo) --- src/rcore.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 49a928372..97a6cce0e 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -682,8 +682,6 @@ void InitWindow(int width, int height, const char *title) // Initialize window data CORE.Window.screen.width = width; CORE.Window.screen.height = height; - CORE.Window.currentFbo.width = CORE.Window.screen.width; - CORE.Window.currentFbo.height = CORE.Window.screen.height; CORE.Window.eventWaiting = false; CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default @@ -709,10 +707,10 @@ void InitWindow(int width, int height, const char *title) // Initialize rlgl default data (buffers and shaders) // NOTE: Current fbo size stored as globals in rlgl for convenience - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + rlglInit(CORE.Window.render.width, CORE.Window.render.height); // Setup default viewport - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + SetupViewport(CORE.Window.render.width, CORE.Window.render.height); #if defined(SUPPORT_MODULE_RTEXT) #if defined(SUPPORT_DEFAULT_FONT) From 4678a544b6f74753755c09291c63215bb37fc535 Mon Sep 17 00:00:00 2001 From: paddy <0xPD33@proton.me> Date: Tue, 17 Feb 2026 12:43:52 +0100 Subject: [PATCH 022/319] [rcore][glfw] Fix window scaling on Wayland with GLFW 3.4+ (#5564) * Fix window scaling on Wayland with GLFW 3.4+ display scaling GLFW 3.4 defaults GLFW_SCALE_FRAMEBUFFER to TRUE on all platforms, causing framebuffer/window size mismatch on Wayland with display scaling (content renders in a subset of the window, mouse coordinates are wrong). Three fixes: - Disable GLFW_SCALE_FRAMEBUFFER on Wayland when FLAG_WINDOW_HIGHDPI is not set, restoring 1:1 window-to-framebuffer mapping - With FLAG_WINDOW_HIGHDPI, read actual framebuffer size from GLFW instead of resizing the window (which double-scales on Wayland where GLFW_SCALE_TO_MONITOR has no effect) - Skip mouse coordinate scaling on Wayland since GLFW already reports coordinates in logical (window) space Tested on NixOS/Niri with GLFW 3.4 at 1x, 1.5x, and 2x scaling. Fixes #5504 * Fix fullscreen and borderless windowed scaling on Wayland with HiDPI ToggleFullscreen and ToggleBorderlessWindowed exit paths manually scale screen size by DPI before passing to glfwSetWindowMonitor, which double-scales on Wayland where GLFW_SCALE_FRAMEBUFFER already handles it. Skip the manual resize on Wayland. Also fix FramebufferSizeCallback fullscreen branch: on Wayland with GLFW_SCALE_FRAMEBUFFER the framebuffer is still scaled in fullscreen, so use the logical window size as screen size and derive screenScale from the framebuffer/window ratio. Fixes #5504 * Apply style fixes from code review: remove duplicate screenScale assignment, collapse single-statement ifs to one line, remove trailing periods from comments --- src/platforms/rcore_desktop_glfw.c | 56 +++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 68a321ffd..4cb3726e1 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -221,7 +221,9 @@ void ToggleFullscreen(void) #if !defined(__APPLE__) // Make sure to restore render size considering HighDPI scaling - if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) + // NOTE: On Wayland, GLFW_SCALE_FRAMEBUFFER handles scaling, skip manual resize + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI) && + (glfwGetPlatform() != GLFW_PLATFORM_WAYLAND)) { Vector2 scaleDpi = GetWindowScaleDPI(); CORE.Window.screen.width = (unsigned int)(CORE.Window.screen.width*scaleDpi.x); @@ -300,7 +302,9 @@ void ToggleBorderlessWindowed(void) #if !defined(__APPLE__) // Make sure to restore size considering HighDPI scaling - if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) + // NOTE: On Wayland, GLFW_SCALE_FRAMEBUFFER handles scaling, skip manual resize + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI) && + (glfwGetPlatform() != GLFW_PLATFORM_WAYLAND)) { Vector2 scaleDpi = GetWindowScaleDPI(); CORE.Window.screen.width = (unsigned int)(CORE.Window.screen.width*scaleDpi.x); @@ -1465,6 +1469,8 @@ int InitPlatform(void) #if defined(__APPLE__) glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE); #endif + // GLFW 3.4+ defaults GLFW_SCALE_FRAMEBUFFER to TRUE, causing framebuffer/window size mismatch on Wayland with display scaling + if (glfwGetPlatform() == GLFW_PLATFORM_WAYLAND) glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE); } // Mouse passthrough @@ -1663,15 +1669,23 @@ int InitPlatform(void) // NOTE: On APPLE platforms system manage window and input scaling // Framebuffer scaling is activated with: glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE); - // Screen scaling matrix is required in case desired screen area is different from display area - CORE.Window.screenScale = MatrixScale(scaleDpi.x, scaleDpi.y, 1.0f); - #if !defined(__APPLE__) - // Mouse input scaling for the new screen size - SetMouseScale(1.0f/scaleDpi.x, 1.0f/scaleDpi.y); + if (glfwGetPlatform() == GLFW_PLATFORM_WAYLAND) + { + // On Wayland, GLFW_SCALE_FRAMEBUFFER handles scaling; read actual framebuffer size instead of resizing the window (which would double-scale) + int fbWidth, fbHeight; + glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight); + CORE.Window.render.width = fbWidth; + CORE.Window.render.height = fbHeight; + } + else + { + // Mouse input scaling for the new screen size + SetMouseScale(1.0f/scaleDpi.x, 1.0f/scaleDpi.y); - // Force window size (and framebuffer) refresh - glfwSetWindowSize(platform.handle, CORE.Window.render.width, CORE.Window.render.height); + // Force window size (and framebuffer) refresh + glfwSetWindowSize(platform.handle, CORE.Window.render.width, CORE.Window.render.height); + } #endif } else CORE.Window.render = CORE.Window.screen; @@ -1855,6 +1869,22 @@ static void FramebufferSizeCallback(GLFWwindow *window, int width, int height) CORE.Window.screen.height = height; CORE.Window.screenScale = MatrixScale(1.0f, 1.0f, 1.0f); SetMouseScale(1.0f, 1.0f); + + // On Wayland with GLFW_SCALE_FRAMEBUFFER, the framebuffer is still scaled in fullscreen, use logical window size as screen and apply screenScale + if ((glfwGetPlatform() == GLFW_PLATFORM_WAYLAND) && + FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) + { + int winWidth, winHeight; + glfwGetWindowSize(platform.handle, &winWidth, &winHeight); + if ((winWidth != width) || (winHeight != height)) + { + CORE.Window.screen.width = winWidth; + CORE.Window.screen.height = winHeight; + float scaleX = (float)width/winWidth; + float scaleY = (float)height/winHeight; + CORE.Window.screenScale = MatrixScale(scaleX, scaleY, 1.0f); + } + } } else // Window mode (including borderless window) { @@ -1867,8 +1897,8 @@ static void FramebufferSizeCallback(GLFWwindow *window, int width, int height) CORE.Window.screen.height = (int)((float)height/scaleDpi.y); CORE.Window.screenScale = MatrixScale(scaleDpi.x, scaleDpi.y, 1.0f); #if !defined(__APPLE__) - // Mouse input scaling for the new screen size - SetMouseScale(1.0f/scaleDpi.x, 1.0f/scaleDpi.y); + // On Wayland, mouse coords are already in logical space + if (glfwGetPlatform() != GLFW_PLATFORM_WAYLAND) SetMouseScale(1.0f/scaleDpi.x, 1.0f/scaleDpi.y); #endif } else @@ -1897,8 +1927,8 @@ static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float s CORE.Window.screenScale = MatrixScale(scalex, scaley, 1.0f); #if !defined(__APPLE__) - // Mouse input scaling for the new screen size - SetMouseScale(1.0f/scalex, 1.0f/scaley); + // On Wayland, mouse coords are already in logical space + if (glfwGetPlatform() != GLFW_PLATFORM_WAYLAND) SetMouseScale(1.0f/scalex, 1.0f/scaley); #endif } From 5bbb2fc1df612d48f53e23c586779ae383b8725d Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Feb 2026 13:22:11 +0100 Subject: [PATCH 023/319] REVIEWED: Wayland checks, using compilation flags when possible #5564 --- src/platforms/rcore_desktop_glfw.c | 46 ++++++++++++++++++------------ 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 4cb3726e1..af3e6b6c1 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -219,11 +219,10 @@ void ToggleFullscreen(void) // and considered by GetWindowScaleDPI() FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); -#if !defined(__APPLE__) +#if !defined(__APPLE__) && !defined(_GLFW_WAYLAND) // Make sure to restore render size considering HighDPI scaling // NOTE: On Wayland, GLFW_SCALE_FRAMEBUFFER handles scaling, skip manual resize - if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI) && - (glfwGetPlatform() != GLFW_PLATFORM_WAYLAND)) + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) { Vector2 scaleDpi = GetWindowScaleDPI(); CORE.Window.screen.width = (unsigned int)(CORE.Window.screen.width*scaleDpi.x); @@ -300,11 +299,10 @@ void ToggleBorderlessWindowed(void) glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE); FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNDECORATED); - #if !defined(__APPLE__) + #if !defined(__APPLE__) && !defined(_GLFW_WAYLAND) // Make sure to restore size considering HighDPI scaling // NOTE: On Wayland, GLFW_SCALE_FRAMEBUFFER handles scaling, skip manual resize - if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI) && - (glfwGetPlatform() != GLFW_PLATFORM_WAYLAND)) + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) { Vector2 scaleDpi = GetWindowScaleDPI(); CORE.Window.screen.width = (unsigned int)(CORE.Window.screen.width*scaleDpi.x); @@ -1469,8 +1467,11 @@ int InitPlatform(void) #if defined(__APPLE__) glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE); #endif - // GLFW 3.4+ defaults GLFW_SCALE_FRAMEBUFFER to TRUE, causing framebuffer/window size mismatch on Wayland with display scaling - if (glfwGetPlatform() == GLFW_PLATFORM_WAYLAND) glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE); +#if defined(_GLFW_WAYLAND) && !defined(_GLFW_X11) + // GLFW 3.4+ defaults GLFW_SCALE_FRAMEBUFFER to TRUE, + // causing framebuffer/window size mismatch on Wayland with display scaling + glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE); +#endif } // Mouse passthrough @@ -1672,9 +1673,12 @@ int InitPlatform(void) #if !defined(__APPLE__) if (glfwGetPlatform() == GLFW_PLATFORM_WAYLAND) { - // On Wayland, GLFW_SCALE_FRAMEBUFFER handles scaling; read actual framebuffer size instead of resizing the window (which would double-scale) - int fbWidth, fbHeight; + // On Wayland, GLFW_SCALE_FRAMEBUFFER handles scaling; read actual framebuffer size + // instead of resizing the window (which would double-scale) + int fbWidth = 0; + int fbHeight = 0; glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight); + CORE.Window.render.width = fbWidth; CORE.Window.render.height = fbHeight; } @@ -1871,20 +1875,24 @@ static void FramebufferSizeCallback(GLFWwindow *window, int width, int height) SetMouseScale(1.0f, 1.0f); // On Wayland with GLFW_SCALE_FRAMEBUFFER, the framebuffer is still scaled in fullscreen, use logical window size as screen and apply screenScale - if ((glfwGetPlatform() == GLFW_PLATFORM_WAYLAND) && - FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) +#if defined(_GLFW_WAYLAND) && !defined(_GLFW_X11) + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) { - int winWidth, winHeight; + int winWidth = 0; + int winHeight = 0; glfwGetWindowSize(platform.handle, &winWidth, &winHeight); + if ((winWidth != width) || (winHeight != height)) { CORE.Window.screen.width = winWidth; CORE.Window.screen.height = winHeight; float scaleX = (float)width/winWidth; float scaleY = (float)height/winHeight; + CORE.Window.screenScale = MatrixScale(scaleX, scaleY, 1.0f); } } +#endif } else // Window mode (including borderless window) { @@ -1896,9 +1904,9 @@ static void FramebufferSizeCallback(GLFWwindow *window, int width, int height) CORE.Window.screen.width = (int)((float)width/scaleDpi.x); CORE.Window.screen.height = (int)((float)height/scaleDpi.y); CORE.Window.screenScale = MatrixScale(scaleDpi.x, scaleDpi.y, 1.0f); -#if !defined(__APPLE__) - // On Wayland, mouse coords are already in logical space - if (glfwGetPlatform() != GLFW_PLATFORM_WAYLAND) SetMouseScale(1.0f/scaleDpi.x, 1.0f/scaleDpi.y); +#if !defined(__APPLE__) && !defined(_GLFW_WAYLAND) + // On macOS and Linux-Wayland, mouse coords are already in logical space + SetMouseScale(1.0f/scaleDpi.x, 1.0f/scaleDpi.y); #endif } else @@ -1926,9 +1934,9 @@ static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float s // Framebuffer scaling is activated with: glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE); CORE.Window.screenScale = MatrixScale(scalex, scaley, 1.0f); -#if !defined(__APPLE__) - // On Wayland, mouse coords are already in logical space - if (glfwGetPlatform() != GLFW_PLATFORM_WAYLAND) SetMouseScale(1.0f/scalex, 1.0f/scaley); +#if !defined(__APPLE__) && !defined(_GLFW_WAYLAND) + // On macOS and Linux-Wayland, mouse coords are already in logical space + SetMouseScale(1.0f/scalex, 1.0f/scaley); #endif } From 4311df1e6d5632d718fb6b14f5d4b72ce6b7744f Mon Sep 17 00:00:00 2001 From: dmitrii-brand <114125495+dmitrii-brand@users.noreply.github.com> Date: Tue, 17 Feb 2026 14:43:46 +0000 Subject: [PATCH 024/319] Add bone blending animation example (#5543) - Demonstrates per-bone animation blending for smooth transitions - Supports upper/lower body selective blending (walk + attack) - Includes uniform blending mode for comparison - Uses GPU skinning for performance - Follows raylib example conventions --- examples/examples_list.txt | 1 + .../models/models_animation_bone_blending.c | 291 ++++++++++++++++++ 2 files changed, 292 insertions(+) create mode 100644 examples/models/models_animation_bone_blending.c diff --git a/examples/examples_list.txt b/examples/examples_list.txt index eda62ee13..f4cfa0e47 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -163,6 +163,7 @@ models;models_heightmap_rendering;★☆☆☆;1.8;3.5;2015;2025;"Ramon Santamar models;models_skybox_rendering;★★☆☆;1.8;4.0;2017;2025;"Ramon Santamaria";@raysan5 models;models_textured_cube;★★☆☆;4.5;4.5;2022;2025;"Ramon Santamaria";@raysan5 models;models_animation_gpu_skinning;★★★☆;4.5;4.5;2024;2025;"Daniel Holden";@orangeduck +models;models_animation_bone_blending;★★★★;5.5;5.5;2025;2025;"[Your Name]";@[your_github] models;models_bone_socket;★★★★;4.5;4.5;2024;2025;"iP";@ipzaur models;models_tesseract_view;★★☆☆;5.6-dev;5.6-dev;2024;2025;"Timothy van der Valk";@arceryz models;models_basic_voxel;★★☆☆;5.5;5.5;2025;2025;"Tim Little";@timlittle diff --git a/examples/models/models_animation_bone_blending.c b/examples/models/models_animation_bone_blending.c new file mode 100644 index 000000000..ea6827762 --- /dev/null +++ b/examples/models/models_animation_bone_blending.c @@ -0,0 +1,291 @@ +/******************************************************************************************* +* +* raylib [models] example - animation bone blending +* +* Example complexity rating: [★★★★] 4/4 +* +* Example originally created with raylib 5.5, last time updated with raylib 5.5 +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* This example demonstrates per-bone animation blending, allowing smooth transitions +* between two animations by interpolating bone transforms. This is useful for: +* - Blending movement animations (walk/run) with action animations (jump/attack) +* - Creating smooth animation transitions +* - Layering animations (e.g., upper body attack while lower body walks) +* +* Note: Due to limitations in the Apple OpenGL driver, GPU skinning does not work on MacOS +* +********************************************************************************************/ + +#include "raylib.h" +#include "raymath.h" +#include // For memcpy +#include // For NULL + +#if defined(PLATFORM_DESKTOP) + #define GLSL_VERSION 330 +#else // PLATFORM_ANDROID, PLATFORM_WEB + #define GLSL_VERSION 100 +#endif + +//------------------------------------------------------------------------------------ +// Check if a bone is part of upper body (for selective blending) +//------------------------------------------------------------------------------------ +bool IsUpperBodyBone(const char *boneName) +{ + // Common upper body bone names (adjust based on your model) + if (TextIsEqual(boneName, "spine") || TextIsEqual(boneName, "spine1") || TextIsEqual(boneName, "spine2") || + TextIsEqual(boneName, "chest") || TextIsEqual(boneName, "upperChest") || + TextIsEqual(boneName, "neck") || TextIsEqual(boneName, "head") || + TextIsEqual(boneName, "shoulder") || TextIsEqual(boneName, "shoulder_L") || TextIsEqual(boneName, "shoulder_R") || + TextIsEqual(boneName, "upperArm") || TextIsEqual(boneName, "upperArm_L") || TextIsEqual(boneName, "upperArm_R") || + TextIsEqual(boneName, "lowerArm") || TextIsEqual(boneName, "lowerArm_L") || TextIsEqual(boneName, "lowerArm_R") || + TextIsEqual(boneName, "hand") || TextIsEqual(boneName, "hand_L") || TextIsEqual(boneName, "hand_R") || + TextIsEqual(boneName, "clavicle") || TextIsEqual(boneName, "clavicle_L") || TextIsEqual(boneName, "clavicle_R")) + { + return true; + } + + // Check if bone name contains upper body keywords + if (strstr(boneName, "spine") != NULL || strstr(boneName, "chest") != NULL || + strstr(boneName, "neck") != NULL || strstr(boneName, "head") != NULL || + strstr(boneName, "shoulder") != NULL || strstr(boneName, "arm") != NULL || + strstr(boneName, "hand") != NULL || strstr(boneName, "clavicle") != NULL) + { + return true; + } + + return false; +} + +//------------------------------------------------------------------------------------ +// Blend two animations per-bone with selective upper/lower body blending +//------------------------------------------------------------------------------------ +void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int frame1, + ModelAnimation *anim2, int frame2, float blendFactor, bool upperBodyBlend) +{ + // Clamp blend factor to [0, 1] + blendFactor = fminf(1.0f, fmaxf(0.0f, blendFactor)); + + // Validate inputs + if (anim1->boneCount == 0 || anim1->framePoses == NULL || + anim2->boneCount == 0 || anim2->framePoses == NULL || + model->boneCount == 0 || model->bindPose == NULL) + { + return; + } + + // Ensure frame indices are valid + if (frame1 >= anim1->frameCount) frame1 = anim1->frameCount - 1; + if (frame2 >= anim2->frameCount) frame2 = anim2->frameCount - 1; + if (frame1 < 0) frame1 = 0; + if (frame2 < 0) frame2 = 0; + + // Find first mesh with bones + int firstMeshWithBones = -1; + for (int i = 0; i < model->meshCount; i++) + { + if (model->meshes[i].boneMatrices) + { + firstMeshWithBones = i; + break; + } + } + + if (firstMeshWithBones == -1) return; + + // Get bone count (use minimum of all to be safe) + int boneCount = model->boneCount; + if (anim1->boneCount < boneCount) boneCount = anim1->boneCount; + if (anim2->boneCount < boneCount) boneCount = anim2->boneCount; + + // Blend each bone + for (int boneId = 0; boneId < boneCount; boneId++) + { + // Determine blend factor for this bone + float boneBlendFactor = blendFactor; + + // If upper body blending is enabled, use different blend factors for upper vs lower body + if (upperBodyBlend) + { + const char *boneName = model->bones[boneId].name; + bool isUpperBody = IsUpperBodyBone(boneName); + + // Upper body: use anim2 (attack), Lower body: use anim1 (walk) + // blendFactor = 0.0 means full anim1 (walk), 1.0 means full anim2 (attack) + if (isUpperBody) + { + // Upper body: blend towards anim2 (attack) + boneBlendFactor = blendFactor; + } + else + { + // Lower body: blend towards anim1 (walk) - invert the blend + boneBlendFactor = 1.0f - blendFactor; + } + } + + // Get transforms from both animations + Transform *bindTransform = &model->bindPose[boneId]; + Transform *anim1Transform = &anim1->framePoses[frame1][boneId]; + Transform *anim2Transform = &anim2->framePoses[frame2][boneId]; + + // Blend the transforms + Transform blended; + blended.translation = Vector3Lerp(anim1Transform->translation, anim2Transform->translation, boneBlendFactor); + blended.rotation = QuaternionSlerp(anim1Transform->rotation, anim2Transform->rotation, boneBlendFactor); + blended.scale = Vector3Lerp(anim1Transform->scale, anim2Transform->scale, boneBlendFactor); + + // Convert bind pose to matrix + Matrix bindMatrix = MatrixMultiply(MatrixMultiply( + MatrixScale(bindTransform->scale.x, bindTransform->scale.y, bindTransform->scale.z), + QuaternionToMatrix(bindTransform->rotation)), + MatrixTranslate(bindTransform->translation.x, bindTransform->translation.y, bindTransform->translation.z)); + + // Convert blended transform to matrix + Matrix blendedMatrix = MatrixMultiply(MatrixMultiply( + MatrixScale(blended.scale.x, blended.scale.y, blended.scale.z), + QuaternionToMatrix(blended.rotation)), + MatrixTranslate(blended.translation.x, blended.translation.y, blended.translation.z)); + + // Calculate final bone matrix (similar to UpdateModelAnimationBones) + model->meshes[firstMeshWithBones].boneMatrices[boneId] = MatrixMultiply(MatrixInvert(bindMatrix), blendedMatrix); + } + + // Copy bone matrices to remaining meshes + for (int i = firstMeshWithBones + 1; i < model->meshCount; i++) + { + if (model->meshes[i].boneMatrices) + { + memcpy(model->meshes[i].boneMatrices, + model->meshes[firstMeshWithBones].boneMatrices, + model->meshes[i].boneCount * sizeof(model->meshes[i].boneMatrices[0])); + } + } +} + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [models] example - animation bone blending"); + + // Define the camera to look into our 3d world + Camera camera = { 0 }; + camera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; // Camera position + camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; // Camera looking at point + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) + camera.fovy = 45.0f; // Camera field-of-view Y + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type + + // Load gltf model + Model characterModel = LoadModel("resources/models/gltf/greenman.glb"); + + // 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; + ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/greenman.glb", &animsCount); + + // Log all available animations for debugging + TraceLog(LOG_INFO, "Found %d animations:", animsCount); + for (int i = 0; i < animsCount; i++) + { + TraceLog(LOG_INFO, " Animation %d: %s (%d frames)", i, modelAnimations[i].name, modelAnimations[i].frameCount); + } + + // Use specific indices: walk/move = 2, attack = 3 + unsigned int animIndex1 = 2; // Walk/Move animation (index 2) + unsigned int animIndex2 = 3; // Attack animation (index 3) + unsigned int animCurrentFrame1 = 0; + unsigned int animCurrentFrame2 = 0; + + // Validate indices + if (animIndex1 >= animsCount) animIndex1 = 0; + if (animIndex2 >= animsCount) animIndex2 = (animsCount > 1) ? 1 : 0; + + TraceLog(LOG_INFO, "Using Walk (index %d): %s", animIndex1, modelAnimations[animIndex1].name); + TraceLog(LOG_INFO, "Using Attack (index %d): %s", animIndex2, modelAnimations[animIndex2].name); + + Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position + bool upperBodyBlend = true; // Toggle: true = upper/lower body blending, false = uniform blending (50/50) + + DisableCursor(); // Limit cursor to relative movement inside the window + + 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 + //---------------------------------------------------------------------------------- + UpdateCamera(&camera, CAMERA_THIRD_PERSON); + + // Toggle upper/lower body blending mode (SPACE key) + if (IsKeyPressed(KEY_SPACE)) upperBodyBlend = !upperBodyBlend; + + // Update animation frames + ModelAnimation anim1 = modelAnimations[animIndex1]; + ModelAnimation anim2 = modelAnimations[animIndex2]; + + animCurrentFrame1 = (animCurrentFrame1 + 1) % anim1.frameCount; + animCurrentFrame2 = (animCurrentFrame2 + 1) % anim2.frameCount; + + // Blend the two animations + characterModel.transform = MatrixTranslate(position.x, position.y, position.z); + // When upperBodyBlend is ON: upper body = attack (1.0), lower body = walk (0.0) + // When upperBodyBlend is OFF: uniform blend at 0.5 (50% walk, 50% attack) + float blendFactor = upperBodyBlend ? 1.0f : 0.5f; + BlendModelAnimationsBones(&characterModel, &anim1, animCurrentFrame1, &anim2, animCurrentFrame2, blendFactor, upperBodyBlend); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + 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(); + + // Draw UI + DrawText("BONE BLENDING EXAMPLE", 10, 10, 20, DARKGRAY); + DrawText(TextFormat("Walk (Animation 2): %s", anim1.name), 10, 35, 10, GRAY); + DrawText(TextFormat("Attack (Animation 3): %s", anim2.name), 10, 50, 10, GRAY); + DrawText(TextFormat("Mode: %s", upperBodyBlend ? "Upper/Lower Body Blending" : "Uniform Blending"), 10, 65, 10, GRAY); + DrawText("SPACE - Toggle blending mode", 10, GetScreenHeight() - 20, 10, DARKGRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + 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 + //-------------------------------------------------------------------------------------- + + return 0; +} From 95edeeccd27c1cafb2241237415da28cc6dce6f0 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Feb 2026 16:17:14 +0100 Subject: [PATCH 025/319] REVIEWED: example: `models_animation_bone_blending` --- examples/examples_list.txt | 1 - .../models/models_animation_bone_blending.c | 297 +++++++++--------- 2 files changed, 151 insertions(+), 147 deletions(-) diff --git a/examples/examples_list.txt b/examples/examples_list.txt index f4cfa0e47..eda62ee13 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -163,7 +163,6 @@ models;models_heightmap_rendering;★☆☆☆;1.8;3.5;2015;2025;"Ramon Santamar models;models_skybox_rendering;★★☆☆;1.8;4.0;2017;2025;"Ramon Santamaria";@raysan5 models;models_textured_cube;★★☆☆;4.5;4.5;2022;2025;"Ramon Santamaria";@raysan5 models;models_animation_gpu_skinning;★★★☆;4.5;4.5;2024;2025;"Daniel Holden";@orangeduck -models;models_animation_bone_blending;★★★★;5.5;5.5;2025;2025;"[Your Name]";@[your_github] models;models_bone_socket;★★★★;4.5;4.5;2024;2025;"iP";@ipzaur models;models_tesseract_view;★★☆☆;5.6-dev;5.6-dev;2024;2025;"Timothy van der Valk";@arceryz models;models_basic_voxel;★★☆☆;5.5;5.5;2025;2025;"Tim Little";@timlittle diff --git a/examples/models/models_animation_bone_blending.c b/examples/models/models_animation_bone_blending.c index ea6827762..11e05a305 100644 --- a/examples/models/models_animation_bone_blending.c +++ b/examples/models/models_animation_bone_blending.c @@ -6,23 +6,29 @@ * * Example originally created with raylib 5.5, last time updated with raylib 5.5 * +* This example demonstrates per-bone animation blending, allowing smooth transitions +* between two animations by interpolating bone transforms. This is useful for: +* - Blending movement animations (walk/run) with action animations (jump/attack) +* - Creating smooth animation transitions +* - Layering animations (e.g., upper body attack while lower body walks) +* +* Example contributed by dmitrii-brand (@dmitrii-brand) and reviewed by Ramon Santamaria (@raysan5) +* +* NOTE: Due to limitations in the Apple OpenGL driver, this feature does not work on MacOS +* * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software * -* This example demonstrates per-bone animation blending, allowing smooth transitions -* between two animations by interpolating bone transforms. This is useful for: -* - Blending movement animations (walk/run) with action animations (jump/attack) -* - Creating smooth animation transitions -* - Layering animations (e.g., upper body attack while lower body walks) -* -* Note: Due to limitations in the Apple OpenGL driver, GPU skinning does not work on MacOS +* Copyright (c) 2026 dmitrii-brand (@dmitrii-brand) * ********************************************************************************************/ #include "raylib.h" + #include "raymath.h" -#include // For memcpy -#include // For NULL + +#include // Required for: memcpy() +#include // Required for: NULL #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 @@ -31,140 +37,11 @@ #endif //------------------------------------------------------------------------------------ -// Check if a bone is part of upper body (for selective blending) +// Module Functions Declaration //------------------------------------------------------------------------------------ -bool IsUpperBodyBone(const char *boneName) -{ - // Common upper body bone names (adjust based on your model) - if (TextIsEqual(boneName, "spine") || TextIsEqual(boneName, "spine1") || TextIsEqual(boneName, "spine2") || - TextIsEqual(boneName, "chest") || TextIsEqual(boneName, "upperChest") || - TextIsEqual(boneName, "neck") || TextIsEqual(boneName, "head") || - TextIsEqual(boneName, "shoulder") || TextIsEqual(boneName, "shoulder_L") || TextIsEqual(boneName, "shoulder_R") || - TextIsEqual(boneName, "upperArm") || TextIsEqual(boneName, "upperArm_L") || TextIsEqual(boneName, "upperArm_R") || - TextIsEqual(boneName, "lowerArm") || TextIsEqual(boneName, "lowerArm_L") || TextIsEqual(boneName, "lowerArm_R") || - TextIsEqual(boneName, "hand") || TextIsEqual(boneName, "hand_L") || TextIsEqual(boneName, "hand_R") || - TextIsEqual(boneName, "clavicle") || TextIsEqual(boneName, "clavicle_L") || TextIsEqual(boneName, "clavicle_R")) - { - return true; - } - - // Check if bone name contains upper body keywords - if (strstr(boneName, "spine") != NULL || strstr(boneName, "chest") != NULL || - strstr(boneName, "neck") != NULL || strstr(boneName, "head") != NULL || - strstr(boneName, "shoulder") != NULL || strstr(boneName, "arm") != NULL || - strstr(boneName, "hand") != NULL || strstr(boneName, "clavicle") != NULL) - { - return true; - } - - return false; -} - -//------------------------------------------------------------------------------------ -// Blend two animations per-bone with selective upper/lower body blending -//------------------------------------------------------------------------------------ -void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int frame1, - ModelAnimation *anim2, int frame2, float blendFactor, bool upperBodyBlend) -{ - // Clamp blend factor to [0, 1] - blendFactor = fminf(1.0f, fmaxf(0.0f, blendFactor)); - - // Validate inputs - if (anim1->boneCount == 0 || anim1->framePoses == NULL || - anim2->boneCount == 0 || anim2->framePoses == NULL || - model->boneCount == 0 || model->bindPose == NULL) - { - return; - } - - // Ensure frame indices are valid - if (frame1 >= anim1->frameCount) frame1 = anim1->frameCount - 1; - if (frame2 >= anim2->frameCount) frame2 = anim2->frameCount - 1; - if (frame1 < 0) frame1 = 0; - if (frame2 < 0) frame2 = 0; - - // Find first mesh with bones - int firstMeshWithBones = -1; - for (int i = 0; i < model->meshCount; i++) - { - if (model->meshes[i].boneMatrices) - { - firstMeshWithBones = i; - break; - } - } - - if (firstMeshWithBones == -1) return; - - // Get bone count (use minimum of all to be safe) - int boneCount = model->boneCount; - if (anim1->boneCount < boneCount) boneCount = anim1->boneCount; - if (anim2->boneCount < boneCount) boneCount = anim2->boneCount; - - // Blend each bone - for (int boneId = 0; boneId < boneCount; boneId++) - { - // Determine blend factor for this bone - float boneBlendFactor = blendFactor; - - // If upper body blending is enabled, use different blend factors for upper vs lower body - if (upperBodyBlend) - { - const char *boneName = model->bones[boneId].name; - bool isUpperBody = IsUpperBodyBone(boneName); - - // Upper body: use anim2 (attack), Lower body: use anim1 (walk) - // blendFactor = 0.0 means full anim1 (walk), 1.0 means full anim2 (attack) - if (isUpperBody) - { - // Upper body: blend towards anim2 (attack) - boneBlendFactor = blendFactor; - } - else - { - // Lower body: blend towards anim1 (walk) - invert the blend - boneBlendFactor = 1.0f - blendFactor; - } - } - - // Get transforms from both animations - Transform *bindTransform = &model->bindPose[boneId]; - Transform *anim1Transform = &anim1->framePoses[frame1][boneId]; - Transform *anim2Transform = &anim2->framePoses[frame2][boneId]; - - // Blend the transforms - Transform blended; - blended.translation = Vector3Lerp(anim1Transform->translation, anim2Transform->translation, boneBlendFactor); - blended.rotation = QuaternionSlerp(anim1Transform->rotation, anim2Transform->rotation, boneBlendFactor); - blended.scale = Vector3Lerp(anim1Transform->scale, anim2Transform->scale, boneBlendFactor); - - // Convert bind pose to matrix - Matrix bindMatrix = MatrixMultiply(MatrixMultiply( - MatrixScale(bindTransform->scale.x, bindTransform->scale.y, bindTransform->scale.z), - QuaternionToMatrix(bindTransform->rotation)), - MatrixTranslate(bindTransform->translation.x, bindTransform->translation.y, bindTransform->translation.z)); - - // Convert blended transform to matrix - Matrix blendedMatrix = MatrixMultiply(MatrixMultiply( - MatrixScale(blended.scale.x, blended.scale.y, blended.scale.z), - QuaternionToMatrix(blended.rotation)), - MatrixTranslate(blended.translation.x, blended.translation.y, blended.translation.z)); - - // Calculate final bone matrix (similar to UpdateModelAnimationBones) - model->meshes[firstMeshWithBones].boneMatrices[boneId] = MatrixMultiply(MatrixInvert(bindMatrix), blendedMatrix); - } - - // Copy bone matrices to remaining meshes - for (int i = firstMeshWithBones + 1; i < model->meshCount; i++) - { - if (model->meshes[i].boneMatrices) - { - memcpy(model->meshes[i].boneMatrices, - model->meshes[firstMeshWithBones].boneMatrices, - model->meshes[i].boneCount * sizeof(model->meshes[i].boneMatrices[0])); - } - } -} +static bool IsUpperBodyBone(const char *boneName); +static void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int frame1, + ModelAnimation *anim2, int frame2, float blendFactor, bool upperBodyBlend); //------------------------------------------------------------------------------------ // Program main entry point @@ -207,8 +84,8 @@ int main(void) } // Use specific indices: walk/move = 2, attack = 3 - unsigned int animIndex1 = 2; // Walk/Move animation (index 2) - unsigned int animIndex2 = 3; // Attack animation (index 3) + unsigned int animIndex1 = 2; // Walk/Move animation (index 2) + unsigned int animIndex2 = 3; // Attack animation (index 3) unsigned int animCurrentFrame1 = 0; unsigned int animCurrentFrame2 = 0; @@ -241,8 +118,8 @@ int main(void) ModelAnimation anim1 = modelAnimations[animIndex1]; ModelAnimation anim2 = modelAnimations[animIndex2]; - animCurrentFrame1 = (animCurrentFrame1 + 1) % anim1.frameCount; - animCurrentFrame2 = (animCurrentFrame2 + 1) % anim2.frameCount; + animCurrentFrame1 = (animCurrentFrame1 + 1)%anim1.frameCount; + animCurrentFrame2 = (animCurrentFrame2 + 1)%anim2.frameCount; // Blend the two animations characterModel.transform = MatrixTranslate(position.x, position.y, position.z); @@ -289,3 +166,131 @@ int main(void) return 0; } + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- +// Check if a bone is part of upper body (for selective blending) +static bool IsUpperBodyBone(const char *boneName) +{ + // Common upper body bone names (adjust based on your model) + if (TextIsEqual(boneName, "spine") || TextIsEqual(boneName, "spine1") || TextIsEqual(boneName, "spine2") || + TextIsEqual(boneName, "chest") || TextIsEqual(boneName, "upperChest") || + TextIsEqual(boneName, "neck") || TextIsEqual(boneName, "head") || + TextIsEqual(boneName, "shoulder") || TextIsEqual(boneName, "shoulder_L") || TextIsEqual(boneName, "shoulder_R") || + TextIsEqual(boneName, "upperArm") || TextIsEqual(boneName, "upperArm_L") || TextIsEqual(boneName, "upperArm_R") || + TextIsEqual(boneName, "lowerArm") || TextIsEqual(boneName, "lowerArm_L") || TextIsEqual(boneName, "lowerArm_R") || + TextIsEqual(boneName, "hand") || TextIsEqual(boneName, "hand_L") || TextIsEqual(boneName, "hand_R") || + TextIsEqual(boneName, "clavicle") || TextIsEqual(boneName, "clavicle_L") || TextIsEqual(boneName, "clavicle_R")) + { + return true; + } + + // Check if bone name contains upper body keywords + if (strstr(boneName, "spine") != NULL || strstr(boneName, "chest") != NULL || + strstr(boneName, "neck") != NULL || strstr(boneName, "head") != NULL || + strstr(boneName, "shoulder") != NULL || strstr(boneName, "arm") != NULL || + strstr(boneName, "hand") != NULL || strstr(boneName, "clavicle") != NULL) + { + return true; + } + + return false; +} + +// Blend two animations per-bone with selective upper/lower body blending +static void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int frame1, + ModelAnimation *anim2, int frame2, float blendFactor, bool upperBodyBlend) +{ + // Validate inputs + if (anim1->boneCount == 0 || anim1->framePoses == NULL || + anim2->boneCount == 0 || anim2->framePoses == NULL || + model->boneCount == 0 || model->bindPose == NULL) + { + return; + } + + // Clamp blend factor to [0, 1] + blendFactor = fminf(1.0f, fmaxf(0.0f, blendFactor)); + + // Ensure frame indices are valid + if (frame1 >= anim1->frameCount) frame1 = anim1->frameCount - 1; + if (frame2 >= anim2->frameCount) frame2 = anim2->frameCount - 1; + if (frame1 < 0) frame1 = 0; + if (frame2 < 0) frame2 = 0; + + // Find first mesh with bones + int firstMeshWithBones = -1; + for (int i = 0; i < model->meshCount; i++) + { + if (model->meshes[i].boneMatrices) + { + firstMeshWithBones = i; + break; + } + } + + if (firstMeshWithBones == -1) return; + + // Get bone count (use minimum of all to be safe) + int boneCount = model->boneCount; + if (anim1->boneCount < boneCount) boneCount = anim1->boneCount; + if (anim2->boneCount < boneCount) boneCount = anim2->boneCount; + + // Blend each bone + for (int boneId = 0; boneId < boneCount; boneId++) + { + // Determine blend factor for this bone + float boneBlendFactor = blendFactor; + + // If upper body blending is enabled, use different blend factors for upper vs lower body + if (upperBodyBlend) + { + const char *boneName = model->bones[boneId].name; + bool isUpperBody = IsUpperBodyBone(boneName); + + // Upper body: use anim2 (attack), Lower body: use anim1 (walk) + // blendFactor = 0.0 means full anim1 (walk), 1.0 means full anim2 (attack) + if (isUpperBody) boneBlendFactor = blendFactor; // Upper body: blend towards anim2 (attack) + else boneBlendFactor = 1.0f - blendFactor; // Lower body: blend towards anim1 (walk) - invert the blend + } + + // Get transforms from both animations + Transform *bindTransform = &model->bindPose[boneId]; + Transform *anim1Transform = &anim1->framePoses[frame1][boneId]; + Transform *anim2Transform = &anim2->framePoses[frame2][boneId]; + + // Blend the transforms + Transform blended; + blended.translation = Vector3Lerp(anim1Transform->translation, anim2Transform->translation, boneBlendFactor); + blended.rotation = QuaternionSlerp(anim1Transform->rotation, anim2Transform->rotation, boneBlendFactor); + blended.scale = Vector3Lerp(anim1Transform->scale, anim2Transform->scale, boneBlendFactor); + + // Convert bind pose to matrix + Matrix bindMatrix = MatrixMultiply(MatrixMultiply( + MatrixScale(bindTransform->scale.x, bindTransform->scale.y, bindTransform->scale.z), + QuaternionToMatrix(bindTransform->rotation)), + MatrixTranslate(bindTransform->translation.x, bindTransform->translation.y, bindTransform->translation.z)); + + // Convert blended transform to matrix + Matrix blendedMatrix = MatrixMultiply(MatrixMultiply( + MatrixScale(blended.scale.x, blended.scale.y, blended.scale.z), + QuaternionToMatrix(blended.rotation)), + MatrixTranslate(blended.translation.x, blended.translation.y, blended.translation.z)); + + // Calculate final bone matrix (similar to UpdateModelAnimationBones) + model->meshes[firstMeshWithBones].boneMatrices[boneId] = MatrixMultiply(MatrixInvert(bindMatrix), blendedMatrix); + } + + // Copy bone matrices to remaining meshes + for (int i = firstMeshWithBones + 1; i < model->meshCount; i++) + { + if (model->meshes[i].boneMatrices) + { + memcpy(model->meshes[i].boneMatrices, + model->meshes[firstMeshWithBones].boneMatrices, + model->meshes[i].boneCount*sizeof(model->meshes[i].boneMatrices[0])); + } + } +} + From 1955516f54585c6a4418866fbaf96cb62eec91ed Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Feb 2026 16:39:02 +0100 Subject: [PATCH 026/319] Updated raygui for examples --- examples/core/core_directory_files.c | 20 +- examples/core/raygui.h | 390 +- .../models/models_directional_billboard.c | 2 + examples/models/raygui.h | 6043 +++++++++++++++++ examples/shaders/raygui.h | 390 +- examples/shapes/raygui.h | 390 +- 6 files changed, 6732 insertions(+), 503 deletions(-) create mode 100644 examples/models/raygui.h diff --git a/examples/core/core_directory_files.c b/examples/core/core_directory_files.c index a98c950f6..5bc7b7e57 100644 --- a/examples/core/core_directory_files.c +++ b/examples/core/core_directory_files.c @@ -37,10 +37,17 @@ int main(void) char directory[MAX_FILEPATH_SIZE] = { 0 }; strcpy(directory, GetWorkingDirectory()); + // Load file-paths on current working directory + // NOTE: LoadDirectoryFiles() loads files and directories by default, + // use LoadDirectoryFilesEx() for custom filters and recursive directories loading FilePathList files = LoadDirectoryFiles(directory); int btnBackPressed = false; + int listScrollIndex = 0; + int listItemActive = -1; + int listItemFocused = -1; + SetTargetFPS(60); //-------------------------------------------------------------------------------------- @@ -62,10 +69,18 @@ int main(void) BeginDrawing(); ClearBackground(RAYWHITE); - DrawText(directory, 100, 40, 20, DARKGRAY); + btnBackPressed = GuiButton((Rectangle){ 40.0f, 10.0f, 48, 28 }, "<"); - btnBackPressed = GuiButton((Rectangle){ 40.0f, 38.0f, 48, 24 }, "<"); + GuiSetStyle(DEFAULT, TEXT_SIZE, GuiGetFont().baseSize*2); + GuiLabel((Rectangle){ 40 + 48 + 10, 10, 700, 28 }, directory); + GuiSetStyle(DEFAULT, TEXT_SIZE, GuiGetFont().baseSize); + GuiSetStyle(LISTVIEW, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + GuiSetStyle(LISTVIEW, TEXT_PADDING, 40); + GuiListViewEx((Rectangle){ 0, 50, GetScreenWidth(), GetScreenHeight() - 40 }, + files.paths, files.count, &listScrollIndex, &listItemActive, &listItemFocused); + + /* for (int i = 0; i < (int)files.count; i++) { Color color = Fade(LIGHTGRAY, 0.3f); @@ -84,6 +99,7 @@ int main(void) DrawRectangle(0, 85 + 40*i, screenWidth, 40, color); DrawText(GetFileName(files.paths[i]), 120, 100 + 40*i, 10, GRAY); } + */ EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/examples/core/raygui.h b/examples/core/raygui.h index 88fe5cc5b..67c16be45 100644 --- a/examples/core/raygui.h +++ b/examples/core/raygui.h @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raygui v4.5-dev - A simple and easy-to-use immediate-mode gui library +* raygui v5.0-dev - A simple and easy-to-use immediate-mode gui library * * DESCRIPTION: * raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also @@ -83,8 +83,8 @@ * used for all controls, when any of those base values is set, it is automatically populated to all * controls, so, specific control values overwriting generic style should be set after base values * -* After the first BASE set we have the EXTENDED properties (by default guiStyle[16..23]), those -* properties are actually common to all controls and can not be overwritten individually (like BASE ones) +* After the first BASE properties set, the EXTENDED properties set is defined (by default guiStyle[16..23]), +* those properties are actually common to all controls and can not be overwritten individually (like BASE ones) * Some of those properties are: TEXT_SIZE, TEXT_SPACING, LINE_COLOR, BACKGROUND_COLOR * * Custom control properties can be defined using the EXTENDED properties for each independent control. @@ -141,13 +141,14 @@ * Draw text bounds rectangles for debug * * VERSIONS HISTORY: -* 5.0 (xx-Nov-2025) ADDED: Support up to 32 controls (v500) +* 5.0 (xx-Mar-2026) ADDED: Support up to 32 controls (v500) * ADDED: guiControlExclusiveMode and guiControlExclusiveRec for exclusive modes * ADDED: GuiValueBoxFloat() * ADDED: GuiDropdonwBox() properties: DROPDOWN_ARROW_HIDDEN, DROPDOWN_ROLL_UP * ADDED: GuiListView() property: LIST_ITEMS_BORDER_WIDTH * ADDED: GuiLoadIconsFromMemory() * ADDED: Multiple new icons +* ADDED: Macros for inputs customization, raylib decoupling * REMOVED: GuiSpinner() from controls list, using BUTTON + VALUEBOX properties * REMOVED: GuiSliderPro(), functionality was redundant * REVIEWED: Controls using text labels to use LABEL properties @@ -165,6 +166,7 @@ * REVIEWED: GuiTextBox(), multiple improvements: autocursor and more * REVIEWED: Functions descriptions, removed wrong return value reference * REDESIGNED: GuiColorPanel(), improved HSV <-> RGBA convertion +* REDESIGNED: WARNING: TEXT_LINE_SPACING does not consider text height, only lines spacing * * 4.0 (12-Sep-2023) ADDED: GuiToggleSlider() * ADDED: GuiColorPickerHSV() and GuiColorPanelHSV() @@ -316,7 +318,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2025 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2026 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -351,10 +353,11 @@ // NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll #if defined(_WIN32) #if defined(BUILD_LIBTYPE_SHARED) - #define RAYGUIAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll) + #define RAYGUIAPI __declspec(dllexport) // Building the library as a Win32 shared library (.dll) #elif defined(USE_LIBTYPE_SHARED) - #define RAYGUIAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll) + #define RAYGUIAPI __declspec(dllimport) // Using the library as a Win32 shared library (.dll) #endif + #define _CRT_SECURE_NO_WARNINGS // Disable unsafe warnings on scanf() functions in MSVC #endif // Function specifiers definition @@ -369,9 +372,48 @@ // NOTE: Avoiding those calls, also avoids const strings memory usage #define RAYGUI_SUPPORT_LOG_INFO #if defined(RAYGUI_SUPPORT_LOG_INFO) - #define RAYGUI_LOG(...) printf(__VA_ARGS__) + #define RAYGUI_LOG(...) printf(__VA_ARGS__) #else - #define RAYGUI_LOG(...) + #define RAYGUI_LOG(...) +#endif + +// Macros to define required UI inputs, including mapping to gamepad controls +// TODO: Define additionally required macros for missing inputs +#if !defined(GUI_BUTTON_DOWN) + #define GUI_BUTTON_DOWN (IsMouseButtonDown(MOUSE_LEFT_BUTTON) || IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) +#endif +#if !defined(GUI_BUTTON_DOWN_ALT) + // Mapping to alternative button down pressed + #define GUI_BUTTON_DOWN_ALT (IsMouseButtonDown(MOUSE_RIGHT_BUTTON) || IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT)) +#endif +#if !defined(GUI_BUTTON_PRESSED) + #define GUI_BUTTON_PRESSED (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsGamepadButtonPressed(0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) +#endif +// TODO: WARNING: GuiTabBar() still requires IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON) +#if !defined(GUI_BUTTON_RELEASED) + #define GUI_BUTTON_RELEASED (IsMouseButtonReleased(MOUSE_LEFT_BUTTON) || IsGamepadButtonReleased(0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) +#endif +#if !defined(GUI_SCROLL_DELTA) + // Mapping to scroll delta changes + // TODO: Review inconsistencies between platforms + #if defined(PLATFORM_WEB) + // NOTE: Gamepad axis triggers not detected on web platform + #define GUI_SCROLL_DELTA ((float)IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_TRIGGER_2) - (float)IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_TRIGGER_2)) + #else + #define GUI_SCROLL_DELTA (GetMouseWheelMove() + (GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_TRIGGER) + 1) - (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_TRIGGER) + 1)) + #endif +#endif +#if !defined(GUI_POINTER_POSITION) + #define GUI_POINTER_POSITION GetMousePosition() +#endif +#if !defined(GUI_KEY_DOWN) + #define GUI_KEY_DOWN(key) IsKeyDown(key) +#endif +#if !defined(GUI_KEY_PRESSED) + #define GUI_KEY_PRESSED(key) IsKeyPressed(key) +#endif +#if !defined(GUI_INPUT_KEY) + #define GUI_INPUT_KEY GetCharPressed() #endif //---------------------------------------------------------------------------------- @@ -567,7 +609,7 @@ typedef enum { //---------------------------------------------------------------------------------- // DEFAULT extended properties // NOTE: Those properties are common to all controls or global -// WARNING: We only have 8 slots for those properties by default!!! -> New global control: TEXT? +// WARNING: Only 8 slots vailable for those properties by default typedef enum { TEXT_SIZE = 16, // Text size (glyphs max height) TEXT_SPACING, // Text spacing between glyphs @@ -1091,7 +1133,7 @@ typedef enum { // Icons data is defined by bit array (every bit represents one pixel) // Those arrays are stored as unsigned int data arrays, so, // every array element defines 32 pixels (bits) of information -// One icon is defined by 8 int, (8 int * 32 bit = 256 bit = 16*16 pixels) +// One icon is defined by 8 int, (8 int*32 bit = 256 bit = 16*16 pixels) // NOTE: Number of elemens depend on RAYGUI_ICON_SIZE (by default 16x16 pixels) #define RAYGUI_ICON_DATA_ELEMENTS (RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32) @@ -1450,12 +1492,12 @@ static bool IsMouseButtonReleased(int button); static bool IsKeyDown(int key); static bool IsKeyPressed(int key); -static int GetCharPressed(void); // -- GuiTextBox(), GuiValueBox() +static int GetCharPressed(void); // -- GuiTextBox(), GuiValueBox() //------------------------------------------------------------------------------- // Drawing required functions //------------------------------------------------------------------------------- -static void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle() +static void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle() static void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker() //------------------------------------------------------------------------------- @@ -1520,11 +1562,11 @@ static Color GuiFade(Color color, float alpha); // Fade color by an alph // Gui Setup Functions Definition //---------------------------------------------------------------------------------- // Enable gui global state -// NOTE: We check for STATE_DISABLED to avoid messing custom global state setups +// NOTE: Checking for STATE_DISABLED to avoid messing custom global state setups void GuiEnable(void) { if (guiState == STATE_DISABLED) guiState = STATE_NORMAL; } // Disable gui global state -// NOTE: We check for STATE_NORMAL to avoid messing custom global state setups +// NOTE: Checking for STATE_NORMAL to avoid messing custom global state setups void GuiDisable(void) { if (guiState == STATE_NORMAL) guiState = STATE_DISABLED; } // Lock gui global state @@ -1557,9 +1599,8 @@ void GuiSetFont(Font font) { if (font.texture.id > 0) { - // NOTE: If we try to setup a font but default style has not been - // lazily loaded before, it will be overwritten, so we need to force - // default style loading first + // NOTE: If a font is tried to be set but default style has not been lazily loaded first, + // it will be overwritten, so default style loading needs to be forced first if (!guiStyleLoaded) GuiLoadStyleDefault(); guiFont = font; @@ -1613,13 +1654,14 @@ int GuiWindowBox(Rectangle bounds, const char *title) //GuiState state = guiState; int statusBarHeight = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT; + int statusBorderWidth = GuiGetStyle(STATUSBAR, BORDER_WIDTH); Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)statusBarHeight }; if (bounds.height < statusBarHeight*2.0f) bounds.height = statusBarHeight*2.0f; const float vPadding = statusBarHeight/2.0f - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT/2.0f; - Rectangle windowPanel = { bounds.x, bounds.y + (float)statusBarHeight - 1, bounds.width, bounds.height - (float)statusBarHeight + 1 }; - Rectangle closeButtonRec = { statusBar.x + statusBar.width - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT - vPadding, + Rectangle windowPanel = { bounds.x, bounds.y + (float)statusBarHeight - (float)statusBorderWidth, bounds.width, bounds.height - (float)statusBarHeight + (float)statusBorderWidth }; + Rectangle closeButtonRec = { statusBar.x + statusBar.width - (float)statusBorderWidth - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT - vPadding, statusBar.y + vPadding, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT }; // Update control @@ -1629,8 +1671,8 @@ int GuiWindowBox(Rectangle bounds, const char *title) // Draw control //-------------------------------------------------------------------- - GuiStatusBar(statusBar, title); // Draw window header as status bar GuiPanel(windowPanel, NULL); // Draw window base + GuiStatusBar(statusBar, title); // Draw window header as status bar // Draw window close button int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); @@ -1786,7 +1828,7 @@ int GuiTabBar(Rectangle bounds, const char **text, int count, int *active) } // Close tab with middle mouse button pressed - if (CheckCollisionPointRec(GetMousePosition(), tabBounds) && IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) result = i; + if (CheckCollisionPointRec(GUI_POINTER_POSITION, tabBounds) && IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) result = i; GuiSetStyle(TOGGLE, TEXT_PADDING, textPadding); GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, textAlignment); @@ -1885,37 +1927,37 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check button state if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; #if defined(SUPPORT_SCROLLBAR_KEY_INPUT) if (hasHorizontalScrollBar) { - if (IsKeyDown(KEY_RIGHT)) scrollPos.x -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); - if (IsKeyDown(KEY_LEFT)) scrollPos.x += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (GUI_KEY_DOWN(KEY_RIGHT)) scrollPos.x -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (GUI_KEY_DOWN(KEY_LEFT)) scrollPos.x += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); } if (hasVerticalScrollBar) { - if (IsKeyDown(KEY_DOWN)) scrollPos.y -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); - if (IsKeyDown(KEY_UP)) scrollPos.y += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (GUI_KEY_DOWN(KEY_DOWN)) scrollPos.y -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (GUI_KEY_DOWN(KEY_UP)) scrollPos.y += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); } #endif - float wheelMove = GetMouseWheelMove(); + float scrollDelta = GUI_SCROLL_DELTA; // Set scrolling speed with mouse wheel based on ratio between bounds and content - Vector2 mouseWheelSpeed = { content.width/bounds.width, content.height/bounds.height }; - if (mouseWheelSpeed.x < RAYGUI_MIN_MOUSE_WHEEL_SPEED) mouseWheelSpeed.x = RAYGUI_MIN_MOUSE_WHEEL_SPEED; - if (mouseWheelSpeed.y < RAYGUI_MIN_MOUSE_WHEEL_SPEED) mouseWheelSpeed.y = RAYGUI_MIN_MOUSE_WHEEL_SPEED; + Vector2 scrollSpeed = { content.width/bounds.width, content.height/bounds.height }; + if (scrollSpeed.x < RAYGUI_MIN_MOUSE_WHEEL_SPEED) scrollSpeed.x = RAYGUI_MIN_MOUSE_WHEEL_SPEED; + if (scrollSpeed.y < RAYGUI_MIN_MOUSE_WHEEL_SPEED) scrollSpeed.y = RAYGUI_MIN_MOUSE_WHEEL_SPEED; // Horizontal and vertical scrolling with mouse wheel - if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*mouseWheelSpeed.x; - else scrollPos.y += wheelMove*mouseWheelSpeed.y; // Vertical scroll + if (hasHorizontalScrollBar && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_LEFT_SHIFT))) scrollPos.x += scrollDelta*scrollSpeed.x; + else scrollPos.y += scrollDelta*scrollSpeed.y; // Vertical scroll } } @@ -2001,15 +2043,15 @@ int GuiButton(Rectangle bounds, const char *text) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check button state if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) result = 1; + if (GUI_BUTTON_RELEASED) result = 1; } } //-------------------------------------------------------------------- @@ -2031,7 +2073,7 @@ int GuiLabelButton(Rectangle bounds, const char *text) GuiState state = guiState; bool pressed = false; - // NOTE: We force bounds.width to be all text + // NOTE: Force bounds.width to be all text float textWidth = (float)GuiGetTextWidth(text); if ((bounds.width - 2*GuiGetStyle(LABEL, BORDER_WIDTH) - 2*GuiGetStyle(LABEL, TEXT_PADDING)) < textWidth) bounds.width = textWidth + 2*GuiGetStyle(LABEL, BORDER_WIDTH) + 2*GuiGetStyle(LABEL, TEXT_PADDING) + 2; @@ -2039,15 +2081,15 @@ int GuiLabelButton(Rectangle bounds, const char *text) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check checkbox state if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true; + if (GUI_BUTTON_RELEASED) pressed = true; } } //-------------------------------------------------------------------- @@ -2073,13 +2115,13 @@ int GuiToggle(Rectangle bounds, const char *text, bool *active) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check toggle button state if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; - else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else if (GUI_BUTTON_RELEASED) { state = STATE_NORMAL; *active = !(*active); @@ -2184,12 +2226,12 @@ int GuiToggleSlider(Rectangle bounds, const char *text, int *active) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; - else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else if (GUI_BUTTON_RELEASED) { state = STATE_PRESSED; (*active)++; @@ -2255,7 +2297,7 @@ int GuiCheckBox(Rectangle bounds, const char *text, bool *checked) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; Rectangle totalBounds = { (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT)? textBounds.x : bounds.x, @@ -2267,10 +2309,10 @@ int GuiCheckBox(Rectangle bounds, const char *text, bool *checked) // Check checkbox state if (CheckCollisionPointRec(mousePoint, totalBounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_RELEASED) { *checked = !(*checked); result = 1; @@ -2323,18 +2365,18 @@ int GuiComboBox(Rectangle bounds, const char *text, int *active) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1) && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) { - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_PRESSED) { *active += 1; if (*active >= itemCount) *active = 0; // Cyclic combobox } - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; } } @@ -2392,7 +2434,7 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1) && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (editMode) { @@ -2401,11 +2443,11 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod // Check if mouse has been pressed or released outside limits if (!CheckCollisionPointRec(mousePoint, boundsOpen)) { - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) result = 1; + if (GUI_BUTTON_PRESSED || GUI_BUTTON_RELEASED) result = 1; } // Check if already selected item has been pressed again - if (CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1; + if (CheckCollisionPointRec(mousePoint, bounds) && GUI_BUTTON_PRESSED) result = 1; // Check focused and selected item for (int i = 0; i < itemCount; i++) @@ -2417,7 +2459,7 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod if (CheckCollisionPointRec(mousePoint, itemBounds)) { itemFocused = i; - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_RELEASED) { itemSelected = i; result = 1; // Item selected @@ -2432,7 +2474,7 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod { if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_PRESSED) { result = 1; state = STATE_PRESSED; @@ -2506,7 +2548,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) int result = 0; GuiState state = guiState; - bool multiline = false; // TODO: Consider multiline text input + bool multiline = false; // TODO: Consider multiline text input int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); Rectangle textBounds = GetTextBounds(TEXTBOX, bounds); @@ -2514,7 +2556,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) int thisCursorIndex = textBoxCursorIndex; if (thisCursorIndex > textLength) thisCursorIndex = textLength; int textWidth = GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex); - int textIndexOffset = 0; // Text index offset to start drawing in the box + int textIndexOffset = 0; // Text index offset to start drawing in the box // Cursor rectangle // NOTE: Position X value should be updated @@ -2547,13 +2589,13 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) !guiControlExclusiveMode && // No gui slider on dragging (wrapMode == TEXT_WRAP_NONE)) // No wrap mode { - Vector2 mousePosition = GetMousePosition(); + Vector2 mousePosition = GUI_POINTER_POSITION; if (editMode) { // GLOBAL: Auto-cursor movement logic // NOTE: Keystrokes are handled repeatedly when button is held down for some time - if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCounter++; + if (GUI_KEY_DOWN(KEY_LEFT) || GUI_KEY_DOWN(KEY_RIGHT) || GUI_KEY_DOWN(KEY_UP) || GUI_KEY_DOWN(KEY_DOWN) || GUI_KEY_DOWN(KEY_BACKSPACE) || GUI_KEY_DOWN(KEY_DELETE)) autoCursorCounter++; else autoCursorCounter = 0; bool autoCursorShouldTrigger = (autoCursorCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) && ((autoCursorCounter % RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0); @@ -2563,7 +2605,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength; // If text does not fit in the textbox and current cursor position is out of bounds, - // we add an index offset to text for drawing only what requires depending on cursor + // adding an index offset to text for drawing only what requires depending on cursor while (textWidth >= textBounds.width) { int nextCodepointSize = 0; @@ -2574,15 +2616,15 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) textWidth = GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex); } - int codepoint = GetCharPressed(); // Get Unicode codepoint - if (multiline && IsKeyPressed(KEY_ENTER)) codepoint = (int)'\n'; + int codepoint = GUI_INPUT_KEY; // Get Unicode codepoint + if (multiline && GUI_KEY_PRESSED(KEY_ENTER)) codepoint = (int)'\n'; // Encode codepoint as UTF-8 int codepointSize = 0; const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize); // Handle text paste action - if (IsKeyPressed(KEY_V) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) + if (GUI_KEY_PRESSED(KEY_V) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) { const char *pasteText = GetClipboardText(); if (pasteText != NULL) @@ -2632,13 +2674,13 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) } // Move cursor to start - if ((textLength > 0) && IsKeyPressed(KEY_HOME)) textBoxCursorIndex = 0; + if ((textLength > 0) && GUI_KEY_PRESSED(KEY_HOME)) textBoxCursorIndex = 0; // Move cursor to end - if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) textBoxCursorIndex = textLength; + if ((textLength > textBoxCursorIndex) && GUI_KEY_PRESSED(KEY_END)) textBoxCursorIndex = textLength; // Delete related codepoints from text, after current cursor position - if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_DELETE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) + if ((textLength > textBoxCursorIndex) && GUI_KEY_PRESSED(KEY_DELETE) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) { int offset = textBoxCursorIndex; int accCodepointSize = 0; @@ -2674,7 +2716,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) textLength -= accCodepointSize; } - else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && autoCursorShouldTrigger))) + else if ((textLength > textBoxCursorIndex) && (GUI_KEY_PRESSED(KEY_DELETE) || (GUI_KEY_DOWN(KEY_DELETE) && autoCursorShouldTrigger))) { // Delete single codepoint from text, after current cursor position @@ -2688,12 +2730,12 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) } // Delete related codepoints from text, before current cursor position - if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_BACKSPACE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) + if ((textBoxCursorIndex > 0) && GUI_KEY_PRESSED(KEY_BACKSPACE) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) { int offset = textBoxCursorIndex; int accCodepointSize = 0; - int prevCodepointSize; - int prevCodepoint; + int prevCodepointSize = 0; + int prevCodepoint = 0; // Check whitespace to delete (ASCII only) while (offset > 0) @@ -2724,7 +2766,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) textBoxCursorIndex -= accCodepointSize; } - else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && autoCursorShouldTrigger))) + else if ((textBoxCursorIndex > 0) && (GUI_KEY_PRESSED(KEY_BACKSPACE) || (GUI_KEY_DOWN(KEY_BACKSPACE) && autoCursorShouldTrigger))) { // Delete single codepoint from text, before current cursor position @@ -2740,12 +2782,12 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) } // Move cursor position with keys - if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_LEFT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) + if ((textBoxCursorIndex > 0) && GUI_KEY_PRESSED(KEY_LEFT) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) { int offset = textBoxCursorIndex; //int accCodepointSize = 0; - int prevCodepointSize; - int prevCodepoint; + int prevCodepointSize = 0; + int prevCodepoint = 0; // Check whitespace to skip (ASCII only) while (offset > 0) @@ -2771,14 +2813,14 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) textBoxCursorIndex = offset; } - else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && autoCursorShouldTrigger))) + else if ((textBoxCursorIndex > 0) && (GUI_KEY_PRESSED(KEY_LEFT) || (GUI_KEY_DOWN(KEY_LEFT) && autoCursorShouldTrigger))) { int prevCodepointSize = 0; GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize); textBoxCursorIndex -= prevCodepointSize; } - else if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_RIGHT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) + else if ((textLength > textBoxCursorIndex) && GUI_KEY_PRESSED(KEY_RIGHT) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) { int offset = textBoxCursorIndex; //int accCodepointSize = 0; @@ -2810,7 +2852,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) textBoxCursorIndex = offset; } - else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && autoCursorShouldTrigger))) + else if ((textLength > textBoxCursorIndex) && (GUI_KEY_PRESSED(KEY_RIGHT) || (GUI_KEY_DOWN(KEY_RIGHT) && autoCursorShouldTrigger))) { int nextCodepointSize = 0; GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize); @@ -2847,14 +2889,14 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) // Check if mouse cursor is at the last position int textEndWidth = GuiGetTextWidth(text + textIndexOffset); - if (GetMousePosition().x >= (textBounds.x + textEndWidth - glyphWidth/2)) + if (GUI_POINTER_POSITION.x >= (textBounds.x + textEndWidth - glyphWidth/2)) { mouseCursor.x = textBounds.x + textEndWidth; mouseCursorIndex = textLength; } // Place cursor at required index on mouse click - if ((mouseCursor.x >= 0) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + if ((mouseCursor.x >= 0) && GUI_BUTTON_PRESSED) { cursor.x = mouseCursor.x; textBoxCursorIndex = mouseCursorIndex; @@ -2867,8 +2909,8 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) //if (multiline) cursor.y = GetTextLines() // Finish text editing on ENTER or mouse click outside bounds - if ((!multiline && IsKeyPressed(KEY_ENTER)) || - (!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) + if ((!multiline && GUI_KEY_PRESSED(KEY_ENTER)) || + (!CheckCollisionPointRec(mousePosition, bounds) && GUI_BUTTON_PRESSED)) { textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes @@ -2881,7 +2923,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) { state = STATE_FOCUSED; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_PRESSED) { textBoxCursorIndex = textLength; // GLOBAL: Place cursor index to the end of current text autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes @@ -2975,12 +3017,12 @@ int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check spinner state if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; } } @@ -3050,7 +3092,7 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; bool valueHasChanged = false; if (editMode) @@ -3060,7 +3102,7 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in int keyCount = (int)strlen(textValue); // Add or remove minus symbol - if (IsKeyPressed(KEY_MINUS)) + if (GUI_KEY_PRESSED(KEY_MINUS)) { if (textValue[0] == '-') { @@ -3089,8 +3131,8 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in // Add new digit to text value if ((keyCount >= 0) && (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) && (GuiGetTextWidth(textValue) < bounds.width)) { - int key = GetCharPressed(); - + int key = GUI_INPUT_KEY; + // Only allow keys in range [48..57] if ((key >= 48) && (key <= 57)) { @@ -3101,7 +3143,7 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in } // Delete text - if ((keyCount > 0) && IsKeyPressed(KEY_BACKSPACE)) + if ((keyCount > 0) && GUI_KEY_PRESSED(KEY_BACKSPACE)) { keyCount--; textValue[keyCount] = '\0'; @@ -3110,11 +3152,11 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in if (valueHasChanged) *value = TextToInteger(textValue); - // NOTE: We are not clamp values until user input finishes + // NOTE: Values are not clamped until user input finishes //if (*value > maxValue) *value = maxValue; //else if (*value < minValue) *value = minValue; - if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) + if ((GUI_KEY_PRESSED(KEY_ENTER) || GUI_KEY_PRESSED(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && GUI_BUTTON_PRESSED)) { if (*value > maxValue) *value = maxValue; else if (*value < minValue) *value = minValue; @@ -3130,7 +3172,7 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in if (CheckCollisionPointRec(mousePoint, bounds)) { state = STATE_FOCUSED; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1; + if (GUI_BUTTON_PRESSED) result = 1; } } } @@ -3191,7 +3233,7 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; bool valueHasChanged = false; @@ -3202,7 +3244,7 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float int keyCount = (int)strlen(textValue); // Add or remove minus symbol - if (IsKeyPressed(KEY_MINUS)) + if (GUI_KEY_PRESSED(KEY_MINUS)) { if (textValue[0] == '-') { @@ -3233,7 +3275,7 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float { if (GuiGetTextWidth(textValue) < bounds.width) { - int key = GetCharPressed(); + int key = GUI_INPUT_KEY; if (((key >= 48) && (key <= 57)) || (key == '.') || ((keyCount == 0) && (key == '+')) || // NOTE: Sign can only be in first position @@ -3248,7 +3290,7 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float } // Pressed backspace - if (IsKeyPressed(KEY_BACKSPACE)) + if (GUI_KEY_PRESSED(KEY_BACKSPACE)) { if (keyCount > 0) { @@ -3260,14 +3302,14 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float if (valueHasChanged) *value = TextToFloat(textValue); - if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) result = 1; + if ((GUI_KEY_PRESSED(KEY_ENTER) || GUI_KEY_PRESSED(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && GUI_BUTTON_PRESSED)) result = 1; } else { if (CheckCollisionPointRec(mousePoint, bounds)) { state = STATE_FOCUSED; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1; + if (GUI_BUTTON_PRESSED) result = 1; } } } @@ -3321,11 +3363,11 @@ int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, flo //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) { @@ -3342,7 +3384,7 @@ int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, flo } else if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { state = STATE_PRESSED; guiControlExclusiveMode = true; @@ -3535,12 +3577,12 @@ int GuiDummyRec(Rectangle bounds, const char *text) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check button state if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; } } @@ -3578,7 +3620,7 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd int itemFocused = (focus == NULL)? -1 : *focus; int itemSelected = (active == NULL)? -1 : *active; - // Check if we need a scroll bar + // Check if scroll bar is needed bool useScrollBar = false; if ((GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING))*count > bounds.height) useScrollBar = true; @@ -3602,7 +3644,7 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check mouse inside list view if (CheckCollisionPointRec(mousePoint, bounds)) @@ -3615,7 +3657,7 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd if (CheckCollisionPointRec(mousePoint, itemBounds)) { itemFocused = startIndex + i; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_PRESSED) { if (itemSelected == (startIndex + i)) itemSelected = -1; else itemSelected = startIndex + i; @@ -3629,8 +3671,8 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd if (useScrollBar) { - int wheelMove = (int)GetMouseWheelMove(); - startIndex -= wheelMove; + float scrollDelta = GUI_SCROLL_DELTA; + startIndex -= (int)scrollDelta; if (startIndex < 0) startIndex = 0; else if (startIndex > (count - visibleItems)) startIndex = count - visibleItems; @@ -3659,7 +3701,7 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd { if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED))); - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED))); + GuiDrawText(text[startIndex + i], GetTextBounds(LISTVIEW, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED))); } else { @@ -3667,18 +3709,18 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd { // Draw item selected GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED))); - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED))); + GuiDrawText(text[startIndex + i], GetTextBounds(LISTVIEW, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED))); } - else if (((startIndex + i) == itemFocused)) // && (focus != NULL)) // NOTE: We want items focused, despite not returned! + else if (((startIndex + i) == itemFocused)) // && (focus != NULL)) // NOTE: Items focused, despite not returned { // Draw item focused GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED))); - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED))); + GuiDrawText(text[startIndex + i], GetTextBounds(LISTVIEW, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED))); } else { // Draw item normal (no rectangle) - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL))); + GuiDrawText(text[startIndex + i], GetTextBounds(LISTVIEW, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL))); } } @@ -3765,11 +3807,11 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) { @@ -3788,7 +3830,7 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) } else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { state = STATE_PRESSED; guiControlExclusiveMode = true; @@ -3850,11 +3892,11 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) { @@ -3873,7 +3915,7 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) } else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { state = STATE_PRESSED; guiControlExclusiveMode = true; @@ -3886,12 +3928,12 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) } else state = STATE_FOCUSED; - /*if (IsKeyDown(KEY_UP)) + /*if (GUI_KEY_DOWN(KEY_UP)) { hue -= 2.0f; if (hue <= 0.0f) hue = 0.0f; } - else if (IsKeyDown(KEY_DOWN)) + else if (GUI_KEY_DOWN(KEY_DOWN)) { hue += 2.0f; if (hue >= 360.0f) hue = 360.0f; @@ -4008,11 +4050,11 @@ int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) { @@ -4042,7 +4084,7 @@ int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) } else if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { state = STATE_PRESSED; guiControlExclusiveMode = true; @@ -4198,6 +4240,9 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment); } + int prevTextBoxAlignment = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT); + GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + if (secretViewActive != NULL) { static char stars[] = "****************"; @@ -4211,6 +4256,8 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co if (GuiTextBox(textBoxBounds, text, textMaxSize, textEditMode)) textEditMode = !textEditMode; } + GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, prevTextBoxAlignment); + int prevBtnTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); @@ -4231,7 +4278,7 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co // Grid control // NOTE: Returns grid mouse-hover selected cell // About drawing lines at subpixel spacing, simple put, not easy solution: -// https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster +// REF: https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell) { // Grid lines alpha amount @@ -4242,7 +4289,7 @@ int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vect int result = 0; GuiState state = guiState; - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; Vector2 currentMouseCell = { -1, -1 }; float spaceWidth = spacing/(float)subdivs; @@ -4410,11 +4457,14 @@ void GuiLoadStyle(const char *fileName) if (fileDataSize > 0) { unsigned char *fileData = (unsigned char *)RAYGUI_CALLOC(fileDataSize, sizeof(unsigned char)); - fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile); + if (fileData != NULL) + { + fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile); - GuiLoadStyleFromMemory(fileData, fileDataSize); + GuiLoadStyleFromMemory(fileData, fileDataSize); - RAYGUI_FREE(fileData); + RAYGUI_FREE(fileData); + } } fclose(rgsFile); @@ -4425,7 +4475,7 @@ void GuiLoadStyle(const char *fileName) // Load style default over global style void GuiLoadStyleDefault(void) { - // We set this variable first to avoid cyclic function calls + // Setting this flag first to avoid cyclic function calls // when calling GuiSetStyle() and GuiGetStyle() guiStyleLoaded = true; @@ -4454,7 +4504,7 @@ void GuiLoadStyleDefault(void) GuiSetStyle(DEFAULT, TEXT_SPACING, 1); // DEFAULT, shared by all controls GuiSetStyle(DEFAULT, LINE_COLOR, 0x90abb5ff); // DEFAULT specific property GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0xf5f5f5ff); // DEFAULT specific property - GuiSetStyle(DEFAULT, TEXT_LINE_SPACING, 15); // DEFAULT, 15 pixels between lines + GuiSetStyle(DEFAULT, TEXT_LINE_SPACING, 5); // DEFAULT, pixels between lines, from bottom of first line to top of second GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE); // DEFAULT, text aligned vertically to middle of text-bounds // Initialize control-specific property values @@ -4520,7 +4570,7 @@ void GuiLoadStyleDefault(void) // NOTE: Default raylib font character 95 is a white square Rectangle whiteChar = guiFont.recs[95]; - // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering + // NOTE: Setting up a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering SetShapesTexture(guiFont.texture, RAYGUI_CLITERAL(Rectangle){ whiteChar.x + 1, whiteChar.y + 1, whiteChar.width - 2, whiteChar.height - 2 }); } } @@ -5037,14 +5087,14 @@ static Rectangle GetTextBounds(int control, Rectangle bounds) } // Get text icon if provided and move text cursor -// NOTE: We support up to 999 values for iconId +// NOTE: Up to #999# values supported for iconId static const char *GetTextIcon(const char *text, int *iconId) { #if !defined(RAYGUI_NO_ICONS) *iconId = -1; - if (text[0] == '#') // Maybe we have an icon! + if (text[0] == '#') // Maybe it is stars with an icon, ending # must be found { - char iconValue[4] = { 0 }; // Maximum length for icon value: 3 digits + '\0' + char iconValue[4] = { 0 }; // Maximum length for icon value: 3 digits + '\0' int pos = 1; while ((pos < 4) && (text[pos] >= '0') && (text[pos] <= '9')) @@ -5076,12 +5126,12 @@ static const char **GetTextLines(const char *text, int *count) static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings - int textSize = (int)strlen(text); + int textLength = (int)strlen(text); lines[0] = text; *count = 1; - for (int i = 0, k = 0; (i < textSize) && (*count < RAYGUI_MAX_TEXT_LINES); i++) + for (int i = 0, k = 0; (i < textLength) && (*count < RAYGUI_MAX_TEXT_LINES); i++) { if (text[i] == '\n') { @@ -5141,7 +5191,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C // - For every line, wordwrap mode is checked (useful for GuitextBox(), read-only) // Get text lines (using '\n' as delimiter) to be processed individually - // WARNING: We can't use GuiTextSplit() function because it can be already used + // WARNING: GuiTextSplit() function can't be used now because it can have already been used // before the GuiDrawText() call and its buffer is static, it would be overriden :( int lineCount = 0; const char **lines = GetTextLines(text, &lineCount); @@ -5152,7 +5202,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); // Wrap-mode only available in read-only mode, no for text editing // TODO: WARNING: This totalHeight is not valid for vertical alignment in case of word-wrap - float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_SIZE)/2); + float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); float posOffsetY = 0.0f; for (int i = 0; i < lineCount; i++) @@ -5165,7 +5215,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C Vector2 textBoundsPosition = { textBounds.x, textBounds.y }; float textBoundsWidthOffset = 0.0f; - // NOTE: We get text size after icon has been processed + // NOTE: Get text size after icon has been processed // WARNING: GuiGetTextWidth() also processes text icon to get width! -> Really needed? int textSizeX = GuiGetTextWidth(lines[i]); @@ -5200,8 +5250,8 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C default: break; } - // NOTE: Make sure we get pixel-perfect coordinates, - // In case of decimals we got weird text positioning + // NOTE: Make sure getting pixel-perfect coordinates, + // In case of decimals, it could result in text positioning artifacts textBoundsPosition.x = (float)((int)textBoundsPosition.x); textBoundsPosition.y = (float)((int)textBoundsPosition.y); //--------------------------------------------------------------------------------- @@ -5211,7 +5261,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C #if !defined(RAYGUI_NO_ICONS) if (iconId >= 0) { - // NOTE: We consider icon height, probably different than text size + // NOTE: Considering icon height, probably different than text size GuiDrawIcon(iconId, (int)textBoundsPosition.x, (int)(textBounds.y + textBounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height)), guiIconScale, tint); textBoundsPosition.x += (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); textBoundsWidthOffset = (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); @@ -5237,8 +5287,8 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C int codepoint = GetCodepointNext(&lines[i][c], &codepointSize); int index = GetGlyphIndex(guiFont, codepoint); - // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) - // but we need to draw all of the bad bytes using the '?' symbol moving one byte + // NOTE: Normally, exiting the decoding sequence as soon as a bad byte is found (and return 0x3f) + // but all of the bad bytes need to be drawn using the '?' symbol, moving one byte if (codepoint == 0x3f) codepointSize = 1; // TODO: Review not recognized codepoints size // Get glyph width to check if it goes out of bounds @@ -5253,7 +5303,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C if ((textOffsetX + glyphWidth) > textBounds.width - textBoundsWidthOffset) { textOffsetX = 0.0f; - textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); + textOffsetY += (GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); if (tempWrapCharMode) // Wrap at char level when too long words { @@ -5282,7 +5332,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C else if ((textOffsetX + nextSpaceWidth) > textBounds.width - textBoundsWidthOffset) { textOffsetX = 0.0f; - textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); + textOffsetY += (GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); } } @@ -5332,7 +5382,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C } } - if (wrapMode == TEXT_WRAP_NONE) posOffsetY += (float)GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); + if (wrapMode == TEXT_WRAP_NONE) posOffsetY += (float)(GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); else if ((wrapMode == TEXT_WRAP_CHAR) || (wrapMode == TEXT_WRAP_WORD)) posOffsetY += (textOffsetY + (float)GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); //--------------------------------------------------------------------------------- } @@ -5374,13 +5424,19 @@ static void GuiTooltip(Rectangle controlRec) if ((controlRec.x + textSize.x + 16) > GetScreenWidth()) controlRec.x -= (textSize.x + 16 - controlRec.width); - GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, NULL); + int lineCount = 0; + GetTextLines(guiTooltipPtr, &lineCount); // Only using the line count + if ((controlRec.y + controlRec.height + textSize.y + 4 + 8*lineCount) > GetScreenHeight()) + controlRec.y -= (controlRec.height + textSize.y + 4 + 8*lineCount); + + // TODO: Probably TEXT_LINE_SPACING should be considered on panel size instead of hardcoding 8.0f + GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, textSize.y + 8.0f*lineCount }, NULL); int textPadding = GuiGetStyle(LABEL, TEXT_PADDING); int textAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); GuiSetStyle(LABEL, TEXT_PADDING, 0); GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); - GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, guiTooltipPtr); + GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, textSize.y + 8.0f*lineCount }, guiTooltipPtr); GuiSetStyle(LABEL, TEXT_ALIGNMENT, textAlignment); GuiSetStyle(LABEL, TEXT_PADDING, textPadding); } @@ -5416,7 +5472,7 @@ static const char **GuiTextSplit(const char *text, char delimiter, int *count, i if (textRow != NULL) textRow[0] = 0; - // Count how many substrings we have on text and point to every one + // Count how many substrings text contains and point to every one of them for (int i = 0; i < RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE; i++) { buffer[i] = text[i]; @@ -5637,11 +5693,11 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON) && + if (GUI_BUTTON_DOWN && !CheckCollisionPointRec(mousePoint, arrowUpLeft) && !CheckCollisionPointRec(mousePoint, arrowDownRight)) { @@ -5664,11 +5720,11 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) state = STATE_FOCUSED; // Handle mouse wheel - int wheel = (int)GetMouseWheelMove(); - if (wheel != 0) value += wheel; + float scrollDelta = GUI_SCROLL_DELTA; + if (scrollDelta != 0) value += (int)scrollDelta; // Handle mouse button down - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_PRESSED) { guiControlExclusiveMode = true; guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts @@ -5690,13 +5746,13 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) /* if (isVertical) { - if (IsKeyDown(KEY_DOWN)) value += 5; - else if (IsKeyDown(KEY_UP)) value -= 5; + if (GUI_KEY_DOWN(KEY_DOWN)) value += 5; + else if (GUI_KEY_DOWN(KEY_UP)) value -= 5; } else { - if (IsKeyDown(KEY_RIGHT)) value += 5; - else if (IsKeyDown(KEY_LEFT)) value -= 5; + if (GUI_KEY_DOWN(KEY_RIGHT)) value += 5; + else if (GUI_KEY_DOWN(KEY_LEFT)) value -= 5; } */ } @@ -5833,7 +5889,7 @@ const char **TextSplit(const char *text, char delimiter, int *count) { counter = 1; - // Count how many substrings we have on text and point to every one + // Count how many substrings text contains and point to every one of them for (int i = 0; i < RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE; i++) { buffer[i] = text[i]; @@ -5866,7 +5922,7 @@ static int TextToInteger(const char *text) text++; } - for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); ++i) value = value*10 + (int)(text[i] - '0'); + for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0'); return value*sign; } @@ -5941,9 +5997,9 @@ static const char *CodepointToUTF8(int codepoint, int *byteSize) } // Get next codepoint in a UTF-8 encoded text, scanning until '\0' is found -// When a invalid UTF-8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned +// When a invalid UTF-8 byte is encountered, exiting as soon as possible and returning a '?'(0x3f) codepoint // Total number of bytes processed are returned as a parameter -// NOTE: the standard says U+FFFD should be returned in case of errors +// NOTE: The standard says U+FFFD should be returned in case of errors // but that character is not supported by the default font in raylib static int GetCodepointNext(const char *text, int *codepointSize) { diff --git a/examples/models/models_directional_billboard.c b/examples/models/models_directional_billboard.c index e2c75c15a..caab94afb 100644 --- a/examples/models/models_directional_billboard.c +++ b/examples/models/models_directional_billboard.c @@ -17,7 +17,9 @@ ********************************************************************************************/ #include "raylib.h" + #include "raymath.h" + #include //------------------------------------------------------------------------------------ diff --git a/examples/models/raygui.h b/examples/models/raygui.h new file mode 100644 index 000000000..67c16be45 --- /dev/null +++ b/examples/models/raygui.h @@ -0,0 +1,6043 @@ +/******************************************************************************************* +* +* raygui v5.0-dev - A simple and easy-to-use immediate-mode gui library +* +* DESCRIPTION: +* raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also +* available as a standalone library, as long as input and drawing functions are provided +* +* FEATURES: +* - Immediate-mode gui, minimal retained data +* - +25 controls provided (basic and advanced) +* - Styling system for colors, font and metrics +* - Icons supported, embedded as a 1-bit icons pack +* - Standalone mode option (custom input/graphics backend) +* - Multiple support tools provided for raygui development +* +* POSSIBLE IMPROVEMENTS: +* - Better standalone mode API for easy plug of custom backends +* - Externalize required inputs, allow user easier customization +* +* LIMITATIONS: +* - No editable multi-line word-wraped text box supported +* - No auto-layout mechanism, up to the user to define controls position and size +* - Standalone mode requires library modification and some user work to plug another backend +* +* NOTES: +* - WARNING: GuiLoadStyle() and GuiLoadStyle{Custom}() functions, allocate memory for +* font atlas recs and glyphs, freeing that memory is (usually) up to the user, +* no unload function is explicitly provided... but note that GuiLoadStyleDefault() unloads +* by default any previously loaded font (texture, recs, glyphs) +* - Global UI alpha (guiAlpha) is applied inside GuiDrawRectangle() and GuiDrawText() functions +* +* CONTROLS PROVIDED: +* # Container/separators Controls +* - WindowBox --> StatusBar, Panel +* - GroupBox --> Line +* - Line +* - Panel --> StatusBar +* - ScrollPanel --> StatusBar +* - TabBar --> Button +* +* # Basic Controls +* - Label +* - LabelButton --> Label +* - Button +* - Toggle +* - ToggleGroup --> Toggle +* - ToggleSlider +* - CheckBox +* - ComboBox +* - DropdownBox +* - TextBox +* - ValueBox --> TextBox +* - Spinner --> Button, ValueBox +* - Slider +* - SliderBar --> Slider +* - ProgressBar +* - StatusBar +* - DummyRec +* - Grid +* +* # Advance Controls +* - ListView +* - ColorPicker --> ColorPanel, ColorBarHue +* - MessageBox --> Window, Label, Button +* - TextInputBox --> Window, Label, TextBox, Button +* +* It also provides a set of functions for styling the controls based on its properties (size, color) +* +* +* RAYGUI STYLE (guiStyle): +* raygui uses a global data array for all gui style properties (allocated on data segment by default), +* when a new style is loaded, it is loaded over the global style... but a default gui style could always be +* recovered with GuiLoadStyleDefault() function, that overwrites the current style to the default one +* +* The global style array size is fixed and depends on the number of controls and properties: +* +* static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)]; +* +* guiStyle size is by default: 16*(16 + 8) = 384 int = 384*4 bytes = 1536 bytes = 1.5 KB +* +* Note that the first set of BASE properties (by default guiStyle[0..15]) belong to the generic style +* used for all controls, when any of those base values is set, it is automatically populated to all +* controls, so, specific control values overwriting generic style should be set after base values +* +* After the first BASE properties set, the EXTENDED properties set is defined (by default guiStyle[16..23]), +* those properties are actually common to all controls and can not be overwritten individually (like BASE ones) +* Some of those properties are: TEXT_SIZE, TEXT_SPACING, LINE_COLOR, BACKGROUND_COLOR +* +* Custom control properties can be defined using the EXTENDED properties for each independent control. +* +* TOOL: rGuiStyler is a visual tool to customize raygui style: github.com/raysan5/rguistyler +* +* +* RAYGUI ICONS (guiIcons): +* raygui could use a global array containing icons data (allocated on data segment by default), +* a custom icons set could be loaded over this array using GuiLoadIcons(), but loaded icons set +* must be same RAYGUI_ICON_SIZE and no more than RAYGUI_ICON_MAX_ICONS will be loaded +* +* Every icon is codified in binary form, using 1 bit per pixel, so, every 16x16 icon +* requires 8 integers (16*16/32) to be stored in memory. +* +* When the icon is draw, actually one quad per pixel is drawn if the bit for that pixel is set +* +* The global icons array size is fixed and depends on the number of icons and size: +* +* static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS]; +* +* guiIcons size is by default: 256*(16*16/32) = 2048*4 = 8192 bytes = 8 KB +* +* TOOL: rGuiIcons is a visual tool to customize/create raygui icons: github.com/raysan5/rguiicons +* +* RAYGUI LAYOUT: +* raygui currently does not provide an auto-layout mechanism like other libraries, +* layouts must be defined manually on controls drawing, providing the right bounds Rectangle for it +* +* TOOL: rGuiLayout is a visual tool to create raygui layouts: github.com/raysan5/rguilayout +* +* CONFIGURATION: +* #define RAYGUI_IMPLEMENTATION +* Generates the implementation of the library into the included file +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation +* +* #define RAYGUI_STANDALONE +* Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined +* internally in the library and input management and drawing functions must be provided by +* the user (check library implementation for further details) +* +* #define RAYGUI_NO_ICONS +* Avoid including embedded ricons data (256 icons, 16x16 pixels, 1-bit per pixel, 2KB) +* +* #define RAYGUI_CUSTOM_ICONS +* Includes custom ricons.h header defining a set of custom icons, +* this file can be generated using rGuiIcons tool +* +* #define RAYGUI_DEBUG_RECS_BOUNDS +* Draw control bounds rectangles for debug +* +* #define RAYGUI_DEBUG_TEXT_BOUNDS +* Draw text bounds rectangles for debug +* +* VERSIONS HISTORY: +* 5.0 (xx-Mar-2026) ADDED: Support up to 32 controls (v500) +* ADDED: guiControlExclusiveMode and guiControlExclusiveRec for exclusive modes +* ADDED: GuiValueBoxFloat() +* ADDED: GuiDropdonwBox() properties: DROPDOWN_ARROW_HIDDEN, DROPDOWN_ROLL_UP +* ADDED: GuiListView() property: LIST_ITEMS_BORDER_WIDTH +* ADDED: GuiLoadIconsFromMemory() +* ADDED: Multiple new icons +* ADDED: Macros for inputs customization, raylib decoupling +* REMOVED: GuiSpinner() from controls list, using BUTTON + VALUEBOX properties +* REMOVED: GuiSliderPro(), functionality was redundant +* REVIEWED: Controls using text labels to use LABEL properties +* REVIEWED: Replaced sprintf() by snprintf() for more safety +* REVIEWED: GuiTabBar(), close tab with mouse middle button +* REVIEWED: GuiScrollPanel(), scroll speed proportional to content +* REVIEWED: GuiDropdownBox(), support roll up and hidden arrow +* REVIEWED: GuiTextBox(), cursor position initialization +* REVIEWED: GuiSliderPro(), control value change check +* REVIEWED: GuiGrid(), simplified implementation +* REVIEWED: GuiIconText(), increase buffer size and reviewed padding +* REVIEWED: GuiDrawText(), improved wrap mode drawing +* REVIEWED: GuiScrollBar(), minor tweaks +* REVIEWED: GuiProgressBar(), improved borders computing +* REVIEWED: GuiTextBox(), multiple improvements: autocursor and more +* REVIEWED: Functions descriptions, removed wrong return value reference +* REDESIGNED: GuiColorPanel(), improved HSV <-> RGBA convertion +* REDESIGNED: WARNING: TEXT_LINE_SPACING does not consider text height, only lines spacing +* +* 4.0 (12-Sep-2023) ADDED: GuiToggleSlider() +* ADDED: GuiColorPickerHSV() and GuiColorPanelHSV() +* ADDED: Multiple new icons, mostly compiler related +* ADDED: New DEFAULT properties: TEXT_LINE_SPACING, TEXT_ALIGNMENT_VERTICAL, TEXT_WRAP_MODE +* ADDED: New enum values: GuiTextAlignment, GuiTextAlignmentVertical, GuiTextWrapMode +* ADDED: Support loading styles with custom font charset from external file +* REDESIGNED: GuiTextBox(), support mouse cursor positioning +* REDESIGNED: GuiDrawText(), support multiline and word-wrap modes (read only) +* REDESIGNED: GuiProgressBar() to be more visual, progress affects border color +* REDESIGNED: Global alpha consideration moved to GuiDrawRectangle() and GuiDrawText() +* REDESIGNED: GuiScrollPanel(), get parameters by reference and return result value +* REDESIGNED: GuiToggleGroup(), get parameters by reference and return result value +* REDESIGNED: GuiComboBox(), get parameters by reference and return result value +* REDESIGNED: GuiCheckBox(), get parameters by reference and return result value +* REDESIGNED: GuiSlider(), get parameters by reference and return result value +* REDESIGNED: GuiSliderBar(), get parameters by reference and return result value +* REDESIGNED: GuiProgressBar(), get parameters by reference and return result value +* REDESIGNED: GuiListView(), get parameters by reference and return result value +* REDESIGNED: GuiColorPicker(), get parameters by reference and return result value +* REDESIGNED: GuiColorPanel(), get parameters by reference and return result value +* REDESIGNED: GuiColorBarAlpha(), get parameters by reference and return result value +* REDESIGNED: GuiColorBarHue(), get parameters by reference and return result value +* REDESIGNED: GuiGrid(), get parameters by reference and return result value +* REDESIGNED: GuiGrid(), added extra parameter +* REDESIGNED: GuiListViewEx(), change parameters order +* REDESIGNED: All controls return result as int value +* REVIEWED: GuiScrollPanel() to avoid smallish scroll-bars +* REVIEWED: All examples and specially controls_test_suite +* RENAMED: gui_file_dialog module to gui_window_file_dialog +* UPDATED: All styles to include ISO-8859-15 charset (as much as possible) +* +* 3.6 (10-May-2023) ADDED: New icon: SAND_TIMER +* ADDED: GuiLoadStyleFromMemory() (binary only) +* REVIEWED: GuiScrollBar() horizontal movement key +* REVIEWED: GuiTextBox() crash on cursor movement +* REVIEWED: GuiTextBox(), additional inputs support +* REVIEWED: GuiLabelButton(), avoid text cut +* REVIEWED: GuiTextInputBox(), password input +* REVIEWED: Local GetCodepointNext(), aligned with raylib +* REDESIGNED: GuiSlider*()/GuiScrollBar() to support out-of-bounds +* +* 3.5 (20-Apr-2023) ADDED: GuiTabBar(), based on GuiToggle() +* ADDED: Helper functions to split text in separate lines +* ADDED: Multiple new icons, useful for code editing tools +* REMOVED: Unneeded icon editing functions +* REMOVED: GuiTextBoxMulti(), very limited and broken +* REMOVED: MeasureTextEx() dependency, logic directly implemented +* REMOVED: DrawTextEx() dependency, logic directly implemented +* REVIEWED: GuiScrollBar(), improve mouse-click behaviour +* REVIEWED: Library header info, more info, better organized +* REDESIGNED: GuiTextBox() to support cursor movement +* REDESIGNED: GuiDrawText() to divide drawing by lines +* +* 3.2 (22-May-2022) RENAMED: Some enum values, for unification, avoiding prefixes +* REMOVED: GuiScrollBar(), only internal +* REDESIGNED: GuiPanel() to support text parameter +* REDESIGNED: GuiScrollPanel() to support text parameter +* REDESIGNED: GuiColorPicker() to support text parameter +* REDESIGNED: GuiColorPanel() to support text parameter +* REDESIGNED: GuiColorBarAlpha() to support text parameter +* REDESIGNED: GuiColorBarHue() to support text parameter +* REDESIGNED: GuiTextInputBox() to support password +* +* 3.1 (12-Jan-2022) REVIEWED: Default style for consistency (aligned with rGuiLayout v2.5 tool) +* REVIEWED: GuiLoadStyle() to support compressed font atlas image data and unload previous textures +* REVIEWED: External icons usage logic +* REVIEWED: GuiLine() for centered alignment when including text +* RENAMED: Multiple controls properties definitions to prepend RAYGUI_ +* RENAMED: RICON_ references to RAYGUI_ICON_ for library consistency +* Projects updated and multiple tweaks +* +* 3.0 (04-Nov-2021) Integrated ricons data to avoid external file +* REDESIGNED: GuiTextBoxMulti() +* REMOVED: GuiImageButton*() +* Multiple minor tweaks and bugs corrected +* +* 2.9 (17-Mar-2021) REMOVED: Tooltip API +* 2.8 (03-May-2020) Centralized rectangles drawing to GuiDrawRectangle() +* 2.7 (20-Feb-2020) ADDED: Possible tooltips API +* 2.6 (09-Sep-2019) ADDED: GuiTextInputBox() +* REDESIGNED: GuiListView*(), GuiDropdownBox(), GuiSlider*(), GuiProgressBar(), GuiMessageBox() +* REVIEWED: GuiTextBox(), GuiSpinner(), GuiValueBox(), GuiLoadStyle() +* Replaced property INNER_PADDING by TEXT_PADDING, renamed some properties +* ADDED: 8 new custom styles ready to use +* Multiple minor tweaks and bugs corrected +* +* 2.5 (28-May-2019) Implemented extended GuiTextBox(), GuiValueBox(), GuiSpinner() +* 2.3 (29-Apr-2019) ADDED: rIcons auxiliar library and support for it, multiple controls reviewed +* Refactor all controls drawing mechanism to use control state +* 2.2 (05-Feb-2019) ADDED: GuiScrollBar(), GuiScrollPanel(), reviewed GuiListView(), removed Gui*Ex() controls +* 2.1 (26-Dec-2018) REDESIGNED: GuiCheckBox(), GuiComboBox(), GuiDropdownBox(), GuiToggleGroup() > Use combined text string +* REDESIGNED: Style system (breaking change) +* 2.0 (08-Nov-2018) ADDED: Support controls guiLock and custom fonts +* REVIEWED: GuiComboBox(), GuiListView()... +* 1.9 (09-Oct-2018) REVIEWED: GuiGrid(), GuiTextBox(), GuiTextBoxMulti(), GuiValueBox()... +* 1.8 (01-May-2018) Lot of rework and redesign to align with rGuiStyler and rGuiLayout +* 1.5 (21-Jun-2017) Working in an improved styles system +* 1.4 (15-Jun-2017) Rewritten all GUI functions (removed useless ones) +* 1.3 (12-Jun-2017) Complete redesign of style system +* 1.1 (01-Jun-2017) Complete review of the library +* 1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria +* 0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria +* 0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria +* +* DEPENDENCIES: +* raylib 5.6-dev - Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing +* +* STANDALONE MODE: +* By default raygui depends on raylib mostly for the inputs and the drawing functionality but that dependency can be disabled +* with the config flag RAYGUI_STANDALONE. In that case is up to the user to provide another backend to cover library needs +* +* The following functions should be redefined for a custom backend: +* +* - Vector2 GetMousePosition(void); +* - float GetMouseWheelMove(void); +* - bool IsMouseButtonDown(int button); +* - bool IsMouseButtonPressed(int button); +* - bool IsMouseButtonReleased(int button); +* - bool IsKeyDown(int key); +* - bool IsKeyPressed(int key); +* - int GetCharPressed(void); // -- GuiTextBox(), GuiValueBox() +* +* - void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle() +* - void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker() +* +* - Font GetFontDefault(void); // -- GuiLoadStyleDefault() +* - Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // -- GuiLoadStyle() +* - Texture2D LoadTextureFromImage(Image image); // -- GuiLoadStyle(), required to load texture from embedded font atlas image +* - void SetShapesTexture(Texture2D tex, Rectangle rec); // -- GuiLoadStyle(), required to set shapes rec to font white rec (optimization) +* - char *LoadFileText(const char *fileName); // -- GuiLoadStyle(), required to load charset data +* - void UnloadFileText(char *text); // -- GuiLoadStyle(), required to unload charset data +* - const char *GetDirectoryPath(const char *filePath); // -- GuiLoadStyle(), required to find charset/font file from text .rgs +* - int *LoadCodepoints(const char *text, int *count); // -- GuiLoadStyle(), required to load required font codepoints list +* - void UnloadCodepoints(int *codepoints); // -- GuiLoadStyle(), required to unload codepoints list +* - unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // -- GuiLoadStyle() +* +* CONTRIBUTORS: +* Ramon Santamaria: Supervision, review, redesign, update and maintenance +* Vlad Adrian: Complete rewrite of GuiTextBox() to support extended features (2019) +* Sergio Martinez: Review, testing (2015) and redesign of multiple controls (2018) +* Adria Arranz: Testing and implementation of additional controls (2018) +* Jordi Jorba: Testing and implementation of additional controls (2018) +* Albert Martos: Review and testing of the library (2015) +* Ian Eito: Review and testing of the library (2015) +* Kevin Gato: Initial implementation of basic components (2014) +* Daniel Nicolas: Initial implementation of basic components (2014) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2014-2026 Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef RAYGUI_H +#define RAYGUI_H + +#define RAYGUI_VERSION_MAJOR 4 +#define RAYGUI_VERSION_MINOR 5 +#define RAYGUI_VERSION_PATCH 0 +#define RAYGUI_VERSION "5.0-dev" + +#if !defined(RAYGUI_STANDALONE) + #include "raylib.h" +#endif + +// Function specifiers in case library is build/used as a shared library (Windows) +// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll +#if defined(_WIN32) + #if defined(BUILD_LIBTYPE_SHARED) + #define RAYGUIAPI __declspec(dllexport) // Building the library as a Win32 shared library (.dll) + #elif defined(USE_LIBTYPE_SHARED) + #define RAYGUIAPI __declspec(dllimport) // Using the library as a Win32 shared library (.dll) + #endif + #define _CRT_SECURE_NO_WARNINGS // Disable unsafe warnings on scanf() functions in MSVC +#endif + +// Function specifiers definition +#ifndef RAYGUIAPI + #define RAYGUIAPI // Functions defined as 'extern' by default (implicit specifiers) +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// Simple log system to avoid printf() calls if required +// NOTE: Avoiding those calls, also avoids const strings memory usage +#define RAYGUI_SUPPORT_LOG_INFO +#if defined(RAYGUI_SUPPORT_LOG_INFO) + #define RAYGUI_LOG(...) printf(__VA_ARGS__) +#else + #define RAYGUI_LOG(...) +#endif + +// Macros to define required UI inputs, including mapping to gamepad controls +// TODO: Define additionally required macros for missing inputs +#if !defined(GUI_BUTTON_DOWN) + #define GUI_BUTTON_DOWN (IsMouseButtonDown(MOUSE_LEFT_BUTTON) || IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) +#endif +#if !defined(GUI_BUTTON_DOWN_ALT) + // Mapping to alternative button down pressed + #define GUI_BUTTON_DOWN_ALT (IsMouseButtonDown(MOUSE_RIGHT_BUTTON) || IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT)) +#endif +#if !defined(GUI_BUTTON_PRESSED) + #define GUI_BUTTON_PRESSED (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsGamepadButtonPressed(0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) +#endif +// TODO: WARNING: GuiTabBar() still requires IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON) +#if !defined(GUI_BUTTON_RELEASED) + #define GUI_BUTTON_RELEASED (IsMouseButtonReleased(MOUSE_LEFT_BUTTON) || IsGamepadButtonReleased(0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) +#endif +#if !defined(GUI_SCROLL_DELTA) + // Mapping to scroll delta changes + // TODO: Review inconsistencies between platforms + #if defined(PLATFORM_WEB) + // NOTE: Gamepad axis triggers not detected on web platform + #define GUI_SCROLL_DELTA ((float)IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_TRIGGER_2) - (float)IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_TRIGGER_2)) + #else + #define GUI_SCROLL_DELTA (GetMouseWheelMove() + (GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_TRIGGER) + 1) - (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_TRIGGER) + 1)) + #endif +#endif +#if !defined(GUI_POINTER_POSITION) + #define GUI_POINTER_POSITION GetMousePosition() +#endif +#if !defined(GUI_KEY_DOWN) + #define GUI_KEY_DOWN(key) IsKeyDown(key) +#endif +#if !defined(GUI_KEY_PRESSED) + #define GUI_KEY_PRESSED(key) IsKeyPressed(key) +#endif +#if !defined(GUI_INPUT_KEY) + #define GUI_INPUT_KEY GetCharPressed() +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +// NOTE: Some types are required for RAYGUI_STANDALONE usage +//---------------------------------------------------------------------------------- +#if defined(RAYGUI_STANDALONE) + #ifndef __cplusplus + // Boolean type + #ifndef true + typedef enum { false, true } bool; + #endif + #endif + + // Vector2 type + typedef struct Vector2 { + float x; + float y; + } Vector2; + + // Vector3 type // -- ConvertHSVtoRGB(), ConvertRGBtoHSV() + typedef struct Vector3 { + float x; + float y; + float z; + } Vector3; + + // Color type, RGBA (32bit) + typedef struct Color { + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; + } Color; + + // Rectangle type + typedef struct Rectangle { + float x; + float y; + float width; + float height; + } Rectangle; + + // TODO: Texture2D type is very coupled to raylib, required by Font type + // It should be redesigned to be provided by user + typedef struct Texture { + unsigned int id; // OpenGL texture id + int width; // Texture base width + int height; // Texture base height + int mipmaps; // Mipmap levels, 1 by default + int format; // Data format (PixelFormat type) + } Texture; + + // Texture2D, same as Texture + typedef Texture Texture2D; + + // Image, pixel data stored in CPU memory (RAM) + typedef struct Image { + void *data; // Image raw data + int width; // Image base width + int height; // Image base height + int mipmaps; // Mipmap levels, 1 by default + int format; // Data format (PixelFormat type) + } Image; + + // GlyphInfo, font characters glyphs info + typedef struct GlyphInfo { + int value; // Character value (Unicode) + int offsetX; // Character offset X when drawing + int offsetY; // Character offset Y when drawing + int advanceX; // Character advance position X + Image image; // Character image data + } GlyphInfo; + + // TODO: Font type is very coupled to raylib, mostly required by GuiLoadStyle() + // It should be redesigned to be provided by user + typedef struct Font { + int baseSize; // Base size (default chars height) + int glyphCount; // Number of glyph characters + int glyphPadding; // Padding around the glyph characters + Texture2D texture; // Texture atlas containing the glyphs + Rectangle *recs; // Rectangles in texture for the glyphs + GlyphInfo *glyphs; // Glyphs info data + } Font; +#endif + +// Style property +// NOTE: Used when exporting style as code for convenience +typedef struct GuiStyleProp { + unsigned short controlId; // Control identifier + unsigned short propertyId; // Property identifier + int propertyValue; // Property value +} GuiStyleProp; + +/* +// Controls text style -NOT USED- +// NOTE: Text style is defined by control +typedef struct GuiTextStyle { + unsigned int size; + int charSpacing; + int lineSpacing; + int alignmentH; + int alignmentV; + int padding; +} GuiTextStyle; +*/ + +// Gui control state +typedef enum { + STATE_NORMAL = 0, + STATE_FOCUSED, + STATE_PRESSED, + STATE_DISABLED +} GuiState; + +// Gui control text alignment +typedef enum { + TEXT_ALIGN_LEFT = 0, + TEXT_ALIGN_CENTER, + TEXT_ALIGN_RIGHT +} GuiTextAlignment; + +// Gui control text alignment vertical +// NOTE: Text vertical position inside the text bounds +typedef enum { + TEXT_ALIGN_TOP = 0, + TEXT_ALIGN_MIDDLE, + TEXT_ALIGN_BOTTOM +} GuiTextAlignmentVertical; + +// Gui control text wrap mode +// NOTE: Useful for multiline text +typedef enum { + TEXT_WRAP_NONE = 0, + TEXT_WRAP_CHAR, + TEXT_WRAP_WORD +} GuiTextWrapMode; + +// Gui controls +typedef enum { + // Default -> populates to all controls when set + DEFAULT = 0, + + // Basic controls + LABEL, // Used also for: LABELBUTTON + BUTTON, + TOGGLE, // Used also for: TOGGLEGROUP + SLIDER, // Used also for: SLIDERBAR, TOGGLESLIDER + PROGRESSBAR, + CHECKBOX, + COMBOBOX, + DROPDOWNBOX, + TEXTBOX, // Used also for: TEXTBOXMULTI + VALUEBOX, + CONTROL11, + LISTVIEW, + COLORPICKER, + SCROLLBAR, + STATUSBAR +} GuiControl; + +// Gui base properties for every control +// NOTE: RAYGUI_MAX_PROPS_BASE properties (by default 16 properties) +typedef enum { + BORDER_COLOR_NORMAL = 0, // Control border color in STATE_NORMAL + BASE_COLOR_NORMAL, // Control base color in STATE_NORMAL + TEXT_COLOR_NORMAL, // Control text color in STATE_NORMAL + BORDER_COLOR_FOCUSED, // Control border color in STATE_FOCUSED + BASE_COLOR_FOCUSED, // Control base color in STATE_FOCUSED + TEXT_COLOR_FOCUSED, // Control text color in STATE_FOCUSED + BORDER_COLOR_PRESSED, // Control border color in STATE_PRESSED + BASE_COLOR_PRESSED, // Control base color in STATE_PRESSED + TEXT_COLOR_PRESSED, // Control text color in STATE_PRESSED + BORDER_COLOR_DISABLED, // Control border color in STATE_DISABLED + BASE_COLOR_DISABLED, // Control base color in STATE_DISABLED + TEXT_COLOR_DISABLED, // Control text color in STATE_DISABLED + BORDER_WIDTH = 12, // Control border size, 0 for no border + //TEXT_SIZE, // Control text size (glyphs max height) -> GLOBAL for all controls + //TEXT_SPACING, // Control text spacing between glyphs -> GLOBAL for all controls + //TEXT_LINE_SPACING, // Control text spacing between lines -> GLOBAL for all controls + TEXT_PADDING = 13, // Control text padding, not considering border + TEXT_ALIGNMENT = 14, // Control text horizontal alignment inside control text bound (after border and padding) + //TEXT_WRAP_MODE // Control text wrap-mode inside text bounds -> GLOBAL for all controls +} GuiControlProperty; + +// TODO: Which text styling properties should be global or per-control? +// At this moment TEXT_PADDING and TEXT_ALIGNMENT is configured and saved per control while +// TEXT_SIZE, TEXT_SPACING, TEXT_LINE_SPACING, TEXT_ALIGNMENT_VERTICAL, TEXT_WRAP_MODE are global and +// should be configured by user as needed while defining the UI layout + +// Gui extended properties depend on control +// NOTE: RAYGUI_MAX_PROPS_EXTENDED properties (by default, max 8 properties) +//---------------------------------------------------------------------------------- +// DEFAULT extended properties +// NOTE: Those properties are common to all controls or global +// WARNING: Only 8 slots vailable for those properties by default +typedef enum { + TEXT_SIZE = 16, // Text size (glyphs max height) + TEXT_SPACING, // Text spacing between glyphs + LINE_COLOR, // Line control color + BACKGROUND_COLOR, // Background color + TEXT_LINE_SPACING, // Text spacing between lines + TEXT_ALIGNMENT_VERTICAL, // Text vertical alignment inside text bounds (after border and padding) + TEXT_WRAP_MODE // Text wrap-mode inside text bounds + //TEXT_DECORATION // Text decoration: 0-None, 1-Underline, 2-Line-through, 3-Overline + //TEXT_DECORATION_THICK // Text decoration line thickness +} GuiDefaultProperty; + +// Other possible text properties: +// TEXT_WEIGHT // Normal, Italic, Bold -> Requires specific font change +// TEXT_INDENT // Text indentation -> Now using TEXT_PADDING... + +// Label +//typedef enum { } GuiLabelProperty; + +// Button/Spinner +//typedef enum { } GuiButtonProperty; + +// Toggle/ToggleGroup +typedef enum { + GROUP_PADDING = 16, // ToggleGroup separation between toggles +} GuiToggleProperty; + +// Slider/SliderBar +typedef enum { + SLIDER_WIDTH = 16, // Slider size of internal bar + SLIDER_PADDING // Slider/SliderBar internal bar padding +} GuiSliderProperty; + +// ProgressBar +typedef enum { + PROGRESS_PADDING = 16, // ProgressBar internal padding +} GuiProgressBarProperty; + +// ScrollBar +typedef enum { + ARROWS_SIZE = 16, // ScrollBar arrows size + ARROWS_VISIBLE, // ScrollBar arrows visible + SCROLL_SLIDER_PADDING, // ScrollBar slider internal padding + SCROLL_SLIDER_SIZE, // ScrollBar slider size + SCROLL_PADDING, // ScrollBar scroll padding from arrows + SCROLL_SPEED, // ScrollBar scrolling speed +} GuiScrollBarProperty; + +// CheckBox +typedef enum { + CHECK_PADDING = 16 // CheckBox internal check padding +} GuiCheckBoxProperty; + +// ComboBox +typedef enum { + COMBO_BUTTON_WIDTH = 16, // ComboBox right button width + COMBO_BUTTON_SPACING // ComboBox button separation +} GuiComboBoxProperty; + +// DropdownBox +typedef enum { + ARROW_PADDING = 16, // DropdownBox arrow separation from border and items + DROPDOWN_ITEMS_SPACING, // DropdownBox items separation + DROPDOWN_ARROW_HIDDEN, // DropdownBox arrow hidden + DROPDOWN_ROLL_UP // DropdownBox roll up flag (default rolls down) +} GuiDropdownBoxProperty; + +// TextBox/TextBoxMulti/ValueBox/Spinner +typedef enum { + TEXT_READONLY = 16, // TextBox in read-only mode: 0-text editable, 1-text no-editable +} GuiTextBoxProperty; + +// ValueBox/Spinner +typedef enum { + SPINNER_BUTTON_WIDTH = 16, // Spinner left/right buttons width + SPINNER_BUTTON_SPACING, // Spinner buttons separation +} GuiValueBoxProperty; + +// Control11 +//typedef enum { } GuiControl11Property; + +// ListView +typedef enum { + LIST_ITEMS_HEIGHT = 16, // ListView items height + LIST_ITEMS_SPACING, // ListView items separation + SCROLLBAR_WIDTH, // ListView scrollbar size (usually width) + SCROLLBAR_SIDE, // ListView scrollbar side (0-SCROLLBAR_LEFT_SIDE, 1-SCROLLBAR_RIGHT_SIDE) + LIST_ITEMS_BORDER_NORMAL, // ListView items border enabled in normal state + LIST_ITEMS_BORDER_WIDTH // ListView items border width +} GuiListViewProperty; + +// ColorPicker +typedef enum { + COLOR_SELECTOR_SIZE = 16, + HUEBAR_WIDTH, // ColorPicker right hue bar width + HUEBAR_PADDING, // ColorPicker right hue bar separation from panel + HUEBAR_SELECTOR_HEIGHT, // ColorPicker right hue bar selector height + HUEBAR_SELECTOR_OVERFLOW // ColorPicker right hue bar selector overflow +} GuiColorPickerProperty; + +#define SCROLLBAR_LEFT_SIDE 0 +#define SCROLLBAR_RIGHT_SIDE 1 + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +// ... + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- + +#if defined(__cplusplus) +extern "C" { // Prevents name mangling of functions +#endif + +// Global gui state control functions +RAYGUIAPI void GuiEnable(void); // Enable gui controls (global state) +RAYGUIAPI void GuiDisable(void); // Disable gui controls (global state) +RAYGUIAPI void GuiLock(void); // Lock gui controls (global state) +RAYGUIAPI void GuiUnlock(void); // Unlock gui controls (global state) +RAYGUIAPI bool GuiIsLocked(void); // Check if gui is locked (global state) +RAYGUIAPI void GuiSetAlpha(float alpha); // Set gui controls alpha (global state), alpha goes from 0.0f to 1.0f +RAYGUIAPI void GuiSetState(int state); // Set gui state (global state) +RAYGUIAPI int GuiGetState(void); // Get gui state (global state) + +// Font set/get functions +RAYGUIAPI void GuiSetFont(Font font); // Set gui custom font (global state) +RAYGUIAPI Font GuiGetFont(void); // Get gui custom font (global state) + +// Style set/get functions +RAYGUIAPI void GuiSetStyle(int control, int property, int value); // Set one style property +RAYGUIAPI int GuiGetStyle(int control, int property); // Get one style property + +// Styles loading functions +RAYGUIAPI void GuiLoadStyle(const char *fileName); // Load style file over global style variable (.rgs) +RAYGUIAPI void GuiLoadStyleDefault(void); // Load style default over global style + +// Tooltips management functions +RAYGUIAPI void GuiEnableTooltip(void); // Enable gui tooltips (global state) +RAYGUIAPI void GuiDisableTooltip(void); // Disable gui tooltips (global state) +RAYGUIAPI void GuiSetTooltip(const char *tooltip); // Set tooltip string + +// Icons functionality +RAYGUIAPI const char *GuiIconText(int iconId, const char *text); // Get text with icon id prepended (if supported) +#if !defined(RAYGUI_NO_ICONS) +RAYGUIAPI void GuiSetIconScale(int scale); // Set default icon drawing size +RAYGUIAPI unsigned int *GuiGetIcons(void); // Get raygui icons data pointer +RAYGUIAPI char **GuiLoadIcons(const char *fileName, bool loadIconsName); // Load raygui icons file (.rgi) into internal icons data +RAYGUIAPI void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color); // Draw icon using pixel size at specified position +#endif + +// Utility functions +RAYGUIAPI int GuiGetTextWidth(const char *text); // Get text width considering gui style and icon size (if required) + +// Controls +//---------------------------------------------------------------------------------------------------------- +// Container/separator controls, useful for controls organization +RAYGUIAPI int GuiWindowBox(Rectangle bounds, const char *title); // Window Box control, shows a window that can be closed +RAYGUIAPI int GuiGroupBox(Rectangle bounds, const char *text); // Group Box control with text name +RAYGUIAPI int GuiLine(Rectangle bounds, const char *text); // Line separator control, could contain text +RAYGUIAPI int GuiPanel(Rectangle bounds, const char *text); // Panel control, useful to group controls +RAYGUIAPI int GuiTabBar(Rectangle bounds, const char **text, int count, int *active); // Tab Bar control, returns TAB to be closed or -1 +RAYGUIAPI int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view); // Scroll Panel control + +// Basic controls set +RAYGUIAPI int GuiLabel(Rectangle bounds, const char *text); // Label control +RAYGUIAPI int GuiButton(Rectangle bounds, const char *text); // Button control, returns true when clicked +RAYGUIAPI int GuiLabelButton(Rectangle bounds, const char *text); // Label button control, returns true when clicked +RAYGUIAPI int GuiToggle(Rectangle bounds, const char *text, bool *active); // Toggle Button control +RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int *active); // Toggle Group control +RAYGUIAPI int GuiToggleSlider(Rectangle bounds, const char *text, int *active); // Toggle Slider control +RAYGUIAPI int GuiCheckBox(Rectangle bounds, const char *text, bool *checked); // Check Box control, returns true when active +RAYGUIAPI int GuiComboBox(Rectangle bounds, const char *text, int *active); // Combo Box control + +RAYGUIAPI int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode); // Dropdown Box control +RAYGUIAPI int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Spinner control +RAYGUIAPI int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Value Box control, updates input text with numbers +RAYGUIAPI int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode); // Value box control for float values +RAYGUIAPI int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control, updates input text + +RAYGUIAPI int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider control +RAYGUIAPI int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider Bar control +RAYGUIAPI int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Progress Bar control +RAYGUIAPI int GuiStatusBar(Rectangle bounds, const char *text); // Status Bar control, shows info text +RAYGUIAPI int GuiDummyRec(Rectangle bounds, const char *text); // Dummy control for placeholders +RAYGUIAPI int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell); // Grid control + +// Advance controls set +RAYGUIAPI int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active); // List View control +RAYGUIAPI int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus); // List View with extended parameters +RAYGUIAPI int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons); // Message Box control, displays a message +RAYGUIAPI int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, bool *secretViewActive); // Text Input Box control, ask for text, supports secret +RAYGUIAPI int GuiColorPicker(Rectangle bounds, const char *text, Color *color); // Color Picker control (multiple color controls) +RAYGUIAPI int GuiColorPanel(Rectangle bounds, const char *text, Color *color); // Color Panel control +RAYGUIAPI int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha); // Color Bar Alpha control +RAYGUIAPI int GuiColorBarHue(Rectangle bounds, const char *text, float *value); // Color Bar Hue control +RAYGUIAPI int GuiColorPickerHSV(Rectangle bounds, const char *text, Vector3 *colorHsv); // Color Picker control that avoids conversion to RGB on each call (multiple color controls) +RAYGUIAPI int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv); // Color Panel control that updates Hue-Saturation-Value color value, used by GuiColorPickerHSV() +//---------------------------------------------------------------------------------------------------------- + +#if !defined(RAYGUI_NO_ICONS) + +#if !defined(RAYGUI_CUSTOM_ICONS) +//---------------------------------------------------------------------------------- +// Icons enumeration +//---------------------------------------------------------------------------------- +typedef enum { + ICON_NONE = 0, + ICON_FOLDER_FILE_OPEN = 1, + ICON_FILE_SAVE_CLASSIC = 2, + ICON_FOLDER_OPEN = 3, + ICON_FOLDER_SAVE = 4, + ICON_FILE_OPEN = 5, + ICON_FILE_SAVE = 6, + ICON_FILE_EXPORT = 7, + ICON_FILE_ADD = 8, + ICON_FILE_DELETE = 9, + ICON_FILETYPE_TEXT = 10, + ICON_FILETYPE_AUDIO = 11, + ICON_FILETYPE_IMAGE = 12, + ICON_FILETYPE_PLAY = 13, + ICON_FILETYPE_VIDEO = 14, + ICON_FILETYPE_INFO = 15, + ICON_FILE_COPY = 16, + ICON_FILE_CUT = 17, + ICON_FILE_PASTE = 18, + ICON_CURSOR_HAND = 19, + ICON_CURSOR_POINTER = 20, + ICON_CURSOR_CLASSIC = 21, + ICON_PENCIL = 22, + ICON_PENCIL_BIG = 23, + ICON_BRUSH_CLASSIC = 24, + ICON_BRUSH_PAINTER = 25, + ICON_WATER_DROP = 26, + ICON_COLOR_PICKER = 27, + ICON_RUBBER = 28, + ICON_COLOR_BUCKET = 29, + ICON_TEXT_T = 30, + ICON_TEXT_A = 31, + ICON_SCALE = 32, + ICON_RESIZE = 33, + ICON_FILTER_POINT = 34, + ICON_FILTER_BILINEAR = 35, + ICON_CROP = 36, + ICON_CROP_ALPHA = 37, + ICON_SQUARE_TOGGLE = 38, + ICON_SYMMETRY = 39, + ICON_SYMMETRY_HORIZONTAL = 40, + ICON_SYMMETRY_VERTICAL = 41, + ICON_LENS = 42, + ICON_LENS_BIG = 43, + ICON_EYE_ON = 44, + ICON_EYE_OFF = 45, + ICON_FILTER_TOP = 46, + ICON_FILTER = 47, + ICON_TARGET_POINT = 48, + ICON_TARGET_SMALL = 49, + ICON_TARGET_BIG = 50, + ICON_TARGET_MOVE = 51, + ICON_CURSOR_MOVE = 52, + ICON_CURSOR_SCALE = 53, + ICON_CURSOR_SCALE_RIGHT = 54, + ICON_CURSOR_SCALE_LEFT = 55, + ICON_UNDO = 56, + ICON_REDO = 57, + ICON_REREDO = 58, + ICON_MUTATE = 59, + ICON_ROTATE = 60, + ICON_REPEAT = 61, + ICON_SHUFFLE = 62, + ICON_EMPTYBOX = 63, + ICON_TARGET = 64, + ICON_TARGET_SMALL_FILL = 65, + ICON_TARGET_BIG_FILL = 66, + ICON_TARGET_MOVE_FILL = 67, + ICON_CURSOR_MOVE_FILL = 68, + ICON_CURSOR_SCALE_FILL = 69, + ICON_CURSOR_SCALE_RIGHT_FILL = 70, + ICON_CURSOR_SCALE_LEFT_FILL = 71, + ICON_UNDO_FILL = 72, + ICON_REDO_FILL = 73, + ICON_REREDO_FILL = 74, + ICON_MUTATE_FILL = 75, + ICON_ROTATE_FILL = 76, + ICON_REPEAT_FILL = 77, + ICON_SHUFFLE_FILL = 78, + ICON_EMPTYBOX_SMALL = 79, + ICON_BOX = 80, + ICON_BOX_TOP = 81, + ICON_BOX_TOP_RIGHT = 82, + ICON_BOX_RIGHT = 83, + ICON_BOX_BOTTOM_RIGHT = 84, + ICON_BOX_BOTTOM = 85, + ICON_BOX_BOTTOM_LEFT = 86, + ICON_BOX_LEFT = 87, + ICON_BOX_TOP_LEFT = 88, + ICON_BOX_CENTER = 89, + ICON_BOX_CIRCLE_MASK = 90, + ICON_POT = 91, + ICON_ALPHA_MULTIPLY = 92, + ICON_ALPHA_CLEAR = 93, + ICON_DITHERING = 94, + ICON_MIPMAPS = 95, + ICON_BOX_GRID = 96, + ICON_GRID = 97, + ICON_BOX_CORNERS_SMALL = 98, + ICON_BOX_CORNERS_BIG = 99, + ICON_FOUR_BOXES = 100, + ICON_GRID_FILL = 101, + ICON_BOX_MULTISIZE = 102, + ICON_ZOOM_SMALL = 103, + ICON_ZOOM_MEDIUM = 104, + ICON_ZOOM_BIG = 105, + ICON_ZOOM_ALL = 106, + ICON_ZOOM_CENTER = 107, + ICON_BOX_DOTS_SMALL = 108, + ICON_BOX_DOTS_BIG = 109, + ICON_BOX_CONCENTRIC = 110, + ICON_BOX_GRID_BIG = 111, + ICON_OK_TICK = 112, + ICON_CROSS = 113, + ICON_ARROW_LEFT = 114, + ICON_ARROW_RIGHT = 115, + ICON_ARROW_DOWN = 116, + ICON_ARROW_UP = 117, + ICON_ARROW_LEFT_FILL = 118, + ICON_ARROW_RIGHT_FILL = 119, + ICON_ARROW_DOWN_FILL = 120, + ICON_ARROW_UP_FILL = 121, + ICON_AUDIO = 122, + ICON_FX = 123, + ICON_WAVE = 124, + ICON_WAVE_SINUS = 125, + ICON_WAVE_SQUARE = 126, + ICON_WAVE_TRIANGULAR = 127, + ICON_CROSS_SMALL = 128, + ICON_PLAYER_PREVIOUS = 129, + ICON_PLAYER_PLAY_BACK = 130, + ICON_PLAYER_PLAY = 131, + ICON_PLAYER_PAUSE = 132, + ICON_PLAYER_STOP = 133, + ICON_PLAYER_NEXT = 134, + ICON_PLAYER_RECORD = 135, + ICON_MAGNET = 136, + ICON_LOCK_CLOSE = 137, + ICON_LOCK_OPEN = 138, + ICON_CLOCK = 139, + ICON_TOOLS = 140, + ICON_GEAR = 141, + ICON_GEAR_BIG = 142, + ICON_BIN = 143, + ICON_HAND_POINTER = 144, + ICON_LASER = 145, + ICON_COIN = 146, + ICON_EXPLOSION = 147, + ICON_1UP = 148, + ICON_PLAYER = 149, + ICON_PLAYER_JUMP = 150, + ICON_KEY = 151, + ICON_DEMON = 152, + ICON_TEXT_POPUP = 153, + ICON_GEAR_EX = 154, + ICON_CRACK = 155, + ICON_CRACK_POINTS = 156, + ICON_STAR = 157, + ICON_DOOR = 158, + ICON_EXIT = 159, + ICON_MODE_2D = 160, + ICON_MODE_3D = 161, + ICON_CUBE = 162, + ICON_CUBE_FACE_TOP = 163, + ICON_CUBE_FACE_LEFT = 164, + ICON_CUBE_FACE_FRONT = 165, + ICON_CUBE_FACE_BOTTOM = 166, + ICON_CUBE_FACE_RIGHT = 167, + ICON_CUBE_FACE_BACK = 168, + ICON_CAMERA = 169, + ICON_SPECIAL = 170, + ICON_LINK_NET = 171, + ICON_LINK_BOXES = 172, + ICON_LINK_MULTI = 173, + ICON_LINK = 174, + ICON_LINK_BROKE = 175, + ICON_TEXT_NOTES = 176, + ICON_NOTEBOOK = 177, + ICON_SUITCASE = 178, + ICON_SUITCASE_ZIP = 179, + ICON_MAILBOX = 180, + ICON_MONITOR = 181, + ICON_PRINTER = 182, + ICON_PHOTO_CAMERA = 183, + ICON_PHOTO_CAMERA_FLASH = 184, + ICON_HOUSE = 185, + ICON_HEART = 186, + ICON_CORNER = 187, + ICON_VERTICAL_BARS = 188, + ICON_VERTICAL_BARS_FILL = 189, + ICON_LIFE_BARS = 190, + ICON_INFO = 191, + ICON_CROSSLINE = 192, + ICON_HELP = 193, + ICON_FILETYPE_ALPHA = 194, + ICON_FILETYPE_HOME = 195, + ICON_LAYERS_VISIBLE = 196, + ICON_LAYERS = 197, + ICON_WINDOW = 198, + ICON_HIDPI = 199, + ICON_FILETYPE_BINARY = 200, + ICON_HEX = 201, + ICON_SHIELD = 202, + ICON_FILE_NEW = 203, + ICON_FOLDER_ADD = 204, + ICON_ALARM = 205, + ICON_CPU = 206, + ICON_ROM = 207, + ICON_STEP_OVER = 208, + ICON_STEP_INTO = 209, + ICON_STEP_OUT = 210, + ICON_RESTART = 211, + ICON_BREAKPOINT_ON = 212, + ICON_BREAKPOINT_OFF = 213, + ICON_BURGER_MENU = 214, + ICON_CASE_SENSITIVE = 215, + ICON_REG_EXP = 216, + ICON_FOLDER = 217, + ICON_FILE = 218, + ICON_SAND_TIMER = 219, + ICON_WARNING = 220, + ICON_HELP_BOX = 221, + ICON_INFO_BOX = 222, + ICON_PRIORITY = 223, + ICON_LAYERS_ISO = 224, + ICON_LAYERS2 = 225, + ICON_MLAYERS = 226, + ICON_MAPS = 227, + ICON_HOT = 228, + ICON_LABEL = 229, + ICON_NAME_ID = 230, + ICON_SLICING = 231, + ICON_MANUAL_CONTROL = 232, + ICON_COLLISION = 233, + ICON_CIRCLE_ADD = 234, + ICON_CIRCLE_ADD_FILL = 235, + ICON_CIRCLE_WARNING = 236, + ICON_CIRCLE_WARNING_FILL = 237, + ICON_BOX_MORE = 238, + ICON_BOX_MORE_FILL = 239, + ICON_BOX_MINUS = 240, + ICON_BOX_MINUS_FILL = 241, + ICON_UNION = 242, + ICON_INTERSECTION = 243, + ICON_DIFFERENCE = 244, + ICON_SPHERE = 245, + ICON_CYLINDER = 246, + ICON_CONE = 247, + ICON_ELLIPSOID = 248, + ICON_CAPSULE = 249, + ICON_250 = 250, + ICON_251 = 251, + ICON_252 = 252, + ICON_253 = 253, + ICON_254 = 254, + ICON_255 = 255 +} GuiIconName; +#endif + +#endif + +#if defined(__cplusplus) +} // Prevents name mangling of functions +#endif + +#endif // RAYGUI_H + +/*********************************************************************************** +* +* RAYGUI IMPLEMENTATION +* +************************************************************************************/ + +#if defined(RAYGUI_IMPLEMENTATION) + +#include // required for: isspace() [GuiTextBox()] +#include // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), snprintf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()] +#include // Required for: strlen() [GuiTextBox(), GuiValueBox()], memset(), memcpy() +#include // Required for: va_list, va_start(), vfprintf(), va_end() [TextFormat()] +#include // Required for: roundf() [GuiColorPicker()] + +// Allow custom memory allocators +#if defined(RAYGUI_MALLOC) || defined(RAYGUI_CALLOC) || defined(RAYGUI_FREE) + #if !defined(RAYGUI_MALLOC) || !defined(RAYGUI_CALLOC) || !defined(RAYGUI_FREE) + #error "RAYGUI: if RAYGUI_MALLOC, RAYGUI_CALLOC, or RAYGUI_FREE is customized, all three must be customized" + #endif +#else + #include // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()] + + #define RAYGUI_MALLOC(sz) malloc(sz) + #define RAYGUI_CALLOC(n,sz) calloc(n,sz) + #define RAYGUI_FREE(p) free(p) +#endif + +#ifdef __cplusplus + #define RAYGUI_CLITERAL(name) name +#else + #define RAYGUI_CLITERAL(name) (name) +#endif + +// Check if two rectangles are equal, used to validate a slider bounds as an id +#ifndef CHECK_BOUNDS_ID + #define CHECK_BOUNDS_ID(src, dst) (((int)src.x == (int)dst.x) && ((int)src.y == (int)dst.y) && ((int)src.width == (int)dst.width) && ((int)src.height == (int)dst.height)) +#endif + +#if !defined(RAYGUI_NO_ICONS) && !defined(RAYGUI_CUSTOM_ICONS) + +// Embedded icons, no external file provided +#define RAYGUI_ICON_SIZE 16 // Size of icons in pixels (squared) +#define RAYGUI_ICON_MAX_ICONS 256 // Maximum number of icons +#define RAYGUI_ICON_MAX_NAME_LENGTH 32 // Maximum length of icon name id + +// Icons data is defined by bit array (every bit represents one pixel) +// Those arrays are stored as unsigned int data arrays, so, +// every array element defines 32 pixels (bits) of information +// One icon is defined by 8 int, (8 int*32 bit = 256 bit = 16*16 pixels) +// NOTE: Number of elemens depend on RAYGUI_ICON_SIZE (by default 16x16 pixels) +#define RAYGUI_ICON_DATA_ELEMENTS (RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32) + +//---------------------------------------------------------------------------------- +// Icons data for all gui possible icons (allocated on data segment by default) +// +// NOTE 1: Every icon is codified in binary form, using 1 bit per pixel, so, +// every 16x16 icon requires 8 integers (16*16/32) to be stored +// +// NOTE 2: A different icon set could be loaded over this array using GuiLoadIcons(), +// but loaded icons set must be same RAYGUI_ICON_SIZE and no more than RAYGUI_ICON_MAX_ICONS +// +// guiIcons size is by default: 256*(16*16/32) = 2048*4 = 8192 bytes = 8 KB +//---------------------------------------------------------------------------------- +static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_NONE + 0x3ff80000, 0x2f082008, 0x2042207e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x00007ffe, // ICON_FOLDER_FILE_OPEN + 0x3ffe0000, 0x44226422, 0x400247e2, 0x5ffa4002, 0x57ea500a, 0x500a500a, 0x40025ffa, 0x00007ffe, // ICON_FILE_SAVE_CLASSIC + 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x41024002, 0x44424282, 0x793e4102, 0x00000100, // ICON_FOLDER_OPEN + 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x41024102, 0x44424102, 0x793e4282, 0x00000000, // ICON_FOLDER_SAVE + 0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, 0x24442284, 0x21042104, 0x20042104, 0x00003ffc, // ICON_FILE_OPEN + 0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, 0x21042104, 0x22842444, 0x20042104, 0x00003ffc, // ICON_FILE_SAVE + 0x3ff00000, 0x201c2010, 0x00042004, 0x20041004, 0x20844784, 0x00841384, 0x20042784, 0x00003ffc, // ICON_FILE_EXPORT + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x22042204, 0x22042f84, 0x20042204, 0x00003ffc, // ICON_FILE_ADD + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x25042884, 0x25042204, 0x20042884, 0x00003ffc, // ICON_FILE_DELETE + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042ff4, 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc, // ICON_FILETYPE_TEXT + 0x3ff00000, 0x201c2010, 0x27042004, 0x244424c4, 0x26442444, 0x20642664, 0x20042004, 0x00003ffc, // ICON_FILETYPE_AUDIO + 0x3ff00000, 0x201c2010, 0x26042604, 0x20042004, 0x35442884, 0x2414222c, 0x20042004, 0x00003ffc, // ICON_FILETYPE_IMAGE + 0x3ff00000, 0x201c2010, 0x20c42004, 0x22442144, 0x22442444, 0x20c42144, 0x20042004, 0x00003ffc, // ICON_FILETYPE_PLAY + 0x3ff00000, 0x3ffc2ff0, 0x3f3c2ff4, 0x3dbc2eb4, 0x3dbc2bb4, 0x3f3c2eb4, 0x3ffc2ff4, 0x00002ff4, // ICON_FILETYPE_VIDEO + 0x3ff00000, 0x201c2010, 0x21842184, 0x21842004, 0x21842184, 0x21842184, 0x20042184, 0x00003ffc, // ICON_FILETYPE_INFO + 0x0ff00000, 0x381c0810, 0x28042804, 0x28042804, 0x28042804, 0x28042804, 0x20102ffc, 0x00003ff0, // ICON_FILE_COPY + 0x00000000, 0x701c0000, 0x079c1e14, 0x55a000f0, 0x079c00f0, 0x701c1e14, 0x00000000, 0x00000000, // ICON_FILE_CUT + 0x01c00000, 0x13e41bec, 0x3f841004, 0x204420c4, 0x20442044, 0x20442044, 0x207c2044, 0x00003fc0, // ICON_FILE_PASTE + 0x00000000, 0x3aa00fe0, 0x2abc2aa0, 0x2aa42aa4, 0x20042aa4, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_CURSOR_HAND + 0x00000000, 0x003c000c, 0x030800c8, 0x30100c10, 0x10202020, 0x04400840, 0x01800280, 0x00000000, // ICON_CURSOR_POINTER + 0x00000000, 0x00180000, 0x01f00078, 0x03e007f0, 0x07c003e0, 0x04000e40, 0x00000000, 0x00000000, // ICON_CURSOR_CLASSIC + 0x00000000, 0x04000000, 0x11000a00, 0x04400a80, 0x01100220, 0x00580088, 0x00000038, 0x00000000, // ICON_PENCIL + 0x04000000, 0x15000a00, 0x50402880, 0x14102820, 0x05040a08, 0x015c028c, 0x007c00bc, 0x00000000, // ICON_PENCIL_BIG + 0x01c00000, 0x01400140, 0x01400140, 0x0ff80140, 0x0ff80808, 0x0aa80808, 0x0aa80aa8, 0x00000ff8, // ICON_BRUSH_CLASSIC + 0x1ffc0000, 0x5ffc7ffe, 0x40004000, 0x00807f80, 0x01c001c0, 0x01c001c0, 0x01c001c0, 0x00000080, // ICON_BRUSH_PAINTER + 0x00000000, 0x00800000, 0x01c00080, 0x03e001c0, 0x07f003e0, 0x036006f0, 0x000001c0, 0x00000000, // ICON_WATER_DROP + 0x00000000, 0x3e003800, 0x1f803f80, 0x0c201e40, 0x02080c10, 0x00840104, 0x00380044, 0x00000000, // ICON_COLOR_PICKER + 0x00000000, 0x07800300, 0x1fe00fc0, 0x3f883fd0, 0x0e021f04, 0x02040402, 0x00f00108, 0x00000000, // ICON_RUBBER + 0x00c00000, 0x02800140, 0x08200440, 0x20081010, 0x2ffe3004, 0x03f807fc, 0x00e001f0, 0x00000040, // ICON_COLOR_BUCKET + 0x00000000, 0x21843ffc, 0x01800180, 0x01800180, 0x01800180, 0x01800180, 0x03c00180, 0x00000000, // ICON_TEXT_T + 0x00800000, 0x01400180, 0x06200340, 0x0c100620, 0x1ff80c10, 0x380c1808, 0x70067004, 0x0000f80f, // ICON_TEXT_A + 0x78000000, 0x50004000, 0x00004800, 0x03c003c0, 0x03c003c0, 0x00100000, 0x0002000a, 0x0000000e, // ICON_SCALE + 0x75560000, 0x5e004002, 0x54001002, 0x41001202, 0x408200fe, 0x40820082, 0x40820082, 0x00006afe, // ICON_RESIZE + 0x00000000, 0x3f003f00, 0x3f003f00, 0x3f003f00, 0x00400080, 0x001c0020, 0x001c001c, 0x00000000, // ICON_FILTER_POINT + 0x6d800000, 0x00004080, 0x40804080, 0x40800000, 0x00406d80, 0x001c0020, 0x001c001c, 0x00000000, // ICON_FILTER_BILINEAR + 0x40080000, 0x1ffe2008, 0x14081008, 0x11081208, 0x10481088, 0x10081028, 0x10047ff8, 0x00001002, // ICON_CROP + 0x00100000, 0x3ffc0010, 0x2ab03550, 0x22b02550, 0x20b02150, 0x20302050, 0x2000fff0, 0x00002000, // ICON_CROP_ALPHA + 0x40000000, 0x1ff82000, 0x04082808, 0x01082208, 0x00482088, 0x00182028, 0x35542008, 0x00000002, // ICON_SQUARE_TOGGLE + 0x00000000, 0x02800280, 0x06c006c0, 0x0ea00ee0, 0x1e901eb0, 0x3e883e98, 0x7efc7e8c, 0x00000000, // ICON_SYMMETRY + 0x01000000, 0x05600100, 0x1d480d50, 0x7d423d44, 0x3d447d42, 0x0d501d48, 0x01000560, 0x00000100, // ICON_SYMMETRY_HORIZONTAL + 0x01800000, 0x04200240, 0x10080810, 0x00001ff8, 0x00007ffe, 0x0ff01ff8, 0x03c007e0, 0x00000180, // ICON_SYMMETRY_VERTICAL + 0x00000000, 0x010800f0, 0x02040204, 0x02040204, 0x07f00308, 0x1c000e00, 0x30003800, 0x00000000, // ICON_LENS + 0x00000000, 0x061803f0, 0x08240c0c, 0x08040814, 0x0c0c0804, 0x23f01618, 0x18002400, 0x00000000, // ICON_LENS_BIG + 0x00000000, 0x00000000, 0x1c7007c0, 0x638e3398, 0x1c703398, 0x000007c0, 0x00000000, 0x00000000, // ICON_EYE_ON + 0x00000000, 0x10002000, 0x04700fc0, 0x610e3218, 0x1c703098, 0x001007a0, 0x00000008, 0x00000000, // ICON_EYE_OFF + 0x00000000, 0x00007ffc, 0x40047ffc, 0x10102008, 0x04400820, 0x02800280, 0x02800280, 0x00000100, // ICON_FILTER_TOP + 0x00000000, 0x40027ffe, 0x10082004, 0x04200810, 0x02400240, 0x02400240, 0x01400240, 0x000000c0, // ICON_FILTER + 0x00800000, 0x00800080, 0x00000080, 0x3c9e0000, 0x00000000, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_POINT + 0x00800000, 0x00800080, 0x00800080, 0x3f7e01c0, 0x008001c0, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_SMALL + 0x00800000, 0x00800080, 0x03e00080, 0x3e3e0220, 0x03e00220, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_BIG + 0x01000000, 0x04400280, 0x01000100, 0x43842008, 0x43849ab2, 0x01002008, 0x04400100, 0x01000280, // ICON_TARGET_MOVE + 0x01000000, 0x04400280, 0x01000100, 0x41042108, 0x41049ff2, 0x01002108, 0x04400100, 0x01000280, // ICON_CURSOR_MOVE + 0x781e0000, 0x500a4002, 0x04204812, 0x00000240, 0x02400000, 0x48120420, 0x4002500a, 0x0000781e, // ICON_CURSOR_SCALE + 0x00000000, 0x20003c00, 0x24002800, 0x01000200, 0x00400080, 0x00140024, 0x003c0004, 0x00000000, // ICON_CURSOR_SCALE_RIGHT + 0x00000000, 0x0004003c, 0x00240014, 0x00800040, 0x02000100, 0x28002400, 0x3c002000, 0x00000000, // ICON_CURSOR_SCALE_LEFT + 0x00000000, 0x00100020, 0x10101fc8, 0x10001020, 0x10001000, 0x10001000, 0x00001fc0, 0x00000000, // ICON_UNDO + 0x00000000, 0x08000400, 0x080813f8, 0x00080408, 0x00080008, 0x00080008, 0x000003f8, 0x00000000, // ICON_REDO + 0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, 0x20402000, 0x3f902020, 0x00400020, 0x00000000, // ICON_REREDO + 0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, 0x20202000, 0x3fc82010, 0x00200010, 0x00000000, // ICON_MUTATE + 0x00000000, 0x0ff00000, 0x10081818, 0x11801008, 0x10001180, 0x18101020, 0x00100fc8, 0x00000020, // ICON_ROTATE + 0x00000000, 0x04000200, 0x240429fc, 0x20042204, 0x20442004, 0x3f942024, 0x00400020, 0x00000000, // ICON_REPEAT + 0x00000000, 0x20001000, 0x22104c0e, 0x00801120, 0x11200040, 0x4c0e2210, 0x10002000, 0x00000000, // ICON_SHUFFLE + 0x7ffe0000, 0x50024002, 0x44024802, 0x41024202, 0x40424082, 0x40124022, 0x4002400a, 0x00007ffe, // ICON_EMPTYBOX + 0x00800000, 0x03e00080, 0x08080490, 0x3c9e0808, 0x08080808, 0x03e00490, 0x00800080, 0x00000000, // ICON_TARGET + 0x00800000, 0x00800080, 0x00800080, 0x3ffe01c0, 0x008001c0, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_SMALL_FILL + 0x00800000, 0x00800080, 0x03e00080, 0x3ffe03e0, 0x03e003e0, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_BIG_FILL + 0x01000000, 0x07c00380, 0x01000100, 0x638c2008, 0x638cfbbe, 0x01002008, 0x07c00100, 0x01000380, // ICON_TARGET_MOVE_FILL + 0x01000000, 0x07c00380, 0x01000100, 0x610c2108, 0x610cfffe, 0x01002108, 0x07c00100, 0x01000380, // ICON_CURSOR_MOVE_FILL + 0x781e0000, 0x6006700e, 0x04204812, 0x00000240, 0x02400000, 0x48120420, 0x700e6006, 0x0000781e, // ICON_CURSOR_SCALE_FILL + 0x00000000, 0x38003c00, 0x24003000, 0x01000200, 0x00400080, 0x000c0024, 0x003c001c, 0x00000000, // ICON_CURSOR_SCALE_RIGHT_FILL + 0x00000000, 0x001c003c, 0x0024000c, 0x00800040, 0x02000100, 0x30002400, 0x3c003800, 0x00000000, // ICON_CURSOR_SCALE_LEFT_FILL + 0x00000000, 0x00300020, 0x10301ff8, 0x10001020, 0x10001000, 0x10001000, 0x00001fc0, 0x00000000, // ICON_UNDO_FILL + 0x00000000, 0x0c000400, 0x0c081ff8, 0x00080408, 0x00080008, 0x00080008, 0x000003f8, 0x00000000, // ICON_REDO_FILL + 0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, 0x20402000, 0x3ff02060, 0x00400060, 0x00000000, // ICON_REREDO_FILL + 0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, 0x20202000, 0x3ff82030, 0x00200030, 0x00000000, // ICON_MUTATE_FILL + 0x00000000, 0x0ff00000, 0x10081818, 0x11801008, 0x10001180, 0x18301020, 0x00300ff8, 0x00000020, // ICON_ROTATE_FILL + 0x00000000, 0x06000200, 0x26042ffc, 0x20042204, 0x20442004, 0x3ff42064, 0x00400060, 0x00000000, // ICON_REPEAT_FILL + 0x00000000, 0x30001000, 0x32107c0e, 0x00801120, 0x11200040, 0x7c0e3210, 0x10003000, 0x00000000, // ICON_SHUFFLE_FILL + 0x00000000, 0x30043ffc, 0x24042804, 0x21042204, 0x20442084, 0x20142024, 0x3ffc200c, 0x00000000, // ICON_EMPTYBOX_SMALL + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX + 0x00000000, 0x23c43ffc, 0x23c423c4, 0x200423c4, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_TOP + 0x00000000, 0x3e043ffc, 0x3e043e04, 0x20043e04, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_TOP_RIGHT + 0x00000000, 0x20043ffc, 0x20042004, 0x3e043e04, 0x3e043e04, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_RIGHT + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x3e042004, 0x3e043e04, 0x3ffc3e04, 0x00000000, // ICON_BOX_BOTTOM_RIGHT + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x23c42004, 0x23c423c4, 0x3ffc23c4, 0x00000000, // ICON_BOX_BOTTOM + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x207c2004, 0x207c207c, 0x3ffc207c, 0x00000000, // ICON_BOX_BOTTOM_LEFT + 0x00000000, 0x20043ffc, 0x20042004, 0x207c207c, 0x207c207c, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_LEFT + 0x00000000, 0x207c3ffc, 0x207c207c, 0x2004207c, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_TOP_LEFT + 0x00000000, 0x20043ffc, 0x20042004, 0x23c423c4, 0x23c423c4, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_CENTER + 0x7ffe0000, 0x40024002, 0x47e24182, 0x4ff247e2, 0x47e24ff2, 0x418247e2, 0x40024002, 0x00007ffe, // ICON_BOX_CIRCLE_MASK + 0x7fff0000, 0x40014001, 0x40014001, 0x49555ddd, 0x4945495d, 0x400149c5, 0x40014001, 0x00007fff, // ICON_POT + 0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, 0x404e40ce, 0x48125432, 0x4006540e, 0x00007ffe, // ICON_ALPHA_MULTIPLY + 0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, 0x5c4e40ce, 0x44124432, 0x40065c0e, 0x00007ffe, // ICON_ALPHA_CLEAR + 0x7ffe0000, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x00007ffe, // ICON_DITHERING + 0x07fe0000, 0x1ffa0002, 0x7fea000a, 0x402a402a, 0x5b2a512a, 0x5128552a, 0x40205128, 0x00007fe0, // ICON_MIPMAPS + 0x00000000, 0x1ff80000, 0x12481248, 0x12481ff8, 0x1ff81248, 0x12481248, 0x00001ff8, 0x00000000, // ICON_BOX_GRID + 0x12480000, 0x7ffe1248, 0x12481248, 0x12487ffe, 0x7ffe1248, 0x12481248, 0x12487ffe, 0x00001248, // ICON_GRID + 0x00000000, 0x1c380000, 0x1c3817e8, 0x08100810, 0x08100810, 0x17e81c38, 0x00001c38, 0x00000000, // ICON_BOX_CORNERS_SMALL + 0x700e0000, 0x700e5ffa, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x5ffa700e, 0x0000700e, // ICON_BOX_CORNERS_BIG + 0x3f7e0000, 0x21422142, 0x21422142, 0x00003f7e, 0x21423f7e, 0x21422142, 0x3f7e2142, 0x00000000, // ICON_FOUR_BOXES + 0x00000000, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x00000000, // ICON_GRID_FILL + 0x7ffe0000, 0x7ffe7ffe, 0x77fe7000, 0x77fe77fe, 0x777e7700, 0x777e777e, 0x777e777e, 0x0000777e, // ICON_BOX_MULTISIZE + 0x781e0000, 0x40024002, 0x00004002, 0x01800000, 0x00000180, 0x40020000, 0x40024002, 0x0000781e, // ICON_ZOOM_SMALL + 0x781e0000, 0x40024002, 0x00004002, 0x03c003c0, 0x03c003c0, 0x40020000, 0x40024002, 0x0000781e, // ICON_ZOOM_MEDIUM + 0x781e0000, 0x40024002, 0x07e04002, 0x07e007e0, 0x07e007e0, 0x400207e0, 0x40024002, 0x0000781e, // ICON_ZOOM_BIG + 0x781e0000, 0x5ffa4002, 0x1ff85ffa, 0x1ff81ff8, 0x1ff81ff8, 0x5ffa1ff8, 0x40025ffa, 0x0000781e, // ICON_ZOOM_ALL + 0x00000000, 0x2004381c, 0x00002004, 0x00000000, 0x00000000, 0x20040000, 0x381c2004, 0x00000000, // ICON_ZOOM_CENTER + 0x00000000, 0x1db80000, 0x10081008, 0x10080000, 0x00001008, 0x10081008, 0x00001db8, 0x00000000, // ICON_BOX_DOTS_SMALL + 0x35560000, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x35562002, 0x00000000, // ICON_BOX_DOTS_BIG + 0x7ffe0000, 0x40024002, 0x48124ff2, 0x49924812, 0x48124992, 0x4ff24812, 0x40024002, 0x00007ffe, // ICON_BOX_CONCENTRIC + 0x00000000, 0x10841ffc, 0x10841084, 0x1ffc1084, 0x10841084, 0x10841084, 0x00001ffc, 0x00000000, // ICON_BOX_GRID_BIG + 0x00000000, 0x00000000, 0x10000000, 0x04000800, 0x01040200, 0x00500088, 0x00000020, 0x00000000, // ICON_OK_TICK + 0x00000000, 0x10080000, 0x04200810, 0x01800240, 0x02400180, 0x08100420, 0x00001008, 0x00000000, // ICON_CROSS + 0x00000000, 0x02000000, 0x00800100, 0x00200040, 0x00200010, 0x00800040, 0x02000100, 0x00000000, // ICON_ARROW_LEFT + 0x00000000, 0x00400000, 0x01000080, 0x04000200, 0x04000800, 0x01000200, 0x00400080, 0x00000000, // ICON_ARROW_RIGHT + 0x00000000, 0x00000000, 0x00000000, 0x08081004, 0x02200410, 0x00800140, 0x00000000, 0x00000000, // ICON_ARROW_DOWN + 0x00000000, 0x00000000, 0x01400080, 0x04100220, 0x10040808, 0x00000000, 0x00000000, 0x00000000, // ICON_ARROW_UP + 0x00000000, 0x02000000, 0x03800300, 0x03e003c0, 0x03e003f0, 0x038003c0, 0x02000300, 0x00000000, // ICON_ARROW_LEFT_FILL + 0x00000000, 0x00400000, 0x01c000c0, 0x07c003c0, 0x07c00fc0, 0x01c003c0, 0x004000c0, 0x00000000, // ICON_ARROW_RIGHT_FILL + 0x00000000, 0x00000000, 0x00000000, 0x0ff81ffc, 0x03e007f0, 0x008001c0, 0x00000000, 0x00000000, // ICON_ARROW_DOWN_FILL + 0x00000000, 0x00000000, 0x01c00080, 0x07f003e0, 0x1ffc0ff8, 0x00000000, 0x00000000, 0x00000000, // ICON_ARROW_UP_FILL + 0x00000000, 0x18a008c0, 0x32881290, 0x24822686, 0x26862482, 0x12903288, 0x08c018a0, 0x00000000, // ICON_AUDIO + 0x00000000, 0x04800780, 0x004000c0, 0x662000f0, 0x08103c30, 0x130a0e18, 0x0000318e, 0x00000000, // ICON_FX + 0x00000000, 0x00800000, 0x08880888, 0x2aaa0a8a, 0x0a8a2aaa, 0x08880888, 0x00000080, 0x00000000, // ICON_WAVE + 0x00000000, 0x00600000, 0x01080090, 0x02040108, 0x42044204, 0x24022402, 0x00001800, 0x00000000, // ICON_WAVE_SINUS + 0x00000000, 0x07f80000, 0x04080408, 0x04080408, 0x04080408, 0x7c0e0408, 0x00000000, 0x00000000, // ICON_WAVE_SQUARE + 0x00000000, 0x00000000, 0x00a00040, 0x22084110, 0x08021404, 0x00000000, 0x00000000, 0x00000000, // ICON_WAVE_TRIANGULAR + 0x00000000, 0x00000000, 0x04200000, 0x01800240, 0x02400180, 0x00000420, 0x00000000, 0x00000000, // ICON_CROSS_SMALL + 0x00000000, 0x18380000, 0x12281428, 0x10a81128, 0x112810a8, 0x14281228, 0x00001838, 0x00000000, // ICON_PLAYER_PREVIOUS + 0x00000000, 0x18000000, 0x11801600, 0x10181060, 0x10601018, 0x16001180, 0x00001800, 0x00000000, // ICON_PLAYER_PLAY_BACK + 0x00000000, 0x00180000, 0x01880068, 0x18080608, 0x06081808, 0x00680188, 0x00000018, 0x00000000, // ICON_PLAYER_PLAY + 0x00000000, 0x1e780000, 0x12481248, 0x12481248, 0x12481248, 0x12481248, 0x00001e78, 0x00000000, // ICON_PLAYER_PAUSE + 0x00000000, 0x1ff80000, 0x10081008, 0x10081008, 0x10081008, 0x10081008, 0x00001ff8, 0x00000000, // ICON_PLAYER_STOP + 0x00000000, 0x1c180000, 0x14481428, 0x15081488, 0x14881508, 0x14281448, 0x00001c18, 0x00000000, // ICON_PLAYER_NEXT + 0x00000000, 0x03c00000, 0x08100420, 0x10081008, 0x10081008, 0x04200810, 0x000003c0, 0x00000000, // ICON_PLAYER_RECORD + 0x00000000, 0x0c3007e0, 0x13c81818, 0x14281668, 0x14281428, 0x1c381c38, 0x08102244, 0x00000000, // ICON_MAGNET + 0x07c00000, 0x08200820, 0x3ff80820, 0x23882008, 0x21082388, 0x20082108, 0x1ff02008, 0x00000000, // ICON_LOCK_CLOSE + 0x07c00000, 0x08000800, 0x3ff80800, 0x23882008, 0x21082388, 0x20082108, 0x1ff02008, 0x00000000, // ICON_LOCK_OPEN + 0x01c00000, 0x0c180770, 0x3086188c, 0x60832082, 0x60034781, 0x30062002, 0x0c18180c, 0x01c00770, // ICON_CLOCK + 0x0a200000, 0x1b201b20, 0x04200e20, 0x04200420, 0x04700420, 0x0e700e70, 0x0e700e70, 0x04200e70, // ICON_TOOLS + 0x01800000, 0x3bdc318c, 0x0ff01ff8, 0x7c3e1e78, 0x1e787c3e, 0x1ff80ff0, 0x318c3bdc, 0x00000180, // ICON_GEAR + 0x01800000, 0x3ffc318c, 0x1c381ff8, 0x781e1818, 0x1818781e, 0x1ff81c38, 0x318c3ffc, 0x00000180, // ICON_GEAR_BIG + 0x00000000, 0x08080ff8, 0x08081ffc, 0x0aa80aa8, 0x0aa80aa8, 0x0aa80aa8, 0x08080aa8, 0x00000ff8, // ICON_BIN + 0x00000000, 0x00000000, 0x20043ffc, 0x08043f84, 0x04040f84, 0x04040784, 0x000007fc, 0x00000000, // ICON_HAND_POINTER + 0x00000000, 0x24400400, 0x00001480, 0x6efe0e00, 0x00000e00, 0x24401480, 0x00000400, 0x00000000, // ICON_LASER + 0x00000000, 0x03c00000, 0x08300460, 0x11181118, 0x11181118, 0x04600830, 0x000003c0, 0x00000000, // ICON_COIN + 0x00000000, 0x10880080, 0x06c00810, 0x366c07e0, 0x07e00240, 0x00001768, 0x04200240, 0x00000000, // ICON_EXPLOSION + 0x00000000, 0x3d280000, 0x2528252c, 0x3d282528, 0x05280528, 0x05e80528, 0x00000000, 0x00000000, // ICON_1UP + 0x01800000, 0x03c003c0, 0x018003c0, 0x0ff007e0, 0x0bd00bd0, 0x0a500bd0, 0x02400240, 0x02400240, // ICON_PLAYER + 0x01800000, 0x03c003c0, 0x118013c0, 0x03c81ff8, 0x07c003c8, 0x04400440, 0x0c080478, 0x00000000, // ICON_PLAYER_JUMP + 0x3ff80000, 0x30183ff8, 0x30183018, 0x3ff83ff8, 0x03000300, 0x03c003c0, 0x03e00300, 0x000003e0, // ICON_KEY + 0x3ff80000, 0x3ff83ff8, 0x33983ff8, 0x3ff83398, 0x3ff83ff8, 0x00000540, 0x0fe00aa0, 0x00000fe0, // ICON_DEMON + 0x00000000, 0x0ff00000, 0x20041008, 0x25442004, 0x10082004, 0x06000bf0, 0x00000300, 0x00000000, // ICON_TEXT_POPUP + 0x00000000, 0x11440000, 0x07f00be8, 0x1c1c0e38, 0x1c1c0c18, 0x07f00e38, 0x11440be8, 0x00000000, // ICON_GEAR_EX + 0x00000000, 0x20080000, 0x0c601010, 0x07c00fe0, 0x07c007c0, 0x0c600fe0, 0x20081010, 0x00000000, // ICON_CRACK + 0x00000000, 0x20080000, 0x0c601010, 0x04400fe0, 0x04405554, 0x0c600fe0, 0x20081010, 0x00000000, // ICON_CRACK_POINTS + 0x00000000, 0x00800080, 0x01c001c0, 0x1ffc3ffe, 0x03e007f0, 0x07f003e0, 0x0c180770, 0x00000808, // ICON_STAR + 0x0ff00000, 0x08180810, 0x08100818, 0x0a100810, 0x08180810, 0x08100818, 0x08100810, 0x00001ff8, // ICON_DOOR + 0x0ff00000, 0x08100810, 0x08100810, 0x10100010, 0x4f902010, 0x10102010, 0x08100010, 0x00000ff0, // ICON_EXIT + 0x00040000, 0x001f000e, 0x0ef40004, 0x12f41284, 0x0ef41214, 0x10040004, 0x7ffc3004, 0x10003000, // ICON_MODE_2D + 0x78040000, 0x501f600e, 0x0ef44004, 0x12f41284, 0x0ef41284, 0x10140004, 0x7ffc300c, 0x10003000, // ICON_MODE_3D + 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe, // ICON_CUBE + 0x7fe00000, 0x5ff87ff0, 0x47fe4ffc, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe, // ICON_CUBE_FACE_TOP + 0x7fe00000, 0x50386030, 0x47c2483c, 0x443e443e, 0x443e443e, 0x241e75fe, 0x0c06140e, 0x000007fe, // ICON_CUBE_FACE_LEFT + 0x7fe00000, 0x50286030, 0x47fe4804, 0x47fe47fe, 0x47fe47fe, 0x27fe77fe, 0x0ffe17fe, 0x000007fe, // ICON_CUBE_FACE_FRONT + 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x3bf27be2, 0x0bfe1bfa, 0x000007fe, // ICON_CUBE_FACE_BOTTOM + 0x7fe00000, 0x70286030, 0x7ffe7804, 0x7c227c02, 0x7c227c22, 0x3c127de2, 0x0c061c0a, 0x000007fe, // ICON_CUBE_FACE_RIGHT + 0x7fe00000, 0x6fe85ff0, 0x781e77e4, 0x7be27be2, 0x7be27be2, 0x24127be2, 0x0c06140a, 0x000007fe, // ICON_CUBE_FACE_BACK + 0x00000000, 0x2a0233fe, 0x22022602, 0x22022202, 0x2a022602, 0x00a033fe, 0x02080110, 0x00000000, // ICON_CAMERA + 0x00000000, 0x200c3ffc, 0x000c000c, 0x3ffc000c, 0x30003000, 0x30003000, 0x3ffc3004, 0x00000000, // ICON_SPECIAL + 0x00000000, 0x0022003e, 0x012201e2, 0x0100013e, 0x01000100, 0x79000100, 0x4f004900, 0x00007800, // ICON_LINK_NET + 0x00000000, 0x44007c00, 0x45004600, 0x00627cbe, 0x00620022, 0x45007cbe, 0x44004600, 0x00007c00, // ICON_LINK_BOXES + 0x00000000, 0x0044007c, 0x0010007c, 0x3f100010, 0x3f1021f0, 0x3f100010, 0x3f0021f0, 0x00000000, // ICON_LINK_MULTI + 0x00000000, 0x0044007c, 0x00440044, 0x0010007c, 0x00100010, 0x44107c10, 0x440047f0, 0x00007c00, // ICON_LINK + 0x00000000, 0x0044007c, 0x00440044, 0x0000007c, 0x00000010, 0x44007c10, 0x44004550, 0x00007c00, // ICON_LINK_BROKE + 0x02a00000, 0x22a43ffc, 0x20042004, 0x20042ff4, 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc, // ICON_TEXT_NOTES + 0x3ffc0000, 0x20042004, 0x245e27c4, 0x27c42444, 0x2004201e, 0x201e2004, 0x20042004, 0x00003ffc, // ICON_NOTEBOOK + 0x00000000, 0x07e00000, 0x04200420, 0x24243ffc, 0x24242424, 0x24242424, 0x3ffc2424, 0x00000000, // ICON_SUITCASE + 0x00000000, 0x0fe00000, 0x08200820, 0x40047ffc, 0x7ffc5554, 0x40045554, 0x7ffc4004, 0x00000000, // ICON_SUITCASE_ZIP + 0x00000000, 0x20043ffc, 0x3ffc2004, 0x13c81008, 0x100813c8, 0x10081008, 0x1ff81008, 0x00000000, // ICON_MAILBOX + 0x00000000, 0x40027ffe, 0x5ffa5ffa, 0x5ffa5ffa, 0x40025ffa, 0x03c07ffe, 0x1ff81ff8, 0x00000000, // ICON_MONITOR + 0x0ff00000, 0x6bfe7ffe, 0x7ffe7ffe, 0x68167ffe, 0x08106816, 0x08100810, 0x0ff00810, 0x00000000, // ICON_PRINTER + 0x3ff80000, 0xfffe2008, 0x870a8002, 0x904a888a, 0x904a904a, 0x870a888a, 0xfffe8002, 0x00000000, // ICON_PHOTO_CAMERA + 0x0fc00000, 0xfcfe0cd8, 0x8002fffe, 0x84428382, 0x84428442, 0x80028382, 0xfffe8002, 0x00000000, // ICON_PHOTO_CAMERA_FLASH + 0x00000000, 0x02400180, 0x08100420, 0x20041008, 0x23c42004, 0x22442244, 0x3ffc2244, 0x00000000, // ICON_HOUSE + 0x00000000, 0x1c700000, 0x3ff83ef8, 0x3ff83ff8, 0x0fe01ff0, 0x038007c0, 0x00000100, 0x00000000, // ICON_HEART + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x80000000, 0xe000c000, // ICON_CORNER + 0x00000000, 0x14001c00, 0x15c01400, 0x15401540, 0x155c1540, 0x15541554, 0x1ddc1554, 0x00000000, // ICON_VERTICAL_BARS + 0x00000000, 0x03000300, 0x1b001b00, 0x1b601b60, 0x1b6c1b60, 0x1b6c1b6c, 0x1b6c1b6c, 0x00000000, // ICON_VERTICAL_BARS_FILL + 0x00000000, 0x00000000, 0x403e7ffe, 0x7ffe403e, 0x7ffe0000, 0x43fe43fe, 0x00007ffe, 0x00000000, // ICON_LIFE_BARS + 0x7ffc0000, 0x43844004, 0x43844284, 0x43844004, 0x42844284, 0x42844284, 0x40044384, 0x00007ffc, // ICON_INFO + 0x40008000, 0x10002000, 0x04000800, 0x01000200, 0x00400080, 0x00100020, 0x00040008, 0x00010002, // ICON_CROSSLINE + 0x00000000, 0x1ff01ff0, 0x18301830, 0x1f001830, 0x03001f00, 0x00000300, 0x03000300, 0x00000000, // ICON_HELP + 0x3ff00000, 0x2abc3550, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x00003ffc, // ICON_FILETYPE_ALPHA + 0x3ff00000, 0x201c2010, 0x22442184, 0x28142424, 0x29942814, 0x2ff42994, 0x20042004, 0x00003ffc, // ICON_FILETYPE_HOME + 0x07fe0000, 0x04020402, 0x7fe20402, 0x44224422, 0x44224422, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_LAYERS_VISIBLE + 0x07fe0000, 0x04020402, 0x7c020402, 0x44024402, 0x44024402, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_LAYERS + 0x00000000, 0x40027ffe, 0x7ffe4002, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000, // ICON_WINDOW + 0x09100000, 0x09f00910, 0x09100910, 0x00000910, 0x24a2779e, 0x27a224a2, 0x709e20a2, 0x00000000, // ICON_HIDPI + 0x3ff00000, 0x201c2010, 0x2a842e84, 0x2e842a84, 0x2ba42004, 0x2aa42aa4, 0x20042ba4, 0x00003ffc, // ICON_FILETYPE_BINARY + 0x00000000, 0x00000000, 0x00120012, 0x4a5e4bd2, 0x485233d2, 0x00004bd2, 0x00000000, 0x00000000, // ICON_HEX + 0x01800000, 0x381c0660, 0x23c42004, 0x23c42044, 0x13c82204, 0x08101008, 0x02400420, 0x00000180, // ICON_SHIELD + 0x007e0000, 0x20023fc2, 0x40227fe2, 0x400a403a, 0x400a400a, 0x400a400a, 0x4008400e, 0x00007ff8, // ICON_FILE_NEW + 0x00000000, 0x0042007e, 0x40027fc2, 0x44024002, 0x5f024402, 0x44024402, 0x7ffe4002, 0x00000000, // ICON_FOLDER_ADD + 0x44220000, 0x12482244, 0xf3cf0000, 0x14280420, 0x48122424, 0x08100810, 0x1ff81008, 0x03c00420, // ICON_ALARM + 0x0aa00000, 0x1ff80aa0, 0x1068700e, 0x1008706e, 0x1008700e, 0x1008700e, 0x0aa01ff8, 0x00000aa0, // ICON_CPU + 0x07e00000, 0x04201db8, 0x04a01c38, 0x04a01d38, 0x04a01d38, 0x04a01d38, 0x04201d38, 0x000007e0, // ICON_ROM + 0x00000000, 0x03c00000, 0x3c382ff0, 0x3c04380c, 0x01800000, 0x03c003c0, 0x00000180, 0x00000000, // ICON_STEP_OVER + 0x01800000, 0x01800180, 0x01800180, 0x03c007e0, 0x00000180, 0x01800000, 0x03c003c0, 0x00000180, // ICON_STEP_INTO + 0x01800000, 0x07e003c0, 0x01800180, 0x01800180, 0x00000180, 0x01800000, 0x03c003c0, 0x00000180, // ICON_STEP_OUT + 0x00000000, 0x0ff003c0, 0x181c1c34, 0x303c301c, 0x30003000, 0x1c301800, 0x03c00ff0, 0x00000000, // ICON_RESTART + 0x00000000, 0x00000000, 0x07e003c0, 0x0ff00ff0, 0x0ff00ff0, 0x03c007e0, 0x00000000, 0x00000000, // ICON_BREAKPOINT_ON + 0x00000000, 0x00000000, 0x042003c0, 0x08100810, 0x08100810, 0x03c00420, 0x00000000, 0x00000000, // ICON_BREAKPOINT_OFF + 0x00000000, 0x00000000, 0x1ff81ff8, 0x1ff80000, 0x00001ff8, 0x1ff81ff8, 0x00000000, 0x00000000, // ICON_BURGER_MENU + 0x00000000, 0x00000000, 0x00880070, 0x0c880088, 0x1e8810f8, 0x3e881288, 0x00000000, 0x00000000, // ICON_CASE_SENSITIVE + 0x00000000, 0x02000000, 0x07000a80, 0x07001fc0, 0x02000a80, 0x00300030, 0x00000000, 0x00000000, // ICON_REG_EXP + 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000, // ICON_FOLDER + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x00003ffc, // ICON_FILE + 0x1ff00000, 0x20082008, 0x17d02fe8, 0x05400ba0, 0x09200540, 0x23881010, 0x2fe827c8, 0x00001ff0, // ICON_SAND_TIMER + 0x01800000, 0x02400240, 0x05a00420, 0x09900990, 0x11881188, 0x21842004, 0x40024182, 0x00003ffc, // ICON_WARNING + 0x7ffe0000, 0x4ff24002, 0x4c324ff2, 0x4f824c02, 0x41824f82, 0x41824002, 0x40024182, 0x00007ffe, // ICON_HELP_BOX + 0x7ffe0000, 0x41824002, 0x40024182, 0x41824182, 0x41824182, 0x41824182, 0x40024182, 0x00007ffe, // ICON_INFO_BOX + 0x01800000, 0x04200240, 0x10080810, 0x7bde2004, 0x0a500a50, 0x08500bd0, 0x08100850, 0x00000ff0, // ICON_PRIORITY + 0x01800000, 0x18180660, 0x80016006, 0x98196006, 0x99996666, 0x19986666, 0x01800660, 0x00000000, // ICON_LAYERS_ISO + 0x07fe0000, 0x1c020402, 0x74021402, 0x54025402, 0x54025402, 0x500857fe, 0x40205ff8, 0x00007fe0, // ICON_LAYERS2 + 0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x422a422a, 0x422e422a, 0x40384e28, 0x00007fe0, // ICON_MLAYERS + 0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x5b2a512a, 0x512e552a, 0x40385128, 0x00007fe0, // ICON_MAPS + 0x04200000, 0x1cf00c60, 0x11f019f0, 0x0f3807b8, 0x1e3c0f3c, 0x1c1c1e1c, 0x1e3c1c1c, 0x00000f70, // ICON_HOT + 0x00000000, 0x20803f00, 0x2a202e40, 0x20082e10, 0x08021004, 0x02040402, 0x00900108, 0x00000060, // ICON_LABEL + 0x00000000, 0x042007e0, 0x47e27c3e, 0x4ffa4002, 0x47fa4002, 0x4ffa4002, 0x7ffe4002, 0x00000000, // ICON_NAME_ID + 0x7fe00000, 0x402e4020, 0x43ce5e0a, 0x40504078, 0x438e4078, 0x402e5e0a, 0x7fe04020, 0x00000000, // ICON_SLICING + 0x00000000, 0x40027ffe, 0x47c24002, 0x55425d42, 0x55725542, 0x50125552, 0x10105016, 0x00001ff0, // ICON_MANUAL_CONTROL + 0x7ffe0000, 0x43c24002, 0x48124422, 0x500a500a, 0x500a500a, 0x44224812, 0x400243c2, 0x00007ffe, // ICON_COLLISION + 0x03c00000, 0x10080c30, 0x21842184, 0x4ff24182, 0x41824ff2, 0x21842184, 0x0c301008, 0x000003c0, // ICON_CIRCLE_ADD + 0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x700e7e7e, 0x7e7e700e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0, // ICON_CIRCLE_ADD_FILL + 0x03c00000, 0x10080c30, 0x21842184, 0x41824182, 0x40024182, 0x21842184, 0x0c301008, 0x000003c0, // ICON_CIRCLE_WARNING + 0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x7e7e7e7e, 0x7ffe7e7e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0, // ICON_CIRCLE_WARNING_FILL + 0x00000000, 0x10041ffc, 0x10841004, 0x13e41084, 0x10841084, 0x10041004, 0x00001ffc, 0x00000000, // ICON_BOX_MORE + 0x00000000, 0x1ffc1ffc, 0x1f7c1ffc, 0x1c1c1f7c, 0x1f7c1f7c, 0x1ffc1ffc, 0x00001ffc, 0x00000000, // ICON_BOX_MORE_FILL + 0x00000000, 0x1ffc1ffc, 0x1ffc1ffc, 0x1c1c1ffc, 0x1ffc1ffc, 0x1ffc1ffc, 0x00001ffc, 0x00000000, // ICON_BOX_MINUS + 0x00000000, 0x10041ffc, 0x10041004, 0x13e41004, 0x10041004, 0x10041004, 0x00001ffc, 0x00000000, // ICON_BOX_MINUS_FILL + 0x07fe0000, 0x055606aa, 0x7ff606aa, 0x55766eba, 0x55766eaa, 0x55606ffe, 0x55606aa0, 0x00007fe0, // ICON_UNION + 0x07fe0000, 0x04020402, 0x7fe20402, 0x456246a2, 0x456246a2, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_INTERSECTION + 0x07fe0000, 0x055606aa, 0x7ff606aa, 0x4436442a, 0x4436442a, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_DIFFERENCE + 0x03c00000, 0x10080c30, 0x20042004, 0x60064002, 0x47e2581a, 0x20042004, 0x0c301008, 0x000003c0, // ICON_SPHERE + 0x03e00000, 0x08080410, 0x0c180808, 0x08080be8, 0x08080808, 0x08080808, 0x04100808, 0x000003e0, // ICON_CYLINDER + 0x00800000, 0x01400140, 0x02200220, 0x04100410, 0x08080808, 0x1c1c13e4, 0x08081004, 0x000007f0, // ICON_CONE + 0x00000000, 0x07e00000, 0x20841918, 0x40824082, 0x40824082, 0x19182084, 0x000007e0, 0x00000000, // ICON_ELLIPSOID + 0x00000000, 0x00000000, 0x20041ff8, 0x40024002, 0x40024002, 0x1ff82004, 0x00000000, 0x00000000, // ICON_CAPSULE + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_250 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_251 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_252 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_253 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_254 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_255 +}; + +// NOTE: A pointer to current icons array should be defined +static unsigned int *guiIconsPtr = guiIcons; + +#endif // !RAYGUI_NO_ICONS && !RAYGUI_CUSTOM_ICONS + +#ifndef RAYGUI_ICON_SIZE + #define RAYGUI_ICON_SIZE 0 +#endif + +// WARNING: Those values define the total size of the style data array, +// if changed, previous saved styles could become incompatible +#define RAYGUI_MAX_CONTROLS 16 // Maximum number of controls +#define RAYGUI_MAX_PROPS_BASE 16 // Maximum number of base properties +#define RAYGUI_MAX_PROPS_EXTENDED 8 // Maximum number of extended properties + +//---------------------------------------------------------------------------------- +// Module Types and Structures Definition +//---------------------------------------------------------------------------------- +// Gui control property style color element +typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static GuiState guiState = STATE_NORMAL; // Gui global state, if !STATE_NORMAL, forces defined state + +static Font guiFont = { 0 }; // Gui current font (WARNING: highly coupled to raylib) +static bool guiLocked = false; // Gui lock state (no inputs processed) +static float guiAlpha = 1.0f; // Gui controls transparency + +static unsigned int guiIconScale = 1; // Gui icon default scale (if icons enabled) + +static bool guiTooltip = false; // Tooltip enabled/disabled +static const char *guiTooltipPtr = NULL; // Tooltip string pointer (string provided by user) + +static bool guiControlExclusiveMode = false; // Gui control exclusive mode (no inputs processed except current control) +static Rectangle guiControlExclusiveRec = { 0 }; // Gui control exclusive bounds rectangle, used as an unique identifier + +static int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*() +//static int blinkCursorFrameCounter = 0; // Frame counter for cursor blinking +static int autoCursorCounter = 0; // Frame counter for automatic repeated cursor movement on key-down (cooldown and delay) + +//---------------------------------------------------------------------------------- +// Style data array for all gui style properties (allocated on data segment by default) +// +// NOTE 1: First set of BASE properties are generic to all controls but could be individually +// overwritten per control, first set of EXTENDED properties are generic to all controls and +// can not be overwritten individually but custom EXTENDED properties can be used by control +// +// NOTE 2: A new style set could be loaded over this array using GuiLoadStyle(), +// but default gui style could always be recovered with GuiLoadStyleDefault() +// +// guiStyle size is by default: 16*(16 + 8) = 384*4 = 1536 bytes = 1.5 KB +//---------------------------------------------------------------------------------- +static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)] = { 0 }; + +static bool guiStyleLoaded = false; // Style loaded flag for lazy style initialization + +//---------------------------------------------------------------------------------- +// Standalone Mode Functions Declaration +// +// NOTE: raygui depend on some raylib input and drawing functions +// To use raygui as standalone library, below functions must be defined by the user +//---------------------------------------------------------------------------------- +#if defined(RAYGUI_STANDALONE) + +#define KEY_RIGHT 262 +#define KEY_LEFT 263 +#define KEY_DOWN 264 +#define KEY_UP 265 +#define KEY_BACKSPACE 259 +#define KEY_ENTER 257 + +#define MOUSE_LEFT_BUTTON 0 + +// Input required functions +//------------------------------------------------------------------------------- +static Vector2 GetMousePosition(void); +static float GetMouseWheelMove(void); +static bool IsMouseButtonDown(int button); +static bool IsMouseButtonPressed(int button); +static bool IsMouseButtonReleased(int button); + +static bool IsKeyDown(int key); +static bool IsKeyPressed(int key); +static int GetCharPressed(void); // -- GuiTextBox(), GuiValueBox() +//------------------------------------------------------------------------------- + +// Drawing required functions +//------------------------------------------------------------------------------- +static void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle() +static void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker() +//------------------------------------------------------------------------------- + +// Text required functions +//------------------------------------------------------------------------------- +static Font GetFontDefault(void); // -- GuiLoadStyleDefault() +static Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // -- GuiLoadStyle(), load font + +static Texture2D LoadTextureFromImage(Image image); // -- GuiLoadStyle(), required to load texture from embedded font atlas image +static void SetShapesTexture(Texture2D tex, Rectangle rec); // -- GuiLoadStyle(), required to set shapes rec to font white rec (optimization) + +static char *LoadFileText(const char *fileName); // -- GuiLoadStyle(), required to load charset data +static void UnloadFileText(char *text); // -- GuiLoadStyle(), required to unload charset data + +static const char *GetDirectoryPath(const char *filePath); // -- GuiLoadStyle(), required to find charset/font file from text .rgs + +static int *LoadCodepoints(const char *text, int *count); // -- GuiLoadStyle(), required to load required font codepoints list +static void UnloadCodepoints(int *codepoints); // -- GuiLoadStyle(), required to unload codepoints list + +static unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // -- GuiLoadStyle() +//------------------------------------------------------------------------------- + +// raylib functions already implemented in raygui +//------------------------------------------------------------------------------- +static Color GetColor(int hexValue); // Returns a Color struct from hexadecimal value +static int ColorToInt(Color color); // Returns hexadecimal value for a Color +static bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle +static const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' +static const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings +static int TextToInteger(const char *text); // Get integer value from text +static float TextToFloat(const char *text); // Get float value from text + +static int GetCodepointNext(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded text +static const char *CodepointToUTF8(int codepoint, int *byteSize); // Encode codepoint into UTF-8 text (char array size returned as parameter) + +static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2); // Draw rectangle vertical gradient +//------------------------------------------------------------------------------- + +#endif // RAYGUI_STANDALONE + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize); // Load style from memory (binary only) + +static Rectangle GetTextBounds(int control, Rectangle bounds); // Get text bounds considering control bounds +static const char *GetTextIcon(const char *text, int *iconId); // Get text icon if provided and move text cursor + +static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, Color tint); // Gui draw text using default font +static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color); // Gui draw rectangle using default raygui style + +static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow); // Split controls text into multiple strings +static Vector3 ConvertHSVtoRGB(Vector3 hsv); // Convert color data from HSV to RGB +static Vector3 ConvertRGBtoHSV(Vector3 rgb); // Convert color data from RGB to HSV + +static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue); // Scroll bar control, used by GuiScrollPanel() +static void GuiTooltip(Rectangle controlRec); // Draw tooltip using control rec position + +static Color GuiFade(Color color, float alpha); // Fade color by an alpha factor + +//---------------------------------------------------------------------------------- +// Gui Setup Functions Definition +//---------------------------------------------------------------------------------- +// Enable gui global state +// NOTE: Checking for STATE_DISABLED to avoid messing custom global state setups +void GuiEnable(void) { if (guiState == STATE_DISABLED) guiState = STATE_NORMAL; } + +// Disable gui global state +// NOTE: Checking for STATE_NORMAL to avoid messing custom global state setups +void GuiDisable(void) { if (guiState == STATE_NORMAL) guiState = STATE_DISABLED; } + +// Lock gui global state +void GuiLock(void) { guiLocked = true; } + +// Unlock gui global state +void GuiUnlock(void) { guiLocked = false; } + +// Check if gui is locked (global state) +bool GuiIsLocked(void) { return guiLocked; } + +// Set gui controls alpha global state +void GuiSetAlpha(float alpha) +{ + if (alpha < 0.0f) alpha = 0.0f; + else if (alpha > 1.0f) alpha = 1.0f; + + guiAlpha = alpha; +} + +// Set gui state (global state) +void GuiSetState(int state) { guiState = (GuiState)state; } + +// Get gui state (global state) +int GuiGetState(void) { return guiState; } + +// Set custom gui font +// NOTE: Font loading/unloading is external to raygui +void GuiSetFont(Font font) +{ + if (font.texture.id > 0) + { + // NOTE: If a font is tried to be set but default style has not been lazily loaded first, + // it will be overwritten, so default style loading needs to be forced first + if (!guiStyleLoaded) GuiLoadStyleDefault(); + + guiFont = font; + } +} + +// Get custom gui font +Font GuiGetFont(void) +{ + return guiFont; +} + +// Set control style property value +void GuiSetStyle(int control, int property, int value) +{ + if (!guiStyleLoaded) GuiLoadStyleDefault(); + guiStyle[control*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property] = value; + + // Default properties are propagated to all controls + if ((control == 0) && (property < RAYGUI_MAX_PROPS_BASE)) + { + for (int i = 1; i < RAYGUI_MAX_CONTROLS; i++) guiStyle[i*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property] = value; + } +} + +// Get control style property value +int GuiGetStyle(int control, int property) +{ + if (!guiStyleLoaded) GuiLoadStyleDefault(); + return guiStyle[control*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property]; +} + +//---------------------------------------------------------------------------------- +// Gui Controls Functions Definition +//---------------------------------------------------------------------------------- + +// Window Box control +int GuiWindowBox(Rectangle bounds, const char *title) +{ + // Window title bar height (including borders) + // NOTE: This define is also used by GuiMessageBox() and GuiTextInputBox() + #if !defined(RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT) + #define RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT 24 + #endif + + #if !defined(RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT) + #define RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT 18 + #endif + + int result = 0; + //GuiState state = guiState; + + int statusBarHeight = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT; + int statusBorderWidth = GuiGetStyle(STATUSBAR, BORDER_WIDTH); + + Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)statusBarHeight }; + if (bounds.height < statusBarHeight*2.0f) bounds.height = statusBarHeight*2.0f; + + const float vPadding = statusBarHeight/2.0f - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT/2.0f; + Rectangle windowPanel = { bounds.x, bounds.y + (float)statusBarHeight - (float)statusBorderWidth, bounds.width, bounds.height - (float)statusBarHeight + (float)statusBorderWidth }; + Rectangle closeButtonRec = { statusBar.x + statusBar.width - (float)statusBorderWidth - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT - vPadding, + statusBar.y + vPadding, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT }; + + // Update control + //-------------------------------------------------------------------- + // NOTE: Logic is directly managed by button + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiPanel(windowPanel, NULL); // Draw window base + GuiStatusBar(statusBar, title); // Draw window header as status bar + + // Draw window close button + int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); + int tempTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, BORDER_WIDTH, 1); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); +#if defined(RAYGUI_NO_ICONS) + result = GuiButton(closeButtonRec, "x"); +#else + result = GuiButton(closeButtonRec, GuiIconText(ICON_CROSS_SMALL, NULL)); +#endif + GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment); + //-------------------------------------------------------------------- + + return result; // Window close button clicked: result = 1 +} + +// Group Box control with text name +int GuiGroupBox(Rectangle bounds, const char *text) +{ + #if !defined(RAYGUI_GROUPBOX_LINE_THICK) + #define RAYGUI_GROUPBOX_LINE_THICK 1 + #endif + + int result = 0; + GuiState state = guiState; + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, RAYGUI_GROUPBOX_LINE_THICK }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR))); + + GuiLine(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2, bounds.width, (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }, text); + //-------------------------------------------------------------------- + + return result; +} + +// Line control +int GuiLine(Rectangle bounds, const char *text) +{ + #if !defined(RAYGUI_LINE_MARGIN_TEXT) + #define RAYGUI_LINE_MARGIN_TEXT 12 + #endif + #if !defined(RAYGUI_LINE_TEXT_PADDING) + #define RAYGUI_LINE_TEXT_PADDING 4 + #endif + + int result = 0; + GuiState state = guiState; + + Color color = GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)); + + // Draw control + //-------------------------------------------------------------------- + if (text == NULL) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height/2, bounds.width, 1 }, 0, BLANK, color); + else + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GuiGetTextWidth(text) + 2; + textBounds.height = bounds.height; + textBounds.x = bounds.x + RAYGUI_LINE_MARGIN_TEXT; + textBounds.y = bounds.y; + + // Draw line with embedded text label: "--- text --------------" + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height/2, RAYGUI_LINE_MARGIN_TEXT - RAYGUI_LINE_TEXT_PADDING, 1 }, 0, BLANK, color); + GuiDrawText(text, textBounds, TEXT_ALIGN_LEFT, color); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + 12 + textBounds.width + 4, bounds.y + bounds.height/2, bounds.width - textBounds.width - RAYGUI_LINE_MARGIN_TEXT - RAYGUI_LINE_TEXT_PADDING, 1 }, 0, BLANK, color); + } + //-------------------------------------------------------------------- + + return result; +} + +// Panel control +int GuiPanel(Rectangle bounds, const char *text) +{ + #if !defined(RAYGUI_PANEL_BORDER_WIDTH) + #define RAYGUI_PANEL_BORDER_WIDTH 1 + #endif + + int result = 0; + GuiState state = guiState; + + // Text will be drawn as a header bar (if provided) + Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT }; + if ((text != NULL) && (bounds.height < RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f)) bounds.height = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f; + + if (text != NULL) + { + // Move panel bounds after the header bar + bounds.y += (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1; + bounds.height -= (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1; + } + + // Draw control + //-------------------------------------------------------------------- + if (text != NULL) GuiStatusBar(statusBar, text); // Draw panel header as status bar + + GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)), + GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BASE_COLOR_DISABLED : (int)BACKGROUND_COLOR))); + //-------------------------------------------------------------------- + + return result; +} + +// Tab Bar control +// NOTE: Using GuiToggle() for the TABS +int GuiTabBar(Rectangle bounds, const char **text, int count, int *active) +{ + #define RAYGUI_TABBAR_ITEM_WIDTH 148 + + int result = -1; + //GuiState state = guiState; + + Rectangle tabBounds = { bounds.x, bounds.y, RAYGUI_TABBAR_ITEM_WIDTH, bounds.height }; + + if (*active < 0) *active = 0; + else if (*active > count - 1) *active = count - 1; + + int offsetX = 0; // Required in case tabs go out of screen + offsetX = (*active + 2)*RAYGUI_TABBAR_ITEM_WIDTH - GetScreenWidth(); + if (offsetX < 0) offsetX = 0; + + bool toggle = false; // Required for individual toggles + + // Draw control + //-------------------------------------------------------------------- + for (int i = 0; i < count; i++) + { + tabBounds.x = bounds.x + (RAYGUI_TABBAR_ITEM_WIDTH + 4)*i - offsetX; + + if (tabBounds.x < GetScreenWidth()) + { + // Draw tabs as toggle controls + int textAlignment = GuiGetStyle(TOGGLE, TEXT_ALIGNMENT); + int textPadding = GuiGetStyle(TOGGLE, TEXT_PADDING); + GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + GuiSetStyle(TOGGLE, TEXT_PADDING, 8); + + if (i == (*active)) + { + toggle = true; + GuiToggle(tabBounds, text[i], &toggle); + } + else + { + toggle = false; + GuiToggle(tabBounds, text[i], &toggle); + if (toggle) *active = i; + } + + // Close tab with middle mouse button pressed + if (CheckCollisionPointRec(GUI_POINTER_POSITION, tabBounds) && IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) result = i; + + GuiSetStyle(TOGGLE, TEXT_PADDING, textPadding); + GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, textAlignment); + + // Draw tab close button + // NOTE: Only draw close button for current tab: if (CheckCollisionPointRec(mousePosition, tabBounds)) + int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); + int tempTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, BORDER_WIDTH, 1); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); +#if defined(RAYGUI_NO_ICONS) + if (GuiButton(RAYGUI_CLITERAL(Rectangle){ tabBounds.x + tabBounds.width - 14 - 5, tabBounds.y + 5, 14, 14 }, "x")) result = i; +#else + if (GuiButton(RAYGUI_CLITERAL(Rectangle){ tabBounds.x + tabBounds.width - 14 - 5, tabBounds.y + 5, 14, 14 }, GuiIconText(ICON_CROSS_SMALL, NULL))) result = i; +#endif + GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment); + } + } + + // Draw tab-bar bottom line + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, 1 }, 0, BLANK, GetColor(GuiGetStyle(TOGGLE, BORDER_COLOR_NORMAL))); + //-------------------------------------------------------------------- + + return result; // Return as result the current TAB closing requested +} + +// Scroll Panel control +int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view) +{ + #define RAYGUI_MIN_SCROLLBAR_WIDTH 40 + #define RAYGUI_MIN_SCROLLBAR_HEIGHT 40 + #define RAYGUI_MIN_MOUSE_WHEEL_SPEED 20 + + int result = 0; + GuiState state = guiState; + + Rectangle temp = { 0 }; + if (view == NULL) view = &temp; + + Vector2 scrollPos = { 0.0f, 0.0f }; + if (scroll != NULL) scrollPos = *scroll; + + // Text will be drawn as a header bar (if provided) + Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT }; + if (bounds.height < RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f) bounds.height = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f; + + if (text != NULL) + { + // Move panel bounds after the header bar + bounds.y += (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1; + bounds.height -= (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + 1; + } + + bool hasHorizontalScrollBar = (content.width > bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH))? true : false; + bool hasVerticalScrollBar = (content.height > bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH))? true : false; + + // Recheck to account for the other scrollbar being visible + if (!hasHorizontalScrollBar) hasHorizontalScrollBar = (hasVerticalScrollBar && (content.width > (bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH))))? true : false; + if (!hasVerticalScrollBar) hasVerticalScrollBar = (hasHorizontalScrollBar && (content.height > (bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH))))? true : false; + + int horizontalScrollBarWidth = hasHorizontalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0; + int verticalScrollBarWidth = hasVerticalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0; + Rectangle horizontalScrollBar = { + (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + verticalScrollBarWidth : (float)bounds.x) + GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)bounds.y + bounds.height - horizontalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)bounds.width - verticalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)horizontalScrollBarWidth + }; + Rectangle verticalScrollBar = { + (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)bounds.x + bounds.width - verticalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH)), + (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)verticalScrollBarWidth, + (float)bounds.height - horizontalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) + }; + + // Make sure scroll bars have a minimum width/height + if (horizontalScrollBar.width < RAYGUI_MIN_SCROLLBAR_WIDTH) horizontalScrollBar.width = RAYGUI_MIN_SCROLLBAR_WIDTH; + if (verticalScrollBar.height < RAYGUI_MIN_SCROLLBAR_HEIGHT) verticalScrollBar.height = RAYGUI_MIN_SCROLLBAR_HEIGHT; + + // Calculate view area (area without the scrollbars) + *view = (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? + RAYGUI_CLITERAL(Rectangle){ bounds.x + verticalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth } : + RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth }; + + // Clip view area to the actual content size + if (view->width > content.width) view->width = content.width; + if (view->height > content.height) view->height = content.height; + + float horizontalMin = hasHorizontalScrollBar? ((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)-verticalScrollBarWidth : 0) - (float)GuiGetStyle(DEFAULT, BORDER_WIDTH) : (((float)GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)-verticalScrollBarWidth : 0) - (float)GuiGetStyle(DEFAULT, BORDER_WIDTH); + float horizontalMax = hasHorizontalScrollBar? content.width - bounds.width + (float)verticalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH) - (((float)GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)verticalScrollBarWidth : 0) : (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH); + float verticalMin = hasVerticalScrollBar? 0.0f : -1.0f; + float verticalMax = hasVerticalScrollBar? content.height - bounds.height + (float)horizontalScrollBarWidth + (float)GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH); + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + // Check button state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else state = STATE_FOCUSED; + +#if defined(SUPPORT_SCROLLBAR_KEY_INPUT) + if (hasHorizontalScrollBar) + { + if (GUI_KEY_DOWN(KEY_RIGHT)) scrollPos.x -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (GUI_KEY_DOWN(KEY_LEFT)) scrollPos.x += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + } + + if (hasVerticalScrollBar) + { + if (GUI_KEY_DOWN(KEY_DOWN)) scrollPos.y -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (GUI_KEY_DOWN(KEY_UP)) scrollPos.y += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + } +#endif + float scrollDelta = GUI_SCROLL_DELTA; + + // Set scrolling speed with mouse wheel based on ratio between bounds and content + Vector2 scrollSpeed = { content.width/bounds.width, content.height/bounds.height }; + if (scrollSpeed.x < RAYGUI_MIN_MOUSE_WHEEL_SPEED) scrollSpeed.x = RAYGUI_MIN_MOUSE_WHEEL_SPEED; + if (scrollSpeed.y < RAYGUI_MIN_MOUSE_WHEEL_SPEED) scrollSpeed.y = RAYGUI_MIN_MOUSE_WHEEL_SPEED; + + // Horizontal and vertical scrolling with mouse wheel + if (hasHorizontalScrollBar && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_LEFT_SHIFT))) scrollPos.x += scrollDelta*scrollSpeed.x; + else scrollPos.y += scrollDelta*scrollSpeed.y; // Vertical scroll + } + } + + // Normalize scroll values + if (scrollPos.x > -horizontalMin) scrollPos.x = -horizontalMin; + if (scrollPos.x < -horizontalMax) scrollPos.x = -horizontalMax; + if (scrollPos.y > -verticalMin) scrollPos.y = -verticalMin; + if (scrollPos.y < -verticalMax) scrollPos.y = -verticalMax; + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (text != NULL) GuiStatusBar(statusBar, text); // Draw panel header as status bar + + GuiDrawRectangle(bounds, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background + + // Save size of the scrollbar slider + const int slider = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE); + + // Draw horizontal scrollbar if visible + if (hasHorizontalScrollBar) + { + // Change scrollbar slider size to show the diff in size between the content width and the widget width + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)(((bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth)/(int)content.width)*((int)bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth))); + scrollPos.x = (float)-GuiScrollBar(horizontalScrollBar, (int)-scrollPos.x, (int)horizontalMin, (int)horizontalMax); + } + else scrollPos.x = 0.0f; + + // Draw vertical scrollbar if visible + if (hasVerticalScrollBar) + { + // Change scrollbar slider size to show the diff in size between the content height and the widget height + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)(((bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)/(int)content.height)*((int)bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth))); + scrollPos.y = (float)-GuiScrollBar(verticalScrollBar, (int)-scrollPos.y, (int)verticalMin, (int)verticalMax); + } + else scrollPos.y = 0.0f; + + // Draw detail corner rectangle if both scroll bars are visible + if (hasHorizontalScrollBar && hasVerticalScrollBar) + { + Rectangle corner = { (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) + 2) : (horizontalScrollBar.x + horizontalScrollBar.width + 2), verticalScrollBar.y + verticalScrollBar.height + 2, (float)horizontalScrollBarWidth - 4, (float)verticalScrollBarWidth - 4 }; + GuiDrawRectangle(corner, 0, BLANK, GetColor(GuiGetStyle(LISTVIEW, TEXT + (state*3)))); + } + + // Draw scrollbar lines depending on current state + GuiDrawRectangle(bounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + (state*3))), BLANK); + + // Set scrollbar slider size back to the way it was before + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, slider); + //-------------------------------------------------------------------- + + if (scroll != NULL) *scroll = scrollPos; + + return result; +} + +// Label control +int GuiLabel(Rectangle bounds, const char *text) +{ + int result = 0; + GuiState state = guiState; + + // Update control + //-------------------------------------------------------------------- + //... + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + return result; +} + +// Button control, returns true when clicked +int GuiButton(Rectangle bounds, const char *text) +{ + int result = 0; + GuiState state = guiState; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + // Check button state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else state = STATE_FOCUSED; + + if (GUI_BUTTON_RELEASED) result = 1; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), GetColor(GuiGetStyle(BUTTON, BASE + (state*3)))); + GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), GetColor(GuiGetStyle(BUTTON, TEXT + (state*3)))); + + if (state == STATE_FOCUSED) GuiTooltip(bounds); + //------------------------------------------------------------------ + + return result; // Button pressed: result = 1 +} + +// Label button control +int GuiLabelButton(Rectangle bounds, const char *text) +{ + GuiState state = guiState; + bool pressed = false; + + // NOTE: Force bounds.width to be all text + float textWidth = (float)GuiGetTextWidth(text); + if ((bounds.width - 2*GuiGetStyle(LABEL, BORDER_WIDTH) - 2*GuiGetStyle(LABEL, TEXT_PADDING)) < textWidth) bounds.width = textWidth + 2*GuiGetStyle(LABEL, BORDER_WIDTH) + 2*GuiGetStyle(LABEL, TEXT_PADDING) + 2; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + // Check checkbox state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else state = STATE_FOCUSED; + + if (GUI_BUTTON_RELEASED) pressed = true; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + return pressed; +} + +// Toggle Button control +int GuiToggle(Rectangle bounds, const char *text, bool *active) +{ + int result = 0; + GuiState state = guiState; + + bool temp = false; + if (active == NULL) active = &temp; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + // Check toggle button state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else if (GUI_BUTTON_RELEASED) + { + state = STATE_NORMAL; + *active = !(*active); + } + else state = STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state == STATE_NORMAL) + { + GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, ((*active)? BORDER_COLOR_PRESSED : (BORDER + state*3)))), GetColor(GuiGetStyle(TOGGLE, ((*active)? BASE_COLOR_PRESSED : (BASE + state*3))))); + GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TOGGLE, ((*active)? TEXT_COLOR_PRESSED : (TEXT + state*3))))); + } + else + { + GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, BORDER + state*3)), GetColor(GuiGetStyle(TOGGLE, BASE + state*3))); + GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TOGGLE, TEXT + state*3))); + } + + if (state == STATE_FOCUSED) GuiTooltip(bounds); + //-------------------------------------------------------------------- + + return result; +} + +// Toggle Group control +int GuiToggleGroup(Rectangle bounds, const char *text, int *active) +{ + #if !defined(RAYGUI_TOGGLEGROUP_MAX_ITEMS) + #define RAYGUI_TOGGLEGROUP_MAX_ITEMS 32 + #endif + + int result = 0; + float initBoundsX = bounds.x; + + int temp = 0; + if (active == NULL) active = &temp; + + bool toggle = false; // Required for individual toggles + + // Get substrings items from text (items pointers) + int rows[RAYGUI_TOGGLEGROUP_MAX_ITEMS] = { 0 }; + int itemCount = 0; + const char **items = GuiTextSplit(text, ';', &itemCount, rows); + + int prevRow = rows[0]; + + for (int i = 0; i < itemCount; i++) + { + if (prevRow != rows[i]) + { + bounds.x = initBoundsX; + bounds.y += (bounds.height + GuiGetStyle(TOGGLE, GROUP_PADDING)); + prevRow = rows[i]; + } + + if (i == (*active)) + { + toggle = true; + GuiToggle(bounds, items[i], &toggle); + } + else + { + toggle = false; + GuiToggle(bounds, items[i], &toggle); + if (toggle) *active = i; + } + + bounds.x += (bounds.width + GuiGetStyle(TOGGLE, GROUP_PADDING)); + } + + return result; +} + +// Toggle Slider control extended +int GuiToggleSlider(Rectangle bounds, const char *text, int *active) +{ + int result = 0; + GuiState state = guiState; + + int temp = 0; + if (active == NULL) active = &temp; + + //bool toggle = false; // Required for individual toggles + + // Get substrings items from text (items pointers) + int itemCount = 0; + const char **items = NULL; + + if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL); + + Rectangle slider = { + 0, // Calculated later depending on the active toggle + bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING), + (bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - (itemCount + 1)*GuiGetStyle(SLIDER, SLIDER_PADDING))/itemCount, + bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) }; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else if (GUI_BUTTON_RELEASED) + { + state = STATE_PRESSED; + (*active)++; + result = 1; + } + else state = STATE_FOCUSED; + } + + if ((*active) && (state != STATE_FOCUSED)) state = STATE_PRESSED; + } + + if (*active >= itemCount) *active = 0; + slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH) + (*active + 1)*GuiGetStyle(SLIDER, SLIDER_PADDING) + (*active)*slider.width; + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, BORDER + (state*3))), + GetColor(GuiGetStyle(TOGGLE, BASE_COLOR_NORMAL))); + + // Draw internal slider + if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED))); + else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_FOCUSED))); + else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED))); + + // Draw text in slider + if (text != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GuiGetTextWidth(text); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = slider.x + slider.width/2 - textBounds.width/2; + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + GuiDrawText(items[*active], textBounds, GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, TEXT + (state*3))), guiAlpha)); + } + //-------------------------------------------------------------------- + + return result; +} + +// Check Box control, returns 1 when state changed +int GuiCheckBox(Rectangle bounds, const char *text, bool *checked) +{ + int result = 0; + GuiState state = guiState; + + bool temp = false; + if (checked == NULL) checked = &temp; + + Rectangle textBounds = { 0 }; + + if (text != NULL) + { + textBounds.width = (float)GuiGetTextWidth(text) + 2; + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + if (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(CHECKBOX, TEXT_PADDING); + } + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + Rectangle totalBounds = { + (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT)? textBounds.x : bounds.x, + bounds.y, + bounds.width + textBounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING), + bounds.height, + }; + + // Check checkbox state + if (CheckCollisionPointRec(mousePoint, totalBounds)) + { + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else state = STATE_FOCUSED; + + if (GUI_BUTTON_RELEASED) + { + *checked = !(*checked); + result = 1; + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(CHECKBOX, BORDER_WIDTH), GetColor(GuiGetStyle(CHECKBOX, BORDER + (state*3))), BLANK); + + if (*checked) + { + Rectangle check = { bounds.x + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING), + bounds.y + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING), + bounds.width - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING)), + bounds.height - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING)) }; + GuiDrawRectangle(check, 0, BLANK, GetColor(GuiGetStyle(CHECKBOX, TEXT + state*3))); + } + + GuiDrawText(text, textBounds, (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + return result; +} + +// Combo Box control +int GuiComboBox(Rectangle bounds, const char *text, int *active) +{ + int result = 0; + GuiState state = guiState; + + int temp = 0; + if (active == NULL) active = &temp; + + bounds.width -= (GuiGetStyle(COMBOBOX, COMBO_BUTTON_WIDTH) + GuiGetStyle(COMBOBOX, COMBO_BUTTON_SPACING)); + + Rectangle selector = { (float)bounds.x + bounds.width + GuiGetStyle(COMBOBOX, COMBO_BUTTON_SPACING), + (float)bounds.y, (float)GuiGetStyle(COMBOBOX, COMBO_BUTTON_WIDTH), (float)bounds.height }; + + // Get substrings items from text (items pointers, lengths and count) + int itemCount = 0; + const char **items = GuiTextSplit(text, ';', &itemCount, NULL); + + if (*active < 0) *active = 0; + else if (*active > (itemCount - 1)) *active = itemCount - 1; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1) && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + if (CheckCollisionPointRec(mousePoint, bounds) || + CheckCollisionPointRec(mousePoint, selector)) + { + if (GUI_BUTTON_PRESSED) + { + *active += 1; + if (*active >= itemCount) *active = 0; // Cyclic combobox + } + + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else state = STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + // Draw combo box main + GuiDrawRectangle(bounds, GuiGetStyle(COMBOBOX, BORDER_WIDTH), GetColor(GuiGetStyle(COMBOBOX, BORDER + (state*3))), GetColor(GuiGetStyle(COMBOBOX, BASE + (state*3)))); + GuiDrawText(items[*active], GetTextBounds(COMBOBOX, bounds), GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(COMBOBOX, TEXT + (state*3)))); + + // Draw selector using a custom button + // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values + int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); + int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, BORDER_WIDTH, 1); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + + GuiButton(selector, TextFormat("%i/%i", *active + 1, itemCount)); + + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign); + GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); + //-------------------------------------------------------------------- + + return result; +} + +// Dropdown Box control +// NOTE: Returns mouse click +int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode) +{ + int result = 0; + GuiState state = guiState; + + int temp = 0; + if (active == NULL) active = &temp; + + int itemSelected = *active; + int itemFocused = -1; + + int direction = 0; // Dropdown box open direction: down (default) + if (GuiGetStyle(DROPDOWNBOX, DROPDOWN_ROLL_UP) == 1) direction = 1; // Up + + // Get substrings items from text (items pointers, lengths and count) + int itemCount = 0; + const char **items = GuiTextSplit(text, ';', &itemCount, NULL); + + Rectangle boundsOpen = bounds; + boundsOpen.height = (itemCount + 1)*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); + if (direction == 1) boundsOpen.y -= itemCount*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)) + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING); + + Rectangle itemBounds = bounds; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1) && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + if (editMode) + { + state = STATE_PRESSED; + + // Check if mouse has been pressed or released outside limits + if (!CheckCollisionPointRec(mousePoint, boundsOpen)) + { + if (GUI_BUTTON_PRESSED || GUI_BUTTON_RELEASED) result = 1; + } + + // Check if already selected item has been pressed again + if (CheckCollisionPointRec(mousePoint, bounds) && GUI_BUTTON_PRESSED) result = 1; + + // Check focused and selected item + for (int i = 0; i < itemCount; i++) + { + // Update item rectangle y position for next item + if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); + else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); + + if (CheckCollisionPointRec(mousePoint, itemBounds)) + { + itemFocused = i; + if (GUI_BUTTON_RELEASED) + { + itemSelected = i; + result = 1; // Item selected + } + break; + } + } + + itemBounds = bounds; + } + else + { + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (GUI_BUTTON_PRESSED) + { + result = 1; + state = STATE_PRESSED; + } + else state = STATE_FOCUSED; + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (editMode) GuiPanel(boundsOpen, NULL); + + GuiDrawRectangle(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER + state*3)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE + state*3))); + GuiDrawText(items[itemSelected], GetTextBounds(DROPDOWNBOX, bounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + state*3))); + + if (editMode) + { + // Draw visible items + for (int i = 0; i < itemCount; i++) + { + // Update item rectangle y position for next item + if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); + else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); + + if (i == itemSelected) + { + GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_PRESSED))); + GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_PRESSED))); + } + else if (i == itemFocused) + { + GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_FOCUSED))); + GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_FOCUSED))); + } + else GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_NORMAL))); + } + } + + if (!GuiGetStyle(DROPDOWNBOX, DROPDOWN_ARROW_HIDDEN)) + { + // Draw arrows (using icon if available) +#if defined(RAYGUI_NO_ICONS) + GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 }, + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); +#else + GuiDrawText(direction? "#121#" : "#120#", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 6, 10, 10 }, + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); // ICON_ARROW_DOWN_FILL +#endif + } + //-------------------------------------------------------------------- + + *active = itemSelected; + + // TODO: Use result to return more internal states: mouse-press out-of-bounds, mouse-press over selected-item... + return result; // Mouse click: result = 1 +} + +// Text Box control +// NOTE: Returns true on ENTER pressed (useful for data validation) +int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) +{ + #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) + #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN 20 // Frames to wait for autocursor movement + #endif + #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) + #define RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY 1 // Frames delay for autocursor movement + #endif + + int result = 0; + GuiState state = guiState; + + bool multiline = false; // TODO: Consider multiline text input + int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); + + Rectangle textBounds = GetTextBounds(TEXTBOX, bounds); + int textLength = (text != NULL)? (int)strlen(text) : 0; // Get current text length + int thisCursorIndex = textBoxCursorIndex; + if (thisCursorIndex > textLength) thisCursorIndex = textLength; + int textWidth = GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex); + int textIndexOffset = 0; // Text index offset to start drawing in the box + + // Cursor rectangle + // NOTE: Position X value should be updated + Rectangle cursor = { + textBounds.x + textWidth + GuiGetStyle(DEFAULT, TEXT_SPACING), + textBounds.y + textBounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE), + 2, + (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*2 + }; + + if (cursor.height >= bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2; + if (cursor.y < (bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH))) cursor.y = bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH); + + // Mouse cursor rectangle + // NOTE: Initialized outside of screen + Rectangle mouseCursor = cursor; + mouseCursor.x = -1; + mouseCursor.width = 1; + + // Blink-cursor frame counter + //if (!autoCursorMode) blinkCursorFrameCounter++; + //else blinkCursorFrameCounter = 0; + + // Update control + //-------------------------------------------------------------------- + // WARNING: Text editing is only supported under certain conditions: + if ((state != STATE_DISABLED) && // Control not disabled + !GuiGetStyle(TEXTBOX, TEXT_READONLY) && // TextBox not on read-only mode + !guiLocked && // Gui not locked + !guiControlExclusiveMode && // No gui slider on dragging + (wrapMode == TEXT_WRAP_NONE)) // No wrap mode + { + Vector2 mousePosition = GUI_POINTER_POSITION; + + if (editMode) + { + // GLOBAL: Auto-cursor movement logic + // NOTE: Keystrokes are handled repeatedly when button is held down for some time + if (GUI_KEY_DOWN(KEY_LEFT) || GUI_KEY_DOWN(KEY_RIGHT) || GUI_KEY_DOWN(KEY_UP) || GUI_KEY_DOWN(KEY_DOWN) || GUI_KEY_DOWN(KEY_BACKSPACE) || GUI_KEY_DOWN(KEY_DELETE)) autoCursorCounter++; + else autoCursorCounter = 0; + + bool autoCursorShouldTrigger = (autoCursorCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) && ((autoCursorCounter % RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0); + + state = STATE_PRESSED; + + if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength; + + // If text does not fit in the textbox and current cursor position is out of bounds, + // adding an index offset to text for drawing only what requires depending on cursor + while (textWidth >= textBounds.width) + { + int nextCodepointSize = 0; + GetCodepointNext(text + textIndexOffset, &nextCodepointSize); + + textIndexOffset += nextCodepointSize; + + textWidth = GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex); + } + + int codepoint = GUI_INPUT_KEY; // Get Unicode codepoint + if (multiline && GUI_KEY_PRESSED(KEY_ENTER)) codepoint = (int)'\n'; + + // Encode codepoint as UTF-8 + int codepointSize = 0; + const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize); + + // Handle text paste action + if (GUI_KEY_PRESSED(KEY_V) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) + { + const char *pasteText = GetClipboardText(); + if (pasteText != NULL) + { + int pasteLength = 0; + int pasteCodepoint; + int pasteCodepointSize; + + // Count how many codepoints to copy, stopping at the first unwanted control character + while (true) + { + pasteCodepoint = GetCodepointNext(pasteText + pasteLength, &pasteCodepointSize); + if (textLength + pasteLength + pasteCodepointSize >= textSize) break; + if (!(multiline && (pasteCodepoint == (int)'\n')) && !(pasteCodepoint >= 32)) break; + pasteLength += pasteCodepointSize; + } + + if (pasteLength > 0) + { + // Move forward data from cursor position + for (int i = textLength + pasteLength; i > textBoxCursorIndex; i--) text[i] = text[i - pasteLength]; + + // Paste data in at cursor + for (int i = 0; i < pasteLength; i++) text[textBoxCursorIndex + i] = pasteText[i]; + + textBoxCursorIndex += pasteLength; + textLength += pasteLength; + text[textLength] = '\0'; + } + } + } + else if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize)) + { + // Adding codepoint to text, at current cursor position + + // Move forward data from cursor position + for (int i = (textLength + codepointSize); i > textBoxCursorIndex; i--) text[i] = text[i - codepointSize]; + + // Add new codepoint in current cursor position + for (int i = 0; i < codepointSize; i++) text[textBoxCursorIndex + i] = charEncoded[i]; + + textBoxCursorIndex += codepointSize; + textLength += codepointSize; + + // Make sure text last character is EOL + text[textLength] = '\0'; + } + + // Move cursor to start + if ((textLength > 0) && GUI_KEY_PRESSED(KEY_HOME)) textBoxCursorIndex = 0; + + // Move cursor to end + if ((textLength > textBoxCursorIndex) && GUI_KEY_PRESSED(KEY_END)) textBoxCursorIndex = textLength; + + // Delete related codepoints from text, after current cursor position + if ((textLength > textBoxCursorIndex) && GUI_KEY_PRESSED(KEY_DELETE) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) + { + int offset = textBoxCursorIndex; + int accCodepointSize = 0; + int nextCodepointSize; + int nextCodepoint; + + // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace) + // Not using isalnum() since it only works on ASCII characters + nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); + bool puctuation = ispunct(nextCodepoint & 0xff); + while (offset < textLength) + { + if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) + break; + offset += nextCodepointSize; + accCodepointSize += nextCodepointSize; + nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); + } + + // Check whitespace to delete (ASCII only) + while (offset < textLength) + { + if (!isspace(nextCodepoint & 0xff)) break; + + offset += nextCodepointSize; + accCodepointSize += nextCodepointSize; + nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); + } + + // Move text after cursor forward (including final null terminator) + for (int i = offset; i <= textLength; i++) text[i - accCodepointSize] = text[i]; + + textLength -= accCodepointSize; + } + + else if ((textLength > textBoxCursorIndex) && (GUI_KEY_PRESSED(KEY_DELETE) || (GUI_KEY_DOWN(KEY_DELETE) && autoCursorShouldTrigger))) + { + // Delete single codepoint from text, after current cursor position + + int nextCodepointSize = 0; + GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize); + + // Move text after cursor forward (including final null terminator) + for (int i = textBoxCursorIndex + nextCodepointSize; i <= textLength; i++) text[i - nextCodepointSize] = text[i]; + + textLength -= nextCodepointSize; + } + + // Delete related codepoints from text, before current cursor position + if ((textBoxCursorIndex > 0) && GUI_KEY_PRESSED(KEY_BACKSPACE) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) + { + int offset = textBoxCursorIndex; + int accCodepointSize = 0; + int prevCodepointSize = 0; + int prevCodepoint = 0; + + // Check whitespace to delete (ASCII only) + while (offset > 0) + { + prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); + if (!isspace(prevCodepoint & 0xff)) break; + + offset -= prevCodepointSize; + accCodepointSize += prevCodepointSize; + } + + // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace) + // Not using isalnum() since it only works on ASCII characters + bool puctuation = ispunct(prevCodepoint & 0xff); + while (offset > 0) + { + prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); + if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break; + + offset -= prevCodepointSize; + accCodepointSize += prevCodepointSize; + } + + // Move text after cursor forward (including final null terminator) + for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - accCodepointSize] = text[i]; + + textLength -= accCodepointSize; + textBoxCursorIndex -= accCodepointSize; + } + + else if ((textBoxCursorIndex > 0) && (GUI_KEY_PRESSED(KEY_BACKSPACE) || (GUI_KEY_DOWN(KEY_BACKSPACE) && autoCursorShouldTrigger))) + { + // Delete single codepoint from text, before current cursor position + + int prevCodepointSize = 0; + + GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize); + + // Move text after cursor forward (including final null terminator) + for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - prevCodepointSize] = text[i]; + + textLength -= prevCodepointSize; + textBoxCursorIndex -= prevCodepointSize; + } + + // Move cursor position with keys + if ((textBoxCursorIndex > 0) && GUI_KEY_PRESSED(KEY_LEFT) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) + { + int offset = textBoxCursorIndex; + //int accCodepointSize = 0; + int prevCodepointSize = 0; + int prevCodepoint = 0; + + // Check whitespace to skip (ASCII only) + while (offset > 0) + { + prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); + if (!isspace(prevCodepoint & 0xff)) break; + + offset -= prevCodepointSize; + //accCodepointSize += prevCodepointSize; + } + + // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace) + // Not using isalnum() since it only works on ASCII characters + bool puctuation = ispunct(prevCodepoint & 0xff); + while (offset > 0) + { + prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); + if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break; + + offset -= prevCodepointSize; + //accCodepointSize += prevCodepointSize; + } + + textBoxCursorIndex = offset; + } + else if ((textBoxCursorIndex > 0) && (GUI_KEY_PRESSED(KEY_LEFT) || (GUI_KEY_DOWN(KEY_LEFT) && autoCursorShouldTrigger))) + { + int prevCodepointSize = 0; + GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize); + + textBoxCursorIndex -= prevCodepointSize; + } + else if ((textLength > textBoxCursorIndex) && GUI_KEY_PRESSED(KEY_RIGHT) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) + { + int offset = textBoxCursorIndex; + //int accCodepointSize = 0; + int nextCodepointSize; + int nextCodepoint; + + // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace) + // Not using isalnum() since it only works on ASCII characters + nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); + bool puctuation = ispunct(nextCodepoint & 0xff); + while (offset < textLength) + { + if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) break; + + offset += nextCodepointSize; + //accCodepointSize += nextCodepointSize; + nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); + } + + // Check whitespace to skip (ASCII only) + while (offset < textLength) + { + if (!isspace(nextCodepoint & 0xff)) break; + + offset += nextCodepointSize; + //accCodepointSize += nextCodepointSize; + nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); + } + + textBoxCursorIndex = offset; + } + else if ((textLength > textBoxCursorIndex) && (GUI_KEY_PRESSED(KEY_RIGHT) || (GUI_KEY_DOWN(KEY_RIGHT) && autoCursorShouldTrigger))) + { + int nextCodepointSize = 0; + GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize); + + textBoxCursorIndex += nextCodepointSize; + } + + // Move cursor position with mouse + if (CheckCollisionPointRec(mousePosition, textBounds)) // Mouse hover text + { + float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/(float)guiFont.baseSize; + int codepointIndex = 0; + float glyphWidth = 0.0f; + float widthToMouseX = 0; + int mouseCursorIndex = 0; + + for (int i = textIndexOffset; i < textLength; i += codepointSize) + { + codepoint = GetCodepointNext(&text[i], &codepointSize); + codepointIndex = GetGlyphIndex(guiFont, codepoint); + + if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor); + else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor); + + if (mousePosition.x <= (textBounds.x + (widthToMouseX + glyphWidth/2))) + { + mouseCursor.x = textBounds.x + widthToMouseX; + mouseCursorIndex = i; + break; + } + + widthToMouseX += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + } + + // Check if mouse cursor is at the last position + int textEndWidth = GuiGetTextWidth(text + textIndexOffset); + if (GUI_POINTER_POSITION.x >= (textBounds.x + textEndWidth - glyphWidth/2)) + { + mouseCursor.x = textBounds.x + textEndWidth; + mouseCursorIndex = textLength; + } + + // Place cursor at required index on mouse click + if ((mouseCursor.x >= 0) && GUI_BUTTON_PRESSED) + { + cursor.x = mouseCursor.x; + textBoxCursorIndex = mouseCursorIndex; + } + } + else mouseCursor.x = -1; + + // Recalculate cursor position.y depending on textBoxCursorIndex + cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING); + //if (multiline) cursor.y = GetTextLines() + + // Finish text editing on ENTER or mouse click outside bounds + if ((!multiline && GUI_KEY_PRESSED(KEY_ENTER)) || + (!CheckCollisionPointRec(mousePosition, bounds) && GUI_BUTTON_PRESSED)) + { + textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index + autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes + result = 1; + } + } + else + { + if (CheckCollisionPointRec(mousePosition, bounds)) + { + state = STATE_FOCUSED; + + if (GUI_BUTTON_PRESSED) + { + textBoxCursorIndex = textLength; // GLOBAL: Place cursor index to the end of current text + autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes + result = 1; + } + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state == STATE_PRESSED) + { + GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED))); + } + else if (state == STATE_DISABLED) + { + GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED))); + } + else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), BLANK); + + // Draw text considering index offset if required + // NOTE: Text index offset depends on cursor position + GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3)))); + + // Draw cursor + if (editMode && !GuiGetStyle(TEXTBOX, TEXT_READONLY)) + { + //if (autoCursorMode || ((blinkCursorFrameCounter/40)%2 == 0)) + GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED))); + + // Draw mouse position cursor (if required) + if (mouseCursor.x >= 0) GuiDrawRectangle(mouseCursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED))); + } + else if (state == STATE_FOCUSED) GuiTooltip(bounds); + //-------------------------------------------------------------------- + + return result; // Mouse button pressed: result = 1 +} + +/* +// Text Box control with multiple lines and word-wrap +// NOTE: This text-box is readonly, no editing supported by default +bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode) +{ + bool pressed = false; + + GuiSetStyle(TEXTBOX, TEXT_READONLY, 1); + GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_WORD); // WARNING: If wrap mode enabled, text editing is not supported + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_TOP); + + // TODO: Implement methods to calculate cursor position properly + pressed = GuiTextBox(bounds, text, textSize, editMode); + + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE); + GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_NONE); + GuiSetStyle(TEXTBOX, TEXT_READONLY, 0); + + return pressed; +} +*/ + +// Spinner control, returns selected value +int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode) +{ + int result = 1; + GuiState state = guiState; + + int tempValue = *value; + + Rectangle valueBoxBounds = { + bounds.x + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING), + bounds.y, + bounds.width - 2*(GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING)), bounds.height }; + Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height }; + Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.y, + (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height }; + + Rectangle textBounds = { 0 }; + if (text != NULL) + { + textBounds.width = (float)GuiGetTextWidth(text) + 2; + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING); + } + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + // Check spinner state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else state = STATE_FOCUSED; + } + } + +#if defined(RAYGUI_NO_ICONS) + if (GuiButton(leftButtonBound, "<")) tempValue--; + if (GuiButton(rightButtonBound, ">")) tempValue++; +#else + if (GuiButton(leftButtonBound, GuiIconText(ICON_ARROW_LEFT_FILL, NULL))) tempValue--; + if (GuiButton(rightButtonBound, GuiIconText(ICON_ARROW_RIGHT_FILL, NULL))) tempValue++; +#endif + + if (!editMode) + { + if (tempValue < minValue) tempValue = minValue; + if (tempValue > maxValue) tempValue = maxValue; + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + result = GuiValueBox(valueBoxBounds, NULL, &tempValue, minValue, maxValue, editMode); + + // Draw value selector custom buttons + // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values + int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); + int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(VALUEBOX, BORDER_WIDTH)); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign); + GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); + + // Draw text label if provided + GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + *value = tempValue; + return result; +} + +// Value Box control, updates input text with numbers +// NOTE: Requires static variables: frameCounter +int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode) +{ + #if !defined(RAYGUI_VALUEBOX_MAX_CHARS) + #define RAYGUI_VALUEBOX_MAX_CHARS 32 + #endif + + int result = 0; + GuiState state = guiState; + + char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = { 0 }; + snprintf(textValue, RAYGUI_VALUEBOX_MAX_CHARS + 1, "%i", *value); + + Rectangle textBounds = { 0 }; + if (text != NULL) + { + textBounds.width = (float)GuiGetTextWidth(text) + 2; + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING); + } + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + bool valueHasChanged = false; + + if (editMode) + { + state = STATE_PRESSED; + + int keyCount = (int)strlen(textValue); + + // Add or remove minus symbol + if (GUI_KEY_PRESSED(KEY_MINUS)) + { + if (textValue[0] == '-') + { + for (int i = 0 ; i < keyCount; i++) textValue[i] = textValue[i + 1]; + + keyCount--; + valueHasChanged = true; + } + else if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) + { + if (keyCount == 0) + { + textValue[0] = '0'; + textValue[1] = '\0'; + keyCount++; + } + + for (int i = keyCount ; i > -1; i--) textValue[i + 1] = textValue[i]; + + textValue[0] = '-'; + keyCount++; + valueHasChanged = true; + } + } + + // Add new digit to text value + if ((keyCount >= 0) && (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) && (GuiGetTextWidth(textValue) < bounds.width)) + { + int key = GUI_INPUT_KEY; + + // Only allow keys in range [48..57] + if ((key >= 48) && (key <= 57)) + { + textValue[keyCount] = (char)key; + keyCount++; + valueHasChanged = true; + } + } + + // Delete text + if ((keyCount > 0) && GUI_KEY_PRESSED(KEY_BACKSPACE)) + { + keyCount--; + textValue[keyCount] = '\0'; + valueHasChanged = true; + } + + if (valueHasChanged) *value = TextToInteger(textValue); + + // NOTE: Values are not clamped until user input finishes + //if (*value > maxValue) *value = maxValue; + //else if (*value < minValue) *value = minValue; + + if ((GUI_KEY_PRESSED(KEY_ENTER) || GUI_KEY_PRESSED(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && GUI_BUTTON_PRESSED)) + { + if (*value > maxValue) *value = maxValue; + else if (*value < minValue) *value = minValue; + + result = 1; + } + } + else + { + if (*value > maxValue) *value = maxValue; + else if (*value < minValue) *value = minValue; + + if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = STATE_FOCUSED; + if (GUI_BUTTON_PRESSED) result = 1; + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + Color baseColor = BLANK; + if (state == STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED)); + else if (state == STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED)); + + GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), baseColor); + GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3)))); + + // Draw cursor rectangle + if (editMode) + { + // NOTE: ValueBox internal text is always centered + Rectangle cursor = { bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1, + bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + 2, + 2, bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2 - 4 }; + if (cursor.height > bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2; + GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED))); + } + + // Draw text label if provided + GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + return result; +} + +// Floating point Value Box control, updates input val_str with numbers +// NOTE: Requires static variables: frameCounter +int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode) +{ + #if !defined(RAYGUI_VALUEBOX_MAX_CHARS) + #define RAYGUI_VALUEBOX_MAX_CHARS 32 + #endif + + int result = 0; + GuiState state = guiState; + + //char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0"; + //snprintf(textValue, sizeof(textValue), "%2.2f", *value); + + Rectangle textBounds = { 0 }; + if (text != NULL) + { + textBounds.width = (float)GuiGetTextWidth(text) + 2; + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING); + } + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + bool valueHasChanged = false; + + if (editMode) + { + state = STATE_PRESSED; + + int keyCount = (int)strlen(textValue); + + // Add or remove minus symbol + if (GUI_KEY_PRESSED(KEY_MINUS)) + { + if (textValue[0] == '-') + { + for (int i = 0; i < keyCount; i++) textValue[i] = textValue[i + 1]; + + keyCount--; + valueHasChanged = true; + } + else if (keyCount < (RAYGUI_VALUEBOX_MAX_CHARS - 1)) + { + if (keyCount == 0) + { + textValue[0] = '0'; + textValue[1] = '\0'; + keyCount++; + } + + for (int i = keyCount; i > -1; i--) textValue[i + 1] = textValue[i]; + + textValue[0] = '-'; + keyCount++; + valueHasChanged = true; + } + } + + // Only allow keys in range [48..57] + if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) + { + if (GuiGetTextWidth(textValue) < bounds.width) + { + int key = GUI_INPUT_KEY; + if (((key >= 48) && (key <= 57)) || + (key == '.') || + ((keyCount == 0) && (key == '+')) || // NOTE: Sign can only be in first position + ((keyCount == 0) && (key == '-'))) + { + textValue[keyCount] = (char)key; + keyCount++; + + valueHasChanged = true; + } + } + } + + // Pressed backspace + if (GUI_KEY_PRESSED(KEY_BACKSPACE)) + { + if (keyCount > 0) + { + keyCount--; + textValue[keyCount] = '\0'; + valueHasChanged = true; + } + } + + if (valueHasChanged) *value = TextToFloat(textValue); + + if ((GUI_KEY_PRESSED(KEY_ENTER) || GUI_KEY_PRESSED(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && GUI_BUTTON_PRESSED)) result = 1; + } + else + { + if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = STATE_FOCUSED; + if (GUI_BUTTON_PRESSED) result = 1; + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + Color baseColor = BLANK; + if (state == STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED)); + else if (state == STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED)); + + GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), baseColor); + GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3)))); + + // Draw cursor + if (editMode) + { + // NOTE: ValueBox internal text is always centered + Rectangle cursor = {bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1, + bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4, + bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH)}; + GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED))); + } + + // Draw text label if provided + GuiDrawText(text, textBounds, + (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, + GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + return result; +} + +// Slider control with pro parameters +// NOTE: Other GuiSlider*() controls use this one +int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue) +{ + int result = 0; + GuiState state = guiState; + + float temp = (maxValue - minValue)/2.0f; + if (value == NULL) value = &temp; + float oldValue = *value; + + int sliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH); + + Rectangle slider = { bounds.x, bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING), + 0, bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) }; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds + { + if (GUI_BUTTON_DOWN) + { + if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) + { + state = STATE_PRESSED; + // Get equivalent value and slider position from mousePosition.x + *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue; + } + } + else + { + guiControlExclusiveMode = false; + guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (GUI_BUTTON_DOWN) + { + state = STATE_PRESSED; + guiControlExclusiveMode = true; + guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts + + if (!CheckCollisionPointRec(mousePoint, slider)) + { + // Get equivalent value and slider position from mousePosition.x + *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue; + } + } + else state = STATE_FOCUSED; + } + + if (*value > maxValue) *value = maxValue; + else if (*value < minValue) *value = minValue; + } + + // Control value change check + if (oldValue == *value) result = 0; + else result = 1; + + // Slider bar limits check + float sliderValue = (((*value - minValue)/(maxValue - minValue))*(bounds.width - sliderWidth - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))); + if (sliderWidth > 0) // Slider + { + slider.x += sliderValue; + slider.width = (float)sliderWidth; + if (slider.x <= (bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH))) slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH); + else if ((slider.x + slider.width) >= (bounds.x + bounds.width)) slider.x = bounds.x + bounds.width - slider.width - GuiGetStyle(SLIDER, BORDER_WIDTH); + } + else if (sliderWidth == 0) // SliderBar + { + slider.x += GuiGetStyle(SLIDER, BORDER_WIDTH); + slider.width = sliderValue; + if (slider.width > bounds.width) slider.width = bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH); + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED))); + + // Draw slider internal bar (depends on state) + if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED))); + else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED))); + else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_PRESSED))); + else if (state == STATE_DISABLED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_DISABLED))); + + // Draw left/right text if provided + if (textLeft != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GuiGetTextWidth(textLeft); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + } + + if (textRight != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GuiGetTextWidth(textRight); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + } + //-------------------------------------------------------------------- + + return result; +} + +// Slider Bar control extended, returns selected value +int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue) +{ + int result = 0; + int preSliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH); + GuiSetStyle(SLIDER, SLIDER_WIDTH, 0); + result = GuiSlider(bounds, textLeft, textRight, value, minValue, maxValue); + GuiSetStyle(SLIDER, SLIDER_WIDTH, preSliderWidth); + + return result; +} + +// Progress Bar control extended, shows current progress value +int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue) +{ + int result = 0; + GuiState state = guiState; + + float temp = (maxValue - minValue)/2.0f; + if (value == NULL) value = &temp; + + // Progress bar + Rectangle progress = { bounds.x + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), + bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) + GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING), 0, + bounds.height - GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) -1 }; + + // Update control + //-------------------------------------------------------------------- + if (*value > maxValue) *value = maxValue; + + // WARNING: Working with floats could lead to rounding issues + if ((state != STATE_DISABLED)) progress.width = ((float)*value/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)); + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state == STATE_DISABLED) + { + GuiDrawRectangle(bounds, GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), GetColor(GuiGetStyle(PROGRESSBAR, BORDER + (state*3))), BLANK); + } + else + { + if (*value > minValue) + { + // Draw progress bar with colored border, more visual + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED))); + } + else GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); + + if (*value >= maxValue) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1}, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED))); + else + { + // Draw borders not yet reached by value + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y + bounds.height - 1, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); + } + + // Draw slider internal progress bar (depends on state) + GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED))); + } + + // Draw left/right text if provided + if (textLeft != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GuiGetTextWidth(textLeft); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x - textBounds.width - GuiGetStyle(PROGRESSBAR, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + } + + if (textRight != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GuiGetTextWidth(textRight); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(PROGRESSBAR, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + } + //-------------------------------------------------------------------- + + return result; +} + +// Status Bar control +int GuiStatusBar(Rectangle bounds, const char *text) +{ + int result = 0; + GuiState state = guiState; + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(STATUSBAR, BORDER_WIDTH), GetColor(GuiGetStyle(STATUSBAR, BORDER + (state*3))), GetColor(GuiGetStyle(STATUSBAR, BASE + (state*3)))); + GuiDrawText(text, GetTextBounds(STATUSBAR, bounds), GuiGetStyle(STATUSBAR, TEXT_ALIGNMENT), GetColor(GuiGetStyle(STATUSBAR, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + return result; +} + +// Dummy rectangle control, intended for placeholding +int GuiDummyRec(Rectangle bounds, const char *text) +{ + int result = 0; + GuiState state = guiState; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + // Check button state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else state = STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED))); + GuiDrawText(text, GetTextBounds(DEFAULT, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(BUTTON, (state != STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED))); + //------------------------------------------------------------------ + + return result; +} + +// List View control +int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active) +{ + int result = 0; + int itemCount = 0; + const char **items = NULL; + + if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL); + + result = GuiListViewEx(bounds, items, itemCount, scrollIndex, active, NULL); + + return result; +} + +// List View control with extended parameters +int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus) +{ + int result = 0; + GuiState state = guiState; + + int itemFocused = (focus == NULL)? -1 : *focus; + int itemSelected = (active == NULL)? -1 : *active; + + // Check if scroll bar is needed + bool useScrollBar = false; + if ((GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING))*count > bounds.height) useScrollBar = true; + + // Define base item rectangle [0] + Rectangle itemBounds = { 0 }; + itemBounds.x = bounds.x + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING); + itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) + GuiGetStyle(DEFAULT, BORDER_WIDTH); + itemBounds.width = bounds.width - 2*GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) - GuiGetStyle(DEFAULT, BORDER_WIDTH); + itemBounds.height = (float)GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT); + if (useScrollBar) itemBounds.width -= GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH); + + // Get items on the list + int visibleItems = (int)bounds.height/(GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING)); + if (visibleItems > count) visibleItems = count; + + int startIndex = (scrollIndex == NULL)? 0 : *scrollIndex; + if ((startIndex < 0) || (startIndex > (count - visibleItems))) startIndex = 0; + int endIndex = startIndex + visibleItems; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + // Check mouse inside list view + if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = STATE_FOCUSED; + + // Check focused and selected item + for (int i = 0; i < visibleItems; i++) + { + if (CheckCollisionPointRec(mousePoint, itemBounds)) + { + itemFocused = startIndex + i; + if (GUI_BUTTON_PRESSED) + { + if (itemSelected == (startIndex + i)) itemSelected = -1; + else itemSelected = startIndex + i; + } + break; + } + + // Update item rectangle y position for next item + itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING)); + } + + if (useScrollBar) + { + float scrollDelta = GUI_SCROLL_DELTA; + startIndex -= (int)scrollDelta; + + if (startIndex < 0) startIndex = 0; + else if (startIndex > (count - visibleItems)) startIndex = count - visibleItems; + + endIndex = startIndex + visibleItems; + if (endIndex > count) endIndex = count; + } + } + else itemFocused = -1; + + // Reset item rectangle y to [0] + itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) + GuiGetStyle(DEFAULT, BORDER_WIDTH); + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background + + // Draw visible items + for (int i = 0; ((i < visibleItems) && (text != NULL)); i++) + { + if (GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_NORMAL)) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), BLANK); + + if (state == STATE_DISABLED) + { + if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED))); + + GuiDrawText(text[startIndex + i], GetTextBounds(LISTVIEW, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED))); + } + else + { + if (((startIndex + i) == itemSelected) && (active != NULL)) + { + // Draw item selected + GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED))); + GuiDrawText(text[startIndex + i], GetTextBounds(LISTVIEW, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED))); + } + else if (((startIndex + i) == itemFocused)) // && (focus != NULL)) // NOTE: Items focused, despite not returned + { + // Draw item focused + GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED))); + GuiDrawText(text[startIndex + i], GetTextBounds(LISTVIEW, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED))); + } + else + { + // Draw item normal (no rectangle) + GuiDrawText(text[startIndex + i], GetTextBounds(LISTVIEW, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL))); + } + } + + // Update item rectangle y position for next item + itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING)); + } + + if (useScrollBar) + { + Rectangle scrollBarBounds = { + bounds.x + bounds.width - GuiGetStyle(LISTVIEW, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH), + bounds.y + GuiGetStyle(LISTVIEW, BORDER_WIDTH), (float)GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH), + bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) + }; + + // Calculate percentage of visible items and apply same percentage to scrollbar + float percentVisible = (float)(endIndex - startIndex)/count; + float sliderSize = bounds.height*percentVisible; + + int prevSliderSize = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE); // Save default slider size + int prevScrollSpeed = GuiGetStyle(SCROLLBAR, SCROLL_SPEED); // Save default scroll speed + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)sliderSize); // Change slider size + GuiSetStyle(SCROLLBAR, SCROLL_SPEED, count - visibleItems); // Change scroll speed + + startIndex = GuiScrollBar(scrollBarBounds, startIndex, 0, count - visibleItems); + + GuiSetStyle(SCROLLBAR, SCROLL_SPEED, prevScrollSpeed); // Reset scroll speed to default + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, prevSliderSize); // Reset slider size to default + } + //-------------------------------------------------------------------- + + if (active != NULL) *active = itemSelected; + if (focus != NULL) *focus = itemFocused; + if (scrollIndex != NULL) *scrollIndex = startIndex; + + return result; +} + +// Color Panel control - Color (RGBA) variant +int GuiColorPanel(Rectangle bounds, const char *text, Color *color) +{ + int result = 0; + + Vector3 vcolor = { (float)color->r/255.0f, (float)color->g/255.0f, (float)color->b/255.0f }; + Vector3 hsv = ConvertRGBtoHSV(vcolor); + Vector3 prevHsv = hsv; // workaround to see if GuiColorPanelHSV modifies the hsv + + GuiColorPanelHSV(bounds, text, &hsv); + + // Check if the hsv was changed, only then change the color + // This is required, because the Color->HSV->Color conversion has precision errors + // Thus the assignment from HSV to Color should only be made, if the HSV has a new user-entered value + // Otherwise GuiColorPanel would often modify it's color without user input + // TODO: GuiColorPanelHSV could return 1 if the slider was dragged, to simplify this check + if (hsv.x != prevHsv.x || hsv.y != prevHsv.y || hsv.z != prevHsv.z) + { + Vector3 rgb = ConvertHSVtoRGB(hsv); + + // NOTE: Vector3ToColor() only available on raylib 1.8.1 + *color = RAYGUI_CLITERAL(Color){ (unsigned char)(255.0f*rgb.x), + (unsigned char)(255.0f*rgb.y), + (unsigned char)(255.0f*rgb.z), + color->a }; + } + return result; +} + +// Color Bar Alpha control +// NOTE: Returns alpha value normalized [0..1] +int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) +{ + #if !defined(RAYGUI_COLORBARALPHA_CHECKED_SIZE) + #define RAYGUI_COLORBARALPHA_CHECKED_SIZE 10 + #endif + + int result = 0; + GuiState state = guiState; + Rectangle selector = { (float)bounds.x + (*alpha)*bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2, + (float)bounds.y - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), + (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT), + (float)bounds.height + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2 }; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds + { + if (GUI_BUTTON_DOWN) + { + if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) + { + state = STATE_PRESSED; + + *alpha = (mousePoint.x - bounds.x)/bounds.width; + if (*alpha <= 0.0f) *alpha = 0.0f; + if (*alpha >= 1.0f) *alpha = 1.0f; + } + } + else + { + guiControlExclusiveMode = false; + guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) + { + if (GUI_BUTTON_DOWN) + { + state = STATE_PRESSED; + guiControlExclusiveMode = true; + guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts + + *alpha = (mousePoint.x - bounds.x)/bounds.width; + if (*alpha <= 0.0f) *alpha = 0.0f; + if (*alpha >= 1.0f) *alpha = 1.0f; + //selector.x = bounds.x + (int)(((alpha - 0)/(100 - 0))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))) - selector.width/2; + } + else state = STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + // Draw alpha bar: checked background + if (state != STATE_DISABLED) + { + int checksX = (int)bounds.width/RAYGUI_COLORBARALPHA_CHECKED_SIZE; + int checksY = (int)bounds.height/RAYGUI_COLORBARALPHA_CHECKED_SIZE; + + for (int x = 0; x < checksX; x++) + { + for (int y = 0; y < checksY; y++) + { + Rectangle check = { bounds.x + x*RAYGUI_COLORBARALPHA_CHECKED_SIZE, bounds.y + y*RAYGUI_COLORBARALPHA_CHECKED_SIZE, RAYGUI_COLORBARALPHA_CHECKED_SIZE, RAYGUI_COLORBARALPHA_CHECKED_SIZE }; + GuiDrawRectangle(check, 0, BLANK, ((x + y)%2)? Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.4f) : Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.4f)); + } + } + + DrawRectangleGradientEx(bounds, RAYGUI_CLITERAL(Color){ 255, 255, 255, 0 }, RAYGUI_CLITERAL(Color){ 255, 255, 255, 0 }, Fade(RAYGUI_CLITERAL(Color){ 0, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 0, 255 }, guiAlpha)); + } + else DrawRectangleGradientEx(bounds, Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha)); + + GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); + + // Draw alpha bar: selector + GuiDrawRectangle(selector, 0, BLANK, GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3))); + //-------------------------------------------------------------------- + + return result; +} + +// Color Bar Hue control +// Returns hue value normalized [0..1] +// NOTE: Other similar bars (for reference): +// Color GuiColorBarSat() [WHITE->color] +// Color GuiColorBarValue() [BLACK->color], HSV/HSL +// float GuiColorBarLuminance() [BLACK->WHITE] +int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) +{ + int result = 0; + GuiState state = guiState; + Rectangle selector = { (float)bounds.x - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (float)bounds.y + (*hue)/360.0f*bounds.height - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2, (float)bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2, (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT) }; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds + { + if (GUI_BUTTON_DOWN) + { + if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) + { + state = STATE_PRESSED; + + *hue = (mousePoint.y - bounds.y)*360/bounds.height; + if (*hue <= 0.0f) *hue = 0.0f; + if (*hue >= 359.0f) *hue = 359.0f; + } + } + else + { + guiControlExclusiveMode = false; + guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) + { + if (GUI_BUTTON_DOWN) + { + state = STATE_PRESSED; + guiControlExclusiveMode = true; + guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts + + *hue = (mousePoint.y - bounds.y)*360/bounds.height; + if (*hue <= 0.0f) *hue = 0.0f; + if (*hue >= 359.0f) *hue = 359.0f; + + } + else state = STATE_FOCUSED; + + /*if (GUI_KEY_DOWN(KEY_UP)) + { + hue -= 2.0f; + if (hue <= 0.0f) hue = 0.0f; + } + else if (GUI_KEY_DOWN(KEY_DOWN)) + { + hue += 2.0f; + if (hue >= 360.0f) hue = 360.0f; + }*/ + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state != STATE_DISABLED) + { + // Draw hue bar:color bars + // TODO: Use directly DrawRectangleGradientEx(bounds, color1, color2, color2, color1); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + bounds.height/6), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 0, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 2*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 255, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 3*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 255, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 4*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 255, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 5*(bounds.height/6)), (int)bounds.width, (int)(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 0, 255 }, guiAlpha)); + } + else DrawRectangleGradientV((int)bounds.x, (int)bounds.y, (int)bounds.width, (int)bounds.height, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha)); + + GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); + + // Draw hue bar: selector + GuiDrawRectangle(selector, 0, BLANK, GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3))); + //-------------------------------------------------------------------- + + return result; +} + +// Color Picker control +// NOTE: It's divided in multiple controls: +// Color GuiColorPanel(Rectangle bounds, Color color) +// float GuiColorBarAlpha(Rectangle bounds, float alpha) +// float GuiColorBarHue(Rectangle bounds, float value) +// NOTE: bounds define GuiColorPanel() size +// NOTE: this picker converts RGB to HSV, which can cause the Hue control to jump. If you have this problem, consider using the HSV variant instead +int GuiColorPicker(Rectangle bounds, const char *text, Color *color) +{ + int result = 0; + + Color temp = { 200, 0, 0, 255 }; + if (color == NULL) color = &temp; + + GuiColorPanel(bounds, NULL, color); + + Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height }; + //Rectangle boundsAlpha = { bounds.x, bounds.y + bounds.height + GuiGetStyle(COLORPICKER, BARS_PADDING), bounds.width, GuiGetStyle(COLORPICKER, BARS_THICK) }; + + // NOTE: this conversion can cause low hue-resolution, if the r, g and b value are very similar, which causes the hue bar to shift around when only the GuiColorPanel is used + Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ (*color).r/255.0f, (*color).g/255.0f, (*color).b/255.0f }); + + GuiColorBarHue(boundsHue, NULL, &hsv.x); + + //color.a = (unsigned char)(GuiColorBarAlpha(boundsAlpha, (float)color.a/255.0f)*255.0f); + Vector3 rgb = ConvertHSVtoRGB(hsv); + + *color = RAYGUI_CLITERAL(Color){ (unsigned char)roundf(rgb.x*255.0f), (unsigned char)roundf(rgb.y*255.0f), (unsigned char)roundf(rgb.z*255.0f), (*color).a }; + + return result; +} + +// Color Picker control that avoids conversion to RGB and back to HSV on each call, thus avoiding jittering +// The user can call ConvertHSVtoRGB() to convert *colorHsv value to RGB +// NOTE: It's divided in multiple controls: +// int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) +// int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) +// float GuiColorBarHue(Rectangle bounds, float value) +// NOTE: bounds define GuiColorPanelHSV() size +int GuiColorPickerHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) +{ + int result = 0; + + Vector3 tempHsv = { 0 }; + + if (colorHsv == NULL) + { + const Vector3 tempColor = { 200.0f/255.0f, 0.0f, 0.0f }; + tempHsv = ConvertRGBtoHSV(tempColor); + colorHsv = &tempHsv; + } + + GuiColorPanelHSV(bounds, NULL, colorHsv); + + const Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height }; + + GuiColorBarHue(boundsHue, NULL, &colorHsv->x); + + return result; +} + +// Color Panel control - HSV variant +int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) +{ + int result = 0; + GuiState state = guiState; + Vector2 pickerSelector = { 0 }; + + const Color colWhite = { 255, 255, 255, 255 }; + const Color colBlack = { 0, 0, 0, 255 }; + + pickerSelector.x = bounds.x + (float)colorHsv->y*bounds.width; // HSV: Saturation + pickerSelector.y = bounds.y + (1.0f - (float)colorHsv->z)*bounds.height; // HSV: Value + + Vector3 maxHue = { colorHsv->x, 1.0f, 1.0f }; + Vector3 rgbHue = ConvertHSVtoRGB(maxHue); + Color maxHueCol = { (unsigned char)(255.0f*rgbHue.x), + (unsigned char)(255.0f*rgbHue.y), + (unsigned char)(255.0f*rgbHue.z), 255 }; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds + { + if (GUI_BUTTON_DOWN) + { + if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) + { + pickerSelector = mousePoint; + + if (pickerSelector.x < bounds.x) pickerSelector.x = bounds.x; + if (pickerSelector.x > bounds.x + bounds.width) pickerSelector.x = bounds.x + bounds.width; + if (pickerSelector.y < bounds.y) pickerSelector.y = bounds.y; + if (pickerSelector.y > bounds.y + bounds.height) pickerSelector.y = bounds.y + bounds.height; + + // Calculate color from picker + Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y }; + + colorPick.x /= (float)bounds.width; // Get normalized value on x + colorPick.y /= (float)bounds.height; // Get normalized value on y + + colorHsv->y = colorPick.x; + colorHsv->z = 1.0f - colorPick.y; + + } + } + else + { + guiControlExclusiveMode = false; + guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (GUI_BUTTON_DOWN) + { + state = STATE_PRESSED; + guiControlExclusiveMode = true; + guiControlExclusiveRec = bounds; + pickerSelector = mousePoint; + + // Calculate color from picker + Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y }; + + colorPick.x /= (float)bounds.width; // Get normalized value on x + colorPick.y /= (float)bounds.height; // Get normalized value on y + + colorHsv->y = colorPick.x; + colorHsv->z = 1.0f - colorPick.y; + } + else state = STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state != STATE_DISABLED) + { + DrawRectangleGradientEx(bounds, Fade(colWhite, guiAlpha), Fade(colWhite, guiAlpha), Fade(maxHueCol, guiAlpha), Fade(maxHueCol, guiAlpha)); + DrawRectangleGradientEx(bounds, Fade(colBlack, 0), Fade(colBlack, guiAlpha), Fade(colBlack, guiAlpha), Fade(colBlack, 0)); + + // Draw color picker: selector + Rectangle selector = { pickerSelector.x - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, pickerSelector.y - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE) }; + GuiDrawRectangle(selector, 0, BLANK, colWhite); + } + else + { + DrawRectangleGradientEx(bounds, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.6f), guiAlpha)); + } + + GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); + //-------------------------------------------------------------------- + + return result; +} + +// Message Box control +int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons) +{ + #if !defined(RAYGUI_MESSAGEBOX_BUTTON_HEIGHT) + #define RAYGUI_MESSAGEBOX_BUTTON_HEIGHT 24 + #endif + #if !defined(RAYGUI_MESSAGEBOX_BUTTON_PADDING) + #define RAYGUI_MESSAGEBOX_BUTTON_PADDING 12 + #endif + + int result = -1; // Returns clicked button from buttons list, 0 refers to closed window button + + int buttonCount = 0; + const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); + Rectangle buttonBounds = { 0 }; + buttonBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING; + buttonBounds.y = bounds.y + bounds.height - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT - RAYGUI_MESSAGEBOX_BUTTON_PADDING; + buttonBounds.width = (bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount; + buttonBounds.height = RAYGUI_MESSAGEBOX_BUTTON_HEIGHT; + + //int textWidth = GuiGetTextWidth(message) + 2; + + Rectangle textBounds = { 0 }; + textBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING; + textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + RAYGUI_MESSAGEBOX_BUTTON_PADDING; + textBounds.width = bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*2; + textBounds.height = bounds.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 3*RAYGUI_MESSAGEBOX_BUTTON_PADDING - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT; + + // Draw control + //-------------------------------------------------------------------- + if (GuiWindowBox(bounds, title)) result = 0; + + int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + GuiLabel(textBounds, message); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment); + + prevTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + + for (int i = 0; i < buttonCount; i++) + { + if (GuiButton(buttonBounds, buttonsText[i])) result = i + 1; + buttonBounds.x += (buttonBounds.width + RAYGUI_MESSAGEBOX_BUTTON_PADDING); + } + + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevTextAlignment); + //-------------------------------------------------------------------- + + return result; +} + +// Text Input Box control, ask for text +int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, bool *secretViewActive) +{ + #if !defined(RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT) + #define RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT 24 + #endif + #if !defined(RAYGUI_TEXTINPUTBOX_BUTTON_PADDING) + #define RAYGUI_TEXTINPUTBOX_BUTTON_PADDING 12 + #endif + #if !defined(RAYGUI_TEXTINPUTBOX_HEIGHT) + #define RAYGUI_TEXTINPUTBOX_HEIGHT 26 + #endif + + // Used to enable text edit mode + // WARNING: No more than one GuiTextInputBox() should be open at the same time + static bool textEditMode = false; + + int result = -1; + + int buttonCount = 0; + const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); + Rectangle buttonBounds = { 0 }; + buttonBounds.x = bounds.x + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; + buttonBounds.y = bounds.y + bounds.height - RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; + buttonBounds.width = (bounds.width - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount; + buttonBounds.height = RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT; + + int messageInputHeight = (int)bounds.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT - 2*RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; + + Rectangle textBounds = { 0 }; + if (message != NULL) + { + int textSize = GuiGetTextWidth(message) + 2; + + textBounds.x = bounds.x + bounds.width/2 - textSize/2; + textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + messageInputHeight/4 - (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + textBounds.width = (float)textSize; + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + } + + Rectangle textBoxBounds = { 0 }; + textBoxBounds.x = bounds.x + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; + textBoxBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - RAYGUI_TEXTINPUTBOX_HEIGHT/2; + if (message == NULL) textBoxBounds.y = bounds.y + 24 + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; + else textBoxBounds.y += (messageInputHeight/2 + messageInputHeight/4); + textBoxBounds.width = bounds.width - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING*2; + textBoxBounds.height = RAYGUI_TEXTINPUTBOX_HEIGHT; + + // Draw control + //-------------------------------------------------------------------- + if (GuiWindowBox(bounds, title)) result = 0; + + // Draw message if available + if (message != NULL) + { + int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + GuiLabel(textBounds, message); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment); + } + + int prevTextBoxAlignment = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT); + GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + + if (secretViewActive != NULL) + { + static char stars[] = "****************"; + if (GuiTextBox(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x, textBoxBounds.y, textBoxBounds.width - 4 - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.height }, + ((*secretViewActive == 1) || textEditMode)? text : stars, textMaxSize, textEditMode)) textEditMode = !textEditMode; + + GuiToggle(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x + textBoxBounds.width - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.y, RAYGUI_TEXTINPUTBOX_HEIGHT, RAYGUI_TEXTINPUTBOX_HEIGHT }, (*secretViewActive == 1)? "#44#" : "#45#", secretViewActive); + } + else + { + if (GuiTextBox(textBoxBounds, text, textMaxSize, textEditMode)) textEditMode = !textEditMode; + } + + GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, prevTextBoxAlignment); + + int prevBtnTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + + for (int i = 0; i < buttonCount; i++) + { + if (GuiButton(buttonBounds, buttonsText[i])) result = i + 1; + buttonBounds.x += (buttonBounds.width + RAYGUI_MESSAGEBOX_BUTTON_PADDING); + } + + if (result >= 0) textEditMode = false; + + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevBtnTextAlignment); + //-------------------------------------------------------------------- + + return result; // Result is the pressed button index +} + +// Grid control +// NOTE: Returns grid mouse-hover selected cell +// About drawing lines at subpixel spacing, simple put, not easy solution: +// REF: https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster +int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell) +{ + // Grid lines alpha amount + #if !defined(RAYGUI_GRID_ALPHA) + #define RAYGUI_GRID_ALPHA 0.15f + #endif + + int result = 0; + GuiState state = guiState; + + Vector2 mousePoint = GUI_POINTER_POSITION; + Vector2 currentMouseCell = { -1, -1 }; + + float spaceWidth = spacing/(float)subdivs; + int linesV = (int)(bounds.width/spaceWidth) + 1; + int linesH = (int)(bounds.height/spaceWidth) + 1; + + int color = GuiGetStyle(DEFAULT, LINE_COLOR); + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + if (CheckCollisionPointRec(mousePoint, bounds)) + { + // NOTE: Cell values must be the upper left of the cell the mouse is in + currentMouseCell.x = floorf((mousePoint.x - bounds.x)/spacing); + currentMouseCell.y = floorf((mousePoint.y - bounds.y)/spacing); + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state == STATE_DISABLED) color = GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED); + + if (subdivs > 0) + { + // Draw vertical grid lines + for (int i = 0; i < linesV; i++) + { + Rectangle lineV = { bounds.x + spacing*i/subdivs, bounds.y, 1, bounds.height + 1 }; + GuiDrawRectangle(lineV, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(color), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(color), RAYGUI_GRID_ALPHA)); + } + + // Draw horizontal grid lines + for (int i = 0; i < linesH; i++) + { + Rectangle lineH = { bounds.x, bounds.y + spacing*i/subdivs, bounds.width + 1, 1 }; + GuiDrawRectangle(lineH, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(color), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(color), RAYGUI_GRID_ALPHA)); + } + } + + if (mouseCell != NULL) *mouseCell = currentMouseCell; + return result; +} + +//---------------------------------------------------------------------------------- +// Tooltip management functions +// NOTE: Tooltips requires some global variables: tooltipPtr +//---------------------------------------------------------------------------------- +// Enable gui tooltips (global state) +void GuiEnableTooltip(void) { guiTooltip = true; } + +// Disable gui tooltips (global state) +void GuiDisableTooltip(void) { guiTooltip = false; } + +// Set tooltip string +void GuiSetTooltip(const char *tooltip) { guiTooltipPtr = tooltip; } + +//---------------------------------------------------------------------------------- +// Styles loading functions +//---------------------------------------------------------------------------------- + +// Load raygui style file (.rgs) +// NOTE: By default a binary file is expected, that file could contain a custom font, +// in that case, custom font image atlas is GRAY+ALPHA and pixel data can be compressed (DEFLATE) +void GuiLoadStyle(const char *fileName) +{ + #define MAX_LINE_BUFFER_SIZE 256 + + bool tryBinary = false; + if (!guiStyleLoaded) GuiLoadStyleDefault(); + + // Try reading the files as text file first + FILE *rgsFile = fopen(fileName, "rt"); + + if (rgsFile != NULL) + { + char buffer[MAX_LINE_BUFFER_SIZE] = { 0 }; + fgets(buffer, MAX_LINE_BUFFER_SIZE, rgsFile); + + if (buffer[0] == '#') + { + int controlId = 0; + int propertyId = 0; + unsigned int propertyValue = 0; + + while (!feof(rgsFile)) + { + switch (buffer[0]) + { + case 'p': + { + // Style property: p + + sscanf(buffer, "p %d %d 0x%x", &controlId, &propertyId, &propertyValue); + GuiSetStyle(controlId, propertyId, (int)propertyValue); + + } break; + case 'f': + { + // Style font: f + + int fontSize = 0; + char charmapFileName[256] = { 0 }; + char fontFileName[256] = { 0 }; + sscanf(buffer, "f %d %s %[^\r\n]s", &fontSize, charmapFileName, fontFileName); + + Font font = { 0 }; + int *codepoints = NULL; + int codepointCount = 0; + + if (charmapFileName[0] != '0') + { + // Load text data from file + // NOTE: Expected an UTF-8 array of codepoints, no separation + char *textData = LoadFileText(TextFormat("%s/%s", GetDirectoryPath(fileName), charmapFileName)); + codepoints = LoadCodepoints(textData, &codepointCount); + UnloadFileText(textData); + } + + if (fontFileName[0] != '\0') + { + // In case a font is already loaded and it is not default internal font, unload it + if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture); + + if (codepointCount > 0) font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, codepoints, codepointCount); + else font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, NULL, 0); // Default to 95 standard codepoints + } + + // If font texture not properly loaded, revert to default font and size/spacing + if (font.texture.id == 0) + { + font = GetFontDefault(); + GuiSetStyle(DEFAULT, TEXT_SIZE, 10); + GuiSetStyle(DEFAULT, TEXT_SPACING, 1); + } + + UnloadCodepoints(codepoints); + + if ((font.texture.id > 0) && (font.glyphCount > 0)) GuiSetFont(font); + + } break; + default: break; + } + + fgets(buffer, MAX_LINE_BUFFER_SIZE, rgsFile); + } + } + else tryBinary = true; + + fclose(rgsFile); + } + + if (tryBinary) + { + rgsFile = fopen(fileName, "rb"); + + if (rgsFile != NULL) + { + fseek(rgsFile, 0, SEEK_END); + int fileDataSize = ftell(rgsFile); + fseek(rgsFile, 0, SEEK_SET); + + if (fileDataSize > 0) + { + unsigned char *fileData = (unsigned char *)RAYGUI_CALLOC(fileDataSize, sizeof(unsigned char)); + if (fileData != NULL) + { + fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile); + + GuiLoadStyleFromMemory(fileData, fileDataSize); + + RAYGUI_FREE(fileData); + } + } + + fclose(rgsFile); + } + } +} + +// Load style default over global style +void GuiLoadStyleDefault(void) +{ + // Setting this flag first to avoid cyclic function calls + // when calling GuiSetStyle() and GuiGetStyle() + guiStyleLoaded = true; + + // Initialize default LIGHT style property values + // WARNING: Default value are applied to all controls on set but + // they can be overwritten later on for every custom control + GuiSetStyle(DEFAULT, BORDER_COLOR_NORMAL, 0x838383ff); + GuiSetStyle(DEFAULT, BASE_COLOR_NORMAL, 0xc9c9c9ff); + GuiSetStyle(DEFAULT, TEXT_COLOR_NORMAL, 0x686868ff); + GuiSetStyle(DEFAULT, BORDER_COLOR_FOCUSED, 0x5bb2d9ff); + GuiSetStyle(DEFAULT, BASE_COLOR_FOCUSED, 0xc9effeff); + GuiSetStyle(DEFAULT, TEXT_COLOR_FOCUSED, 0x6c9bbcff); + GuiSetStyle(DEFAULT, BORDER_COLOR_PRESSED, 0x0492c7ff); + GuiSetStyle(DEFAULT, BASE_COLOR_PRESSED, 0x97e8ffff); + GuiSetStyle(DEFAULT, TEXT_COLOR_PRESSED, 0x368bafff); + GuiSetStyle(DEFAULT, BORDER_COLOR_DISABLED, 0xb5c1c2ff); + GuiSetStyle(DEFAULT, BASE_COLOR_DISABLED, 0xe6e9e9ff); + GuiSetStyle(DEFAULT, TEXT_COLOR_DISABLED, 0xaeb7b8ff); + GuiSetStyle(DEFAULT, BORDER_WIDTH, 1); + GuiSetStyle(DEFAULT, TEXT_PADDING, 0); + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + + // Initialize default extended property values + // NOTE: By default, extended property values are initialized to 0 + GuiSetStyle(DEFAULT, TEXT_SIZE, 10); // DEFAULT, shared by all controls + GuiSetStyle(DEFAULT, TEXT_SPACING, 1); // DEFAULT, shared by all controls + GuiSetStyle(DEFAULT, LINE_COLOR, 0x90abb5ff); // DEFAULT specific property + GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0xf5f5f5ff); // DEFAULT specific property + GuiSetStyle(DEFAULT, TEXT_LINE_SPACING, 5); // DEFAULT, pixels between lines, from bottom of first line to top of second + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE); // DEFAULT, text aligned vertically to middle of text-bounds + + // Initialize control-specific property values + // NOTE: Those properties are in default list but require specific values by control type + GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + GuiSetStyle(BUTTON, BORDER_WIDTH, 2); + GuiSetStyle(SLIDER, TEXT_PADDING, 4); + GuiSetStyle(PROGRESSBAR, TEXT_PADDING, 4); + GuiSetStyle(CHECKBOX, TEXT_PADDING, 4); + GuiSetStyle(CHECKBOX, TEXT_ALIGNMENT, TEXT_ALIGN_RIGHT); + GuiSetStyle(DROPDOWNBOX, TEXT_PADDING, 0); + GuiSetStyle(DROPDOWNBOX, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + GuiSetStyle(TEXTBOX, TEXT_PADDING, 4); + GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + GuiSetStyle(VALUEBOX, TEXT_PADDING, 0); + GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + GuiSetStyle(STATUSBAR, TEXT_PADDING, 8); + GuiSetStyle(STATUSBAR, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + + // Initialize extended property values + // NOTE: By default, extended property values are initialized to 0 + GuiSetStyle(TOGGLE, GROUP_PADDING, 2); + GuiSetStyle(SLIDER, SLIDER_WIDTH, 16); + GuiSetStyle(SLIDER, SLIDER_PADDING, 1); + GuiSetStyle(PROGRESSBAR, PROGRESS_PADDING, 1); + GuiSetStyle(CHECKBOX, CHECK_PADDING, 1); + GuiSetStyle(COMBOBOX, COMBO_BUTTON_WIDTH, 32); + GuiSetStyle(COMBOBOX, COMBO_BUTTON_SPACING, 2); + GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16); + GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 2); + GuiSetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH, 24); + GuiSetStyle(VALUEBOX, SPINNER_BUTTON_SPACING, 2); + GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0); + GuiSetStyle(SCROLLBAR, ARROWS_VISIBLE, 0); + GuiSetStyle(SCROLLBAR, ARROWS_SIZE, 6); + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING, 0); + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, 16); + GuiSetStyle(SCROLLBAR, SCROLL_PADDING, 0); + GuiSetStyle(SCROLLBAR, SCROLL_SPEED, 12); + GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 28); + GuiSetStyle(LISTVIEW, LIST_ITEMS_SPACING, 2); + GuiSetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH, 1); + GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 12); + GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE); + GuiSetStyle(COLORPICKER, COLOR_SELECTOR_SIZE, 8); + GuiSetStyle(COLORPICKER, HUEBAR_WIDTH, 16); + GuiSetStyle(COLORPICKER, HUEBAR_PADDING, 8); + GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT, 8); + GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW, 2); + + if (guiFont.texture.id != GetFontDefault().texture.id) + { + // Unload previous font texture + UnloadTexture(guiFont.texture); + RAYGUI_FREE(guiFont.recs); + RAYGUI_FREE(guiFont.glyphs); + guiFont.recs = NULL; + guiFont.glyphs = NULL; + + // Setup default raylib font + guiFont = GetFontDefault(); + + // NOTE: Default raylib font character 95 is a white square + Rectangle whiteChar = guiFont.recs[95]; + + // NOTE: Setting up a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering + SetShapesTexture(guiFont.texture, RAYGUI_CLITERAL(Rectangle){ whiteChar.x + 1, whiteChar.y + 1, whiteChar.width - 2, whiteChar.height - 2 }); + } +} + +// Get text with icon id prepended +// NOTE: Useful to add icons by name id (enum) instead of +// a number that can change between ricon versions +const char *GuiIconText(int iconId, const char *text) +{ +#if defined(RAYGUI_NO_ICONS) + return NULL; +#else + static char buffer[1024] = { 0 }; + static char iconBuffer[16] = { 0 }; + + if (text != NULL) + { + memset(buffer, 0, 1024); + snprintf(buffer, 1024, "#%03i#", iconId); + + for (int i = 5; i < 1024; i++) + { + buffer[i] = text[i - 5]; + if (text[i - 5] == '\0') break; + } + + return buffer; + } + else + { + snprintf(iconBuffer, 16, "#%03i#", iconId); + + return iconBuffer; + } +#endif +} + +#if !defined(RAYGUI_NO_ICONS) +// Get full icons data pointer +unsigned int *GuiGetIcons(void) { return guiIconsPtr; } + +// Load raygui icons file (.rgi) +// NOTE: In case nameIds are required, they can be requested with loadIconsName, +// they are returned as a guiIconsName[iconCount][RAYGUI_ICON_MAX_NAME_LENGTH], +// WARNING: guiIconsName[]][] memory should be manually freed! +char **GuiLoadIcons(const char *fileName, bool loadIconsName) +{ + // Style File Structure (.rgi) + // ------------------------------------------------------ + // Offset | Size | Type | Description + // ------------------------------------------------------ + // 0 | 4 | char | Signature: "rGI " + // 4 | 2 | short | Version: 100 + // 6 | 2 | short | reserved + + // 8 | 2 | short | Num icons (N) + // 10 | 2 | short | Icons size (Options: 16, 32, 64) (S) + + // Icons name id (32 bytes per name id) + // foreach (icon) + // { + // 12+32*i | 32 | char | Icon NameId + // } + + // Icons data: One bit per pixel, stored as unsigned int array (depends on icon size) + // S*S pixels/32bit per unsigned int = K unsigned int per icon + // foreach (icon) + // { + // ... | K | unsigned int | Icon Data + // } + + FILE *rgiFile = fopen(fileName, "rb"); + + char **guiIconsName = NULL; + + if (rgiFile != NULL) + { + char signature[5] = { 0 }; + short version = 0; + short reserved = 0; + short iconCount = 0; + short iconSize = 0; + + fread(signature, 1, 4, rgiFile); + fread(&version, sizeof(short), 1, rgiFile); + fread(&reserved, sizeof(short), 1, rgiFile); + fread(&iconCount, sizeof(short), 1, rgiFile); + fread(&iconSize, sizeof(short), 1, rgiFile); + + if ((signature[0] == 'r') && + (signature[1] == 'G') && + (signature[2] == 'I') && + (signature[3] == ' ')) + { + if (loadIconsName) + { + guiIconsName = (char **)RAYGUI_CALLOC(iconCount, sizeof(char *)); + for (int i = 0; i < iconCount; i++) + { + guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char)); + fread(guiIconsName[i], 1, RAYGUI_ICON_MAX_NAME_LENGTH, rgiFile); + } + } + else fseek(rgiFile, iconCount*RAYGUI_ICON_MAX_NAME_LENGTH, SEEK_CUR); + + // Read icons data directly over internal icons array + fread(guiIconsPtr, sizeof(unsigned int), (int)iconCount*((int)iconSize*(int)iconSize/32), rgiFile); + } + + fclose(rgiFile); + } + + return guiIconsName; +} + +// Load icons from memory +// WARNING: Binary files only +char **GuiLoadIconsFromMemory(const unsigned char *fileData, int dataSize, bool loadIconsName) +{ + unsigned char *fileDataPtr = (unsigned char *)fileData; + char **guiIconsName = NULL; + + char signature[5] = { 0 }; + short version = 0; + short reserved = 0; + short iconCount = 0; + short iconSize = 0; + + memcpy(signature, fileDataPtr, 4); + memcpy(&version, fileDataPtr + 4, sizeof(short)); + memcpy(&reserved, fileDataPtr + 4 + 2, sizeof(short)); + memcpy(&iconCount, fileDataPtr + 4 + 2 + 2, sizeof(short)); + memcpy(&iconSize, fileDataPtr + 4 + 2 + 2 + 2, sizeof(short)); + fileDataPtr += 12; + + if ((signature[0] == 'r') && + (signature[1] == 'G') && + (signature[2] == 'I') && + (signature[3] == ' ')) + { + if (loadIconsName) + { + guiIconsName = (char **)RAYGUI_CALLOC(iconCount, sizeof(char *)); + for (int i = 0; i < iconCount; i++) + { + guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char)); + memcpy(guiIconsName[i], fileDataPtr, RAYGUI_ICON_MAX_NAME_LENGTH); + fileDataPtr += RAYGUI_ICON_MAX_NAME_LENGTH; + } + } + else + { + // Skip icon name data if not required + fileDataPtr += iconCount*RAYGUI_ICON_MAX_NAME_LENGTH; + } + + int iconDataSize = iconCount*((int)iconSize*(int)iconSize/32)*(int)sizeof(unsigned int); + guiIconsPtr = (unsigned int *)RAYGUI_CALLOC(iconDataSize, 1); + + memcpy(guiIconsPtr, fileDataPtr, iconDataSize); + } + + return guiIconsName; +} + +// Draw selected icon using rectangles pixel-by-pixel +void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color) +{ + #define BIT_CHECK(a,b) ((a) & (1u<<(b))) + + for (int i = 0, y = 0; i < RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32; i++) + { + for (int k = 0; k < 32; k++) + { + if (BIT_CHECK(guiIconsPtr[iconId*RAYGUI_ICON_DATA_ELEMENTS + i], k)) + { + #if !defined(RAYGUI_STANDALONE) + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ (float)posX + (k%RAYGUI_ICON_SIZE)*pixelSize, (float)posY + y*pixelSize, (float)pixelSize, (float)pixelSize }, 0, BLANK, color); + #endif + } + + if ((k == 15) || (k == 31)) y++; + } + } +} + +// Set icon drawing size +void GuiSetIconScale(int scale) +{ + if (scale >= 1) guiIconScale = scale; +} + +// Get text width considering gui style and icon size (if required) +int GuiGetTextWidth(const char *text) +{ + #if !defined(ICON_TEXT_PADDING) + #define ICON_TEXT_PADDING 4 + #endif + + Vector2 textSize = { 0 }; + int textIconOffset = 0; + + if ((text != NULL) && (text[0] != '\0')) + { + if (text[0] == '#') + { + for (int i = 1; (i < 5) && (text[i] != '\0'); i++) + { + if (text[i] == '#') + { + textIconOffset = i; + break; + } + } + } + + text += textIconOffset; + + // Make sure guiFont is set, GuiGetStyle() initializes it lazynessly + float fontSize = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + + // Custom MeasureText() implementation + if ((guiFont.texture.id > 0) && (text != NULL)) + { + // Get size in bytes of text, considering end of line and line break + int size = 0; + for (int i = 0; i < MAX_LINE_BUFFER_SIZE; i++) + { + if ((text[i] != '\0') && (text[i] != '\n')) size++; + else break; + } + + float scaleFactor = fontSize/(float)guiFont.baseSize; + textSize.y = (float)guiFont.baseSize*scaleFactor; + float glyphWidth = 0.0f; + + for (int i = 0, codepointSize = 0; i < size; i += codepointSize) + { + int codepoint = GetCodepointNext(&text[i], &codepointSize); + int codepointIndex = GetGlyphIndex(guiFont, codepoint); + + if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor); + else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor); + + textSize.x += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + } + } + + if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE + ICON_TEXT_PADDING); + } + + return (int)textSize.x; +} + +#endif // !RAYGUI_NO_ICONS + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- +// Load style from memory +// WARNING: Binary files only +static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize) +{ + unsigned char *fileDataPtr = (unsigned char *)fileData; + + char signature[5] = { 0 }; + short version = 0; + short reserved = 0; + int propertyCount = 0; + + memcpy(signature, fileDataPtr, 4); + memcpy(&version, fileDataPtr + 4, sizeof(short)); + memcpy(&reserved, fileDataPtr + 4 + 2, sizeof(short)); + memcpy(&propertyCount, fileDataPtr + 4 + 2 + 2, sizeof(int)); + fileDataPtr += 12; + + if ((signature[0] == 'r') && + (signature[1] == 'G') && + (signature[2] == 'S') && + (signature[3] == ' ')) + { + short controlId = 0; + short propertyId = 0; + unsigned int propertyValue = 0; + + for (int i = 0; i < propertyCount; i++) + { + memcpy(&controlId, fileDataPtr, sizeof(short)); + memcpy(&propertyId, fileDataPtr + 2, sizeof(short)); + memcpy(&propertyValue, fileDataPtr + 2 + 2, sizeof(unsigned int)); + fileDataPtr += 8; + + if (controlId == 0) // DEFAULT control + { + // If a DEFAULT property is loaded, it is propagated to all controls + // NOTE: All DEFAULT properties should be defined first in the file + GuiSetStyle(0, (int)propertyId, propertyValue); + + if (propertyId < RAYGUI_MAX_PROPS_BASE) for (int j = 1; j < RAYGUI_MAX_CONTROLS; j++) GuiSetStyle(j, (int)propertyId, propertyValue); + } + else GuiSetStyle((int)controlId, (int)propertyId, propertyValue); + } + + // Font loading is highly dependant on raylib API to load font data and image + +#if !defined(RAYGUI_STANDALONE) + // Load custom font if available + int fontDataSize = 0; + memcpy(&fontDataSize, fileDataPtr, sizeof(int)); + fileDataPtr += 4; + + if (fontDataSize > 0) + { + Font font = { 0 }; + int fontType = 0; // 0-Normal, 1-SDF + + memcpy(&font.baseSize, fileDataPtr, sizeof(int)); + memcpy(&font.glyphCount, fileDataPtr + 4, sizeof(int)); + memcpy(&fontType, fileDataPtr + 4 + 4, sizeof(int)); + fileDataPtr += 12; + + // Load font white rectangle + Rectangle fontWhiteRec = { 0 }; + memcpy(&fontWhiteRec, fileDataPtr, sizeof(Rectangle)); + fileDataPtr += 16; + + // Load font image parameters + int fontImageUncompSize = 0; + int fontImageCompSize = 0; + memcpy(&fontImageUncompSize, fileDataPtr, sizeof(int)); + memcpy(&fontImageCompSize, fileDataPtr + 4, sizeof(int)); + fileDataPtr += 8; + + Image imFont = { 0 }; + imFont.mipmaps = 1; + memcpy(&imFont.width, fileDataPtr, sizeof(int)); + memcpy(&imFont.height, fileDataPtr + 4, sizeof(int)); + memcpy(&imFont.format, fileDataPtr + 4 + 4, sizeof(int)); + fileDataPtr += 12; + + if ((fontImageCompSize > 0) && (fontImageCompSize != fontImageUncompSize)) + { + // Compressed font atlas image data (DEFLATE), it requires DecompressData() + int dataUncompSize = 0; + unsigned char *compData = (unsigned char *)RAYGUI_CALLOC(fontImageCompSize, sizeof(unsigned char)); + memcpy(compData, fileDataPtr, fontImageCompSize); + fileDataPtr += fontImageCompSize; + + imFont.data = DecompressData(compData, fontImageCompSize, &dataUncompSize); + + // Security check, dataUncompSize must match the provided fontImageUncompSize + if (dataUncompSize != fontImageUncompSize) RAYGUI_LOG("WARNING: Uncompressed font atlas image data could be corrupted"); + + RAYGUI_FREE(compData); + } + else + { + // Font atlas image data is not compressed + imFont.data = (unsigned char *)RAYGUI_CALLOC(fontImageUncompSize, sizeof(unsigned char)); + memcpy(imFont.data, fileDataPtr, fontImageUncompSize); + fileDataPtr += fontImageUncompSize; + } + + if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture); + font.texture = LoadTextureFromImage(imFont); + + RAYGUI_FREE(imFont.data); + + // Validate font atlas texture was loaded correctly + if (font.texture.id != 0) + { + // Load font recs data + int recsDataSize = font.glyphCount*sizeof(Rectangle); + int recsDataCompressedSize = 0; + + // WARNING: Version 400 adds the compression size parameter + if (version >= 400) + { + // RGS files version 400 support compressed recs data + memcpy(&recsDataCompressedSize, fileDataPtr, sizeof(int)); + fileDataPtr += sizeof(int); + } + + if ((recsDataCompressedSize > 0) && (recsDataCompressedSize != recsDataSize)) + { + // Recs data is compressed, uncompress it + unsigned char *recsDataCompressed = (unsigned char *)RAYGUI_CALLOC(recsDataCompressedSize, sizeof(unsigned char)); + + memcpy(recsDataCompressed, fileDataPtr, recsDataCompressedSize); + fileDataPtr += recsDataCompressedSize; + + int recsDataUncompSize = 0; + font.recs = (Rectangle *)DecompressData(recsDataCompressed, recsDataCompressedSize, &recsDataUncompSize); + + // Security check, data uncompressed size must match the expected original data size + if (recsDataUncompSize != recsDataSize) RAYGUI_LOG("WARNING: Uncompressed font recs data could be corrupted"); + + RAYGUI_FREE(recsDataCompressed); + } + else + { + // Recs data is uncompressed + font.recs = (Rectangle *)RAYGUI_CALLOC(font.glyphCount, sizeof(Rectangle)); + for (int i = 0; i < font.glyphCount; i++) + { + memcpy(&font.recs[i], fileDataPtr, sizeof(Rectangle)); + fileDataPtr += sizeof(Rectangle); + } + } + + // Load font glyphs info data + int glyphsDataSize = font.glyphCount*16; // 16 bytes data per glyph + int glyphsDataCompressedSize = 0; + + // WARNING: Version 400 adds the compression size parameter + if (version >= 400) + { + // RGS files version 400 support compressed glyphs data + memcpy(&glyphsDataCompressedSize, fileDataPtr, sizeof(int)); + fileDataPtr += sizeof(int); + } + + // Allocate required glyphs space to fill with data + font.glyphs = (GlyphInfo *)RAYGUI_CALLOC(font.glyphCount, sizeof(GlyphInfo)); + + if ((glyphsDataCompressedSize > 0) && (glyphsDataCompressedSize != glyphsDataSize)) + { + // Glyphs data is compressed, uncompress it + unsigned char *glypsDataCompressed = (unsigned char *)RAYGUI_CALLOC(glyphsDataCompressedSize, sizeof(unsigned char)); + + memcpy(glypsDataCompressed, fileDataPtr, glyphsDataCompressedSize); + fileDataPtr += glyphsDataCompressedSize; + + int glyphsDataUncompSize = 0; + unsigned char *glyphsDataUncomp = DecompressData(glypsDataCompressed, glyphsDataCompressedSize, &glyphsDataUncompSize); + + // Security check, data uncompressed size must match the expected original data size + if (glyphsDataUncompSize != glyphsDataSize) RAYGUI_LOG("WARNING: Uncompressed font glyphs data could be corrupted"); + + unsigned char *glyphsDataUncompPtr = glyphsDataUncomp; + + for (int i = 0; i < font.glyphCount; i++) + { + memcpy(&font.glyphs[i].value, glyphsDataUncompPtr, sizeof(int)); + memcpy(&font.glyphs[i].offsetX, glyphsDataUncompPtr + 4, sizeof(int)); + memcpy(&font.glyphs[i].offsetY, glyphsDataUncompPtr + 8, sizeof(int)); + memcpy(&font.glyphs[i].advanceX, glyphsDataUncompPtr + 12, sizeof(int)); + glyphsDataUncompPtr += 16; + } + + RAYGUI_FREE(glypsDataCompressed); + RAYGUI_FREE(glyphsDataUncomp); + } + else + { + // Glyphs data is uncompressed + for (int i = 0; i < font.glyphCount; i++) + { + memcpy(&font.glyphs[i].value, fileDataPtr, sizeof(int)); + memcpy(&font.glyphs[i].offsetX, fileDataPtr + 4, sizeof(int)); + memcpy(&font.glyphs[i].offsetY, fileDataPtr + 8, sizeof(int)); + memcpy(&font.glyphs[i].advanceX, fileDataPtr + 12, sizeof(int)); + fileDataPtr += 16; + } + } + } + else font = GetFontDefault(); // Fallback in case of errors loading font atlas texture + + GuiSetFont(font); + + // Set font texture source rectangle to be used as white texture to draw shapes + // NOTE: It makes possible to draw shapes and text (full UI) in a single draw call + if ((fontWhiteRec.x > 0) && + (fontWhiteRec.y > 0) && + (fontWhiteRec.width > 0) && + (fontWhiteRec.height > 0)) SetShapesTexture(font.texture, fontWhiteRec); + } +#endif + } +} + +// Get text bounds considering control bounds +static Rectangle GetTextBounds(int control, Rectangle bounds) +{ + Rectangle textBounds = bounds; + + textBounds.x = bounds.x + GuiGetStyle(control, BORDER_WIDTH); + textBounds.y = bounds.y + GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, TEXT_PADDING); + textBounds.width = bounds.width - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING); + textBounds.height = bounds.height - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING); // NOTE: Text is processed line per line! + + // Depending on control, TEXT_PADDING and TEXT_ALIGNMENT properties could affect the text-bounds + switch (control) + { + case COMBOBOX: + case DROPDOWNBOX: + case LISTVIEW: + // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW + case SLIDER: + case CHECKBOX: + case VALUEBOX: + case CONTROL11: + // TODO: More special cases (label on side): SLIDER, CHECKBOX, VALUEBOX, SPINNER + default: + { + // TODO: WARNING: TEXT_ALIGNMENT is already considered in GuiDrawText() + if (GuiGetStyle(control, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT) textBounds.x -= GuiGetStyle(control, TEXT_PADDING); + else textBounds.x += GuiGetStyle(control, TEXT_PADDING); + } + break; + } + + return textBounds; +} + +// Get text icon if provided and move text cursor +// NOTE: Up to #999# values supported for iconId +static const char *GetTextIcon(const char *text, int *iconId) +{ +#if !defined(RAYGUI_NO_ICONS) + *iconId = -1; + if (text[0] == '#') // Maybe it is stars with an icon, ending # must be found + { + char iconValue[4] = { 0 }; // Maximum length for icon value: 3 digits + '\0' + + int pos = 1; + while ((pos < 4) && (text[pos] >= '0') && (text[pos] <= '9')) + { + iconValue[pos - 1] = text[pos]; + pos++; + } + + if (text[pos] == '#') + { + *iconId = TextToInteger(iconValue); + + // Move text pointer after icon + // WARNING: If only icon provided, it could point to EOL character: '\0' + if (*iconId >= 0) text += (pos + 1); + } + } +#endif + + return text; +} + +// Get text divided into lines (by line-breaks '\n') +// WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator! +static const char **GetTextLines(const char *text, int *count) +{ + #define RAYGUI_MAX_TEXT_LINES 128 + + static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; + for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings + + int textLength = (int)strlen(text); + + lines[0] = text; + *count = 1; + + for (int i = 0, k = 0; (i < textLength) && (*count < RAYGUI_MAX_TEXT_LINES); i++) + { + if (text[i] == '\n') + { + k++; + lines[k] = &text[i + 1]; // WARNING: next value is valid? + *count += 1; + } + } + + return lines; +} + +// Get text width to next space for provided string +static float GetNextSpaceWidth(const char *text, int *nextSpaceIndex) +{ + float width = 0; + int codepointByteCount = 0; + int codepoint = 0; + int index = 0; + float glyphWidth = 0; + float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize; + + for (int i = 0; text[i] != '\0'; i++) + { + if (text[i] != ' ') + { + codepoint = GetCodepoint(&text[i], &codepointByteCount); + index = GetGlyphIndex(guiFont, codepoint); + glyphWidth = (guiFont.glyphs[index].advanceX == 0)? guiFont.recs[index].width*scaleFactor : guiFont.glyphs[index].advanceX*scaleFactor; + width += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + } + else + { + *nextSpaceIndex = i; + break; + } + } + + return width; +} + +// Gui draw text using default font +static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, Color tint) +{ + #define TEXT_VALIGN_PIXEL_OFFSET(h) ((int)h%2) // Vertical alignment for pixel perfect + + #if !defined(ICON_TEXT_PADDING) + #define ICON_TEXT_PADDING 4 + #endif + + if ((text == NULL) || (text[0] == '\0')) return; // Security check + + // PROCEDURE: + // - Text is processed line per line + // - For every line, horizontal alignment is defined + // - For all text, vertical alignment is defined (multiline text only) + // - For every line, wordwrap mode is checked (useful for GuitextBox(), read-only) + + // Get text lines (using '\n' as delimiter) to be processed individually + // WARNING: GuiTextSplit() function can't be used now because it can have already been used + // before the GuiDrawText() call and its buffer is static, it would be overriden :( + int lineCount = 0; + const char **lines = GetTextLines(text, &lineCount); + + // Text style variables + //int alignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT); + int alignmentVertical = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL); + int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); // Wrap-mode only available in read-only mode, no for text editing + + // TODO: WARNING: This totalHeight is not valid for vertical alignment in case of word-wrap + float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); + float posOffsetY = 0.0f; + + for (int i = 0; i < lineCount; i++) + { + int iconId = 0; + lines[i] = GetTextIcon(lines[i], &iconId); // Check text for icon and move cursor + + // Get text position depending on alignment and iconId + //--------------------------------------------------------------------------------- + Vector2 textBoundsPosition = { textBounds.x, textBounds.y }; + float textBoundsWidthOffset = 0.0f; + + // NOTE: Get text size after icon has been processed + // WARNING: GuiGetTextWidth() also processes text icon to get width! -> Really needed? + int textSizeX = GuiGetTextWidth(lines[i]); + + // If text requires an icon, add size to measure + if (iconId >= 0) + { + textSizeX += RAYGUI_ICON_SIZE*guiIconScale; + + // WARNING: If only icon provided, text could be pointing to EOF character: '\0' +#if !defined(RAYGUI_NO_ICONS) + if ((lines[i] != NULL) && (lines[i][0] != '\0')) textSizeX += ICON_TEXT_PADDING; +#endif + } + + // Check guiTextAlign global variables + switch (alignment) + { + case TEXT_ALIGN_LEFT: textBoundsPosition.x = textBounds.x; break; + case TEXT_ALIGN_CENTER: textBoundsPosition.x = textBounds.x + textBounds.width/2 - textSizeX/2; break; + case TEXT_ALIGN_RIGHT: textBoundsPosition.x = textBounds.x + textBounds.width - textSizeX; break; + default: break; + } + + if (textSizeX > textBounds.width && (lines[i] != NULL) && (lines[i][0] != '\0')) textBoundsPosition.x = textBounds.x; + + switch (alignmentVertical) + { + // Only valid in case of wordWrap = 0; + case TEXT_ALIGN_TOP: textBoundsPosition.y = textBounds.y + posOffsetY; break; + case TEXT_ALIGN_MIDDLE: textBoundsPosition.y = textBounds.y + posOffsetY + textBounds.height/2 - totalHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height); break; + case TEXT_ALIGN_BOTTOM: textBoundsPosition.y = textBounds.y + posOffsetY + textBounds.height - totalHeight + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height); break; + default: break; + } + + // NOTE: Make sure getting pixel-perfect coordinates, + // In case of decimals, it could result in text positioning artifacts + textBoundsPosition.x = (float)((int)textBoundsPosition.x); + textBoundsPosition.y = (float)((int)textBoundsPosition.y); + //--------------------------------------------------------------------------------- + + // Draw text (with icon if available) + //--------------------------------------------------------------------------------- +#if !defined(RAYGUI_NO_ICONS) + if (iconId >= 0) + { + // NOTE: Considering icon height, probably different than text size + GuiDrawIcon(iconId, (int)textBoundsPosition.x, (int)(textBounds.y + textBounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height)), guiIconScale, tint); + textBoundsPosition.x += (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); + textBoundsWidthOffset = (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); + } +#endif + // Get size in bytes of text, + // considering end of line and line break + int lineSize = 0; + for (int c = 0; (lines[i][c] != '\0') && (lines[i][c] != '\n') && (lines[i][c] != '\r'); c++, lineSize++){ } + float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize; + + int lastSpaceIndex = 0; + bool tempWrapCharMode = false; + + int textOffsetY = 0; + float textOffsetX = 0.0f; + float glyphWidth = 0; + + int ellipsisWidth = GuiGetTextWidth("..."); + bool textOverflow = false; + for (int c = 0, codepointSize = 0; c < lineSize; c += codepointSize) + { + int codepoint = GetCodepointNext(&lines[i][c], &codepointSize); + int index = GetGlyphIndex(guiFont, codepoint); + + // NOTE: Normally, exiting the decoding sequence as soon as a bad byte is found (and return 0x3f) + // but all of the bad bytes need to be drawn using the '?' symbol, moving one byte + if (codepoint == 0x3f) codepointSize = 1; // TODO: Review not recognized codepoints size + + // Get glyph width to check if it goes out of bounds + if (guiFont.glyphs[index].advanceX == 0) glyphWidth = ((float)guiFont.recs[index].width*scaleFactor); + else glyphWidth = (float)guiFont.glyphs[index].advanceX*scaleFactor; + + // Wrap mode text measuring, to validate if + // it can be drawn or a new line is required + if (wrapMode == TEXT_WRAP_CHAR) + { + // Jump to next line if current character reach end of the box limits + if ((textOffsetX + glyphWidth) > textBounds.width - textBoundsWidthOffset) + { + textOffsetX = 0.0f; + textOffsetY += (GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); + + if (tempWrapCharMode) // Wrap at char level when too long words + { + wrapMode = TEXT_WRAP_WORD; + tempWrapCharMode = false; + } + } + } + else if (wrapMode == TEXT_WRAP_WORD) + { + if (codepoint == 32) lastSpaceIndex = c; + + // Get width to next space in line + int nextSpaceIndex = 0; + float nextSpaceWidth = GetNextSpaceWidth(lines[i] + c, &nextSpaceIndex); + + int nextSpaceIndex2 = 0; + float nextWordSize = GetNextSpaceWidth(lines[i] + lastSpaceIndex + 1, &nextSpaceIndex2); + + if (nextWordSize > textBounds.width - textBoundsWidthOffset) + { + // Considering the case the next word is longer than bounds + tempWrapCharMode = true; + wrapMode = TEXT_WRAP_CHAR; + } + else if ((textOffsetX + nextSpaceWidth) > textBounds.width - textBoundsWidthOffset) + { + textOffsetX = 0.0f; + textOffsetY += (GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); + } + } + + if (codepoint == '\n') break; // WARNING: Lines are already processed manually, no need to keep drawing after this codepoint + else + { + // TODO: There are multiple types of spaces in Unicode, + // maybe it's a good idea to add support for more: http://jkorpela.fi/chars/spaces.html + if ((codepoint != ' ') && (codepoint != '\t')) // Do not draw codepoints with no glyph + { + if (wrapMode == TEXT_WRAP_NONE) + { + // Draw only required text glyphs fitting the textBounds.width + if (textSizeX > textBounds.width) + { + if (textOffsetX <= (textBounds.width - glyphWidth - textBoundsWidthOffset - ellipsisWidth)) + { + DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha)); + } + else if (!textOverflow) + { + textOverflow = true; + + for (int j = 0; j < ellipsisWidth; j += ellipsisWidth/3) + { + DrawTextCodepoint(guiFont, '.', RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX + j, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha)); + } + } + } + else + { + DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha)); + } + } + else if ((wrapMode == TEXT_WRAP_CHAR) || (wrapMode == TEXT_WRAP_WORD)) + { + // Draw only glyphs inside the bounds + if ((textBoundsPosition.y + textOffsetY) <= (textBounds.y + textBounds.height - GuiGetStyle(DEFAULT, TEXT_SIZE))) + { + DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha)); + } + } + } + + if (guiFont.glyphs[index].advanceX == 0) textOffsetX += ((float)guiFont.recs[index].width*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + else textOffsetX += ((float)guiFont.glyphs[index].advanceX*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + } + } + + if (wrapMode == TEXT_WRAP_NONE) posOffsetY += (float)(GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); + else if ((wrapMode == TEXT_WRAP_CHAR) || (wrapMode == TEXT_WRAP_WORD)) posOffsetY += (textOffsetY + (float)GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); + //--------------------------------------------------------------------------------- + } + +#if defined(RAYGUI_DEBUG_TEXT_BOUNDS) + GuiDrawRectangle(textBounds, 0, WHITE, Fade(BLUE, 0.4f)); +#endif +} + +// Gui draw rectangle using default raygui plain style with borders +static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color) +{ + if (color.a > 0) + { + // Draw rectangle filled with color + DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, GuiFade(color, guiAlpha)); + } + + if (borderWidth > 0) + { + // Draw rectangle border lines with color + DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, borderWidth, GuiFade(borderColor, guiAlpha)); + DrawRectangle((int)rec.x, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, GuiFade(borderColor, guiAlpha)); + DrawRectangle((int)rec.x + (int)rec.width - borderWidth, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, GuiFade(borderColor, guiAlpha)); + DrawRectangle((int)rec.x, (int)rec.y + (int)rec.height - borderWidth, (int)rec.width, borderWidth, GuiFade(borderColor, guiAlpha)); + } + +#if defined(RAYGUI_DEBUG_RECS_BOUNDS) + DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, Fade(RED, 0.4f)); +#endif +} + +// Draw tooltip using control bounds +static void GuiTooltip(Rectangle controlRec) +{ + if (!guiLocked && guiTooltip && (guiTooltipPtr != NULL) && !guiControlExclusiveMode) + { + Vector2 textSize = MeasureTextEx(GuiGetFont(), guiTooltipPtr, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + + if ((controlRec.x + textSize.x + 16) > GetScreenWidth()) controlRec.x -= (textSize.x + 16 - controlRec.width); + + int lineCount = 0; + GetTextLines(guiTooltipPtr, &lineCount); // Only using the line count + if ((controlRec.y + controlRec.height + textSize.y + 4 + 8*lineCount) > GetScreenHeight()) + controlRec.y -= (controlRec.height + textSize.y + 4 + 8*lineCount); + + // TODO: Probably TEXT_LINE_SPACING should be considered on panel size instead of hardcoding 8.0f + GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, textSize.y + 8.0f*lineCount }, NULL); + + int textPadding = GuiGetStyle(LABEL, TEXT_PADDING); + int textAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); + GuiSetStyle(LABEL, TEXT_PADDING, 0); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, textSize.y + 8.0f*lineCount }, guiTooltipPtr); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, textAlignment); + GuiSetStyle(LABEL, TEXT_PADDING, textPadding); + } +} + +// Split controls text into multiple strings +// Also check for multiple columns (required by GuiToggleGroup()) +static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow) +{ + // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) + // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, + // all used memory is static... it has some limitations: + // 1. Maximum number of possible split strings is set by RAYGUI_TEXTSPLIT_MAX_ITEMS + // 2. Maximum size of text to split is RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE + // NOTE: Those definitions could be externally provided if required + + // TODO: HACK: GuiTextSplit() - Review how textRows are returned to user + // textRow is an externally provided array of integers that stores row number for every splitted string + + #if !defined(RAYGUI_TEXTSPLIT_MAX_ITEMS) + #define RAYGUI_TEXTSPLIT_MAX_ITEMS 128 + #endif + #if !defined(RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE) + #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE 1024 + #endif + + static const char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL }; // String pointers array (points to buffer data) + static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 }; // Buffer data (text input copy with '\0' added) + memset(buffer, 0, RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE); + + result[0] = buffer; + int counter = 1; + + if (textRow != NULL) textRow[0] = 0; + + // Count how many substrings text contains and point to every one of them + for (int i = 0; i < RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE; i++) + { + buffer[i] = text[i]; + if (buffer[i] == '\0') break; + else if ((buffer[i] == delimiter) || (buffer[i] == '\n')) + { + result[counter] = buffer + i + 1; + + if (textRow != NULL) + { + if (buffer[i] == '\n') textRow[counter] = textRow[counter - 1] + 1; + else textRow[counter] = textRow[counter - 1]; + } + + buffer[i] = '\0'; // Set an end of string at this point + + counter++; + if (counter >= RAYGUI_TEXTSPLIT_MAX_ITEMS) break; + } + } + + *count = counter; + + return result; +} + +// Convert color data from RGB to HSV +// NOTE: Color data should be passed normalized +static Vector3 ConvertRGBtoHSV(Vector3 rgb) +{ + Vector3 hsv = { 0 }; + float min = 0.0f; + float max = 0.0f; + float delta = 0.0f; + + min = (rgb.x < rgb.y)? rgb.x : rgb.y; + min = (min < rgb.z)? min : rgb.z; + + max = (rgb.x > rgb.y)? rgb.x : rgb.y; + max = (max > rgb.z)? max : rgb.z; + + hsv.z = max; // Value + delta = max - min; + + if (delta < 0.00001f) + { + hsv.y = 0.0f; + hsv.x = 0.0f; // Undefined, maybe NAN? + return hsv; + } + + if (max > 0.0f) + { + // NOTE: If max is 0, this divide would cause a crash + hsv.y = (delta/max); // Saturation + } + else + { + // NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined + hsv.y = 0.0f; + hsv.x = 0.0f; // Undefined, maybe NAN? + return hsv; + } + + // NOTE: Comparing float values could not work properly + if (rgb.x >= max) hsv.x = (rgb.y - rgb.z)/delta; // Between yellow & magenta + else + { + if (rgb.y >= max) hsv.x = 2.0f + (rgb.z - rgb.x)/delta; // Between cyan & yellow + else hsv.x = 4.0f + (rgb.x - rgb.y)/delta; // Between magenta & cyan + } + + hsv.x *= 60.0f; // Convert to degrees + + if (hsv.x < 0.0f) hsv.x += 360.0f; + + return hsv; +} + +// Convert color data from HSV to RGB +// NOTE: Color data should be passed normalized +static Vector3 ConvertHSVtoRGB(Vector3 hsv) +{ + Vector3 rgb = { 0 }; + float hh = 0.0f, p = 0.0f, q = 0.0f, t = 0.0f, ff = 0.0f; + long i = 0; + + // NOTE: Comparing float values could not work properly + if (hsv.y <= 0.0f) + { + rgb.x = hsv.z; + rgb.y = hsv.z; + rgb.z = hsv.z; + return rgb; + } + + hh = hsv.x; + if (hh >= 360.0f) hh = 0.0f; + hh /= 60.0f; + + i = (long)hh; + ff = hh - i; + p = hsv.z*(1.0f - hsv.y); + q = hsv.z*(1.0f - (hsv.y*ff)); + t = hsv.z*(1.0f - (hsv.y*(1.0f - ff))); + + switch (i) + { + case 0: + { + rgb.x = hsv.z; + rgb.y = t; + rgb.z = p; + } break; + case 1: + { + rgb.x = q; + rgb.y = hsv.z; + rgb.z = p; + } break; + case 2: + { + rgb.x = p; + rgb.y = hsv.z; + rgb.z = t; + } break; + case 3: + { + rgb.x = p; + rgb.y = q; + rgb.z = hsv.z; + } break; + case 4: + { + rgb.x = t; + rgb.y = p; + rgb.z = hsv.z; + } break; + case 5: + default: + { + rgb.x = hsv.z; + rgb.y = p; + rgb.z = q; + } break; + } + + return rgb; +} + +// Scroll bar control (used by GuiScrollPanel()) +static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) +{ + GuiState state = guiState; + + // Is the scrollbar horizontal or vertical? + bool isVertical = (bounds.width > bounds.height)? false : true; + + // The size (width or height depending on scrollbar type) of the spinner buttons + const int spinnerSize = GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE)? + (isVertical? (int)bounds.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) : + (int)bounds.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH)) : 0; + + // Arrow buttons [<] [>] [∧] [∨] + Rectangle arrowUpLeft = { 0 }; + Rectangle arrowDownRight = { 0 }; + + // Actual area of the scrollbar excluding the arrow buttons + Rectangle scrollbar = { 0 }; + + // Slider bar that moves --[///]----- + Rectangle slider = { 0 }; + + // Normalize value + if (value > maxValue) value = maxValue; + if (value < minValue) value = minValue; + + int valueRange = maxValue - minValue; + if (valueRange <= 0) valueRange = 1; + + int sliderSize = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE); + if (sliderSize < 1) sliderSize = 1; // TODO: Consider a minimum slider size + + // Calculate rectangles for all of the components + arrowUpLeft = RAYGUI_CLITERAL(Rectangle){ + (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), + (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), + (float)spinnerSize, (float)spinnerSize }; + + if (isVertical) + { + arrowDownRight = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + bounds.height - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize }; + scrollbar = RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING), arrowUpLeft.y + arrowUpLeft.height, bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING)), bounds.height - arrowUpLeft.height - arrowDownRight.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) }; + + // Make sure the slider won't get outside of the scrollbar + sliderSize = (sliderSize >= scrollbar.height)? ((int)scrollbar.height - 2) : sliderSize; + slider = RAYGUI_CLITERAL(Rectangle){ + bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING), + scrollbar.y + (int)(((float)(value - minValue)/valueRange)*(scrollbar.height - sliderSize)), + bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)), + (float)sliderSize }; + } + else // horizontal + { + arrowDownRight = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + bounds.width - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize }; + scrollbar = RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x + arrowUpLeft.width, bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING), bounds.width - arrowUpLeft.width - arrowDownRight.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH), bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING)) }; + + // Make sure the slider won't get outside of the scrollbar + sliderSize = (sliderSize >= scrollbar.width)? ((int)scrollbar.width - 2) : sliderSize; + slider = RAYGUI_CLITERAL(Rectangle){ + scrollbar.x + (int)(((float)(value - minValue)/valueRange)*(scrollbar.width - sliderSize)), + bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING), + (float)sliderSize, + bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)) }; + } + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds + { + if (GUI_BUTTON_DOWN && + !CheckCollisionPointRec(mousePoint, arrowUpLeft) && + !CheckCollisionPointRec(mousePoint, arrowDownRight)) + { + if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) + { + state = STATE_PRESSED; + + if (isVertical) value = (int)(((float)(mousePoint.y - scrollbar.y - slider.height/2)*valueRange)/(scrollbar.height - slider.height) + minValue); + else value = (int)(((float)(mousePoint.x - scrollbar.x - slider.width/2)*valueRange)/(scrollbar.width - slider.width) + minValue); + } + } + else + { + guiControlExclusiveMode = false; + guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = STATE_FOCUSED; + + // Handle mouse wheel + float scrollDelta = GUI_SCROLL_DELTA; + if (scrollDelta != 0) value += (int)scrollDelta; + + // Handle mouse button down + if (GUI_BUTTON_PRESSED) + { + guiControlExclusiveMode = true; + guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts + + // Check arrows click + if (CheckCollisionPointRec(mousePoint, arrowUpLeft)) value -= valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + else if (CheckCollisionPointRec(mousePoint, arrowDownRight)) value += valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + else if (!CheckCollisionPointRec(mousePoint, slider)) + { + // If click on scrollbar position but not on slider, place slider directly on that position + if (isVertical) value = (int)(((float)(mousePoint.y - scrollbar.y - slider.height/2)*valueRange)/(scrollbar.height - slider.height) + minValue); + else value = (int)(((float)(mousePoint.x - scrollbar.x - slider.width/2)*valueRange)/(scrollbar.width - slider.width) + minValue); + } + + state = STATE_PRESSED; + } + + // Keyboard control on mouse hover scrollbar + /* + if (isVertical) + { + if (GUI_KEY_DOWN(KEY_DOWN)) value += 5; + else if (GUI_KEY_DOWN(KEY_UP)) value -= 5; + } + else + { + if (GUI_KEY_DOWN(KEY_RIGHT)) value += 5; + else if (GUI_KEY_DOWN(KEY_LEFT)) value -= 5; + } + */ + } + + // Normalize value + if (value > maxValue) value = maxValue; + if (value < minValue) value = minValue; + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(SCROLLBAR, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED))); // Draw the background + + GuiDrawRectangle(scrollbar, 0, BLANK, GetColor(GuiGetStyle(BUTTON, BASE_COLOR_NORMAL))); // Draw the scrollbar active area background + GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BORDER + state*3))); // Draw the slider bar + + // Draw arrows (using icon if available) + if (GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE)) + { +#if defined(RAYGUI_NO_ICONS) + GuiDrawText(isVertical? "^" : "<", + RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); + GuiDrawText(isVertical? "v" : ">", + RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); +#else + GuiDrawText(isVertical? "#121#" : "#118#", + RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3))); // ICON_ARROW_UP_FILL / ICON_ARROW_LEFT_FILL + GuiDrawText(isVertical? "#120#" : "#119#", + RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3))); // ICON_ARROW_DOWN_FILL / ICON_ARROW_RIGHT_FILL +#endif + } + //-------------------------------------------------------------------- + + return value; +} + +// Color fade-in or fade-out, alpha goes from 0.0f to 1.0f +// WARNING: It multiplies current alpha by alpha scale factor +static Color GuiFade(Color color, float alpha) +{ + if (alpha < 0.0f) alpha = 0.0f; + else if (alpha > 1.0f) alpha = 1.0f; + + Color result = { color.r, color.g, color.b, (unsigned char)(color.a*alpha) }; + + return result; +} + +#if defined(RAYGUI_STANDALONE) +// Returns a Color struct from hexadecimal value +static Color GetColor(int hexValue) +{ + Color color; + + color.r = (unsigned char)(hexValue >> 24) & 0xff; + color.g = (unsigned char)(hexValue >> 16) & 0xff; + color.b = (unsigned char)(hexValue >> 8) & 0xff; + color.a = (unsigned char)hexValue & 0xff; + + return color; +} + +// Returns hexadecimal value for a Color +static int ColorToInt(Color color) +{ + return (((int)color.r << 24) | ((int)color.g << 16) | ((int)color.b << 8) | (int)color.a); +} + +// Check if point is inside rectangle +static bool CheckCollisionPointRec(Vector2 point, Rectangle rec) +{ + bool collision = false; + + if ((point.x >= rec.x) && (point.x <= (rec.x + rec.width)) && + (point.y >= rec.y) && (point.y <= (rec.y + rec.height))) collision = true; + + return collision; +} + +// Formatting of text with variables to 'embed' +static const char *TextFormat(const char *text, ...) +{ + #if !defined(RAYGUI_TEXTFORMAT_MAX_SIZE) + #define RAYGUI_TEXTFORMAT_MAX_SIZE 256 + #endif + + static char buffer[RAYGUI_TEXTFORMAT_MAX_SIZE]; + + va_list args; + va_start(args, text); + vsnprintf(buffer, RAYGUI_TEXTFORMAT_MAX_SIZE, text, args); + va_end(args); + + return buffer; +} + +// Draw rectangle with vertical gradient fill color +// NOTE: This function is only used by GuiColorPicker() +static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2) +{ + Rectangle bounds = { (float)posX, (float)posY, (float)width, (float)height }; + DrawRectangleGradientEx(bounds, color1, color2, color2, color1); +} + +// Split string into multiple strings +const char **TextSplit(const char *text, char delimiter, int *count) +{ + // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) + // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, + // all used memory is static... it has some limitations: + // 1. Maximum number of possible split strings is set by RAYGUI_TEXTSPLIT_MAX_ITEMS + // 2. Maximum size of text to split is RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE + + #if !defined(RAYGUI_TEXTSPLIT_MAX_ITEMS) + #define RAYGUI_TEXTSPLIT_MAX_ITEMS 128 + #endif + #if !defined(RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE) + #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE 1024 + #endif + + static const char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL }; + static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 }; + memset(buffer, 0, RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE); + + result[0] = buffer; + int counter = 0; + + if (text != NULL) + { + counter = 1; + + // Count how many substrings text contains and point to every one of them + for (int i = 0; i < RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE; i++) + { + buffer[i] = text[i]; + if (buffer[i] == '\0') break; + else if (buffer[i] == delimiter) + { + buffer[i] = '\0'; // Set an end of string at this point + result[counter] = buffer + i + 1; + counter++; + + if (counter == RAYGUI_TEXTSPLIT_MAX_ITEMS) break; + } + } + } + + *count = counter; + return result; +} + +// Get integer value from text +// NOTE: This function replaces atoi() [stdlib.h] +static int TextToInteger(const char *text) +{ + int value = 0; + int sign = 1; + + if ((text[0] == '+') || (text[0] == '-')) + { + if (text[0] == '-') sign = -1; + text++; + } + + for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0'); + + return value*sign; +} + +// Get float value from text +// NOTE: This function replaces atof() [stdlib.h] +// WARNING: Only '.' character is understood as decimal point +static float TextToFloat(const char *text) +{ + float value = 0.0f; + float sign = 1.0f; + + if ((text[0] == '+') || (text[0] == '-')) + { + if (text[0] == '-') sign = -1.0f; + text++; + } + + int i = 0; + for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0'); + + if (text[i++] != '.') value *= sign; + else + { + float divisor = 10.0f; + for (; ((text[i] >= '0') && (text[i] <= '9')); i++) + { + value += ((float)(text[i] - '0'))/divisor; + divisor = divisor*10.0f; + } + } + + return value; +} + +// Encode codepoint into UTF-8 text (char array size returned as parameter) +static const char *CodepointToUTF8(int codepoint, int *byteSize) +{ + static char utf8[6] = { 0 }; + int size = 0; + + if (codepoint <= 0x7f) + { + utf8[0] = (char)codepoint; + size = 1; + } + else if (codepoint <= 0x7ff) + { + utf8[0] = (char)(((codepoint >> 6) & 0x1f) | 0xc0); + utf8[1] = (char)((codepoint & 0x3f) | 0x80); + size = 2; + } + else if (codepoint <= 0xffff) + { + utf8[0] = (char)(((codepoint >> 12) & 0x0f) | 0xe0); + utf8[1] = (char)(((codepoint >> 6) & 0x3f) | 0x80); + utf8[2] = (char)((codepoint & 0x3f) | 0x80); + size = 3; + } + else if (codepoint <= 0x10ffff) + { + utf8[0] = (char)(((codepoint >> 18) & 0x07) | 0xf0); + utf8[1] = (char)(((codepoint >> 12) & 0x3f) | 0x80); + utf8[2] = (char)(((codepoint >> 6) & 0x3f) | 0x80); + utf8[3] = (char)((codepoint & 0x3f) | 0x80); + size = 4; + } + + *byteSize = size; + + return utf8; +} + +// Get next codepoint in a UTF-8 encoded text, scanning until '\0' is found +// When a invalid UTF-8 byte is encountered, exiting as soon as possible and returning a '?'(0x3f) codepoint +// Total number of bytes processed are returned as a parameter +// NOTE: The standard says U+FFFD should be returned in case of errors +// but that character is not supported by the default font in raylib +static int GetCodepointNext(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; +} +#endif // RAYGUI_STANDALONE + +#endif // RAYGUI_IMPLEMENTATION diff --git a/examples/shaders/raygui.h b/examples/shaders/raygui.h index 88fe5cc5b..67c16be45 100644 --- a/examples/shaders/raygui.h +++ b/examples/shaders/raygui.h @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raygui v4.5-dev - A simple and easy-to-use immediate-mode gui library +* raygui v5.0-dev - A simple and easy-to-use immediate-mode gui library * * DESCRIPTION: * raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also @@ -83,8 +83,8 @@ * used for all controls, when any of those base values is set, it is automatically populated to all * controls, so, specific control values overwriting generic style should be set after base values * -* After the first BASE set we have the EXTENDED properties (by default guiStyle[16..23]), those -* properties are actually common to all controls and can not be overwritten individually (like BASE ones) +* After the first BASE properties set, the EXTENDED properties set is defined (by default guiStyle[16..23]), +* those properties are actually common to all controls and can not be overwritten individually (like BASE ones) * Some of those properties are: TEXT_SIZE, TEXT_SPACING, LINE_COLOR, BACKGROUND_COLOR * * Custom control properties can be defined using the EXTENDED properties for each independent control. @@ -141,13 +141,14 @@ * Draw text bounds rectangles for debug * * VERSIONS HISTORY: -* 5.0 (xx-Nov-2025) ADDED: Support up to 32 controls (v500) +* 5.0 (xx-Mar-2026) ADDED: Support up to 32 controls (v500) * ADDED: guiControlExclusiveMode and guiControlExclusiveRec for exclusive modes * ADDED: GuiValueBoxFloat() * ADDED: GuiDropdonwBox() properties: DROPDOWN_ARROW_HIDDEN, DROPDOWN_ROLL_UP * ADDED: GuiListView() property: LIST_ITEMS_BORDER_WIDTH * ADDED: GuiLoadIconsFromMemory() * ADDED: Multiple new icons +* ADDED: Macros for inputs customization, raylib decoupling * REMOVED: GuiSpinner() from controls list, using BUTTON + VALUEBOX properties * REMOVED: GuiSliderPro(), functionality was redundant * REVIEWED: Controls using text labels to use LABEL properties @@ -165,6 +166,7 @@ * REVIEWED: GuiTextBox(), multiple improvements: autocursor and more * REVIEWED: Functions descriptions, removed wrong return value reference * REDESIGNED: GuiColorPanel(), improved HSV <-> RGBA convertion +* REDESIGNED: WARNING: TEXT_LINE_SPACING does not consider text height, only lines spacing * * 4.0 (12-Sep-2023) ADDED: GuiToggleSlider() * ADDED: GuiColorPickerHSV() and GuiColorPanelHSV() @@ -316,7 +318,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2025 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2026 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -351,10 +353,11 @@ // NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll #if defined(_WIN32) #if defined(BUILD_LIBTYPE_SHARED) - #define RAYGUIAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll) + #define RAYGUIAPI __declspec(dllexport) // Building the library as a Win32 shared library (.dll) #elif defined(USE_LIBTYPE_SHARED) - #define RAYGUIAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll) + #define RAYGUIAPI __declspec(dllimport) // Using the library as a Win32 shared library (.dll) #endif + #define _CRT_SECURE_NO_WARNINGS // Disable unsafe warnings on scanf() functions in MSVC #endif // Function specifiers definition @@ -369,9 +372,48 @@ // NOTE: Avoiding those calls, also avoids const strings memory usage #define RAYGUI_SUPPORT_LOG_INFO #if defined(RAYGUI_SUPPORT_LOG_INFO) - #define RAYGUI_LOG(...) printf(__VA_ARGS__) + #define RAYGUI_LOG(...) printf(__VA_ARGS__) #else - #define RAYGUI_LOG(...) + #define RAYGUI_LOG(...) +#endif + +// Macros to define required UI inputs, including mapping to gamepad controls +// TODO: Define additionally required macros for missing inputs +#if !defined(GUI_BUTTON_DOWN) + #define GUI_BUTTON_DOWN (IsMouseButtonDown(MOUSE_LEFT_BUTTON) || IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) +#endif +#if !defined(GUI_BUTTON_DOWN_ALT) + // Mapping to alternative button down pressed + #define GUI_BUTTON_DOWN_ALT (IsMouseButtonDown(MOUSE_RIGHT_BUTTON) || IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT)) +#endif +#if !defined(GUI_BUTTON_PRESSED) + #define GUI_BUTTON_PRESSED (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsGamepadButtonPressed(0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) +#endif +// TODO: WARNING: GuiTabBar() still requires IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON) +#if !defined(GUI_BUTTON_RELEASED) + #define GUI_BUTTON_RELEASED (IsMouseButtonReleased(MOUSE_LEFT_BUTTON) || IsGamepadButtonReleased(0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) +#endif +#if !defined(GUI_SCROLL_DELTA) + // Mapping to scroll delta changes + // TODO: Review inconsistencies between platforms + #if defined(PLATFORM_WEB) + // NOTE: Gamepad axis triggers not detected on web platform + #define GUI_SCROLL_DELTA ((float)IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_TRIGGER_2) - (float)IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_TRIGGER_2)) + #else + #define GUI_SCROLL_DELTA (GetMouseWheelMove() + (GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_TRIGGER) + 1) - (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_TRIGGER) + 1)) + #endif +#endif +#if !defined(GUI_POINTER_POSITION) + #define GUI_POINTER_POSITION GetMousePosition() +#endif +#if !defined(GUI_KEY_DOWN) + #define GUI_KEY_DOWN(key) IsKeyDown(key) +#endif +#if !defined(GUI_KEY_PRESSED) + #define GUI_KEY_PRESSED(key) IsKeyPressed(key) +#endif +#if !defined(GUI_INPUT_KEY) + #define GUI_INPUT_KEY GetCharPressed() #endif //---------------------------------------------------------------------------------- @@ -567,7 +609,7 @@ typedef enum { //---------------------------------------------------------------------------------- // DEFAULT extended properties // NOTE: Those properties are common to all controls or global -// WARNING: We only have 8 slots for those properties by default!!! -> New global control: TEXT? +// WARNING: Only 8 slots vailable for those properties by default typedef enum { TEXT_SIZE = 16, // Text size (glyphs max height) TEXT_SPACING, // Text spacing between glyphs @@ -1091,7 +1133,7 @@ typedef enum { // Icons data is defined by bit array (every bit represents one pixel) // Those arrays are stored as unsigned int data arrays, so, // every array element defines 32 pixels (bits) of information -// One icon is defined by 8 int, (8 int * 32 bit = 256 bit = 16*16 pixels) +// One icon is defined by 8 int, (8 int*32 bit = 256 bit = 16*16 pixels) // NOTE: Number of elemens depend on RAYGUI_ICON_SIZE (by default 16x16 pixels) #define RAYGUI_ICON_DATA_ELEMENTS (RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32) @@ -1450,12 +1492,12 @@ static bool IsMouseButtonReleased(int button); static bool IsKeyDown(int key); static bool IsKeyPressed(int key); -static int GetCharPressed(void); // -- GuiTextBox(), GuiValueBox() +static int GetCharPressed(void); // -- GuiTextBox(), GuiValueBox() //------------------------------------------------------------------------------- // Drawing required functions //------------------------------------------------------------------------------- -static void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle() +static void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle() static void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker() //------------------------------------------------------------------------------- @@ -1520,11 +1562,11 @@ static Color GuiFade(Color color, float alpha); // Fade color by an alph // Gui Setup Functions Definition //---------------------------------------------------------------------------------- // Enable gui global state -// NOTE: We check for STATE_DISABLED to avoid messing custom global state setups +// NOTE: Checking for STATE_DISABLED to avoid messing custom global state setups void GuiEnable(void) { if (guiState == STATE_DISABLED) guiState = STATE_NORMAL; } // Disable gui global state -// NOTE: We check for STATE_NORMAL to avoid messing custom global state setups +// NOTE: Checking for STATE_NORMAL to avoid messing custom global state setups void GuiDisable(void) { if (guiState == STATE_NORMAL) guiState = STATE_DISABLED; } // Lock gui global state @@ -1557,9 +1599,8 @@ void GuiSetFont(Font font) { if (font.texture.id > 0) { - // NOTE: If we try to setup a font but default style has not been - // lazily loaded before, it will be overwritten, so we need to force - // default style loading first + // NOTE: If a font is tried to be set but default style has not been lazily loaded first, + // it will be overwritten, so default style loading needs to be forced first if (!guiStyleLoaded) GuiLoadStyleDefault(); guiFont = font; @@ -1613,13 +1654,14 @@ int GuiWindowBox(Rectangle bounds, const char *title) //GuiState state = guiState; int statusBarHeight = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT; + int statusBorderWidth = GuiGetStyle(STATUSBAR, BORDER_WIDTH); Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)statusBarHeight }; if (bounds.height < statusBarHeight*2.0f) bounds.height = statusBarHeight*2.0f; const float vPadding = statusBarHeight/2.0f - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT/2.0f; - Rectangle windowPanel = { bounds.x, bounds.y + (float)statusBarHeight - 1, bounds.width, bounds.height - (float)statusBarHeight + 1 }; - Rectangle closeButtonRec = { statusBar.x + statusBar.width - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT - vPadding, + Rectangle windowPanel = { bounds.x, bounds.y + (float)statusBarHeight - (float)statusBorderWidth, bounds.width, bounds.height - (float)statusBarHeight + (float)statusBorderWidth }; + Rectangle closeButtonRec = { statusBar.x + statusBar.width - (float)statusBorderWidth - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT - vPadding, statusBar.y + vPadding, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT }; // Update control @@ -1629,8 +1671,8 @@ int GuiWindowBox(Rectangle bounds, const char *title) // Draw control //-------------------------------------------------------------------- - GuiStatusBar(statusBar, title); // Draw window header as status bar GuiPanel(windowPanel, NULL); // Draw window base + GuiStatusBar(statusBar, title); // Draw window header as status bar // Draw window close button int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); @@ -1786,7 +1828,7 @@ int GuiTabBar(Rectangle bounds, const char **text, int count, int *active) } // Close tab with middle mouse button pressed - if (CheckCollisionPointRec(GetMousePosition(), tabBounds) && IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) result = i; + if (CheckCollisionPointRec(GUI_POINTER_POSITION, tabBounds) && IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) result = i; GuiSetStyle(TOGGLE, TEXT_PADDING, textPadding); GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, textAlignment); @@ -1885,37 +1927,37 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check button state if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; #if defined(SUPPORT_SCROLLBAR_KEY_INPUT) if (hasHorizontalScrollBar) { - if (IsKeyDown(KEY_RIGHT)) scrollPos.x -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); - if (IsKeyDown(KEY_LEFT)) scrollPos.x += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (GUI_KEY_DOWN(KEY_RIGHT)) scrollPos.x -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (GUI_KEY_DOWN(KEY_LEFT)) scrollPos.x += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); } if (hasVerticalScrollBar) { - if (IsKeyDown(KEY_DOWN)) scrollPos.y -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); - if (IsKeyDown(KEY_UP)) scrollPos.y += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (GUI_KEY_DOWN(KEY_DOWN)) scrollPos.y -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (GUI_KEY_DOWN(KEY_UP)) scrollPos.y += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); } #endif - float wheelMove = GetMouseWheelMove(); + float scrollDelta = GUI_SCROLL_DELTA; // Set scrolling speed with mouse wheel based on ratio between bounds and content - Vector2 mouseWheelSpeed = { content.width/bounds.width, content.height/bounds.height }; - if (mouseWheelSpeed.x < RAYGUI_MIN_MOUSE_WHEEL_SPEED) mouseWheelSpeed.x = RAYGUI_MIN_MOUSE_WHEEL_SPEED; - if (mouseWheelSpeed.y < RAYGUI_MIN_MOUSE_WHEEL_SPEED) mouseWheelSpeed.y = RAYGUI_MIN_MOUSE_WHEEL_SPEED; + Vector2 scrollSpeed = { content.width/bounds.width, content.height/bounds.height }; + if (scrollSpeed.x < RAYGUI_MIN_MOUSE_WHEEL_SPEED) scrollSpeed.x = RAYGUI_MIN_MOUSE_WHEEL_SPEED; + if (scrollSpeed.y < RAYGUI_MIN_MOUSE_WHEEL_SPEED) scrollSpeed.y = RAYGUI_MIN_MOUSE_WHEEL_SPEED; // Horizontal and vertical scrolling with mouse wheel - if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*mouseWheelSpeed.x; - else scrollPos.y += wheelMove*mouseWheelSpeed.y; // Vertical scroll + if (hasHorizontalScrollBar && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_LEFT_SHIFT))) scrollPos.x += scrollDelta*scrollSpeed.x; + else scrollPos.y += scrollDelta*scrollSpeed.y; // Vertical scroll } } @@ -2001,15 +2043,15 @@ int GuiButton(Rectangle bounds, const char *text) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check button state if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) result = 1; + if (GUI_BUTTON_RELEASED) result = 1; } } //-------------------------------------------------------------------- @@ -2031,7 +2073,7 @@ int GuiLabelButton(Rectangle bounds, const char *text) GuiState state = guiState; bool pressed = false; - // NOTE: We force bounds.width to be all text + // NOTE: Force bounds.width to be all text float textWidth = (float)GuiGetTextWidth(text); if ((bounds.width - 2*GuiGetStyle(LABEL, BORDER_WIDTH) - 2*GuiGetStyle(LABEL, TEXT_PADDING)) < textWidth) bounds.width = textWidth + 2*GuiGetStyle(LABEL, BORDER_WIDTH) + 2*GuiGetStyle(LABEL, TEXT_PADDING) + 2; @@ -2039,15 +2081,15 @@ int GuiLabelButton(Rectangle bounds, const char *text) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check checkbox state if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true; + if (GUI_BUTTON_RELEASED) pressed = true; } } //-------------------------------------------------------------------- @@ -2073,13 +2115,13 @@ int GuiToggle(Rectangle bounds, const char *text, bool *active) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check toggle button state if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; - else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else if (GUI_BUTTON_RELEASED) { state = STATE_NORMAL; *active = !(*active); @@ -2184,12 +2226,12 @@ int GuiToggleSlider(Rectangle bounds, const char *text, int *active) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; - else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else if (GUI_BUTTON_RELEASED) { state = STATE_PRESSED; (*active)++; @@ -2255,7 +2297,7 @@ int GuiCheckBox(Rectangle bounds, const char *text, bool *checked) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; Rectangle totalBounds = { (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT)? textBounds.x : bounds.x, @@ -2267,10 +2309,10 @@ int GuiCheckBox(Rectangle bounds, const char *text, bool *checked) // Check checkbox state if (CheckCollisionPointRec(mousePoint, totalBounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_RELEASED) { *checked = !(*checked); result = 1; @@ -2323,18 +2365,18 @@ int GuiComboBox(Rectangle bounds, const char *text, int *active) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1) && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) { - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_PRESSED) { *active += 1; if (*active >= itemCount) *active = 0; // Cyclic combobox } - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; } } @@ -2392,7 +2434,7 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1) && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (editMode) { @@ -2401,11 +2443,11 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod // Check if mouse has been pressed or released outside limits if (!CheckCollisionPointRec(mousePoint, boundsOpen)) { - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) result = 1; + if (GUI_BUTTON_PRESSED || GUI_BUTTON_RELEASED) result = 1; } // Check if already selected item has been pressed again - if (CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1; + if (CheckCollisionPointRec(mousePoint, bounds) && GUI_BUTTON_PRESSED) result = 1; // Check focused and selected item for (int i = 0; i < itemCount; i++) @@ -2417,7 +2459,7 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod if (CheckCollisionPointRec(mousePoint, itemBounds)) { itemFocused = i; - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_RELEASED) { itemSelected = i; result = 1; // Item selected @@ -2432,7 +2474,7 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod { if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_PRESSED) { result = 1; state = STATE_PRESSED; @@ -2506,7 +2548,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) int result = 0; GuiState state = guiState; - bool multiline = false; // TODO: Consider multiline text input + bool multiline = false; // TODO: Consider multiline text input int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); Rectangle textBounds = GetTextBounds(TEXTBOX, bounds); @@ -2514,7 +2556,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) int thisCursorIndex = textBoxCursorIndex; if (thisCursorIndex > textLength) thisCursorIndex = textLength; int textWidth = GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex); - int textIndexOffset = 0; // Text index offset to start drawing in the box + int textIndexOffset = 0; // Text index offset to start drawing in the box // Cursor rectangle // NOTE: Position X value should be updated @@ -2547,13 +2589,13 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) !guiControlExclusiveMode && // No gui slider on dragging (wrapMode == TEXT_WRAP_NONE)) // No wrap mode { - Vector2 mousePosition = GetMousePosition(); + Vector2 mousePosition = GUI_POINTER_POSITION; if (editMode) { // GLOBAL: Auto-cursor movement logic // NOTE: Keystrokes are handled repeatedly when button is held down for some time - if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCounter++; + if (GUI_KEY_DOWN(KEY_LEFT) || GUI_KEY_DOWN(KEY_RIGHT) || GUI_KEY_DOWN(KEY_UP) || GUI_KEY_DOWN(KEY_DOWN) || GUI_KEY_DOWN(KEY_BACKSPACE) || GUI_KEY_DOWN(KEY_DELETE)) autoCursorCounter++; else autoCursorCounter = 0; bool autoCursorShouldTrigger = (autoCursorCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) && ((autoCursorCounter % RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0); @@ -2563,7 +2605,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength; // If text does not fit in the textbox and current cursor position is out of bounds, - // we add an index offset to text for drawing only what requires depending on cursor + // adding an index offset to text for drawing only what requires depending on cursor while (textWidth >= textBounds.width) { int nextCodepointSize = 0; @@ -2574,15 +2616,15 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) textWidth = GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex); } - int codepoint = GetCharPressed(); // Get Unicode codepoint - if (multiline && IsKeyPressed(KEY_ENTER)) codepoint = (int)'\n'; + int codepoint = GUI_INPUT_KEY; // Get Unicode codepoint + if (multiline && GUI_KEY_PRESSED(KEY_ENTER)) codepoint = (int)'\n'; // Encode codepoint as UTF-8 int codepointSize = 0; const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize); // Handle text paste action - if (IsKeyPressed(KEY_V) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) + if (GUI_KEY_PRESSED(KEY_V) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) { const char *pasteText = GetClipboardText(); if (pasteText != NULL) @@ -2632,13 +2674,13 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) } // Move cursor to start - if ((textLength > 0) && IsKeyPressed(KEY_HOME)) textBoxCursorIndex = 0; + if ((textLength > 0) && GUI_KEY_PRESSED(KEY_HOME)) textBoxCursorIndex = 0; // Move cursor to end - if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) textBoxCursorIndex = textLength; + if ((textLength > textBoxCursorIndex) && GUI_KEY_PRESSED(KEY_END)) textBoxCursorIndex = textLength; // Delete related codepoints from text, after current cursor position - if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_DELETE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) + if ((textLength > textBoxCursorIndex) && GUI_KEY_PRESSED(KEY_DELETE) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) { int offset = textBoxCursorIndex; int accCodepointSize = 0; @@ -2674,7 +2716,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) textLength -= accCodepointSize; } - else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && autoCursorShouldTrigger))) + else if ((textLength > textBoxCursorIndex) && (GUI_KEY_PRESSED(KEY_DELETE) || (GUI_KEY_DOWN(KEY_DELETE) && autoCursorShouldTrigger))) { // Delete single codepoint from text, after current cursor position @@ -2688,12 +2730,12 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) } // Delete related codepoints from text, before current cursor position - if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_BACKSPACE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) + if ((textBoxCursorIndex > 0) && GUI_KEY_PRESSED(KEY_BACKSPACE) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) { int offset = textBoxCursorIndex; int accCodepointSize = 0; - int prevCodepointSize; - int prevCodepoint; + int prevCodepointSize = 0; + int prevCodepoint = 0; // Check whitespace to delete (ASCII only) while (offset > 0) @@ -2724,7 +2766,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) textBoxCursorIndex -= accCodepointSize; } - else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && autoCursorShouldTrigger))) + else if ((textBoxCursorIndex > 0) && (GUI_KEY_PRESSED(KEY_BACKSPACE) || (GUI_KEY_DOWN(KEY_BACKSPACE) && autoCursorShouldTrigger))) { // Delete single codepoint from text, before current cursor position @@ -2740,12 +2782,12 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) } // Move cursor position with keys - if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_LEFT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) + if ((textBoxCursorIndex > 0) && GUI_KEY_PRESSED(KEY_LEFT) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) { int offset = textBoxCursorIndex; //int accCodepointSize = 0; - int prevCodepointSize; - int prevCodepoint; + int prevCodepointSize = 0; + int prevCodepoint = 0; // Check whitespace to skip (ASCII only) while (offset > 0) @@ -2771,14 +2813,14 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) textBoxCursorIndex = offset; } - else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && autoCursorShouldTrigger))) + else if ((textBoxCursorIndex > 0) && (GUI_KEY_PRESSED(KEY_LEFT) || (GUI_KEY_DOWN(KEY_LEFT) && autoCursorShouldTrigger))) { int prevCodepointSize = 0; GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize); textBoxCursorIndex -= prevCodepointSize; } - else if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_RIGHT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) + else if ((textLength > textBoxCursorIndex) && GUI_KEY_PRESSED(KEY_RIGHT) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) { int offset = textBoxCursorIndex; //int accCodepointSize = 0; @@ -2810,7 +2852,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) textBoxCursorIndex = offset; } - else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && autoCursorShouldTrigger))) + else if ((textLength > textBoxCursorIndex) && (GUI_KEY_PRESSED(KEY_RIGHT) || (GUI_KEY_DOWN(KEY_RIGHT) && autoCursorShouldTrigger))) { int nextCodepointSize = 0; GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize); @@ -2847,14 +2889,14 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) // Check if mouse cursor is at the last position int textEndWidth = GuiGetTextWidth(text + textIndexOffset); - if (GetMousePosition().x >= (textBounds.x + textEndWidth - glyphWidth/2)) + if (GUI_POINTER_POSITION.x >= (textBounds.x + textEndWidth - glyphWidth/2)) { mouseCursor.x = textBounds.x + textEndWidth; mouseCursorIndex = textLength; } // Place cursor at required index on mouse click - if ((mouseCursor.x >= 0) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + if ((mouseCursor.x >= 0) && GUI_BUTTON_PRESSED) { cursor.x = mouseCursor.x; textBoxCursorIndex = mouseCursorIndex; @@ -2867,8 +2909,8 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) //if (multiline) cursor.y = GetTextLines() // Finish text editing on ENTER or mouse click outside bounds - if ((!multiline && IsKeyPressed(KEY_ENTER)) || - (!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) + if ((!multiline && GUI_KEY_PRESSED(KEY_ENTER)) || + (!CheckCollisionPointRec(mousePosition, bounds) && GUI_BUTTON_PRESSED)) { textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes @@ -2881,7 +2923,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) { state = STATE_FOCUSED; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_PRESSED) { textBoxCursorIndex = textLength; // GLOBAL: Place cursor index to the end of current text autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes @@ -2975,12 +3017,12 @@ int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check spinner state if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; } } @@ -3050,7 +3092,7 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; bool valueHasChanged = false; if (editMode) @@ -3060,7 +3102,7 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in int keyCount = (int)strlen(textValue); // Add or remove minus symbol - if (IsKeyPressed(KEY_MINUS)) + if (GUI_KEY_PRESSED(KEY_MINUS)) { if (textValue[0] == '-') { @@ -3089,8 +3131,8 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in // Add new digit to text value if ((keyCount >= 0) && (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) && (GuiGetTextWidth(textValue) < bounds.width)) { - int key = GetCharPressed(); - + int key = GUI_INPUT_KEY; + // Only allow keys in range [48..57] if ((key >= 48) && (key <= 57)) { @@ -3101,7 +3143,7 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in } // Delete text - if ((keyCount > 0) && IsKeyPressed(KEY_BACKSPACE)) + if ((keyCount > 0) && GUI_KEY_PRESSED(KEY_BACKSPACE)) { keyCount--; textValue[keyCount] = '\0'; @@ -3110,11 +3152,11 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in if (valueHasChanged) *value = TextToInteger(textValue); - // NOTE: We are not clamp values until user input finishes + // NOTE: Values are not clamped until user input finishes //if (*value > maxValue) *value = maxValue; //else if (*value < minValue) *value = minValue; - if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) + if ((GUI_KEY_PRESSED(KEY_ENTER) || GUI_KEY_PRESSED(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && GUI_BUTTON_PRESSED)) { if (*value > maxValue) *value = maxValue; else if (*value < minValue) *value = minValue; @@ -3130,7 +3172,7 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in if (CheckCollisionPointRec(mousePoint, bounds)) { state = STATE_FOCUSED; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1; + if (GUI_BUTTON_PRESSED) result = 1; } } } @@ -3191,7 +3233,7 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; bool valueHasChanged = false; @@ -3202,7 +3244,7 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float int keyCount = (int)strlen(textValue); // Add or remove minus symbol - if (IsKeyPressed(KEY_MINUS)) + if (GUI_KEY_PRESSED(KEY_MINUS)) { if (textValue[0] == '-') { @@ -3233,7 +3275,7 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float { if (GuiGetTextWidth(textValue) < bounds.width) { - int key = GetCharPressed(); + int key = GUI_INPUT_KEY; if (((key >= 48) && (key <= 57)) || (key == '.') || ((keyCount == 0) && (key == '+')) || // NOTE: Sign can only be in first position @@ -3248,7 +3290,7 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float } // Pressed backspace - if (IsKeyPressed(KEY_BACKSPACE)) + if (GUI_KEY_PRESSED(KEY_BACKSPACE)) { if (keyCount > 0) { @@ -3260,14 +3302,14 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float if (valueHasChanged) *value = TextToFloat(textValue); - if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) result = 1; + if ((GUI_KEY_PRESSED(KEY_ENTER) || GUI_KEY_PRESSED(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && GUI_BUTTON_PRESSED)) result = 1; } else { if (CheckCollisionPointRec(mousePoint, bounds)) { state = STATE_FOCUSED; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1; + if (GUI_BUTTON_PRESSED) result = 1; } } } @@ -3321,11 +3363,11 @@ int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, flo //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) { @@ -3342,7 +3384,7 @@ int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, flo } else if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { state = STATE_PRESSED; guiControlExclusiveMode = true; @@ -3535,12 +3577,12 @@ int GuiDummyRec(Rectangle bounds, const char *text) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check button state if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; } } @@ -3578,7 +3620,7 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd int itemFocused = (focus == NULL)? -1 : *focus; int itemSelected = (active == NULL)? -1 : *active; - // Check if we need a scroll bar + // Check if scroll bar is needed bool useScrollBar = false; if ((GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING))*count > bounds.height) useScrollBar = true; @@ -3602,7 +3644,7 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check mouse inside list view if (CheckCollisionPointRec(mousePoint, bounds)) @@ -3615,7 +3657,7 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd if (CheckCollisionPointRec(mousePoint, itemBounds)) { itemFocused = startIndex + i; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_PRESSED) { if (itemSelected == (startIndex + i)) itemSelected = -1; else itemSelected = startIndex + i; @@ -3629,8 +3671,8 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd if (useScrollBar) { - int wheelMove = (int)GetMouseWheelMove(); - startIndex -= wheelMove; + float scrollDelta = GUI_SCROLL_DELTA; + startIndex -= (int)scrollDelta; if (startIndex < 0) startIndex = 0; else if (startIndex > (count - visibleItems)) startIndex = count - visibleItems; @@ -3659,7 +3701,7 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd { if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED))); - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED))); + GuiDrawText(text[startIndex + i], GetTextBounds(LISTVIEW, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED))); } else { @@ -3667,18 +3709,18 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd { // Draw item selected GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED))); - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED))); + GuiDrawText(text[startIndex + i], GetTextBounds(LISTVIEW, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED))); } - else if (((startIndex + i) == itemFocused)) // && (focus != NULL)) // NOTE: We want items focused, despite not returned! + else if (((startIndex + i) == itemFocused)) // && (focus != NULL)) // NOTE: Items focused, despite not returned { // Draw item focused GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED))); - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED))); + GuiDrawText(text[startIndex + i], GetTextBounds(LISTVIEW, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED))); } else { // Draw item normal (no rectangle) - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL))); + GuiDrawText(text[startIndex + i], GetTextBounds(LISTVIEW, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL))); } } @@ -3765,11 +3807,11 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) { @@ -3788,7 +3830,7 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) } else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { state = STATE_PRESSED; guiControlExclusiveMode = true; @@ -3850,11 +3892,11 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) { @@ -3873,7 +3915,7 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) } else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { state = STATE_PRESSED; guiControlExclusiveMode = true; @@ -3886,12 +3928,12 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) } else state = STATE_FOCUSED; - /*if (IsKeyDown(KEY_UP)) + /*if (GUI_KEY_DOWN(KEY_UP)) { hue -= 2.0f; if (hue <= 0.0f) hue = 0.0f; } - else if (IsKeyDown(KEY_DOWN)) + else if (GUI_KEY_DOWN(KEY_DOWN)) { hue += 2.0f; if (hue >= 360.0f) hue = 360.0f; @@ -4008,11 +4050,11 @@ int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) { @@ -4042,7 +4084,7 @@ int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) } else if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { state = STATE_PRESSED; guiControlExclusiveMode = true; @@ -4198,6 +4240,9 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment); } + int prevTextBoxAlignment = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT); + GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + if (secretViewActive != NULL) { static char stars[] = "****************"; @@ -4211,6 +4256,8 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co if (GuiTextBox(textBoxBounds, text, textMaxSize, textEditMode)) textEditMode = !textEditMode; } + GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, prevTextBoxAlignment); + int prevBtnTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); @@ -4231,7 +4278,7 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co // Grid control // NOTE: Returns grid mouse-hover selected cell // About drawing lines at subpixel spacing, simple put, not easy solution: -// https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster +// REF: https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell) { // Grid lines alpha amount @@ -4242,7 +4289,7 @@ int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vect int result = 0; GuiState state = guiState; - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; Vector2 currentMouseCell = { -1, -1 }; float spaceWidth = spacing/(float)subdivs; @@ -4410,11 +4457,14 @@ void GuiLoadStyle(const char *fileName) if (fileDataSize > 0) { unsigned char *fileData = (unsigned char *)RAYGUI_CALLOC(fileDataSize, sizeof(unsigned char)); - fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile); + if (fileData != NULL) + { + fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile); - GuiLoadStyleFromMemory(fileData, fileDataSize); + GuiLoadStyleFromMemory(fileData, fileDataSize); - RAYGUI_FREE(fileData); + RAYGUI_FREE(fileData); + } } fclose(rgsFile); @@ -4425,7 +4475,7 @@ void GuiLoadStyle(const char *fileName) // Load style default over global style void GuiLoadStyleDefault(void) { - // We set this variable first to avoid cyclic function calls + // Setting this flag first to avoid cyclic function calls // when calling GuiSetStyle() and GuiGetStyle() guiStyleLoaded = true; @@ -4454,7 +4504,7 @@ void GuiLoadStyleDefault(void) GuiSetStyle(DEFAULT, TEXT_SPACING, 1); // DEFAULT, shared by all controls GuiSetStyle(DEFAULT, LINE_COLOR, 0x90abb5ff); // DEFAULT specific property GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0xf5f5f5ff); // DEFAULT specific property - GuiSetStyle(DEFAULT, TEXT_LINE_SPACING, 15); // DEFAULT, 15 pixels between lines + GuiSetStyle(DEFAULT, TEXT_LINE_SPACING, 5); // DEFAULT, pixels between lines, from bottom of first line to top of second GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE); // DEFAULT, text aligned vertically to middle of text-bounds // Initialize control-specific property values @@ -4520,7 +4570,7 @@ void GuiLoadStyleDefault(void) // NOTE: Default raylib font character 95 is a white square Rectangle whiteChar = guiFont.recs[95]; - // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering + // NOTE: Setting up a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering SetShapesTexture(guiFont.texture, RAYGUI_CLITERAL(Rectangle){ whiteChar.x + 1, whiteChar.y + 1, whiteChar.width - 2, whiteChar.height - 2 }); } } @@ -5037,14 +5087,14 @@ static Rectangle GetTextBounds(int control, Rectangle bounds) } // Get text icon if provided and move text cursor -// NOTE: We support up to 999 values for iconId +// NOTE: Up to #999# values supported for iconId static const char *GetTextIcon(const char *text, int *iconId) { #if !defined(RAYGUI_NO_ICONS) *iconId = -1; - if (text[0] == '#') // Maybe we have an icon! + if (text[0] == '#') // Maybe it is stars with an icon, ending # must be found { - char iconValue[4] = { 0 }; // Maximum length for icon value: 3 digits + '\0' + char iconValue[4] = { 0 }; // Maximum length for icon value: 3 digits + '\0' int pos = 1; while ((pos < 4) && (text[pos] >= '0') && (text[pos] <= '9')) @@ -5076,12 +5126,12 @@ static const char **GetTextLines(const char *text, int *count) static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings - int textSize = (int)strlen(text); + int textLength = (int)strlen(text); lines[0] = text; *count = 1; - for (int i = 0, k = 0; (i < textSize) && (*count < RAYGUI_MAX_TEXT_LINES); i++) + for (int i = 0, k = 0; (i < textLength) && (*count < RAYGUI_MAX_TEXT_LINES); i++) { if (text[i] == '\n') { @@ -5141,7 +5191,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C // - For every line, wordwrap mode is checked (useful for GuitextBox(), read-only) // Get text lines (using '\n' as delimiter) to be processed individually - // WARNING: We can't use GuiTextSplit() function because it can be already used + // WARNING: GuiTextSplit() function can't be used now because it can have already been used // before the GuiDrawText() call and its buffer is static, it would be overriden :( int lineCount = 0; const char **lines = GetTextLines(text, &lineCount); @@ -5152,7 +5202,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); // Wrap-mode only available in read-only mode, no for text editing // TODO: WARNING: This totalHeight is not valid for vertical alignment in case of word-wrap - float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_SIZE)/2); + float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); float posOffsetY = 0.0f; for (int i = 0; i < lineCount; i++) @@ -5165,7 +5215,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C Vector2 textBoundsPosition = { textBounds.x, textBounds.y }; float textBoundsWidthOffset = 0.0f; - // NOTE: We get text size after icon has been processed + // NOTE: Get text size after icon has been processed // WARNING: GuiGetTextWidth() also processes text icon to get width! -> Really needed? int textSizeX = GuiGetTextWidth(lines[i]); @@ -5200,8 +5250,8 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C default: break; } - // NOTE: Make sure we get pixel-perfect coordinates, - // In case of decimals we got weird text positioning + // NOTE: Make sure getting pixel-perfect coordinates, + // In case of decimals, it could result in text positioning artifacts textBoundsPosition.x = (float)((int)textBoundsPosition.x); textBoundsPosition.y = (float)((int)textBoundsPosition.y); //--------------------------------------------------------------------------------- @@ -5211,7 +5261,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C #if !defined(RAYGUI_NO_ICONS) if (iconId >= 0) { - // NOTE: We consider icon height, probably different than text size + // NOTE: Considering icon height, probably different than text size GuiDrawIcon(iconId, (int)textBoundsPosition.x, (int)(textBounds.y + textBounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height)), guiIconScale, tint); textBoundsPosition.x += (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); textBoundsWidthOffset = (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); @@ -5237,8 +5287,8 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C int codepoint = GetCodepointNext(&lines[i][c], &codepointSize); int index = GetGlyphIndex(guiFont, codepoint); - // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) - // but we need to draw all of the bad bytes using the '?' symbol moving one byte + // NOTE: Normally, exiting the decoding sequence as soon as a bad byte is found (and return 0x3f) + // but all of the bad bytes need to be drawn using the '?' symbol, moving one byte if (codepoint == 0x3f) codepointSize = 1; // TODO: Review not recognized codepoints size // Get glyph width to check if it goes out of bounds @@ -5253,7 +5303,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C if ((textOffsetX + glyphWidth) > textBounds.width - textBoundsWidthOffset) { textOffsetX = 0.0f; - textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); + textOffsetY += (GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); if (tempWrapCharMode) // Wrap at char level when too long words { @@ -5282,7 +5332,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C else if ((textOffsetX + nextSpaceWidth) > textBounds.width - textBoundsWidthOffset) { textOffsetX = 0.0f; - textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); + textOffsetY += (GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); } } @@ -5332,7 +5382,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C } } - if (wrapMode == TEXT_WRAP_NONE) posOffsetY += (float)GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); + if (wrapMode == TEXT_WRAP_NONE) posOffsetY += (float)(GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); else if ((wrapMode == TEXT_WRAP_CHAR) || (wrapMode == TEXT_WRAP_WORD)) posOffsetY += (textOffsetY + (float)GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); //--------------------------------------------------------------------------------- } @@ -5374,13 +5424,19 @@ static void GuiTooltip(Rectangle controlRec) if ((controlRec.x + textSize.x + 16) > GetScreenWidth()) controlRec.x -= (textSize.x + 16 - controlRec.width); - GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, NULL); + int lineCount = 0; + GetTextLines(guiTooltipPtr, &lineCount); // Only using the line count + if ((controlRec.y + controlRec.height + textSize.y + 4 + 8*lineCount) > GetScreenHeight()) + controlRec.y -= (controlRec.height + textSize.y + 4 + 8*lineCount); + + // TODO: Probably TEXT_LINE_SPACING should be considered on panel size instead of hardcoding 8.0f + GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, textSize.y + 8.0f*lineCount }, NULL); int textPadding = GuiGetStyle(LABEL, TEXT_PADDING); int textAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); GuiSetStyle(LABEL, TEXT_PADDING, 0); GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); - GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, guiTooltipPtr); + GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, textSize.y + 8.0f*lineCount }, guiTooltipPtr); GuiSetStyle(LABEL, TEXT_ALIGNMENT, textAlignment); GuiSetStyle(LABEL, TEXT_PADDING, textPadding); } @@ -5416,7 +5472,7 @@ static const char **GuiTextSplit(const char *text, char delimiter, int *count, i if (textRow != NULL) textRow[0] = 0; - // Count how many substrings we have on text and point to every one + // Count how many substrings text contains and point to every one of them for (int i = 0; i < RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE; i++) { buffer[i] = text[i]; @@ -5637,11 +5693,11 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON) && + if (GUI_BUTTON_DOWN && !CheckCollisionPointRec(mousePoint, arrowUpLeft) && !CheckCollisionPointRec(mousePoint, arrowDownRight)) { @@ -5664,11 +5720,11 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) state = STATE_FOCUSED; // Handle mouse wheel - int wheel = (int)GetMouseWheelMove(); - if (wheel != 0) value += wheel; + float scrollDelta = GUI_SCROLL_DELTA; + if (scrollDelta != 0) value += (int)scrollDelta; // Handle mouse button down - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_PRESSED) { guiControlExclusiveMode = true; guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts @@ -5690,13 +5746,13 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) /* if (isVertical) { - if (IsKeyDown(KEY_DOWN)) value += 5; - else if (IsKeyDown(KEY_UP)) value -= 5; + if (GUI_KEY_DOWN(KEY_DOWN)) value += 5; + else if (GUI_KEY_DOWN(KEY_UP)) value -= 5; } else { - if (IsKeyDown(KEY_RIGHT)) value += 5; - else if (IsKeyDown(KEY_LEFT)) value -= 5; + if (GUI_KEY_DOWN(KEY_RIGHT)) value += 5; + else if (GUI_KEY_DOWN(KEY_LEFT)) value -= 5; } */ } @@ -5833,7 +5889,7 @@ const char **TextSplit(const char *text, char delimiter, int *count) { counter = 1; - // Count how many substrings we have on text and point to every one + // Count how many substrings text contains and point to every one of them for (int i = 0; i < RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE; i++) { buffer[i] = text[i]; @@ -5866,7 +5922,7 @@ static int TextToInteger(const char *text) text++; } - for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); ++i) value = value*10 + (int)(text[i] - '0'); + for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0'); return value*sign; } @@ -5941,9 +5997,9 @@ static const char *CodepointToUTF8(int codepoint, int *byteSize) } // Get next codepoint in a UTF-8 encoded text, scanning until '\0' is found -// When a invalid UTF-8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned +// When a invalid UTF-8 byte is encountered, exiting as soon as possible and returning a '?'(0x3f) codepoint // Total number of bytes processed are returned as a parameter -// NOTE: the standard says U+FFFD should be returned in case of errors +// NOTE: The standard says U+FFFD should be returned in case of errors // but that character is not supported by the default font in raylib static int GetCodepointNext(const char *text, int *codepointSize) { diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h index 88fe5cc5b..67c16be45 100644 --- a/examples/shapes/raygui.h +++ b/examples/shapes/raygui.h @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raygui v4.5-dev - A simple and easy-to-use immediate-mode gui library +* raygui v5.0-dev - A simple and easy-to-use immediate-mode gui library * * DESCRIPTION: * raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also @@ -83,8 +83,8 @@ * used for all controls, when any of those base values is set, it is automatically populated to all * controls, so, specific control values overwriting generic style should be set after base values * -* After the first BASE set we have the EXTENDED properties (by default guiStyle[16..23]), those -* properties are actually common to all controls and can not be overwritten individually (like BASE ones) +* After the first BASE properties set, the EXTENDED properties set is defined (by default guiStyle[16..23]), +* those properties are actually common to all controls and can not be overwritten individually (like BASE ones) * Some of those properties are: TEXT_SIZE, TEXT_SPACING, LINE_COLOR, BACKGROUND_COLOR * * Custom control properties can be defined using the EXTENDED properties for each independent control. @@ -141,13 +141,14 @@ * Draw text bounds rectangles for debug * * VERSIONS HISTORY: -* 5.0 (xx-Nov-2025) ADDED: Support up to 32 controls (v500) +* 5.0 (xx-Mar-2026) ADDED: Support up to 32 controls (v500) * ADDED: guiControlExclusiveMode and guiControlExclusiveRec for exclusive modes * ADDED: GuiValueBoxFloat() * ADDED: GuiDropdonwBox() properties: DROPDOWN_ARROW_HIDDEN, DROPDOWN_ROLL_UP * ADDED: GuiListView() property: LIST_ITEMS_BORDER_WIDTH * ADDED: GuiLoadIconsFromMemory() * ADDED: Multiple new icons +* ADDED: Macros for inputs customization, raylib decoupling * REMOVED: GuiSpinner() from controls list, using BUTTON + VALUEBOX properties * REMOVED: GuiSliderPro(), functionality was redundant * REVIEWED: Controls using text labels to use LABEL properties @@ -165,6 +166,7 @@ * REVIEWED: GuiTextBox(), multiple improvements: autocursor and more * REVIEWED: Functions descriptions, removed wrong return value reference * REDESIGNED: GuiColorPanel(), improved HSV <-> RGBA convertion +* REDESIGNED: WARNING: TEXT_LINE_SPACING does not consider text height, only lines spacing * * 4.0 (12-Sep-2023) ADDED: GuiToggleSlider() * ADDED: GuiColorPickerHSV() and GuiColorPanelHSV() @@ -316,7 +318,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2025 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2026 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -351,10 +353,11 @@ // NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll #if defined(_WIN32) #if defined(BUILD_LIBTYPE_SHARED) - #define RAYGUIAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll) + #define RAYGUIAPI __declspec(dllexport) // Building the library as a Win32 shared library (.dll) #elif defined(USE_LIBTYPE_SHARED) - #define RAYGUIAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll) + #define RAYGUIAPI __declspec(dllimport) // Using the library as a Win32 shared library (.dll) #endif + #define _CRT_SECURE_NO_WARNINGS // Disable unsafe warnings on scanf() functions in MSVC #endif // Function specifiers definition @@ -369,9 +372,48 @@ // NOTE: Avoiding those calls, also avoids const strings memory usage #define RAYGUI_SUPPORT_LOG_INFO #if defined(RAYGUI_SUPPORT_LOG_INFO) - #define RAYGUI_LOG(...) printf(__VA_ARGS__) + #define RAYGUI_LOG(...) printf(__VA_ARGS__) #else - #define RAYGUI_LOG(...) + #define RAYGUI_LOG(...) +#endif + +// Macros to define required UI inputs, including mapping to gamepad controls +// TODO: Define additionally required macros for missing inputs +#if !defined(GUI_BUTTON_DOWN) + #define GUI_BUTTON_DOWN (IsMouseButtonDown(MOUSE_LEFT_BUTTON) || IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) +#endif +#if !defined(GUI_BUTTON_DOWN_ALT) + // Mapping to alternative button down pressed + #define GUI_BUTTON_DOWN_ALT (IsMouseButtonDown(MOUSE_RIGHT_BUTTON) || IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT)) +#endif +#if !defined(GUI_BUTTON_PRESSED) + #define GUI_BUTTON_PRESSED (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsGamepadButtonPressed(0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) +#endif +// TODO: WARNING: GuiTabBar() still requires IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON) +#if !defined(GUI_BUTTON_RELEASED) + #define GUI_BUTTON_RELEASED (IsMouseButtonReleased(MOUSE_LEFT_BUTTON) || IsGamepadButtonReleased(0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) +#endif +#if !defined(GUI_SCROLL_DELTA) + // Mapping to scroll delta changes + // TODO: Review inconsistencies between platforms + #if defined(PLATFORM_WEB) + // NOTE: Gamepad axis triggers not detected on web platform + #define GUI_SCROLL_DELTA ((float)IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_TRIGGER_2) - (float)IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_TRIGGER_2)) + #else + #define GUI_SCROLL_DELTA (GetMouseWheelMove() + (GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_TRIGGER) + 1) - (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_TRIGGER) + 1)) + #endif +#endif +#if !defined(GUI_POINTER_POSITION) + #define GUI_POINTER_POSITION GetMousePosition() +#endif +#if !defined(GUI_KEY_DOWN) + #define GUI_KEY_DOWN(key) IsKeyDown(key) +#endif +#if !defined(GUI_KEY_PRESSED) + #define GUI_KEY_PRESSED(key) IsKeyPressed(key) +#endif +#if !defined(GUI_INPUT_KEY) + #define GUI_INPUT_KEY GetCharPressed() #endif //---------------------------------------------------------------------------------- @@ -567,7 +609,7 @@ typedef enum { //---------------------------------------------------------------------------------- // DEFAULT extended properties // NOTE: Those properties are common to all controls or global -// WARNING: We only have 8 slots for those properties by default!!! -> New global control: TEXT? +// WARNING: Only 8 slots vailable for those properties by default typedef enum { TEXT_SIZE = 16, // Text size (glyphs max height) TEXT_SPACING, // Text spacing between glyphs @@ -1091,7 +1133,7 @@ typedef enum { // Icons data is defined by bit array (every bit represents one pixel) // Those arrays are stored as unsigned int data arrays, so, // every array element defines 32 pixels (bits) of information -// One icon is defined by 8 int, (8 int * 32 bit = 256 bit = 16*16 pixels) +// One icon is defined by 8 int, (8 int*32 bit = 256 bit = 16*16 pixels) // NOTE: Number of elemens depend on RAYGUI_ICON_SIZE (by default 16x16 pixels) #define RAYGUI_ICON_DATA_ELEMENTS (RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32) @@ -1450,12 +1492,12 @@ static bool IsMouseButtonReleased(int button); static bool IsKeyDown(int key); static bool IsKeyPressed(int key); -static int GetCharPressed(void); // -- GuiTextBox(), GuiValueBox() +static int GetCharPressed(void); // -- GuiTextBox(), GuiValueBox() //------------------------------------------------------------------------------- // Drawing required functions //------------------------------------------------------------------------------- -static void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle() +static void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle() static void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker() //------------------------------------------------------------------------------- @@ -1520,11 +1562,11 @@ static Color GuiFade(Color color, float alpha); // Fade color by an alph // Gui Setup Functions Definition //---------------------------------------------------------------------------------- // Enable gui global state -// NOTE: We check for STATE_DISABLED to avoid messing custom global state setups +// NOTE: Checking for STATE_DISABLED to avoid messing custom global state setups void GuiEnable(void) { if (guiState == STATE_DISABLED) guiState = STATE_NORMAL; } // Disable gui global state -// NOTE: We check for STATE_NORMAL to avoid messing custom global state setups +// NOTE: Checking for STATE_NORMAL to avoid messing custom global state setups void GuiDisable(void) { if (guiState == STATE_NORMAL) guiState = STATE_DISABLED; } // Lock gui global state @@ -1557,9 +1599,8 @@ void GuiSetFont(Font font) { if (font.texture.id > 0) { - // NOTE: If we try to setup a font but default style has not been - // lazily loaded before, it will be overwritten, so we need to force - // default style loading first + // NOTE: If a font is tried to be set but default style has not been lazily loaded first, + // it will be overwritten, so default style loading needs to be forced first if (!guiStyleLoaded) GuiLoadStyleDefault(); guiFont = font; @@ -1613,13 +1654,14 @@ int GuiWindowBox(Rectangle bounds, const char *title) //GuiState state = guiState; int statusBarHeight = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT; + int statusBorderWidth = GuiGetStyle(STATUSBAR, BORDER_WIDTH); Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)statusBarHeight }; if (bounds.height < statusBarHeight*2.0f) bounds.height = statusBarHeight*2.0f; const float vPadding = statusBarHeight/2.0f - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT/2.0f; - Rectangle windowPanel = { bounds.x, bounds.y + (float)statusBarHeight - 1, bounds.width, bounds.height - (float)statusBarHeight + 1 }; - Rectangle closeButtonRec = { statusBar.x + statusBar.width - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT - vPadding, + Rectangle windowPanel = { bounds.x, bounds.y + (float)statusBarHeight - (float)statusBorderWidth, bounds.width, bounds.height - (float)statusBarHeight + (float)statusBorderWidth }; + Rectangle closeButtonRec = { statusBar.x + statusBar.width - (float)statusBorderWidth - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT - vPadding, statusBar.y + vPadding, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT }; // Update control @@ -1629,8 +1671,8 @@ int GuiWindowBox(Rectangle bounds, const char *title) // Draw control //-------------------------------------------------------------------- - GuiStatusBar(statusBar, title); // Draw window header as status bar GuiPanel(windowPanel, NULL); // Draw window base + GuiStatusBar(statusBar, title); // Draw window header as status bar // Draw window close button int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); @@ -1786,7 +1828,7 @@ int GuiTabBar(Rectangle bounds, const char **text, int count, int *active) } // Close tab with middle mouse button pressed - if (CheckCollisionPointRec(GetMousePosition(), tabBounds) && IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) result = i; + if (CheckCollisionPointRec(GUI_POINTER_POSITION, tabBounds) && IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) result = i; GuiSetStyle(TOGGLE, TEXT_PADDING, textPadding); GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, textAlignment); @@ -1885,37 +1927,37 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check button state if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; #if defined(SUPPORT_SCROLLBAR_KEY_INPUT) if (hasHorizontalScrollBar) { - if (IsKeyDown(KEY_RIGHT)) scrollPos.x -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); - if (IsKeyDown(KEY_LEFT)) scrollPos.x += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (GUI_KEY_DOWN(KEY_RIGHT)) scrollPos.x -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (GUI_KEY_DOWN(KEY_LEFT)) scrollPos.x += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); } if (hasVerticalScrollBar) { - if (IsKeyDown(KEY_DOWN)) scrollPos.y -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); - if (IsKeyDown(KEY_UP)) scrollPos.y += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (GUI_KEY_DOWN(KEY_DOWN)) scrollPos.y -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (GUI_KEY_DOWN(KEY_UP)) scrollPos.y += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); } #endif - float wheelMove = GetMouseWheelMove(); + float scrollDelta = GUI_SCROLL_DELTA; // Set scrolling speed with mouse wheel based on ratio between bounds and content - Vector2 mouseWheelSpeed = { content.width/bounds.width, content.height/bounds.height }; - if (mouseWheelSpeed.x < RAYGUI_MIN_MOUSE_WHEEL_SPEED) mouseWheelSpeed.x = RAYGUI_MIN_MOUSE_WHEEL_SPEED; - if (mouseWheelSpeed.y < RAYGUI_MIN_MOUSE_WHEEL_SPEED) mouseWheelSpeed.y = RAYGUI_MIN_MOUSE_WHEEL_SPEED; + Vector2 scrollSpeed = { content.width/bounds.width, content.height/bounds.height }; + if (scrollSpeed.x < RAYGUI_MIN_MOUSE_WHEEL_SPEED) scrollSpeed.x = RAYGUI_MIN_MOUSE_WHEEL_SPEED; + if (scrollSpeed.y < RAYGUI_MIN_MOUSE_WHEEL_SPEED) scrollSpeed.y = RAYGUI_MIN_MOUSE_WHEEL_SPEED; // Horizontal and vertical scrolling with mouse wheel - if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*mouseWheelSpeed.x; - else scrollPos.y += wheelMove*mouseWheelSpeed.y; // Vertical scroll + if (hasHorizontalScrollBar && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_LEFT_SHIFT))) scrollPos.x += scrollDelta*scrollSpeed.x; + else scrollPos.y += scrollDelta*scrollSpeed.y; // Vertical scroll } } @@ -2001,15 +2043,15 @@ int GuiButton(Rectangle bounds, const char *text) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check button state if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) result = 1; + if (GUI_BUTTON_RELEASED) result = 1; } } //-------------------------------------------------------------------- @@ -2031,7 +2073,7 @@ int GuiLabelButton(Rectangle bounds, const char *text) GuiState state = guiState; bool pressed = false; - // NOTE: We force bounds.width to be all text + // NOTE: Force bounds.width to be all text float textWidth = (float)GuiGetTextWidth(text); if ((bounds.width - 2*GuiGetStyle(LABEL, BORDER_WIDTH) - 2*GuiGetStyle(LABEL, TEXT_PADDING)) < textWidth) bounds.width = textWidth + 2*GuiGetStyle(LABEL, BORDER_WIDTH) + 2*GuiGetStyle(LABEL, TEXT_PADDING) + 2; @@ -2039,15 +2081,15 @@ int GuiLabelButton(Rectangle bounds, const char *text) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check checkbox state if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true; + if (GUI_BUTTON_RELEASED) pressed = true; } } //-------------------------------------------------------------------- @@ -2073,13 +2115,13 @@ int GuiToggle(Rectangle bounds, const char *text, bool *active) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check toggle button state if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; - else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else if (GUI_BUTTON_RELEASED) { state = STATE_NORMAL; *active = !(*active); @@ -2184,12 +2226,12 @@ int GuiToggleSlider(Rectangle bounds, const char *text, int *active) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; - else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else if (GUI_BUTTON_RELEASED) { state = STATE_PRESSED; (*active)++; @@ -2255,7 +2297,7 @@ int GuiCheckBox(Rectangle bounds, const char *text, bool *checked) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; Rectangle totalBounds = { (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT)? textBounds.x : bounds.x, @@ -2267,10 +2309,10 @@ int GuiCheckBox(Rectangle bounds, const char *text, bool *checked) // Check checkbox state if (CheckCollisionPointRec(mousePoint, totalBounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_RELEASED) { *checked = !(*checked); result = 1; @@ -2323,18 +2365,18 @@ int GuiComboBox(Rectangle bounds, const char *text, int *active) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1) && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) { - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_PRESSED) { *active += 1; if (*active >= itemCount) *active = 0; // Cyclic combobox } - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; } } @@ -2392,7 +2434,7 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1) && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (editMode) { @@ -2401,11 +2443,11 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod // Check if mouse has been pressed or released outside limits if (!CheckCollisionPointRec(mousePoint, boundsOpen)) { - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) result = 1; + if (GUI_BUTTON_PRESSED || GUI_BUTTON_RELEASED) result = 1; } // Check if already selected item has been pressed again - if (CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1; + if (CheckCollisionPointRec(mousePoint, bounds) && GUI_BUTTON_PRESSED) result = 1; // Check focused and selected item for (int i = 0; i < itemCount; i++) @@ -2417,7 +2459,7 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod if (CheckCollisionPointRec(mousePoint, itemBounds)) { itemFocused = i; - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_RELEASED) { itemSelected = i; result = 1; // Item selected @@ -2432,7 +2474,7 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod { if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_PRESSED) { result = 1; state = STATE_PRESSED; @@ -2506,7 +2548,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) int result = 0; GuiState state = guiState; - bool multiline = false; // TODO: Consider multiline text input + bool multiline = false; // TODO: Consider multiline text input int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); Rectangle textBounds = GetTextBounds(TEXTBOX, bounds); @@ -2514,7 +2556,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) int thisCursorIndex = textBoxCursorIndex; if (thisCursorIndex > textLength) thisCursorIndex = textLength; int textWidth = GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex); - int textIndexOffset = 0; // Text index offset to start drawing in the box + int textIndexOffset = 0; // Text index offset to start drawing in the box // Cursor rectangle // NOTE: Position X value should be updated @@ -2547,13 +2589,13 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) !guiControlExclusiveMode && // No gui slider on dragging (wrapMode == TEXT_WRAP_NONE)) // No wrap mode { - Vector2 mousePosition = GetMousePosition(); + Vector2 mousePosition = GUI_POINTER_POSITION; if (editMode) { // GLOBAL: Auto-cursor movement logic // NOTE: Keystrokes are handled repeatedly when button is held down for some time - if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCounter++; + if (GUI_KEY_DOWN(KEY_LEFT) || GUI_KEY_DOWN(KEY_RIGHT) || GUI_KEY_DOWN(KEY_UP) || GUI_KEY_DOWN(KEY_DOWN) || GUI_KEY_DOWN(KEY_BACKSPACE) || GUI_KEY_DOWN(KEY_DELETE)) autoCursorCounter++; else autoCursorCounter = 0; bool autoCursorShouldTrigger = (autoCursorCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) && ((autoCursorCounter % RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0); @@ -2563,7 +2605,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength; // If text does not fit in the textbox and current cursor position is out of bounds, - // we add an index offset to text for drawing only what requires depending on cursor + // adding an index offset to text for drawing only what requires depending on cursor while (textWidth >= textBounds.width) { int nextCodepointSize = 0; @@ -2574,15 +2616,15 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) textWidth = GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex); } - int codepoint = GetCharPressed(); // Get Unicode codepoint - if (multiline && IsKeyPressed(KEY_ENTER)) codepoint = (int)'\n'; + int codepoint = GUI_INPUT_KEY; // Get Unicode codepoint + if (multiline && GUI_KEY_PRESSED(KEY_ENTER)) codepoint = (int)'\n'; // Encode codepoint as UTF-8 int codepointSize = 0; const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize); // Handle text paste action - if (IsKeyPressed(KEY_V) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) + if (GUI_KEY_PRESSED(KEY_V) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) { const char *pasteText = GetClipboardText(); if (pasteText != NULL) @@ -2632,13 +2674,13 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) } // Move cursor to start - if ((textLength > 0) && IsKeyPressed(KEY_HOME)) textBoxCursorIndex = 0; + if ((textLength > 0) && GUI_KEY_PRESSED(KEY_HOME)) textBoxCursorIndex = 0; // Move cursor to end - if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) textBoxCursorIndex = textLength; + if ((textLength > textBoxCursorIndex) && GUI_KEY_PRESSED(KEY_END)) textBoxCursorIndex = textLength; // Delete related codepoints from text, after current cursor position - if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_DELETE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) + if ((textLength > textBoxCursorIndex) && GUI_KEY_PRESSED(KEY_DELETE) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) { int offset = textBoxCursorIndex; int accCodepointSize = 0; @@ -2674,7 +2716,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) textLength -= accCodepointSize; } - else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && autoCursorShouldTrigger))) + else if ((textLength > textBoxCursorIndex) && (GUI_KEY_PRESSED(KEY_DELETE) || (GUI_KEY_DOWN(KEY_DELETE) && autoCursorShouldTrigger))) { // Delete single codepoint from text, after current cursor position @@ -2688,12 +2730,12 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) } // Delete related codepoints from text, before current cursor position - if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_BACKSPACE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) + if ((textBoxCursorIndex > 0) && GUI_KEY_PRESSED(KEY_BACKSPACE) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) { int offset = textBoxCursorIndex; int accCodepointSize = 0; - int prevCodepointSize; - int prevCodepoint; + int prevCodepointSize = 0; + int prevCodepoint = 0; // Check whitespace to delete (ASCII only) while (offset > 0) @@ -2724,7 +2766,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) textBoxCursorIndex -= accCodepointSize; } - else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && autoCursorShouldTrigger))) + else if ((textBoxCursorIndex > 0) && (GUI_KEY_PRESSED(KEY_BACKSPACE) || (GUI_KEY_DOWN(KEY_BACKSPACE) && autoCursorShouldTrigger))) { // Delete single codepoint from text, before current cursor position @@ -2740,12 +2782,12 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) } // Move cursor position with keys - if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_LEFT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) + if ((textBoxCursorIndex > 0) && GUI_KEY_PRESSED(KEY_LEFT) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) { int offset = textBoxCursorIndex; //int accCodepointSize = 0; - int prevCodepointSize; - int prevCodepoint; + int prevCodepointSize = 0; + int prevCodepoint = 0; // Check whitespace to skip (ASCII only) while (offset > 0) @@ -2771,14 +2813,14 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) textBoxCursorIndex = offset; } - else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && autoCursorShouldTrigger))) + else if ((textBoxCursorIndex > 0) && (GUI_KEY_PRESSED(KEY_LEFT) || (GUI_KEY_DOWN(KEY_LEFT) && autoCursorShouldTrigger))) { int prevCodepointSize = 0; GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize); textBoxCursorIndex -= prevCodepointSize; } - else if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_RIGHT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) + else if ((textLength > textBoxCursorIndex) && GUI_KEY_PRESSED(KEY_RIGHT) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) { int offset = textBoxCursorIndex; //int accCodepointSize = 0; @@ -2810,7 +2852,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) textBoxCursorIndex = offset; } - else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && autoCursorShouldTrigger))) + else if ((textLength > textBoxCursorIndex) && (GUI_KEY_PRESSED(KEY_RIGHT) || (GUI_KEY_DOWN(KEY_RIGHT) && autoCursorShouldTrigger))) { int nextCodepointSize = 0; GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize); @@ -2847,14 +2889,14 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) // Check if mouse cursor is at the last position int textEndWidth = GuiGetTextWidth(text + textIndexOffset); - if (GetMousePosition().x >= (textBounds.x + textEndWidth - glyphWidth/2)) + if (GUI_POINTER_POSITION.x >= (textBounds.x + textEndWidth - glyphWidth/2)) { mouseCursor.x = textBounds.x + textEndWidth; mouseCursorIndex = textLength; } // Place cursor at required index on mouse click - if ((mouseCursor.x >= 0) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + if ((mouseCursor.x >= 0) && GUI_BUTTON_PRESSED) { cursor.x = mouseCursor.x; textBoxCursorIndex = mouseCursorIndex; @@ -2867,8 +2909,8 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) //if (multiline) cursor.y = GetTextLines() // Finish text editing on ENTER or mouse click outside bounds - if ((!multiline && IsKeyPressed(KEY_ENTER)) || - (!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) + if ((!multiline && GUI_KEY_PRESSED(KEY_ENTER)) || + (!CheckCollisionPointRec(mousePosition, bounds) && GUI_BUTTON_PRESSED)) { textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes @@ -2881,7 +2923,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) { state = STATE_FOCUSED; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_PRESSED) { textBoxCursorIndex = textLength; // GLOBAL: Place cursor index to the end of current text autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes @@ -2975,12 +3017,12 @@ int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check spinner state if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; } } @@ -3050,7 +3092,7 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; bool valueHasChanged = false; if (editMode) @@ -3060,7 +3102,7 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in int keyCount = (int)strlen(textValue); // Add or remove minus symbol - if (IsKeyPressed(KEY_MINUS)) + if (GUI_KEY_PRESSED(KEY_MINUS)) { if (textValue[0] == '-') { @@ -3089,8 +3131,8 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in // Add new digit to text value if ((keyCount >= 0) && (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) && (GuiGetTextWidth(textValue) < bounds.width)) { - int key = GetCharPressed(); - + int key = GUI_INPUT_KEY; + // Only allow keys in range [48..57] if ((key >= 48) && (key <= 57)) { @@ -3101,7 +3143,7 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in } // Delete text - if ((keyCount > 0) && IsKeyPressed(KEY_BACKSPACE)) + if ((keyCount > 0) && GUI_KEY_PRESSED(KEY_BACKSPACE)) { keyCount--; textValue[keyCount] = '\0'; @@ -3110,11 +3152,11 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in if (valueHasChanged) *value = TextToInteger(textValue); - // NOTE: We are not clamp values until user input finishes + // NOTE: Values are not clamped until user input finishes //if (*value > maxValue) *value = maxValue; //else if (*value < minValue) *value = minValue; - if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) + if ((GUI_KEY_PRESSED(KEY_ENTER) || GUI_KEY_PRESSED(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && GUI_BUTTON_PRESSED)) { if (*value > maxValue) *value = maxValue; else if (*value < minValue) *value = minValue; @@ -3130,7 +3172,7 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in if (CheckCollisionPointRec(mousePoint, bounds)) { state = STATE_FOCUSED; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1; + if (GUI_BUTTON_PRESSED) result = 1; } } } @@ -3191,7 +3233,7 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; bool valueHasChanged = false; @@ -3202,7 +3244,7 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float int keyCount = (int)strlen(textValue); // Add or remove minus symbol - if (IsKeyPressed(KEY_MINUS)) + if (GUI_KEY_PRESSED(KEY_MINUS)) { if (textValue[0] == '-') { @@ -3233,7 +3275,7 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float { if (GuiGetTextWidth(textValue) < bounds.width) { - int key = GetCharPressed(); + int key = GUI_INPUT_KEY; if (((key >= 48) && (key <= 57)) || (key == '.') || ((keyCount == 0) && (key == '+')) || // NOTE: Sign can only be in first position @@ -3248,7 +3290,7 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float } // Pressed backspace - if (IsKeyPressed(KEY_BACKSPACE)) + if (GUI_KEY_PRESSED(KEY_BACKSPACE)) { if (keyCount > 0) { @@ -3260,14 +3302,14 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float if (valueHasChanged) *value = TextToFloat(textValue); - if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) result = 1; + if ((GUI_KEY_PRESSED(KEY_ENTER) || GUI_KEY_PRESSED(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && GUI_BUTTON_PRESSED)) result = 1; } else { if (CheckCollisionPointRec(mousePoint, bounds)) { state = STATE_FOCUSED; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1; + if (GUI_BUTTON_PRESSED) result = 1; } } } @@ -3321,11 +3363,11 @@ int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, flo //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) { @@ -3342,7 +3384,7 @@ int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, flo } else if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { state = STATE_PRESSED; guiControlExclusiveMode = true; @@ -3535,12 +3577,12 @@ int GuiDummyRec(Rectangle bounds, const char *text) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check button state if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; else state = STATE_FOCUSED; } } @@ -3578,7 +3620,7 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd int itemFocused = (focus == NULL)? -1 : *focus; int itemSelected = (active == NULL)? -1 : *active; - // Check if we need a scroll bar + // Check if scroll bar is needed bool useScrollBar = false; if ((GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING))*count > bounds.height) useScrollBar = true; @@ -3602,7 +3644,7 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; // Check mouse inside list view if (CheckCollisionPointRec(mousePoint, bounds)) @@ -3615,7 +3657,7 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd if (CheckCollisionPointRec(mousePoint, itemBounds)) { itemFocused = startIndex + i; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_PRESSED) { if (itemSelected == (startIndex + i)) itemSelected = -1; else itemSelected = startIndex + i; @@ -3629,8 +3671,8 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd if (useScrollBar) { - int wheelMove = (int)GetMouseWheelMove(); - startIndex -= wheelMove; + float scrollDelta = GUI_SCROLL_DELTA; + startIndex -= (int)scrollDelta; if (startIndex < 0) startIndex = 0; else if (startIndex > (count - visibleItems)) startIndex = count - visibleItems; @@ -3659,7 +3701,7 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd { if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED))); - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED))); + GuiDrawText(text[startIndex + i], GetTextBounds(LISTVIEW, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED))); } else { @@ -3667,18 +3709,18 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd { // Draw item selected GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED))); - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED))); + GuiDrawText(text[startIndex + i], GetTextBounds(LISTVIEW, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED))); } - else if (((startIndex + i) == itemFocused)) // && (focus != NULL)) // NOTE: We want items focused, despite not returned! + else if (((startIndex + i) == itemFocused)) // && (focus != NULL)) // NOTE: Items focused, despite not returned { // Draw item focused GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED))); - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED))); + GuiDrawText(text[startIndex + i], GetTextBounds(LISTVIEW, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED))); } else { // Draw item normal (no rectangle) - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL))); + GuiDrawText(text[startIndex + i], GetTextBounds(LISTVIEW, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL))); } } @@ -3765,11 +3807,11 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) { @@ -3788,7 +3830,7 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) } else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { state = STATE_PRESSED; guiControlExclusiveMode = true; @@ -3850,11 +3892,11 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) { @@ -3873,7 +3915,7 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) } else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { state = STATE_PRESSED; guiControlExclusiveMode = true; @@ -3886,12 +3928,12 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) } else state = STATE_FOCUSED; - /*if (IsKeyDown(KEY_UP)) + /*if (GUI_KEY_DOWN(KEY_UP)) { hue -= 2.0f; if (hue <= 0.0f) hue = 0.0f; } - else if (IsKeyDown(KEY_DOWN)) + else if (GUI_KEY_DOWN(KEY_DOWN)) { hue += 2.0f; if (hue >= 360.0f) hue = 360.0f; @@ -4008,11 +4050,11 @@ int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) { @@ -4042,7 +4084,7 @@ int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) } else if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_DOWN) { state = STATE_PRESSED; guiControlExclusiveMode = true; @@ -4198,6 +4240,9 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment); } + int prevTextBoxAlignment = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT); + GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + if (secretViewActive != NULL) { static char stars[] = "****************"; @@ -4211,6 +4256,8 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co if (GuiTextBox(textBoxBounds, text, textMaxSize, textEditMode)) textEditMode = !textEditMode; } + GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, prevTextBoxAlignment); + int prevBtnTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); @@ -4231,7 +4278,7 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co // Grid control // NOTE: Returns grid mouse-hover selected cell // About drawing lines at subpixel spacing, simple put, not easy solution: -// https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster +// REF: https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell) { // Grid lines alpha amount @@ -4242,7 +4289,7 @@ int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vect int result = 0; GuiState state = guiState; - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; Vector2 currentMouseCell = { -1, -1 }; float spaceWidth = spacing/(float)subdivs; @@ -4410,11 +4457,14 @@ void GuiLoadStyle(const char *fileName) if (fileDataSize > 0) { unsigned char *fileData = (unsigned char *)RAYGUI_CALLOC(fileDataSize, sizeof(unsigned char)); - fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile); + if (fileData != NULL) + { + fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile); - GuiLoadStyleFromMemory(fileData, fileDataSize); + GuiLoadStyleFromMemory(fileData, fileDataSize); - RAYGUI_FREE(fileData); + RAYGUI_FREE(fileData); + } } fclose(rgsFile); @@ -4425,7 +4475,7 @@ void GuiLoadStyle(const char *fileName) // Load style default over global style void GuiLoadStyleDefault(void) { - // We set this variable first to avoid cyclic function calls + // Setting this flag first to avoid cyclic function calls // when calling GuiSetStyle() and GuiGetStyle() guiStyleLoaded = true; @@ -4454,7 +4504,7 @@ void GuiLoadStyleDefault(void) GuiSetStyle(DEFAULT, TEXT_SPACING, 1); // DEFAULT, shared by all controls GuiSetStyle(DEFAULT, LINE_COLOR, 0x90abb5ff); // DEFAULT specific property GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0xf5f5f5ff); // DEFAULT specific property - GuiSetStyle(DEFAULT, TEXT_LINE_SPACING, 15); // DEFAULT, 15 pixels between lines + GuiSetStyle(DEFAULT, TEXT_LINE_SPACING, 5); // DEFAULT, pixels between lines, from bottom of first line to top of second GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE); // DEFAULT, text aligned vertically to middle of text-bounds // Initialize control-specific property values @@ -4520,7 +4570,7 @@ void GuiLoadStyleDefault(void) // NOTE: Default raylib font character 95 is a white square Rectangle whiteChar = guiFont.recs[95]; - // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering + // NOTE: Setting up a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering SetShapesTexture(guiFont.texture, RAYGUI_CLITERAL(Rectangle){ whiteChar.x + 1, whiteChar.y + 1, whiteChar.width - 2, whiteChar.height - 2 }); } } @@ -5037,14 +5087,14 @@ static Rectangle GetTextBounds(int control, Rectangle bounds) } // Get text icon if provided and move text cursor -// NOTE: We support up to 999 values for iconId +// NOTE: Up to #999# values supported for iconId static const char *GetTextIcon(const char *text, int *iconId) { #if !defined(RAYGUI_NO_ICONS) *iconId = -1; - if (text[0] == '#') // Maybe we have an icon! + if (text[0] == '#') // Maybe it is stars with an icon, ending # must be found { - char iconValue[4] = { 0 }; // Maximum length for icon value: 3 digits + '\0' + char iconValue[4] = { 0 }; // Maximum length for icon value: 3 digits + '\0' int pos = 1; while ((pos < 4) && (text[pos] >= '0') && (text[pos] <= '9')) @@ -5076,12 +5126,12 @@ static const char **GetTextLines(const char *text, int *count) static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings - int textSize = (int)strlen(text); + int textLength = (int)strlen(text); lines[0] = text; *count = 1; - for (int i = 0, k = 0; (i < textSize) && (*count < RAYGUI_MAX_TEXT_LINES); i++) + for (int i = 0, k = 0; (i < textLength) && (*count < RAYGUI_MAX_TEXT_LINES); i++) { if (text[i] == '\n') { @@ -5141,7 +5191,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C // - For every line, wordwrap mode is checked (useful for GuitextBox(), read-only) // Get text lines (using '\n' as delimiter) to be processed individually - // WARNING: We can't use GuiTextSplit() function because it can be already used + // WARNING: GuiTextSplit() function can't be used now because it can have already been used // before the GuiDrawText() call and its buffer is static, it would be overriden :( int lineCount = 0; const char **lines = GetTextLines(text, &lineCount); @@ -5152,7 +5202,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); // Wrap-mode only available in read-only mode, no for text editing // TODO: WARNING: This totalHeight is not valid for vertical alignment in case of word-wrap - float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_SIZE)/2); + float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); float posOffsetY = 0.0f; for (int i = 0; i < lineCount; i++) @@ -5165,7 +5215,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C Vector2 textBoundsPosition = { textBounds.x, textBounds.y }; float textBoundsWidthOffset = 0.0f; - // NOTE: We get text size after icon has been processed + // NOTE: Get text size after icon has been processed // WARNING: GuiGetTextWidth() also processes text icon to get width! -> Really needed? int textSizeX = GuiGetTextWidth(lines[i]); @@ -5200,8 +5250,8 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C default: break; } - // NOTE: Make sure we get pixel-perfect coordinates, - // In case of decimals we got weird text positioning + // NOTE: Make sure getting pixel-perfect coordinates, + // In case of decimals, it could result in text positioning artifacts textBoundsPosition.x = (float)((int)textBoundsPosition.x); textBoundsPosition.y = (float)((int)textBoundsPosition.y); //--------------------------------------------------------------------------------- @@ -5211,7 +5261,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C #if !defined(RAYGUI_NO_ICONS) if (iconId >= 0) { - // NOTE: We consider icon height, probably different than text size + // NOTE: Considering icon height, probably different than text size GuiDrawIcon(iconId, (int)textBoundsPosition.x, (int)(textBounds.y + textBounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height)), guiIconScale, tint); textBoundsPosition.x += (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); textBoundsWidthOffset = (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); @@ -5237,8 +5287,8 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C int codepoint = GetCodepointNext(&lines[i][c], &codepointSize); int index = GetGlyphIndex(guiFont, codepoint); - // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) - // but we need to draw all of the bad bytes using the '?' symbol moving one byte + // NOTE: Normally, exiting the decoding sequence as soon as a bad byte is found (and return 0x3f) + // but all of the bad bytes need to be drawn using the '?' symbol, moving one byte if (codepoint == 0x3f) codepointSize = 1; // TODO: Review not recognized codepoints size // Get glyph width to check if it goes out of bounds @@ -5253,7 +5303,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C if ((textOffsetX + glyphWidth) > textBounds.width - textBoundsWidthOffset) { textOffsetX = 0.0f; - textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); + textOffsetY += (GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); if (tempWrapCharMode) // Wrap at char level when too long words { @@ -5282,7 +5332,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C else if ((textOffsetX + nextSpaceWidth) > textBounds.width - textBoundsWidthOffset) { textOffsetX = 0.0f; - textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); + textOffsetY += (GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); } } @@ -5332,7 +5382,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C } } - if (wrapMode == TEXT_WRAP_NONE) posOffsetY += (float)GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); + if (wrapMode == TEXT_WRAP_NONE) posOffsetY += (float)(GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); else if ((wrapMode == TEXT_WRAP_CHAR) || (wrapMode == TEXT_WRAP_WORD)) posOffsetY += (textOffsetY + (float)GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); //--------------------------------------------------------------------------------- } @@ -5374,13 +5424,19 @@ static void GuiTooltip(Rectangle controlRec) if ((controlRec.x + textSize.x + 16) > GetScreenWidth()) controlRec.x -= (textSize.x + 16 - controlRec.width); - GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, NULL); + int lineCount = 0; + GetTextLines(guiTooltipPtr, &lineCount); // Only using the line count + if ((controlRec.y + controlRec.height + textSize.y + 4 + 8*lineCount) > GetScreenHeight()) + controlRec.y -= (controlRec.height + textSize.y + 4 + 8*lineCount); + + // TODO: Probably TEXT_LINE_SPACING should be considered on panel size instead of hardcoding 8.0f + GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, textSize.y + 8.0f*lineCount }, NULL); int textPadding = GuiGetStyle(LABEL, TEXT_PADDING); int textAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); GuiSetStyle(LABEL, TEXT_PADDING, 0); GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); - GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, guiTooltipPtr); + GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, textSize.y + 8.0f*lineCount }, guiTooltipPtr); GuiSetStyle(LABEL, TEXT_ALIGNMENT, textAlignment); GuiSetStyle(LABEL, TEXT_PADDING, textPadding); } @@ -5416,7 +5472,7 @@ static const char **GuiTextSplit(const char *text, char delimiter, int *count, i if (textRow != NULL) textRow[0] = 0; - // Count how many substrings we have on text and point to every one + // Count how many substrings text contains and point to every one of them for (int i = 0; i < RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE; i++) { buffer[i] = text[i]; @@ -5637,11 +5693,11 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePoint = GUI_POINTER_POSITION; if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON) && + if (GUI_BUTTON_DOWN && !CheckCollisionPointRec(mousePoint, arrowUpLeft) && !CheckCollisionPointRec(mousePoint, arrowDownRight)) { @@ -5664,11 +5720,11 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) state = STATE_FOCUSED; // Handle mouse wheel - int wheel = (int)GetMouseWheelMove(); - if (wheel != 0) value += wheel; + float scrollDelta = GUI_SCROLL_DELTA; + if (scrollDelta != 0) value += (int)scrollDelta; // Handle mouse button down - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + if (GUI_BUTTON_PRESSED) { guiControlExclusiveMode = true; guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts @@ -5690,13 +5746,13 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) /* if (isVertical) { - if (IsKeyDown(KEY_DOWN)) value += 5; - else if (IsKeyDown(KEY_UP)) value -= 5; + if (GUI_KEY_DOWN(KEY_DOWN)) value += 5; + else if (GUI_KEY_DOWN(KEY_UP)) value -= 5; } else { - if (IsKeyDown(KEY_RIGHT)) value += 5; - else if (IsKeyDown(KEY_LEFT)) value -= 5; + if (GUI_KEY_DOWN(KEY_RIGHT)) value += 5; + else if (GUI_KEY_DOWN(KEY_LEFT)) value -= 5; } */ } @@ -5833,7 +5889,7 @@ const char **TextSplit(const char *text, char delimiter, int *count) { counter = 1; - // Count how many substrings we have on text and point to every one + // Count how many substrings text contains and point to every one of them for (int i = 0; i < RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE; i++) { buffer[i] = text[i]; @@ -5866,7 +5922,7 @@ static int TextToInteger(const char *text) text++; } - for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); ++i) value = value*10 + (int)(text[i] - '0'); + for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0'); return value*sign; } @@ -5941,9 +5997,9 @@ static const char *CodepointToUTF8(int codepoint, int *byteSize) } // Get next codepoint in a UTF-8 encoded text, scanning until '\0' is found -// When a invalid UTF-8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned +// When a invalid UTF-8 byte is encountered, exiting as soon as possible and returning a '?'(0x3f) codepoint // Total number of bytes processed are returned as a parameter -// NOTE: the standard says U+FFFD should be returned in case of errors +// NOTE: The standard says U+FFFD should be returned in case of errors // but that character is not supported by the default font in raylib static int GetCodepointNext(const char *text, int *codepointSize) { From 872cfae7ca4eb28d48201fd1478ea6235ecf1e6d Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Feb 2026 16:51:50 +0100 Subject: [PATCH 027/319] REVIEWED: `LoadDirectoryFilesEx()`, minor tweak #5569 --- examples/core/core_directory_files.c | 5 +++-- src/rcore.c | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/core/core_directory_files.c b/examples/core/core_directory_files.c index 5bc7b7e57..f879b933e 100644 --- a/examples/core/core_directory_files.c +++ b/examples/core/core_directory_files.c @@ -40,7 +40,8 @@ int main(void) // Load file-paths on current working directory // NOTE: LoadDirectoryFiles() loads files and directories by default, // use LoadDirectoryFilesEx() for custom filters and recursive directories loading - FilePathList files = LoadDirectoryFiles(directory); + //FilePathList files = LoadDirectoryFiles(directory); + FilePathList files = LoadDirectoryFilesEx(directory, ".png;.c", false); int btnBackPressed = false; @@ -77,7 +78,7 @@ int main(void) GuiSetStyle(LISTVIEW, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); GuiSetStyle(LISTVIEW, TEXT_PADDING, 40); - GuiListViewEx((Rectangle){ 0, 50, GetScreenWidth(), GetScreenHeight() - 40 }, + GuiListViewEx((Rectangle){ 0, 50, GetScreenWidth(), GetScreenHeight() - 50 }, files.paths, files.count, &listScrollIndex, &listItemActive, &listItemFocused); /* diff --git a/src/rcore.c b/src/rcore.c index 97a6cce0e..c29fb1e19 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2773,6 +2773,8 @@ FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool if (DirectoryExists(basePath)) // It's a directory { + if ((filter != NULL) && (filter[0] == '\0')) filter = NULL; + // SCAN 1: Count files unsigned int fileCounter = GetDirectoryFileCountEx(basePath, filter, scanSubdirs); From e8ce00dc0bf1a64e6d2ff191c91c2f17aee2a378 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Feb 2026 17:02:19 +0100 Subject: [PATCH 028/319] REVIEWED: `LoadGLTF()`, log warning about draco compression not supported #5567 --- src/rmodels.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index eadc16f07..0ee103212 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5373,6 +5373,7 @@ static Model LoadGLTF(const char *fileName) if (result != cgltf_result_success) TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load mesh/material buffers", fileName); int primitivesCount = 0; + bool dracoCompression = false; // NOTE: We will load every primitive in the glTF as a separate raylib Mesh // Determine total number of meshes needed from the node hierarchy @@ -5384,9 +5385,22 @@ static Model LoadGLTF(const char *fileName) for (unsigned int p = 0; p < mesh->primitives_count; p++) { - if (mesh->primitives[p].type == cgltf_primitive_type_triangles) primitivesCount++; + if (mesh->primitives[p].has_draco_mesh_compression) + { + dracoCompression = true; + TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load mesh data, Draco compression not supported", fileName); + break; + } + else if (mesh->primitives[p].type == cgltf_primitive_type_triangles) primitivesCount++; } } + + if (dracoCompression) + { + return model; + TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load glTF data", fileName); + } + TRACELOG(LOG_DEBUG, " > Primitives (triangles only) count based on hierarchy : %i", primitivesCount); // Load our model data: meshes and materials From 97023def48c4be1e27bbaab70242521dd0d829df Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Feb 2026 18:45:14 +0100 Subject: [PATCH 029/319] REVIEWED: `GenImageFontAtlas()`, scale image to fit all characters, in case it fails size estimation #5542 --- src/rtext.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index 8e7fafec2..0fd2d8e6b 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -796,7 +796,7 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp if (glyphs == NULL) { - TRACELOG(LOG_WARNING, "FONT: Provided chars info not valid, returning empty image atlas"); + TRACELOG(LOG_WARNING, "FONT: Provided glyphs info not valid, returning empty image atlas"); return atlas; } @@ -881,16 +881,17 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp if (offsetY > (atlas.height - fontSize - padding)) { - for (int j = i + 1; j < glyphCount; j++) - { - TRACELOG(LOG_WARNING, "FONT: Failed to package character (0x%02x)", glyphs[j].value); - // Make sure remaining recs contain valid data - recs[j].x = 0; - recs[j].y = 0; - recs[j].width = 0; - recs[j].height = 0; - } - break; // Break for() loop, stop processing glyphs + TRACELOG(LOG_WARNING, "FONT: Updating atlas size to fit all characters"); + + // TODO: Increment atlas size (atlas.height*2) and continue adding glyphs + int updatedAtlasHeight = atlas.height*2; + int updatedAtlasDataSize = atlas.width*atlas.height; + unsigned char *updatedAtlasData = (unsigned char *)RL_CALLOC(updatedAtlasDataSize, 1); + + memcpy(updatedAtlasData, atlas.data, atlasDataSize); + RL_FREE(atlas.data); + atlas.height = updatedAtlasHeight; + atlasDataSize = updatedAtlasDataSize; } } @@ -967,7 +968,7 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp } } } - else TRACELOG(LOG_WARNING, "FONT: Failed to package character (0x%02x)", glyphs[i].value); + else TRACELOG(LOG_WARNING, "FONT: Failed to package glyph (0x%02x)", glyphs[i].value); } RL_FREE(rects); From 0e6cb0993d1b86c75771969cb998b682c77d546f Mon Sep 17 00:00:00 2001 From: Kirandeep-Singh-Khehra <107160937+Kirandeep-Singh-Khehra@users.noreply.github.com> Date: Thu, 19 Feb 2026 17:27:05 +0530 Subject: [PATCH 030/319] [rmodels] Added implementation of `UpdateModelAnimationBonesWithBlending()` function (#4578) * [rmodels] Added implementation of `UpdateModelAnimationBonesWithBlending()` function Signed-off-by: Kirandeep-Singh-Khehra * [rmodels] Added example for animation blending and fixed wrap issue for blend factor Signed-off-by: Kirandeep-Singh-Khehra * [rmodels] Updated build information for animation blending example Signed-off-by: Kirandeep-Singh-Khehra * [rmodels] Fixed typos in anmation blending example Signed-off-by: Kirandeep-Singh-Khehra * [rmodels] Updated blend function signature and added function to update verts from bones Signed-off-by: Kirandeep-Singh-Khehra * [rmodels] Updated documentation Signed-off-by: Kirandeep-Singh-Khehra * rlparser: update raylib_api.* by CI * rlparser: update raylib_api.* by CI --------- Signed-off-by: Kirandeep-Singh-Khehra Co-authored-by: Ray Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- examples/models/models_animation_blending.c | 152 + examples/models/models_animation_blending.png | Bin 0 -> 26143 bytes .../models_animation_blending.vcxproj | 387 + projects/VS2022/raylib.sln | 11340 ++++++++-------- src/raylib.h | 2 + src/rmodels.c | 77 +- tools/rlparser/output/raylib_api.json | 42 + tools/rlparser/output/raylib_api.lua | 21 + tools/rlparser/output/raylib_api.txt | 171 +- tools/rlparser/output/raylib_api.xml | 13 +- 10 files changed, 6451 insertions(+), 5754 deletions(-) create mode 100644 examples/models/models_animation_blending.c create mode 100644 examples/models/models_animation_blending.png create mode 100644 projects/VS2022/examples/models_animation_blending.vcxproj diff --git a/examples/models/models_animation_blending.c b/examples/models/models_animation_blending.c new file mode 100644 index 000000000..225c1bfbb --- /dev/null +++ b/examples/models/models_animation_blending.c @@ -0,0 +1,152 @@ +/******************************************************************************************* +* +* raylib [core] example - Model animation blending +* +* Example originally created with raylib 5.5 +* +* Example contributed by Kirandeep (@Kirandeep-Singh-Khehra) +* +* 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) 2024 Kirandeep (@Kirandeep-Singh-Khehra) +* +* Note: Due to limitations in the Apple OpenGL driver, this feature does not work on MacOS +* Note: This example uses CPU for updating meshes. +* For GPU skinning see comments with 'INFO:'. +* +********************************************************************************************/ + +#include "raylib.h" + +#define clamp(x,a,b) ((x < a)? a : (x > b)? b : x) + +#if defined(PLATFORM_DESKTOP) + #define GLSL_VERSION 330 +#else // PLATFORM_ANDROID, PLATFORM_WEB + #define GLSL_VERSION 100 +#endif + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [models] example - Model Animation Blending"); + + // Define the camera to look into our 3d world + Camera camera = { 0 }; + camera.position = (Vector3){ 8.0f, 8.0f, 8.0f }; // Camera position + camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; // Camera looking at point + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) + camera.fovy = 45.0f; // Camera field-of-view Y + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type + + // Load gltf model + Model characterModel = LoadModel("resources/models/gltf/robot.glb"); // Load character model + +/* INFO: Uncomment this to use GPU skinning + // Load skinning shader + Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION), + TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION)); + + for (int i = 0; i < characterModel.materialCount; i++) + { + characterModel.materials[i].shader = skinningShader; + } +*/ + + // Load gltf model animations + int animsCount = 0; + unsigned int animIndex0 = 0; + unsigned int animIndex1 = 0; + unsigned int animCurrentFrame = 0; + ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/robot.glb", &animsCount); + + float blendFactor = 0.5f; + + DisableCursor(); // Limit cursor to relative movement inside the window + + 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 + //---------------------------------------------------------------------------------- + UpdateCamera(&camera, CAMERA_THIRD_PERSON); + + // Select current animation + if (IsKeyPressed(KEY_T)) animIndex0 = (animIndex0 + 1)%animsCount; + else if (IsKeyPressed(KEY_G)) animIndex0 = (animIndex0 + animsCount - 1)%animsCount; + if (IsKeyPressed(KEY_Y)) animIndex1 = (animIndex1 + 1)%animsCount; + else if (IsKeyPressed(KEY_H)) animIndex1 = (animIndex1 + animsCount - 1)%animsCount; + + // Select blend factor + if (IsKeyPressed(KEY_U)) blendFactor = clamp(blendFactor - 0.1, 0.0f, 1.0f); + else if (IsKeyPressed(KEY_J)) blendFactor = clamp(blendFactor + 0.1, 0.0f, 1.0f); + + // Update animation + animCurrentFrame++; + + // Update bones + // Note: Same animation frame index is used below. By default it loops both animations + UpdateModelAnimationBonesLerp(characterModel, modelAnimations[animIndex0], animCurrentFrame, modelAnimations[animIndex1], animCurrentFrame, blendFactor); + +// INFO: Comment the following line to use GPU skinning + UpdateModelVertsToCurrentBones(characterModel); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + +/* INFO: Uncomment this to use GPU skinning + // Draw character mesh, pose calculation is done in shader (GPU skinning) + for (int i = 0; i < characterModel.meshCount; i++) + { + DrawMesh(characterModel.meshes[i], characterModel.materials[characterModel.meshMaterial[i]], characterModel.transform); + } +*/ + +// INFO: Comment the following line to use GPU skinning + DrawModel(characterModel, (Vector3){0.0f, 0.0f, 0.0f}, 1.0f, WHITE); + + + DrawGrid(10, 1.0f); + + EndMode3D(); + + DrawText("Use the U/J to adjust blend factor", 10, 10, 20, GRAY); + DrawText("Use the T/G to switch first animation", 10, 30, 20, GRAY); + DrawText("Use the Y/H to switch second animation", 10, 50, 20, GRAY); + DrawText(TextFormat("Animations: %s, %s", modelAnimations[animIndex0].name, modelAnimations[animIndex1].name), 10, 70, 20, BLACK); + DrawText(TextFormat("Blend Factor: %f", blendFactor), 10, 86, 20, BLACK); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadModelAnimations(modelAnimations, animsCount); // Unload model animation + UnloadModel(characterModel); // Unload model and meshes/material + +// INFO: Uncomment the following line to use GPU skinning + // UnloadShader(skinningShader); // Unload GPU skinning shader + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/models/models_animation_blending.png b/examples/models/models_animation_blending.png new file mode 100644 index 0000000000000000000000000000000000000000..0d70c1a88436a01b3689693035840c34133cd936 GIT binary patch literal 26143 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 + + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + Win32 + + + Release + x64 + + + + {AFDDE100-2D36-4749-817D-12E54C56312F} + Win32Proj + models_animation_blending + 10.0 + models_animation_blending + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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)\ + + + $(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) + + + 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)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 47e83ef4c..8483dc563 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -1,5670 +1,5670 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31912.275 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "raylib", "raylib\raylib.vcxproj", "{E89D61AC-55DE-4482-AFD4-DF7242EBC859}" -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}") = "others", "others", "{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_animation", "examples\textures_sprite_animation.vcxproj", "{C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_srcrec_dstrec", "examples\textures_srcrec_dstrec.vcxproj", "{103B292B-049B-4B15-85A1-9F902840DB2C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_image_drawing", "examples\textures_image_drawing.vcxproj", "{0C2D2F82-AE67-400C-B19C-8C9B957B132A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_module_playing", "examples\audio_module_playing.vcxproj", "{E6784F91-4E4E-4956-A079-73FAB1AC7BE6}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_music_stream", "examples\audio_music_stream.vcxproj", "{BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_raw_stream", "examples\audio_raw_stream.vcxproj", "{93A1F656-0D29-4C5E-B140-11F23FF5D6AB}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_sound_loading", "examples\audio_sound_loading.vcxproj", "{F81C5819-85B6-4D2E-B6DC-104A7634461B}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_2d_camera", "examples\core_2d_camera.vcxproj", "{66CC5B13-881A-412F-8C51-746622A91C5A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_2d_camera_platformer", "examples\core_2d_camera_platformer.vcxproj", "{CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_camera_first_person", "examples\core_3d_camera_first_person.vcxproj", "{557138B0-7BE2-4392-B2E2-B45734031A62}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_camera_free", "examples\core_3d_camera_free.vcxproj", "{9EED87BB-527F-4D05-9384-6D16CFD627A8}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_camera_mode", "examples\core_3d_camera_mode.vcxproj", "{6D1CA2F1-7FCA-4249-9220-075C2DF4F965}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_camera_split_screen", "examples\core_3d_camera_split_screen.vcxproj", "{946A1700-C7AA-46F0-AEF2-67C98B5722AC}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_picking", "examples\core_3d_picking.vcxproj", "{FD193822-3D5C-4161-A147-884C2ABDE483}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_custom_logging", "examples\core_custom_logging.vcxproj", "{20AD0AC9-9159-4744-99CC-6AC5779D6B87}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_drop_files", "examples\core_drop_files.vcxproj", "{0199E349-0701-40BC-8A7F-06A54FFA3E7C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_highdpi_demo", "examples\core_highdpi_demo.vcxproj", "{BCB71111-8505-4B35-8CEF-EC6115DC9D4D}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_gamepad", "examples\core_input_gamepad.vcxproj", "{8F19E3DA-8929-4000-87B5-3CA6929636CC}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_gestures", "examples\core_input_gestures.vcxproj", "{51A00565-5787-4911-9CC0-28403AA4909D}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_keys", "examples\core_input_keys.vcxproj", "{92B64AE7-D773-4F05-89F1-CE59BBF4F053}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_mouse", "examples\core_input_mouse.vcxproj", "{A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_multitouch", "examples\core_input_multitouch.vcxproj", "{A643BB06-735D-47F3-BFE7-B6D3C36F7097}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_random_sequence", "examples\core_random_sequence.vcxproj", "{6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_random_values", "examples\core_random_values.vcxproj", "{B332DCA8-3599-4A99-917A-82261BDC27AC}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_scissor_test", "examples\core_scissor_test.vcxproj", "{59089B0C-AAB4-4532-B294-44DEAE7178B7}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_storage_values", "examples\core_storage_values.vcxproj", "{C298876B-6C12-4EA4-903B-33450BCD9884}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_vr_simulator", "examples\core_vr_simulator.vcxproj", "{83F586FA-C801-4979-ACCA-006BD628CC88}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_window_flags", "examples\core_window_flags.vcxproj", "{86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_window_letterbox", "examples\core_window_letterbox.vcxproj", "{FF2970AE-E2E9-405F-B321-D523A1BD44A0}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_world_screen", "examples\core_world_screen.vcxproj", "{79417CE2-FEEB-42F0-BC53-62D5267B19B1}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_playing", "examples\models_animation_playing.vcxproj", "{AFDDE100-2D36-4749-817D-12E54C56312F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_billboard_rendering", "examples\models_billboard_rendering.vcxproj", "{B7812167-50FB-4934-996F-DF6FE4CBBFDF}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_box_collisions", "examples\models_box_collisions.vcxproj", "{39DB56C7-05F8-492C-A8D4-F19E40FECB59}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_cubicmap_rendering", "examples\models_cubicmap_rendering.vcxproj", "{82F3D34B-8DB2-4C6A-98B1-132245DD9D99}" -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}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_heightmap_rendering", "examples\models_heightmap_rendering.vcxproj", "{0859A973-E4FE-4688-8D16-0253163FDE24}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_loading", "examples\models_loading.vcxproj", "{F3412853-2B6A-4334-8CF2-B796CDAE0850}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_mesh_generation", "examples\models_mesh_generation.vcxproj", "{BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_mesh_picking", "examples\models_mesh_picking.vcxproj", "{D03F2C82-9553-4AFA-8F49-9234009122B6}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_orthographic_projection", "examples\models_orthographic_projection.vcxproj", "{FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_rlgl_solar_system", "examples\models_rlgl_solar_system.vcxproj", "{A53CCF42-A972-478F-9336-0F618B3EC06A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_skybox_rendering", "examples\models_skybox_rendering.vcxproj", "{0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_waving_cubes", "examples\models_waving_cubes.vcxproj", "{870723DD-945A-4136-B65B-4AF3BF85369C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_yaw_pitch_roll", "examples\models_yaw_pitch_roll.vcxproj", "{EA6488AD-445B-4835-87FB-EBC9E2EDAF97}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_to_image", "examples\textures_to_image.vcxproj", "{E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_sprite_explosion", "examples\textures_sprite_explosion.vcxproj", "{472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_sprite_button", "examples\textures_sprite_button.vcxproj", "{589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_raw_data", "examples\textures_raw_data.vcxproj", "{3AD868E6-8355-4F29-B5ED-7DE94AD786E7}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_particles_blending", "examples\textures_particles_blending.vcxproj", "{2B78CF0A-5403-45E2-99BD-493F1679BCDB}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_npatch_drawing", "examples\textures_npatch_drawing.vcxproj", "{0AB968E0-E993-45CE-8875-7453C96DF583}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_mouse_painting", "examples\textures_mouse_painting.vcxproj", "{25923141-9859-4AFE-8168-0DF78322FC63}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_logo_raylib", "examples\textures_logo_raylib.vcxproj", "{7E855020-7FA4-482D-B510-2E709354FE8B}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_image_text", "examples\textures_image_text.vcxproj", "{9782E0C8-2BD3-4F67-B420-21CF19CA2435}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_image_processing", "examples\textures_image_processing.vcxproj", "{9F4135E3-9814-452C-9B35-0EFBCD792B49}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_image_loading", "examples\textures_image_loading.vcxproj", "{C45343E6-DAB6-4F3A-A00A-8BED71A098BE}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_image_generation", "examples\textures_image_generation.vcxproj", "{B19DD336-538E-4091-A559-EAA717FEC899}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_tiled_drawing", "examples\textures_tiled_drawing.vcxproj", "{0BF60202-43F7-48E9-8717-D31E56FA5BE0}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_bunnymark", "examples\textures_bunnymark.vcxproj", "{4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_blend_modes", "examples\textures_blend_modes.vcxproj", "{6D75CD88-1A03-4955-B8C7-ACFC3742154F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_background_scrolling", "examples\textures_background_scrolling.vcxproj", "{8DD0EB7E-668E-452D-91D7-906C64A9C8AC}" -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_emojis", "examples\text_unicode_emojis.vcxproj", "{1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_rectangle_bounds", "examples\text_rectangle_bounds.vcxproj", "{25BCB876-B60A-499B-9046-E9801CFD7780}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_sprite_fonts", "examples\text_sprite_fonts.vcxproj", "{56FB0A45-145F-4EAE-B2C8-E5833E682D8F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_input_box", "examples\text_input_box.vcxproj", "{2BB0C1D4-9298-45AC-B244-67A99769A292}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_format_text", "examples\text_format_text.vcxproj", "{99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_font_spritefont", "examples\text_font_spritefont.vcxproj", "{81064BCE-EEC1-43B0-9912-F05F2B54B11A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_font_sdf", "examples\text_font_sdf.vcxproj", "{31B41997-3890-45E3-93FE-C57B363E9C0D}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_font_loading", "examples\text_font_loading.vcxproj", "{D550AB93-DF31-4B76-873F-F075018352F4}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_font_filters", "examples\text_font_filters.vcxproj", "{8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_rectangle_scaling", "examples\shapes_rectangle_scaling.vcxproj", "{F90FCDC5-EE14-4B89-96DB-4392E28F34AF}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_logo_raylib_anim", "examples\shapes_logo_raylib_anim.vcxproj", "{93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_logo_raylib", "examples\shapes_logo_raylib.vcxproj", "{56E68E37-B3FC-4799-91AF-0CA10B6D55A5}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_lines_bezier", "examples\shapes_lines_bezier.vcxproj", "{03E7018C-44A2-4C46-9CE7-F2A135A2692B}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_following_eyes", "examples\shapes_following_eyes.vcxproj", "{F3F6FE4D-9D9E-451A-B0BA-81456104B672}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_basic_shapes", "examples\shapes_basic_shapes.vcxproj", "{C27794B5-1293-4EA7-BC0E-0F18E6325539}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_bouncing_ball", "examples\shapes_bouncing_ball.vcxproj", "{02F41059-12A2-4A96-8D77-07EFE4B108FD}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_collision_area", "examples\shapes_collision_area.vcxproj", "{B774E0B9-9514-4E88-975F-4EB6C3B8D519}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_colors_palette", "examples\shapes_colors_palette.vcxproj", "{D91367C2-2189-4859-A7FE-D2CAB84FA15C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_circle_sector_drawing", "examples\shapes_circle_sector_drawing.vcxproj", "{33459B4E-1839-4856-BF6B-22480D11FE31}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_rounded_rectangle_drawing", "examples\shapes_rounded_rectangle_drawing.vcxproj", "{48871156-181A-475A-BD8D-200086A09675}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_ring_drawing", "examples\shapes_ring_drawing.vcxproj", "{C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_easings_ball", "examples\shapes_easings_ball.vcxproj", "{1C49E35A-2838-49D9-9D5F-4B8134960EF6}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_easings_box", "examples\shapes_easings_box.vcxproj", "{F91142E2-A999-47F0-9E74-38C1E2930EBE}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_easings_rectangles", "examples\shapes_easings_rectangles.vcxproj", "{1EDD4BCF-345C-4065-8CBD-7285224293C3}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_basic_lighting", "examples\shaders_basic_lighting.vcxproj", "{A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_custom_uniform", "examples\shaders_custom_uniform.vcxproj", "{B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_eratosthenes_sieve", "examples\shaders_eratosthenes_sieve.vcxproj", "{D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_fog_rendering", "examples\shaders_fog_rendering.vcxproj", "{4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_hot_reloading", "examples\shaders_hot_reloading.vcxproj", "{CF3755C4-937D-4ABF-B7B3-95140808717F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_julia_set", "examples\shaders_julia_set.vcxproj", "{D34939FE-8873-4C53-8D6C-74DED78EA3C4}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_model_shader", "examples\shaders_model_shader.vcxproj", "{D408A730-363A-4ABF-BCEF-5D63DCC66042}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_multi_sample2d", "examples\shaders_multi_sample2d.vcxproj", "{F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_palette_switch", "examples\shaders_palette_switch.vcxproj", "{52FB7463-C128-42AF-A02F-78F48473EA9A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_postprocessing", "examples\shaders_postprocessing.vcxproj", "{7381D91E-5C72-48F0-AAB4-95C9B10D7484}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_raymarching_rendering", "examples\shaders_raymarching_rendering.vcxproj", "{D36EC43E-B31F-4CF4-8285-93A7A9D90189}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_mesh_instancing", "examples\shaders_mesh_instancing.vcxproj", "{274C0319-7E1E-4188-936B-8DF3331230B3}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_shapes_textures", "examples\shaders_shapes_textures.vcxproj", "{41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_simple_mask", "examples\shaders_simple_mask.vcxproj", "{600C3D4F-0670-4DB4-B30F-520A729053B5}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_spotlight_rendering", "examples\shaders_spotlight_rendering.vcxproj", "{11F33A39-74B7-4018-B5F9-CC285A673A8F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_texture_rendering", "examples\shaders_texture_rendering.vcxproj", "{A6F5E35E-B4A7-41B3-853A-75558E6E0715}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_texture_waves", "examples\shaders_texture_waves.vcxproj", "{291B4975-8EFF-4C7C-8AF3-44A77B8491B8}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "embedded_files_loading", "examples\embedded_files_loading.vcxproj", "{FDE6080B-E203-4066-910D-AD0302566008}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "easings_testbed", "examples\easings_testbed.vcxproj", "{E1B6D565-9D7C-46B7-9202-ECF54974DE50}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rlgl_standalone", "examples\rlgl_standalone.vcxproj", "{C8765523-58F8-4C8E-9914-693396F6F0FF}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_loading_vox", "examples\models_loading_vox.vcxproj", "{2F1B955B-275E-4D8E-8864-06FEC44D7912}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_loading_gltf", "examples\models_loading_gltf.vcxproj", "{F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_codepoints_loading", "examples\text_codepoints_loading.vcxproj", "{F2DB2E59-76BF-4D81-859A-AFC289C046C0}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_window_should_close", "examples\core_window_should_close.vcxproj", "{3FE7E9B6-49AC-4246-A789-28DB4644567B}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_fog_of_war", "examples\textures_fog_of_war.vcxproj", "{EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_gif_player", "examples\textures_gif_player.vcxproj", "{191A5289-BA65-4638-A215-C521F0187313}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_2d_camera_mouse_zoom", "examples\core_2d_camera_mouse_zoom.vcxproj", "{3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_basic_screen_manager", "examples\core_basic_screen_manager.vcxproj", "{8B1AF423-00F1-4924-AC54-F77D402D2AC9}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_custom_frame_control", "examples\core_custom_frame_control.vcxproj", "{658A1B85-554E-4A5D-973A-FFE592CDD5F2}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rlgl_compute_shader", "examples\rlgl_compute_shader.vcxproj", "{07CA51AD-72AE-46A2-AAED-DC3E3F807976}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_3d_drawing", "examples\text_3d_drawing.vcxproj", "{27B110CC-43C0-400A-89D9-245E681647D7}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_polygon_drawing", "examples\textures_polygon_drawing.vcxproj", "{1DE84812-E143-4C4B-A61D-9267AAD55401}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_stream_effects", "examples\audio_stream_effects.vcxproj", "{4A87569C-4BD3-4113-B4B9-573D65B3D3F8}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_textured_curve", "examples\textures_textured_curve.vcxproj", "{769FF0C1-4424-4FA3-BC44-D7A7DA312A06}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_loading_m3d", "examples\models_loading_m3d.vcxproj", "{6D9E00D8-2893-45E4-9363-3F7F61D416BD}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_depth_writing", "examples\shaders_depth_writing.vcxproj", "{70B35F59-AFC2-4D8F-8833-5314D2047A81}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_depth_rendering", "examples\shaders_depth_rendering.vcxproj", "{DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_hybrid_rendering", "examples\shaders_hybrid_rendering.vcxproj", "{3755E9F4-CB48-4EC3-B561-3B85964EBDEF}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_sound_multi", "examples\audio_sound_multi.vcxproj", "{F81C5819-85B4-4D2E-B6DC-104A7634461B}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_2d_camera_split_screen", "examples\core_2d_camera_split_screen.vcxproj", "{CC62F7DB-D089-4677-8575-CAB7A7815C43}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_automation_events", "examples\core_automation_events.vcxproj", "{7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_mixed_processor", "examples\audio_mixed_processor.vcxproj", "{A4B0D971-3CD6-41C9-8AB2-055D25A33373}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_mouse_wheel", "examples\core_input_mouse_wheel.vcxproj", "{15CDD310-6980-42A6-8082-3A6B7730D13F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_smooth_pixelperfect", "examples\core_smooth_pixelperfect.vcxproj", "{71DB4284-5B1C-4E86-9AF5-B91542D44A6F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_textured_cube", "examples\models_textured_cube.vcxproj", "{4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_deferred_rendering", "examples\shaders_deferred_rendering.vcxproj", "{88DE5AD6-0074-4A5A-BE22-C840153E35D5}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_texture_outline", "examples\shaders_texture_outline.vcxproj", "{A546E75A-5242-46E6-9A9E-6C91554EAB84}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_texture_tiling", "examples\shaders_texture_tiling.vcxproj", "{EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_splines_drawing", "examples\shapes_splines_drawing.vcxproj", "{DF25E545-00FF-4E64-844C-7DF98991F901}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_top_down_lights", "examples\shapes_top_down_lights.vcxproj", "{703BE7BA-5B99-4F70-806D-3A259F6A991E}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_rectangle_advanced", "examples\shapes_rectangle_advanced.vcxproj", "{FAFEE2F9-24B0-4AF1-B512-433E9590033F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_gpu_skinning", "examples\models_animation_gpu_skinning.vcxproj", "{8245DAD9-D402-4D5C-8F45-32229CD3B263}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_shadowmap_rendering", "examples\shaders_shadowmap_rendering.vcxproj", "{41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_bone_socket", "examples\models_bone_socket.vcxproj", "{3A7FE53D-35F7-49DC-9C9A-A5204A53523F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_vertex_displacement", "examples\shaders_vertex_displacement.vcxproj", "{CCA63A76-D9FC-4130-9F67-4D97F9770D53}" -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_rendering", "examples\shaders_lightmap_rendering.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 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_virtual_controls", "examples\core_input_virtual_controls.vcxproj", "{0981CA28-E4A5-4DF1-987F-A41D09131EFC}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_camera_fps", "examples\core_3d_camera_fps.vcxproj", "{6B1A933E-71B8-4C1F-9E79-02D98830E671}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_normalmap_rendering", "examples\shaders_normalmap_rendering.vcxproj", "{6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_unicode_ranges", "examples\text_unicode_ranges.vcxproj", "{6777EC3C-077C-42FC-B4AD-B799CE55CCE4}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_gestures_testbed", "examples\core_input_gestures_testbed.vcxproj", "{A61DAD9C-271C-4E95-81AA-DB4CD58564D4}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_render_texture", "examples\core_render_texture.vcxproj", "{49C67F03-1A56-4F96-B278-39B66EC93678}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_inline_styling", "examples\text_inline_styling.vcxproj", "{D496308F-3C3C-40B3-A3ED-EA327D244B3E}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_undo_redo", "examples\core_undo_redo.vcxproj", "{3B27F358-2679-4F38-B297-17B536F580BB}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_actions", "examples\core_input_actions.vcxproj", "{718FCBD0-591D-448C-B7D5-9F1CA8544E7B}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_delta_time", "examples\core_delta_time.vcxproj", "{19CA0070-B4B2-4394-90B7-D0C259AA35BA}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_bullet_hell", "examples\shapes_bullet_hell.vcxproj", "{2CCCD9E4-9058-4291-BD89-39C979F0CA1E}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_vector_angle", "examples\shapes_vector_angle.vcxproj", "{9DB1F875-6E65-4195-B23F-ED8095C0B99C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_basic_voxel", "examples\models_basic_voxel.vcxproj", "{52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_dashed_line", "examples\shapes_dashed_line.vcxproj", "{8E132D5A-2C00-48D0-8747-97E41356F26F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_rotating_cube", "examples\models_rotating_cube.vcxproj", "{A4662163-83E7-4309-8CAA-B0BF13655FE6}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_ascii_rendering", "examples\shaders_ascii_rendering.vcxproj", "{5F4B766F-DD52-4B53-B6C3-BC7611E17F20}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_monitor_detector", "examples\core_monitor_detector.vcxproj", "{FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "web_basic_window", "examples\web_basic_window.vcxproj", "{A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_kaleidoscope", "examples\shapes_kaleidoscope.vcxproj", "{0C442799-B09C-4CD1-9538-711B6E85E9BF}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_recursive_tree", "examples\shapes_recursive_tree.vcxproj", "{DFB40A10-F8B7-412A-BCC3-5EE49294D816}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_triangle_strip", "examples\shapes_triangle_strip.vcxproj", "{BB58A5FB-1A35-4471-86D0-A5189EC541B3}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_pie_chart", "examples\shapes_pie_chart.vcxproj", "{61997220-5383-4AE5-ABD4-5F45AE1B0F2A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_directory_files", "examples\core_directory_files.vcxproj", "{7467E9AE-844F-444D-8A3F-17397544BA21}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_simple_particles", "examples\shapes_simple_particles.vcxproj", "{497FDF54-9762-4048-A833-61CC3980A0FB}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_words_alignment", "examples\text_words_alignment.vcxproj", "{29B00F47-BE91-4A1F-B87D-B1302F038316}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_clipboard_text", "examples\core_clipboard_text.vcxproj", "{124935CC-73BB-489E-92E8-4F922A85DB5D}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_clock_of_clocks", "examples\shapes_clock_of_clocks.vcxproj", "{AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_mouse_trail", "examples\shapes_mouse_trail.vcxproj", "{0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_starfield_effect", "examples\shapes_starfield_effect.vcxproj", "{EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_highdpi_testbed", "examples\core_highdpi_testbed.vcxproj", "{1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_screen_recording", "examples\core_screen_recording.vcxproj", "{9DE2FC01-A839-4F89-8319-9071D4C54821}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_text_file_loading", "examples\core_text_file_loading.vcxproj", "{2F578155-D51F-4C03-AB7F-5C5122CA46CC}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_mandelbrot_set", "examples\shaders_mandelbrot_set.vcxproj", "{1C829D1A-892C-451C-AF0B-AC65C85F5CC6}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_math_angle_rotation", "examples\shapes_math_angle_rotation.vcxproj", "{84DE22BB-C25F-425C-A7FE-0120CF107B83}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_color_correction", "examples\shaders_color_correction.vcxproj", "{98152EDD-7E28-4FA3-89D8-B636ED5D5F65}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_math_sine_cosine", "examples\shapes_math_sine_cosine.vcxproj", "{B7FDD40F-DDA4-468E-9C40-EEB175964A26}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_decals", "examples\models_decals.vcxproj", "{028F0967-B253-45DA-B1C4-FACCE45D0D8D}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_lines_drawing", "examples\shapes_lines_drawing.vcxproj", "{666346D7-C84B-498D-AE17-53B20C62DB1A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_viewport_scaling", "examples\core_viewport_scaling.vcxproj", "{AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_compute_hash", "examples\core_compute_hash.vcxproj", "{6C897101-BE52-4387-8AA2-062123A76BA1}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_screen_buffer", "examples\textures_screen_buffer.vcxproj", "{4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_spectrum_visualizer", "examples\audio_spectrum_visualizer.vcxproj", "{2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_directional_billboard", "examples\models_directional_billboard.vcxproj", "{30011884-25EE-42C9-BB15-888CAFB1AA6E}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_rlgl_color_wheel", "examples\shapes_rlgl_color_wheel.vcxproj", "{32FE2658-1D70-442E-8672-0AC5C6F0BD7B}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_rlgl_triangle", "examples\shapes_rlgl_triangle.vcxproj", "{842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_sprite_stacking", "examples\textures_sprite_stacking.vcxproj", "{FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_ball_physics", "examples\shapes_ball_physics.vcxproj", "{0653AFAF-5578-4C02-AF29-0C873E7634AE}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_game_of_life", "examples\shaders_game_of_life.vcxproj", "{071E64F3-1396-4A97-97CA-98CAC059B168}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_penrose_tile", "examples\shapes_penrose_tile.vcxproj", "{7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_strings_management", "examples\text_strings_management.vcxproj", "{1F4722E7-F78E-413F-A106-D3490211EA57}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_cellular_automata", "examples\textures_cellular_automata.vcxproj", "{0A0FC982-6E31-401F-BA77-3C5E8AB02C68}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_hilbert_curve", "examples\shapes_hilbert_curve.vcxproj", "{DC163251-16C3-4B72-B965-ACDBA0F02BD1}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_keyboard_testbed", "examples\core_keyboard_testbed.vcxproj", "{D35D2FDA-B53F-4F70-81CA-24D95812B89C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_framebuffer_rendering", "examples\textures_framebuffer_rendering.vcxproj", "{F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug.DLL|ARM64 = Debug.DLL|ARM64 - Debug.DLL|x64 = Debug.DLL|x64 - Debug.DLL|x86 = Debug.DLL|x86 - Debug|ARM64 = Debug|ARM64 - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release.DLL|ARM64 = Release.DLL|ARM64 - Release.DLL|x64 = Release.DLL|x64 - Release.DLL|x86 = Release.DLL|x86 - Release|ARM64 = Release|ARM64 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug|ARM64.Build.0 = Debug|ARM64 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug|x64.ActiveCfg = Debug|x64 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug|x64.Build.0 = Debug|x64 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug|x86.ActiveCfg = Debug|Win32 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug|x86.Build.0 = Debug|Win32 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release|ARM64.ActiveCfg = Release|ARM64 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release|ARM64.Build.0 = Release|ARM64 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release|x64.ActiveCfg = Release|x64 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release|x64.Build.0 = Release|x64 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release|x86.ActiveCfg = Release|Win32 - {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release|x86.Build.0 = Release|Win32 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug|ARM64.Build.0 = Debug|ARM64 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug|x64.ActiveCfg = Debug|x64 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug|x64.Build.0 = Debug|x64 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug|x86.ActiveCfg = Debug|Win32 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug|x86.Build.0 = Debug|Win32 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release|ARM64.ActiveCfg = Release|ARM64 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release|ARM64.Build.0 = Release|ARM64 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release|x64.ActiveCfg = Release|x64 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release|x64.Build.0 = Release|x64 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release|x86.ActiveCfg = Release|Win32 - {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release|x86.Build.0 = Release|Win32 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug|ARM64.Build.0 = Debug|ARM64 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug|x64.ActiveCfg = Debug|x64 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug|x64.Build.0 = Debug|x64 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug|x86.ActiveCfg = Debug|Win32 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug|x86.Build.0 = Debug|Win32 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release|ARM64.ActiveCfg = Release|ARM64 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release|ARM64.Build.0 = Release|ARM64 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release|x64.ActiveCfg = Release|x64 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release|x64.Build.0 = Release|x64 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release|x86.ActiveCfg = Release|Win32 - {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release|x86.Build.0 = Release|Win32 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug|ARM64.Build.0 = Debug|ARM64 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug|x64.ActiveCfg = Debug|x64 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug|x64.Build.0 = Debug|x64 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug|x86.ActiveCfg = Debug|Win32 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug|x86.Build.0 = Debug|Win32 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Release|ARM64.ActiveCfg = Release|ARM64 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Release|ARM64.Build.0 = Release|ARM64 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Release|x64.ActiveCfg = Release|x64 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Release|x64.Build.0 = Release|x64 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Release|x86.ActiveCfg = Release|Win32 - {103B292B-049B-4B15-85A1-9F902840DB2C}.Release|x86.Build.0 = Release|Win32 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug|ARM64.Build.0 = Debug|ARM64 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug|x64.ActiveCfg = Debug|x64 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug|x64.Build.0 = Debug|x64 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug|x86.ActiveCfg = Debug|Win32 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug|x86.Build.0 = Debug|Win32 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release|ARM64.ActiveCfg = Release|ARM64 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release|ARM64.Build.0 = Release|ARM64 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release|x64.ActiveCfg = Release|x64 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release|x64.Build.0 = Release|x64 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release|x86.ActiveCfg = Release|Win32 - {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release|x86.Build.0 = Release|Win32 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug|ARM64.Build.0 = Debug|ARM64 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug|x64.ActiveCfg = Debug|x64 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug|x64.Build.0 = Debug|x64 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug|x86.ActiveCfg = Debug|Win32 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug|x86.Build.0 = Debug|Win32 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release|ARM64.ActiveCfg = Release|ARM64 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release|ARM64.Build.0 = Release|ARM64 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release|x64.ActiveCfg = Release|x64 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release|x64.Build.0 = Release|x64 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release|x86.ActiveCfg = Release|Win32 - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release|x86.Build.0 = Release|Win32 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug|ARM64.Build.0 = Debug|ARM64 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug|x64.ActiveCfg = Debug|x64 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug|x64.Build.0 = Debug|x64 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug|x86.ActiveCfg = Debug|Win32 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug|x86.Build.0 = Debug|Win32 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release|ARM64.ActiveCfg = Release|ARM64 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release|ARM64.Build.0 = Release|ARM64 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release|x64.ActiveCfg = Release|x64 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release|x64.Build.0 = Release|x64 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release|x86.ActiveCfg = Release|Win32 - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release|x86.Build.0 = Release|Win32 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug|ARM64.Build.0 = Debug|ARM64 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug|x64.ActiveCfg = Debug|x64 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug|x64.Build.0 = Debug|x64 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug|x86.ActiveCfg = Debug|Win32 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug|x86.Build.0 = Debug|Win32 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release|ARM64.ActiveCfg = Release|ARM64 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release|ARM64.Build.0 = Release|ARM64 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release|x64.ActiveCfg = Release|x64 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release|x64.Build.0 = Release|x64 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release|x86.ActiveCfg = Release|Win32 - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release|x86.Build.0 = Release|Win32 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug|ARM64.Build.0 = Debug|ARM64 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug|x64.ActiveCfg = Debug|x64 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug|x64.Build.0 = Debug|x64 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug|x86.ActiveCfg = Debug|Win32 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug|x86.Build.0 = Debug|Win32 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release|ARM64.ActiveCfg = Release|ARM64 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release|ARM64.Build.0 = Release|ARM64 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release|x64.ActiveCfg = Release|x64 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release|x64.Build.0 = Release|x64 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release|x86.ActiveCfg = Release|Win32 - {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release|x86.Build.0 = Release|Win32 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug|ARM64.Build.0 = Debug|ARM64 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug|x64.ActiveCfg = Debug|x64 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug|x64.Build.0 = Debug|x64 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug|x86.ActiveCfg = Debug|Win32 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug|x86.Build.0 = Debug|Win32 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Release|ARM64.ActiveCfg = Release|ARM64 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Release|ARM64.Build.0 = Release|ARM64 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Release|x64.ActiveCfg = Release|x64 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Release|x64.Build.0 = Release|x64 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Release|x86.ActiveCfg = Release|Win32 - {66CC5B13-881A-412F-8C51-746622A91C5A}.Release|x86.Build.0 = Release|Win32 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug|ARM64.Build.0 = Debug|ARM64 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug|x64.ActiveCfg = Debug|x64 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug|x64.Build.0 = Debug|x64 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug|x86.ActiveCfg = Debug|Win32 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug|x86.Build.0 = Debug|Win32 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release|ARM64.ActiveCfg = Release|ARM64 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release|ARM64.Build.0 = Release|ARM64 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release|x64.ActiveCfg = Release|x64 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release|x64.Build.0 = Release|x64 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release|x86.ActiveCfg = Release|Win32 - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release|x86.Build.0 = Release|Win32 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug|ARM64.Build.0 = Debug|ARM64 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug|x64.ActiveCfg = Debug|x64 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug|x64.Build.0 = Debug|x64 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug|x86.ActiveCfg = Debug|Win32 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug|x86.Build.0 = Debug|Win32 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Release|ARM64.ActiveCfg = Release|ARM64 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Release|ARM64.Build.0 = Release|ARM64 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Release|x64.ActiveCfg = Release|x64 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Release|x64.Build.0 = Release|x64 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Release|x86.ActiveCfg = Release|Win32 - {557138B0-7BE2-4392-B2E2-B45734031A62}.Release|x86.Build.0 = Release|Win32 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug|ARM64.Build.0 = Debug|ARM64 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug|x64.ActiveCfg = Debug|x64 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug|x64.Build.0 = Debug|x64 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug|x86.ActiveCfg = Debug|Win32 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug|x86.Build.0 = Debug|Win32 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release|ARM64.ActiveCfg = Release|ARM64 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release|ARM64.Build.0 = Release|ARM64 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release|x64.ActiveCfg = Release|x64 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release|x64.Build.0 = Release|x64 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release|x86.ActiveCfg = Release|Win32 - {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release|x86.Build.0 = Release|Win32 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug|ARM64.Build.0 = Debug|ARM64 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug|x64.ActiveCfg = Debug|x64 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug|x64.Build.0 = Debug|x64 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug|x86.ActiveCfg = Debug|Win32 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug|x86.Build.0 = Debug|Win32 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release|ARM64.ActiveCfg = Release|ARM64 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release|ARM64.Build.0 = Release|ARM64 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release|x64.ActiveCfg = Release|x64 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release|x64.Build.0 = Release|x64 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release|x86.ActiveCfg = Release|Win32 - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release|x86.Build.0 = Release|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|ARM64.Build.0 = Debug|ARM64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x64.ActiveCfg = Debug|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x64.Build.0 = Debug|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x86.ActiveCfg = Debug|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x86.Build.0 = Debug|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|ARM64.ActiveCfg = Release|ARM64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|ARM64.Build.0 = Release|ARM64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x64.ActiveCfg = Release|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x64.Build.0 = Release|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x86.ActiveCfg = Release|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x86.Build.0 = Release|Win32 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug|ARM64.Build.0 = Debug|ARM64 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug|x64.ActiveCfg = Debug|x64 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug|x64.Build.0 = Debug|x64 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug|x86.ActiveCfg = Debug|Win32 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug|x86.Build.0 = Debug|Win32 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Release|ARM64.ActiveCfg = Release|ARM64 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Release|ARM64.Build.0 = Release|ARM64 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Release|x64.ActiveCfg = Release|x64 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Release|x64.Build.0 = Release|x64 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Release|x86.ActiveCfg = Release|Win32 - {FD193822-3D5C-4161-A147-884C2ABDE483}.Release|x86.Build.0 = Release|Win32 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug|ARM64.Build.0 = Debug|ARM64 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug|x64.ActiveCfg = Debug|x64 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug|x64.Build.0 = Debug|x64 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug|x86.ActiveCfg = Debug|Win32 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug|x86.Build.0 = Debug|Win32 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release|ARM64.ActiveCfg = Release|ARM64 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release|ARM64.Build.0 = Release|ARM64 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release|x64.ActiveCfg = Release|x64 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release|x64.Build.0 = Release|x64 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release|x86.ActiveCfg = Release|Win32 - {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release|x86.Build.0 = Release|Win32 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug|ARM64.Build.0 = Debug|ARM64 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug|x64.ActiveCfg = Debug|x64 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug|x64.Build.0 = Debug|x64 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug|x86.ActiveCfg = Debug|Win32 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug|x86.Build.0 = Debug|Win32 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release|ARM64.ActiveCfg = Release|ARM64 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release|ARM64.Build.0 = Release|ARM64 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release|x64.ActiveCfg = Release|x64 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release|x64.Build.0 = Release|x64 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release|x86.ActiveCfg = Release|Win32 - {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release|x86.Build.0 = Release|Win32 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug|ARM64.Build.0 = Debug|ARM64 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug|x64.ActiveCfg = Debug|x64 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug|x64.Build.0 = Debug|x64 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug|x86.ActiveCfg = Debug|Win32 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug|x86.Build.0 = Debug|Win32 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release|ARM64.ActiveCfg = Release|ARM64 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release|ARM64.Build.0 = Release|ARM64 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release|x64.ActiveCfg = Release|x64 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release|x64.Build.0 = Release|x64 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release|x86.ActiveCfg = Release|Win32 - {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release|x86.Build.0 = Release|Win32 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug|ARM64.Build.0 = Debug|ARM64 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug|x64.ActiveCfg = Debug|x64 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug|x64.Build.0 = Debug|x64 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug|x86.ActiveCfg = Debug|Win32 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug|x86.Build.0 = Debug|Win32 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release|ARM64.ActiveCfg = Release|ARM64 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release|ARM64.Build.0 = Release|ARM64 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release|x64.ActiveCfg = Release|x64 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release|x64.Build.0 = Release|x64 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release|x86.ActiveCfg = Release|Win32 - {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release|x86.Build.0 = Release|Win32 - {51A00565-5787-4911-9CC0-28403AA4909D}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {51A00565-5787-4911-9CC0-28403AA4909D}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {51A00565-5787-4911-9CC0-28403AA4909D}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {51A00565-5787-4911-9CC0-28403AA4909D}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {51A00565-5787-4911-9CC0-28403AA4909D}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {51A00565-5787-4911-9CC0-28403AA4909D}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {51A00565-5787-4911-9CC0-28403AA4909D}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {51A00565-5787-4911-9CC0-28403AA4909D}.Debug|ARM64.Build.0 = Debug|ARM64 - {51A00565-5787-4911-9CC0-28403AA4909D}.Debug|x64.ActiveCfg = Debug|x64 - {51A00565-5787-4911-9CC0-28403AA4909D}.Debug|x64.Build.0 = Debug|x64 - {51A00565-5787-4911-9CC0-28403AA4909D}.Debug|x86.ActiveCfg = Debug|Win32 - {51A00565-5787-4911-9CC0-28403AA4909D}.Debug|x86.Build.0 = Debug|Win32 - {51A00565-5787-4911-9CC0-28403AA4909D}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {51A00565-5787-4911-9CC0-28403AA4909D}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {51A00565-5787-4911-9CC0-28403AA4909D}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {51A00565-5787-4911-9CC0-28403AA4909D}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {51A00565-5787-4911-9CC0-28403AA4909D}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {51A00565-5787-4911-9CC0-28403AA4909D}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {51A00565-5787-4911-9CC0-28403AA4909D}.Release|ARM64.ActiveCfg = Release|ARM64 - {51A00565-5787-4911-9CC0-28403AA4909D}.Release|ARM64.Build.0 = Release|ARM64 - {51A00565-5787-4911-9CC0-28403AA4909D}.Release|x64.ActiveCfg = Release|x64 - {51A00565-5787-4911-9CC0-28403AA4909D}.Release|x64.Build.0 = Release|x64 - {51A00565-5787-4911-9CC0-28403AA4909D}.Release|x86.ActiveCfg = Release|Win32 - {51A00565-5787-4911-9CC0-28403AA4909D}.Release|x86.Build.0 = Release|Win32 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug|ARM64.Build.0 = Debug|ARM64 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug|x64.ActiveCfg = Debug|x64 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug|x64.Build.0 = Debug|x64 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug|x86.ActiveCfg = Debug|Win32 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug|x86.Build.0 = Debug|Win32 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release|ARM64.ActiveCfg = Release|ARM64 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release|ARM64.Build.0 = Release|ARM64 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release|x64.ActiveCfg = Release|x64 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release|x64.Build.0 = Release|x64 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release|x86.ActiveCfg = Release|Win32 - {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release|x86.Build.0 = Release|Win32 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug|ARM64.Build.0 = Debug|ARM64 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug|x64.ActiveCfg = Debug|x64 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug|x64.Build.0 = Debug|x64 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug|x86.ActiveCfg = Debug|Win32 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug|x86.Build.0 = Debug|Win32 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release|ARM64.ActiveCfg = Release|ARM64 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release|ARM64.Build.0 = Release|ARM64 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release|x64.ActiveCfg = Release|x64 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release|x64.Build.0 = Release|x64 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release|x86.ActiveCfg = Release|Win32 - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release|x86.Build.0 = Release|Win32 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug|ARM64.Build.0 = Debug|ARM64 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug|x64.ActiveCfg = Debug|x64 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug|x64.Build.0 = Debug|x64 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug|x86.ActiveCfg = Debug|Win32 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug|x86.Build.0 = Debug|Win32 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release|ARM64.ActiveCfg = Release|ARM64 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release|ARM64.Build.0 = Release|ARM64 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release|x64.ActiveCfg = Release|x64 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release|x64.Build.0 = Release|x64 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release|x86.ActiveCfg = Release|Win32 - {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release|x86.Build.0 = Release|Win32 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug|ARM64.Build.0 = Debug|ARM64 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug|x64.ActiveCfg = Debug|x64 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug|x64.Build.0 = Debug|x64 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug|x86.ActiveCfg = Debug|Win32 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug|x86.Build.0 = Debug|Win32 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release|ARM64.ActiveCfg = Release|ARM64 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release|ARM64.Build.0 = Release|ARM64 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release|x64.ActiveCfg = Release|x64 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release|x64.Build.0 = Release|x64 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release|x86.ActiveCfg = Release|Win32 - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release|x86.Build.0 = Release|Win32 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug|ARM64.Build.0 = Debug|ARM64 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug|x64.ActiveCfg = Debug|x64 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug|x64.Build.0 = Debug|x64 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug|x86.ActiveCfg = Debug|Win32 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug|x86.Build.0 = Debug|Win32 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release|ARM64.ActiveCfg = Release|ARM64 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release|ARM64.Build.0 = Release|ARM64 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release|x64.ActiveCfg = Release|x64 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release|x64.Build.0 = Release|x64 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release|x86.ActiveCfg = Release|Win32 - {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release|x86.Build.0 = Release|Win32 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug|ARM64.Build.0 = Debug|ARM64 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug|x64.ActiveCfg = Debug|x64 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug|x64.Build.0 = Debug|x64 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug|x86.ActiveCfg = Debug|Win32 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug|x86.Build.0 = Debug|Win32 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release|ARM64.ActiveCfg = Release|ARM64 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release|ARM64.Build.0 = Release|ARM64 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release|x64.ActiveCfg = Release|x64 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release|x64.Build.0 = Release|x64 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release|x86.ActiveCfg = Release|Win32 - {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release|x86.Build.0 = Release|Win32 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug|ARM64.Build.0 = Debug|ARM64 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug|x64.ActiveCfg = Debug|x64 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug|x64.Build.0 = Debug|x64 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug|x86.ActiveCfg = Debug|Win32 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug|x86.Build.0 = Debug|Win32 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Release|ARM64.ActiveCfg = Release|ARM64 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Release|ARM64.Build.0 = Release|ARM64 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Release|x64.ActiveCfg = Release|x64 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Release|x64.Build.0 = Release|x64 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Release|x86.ActiveCfg = Release|Win32 - {C298876B-6C12-4EA4-903B-33450BCD9884}.Release|x86.Build.0 = Release|Win32 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug|ARM64.Build.0 = Debug|ARM64 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug|x64.ActiveCfg = Debug|x64 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug|x64.Build.0 = Debug|x64 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug|x86.ActiveCfg = Debug|Win32 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug|x86.Build.0 = Debug|Win32 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Release|ARM64.ActiveCfg = Release|ARM64 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Release|ARM64.Build.0 = Release|ARM64 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Release|x64.ActiveCfg = Release|x64 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Release|x64.Build.0 = Release|x64 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Release|x86.ActiveCfg = Release|Win32 - {83F586FA-C801-4979-ACCA-006BD628CC88}.Release|x86.Build.0 = Release|Win32 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug|ARM64.Build.0 = Debug|ARM64 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug|x64.ActiveCfg = Debug|x64 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug|x64.Build.0 = Debug|x64 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug|x86.ActiveCfg = Debug|Win32 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug|x86.Build.0 = Debug|Win32 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release|ARM64.ActiveCfg = Release|ARM64 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release|ARM64.Build.0 = Release|ARM64 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release|x64.ActiveCfg = Release|x64 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release|x64.Build.0 = Release|x64 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release|x86.ActiveCfg = Release|Win32 - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release|x86.Build.0 = Release|Win32 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug|ARM64.Build.0 = Debug|ARM64 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug|x64.ActiveCfg = Debug|x64 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug|x64.Build.0 = Debug|x64 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug|x86.ActiveCfg = Debug|Win32 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug|x86.Build.0 = Debug|Win32 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release|ARM64.ActiveCfg = Release|ARM64 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release|ARM64.Build.0 = Release|ARM64 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release|x64.ActiveCfg = Release|x64 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release|x64.Build.0 = Release|x64 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release|x86.ActiveCfg = Release|Win32 - {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release|x86.Build.0 = Release|Win32 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug|ARM64.Build.0 = Debug|ARM64 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug|x64.ActiveCfg = Debug|x64 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug|x64.Build.0 = Debug|x64 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug|x86.ActiveCfg = Debug|Win32 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug|x86.Build.0 = Debug|Win32 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release|ARM64.ActiveCfg = Release|ARM64 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release|ARM64.Build.0 = Release|ARM64 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release|x64.ActiveCfg = Release|x64 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release|x64.Build.0 = Release|x64 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release|x86.ActiveCfg = Release|Win32 - {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release|x86.Build.0 = Release|Win32 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug|ARM64.Build.0 = Debug|ARM64 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug|x64.ActiveCfg = Debug|x64 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug|x64.Build.0 = Debug|x64 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug|x86.ActiveCfg = Debug|Win32 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug|x86.Build.0 = Debug|Win32 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Release|ARM64.ActiveCfg = Release|ARM64 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Release|ARM64.Build.0 = Release|ARM64 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Release|x64.ActiveCfg = Release|x64 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Release|x64.Build.0 = Release|x64 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Release|x86.ActiveCfg = Release|Win32 - {AFDDE100-2D36-4749-817D-12E54C56312F}.Release|x86.Build.0 = Release|Win32 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug|ARM64.Build.0 = Debug|ARM64 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug|x64.ActiveCfg = Debug|x64 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug|x64.Build.0 = Debug|x64 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug|x86.ActiveCfg = Debug|Win32 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug|x86.Build.0 = Debug|Win32 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release|ARM64.ActiveCfg = Release|ARM64 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release|ARM64.Build.0 = Release|ARM64 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release|x64.ActiveCfg = Release|x64 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release|x64.Build.0 = Release|x64 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release|x86.ActiveCfg = Release|Win32 - {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release|x86.Build.0 = Release|Win32 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug|ARM64.Build.0 = Debug|ARM64 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug|x64.ActiveCfg = Debug|x64 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug|x64.Build.0 = Debug|x64 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug|x86.ActiveCfg = Debug|Win32 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug|x86.Build.0 = Debug|Win32 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release|ARM64.ActiveCfg = Release|ARM64 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release|ARM64.Build.0 = Release|ARM64 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release|x64.ActiveCfg = Release|x64 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release|x64.Build.0 = Release|x64 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release|x86.ActiveCfg = Release|Win32 - {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release|x86.Build.0 = Release|Win32 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug|ARM64.Build.0 = Debug|ARM64 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug|x64.ActiveCfg = Debug|x64 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug|x64.Build.0 = Debug|x64 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug|x86.ActiveCfg = Debug|Win32 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug|x86.Build.0 = Debug|Win32 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release|ARM64.ActiveCfg = Release|ARM64 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release|ARM64.Build.0 = Release|ARM64 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release|x64.ActiveCfg = Release|x64 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release|x64.Build.0 = Release|x64 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release|x86.ActiveCfg = Release|Win32 - {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release|x86.Build.0 = Release|Win32 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug|ARM64.Build.0 = Debug|ARM64 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug|x64.ActiveCfg = Debug|x64 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug|x64.Build.0 = Debug|x64 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug|x86.ActiveCfg = Debug|Win32 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug|x86.Build.0 = Debug|Win32 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release|ARM64.ActiveCfg = Release|ARM64 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release|ARM64.Build.0 = Release|ARM64 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release|x64.ActiveCfg = Release|x64 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release|x64.Build.0 = Release|x64 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release|x86.ActiveCfg = Release|Win32 - {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release|x86.Build.0 = Release|Win32 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug|ARM64.Build.0 = Debug|ARM64 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug|x64.ActiveCfg = Debug|x64 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug|x64.Build.0 = Debug|x64 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug|x86.ActiveCfg = Debug|Win32 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug|x86.Build.0 = Debug|Win32 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release|ARM64.ActiveCfg = Release|ARM64 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release|ARM64.Build.0 = Release|ARM64 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release|x64.ActiveCfg = Release|x64 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release|x64.Build.0 = Release|x64 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release|x86.ActiveCfg = Release|Win32 - {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release|x86.Build.0 = Release|Win32 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug|ARM64.Build.0 = Debug|ARM64 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug|x64.ActiveCfg = Debug|x64 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug|x64.Build.0 = Debug|x64 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug|x86.ActiveCfg = Debug|Win32 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug|x86.Build.0 = Debug|Win32 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Release|ARM64.ActiveCfg = Release|ARM64 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Release|ARM64.Build.0 = Release|ARM64 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Release|x64.ActiveCfg = Release|x64 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Release|x64.Build.0 = Release|x64 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Release|x86.ActiveCfg = Release|Win32 - {0859A973-E4FE-4688-8D16-0253163FDE24}.Release|x86.Build.0 = Release|Win32 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug|ARM64.Build.0 = Debug|ARM64 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug|x64.ActiveCfg = Debug|x64 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug|x64.Build.0 = Debug|x64 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug|x86.ActiveCfg = Debug|Win32 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug|x86.Build.0 = Debug|Win32 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release|ARM64.ActiveCfg = Release|ARM64 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release|ARM64.Build.0 = Release|ARM64 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release|x64.ActiveCfg = Release|x64 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release|x64.Build.0 = Release|x64 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release|x86.ActiveCfg = Release|Win32 - {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release|x86.Build.0 = Release|Win32 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug|ARM64.Build.0 = Debug|ARM64 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug|x64.ActiveCfg = Debug|x64 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug|x64.Build.0 = Debug|x64 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug|x86.ActiveCfg = Debug|Win32 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug|x86.Build.0 = Debug|Win32 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release|ARM64.ActiveCfg = Release|ARM64 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release|ARM64.Build.0 = Release|ARM64 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release|x64.ActiveCfg = Release|x64 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release|x64.Build.0 = Release|x64 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release|x86.ActiveCfg = Release|Win32 - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release|x86.Build.0 = Release|Win32 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug|ARM64.Build.0 = Debug|ARM64 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug|x64.ActiveCfg = Debug|x64 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug|x64.Build.0 = Debug|x64 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug|x86.ActiveCfg = Debug|Win32 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug|x86.Build.0 = Debug|Win32 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release|ARM64.ActiveCfg = Release|ARM64 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release|ARM64.Build.0 = Release|ARM64 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release|x64.ActiveCfg = Release|x64 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release|x64.Build.0 = Release|x64 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release|x86.ActiveCfg = Release|Win32 - {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release|x86.Build.0 = Release|Win32 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug|ARM64.Build.0 = Debug|ARM64 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug|x64.ActiveCfg = Debug|x64 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug|x64.Build.0 = Debug|x64 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug|x86.ActiveCfg = Debug|Win32 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug|x86.Build.0 = Debug|Win32 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release|ARM64.ActiveCfg = Release|ARM64 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release|ARM64.Build.0 = Release|ARM64 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release|x64.ActiveCfg = Release|x64 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release|x64.Build.0 = Release|x64 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release|x86.ActiveCfg = Release|Win32 - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release|x86.Build.0 = Release|Win32 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug|ARM64.Build.0 = Debug|ARM64 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug|x64.ActiveCfg = Debug|x64 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug|x64.Build.0 = Debug|x64 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug|x86.ActiveCfg = Debug|Win32 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug|x86.Build.0 = Debug|Win32 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release|ARM64.ActiveCfg = Release|ARM64 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release|ARM64.Build.0 = Release|ARM64 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release|x64.ActiveCfg = Release|x64 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release|x64.Build.0 = Release|x64 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release|x86.ActiveCfg = Release|Win32 - {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release|x86.Build.0 = Release|Win32 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug|ARM64.Build.0 = Debug|ARM64 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug|x64.ActiveCfg = Debug|x64 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug|x64.Build.0 = Debug|x64 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug|x86.ActiveCfg = Debug|Win32 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug|x86.Build.0 = Debug|Win32 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release|ARM64.ActiveCfg = Release|ARM64 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release|ARM64.Build.0 = Release|ARM64 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release|x64.ActiveCfg = Release|x64 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release|x64.Build.0 = Release|x64 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release|x86.ActiveCfg = Release|Win32 - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release|x86.Build.0 = Release|Win32 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug|ARM64.Build.0 = Debug|ARM64 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug|x64.ActiveCfg = Debug|x64 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug|x64.Build.0 = Debug|x64 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug|x86.ActiveCfg = Debug|Win32 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug|x86.Build.0 = Debug|Win32 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Release|ARM64.ActiveCfg = Release|ARM64 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Release|ARM64.Build.0 = Release|ARM64 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Release|x64.ActiveCfg = Release|x64 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Release|x64.Build.0 = Release|x64 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Release|x86.ActiveCfg = Release|Win32 - {870723DD-945A-4136-B65B-4AF3BF85369C}.Release|x86.Build.0 = Release|Win32 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug|ARM64.Build.0 = Debug|ARM64 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug|x64.ActiveCfg = Debug|x64 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug|x64.Build.0 = Debug|x64 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug|x86.ActiveCfg = Debug|Win32 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug|x86.Build.0 = Debug|Win32 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release|ARM64.ActiveCfg = Release|ARM64 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release|ARM64.Build.0 = Release|ARM64 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release|x64.ActiveCfg = Release|x64 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release|x64.Build.0 = Release|x64 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release|x86.ActiveCfg = Release|Win32 - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release|x86.Build.0 = Release|Win32 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug|ARM64.Build.0 = Debug|ARM64 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug|x64.ActiveCfg = Debug|x64 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug|x64.Build.0 = Debug|x64 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug|x86.ActiveCfg = Debug|Win32 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug|x86.Build.0 = Debug|Win32 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release|ARM64.ActiveCfg = Release|ARM64 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release|ARM64.Build.0 = Release|ARM64 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release|x64.ActiveCfg = Release|x64 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release|x64.Build.0 = Release|x64 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release|x86.ActiveCfg = Release|Win32 - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release|x86.Build.0 = Release|Win32 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug|ARM64.Build.0 = Debug|ARM64 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug|x64.ActiveCfg = Debug|x64 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug|x64.Build.0 = Debug|x64 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug|x86.ActiveCfg = Debug|Win32 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug|x86.Build.0 = Debug|Win32 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release|ARM64.ActiveCfg = Release|ARM64 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release|ARM64.Build.0 = Release|ARM64 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release|x64.ActiveCfg = Release|x64 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release|x64.Build.0 = Release|x64 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release|x86.ActiveCfg = Release|Win32 - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release|x86.Build.0 = Release|Win32 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug|ARM64.Build.0 = Debug|ARM64 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug|x64.ActiveCfg = Debug|x64 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug|x64.Build.0 = Debug|x64 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug|x86.ActiveCfg = Debug|Win32 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug|x86.Build.0 = Debug|Win32 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release|ARM64.ActiveCfg = Release|ARM64 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release|ARM64.Build.0 = Release|ARM64 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release|x64.ActiveCfg = Release|x64 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release|x64.Build.0 = Release|x64 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release|x86.ActiveCfg = Release|Win32 - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release|x86.Build.0 = Release|Win32 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug|ARM64.Build.0 = Debug|ARM64 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug|x64.ActiveCfg = Debug|x64 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug|x64.Build.0 = Debug|x64 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug|x86.ActiveCfg = Debug|Win32 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug|x86.Build.0 = Debug|Win32 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release|ARM64.ActiveCfg = Release|ARM64 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release|ARM64.Build.0 = Release|ARM64 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release|x64.ActiveCfg = Release|x64 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release|x64.Build.0 = Release|x64 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release|x86.ActiveCfg = Release|Win32 - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release|x86.Build.0 = Release|Win32 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug|ARM64.Build.0 = Debug|ARM64 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug|x64.ActiveCfg = Debug|x64 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug|x64.Build.0 = Debug|x64 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug|x86.ActiveCfg = Debug|Win32 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug|x86.Build.0 = Debug|Win32 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release|ARM64.ActiveCfg = Release|ARM64 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release|ARM64.Build.0 = Release|ARM64 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release|x64.ActiveCfg = Release|x64 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release|x64.Build.0 = Release|x64 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release|x86.ActiveCfg = Release|Win32 - {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release|x86.Build.0 = Release|Win32 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug|ARM64.Build.0 = Debug|ARM64 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug|x64.ActiveCfg = Debug|x64 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug|x64.Build.0 = Debug|x64 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug|x86.ActiveCfg = Debug|Win32 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug|x86.Build.0 = Debug|Win32 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Release|ARM64.ActiveCfg = Release|ARM64 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Release|ARM64.Build.0 = Release|ARM64 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Release|x64.ActiveCfg = Release|x64 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Release|x64.Build.0 = Release|x64 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Release|x86.ActiveCfg = Release|Win32 - {0AB968E0-E993-45CE-8875-7453C96DF583}.Release|x86.Build.0 = Release|Win32 - {25923141-9859-4AFE-8168-0DF78322FC63}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {25923141-9859-4AFE-8168-0DF78322FC63}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {25923141-9859-4AFE-8168-0DF78322FC63}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {25923141-9859-4AFE-8168-0DF78322FC63}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {25923141-9859-4AFE-8168-0DF78322FC63}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {25923141-9859-4AFE-8168-0DF78322FC63}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {25923141-9859-4AFE-8168-0DF78322FC63}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {25923141-9859-4AFE-8168-0DF78322FC63}.Debug|ARM64.Build.0 = Debug|ARM64 - {25923141-9859-4AFE-8168-0DF78322FC63}.Debug|x64.ActiveCfg = Debug|x64 - {25923141-9859-4AFE-8168-0DF78322FC63}.Debug|x64.Build.0 = Debug|x64 - {25923141-9859-4AFE-8168-0DF78322FC63}.Debug|x86.ActiveCfg = Debug|Win32 - {25923141-9859-4AFE-8168-0DF78322FC63}.Debug|x86.Build.0 = Debug|Win32 - {25923141-9859-4AFE-8168-0DF78322FC63}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {25923141-9859-4AFE-8168-0DF78322FC63}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {25923141-9859-4AFE-8168-0DF78322FC63}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {25923141-9859-4AFE-8168-0DF78322FC63}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {25923141-9859-4AFE-8168-0DF78322FC63}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {25923141-9859-4AFE-8168-0DF78322FC63}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {25923141-9859-4AFE-8168-0DF78322FC63}.Release|ARM64.ActiveCfg = Release|ARM64 - {25923141-9859-4AFE-8168-0DF78322FC63}.Release|ARM64.Build.0 = Release|ARM64 - {25923141-9859-4AFE-8168-0DF78322FC63}.Release|x64.ActiveCfg = Release|x64 - {25923141-9859-4AFE-8168-0DF78322FC63}.Release|x64.Build.0 = Release|x64 - {25923141-9859-4AFE-8168-0DF78322FC63}.Release|x86.ActiveCfg = Release|Win32 - {25923141-9859-4AFE-8168-0DF78322FC63}.Release|x86.Build.0 = Release|Win32 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug|ARM64.Build.0 = Debug|ARM64 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug|x64.ActiveCfg = Debug|x64 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug|x64.Build.0 = Debug|x64 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug|x86.ActiveCfg = Debug|Win32 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug|x86.Build.0 = Debug|Win32 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Release|ARM64.ActiveCfg = Release|ARM64 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Release|ARM64.Build.0 = Release|ARM64 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Release|x64.ActiveCfg = Release|x64 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Release|x64.Build.0 = Release|x64 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Release|x86.ActiveCfg = Release|Win32 - {7E855020-7FA4-482D-B510-2E709354FE8B}.Release|x86.Build.0 = Release|Win32 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug|ARM64.Build.0 = Debug|ARM64 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug|x64.ActiveCfg = Debug|x64 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug|x64.Build.0 = Debug|x64 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug|x86.ActiveCfg = Debug|Win32 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug|x86.Build.0 = Debug|Win32 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release|ARM64.ActiveCfg = Release|ARM64 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release|ARM64.Build.0 = Release|ARM64 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release|x64.ActiveCfg = Release|x64 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release|x64.Build.0 = Release|x64 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release|x86.ActiveCfg = Release|Win32 - {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release|x86.Build.0 = Release|Win32 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug|ARM64.Build.0 = Debug|ARM64 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug|x64.ActiveCfg = Debug|x64 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug|x64.Build.0 = Debug|x64 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug|x86.ActiveCfg = Debug|Win32 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug|x86.Build.0 = Debug|Win32 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release|ARM64.ActiveCfg = Release|ARM64 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release|ARM64.Build.0 = Release|ARM64 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release|x64.ActiveCfg = Release|x64 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release|x64.Build.0 = Release|x64 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release|x86.ActiveCfg = Release|Win32 - {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release|x86.Build.0 = Release|Win32 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug|ARM64.Build.0 = Debug|ARM64 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug|x64.ActiveCfg = Debug|x64 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug|x64.Build.0 = Debug|x64 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug|x86.ActiveCfg = Debug|Win32 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug|x86.Build.0 = Debug|Win32 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release|ARM64.ActiveCfg = Release|ARM64 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release|ARM64.Build.0 = Release|ARM64 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release|x64.ActiveCfg = Release|x64 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release|x64.Build.0 = Release|x64 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release|x86.ActiveCfg = Release|Win32 - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release|x86.Build.0 = Release|Win32 - {B19DD336-538E-4091-A559-EAA717FEC899}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {B19DD336-538E-4091-A559-EAA717FEC899}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {B19DD336-538E-4091-A559-EAA717FEC899}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {B19DD336-538E-4091-A559-EAA717FEC899}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {B19DD336-538E-4091-A559-EAA717FEC899}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {B19DD336-538E-4091-A559-EAA717FEC899}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {B19DD336-538E-4091-A559-EAA717FEC899}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {B19DD336-538E-4091-A559-EAA717FEC899}.Debug|ARM64.Build.0 = Debug|ARM64 - {B19DD336-538E-4091-A559-EAA717FEC899}.Debug|x64.ActiveCfg = Debug|x64 - {B19DD336-538E-4091-A559-EAA717FEC899}.Debug|x64.Build.0 = Debug|x64 - {B19DD336-538E-4091-A559-EAA717FEC899}.Debug|x86.ActiveCfg = Debug|Win32 - {B19DD336-538E-4091-A559-EAA717FEC899}.Debug|x86.Build.0 = Debug|Win32 - {B19DD336-538E-4091-A559-EAA717FEC899}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {B19DD336-538E-4091-A559-EAA717FEC899}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {B19DD336-538E-4091-A559-EAA717FEC899}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {B19DD336-538E-4091-A559-EAA717FEC899}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {B19DD336-538E-4091-A559-EAA717FEC899}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {B19DD336-538E-4091-A559-EAA717FEC899}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {B19DD336-538E-4091-A559-EAA717FEC899}.Release|ARM64.ActiveCfg = Release|ARM64 - {B19DD336-538E-4091-A559-EAA717FEC899}.Release|ARM64.Build.0 = Release|ARM64 - {B19DD336-538E-4091-A559-EAA717FEC899}.Release|x64.ActiveCfg = Release|x64 - {B19DD336-538E-4091-A559-EAA717FEC899}.Release|x64.Build.0 = Release|x64 - {B19DD336-538E-4091-A559-EAA717FEC899}.Release|x86.ActiveCfg = Release|Win32 - {B19DD336-538E-4091-A559-EAA717FEC899}.Release|x86.Build.0 = Release|Win32 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug|ARM64.Build.0 = Debug|ARM64 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug|x64.ActiveCfg = Debug|x64 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug|x64.Build.0 = Debug|x64 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug|x86.ActiveCfg = Debug|Win32 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug|x86.Build.0 = Debug|Win32 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release|ARM64.ActiveCfg = Release|ARM64 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release|ARM64.Build.0 = Release|ARM64 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release|x64.ActiveCfg = Release|x64 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release|x64.Build.0 = Release|x64 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release|x86.ActiveCfg = Release|Win32 - {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release|x86.Build.0 = Release|Win32 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug|ARM64.Build.0 = Debug|ARM64 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug|x64.ActiveCfg = Debug|x64 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug|x64.Build.0 = Debug|x64 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug|x86.ActiveCfg = Debug|Win32 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug|x86.Build.0 = Debug|Win32 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release|ARM64.ActiveCfg = Release|ARM64 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release|ARM64.Build.0 = Release|ARM64 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release|x64.ActiveCfg = Release|x64 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release|x64.Build.0 = Release|x64 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release|x86.ActiveCfg = Release|Win32 - {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release|x86.Build.0 = Release|Win32 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug|ARM64.Build.0 = Debug|ARM64 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug|x64.ActiveCfg = Debug|x64 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug|x64.Build.0 = Debug|x64 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug|x86.ActiveCfg = Debug|Win32 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug|x86.Build.0 = Debug|Win32 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release|ARM64.ActiveCfg = Release|ARM64 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release|ARM64.Build.0 = Release|ARM64 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release|x64.ActiveCfg = Release|x64 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release|x64.Build.0 = Release|x64 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release|x86.ActiveCfg = Release|Win32 - {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release|x86.Build.0 = Release|Win32 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug|ARM64.Build.0 = Debug|ARM64 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug|x64.ActiveCfg = Debug|x64 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug|x64.Build.0 = Debug|x64 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug|x86.ActiveCfg = Debug|Win32 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug|x86.Build.0 = Debug|Win32 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release|ARM64.ActiveCfg = Release|ARM64 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release|ARM64.Build.0 = Release|ARM64 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release|x64.ActiveCfg = Release|x64 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release|x64.Build.0 = Release|x64 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release|x86.ActiveCfg = Release|Win32 - {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release|x86.Build.0 = Release|Win32 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug|ARM64.Build.0 = Debug|ARM64 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug|x64.ActiveCfg = Debug|x64 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug|x64.Build.0 = Debug|x64 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug|x86.ActiveCfg = Debug|Win32 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug|x86.Build.0 = Debug|Win32 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release|ARM64.ActiveCfg = Release|ARM64 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release|ARM64.Build.0 = Release|ARM64 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release|x64.ActiveCfg = Release|x64 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release|x64.Build.0 = Release|x64 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release|x86.ActiveCfg = Release|Win32 - {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release|x86.Build.0 = Release|Win32 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug|ARM64.Build.0 = Debug|ARM64 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug|x64.ActiveCfg = Debug|x64 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug|x64.Build.0 = Debug|x64 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug|x86.ActiveCfg = Debug|Win32 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug|x86.Build.0 = Debug|Win32 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release|ARM64.ActiveCfg = Release|ARM64 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release|ARM64.Build.0 = Release|ARM64 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release|x64.ActiveCfg = Release|x64 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release|x64.Build.0 = Release|x64 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release|x86.ActiveCfg = Release|Win32 - {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release|x86.Build.0 = Release|Win32 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug|ARM64.Build.0 = Debug|ARM64 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug|x64.ActiveCfg = Debug|x64 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug|x64.Build.0 = Debug|x64 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug|x86.ActiveCfg = Debug|Win32 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug|x86.Build.0 = Debug|Win32 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Release|ARM64.ActiveCfg = Release|ARM64 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Release|ARM64.Build.0 = Release|ARM64 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Release|x64.ActiveCfg = Release|x64 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Release|x64.Build.0 = Release|x64 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Release|x86.ActiveCfg = Release|Win32 - {25BCB876-B60A-499B-9046-E9801CFD7780}.Release|x86.Build.0 = Release|Win32 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug|ARM64.Build.0 = Debug|ARM64 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug|x64.ActiveCfg = Debug|x64 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug|x64.Build.0 = Debug|x64 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug|x86.ActiveCfg = Debug|Win32 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug|x86.Build.0 = Debug|Win32 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release|ARM64.ActiveCfg = Release|ARM64 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release|ARM64.Build.0 = Release|ARM64 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release|x64.ActiveCfg = Release|x64 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release|x64.Build.0 = Release|x64 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release|x86.ActiveCfg = Release|Win32 - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release|x86.Build.0 = Release|Win32 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug|ARM64.Build.0 = Debug|ARM64 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug|x64.ActiveCfg = Debug|x64 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug|x64.Build.0 = Debug|x64 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug|x86.ActiveCfg = Debug|Win32 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug|x86.Build.0 = Debug|Win32 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release|ARM64.ActiveCfg = Release|ARM64 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release|ARM64.Build.0 = Release|ARM64 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release|x64.ActiveCfg = Release|x64 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release|x64.Build.0 = Release|x64 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release|x86.ActiveCfg = Release|Win32 - {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release|x86.Build.0 = Release|Win32 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug|ARM64.Build.0 = Debug|ARM64 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug|x64.ActiveCfg = Debug|x64 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug|x64.Build.0 = Debug|x64 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug|x86.ActiveCfg = Debug|Win32 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug|x86.Build.0 = Debug|Win32 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release|ARM64.ActiveCfg = Release|ARM64 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release|ARM64.Build.0 = Release|ARM64 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release|x64.ActiveCfg = Release|x64 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release|x64.Build.0 = Release|x64 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release|x86.ActiveCfg = Release|Win32 - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release|x86.Build.0 = Release|Win32 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug|ARM64.Build.0 = Debug|ARM64 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug|x64.ActiveCfg = Debug|x64 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug|x64.Build.0 = Debug|x64 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug|x86.ActiveCfg = Debug|Win32 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug|x86.Build.0 = Debug|Win32 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release|ARM64.ActiveCfg = Release|ARM64 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release|ARM64.Build.0 = Release|ARM64 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release|x64.ActiveCfg = Release|x64 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release|x64.Build.0 = Release|x64 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release|x86.ActiveCfg = Release|Win32 - {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release|x86.Build.0 = Release|Win32 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug|ARM64.Build.0 = Debug|ARM64 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug|x64.ActiveCfg = Debug|x64 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug|x64.Build.0 = Debug|x64 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug|x86.ActiveCfg = Debug|Win32 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug|x86.Build.0 = Debug|Win32 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release|ARM64.ActiveCfg = Release|ARM64 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release|ARM64.Build.0 = Release|ARM64 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release|x64.ActiveCfg = Release|x64 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release|x64.Build.0 = Release|x64 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release|x86.ActiveCfg = Release|Win32 - {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release|x86.Build.0 = Release|Win32 - {D550AB93-DF31-4B76-873F-F075018352F4}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {D550AB93-DF31-4B76-873F-F075018352F4}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {D550AB93-DF31-4B76-873F-F075018352F4}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {D550AB93-DF31-4B76-873F-F075018352F4}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {D550AB93-DF31-4B76-873F-F075018352F4}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {D550AB93-DF31-4B76-873F-F075018352F4}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {D550AB93-DF31-4B76-873F-F075018352F4}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {D550AB93-DF31-4B76-873F-F075018352F4}.Debug|ARM64.Build.0 = Debug|ARM64 - {D550AB93-DF31-4B76-873F-F075018352F4}.Debug|x64.ActiveCfg = Debug|x64 - {D550AB93-DF31-4B76-873F-F075018352F4}.Debug|x64.Build.0 = Debug|x64 - {D550AB93-DF31-4B76-873F-F075018352F4}.Debug|x86.ActiveCfg = Debug|Win32 - {D550AB93-DF31-4B76-873F-F075018352F4}.Debug|x86.Build.0 = Debug|Win32 - {D550AB93-DF31-4B76-873F-F075018352F4}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {D550AB93-DF31-4B76-873F-F075018352F4}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {D550AB93-DF31-4B76-873F-F075018352F4}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {D550AB93-DF31-4B76-873F-F075018352F4}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {D550AB93-DF31-4B76-873F-F075018352F4}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {D550AB93-DF31-4B76-873F-F075018352F4}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {D550AB93-DF31-4B76-873F-F075018352F4}.Release|ARM64.ActiveCfg = Release|ARM64 - {D550AB93-DF31-4B76-873F-F075018352F4}.Release|ARM64.Build.0 = Release|ARM64 - {D550AB93-DF31-4B76-873F-F075018352F4}.Release|x64.ActiveCfg = Release|x64 - {D550AB93-DF31-4B76-873F-F075018352F4}.Release|x64.Build.0 = Release|x64 - {D550AB93-DF31-4B76-873F-F075018352F4}.Release|x86.ActiveCfg = Release|Win32 - {D550AB93-DF31-4B76-873F-F075018352F4}.Release|x86.Build.0 = Release|Win32 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug|ARM64.Build.0 = Debug|ARM64 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug|x64.ActiveCfg = Debug|x64 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug|x64.Build.0 = Debug|x64 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug|x86.ActiveCfg = Debug|Win32 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug|x86.Build.0 = Debug|Win32 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release|ARM64.ActiveCfg = Release|ARM64 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release|ARM64.Build.0 = Release|ARM64 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release|x64.ActiveCfg = Release|x64 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release|x64.Build.0 = Release|x64 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release|x86.ActiveCfg = Release|Win32 - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release|x86.Build.0 = Release|Win32 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug|ARM64.Build.0 = Debug|ARM64 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug|x64.ActiveCfg = Debug|x64 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug|x64.Build.0 = Debug|x64 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug|x86.ActiveCfg = Debug|Win32 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug|x86.Build.0 = Debug|Win32 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release|ARM64.ActiveCfg = Release|ARM64 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release|ARM64.Build.0 = Release|ARM64 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release|x64.ActiveCfg = Release|x64 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release|x64.Build.0 = Release|x64 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release|x86.ActiveCfg = Release|Win32 - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release|x86.Build.0 = Release|Win32 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug|ARM64.Build.0 = Debug|ARM64 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug|x64.ActiveCfg = Debug|x64 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug|x64.Build.0 = Debug|x64 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug|x86.ActiveCfg = Debug|Win32 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug|x86.Build.0 = Debug|Win32 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release|ARM64.ActiveCfg = Release|ARM64 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release|ARM64.Build.0 = Release|ARM64 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release|x64.ActiveCfg = Release|x64 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release|x64.Build.0 = Release|x64 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release|x86.ActiveCfg = Release|Win32 - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release|x86.Build.0 = Release|Win32 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug|ARM64.Build.0 = Debug|ARM64 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug|x64.ActiveCfg = Debug|x64 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug|x64.Build.0 = Debug|x64 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug|x86.ActiveCfg = Debug|Win32 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug|x86.Build.0 = Debug|Win32 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release|ARM64.ActiveCfg = Release|ARM64 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release|ARM64.Build.0 = Release|ARM64 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release|x64.ActiveCfg = Release|x64 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release|x64.Build.0 = Release|x64 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release|x86.ActiveCfg = Release|Win32 - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release|x86.Build.0 = Release|Win32 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug|ARM64.Build.0 = Debug|ARM64 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug|x64.ActiveCfg = Debug|x64 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug|x64.Build.0 = Debug|x64 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug|x86.ActiveCfg = Debug|Win32 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug|x86.Build.0 = Debug|Win32 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release|ARM64.ActiveCfg = Release|ARM64 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release|ARM64.Build.0 = Release|ARM64 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release|x64.ActiveCfg = Release|x64 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release|x64.Build.0 = Release|x64 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release|x86.ActiveCfg = Release|Win32 - {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release|x86.Build.0 = Release|Win32 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug|ARM64.Build.0 = Debug|ARM64 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug|x64.ActiveCfg = Debug|x64 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug|x64.Build.0 = Debug|x64 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug|x86.ActiveCfg = Debug|Win32 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug|x86.Build.0 = Debug|Win32 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release|ARM64.ActiveCfg = Release|ARM64 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release|ARM64.Build.0 = Release|ARM64 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release|x64.ActiveCfg = Release|x64 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release|x64.Build.0 = Release|x64 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release|x86.ActiveCfg = Release|Win32 - {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release|x86.Build.0 = Release|Win32 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug|ARM64.Build.0 = Debug|ARM64 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug|x64.ActiveCfg = Debug|x64 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug|x64.Build.0 = Debug|x64 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug|x86.ActiveCfg = Debug|Win32 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug|x86.Build.0 = Debug|Win32 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release|ARM64.ActiveCfg = Release|ARM64 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release|ARM64.Build.0 = Release|ARM64 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release|x64.ActiveCfg = Release|x64 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release|x64.Build.0 = Release|x64 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release|x86.ActiveCfg = Release|Win32 - {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release|x86.Build.0 = Release|Win32 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug|ARM64.Build.0 = Debug|ARM64 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug|x64.ActiveCfg = Debug|x64 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug|x64.Build.0 = Debug|x64 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug|x86.ActiveCfg = Debug|Win32 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug|x86.Build.0 = Debug|Win32 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release|ARM64.ActiveCfg = Release|ARM64 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release|ARM64.Build.0 = Release|ARM64 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release|x64.ActiveCfg = Release|x64 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release|x64.Build.0 = Release|x64 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release|x86.ActiveCfg = Release|Win32 - {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release|x86.Build.0 = Release|Win32 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug|ARM64.Build.0 = Debug|ARM64 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug|x64.ActiveCfg = Debug|x64 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug|x64.Build.0 = Debug|x64 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug|x86.ActiveCfg = Debug|Win32 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug|x86.Build.0 = Debug|Win32 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release|ARM64.ActiveCfg = Release|ARM64 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release|ARM64.Build.0 = Release|ARM64 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release|x64.ActiveCfg = Release|x64 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release|x64.Build.0 = Release|x64 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release|x86.ActiveCfg = Release|Win32 - {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release|x86.Build.0 = Release|Win32 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug|ARM64.Build.0 = Debug|ARM64 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug|x64.ActiveCfg = Debug|x64 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug|x64.Build.0 = Debug|x64 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug|x86.ActiveCfg = Debug|Win32 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug|x86.Build.0 = Debug|Win32 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release|ARM64.ActiveCfg = Release|ARM64 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release|ARM64.Build.0 = Release|ARM64 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release|x64.ActiveCfg = Release|x64 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release|x64.Build.0 = Release|x64 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release|x86.ActiveCfg = Release|Win32 - {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release|x86.Build.0 = Release|Win32 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug|ARM64.Build.0 = Debug|ARM64 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug|x64.ActiveCfg = Debug|x64 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug|x64.Build.0 = Debug|x64 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug|x86.ActiveCfg = Debug|Win32 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug|x86.Build.0 = Debug|Win32 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Release|ARM64.ActiveCfg = Release|ARM64 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Release|ARM64.Build.0 = Release|ARM64 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Release|x64.ActiveCfg = Release|x64 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Release|x64.Build.0 = Release|x64 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Release|x86.ActiveCfg = Release|Win32 - {33459B4E-1839-4856-BF6B-22480D11FE31}.Release|x86.Build.0 = Release|Win32 - {48871156-181A-475A-BD8D-200086A09675}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {48871156-181A-475A-BD8D-200086A09675}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {48871156-181A-475A-BD8D-200086A09675}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {48871156-181A-475A-BD8D-200086A09675}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {48871156-181A-475A-BD8D-200086A09675}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {48871156-181A-475A-BD8D-200086A09675}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {48871156-181A-475A-BD8D-200086A09675}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {48871156-181A-475A-BD8D-200086A09675}.Debug|ARM64.Build.0 = Debug|ARM64 - {48871156-181A-475A-BD8D-200086A09675}.Debug|x64.ActiveCfg = Debug|x64 - {48871156-181A-475A-BD8D-200086A09675}.Debug|x64.Build.0 = Debug|x64 - {48871156-181A-475A-BD8D-200086A09675}.Debug|x86.ActiveCfg = Debug|Win32 - {48871156-181A-475A-BD8D-200086A09675}.Debug|x86.Build.0 = Debug|Win32 - {48871156-181A-475A-BD8D-200086A09675}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {48871156-181A-475A-BD8D-200086A09675}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {48871156-181A-475A-BD8D-200086A09675}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {48871156-181A-475A-BD8D-200086A09675}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {48871156-181A-475A-BD8D-200086A09675}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {48871156-181A-475A-BD8D-200086A09675}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {48871156-181A-475A-BD8D-200086A09675}.Release|ARM64.ActiveCfg = Release|ARM64 - {48871156-181A-475A-BD8D-200086A09675}.Release|ARM64.Build.0 = Release|ARM64 - {48871156-181A-475A-BD8D-200086A09675}.Release|x64.ActiveCfg = Release|x64 - {48871156-181A-475A-BD8D-200086A09675}.Release|x64.Build.0 = Release|x64 - {48871156-181A-475A-BD8D-200086A09675}.Release|x86.ActiveCfg = Release|Win32 - {48871156-181A-475A-BD8D-200086A09675}.Release|x86.Build.0 = Release|Win32 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug|ARM64.Build.0 = Debug|ARM64 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug|x64.ActiveCfg = Debug|x64 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug|x64.Build.0 = Debug|x64 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug|x86.ActiveCfg = Debug|Win32 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug|x86.Build.0 = Debug|Win32 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release|ARM64.ActiveCfg = Release|ARM64 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release|ARM64.Build.0 = Release|ARM64 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release|x64.ActiveCfg = Release|x64 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release|x64.Build.0 = Release|x64 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release|x86.ActiveCfg = Release|Win32 - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release|x86.Build.0 = Release|Win32 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug|ARM64.Build.0 = Debug|ARM64 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug|x64.ActiveCfg = Debug|x64 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug|x64.Build.0 = Debug|x64 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug|x86.ActiveCfg = Debug|Win32 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug|x86.Build.0 = Debug|Win32 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release|ARM64.ActiveCfg = Release|ARM64 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release|ARM64.Build.0 = Release|ARM64 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release|x64.ActiveCfg = Release|x64 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release|x64.Build.0 = Release|x64 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release|x86.ActiveCfg = Release|Win32 - {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release|x86.Build.0 = Release|Win32 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug|ARM64.Build.0 = Debug|ARM64 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug|x64.ActiveCfg = Debug|x64 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug|x64.Build.0 = Debug|x64 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug|x86.ActiveCfg = Debug|Win32 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug|x86.Build.0 = Debug|Win32 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release|ARM64.ActiveCfg = Release|ARM64 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release|ARM64.Build.0 = Release|ARM64 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release|x64.ActiveCfg = Release|x64 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release|x64.Build.0 = Release|x64 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release|x86.ActiveCfg = Release|Win32 - {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release|x86.Build.0 = Release|Win32 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug|ARM64.Build.0 = Debug|ARM64 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug|x64.ActiveCfg = Debug|x64 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug|x64.Build.0 = Debug|x64 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug|x86.ActiveCfg = Debug|Win32 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug|x86.Build.0 = Debug|Win32 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release|ARM64.ActiveCfg = Release|ARM64 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release|ARM64.Build.0 = Release|ARM64 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release|x64.ActiveCfg = Release|x64 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release|x64.Build.0 = Release|x64 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release|x86.ActiveCfg = Release|Win32 - {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release|x86.Build.0 = Release|Win32 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug|ARM64.Build.0 = Debug|ARM64 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug|x64.ActiveCfg = Debug|x64 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug|x64.Build.0 = Debug|x64 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug|x86.ActiveCfg = Debug|Win32 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug|x86.Build.0 = Debug|Win32 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release|ARM64.ActiveCfg = Release|ARM64 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release|ARM64.Build.0 = Release|ARM64 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release|x64.ActiveCfg = Release|x64 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release|x64.Build.0 = Release|x64 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release|x86.ActiveCfg = Release|Win32 - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release|x86.Build.0 = Release|Win32 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug|ARM64.Build.0 = Debug|ARM64 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug|x64.ActiveCfg = Debug|x64 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug|x64.Build.0 = Debug|x64 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug|x86.ActiveCfg = Debug|Win32 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug|x86.Build.0 = Debug|Win32 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release|ARM64.ActiveCfg = Release|ARM64 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release|ARM64.Build.0 = Release|ARM64 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release|x64.ActiveCfg = Release|x64 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release|x64.Build.0 = Release|x64 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release|x86.ActiveCfg = Release|Win32 - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release|x86.Build.0 = Release|Win32 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug|ARM64.Build.0 = Debug|ARM64 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug|x64.ActiveCfg = Debug|x64 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug|x64.Build.0 = Debug|x64 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug|x86.ActiveCfg = Debug|Win32 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug|x86.Build.0 = Debug|Win32 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release|ARM64.ActiveCfg = Release|ARM64 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release|ARM64.Build.0 = Release|ARM64 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release|x64.ActiveCfg = Release|x64 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release|x64.Build.0 = Release|x64 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release|x86.ActiveCfg = Release|Win32 - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release|x86.Build.0 = Release|Win32 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug|ARM64.Build.0 = Debug|ARM64 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug|x64.ActiveCfg = Debug|x64 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug|x64.Build.0 = Debug|x64 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug|x86.ActiveCfg = Debug|Win32 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug|x86.Build.0 = Debug|Win32 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release|ARM64.ActiveCfg = Release|ARM64 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release|ARM64.Build.0 = Release|ARM64 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release|x64.ActiveCfg = Release|x64 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release|x64.Build.0 = Release|x64 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release|x86.ActiveCfg = Release|Win32 - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release|x86.Build.0 = Release|Win32 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug|ARM64.Build.0 = Debug|ARM64 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug|x64.ActiveCfg = Debug|x64 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug|x64.Build.0 = Debug|x64 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug|x86.ActiveCfg = Debug|Win32 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug|x86.Build.0 = Debug|Win32 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release|ARM64.ActiveCfg = Release|ARM64 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release|ARM64.Build.0 = Release|ARM64 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release|x64.ActiveCfg = Release|x64 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release|x64.Build.0 = Release|x64 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release|x86.ActiveCfg = Release|Win32 - {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release|x86.Build.0 = Release|Win32 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug|ARM64.Build.0 = Debug|ARM64 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug|x64.ActiveCfg = Debug|x64 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug|x64.Build.0 = Debug|x64 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug|x86.ActiveCfg = Debug|Win32 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug|x86.Build.0 = Debug|Win32 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release|ARM64.ActiveCfg = Release|ARM64 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release|ARM64.Build.0 = Release|ARM64 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release|x64.ActiveCfg = Release|x64 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release|x64.Build.0 = Release|x64 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release|x86.ActiveCfg = Release|Win32 - {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release|x86.Build.0 = Release|Win32 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug|ARM64.Build.0 = Debug|ARM64 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug|x64.ActiveCfg = Debug|x64 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug|x64.Build.0 = Debug|x64 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug|x86.ActiveCfg = Debug|Win32 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug|x86.Build.0 = Debug|Win32 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release|ARM64.ActiveCfg = Release|ARM64 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release|ARM64.Build.0 = Release|ARM64 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release|x64.ActiveCfg = Release|x64 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release|x64.Build.0 = Release|x64 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release|x86.ActiveCfg = Release|Win32 - {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release|x86.Build.0 = Release|Win32 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug|ARM64.Build.0 = Debug|ARM64 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug|x64.ActiveCfg = Debug|x64 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug|x64.Build.0 = Debug|x64 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug|x86.ActiveCfg = Debug|Win32 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug|x86.Build.0 = Debug|Win32 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release|ARM64.ActiveCfg = Release|ARM64 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release|ARM64.Build.0 = Release|ARM64 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release|x64.ActiveCfg = Release|x64 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release|x64.Build.0 = Release|x64 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release|x86.ActiveCfg = Release|Win32 - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release|x86.Build.0 = Release|Win32 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug|ARM64.Build.0 = Debug|ARM64 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug|x64.ActiveCfg = Debug|x64 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug|x64.Build.0 = Debug|x64 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug|x86.ActiveCfg = Debug|Win32 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug|x86.Build.0 = Debug|Win32 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release|ARM64.ActiveCfg = Release|ARM64 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release|ARM64.Build.0 = Release|ARM64 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release|x64.ActiveCfg = Release|x64 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release|x64.Build.0 = Release|x64 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release|x86.ActiveCfg = Release|Win32 - {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release|x86.Build.0 = Release|Win32 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug|ARM64.Build.0 = Debug|ARM64 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug|x64.ActiveCfg = Debug|x64 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug|x64.Build.0 = Debug|x64 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug|x86.ActiveCfg = Debug|Win32 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug|x86.Build.0 = Debug|Win32 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release|ARM64.ActiveCfg = Release|ARM64 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release|ARM64.Build.0 = Release|ARM64 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release|x64.ActiveCfg = Release|x64 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release|x64.Build.0 = Release|x64 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release|x86.ActiveCfg = Release|Win32 - {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release|x86.Build.0 = Release|Win32 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug|ARM64.Build.0 = Debug|ARM64 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug|x64.ActiveCfg = Debug|x64 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug|x64.Build.0 = Debug|x64 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug|x86.ActiveCfg = Debug|Win32 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug|x86.Build.0 = Debug|Win32 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release|ARM64.ActiveCfg = Release|ARM64 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release|ARM64.Build.0 = Release|ARM64 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release|x64.ActiveCfg = Release|x64 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release|x64.Build.0 = Release|x64 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release|x86.ActiveCfg = Release|Win32 - {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release|x86.Build.0 = Release|Win32 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug|ARM64.Build.0 = Debug|ARM64 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug|x64.ActiveCfg = Debug|x64 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug|x64.Build.0 = Debug|x64 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug|x86.ActiveCfg = Debug|Win32 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug|x86.Build.0 = Debug|Win32 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Release|ARM64.ActiveCfg = Release|ARM64 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Release|ARM64.Build.0 = Release|ARM64 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Release|x64.ActiveCfg = Release|x64 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Release|x64.Build.0 = Release|x64 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Release|x86.ActiveCfg = Release|Win32 - {274C0319-7E1E-4188-936B-8DF3331230B3}.Release|x86.Build.0 = Release|Win32 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug|ARM64.Build.0 = Debug|ARM64 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug|x64.ActiveCfg = Debug|x64 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug|x64.Build.0 = Debug|x64 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug|x86.ActiveCfg = Debug|Win32 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug|x86.Build.0 = Debug|Win32 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release|ARM64.ActiveCfg = Release|ARM64 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release|ARM64.Build.0 = Release|ARM64 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release|x64.ActiveCfg = Release|x64 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release|x64.Build.0 = Release|x64 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release|x86.ActiveCfg = Release|Win32 - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release|x86.Build.0 = Release|Win32 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug|ARM64.Build.0 = Debug|ARM64 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug|x64.ActiveCfg = Debug|x64 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug|x64.Build.0 = Debug|x64 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug|x86.ActiveCfg = Debug|Win32 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug|x86.Build.0 = Debug|Win32 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release|ARM64.ActiveCfg = Release|ARM64 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release|ARM64.Build.0 = Release|ARM64 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release|x64.ActiveCfg = Release|x64 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release|x64.Build.0 = Release|x64 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release|x86.ActiveCfg = Release|Win32 - {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release|x86.Build.0 = Release|Win32 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug|ARM64.Build.0 = Debug|ARM64 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug|x64.ActiveCfg = Debug|x64 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug|x64.Build.0 = Debug|x64 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug|x86.ActiveCfg = Debug|Win32 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug|x86.Build.0 = Debug|Win32 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release|ARM64.ActiveCfg = Release|ARM64 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release|ARM64.Build.0 = Release|ARM64 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release|x64.ActiveCfg = Release|x64 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release|x64.Build.0 = Release|x64 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release|x86.ActiveCfg = Release|Win32 - {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release|x86.Build.0 = Release|Win32 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug|ARM64.Build.0 = Debug|ARM64 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug|x64.ActiveCfg = Debug|x64 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug|x64.Build.0 = Debug|x64 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug|x86.ActiveCfg = Debug|Win32 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug|x86.Build.0 = Debug|Win32 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release|ARM64.ActiveCfg = Release|ARM64 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release|ARM64.Build.0 = Release|ARM64 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release|x64.ActiveCfg = Release|x64 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release|x64.Build.0 = Release|x64 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release|x86.ActiveCfg = Release|Win32 - {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release|x86.Build.0 = Release|Win32 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug|ARM64.Build.0 = Debug|ARM64 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug|x64.ActiveCfg = Debug|x64 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug|x64.Build.0 = Debug|x64 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug|x86.ActiveCfg = Debug|Win32 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug|x86.Build.0 = Debug|Win32 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release|ARM64.ActiveCfg = Release|ARM64 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release|ARM64.Build.0 = Release|ARM64 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release|x64.ActiveCfg = Release|x64 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release|x64.Build.0 = Release|x64 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release|x86.ActiveCfg = Release|Win32 - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release|x86.Build.0 = Release|Win32 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug|ARM64.Build.0 = Debug|ARM64 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug|x64.ActiveCfg = Debug|x64 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug|x64.Build.0 = Debug|x64 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug|x86.ActiveCfg = Debug|Win32 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug|x86.Build.0 = Debug|Win32 - {FDE6080B-E203-4066-910D-AD0302566008}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {FDE6080B-E203-4066-910D-AD0302566008}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {FDE6080B-E203-4066-910D-AD0302566008}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {FDE6080B-E203-4066-910D-AD0302566008}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {FDE6080B-E203-4066-910D-AD0302566008}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {FDE6080B-E203-4066-910D-AD0302566008}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {FDE6080B-E203-4066-910D-AD0302566008}.Release|ARM64.ActiveCfg = Release|ARM64 - {FDE6080B-E203-4066-910D-AD0302566008}.Release|ARM64.Build.0 = Release|ARM64 - {FDE6080B-E203-4066-910D-AD0302566008}.Release|x64.ActiveCfg = Release|x64 - {FDE6080B-E203-4066-910D-AD0302566008}.Release|x64.Build.0 = Release|x64 - {FDE6080B-E203-4066-910D-AD0302566008}.Release|x86.ActiveCfg = Release|Win32 - {FDE6080B-E203-4066-910D-AD0302566008}.Release|x86.Build.0 = Release|Win32 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug|ARM64.Build.0 = Debug|ARM64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug|x64.ActiveCfg = Debug|x64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug|x64.Build.0 = Debug|x64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug|x86.ActiveCfg = Debug|Win32 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug|x86.Build.0 = Debug|Win32 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release|ARM64.ActiveCfg = Release|ARM64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release|ARM64.Build.0 = Release|ARM64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release|x64.ActiveCfg = Release|x64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release|x64.Build.0 = Release|x64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release|x86.ActiveCfg = Release|Win32 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release|x86.Build.0 = Release|Win32 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug|ARM64.Build.0 = Debug|ARM64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug|x64.ActiveCfg = Debug|x64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug|x64.Build.0 = Debug|x64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug|x86.ActiveCfg = Debug|Win32 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug|x86.Build.0 = Debug|Win32 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|ARM64.ActiveCfg = Release|ARM64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|ARM64.Build.0 = Release|ARM64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|x64.ActiveCfg = Release|x64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|x64.Build.0 = Release|x64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|x86.ActiveCfg = Release|Win32 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|x86.Build.0 = Release|Win32 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug|ARM64.Build.0 = Debug|ARM64 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug|x64.ActiveCfg = Debug|x64 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug|x64.Build.0 = Debug|x64 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug|x86.ActiveCfg = Debug|Win32 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug|x86.Build.0 = Debug|Win32 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release|ARM64.ActiveCfg = Release|ARM64 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release|ARM64.Build.0 = Release|ARM64 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release|x64.ActiveCfg = Release|x64 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release|x64.Build.0 = Release|x64 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release|x86.ActiveCfg = Release|Win32 - {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release|x86.Build.0 = Release|Win32 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug|ARM64.Build.0 = Debug|ARM64 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug|x64.ActiveCfg = Debug|x64 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug|x64.Build.0 = Debug|x64 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug|x86.ActiveCfg = Debug|Win32 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug|x86.Build.0 = Debug|Win32 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release|ARM64.ActiveCfg = Release|ARM64 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release|ARM64.Build.0 = Release|ARM64 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release|x64.ActiveCfg = Release|x64 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release|x64.Build.0 = Release|x64 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release|x86.ActiveCfg = Release|Win32 - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release|x86.Build.0 = Release|Win32 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug|ARM64.Build.0 = Debug|ARM64 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug|x64.ActiveCfg = Debug|x64 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug|x64.Build.0 = Debug|x64 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug|x86.ActiveCfg = Debug|Win32 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug|x86.Build.0 = Debug|Win32 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release|ARM64.ActiveCfg = Release|ARM64 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release|ARM64.Build.0 = Release|ARM64 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release|x64.ActiveCfg = Release|x64 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release|x64.Build.0 = Release|x64 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release|x86.ActiveCfg = Release|Win32 - {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release|x86.Build.0 = Release|Win32 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug|ARM64.Build.0 = Debug|ARM64 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug|x64.ActiveCfg = Debug|x64 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug|x64.Build.0 = Debug|x64 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug|x86.ActiveCfg = Debug|Win32 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug|x86.Build.0 = Debug|Win32 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release|ARM64.ActiveCfg = Release|ARM64 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release|ARM64.Build.0 = Release|ARM64 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release|x64.ActiveCfg = Release|x64 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release|x64.Build.0 = Release|x64 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release|x86.ActiveCfg = Release|Win32 - {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release|x86.Build.0 = Release|Win32 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug|ARM64.Build.0 = Debug|ARM64 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug|x64.ActiveCfg = Debug|x64 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug|x64.Build.0 = Debug|x64 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug|x86.ActiveCfg = Debug|Win32 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug|x86.Build.0 = Debug|Win32 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release|ARM64.ActiveCfg = Release|ARM64 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release|ARM64.Build.0 = Release|ARM64 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release|x64.ActiveCfg = Release|x64 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release|x64.Build.0 = Release|x64 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release|x86.ActiveCfg = Release|Win32 - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release|x86.Build.0 = Release|Win32 - {191A5289-BA65-4638-A215-C521F0187313}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {191A5289-BA65-4638-A215-C521F0187313}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {191A5289-BA65-4638-A215-C521F0187313}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {191A5289-BA65-4638-A215-C521F0187313}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {191A5289-BA65-4638-A215-C521F0187313}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {191A5289-BA65-4638-A215-C521F0187313}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {191A5289-BA65-4638-A215-C521F0187313}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {191A5289-BA65-4638-A215-C521F0187313}.Debug|ARM64.Build.0 = Debug|ARM64 - {191A5289-BA65-4638-A215-C521F0187313}.Debug|x64.ActiveCfg = Debug|x64 - {191A5289-BA65-4638-A215-C521F0187313}.Debug|x64.Build.0 = Debug|x64 - {191A5289-BA65-4638-A215-C521F0187313}.Debug|x86.ActiveCfg = Debug|Win32 - {191A5289-BA65-4638-A215-C521F0187313}.Debug|x86.Build.0 = Debug|Win32 - {191A5289-BA65-4638-A215-C521F0187313}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {191A5289-BA65-4638-A215-C521F0187313}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {191A5289-BA65-4638-A215-C521F0187313}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {191A5289-BA65-4638-A215-C521F0187313}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {191A5289-BA65-4638-A215-C521F0187313}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {191A5289-BA65-4638-A215-C521F0187313}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {191A5289-BA65-4638-A215-C521F0187313}.Release|ARM64.ActiveCfg = Release|ARM64 - {191A5289-BA65-4638-A215-C521F0187313}.Release|ARM64.Build.0 = Release|ARM64 - {191A5289-BA65-4638-A215-C521F0187313}.Release|x64.ActiveCfg = Release|x64 - {191A5289-BA65-4638-A215-C521F0187313}.Release|x64.Build.0 = Release|x64 - {191A5289-BA65-4638-A215-C521F0187313}.Release|x86.ActiveCfg = Release|Win32 - {191A5289-BA65-4638-A215-C521F0187313}.Release|x86.Build.0 = Release|Win32 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug|ARM64.Build.0 = Debug|ARM64 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug|x64.ActiveCfg = Debug|x64 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug|x64.Build.0 = Debug|x64 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug|x86.ActiveCfg = Debug|Win32 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug|x86.Build.0 = Debug|Win32 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release|ARM64.ActiveCfg = Release|ARM64 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release|ARM64.Build.0 = Release|ARM64 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release|x64.ActiveCfg = Release|x64 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release|x64.Build.0 = Release|x64 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release|x86.ActiveCfg = Release|Win32 - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release|x86.Build.0 = Release|Win32 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug|ARM64.Build.0 = Debug|ARM64 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug|x64.ActiveCfg = Debug|x64 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug|x64.Build.0 = Debug|x64 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug|x86.ActiveCfg = Debug|Win32 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug|x86.Build.0 = Debug|Win32 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release|ARM64.ActiveCfg = Release|ARM64 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release|ARM64.Build.0 = Release|ARM64 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release|x64.ActiveCfg = Release|x64 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release|x64.Build.0 = Release|x64 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release|x86.ActiveCfg = Release|Win32 - {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release|x86.Build.0 = Release|Win32 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug|ARM64.Build.0 = Debug|ARM64 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug|x64.ActiveCfg = Debug|x64 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug|x64.Build.0 = Debug|x64 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug|x86.ActiveCfg = Debug|Win32 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug|x86.Build.0 = Debug|Win32 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release|ARM64.ActiveCfg = Release|ARM64 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release|ARM64.Build.0 = Release|ARM64 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release|x64.ActiveCfg = Release|x64 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release|x64.Build.0 = Release|x64 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release|x86.ActiveCfg = Release|Win32 - {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release|x86.Build.0 = Release|Win32 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug|ARM64.Build.0 = Debug|ARM64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug|x64.ActiveCfg = Debug|x64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug|x64.Build.0 = Debug|x64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug|x86.ActiveCfg = Debug|Win32 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug|x86.Build.0 = Debug|Win32 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release|ARM64.ActiveCfg = Release|ARM64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release|ARM64.Build.0 = Release|ARM64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release|x64.ActiveCfg = Release|x64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release|x64.Build.0 = Release|x64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release|x86.ActiveCfg = Release|Win32 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release|x86.Build.0 = Release|Win32 - {27B110CC-43C0-400A-89D9-245E681647D7}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {27B110CC-43C0-400A-89D9-245E681647D7}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {27B110CC-43C0-400A-89D9-245E681647D7}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {27B110CC-43C0-400A-89D9-245E681647D7}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {27B110CC-43C0-400A-89D9-245E681647D7}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {27B110CC-43C0-400A-89D9-245E681647D7}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {27B110CC-43C0-400A-89D9-245E681647D7}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {27B110CC-43C0-400A-89D9-245E681647D7}.Debug|ARM64.Build.0 = Debug|ARM64 - {27B110CC-43C0-400A-89D9-245E681647D7}.Debug|x64.ActiveCfg = Debug|x64 - {27B110CC-43C0-400A-89D9-245E681647D7}.Debug|x64.Build.0 = Debug|x64 - {27B110CC-43C0-400A-89D9-245E681647D7}.Debug|x86.ActiveCfg = Debug|Win32 - {27B110CC-43C0-400A-89D9-245E681647D7}.Debug|x86.Build.0 = Debug|Win32 - {27B110CC-43C0-400A-89D9-245E681647D7}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {27B110CC-43C0-400A-89D9-245E681647D7}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {27B110CC-43C0-400A-89D9-245E681647D7}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {27B110CC-43C0-400A-89D9-245E681647D7}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {27B110CC-43C0-400A-89D9-245E681647D7}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {27B110CC-43C0-400A-89D9-245E681647D7}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {27B110CC-43C0-400A-89D9-245E681647D7}.Release|ARM64.ActiveCfg = Release|ARM64 - {27B110CC-43C0-400A-89D9-245E681647D7}.Release|ARM64.Build.0 = Release|ARM64 - {27B110CC-43C0-400A-89D9-245E681647D7}.Release|x64.ActiveCfg = Release|x64 - {27B110CC-43C0-400A-89D9-245E681647D7}.Release|x64.Build.0 = Release|x64 - {27B110CC-43C0-400A-89D9-245E681647D7}.Release|x86.ActiveCfg = Release|Win32 - {27B110CC-43C0-400A-89D9-245E681647D7}.Release|x86.Build.0 = Release|Win32 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug|ARM64.Build.0 = Debug|ARM64 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug|x64.ActiveCfg = Debug|x64 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug|x64.Build.0 = Debug|x64 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug|x86.ActiveCfg = Debug|Win32 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug|x86.Build.0 = Debug|Win32 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release|ARM64.ActiveCfg = Release|ARM64 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release|ARM64.Build.0 = Release|ARM64 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release|x64.ActiveCfg = Release|x64 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release|x64.Build.0 = Release|x64 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release|x86.ActiveCfg = Release|Win32 - {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release|x86.Build.0 = Release|Win32 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug|ARM64.Build.0 = Debug|ARM64 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug|x64.ActiveCfg = Debug|x64 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug|x64.Build.0 = Debug|x64 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug|x86.ActiveCfg = Debug|Win32 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug|x86.Build.0 = Debug|Win32 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release|ARM64.ActiveCfg = Release|ARM64 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release|ARM64.Build.0 = Release|ARM64 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release|x64.ActiveCfg = Release|x64 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release|x64.Build.0 = Release|x64 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release|x86.ActiveCfg = Release|Win32 - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release|x86.Build.0 = Release|Win32 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug|ARM64.Build.0 = Debug|ARM64 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug|x64.ActiveCfg = Debug|x64 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug|x64.Build.0 = Debug|x64 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug|x86.ActiveCfg = Debug|Win32 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug|x86.Build.0 = Debug|Win32 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release|ARM64.ActiveCfg = Release|ARM64 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release|ARM64.Build.0 = Release|ARM64 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release|x64.ActiveCfg = Release|x64 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release|x64.Build.0 = Release|x64 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release|x86.ActiveCfg = Release|Win32 - {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release|x86.Build.0 = Release|Win32 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug|ARM64.Build.0 = Debug|ARM64 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug|x64.ActiveCfg = Debug|x64 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug|x64.Build.0 = Debug|x64 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug|x86.ActiveCfg = Debug|Win32 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug|x86.Build.0 = Debug|Win32 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release|ARM64.ActiveCfg = Release|ARM64 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release|ARM64.Build.0 = Release|ARM64 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release|x64.ActiveCfg = Release|x64 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release|x64.Build.0 = Release|x64 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release|x86.ActiveCfg = Release|Win32 - {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release|x86.Build.0 = Release|Win32 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug|ARM64.Build.0 = Debug|ARM64 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug|x64.ActiveCfg = Debug|x64 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug|x64.Build.0 = Debug|x64 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug|x86.ActiveCfg = Debug|Win32 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug|x86.Build.0 = Debug|Win32 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release|ARM64.ActiveCfg = Release|ARM64 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release|ARM64.Build.0 = Release|ARM64 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release|x64.ActiveCfg = Release|x64 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release|x64.Build.0 = Release|x64 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release|x86.ActiveCfg = Release|Win32 - {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release|x86.Build.0 = Release|Win32 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug|ARM64.Build.0 = Debug|ARM64 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug|x64.ActiveCfg = Debug|x64 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug|x64.Build.0 = Debug|x64 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug|x86.ActiveCfg = Debug|Win32 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug|x86.Build.0 = Debug|Win32 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release|ARM64.ActiveCfg = Release|ARM64 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release|ARM64.Build.0 = Release|ARM64 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release|x64.ActiveCfg = Release|x64 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release|x64.Build.0 = Release|x64 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release|x86.ActiveCfg = Release|Win32 - {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release|x86.Build.0 = Release|Win32 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug|ARM64.Build.0 = Debug|ARM64 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug|x64.ActiveCfg = Debug|x64 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug|x64.Build.0 = Debug|x64 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug|x86.ActiveCfg = Debug|Win32 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug|x86.Build.0 = Debug|Win32 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|ARM64.ActiveCfg = Release|ARM64 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|ARM64.Build.0 = Release|ARM64 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|x64.ActiveCfg = Release|x64 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|x64.Build.0 = Release|x64 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|x86.ActiveCfg = Release|Win32 - {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|x86.Build.0 = Release|Win32 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug|ARM64.Build.0 = Debug|ARM64 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug|x64.ActiveCfg = Debug|x64 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug|x64.Build.0 = Debug|x64 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug|x86.ActiveCfg = Debug|Win32 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug|x86.Build.0 = Debug|Win32 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|ARM64.ActiveCfg = Release|ARM64 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|ARM64.Build.0 = Release|ARM64 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x64.ActiveCfg = Release|x64 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x64.Build.0 = Release|x64 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x86.ActiveCfg = Release|Win32 - {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x86.Build.0 = Release|Win32 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug|ARM64.Build.0 = Debug|ARM64 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug|x64.ActiveCfg = Debug|x64 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug|x64.Build.0 = Debug|x64 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug|x86.ActiveCfg = Debug|Win32 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug|x86.Build.0 = Debug|Win32 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|ARM64.ActiveCfg = Release|ARM64 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|ARM64.Build.0 = Release|ARM64 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x64.ActiveCfg = Release|x64 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x64.Build.0 = Release|x64 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x86.ActiveCfg = Release|Win32 - {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x86.Build.0 = Release|Win32 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|ARM64.Build.0 = Debug|ARM64 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x64.ActiveCfg = Debug|x64 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x64.Build.0 = Debug|x64 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x86.ActiveCfg = Debug|Win32 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x86.Build.0 = Debug|Win32 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|ARM64.ActiveCfg = Release|ARM64 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|ARM64.Build.0 = Release|ARM64 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x64.ActiveCfg = Release|x64 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x64.Build.0 = Release|x64 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x86.ActiveCfg = Release|Win32 - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x86.Build.0 = Release|Win32 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug|ARM64.Build.0 = Debug|ARM64 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug|x64.ActiveCfg = Debug|x64 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug|x64.Build.0 = Debug|x64 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug|x86.ActiveCfg = Debug|Win32 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug|x86.Build.0 = Debug|Win32 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release|ARM64.ActiveCfg = Release|ARM64 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release|ARM64.Build.0 = Release|ARM64 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release|x64.ActiveCfg = Release|x64 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release|x64.Build.0 = Release|x64 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release|x86.ActiveCfg = Release|Win32 - {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release|x86.Build.0 = Release|Win32 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug|ARM64.Build.0 = Debug|ARM64 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug|x64.ActiveCfg = Debug|x64 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug|x64.Build.0 = Debug|x64 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug|x86.ActiveCfg = Debug|Win32 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug|x86.Build.0 = Debug|Win32 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release|ARM64.ActiveCfg = Release|ARM64 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release|ARM64.Build.0 = Release|ARM64 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release|x64.ActiveCfg = Release|x64 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release|x64.Build.0 = Release|x64 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release|x86.ActiveCfg = Release|Win32 - {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release|x86.Build.0 = Release|Win32 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug|ARM64.Build.0 = Debug|ARM64 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug|x64.ActiveCfg = Debug|x64 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug|x64.Build.0 = Debug|x64 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug|x86.ActiveCfg = Debug|Win32 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug|x86.Build.0 = Debug|Win32 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release|ARM64.ActiveCfg = Release|ARM64 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release|ARM64.Build.0 = Release|ARM64 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release|x64.ActiveCfg = Release|x64 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release|x64.Build.0 = Release|x64 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release|x86.ActiveCfg = Release|Win32 - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release|x86.Build.0 = Release|Win32 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug|ARM64.Build.0 = Debug|ARM64 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug|x64.ActiveCfg = Debug|x64 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug|x64.Build.0 = Debug|x64 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug|x86.ActiveCfg = Debug|Win32 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug|x86.Build.0 = Debug|Win32 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release|ARM64.ActiveCfg = Release|ARM64 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release|ARM64.Build.0 = Release|ARM64 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release|x64.ActiveCfg = Release|x64 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release|x64.Build.0 = Release|x64 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release|x86.ActiveCfg = Release|Win32 - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release|x86.Build.0 = Release|Win32 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug|ARM64.Build.0 = Debug|ARM64 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug|x64.ActiveCfg = Debug|x64 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug|x64.Build.0 = Debug|x64 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug|x86.ActiveCfg = Debug|Win32 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug|x86.Build.0 = Debug|Win32 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release|ARM64.ActiveCfg = Release|ARM64 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release|ARM64.Build.0 = Release|ARM64 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release|x64.ActiveCfg = Release|x64 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release|x64.Build.0 = Release|x64 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release|x86.ActiveCfg = Release|Win32 - {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release|x86.Build.0 = Release|Win32 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug|ARM64.Build.0 = Debug|ARM64 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug|x64.ActiveCfg = Debug|x64 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug|x64.Build.0 = Debug|x64 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug|x86.ActiveCfg = Debug|Win32 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug|x86.Build.0 = Debug|Win32 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release|ARM64.ActiveCfg = Release|ARM64 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release|ARM64.Build.0 = Release|ARM64 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release|x64.ActiveCfg = Release|x64 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release|x64.Build.0 = Release|x64 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release|x86.ActiveCfg = Release|Win32 - {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release|x86.Build.0 = Release|Win32 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug|ARM64.Build.0 = Debug|ARM64 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug|x64.ActiveCfg = Debug|x64 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug|x64.Build.0 = Debug|x64 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug|x86.ActiveCfg = Debug|Win32 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug|x86.Build.0 = Debug|Win32 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release|ARM64.ActiveCfg = Release|ARM64 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release|ARM64.Build.0 = Release|ARM64 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release|x64.ActiveCfg = Release|x64 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release|x64.Build.0 = Release|x64 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release|x86.ActiveCfg = Release|Win32 - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release|x86.Build.0 = Release|Win32 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug|ARM64.Build.0 = Debug|ARM64 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug|x64.ActiveCfg = Debug|x64 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug|x64.Build.0 = Debug|x64 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug|x86.ActiveCfg = Debug|Win32 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug|x86.Build.0 = Debug|Win32 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Release|ARM64.ActiveCfg = Release|ARM64 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Release|ARM64.Build.0 = Release|ARM64 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Release|x64.ActiveCfg = Release|x64 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Release|x64.Build.0 = Release|x64 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Release|x86.ActiveCfg = Release|Win32 - {DF25E545-00FF-4E64-844C-7DF98991F901}.Release|x86.Build.0 = Release|Win32 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug|ARM64.Build.0 = Debug|ARM64 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug|x64.ActiveCfg = Debug|x64 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug|x64.Build.0 = Debug|x64 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug|x86.ActiveCfg = Debug|Win32 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug|x86.Build.0 = Debug|Win32 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release|ARM64.ActiveCfg = Release|ARM64 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release|ARM64.Build.0 = Release|ARM64 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release|x64.ActiveCfg = Release|x64 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release|x64.Build.0 = Release|x64 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release|x86.ActiveCfg = Release|Win32 - {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release|x86.Build.0 = Release|Win32 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug|ARM64.Build.0 = Debug|ARM64 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug|x64.ActiveCfg = Debug|x64 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug|x64.Build.0 = Debug|x64 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug|x86.ActiveCfg = Debug|Win32 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug|x86.Build.0 = Debug|Win32 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release|ARM64.ActiveCfg = Release|ARM64 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release|ARM64.Build.0 = Release|ARM64 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release|x64.ActiveCfg = Release|x64 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release|x64.Build.0 = Release|x64 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release|x86.ActiveCfg = Release|Win32 - {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release|x86.Build.0 = Release|Win32 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug|ARM64.Build.0 = Debug|ARM64 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug|x64.ActiveCfg = Debug|x64 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug|x64.Build.0 = Debug|x64 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug|x86.ActiveCfg = Debug|Win32 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug|x86.Build.0 = Debug|Win32 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release|ARM64.ActiveCfg = Release|ARM64 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release|ARM64.Build.0 = Release|ARM64 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release|x64.ActiveCfg = Release|x64 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release|x64.Build.0 = Release|x64 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release|x86.ActiveCfg = Release|Win32 - {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release|x86.Build.0 = Release|Win32 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug|ARM64.Build.0 = Debug|ARM64 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug|x64.ActiveCfg = Debug|x64 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug|x64.Build.0 = Debug|x64 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug|x86.ActiveCfg = Debug|Win32 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug|x86.Build.0 = Debug|Win32 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release|ARM64.ActiveCfg = Release|ARM64 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release|ARM64.Build.0 = Release|ARM64 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release|x64.ActiveCfg = Release|x64 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release|x64.Build.0 = Release|x64 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release|x86.ActiveCfg = Release|Win32 - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release|x86.Build.0 = Release|Win32 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug|ARM64.Build.0 = Debug|ARM64 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug|x64.ActiveCfg = Debug|x64 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug|x64.Build.0 = Debug|x64 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug|x86.ActiveCfg = Debug|Win32 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug|x86.Build.0 = Debug|Win32 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release|ARM64.ActiveCfg = Release|ARM64 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release|ARM64.Build.0 = Release|ARM64 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release|x64.ActiveCfg = Release|x64 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release|x64.Build.0 = Release|x64 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release|x86.ActiveCfg = Release|Win32 - {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release|x86.Build.0 = Release|Win32 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug|ARM64.Build.0 = Debug|ARM64 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug|x64.ActiveCfg = Debug|x64 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug|x64.Build.0 = Debug|x64 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug|x86.ActiveCfg = Debug|Win32 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug|x86.Build.0 = Debug|Win32 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release|ARM64.ActiveCfg = Release|ARM64 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release|ARM64.Build.0 = Release|ARM64 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release|x64.ActiveCfg = Release|x64 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release|x64.Build.0 = Release|x64 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release|x86.ActiveCfg = Release|Win32 - {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release|x86.Build.0 = Release|Win32 - {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 - {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 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug|ARM64.Build.0 = Debug|ARM64 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug|x64.ActiveCfg = Debug|x64 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug|x64.Build.0 = Debug|x64 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug|x86.ActiveCfg = Debug|Win32 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug|x86.Build.0 = Debug|Win32 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release|ARM64.ActiveCfg = Release|ARM64 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release|ARM64.Build.0 = Release|ARM64 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release|x64.ActiveCfg = Release|x64 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release|x64.Build.0 = Release|x64 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release|x86.ActiveCfg = Release|Win32 - {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release|x86.Build.0 = Release|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.Build.0 = Debug|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.ActiveCfg = Debug|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.Build.0 = Debug|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.ActiveCfg = Debug|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.Build.0 = Debug|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.ActiveCfg = Release|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.Build.0 = Release|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.ActiveCfg = Release|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.Build.0 = Release|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.ActiveCfg = Release|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.Build.0 = Release|Win32 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug|ARM64.Build.0 = Debug|ARM64 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug|x64.ActiveCfg = Debug|x64 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug|x64.Build.0 = Debug|x64 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug|x86.ActiveCfg = Debug|Win32 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug|x86.Build.0 = Debug|Win32 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release|ARM64.ActiveCfg = Release|ARM64 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release|ARM64.Build.0 = Release|ARM64 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release|x64.ActiveCfg = Release|x64 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release|x64.Build.0 = Release|x64 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release|x86.ActiveCfg = Release|Win32 - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release|x86.Build.0 = Release|Win32 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug|ARM64.Build.0 = Debug|ARM64 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug|x64.ActiveCfg = Debug|x64 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug|x64.Build.0 = Debug|x64 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug|x86.ActiveCfg = Debug|Win32 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug|x86.Build.0 = Debug|Win32 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release|ARM64.ActiveCfg = Release|ARM64 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release|ARM64.Build.0 = Release|ARM64 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release|x64.ActiveCfg = Release|x64 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release|x64.Build.0 = Release|x64 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release|x86.ActiveCfg = Release|Win32 - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release|x86.Build.0 = Release|Win32 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug|ARM64.Build.0 = Debug|ARM64 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug|x64.ActiveCfg = Debug|x64 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug|x64.Build.0 = Debug|x64 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug|x86.ActiveCfg = Debug|Win32 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug|x86.Build.0 = Debug|Win32 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release|ARM64.ActiveCfg = Release|ARM64 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release|ARM64.Build.0 = Release|ARM64 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release|x64.ActiveCfg = Release|x64 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release|x64.Build.0 = Release|x64 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release|x86.ActiveCfg = Release|Win32 - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release|x86.Build.0 = Release|Win32 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug|ARM64.Build.0 = Debug|ARM64 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug|x64.ActiveCfg = Debug|x64 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug|x64.Build.0 = Debug|x64 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug|x86.ActiveCfg = Debug|Win32 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug|x86.Build.0 = Debug|Win32 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Release|ARM64.ActiveCfg = Release|ARM64 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Release|ARM64.Build.0 = Release|ARM64 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Release|x64.ActiveCfg = Release|x64 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Release|x64.Build.0 = Release|x64 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Release|x86.ActiveCfg = Release|Win32 - {49C67F03-1A56-4F96-B278-39B66EC93678}.Release|x86.Build.0 = Release|Win32 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug|ARM64.Build.0 = Debug|ARM64 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug|x64.ActiveCfg = Debug|x64 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug|x64.Build.0 = Debug|x64 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug|x86.ActiveCfg = Debug|Win32 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug|x86.Build.0 = Debug|Win32 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release|ARM64.ActiveCfg = Release|ARM64 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release|ARM64.Build.0 = Release|ARM64 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release|x64.ActiveCfg = Release|x64 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release|x64.Build.0 = Release|x64 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release|x86.ActiveCfg = Release|Win32 - {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release|x86.Build.0 = Release|Win32 - {3B27F358-2679-4F38-B297-17B536F580BB}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {3B27F358-2679-4F38-B297-17B536F580BB}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {3B27F358-2679-4F38-B297-17B536F580BB}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {3B27F358-2679-4F38-B297-17B536F580BB}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {3B27F358-2679-4F38-B297-17B536F580BB}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {3B27F358-2679-4F38-B297-17B536F580BB}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {3B27F358-2679-4F38-B297-17B536F580BB}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {3B27F358-2679-4F38-B297-17B536F580BB}.Debug|ARM64.Build.0 = Debug|ARM64 - {3B27F358-2679-4F38-B297-17B536F580BB}.Debug|x64.ActiveCfg = Debug|x64 - {3B27F358-2679-4F38-B297-17B536F580BB}.Debug|x64.Build.0 = Debug|x64 - {3B27F358-2679-4F38-B297-17B536F580BB}.Debug|x86.ActiveCfg = Debug|Win32 - {3B27F358-2679-4F38-B297-17B536F580BB}.Debug|x86.Build.0 = Debug|Win32 - {3B27F358-2679-4F38-B297-17B536F580BB}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {3B27F358-2679-4F38-B297-17B536F580BB}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {3B27F358-2679-4F38-B297-17B536F580BB}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {3B27F358-2679-4F38-B297-17B536F580BB}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {3B27F358-2679-4F38-B297-17B536F580BB}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {3B27F358-2679-4F38-B297-17B536F580BB}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {3B27F358-2679-4F38-B297-17B536F580BB}.Release|ARM64.ActiveCfg = Release|ARM64 - {3B27F358-2679-4F38-B297-17B536F580BB}.Release|ARM64.Build.0 = Release|ARM64 - {3B27F358-2679-4F38-B297-17B536F580BB}.Release|x64.ActiveCfg = Release|x64 - {3B27F358-2679-4F38-B297-17B536F580BB}.Release|x64.Build.0 = Release|x64 - {3B27F358-2679-4F38-B297-17B536F580BB}.Release|x86.ActiveCfg = Release|Win32 - {3B27F358-2679-4F38-B297-17B536F580BB}.Release|x86.Build.0 = Release|Win32 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|ARM64.Build.0 = Debug|ARM64 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|x64.ActiveCfg = Debug|x64 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|x64.Build.0 = Debug|x64 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|x86.ActiveCfg = Debug|Win32 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|x86.Build.0 = Debug|Win32 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|ARM64.ActiveCfg = Release|ARM64 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|ARM64.Build.0 = Release|ARM64 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|x64.ActiveCfg = Release|x64 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|x64.Build.0 = Release|x64 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|x86.ActiveCfg = Release|Win32 - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|x86.Build.0 = Release|Win32 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug|ARM64.Build.0 = Debug|ARM64 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug|x64.ActiveCfg = Debug|x64 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug|x64.Build.0 = Debug|x64 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug|x86.ActiveCfg = Debug|Win32 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug|x86.Build.0 = Debug|Win32 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release|ARM64.ActiveCfg = Release|ARM64 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release|ARM64.Build.0 = Release|ARM64 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release|x64.ActiveCfg = Release|x64 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release|x64.Build.0 = Release|x64 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release|x86.ActiveCfg = Release|Win32 - {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release|x86.Build.0 = Release|Win32 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|ARM64.Build.0 = Debug|ARM64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|x64.ActiveCfg = Debug|x64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|x64.Build.0 = Debug|x64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|x86.ActiveCfg = Debug|Win32 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|x86.Build.0 = Debug|Win32 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|ARM64.ActiveCfg = Release|ARM64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|ARM64.Build.0 = Release|ARM64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|x64.ActiveCfg = Release|x64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|x64.Build.0 = Release|x64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|x86.ActiveCfg = Release|Win32 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|x86.Build.0 = Release|Win32 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug|ARM64.Build.0 = Debug|ARM64 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug|x64.ActiveCfg = Debug|x64 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug|x64.Build.0 = Debug|x64 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug|x86.ActiveCfg = Debug|Win32 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug|x86.Build.0 = Debug|Win32 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release|ARM64.ActiveCfg = Release|ARM64 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release|ARM64.Build.0 = Release|ARM64 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release|x64.ActiveCfg = Release|x64 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release|x64.Build.0 = Release|x64 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release|x86.ActiveCfg = Release|Win32 - {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release|x86.Build.0 = Release|Win32 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug|ARM64.Build.0 = Debug|ARM64 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug|x64.ActiveCfg = Debug|x64 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug|x64.Build.0 = Debug|x64 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug|x86.ActiveCfg = Debug|Win32 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug|x86.Build.0 = Debug|Win32 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release|ARM64.ActiveCfg = Release|ARM64 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release|ARM64.Build.0 = Release|ARM64 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release|x64.ActiveCfg = Release|x64 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release|x64.Build.0 = Release|x64 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release|x86.ActiveCfg = Release|Win32 - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release|x86.Build.0 = Release|Win32 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug|ARM64.Build.0 = Debug|ARM64 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug|x64.ActiveCfg = Debug|x64 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug|x64.Build.0 = Debug|x64 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug|x86.ActiveCfg = Debug|Win32 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug|x86.Build.0 = Debug|Win32 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release|ARM64.ActiveCfg = Release|ARM64 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release|ARM64.Build.0 = Release|ARM64 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release|x64.ActiveCfg = Release|x64 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release|x64.Build.0 = Release|x64 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release|x86.ActiveCfg = Release|Win32 - {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release|x86.Build.0 = Release|Win32 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug|ARM64.Build.0 = Debug|ARM64 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug|x64.ActiveCfg = Debug|x64 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug|x64.Build.0 = Debug|x64 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug|x86.ActiveCfg = Debug|Win32 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug|x86.Build.0 = Debug|Win32 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release|ARM64.ActiveCfg = Release|ARM64 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release|ARM64.Build.0 = Release|ARM64 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release|x64.ActiveCfg = Release|x64 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release|x64.Build.0 = Release|x64 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release|x86.ActiveCfg = Release|Win32 - {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release|x86.Build.0 = Release|Win32 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug|ARM64.Build.0 = Debug|ARM64 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug|x64.ActiveCfg = Debug|x64 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug|x64.Build.0 = Debug|x64 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug|x86.ActiveCfg = Debug|Win32 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug|x86.Build.0 = Debug|Win32 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release|ARM64.ActiveCfg = Release|ARM64 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release|ARM64.Build.0 = Release|ARM64 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release|x64.ActiveCfg = Release|x64 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release|x64.Build.0 = Release|x64 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release|x86.ActiveCfg = Release|Win32 - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release|x86.Build.0 = Release|Win32 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug|ARM64.Build.0 = Debug|ARM64 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug|x64.ActiveCfg = Debug|x64 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug|x64.Build.0 = Debug|x64 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug|x86.ActiveCfg = Debug|Win32 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug|x86.Build.0 = Debug|Win32 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release|ARM64.ActiveCfg = Release|ARM64 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release|ARM64.Build.0 = Release|ARM64 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release|x64.ActiveCfg = Release|x64 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release|x64.Build.0 = Release|x64 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release|x86.ActiveCfg = Release|Win32 - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release|x86.Build.0 = Release|Win32 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug|ARM64.Build.0 = Debug|ARM64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug|x64.ActiveCfg = Debug|x64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug|x64.Build.0 = Debug|x64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug|x86.ActiveCfg = Debug|Win32 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug|x86.Build.0 = Debug|Win32 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release|ARM64.ActiveCfg = Release|ARM64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release|ARM64.Build.0 = Release|ARM64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release|x64.ActiveCfg = Release|x64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release|x64.Build.0 = Release|x64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release|x86.ActiveCfg = Release|Win32 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release|x86.Build.0 = Release|Win32 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug|ARM64.Build.0 = Debug|ARM64 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug|x64.ActiveCfg = Debug|x64 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug|x64.Build.0 = Debug|x64 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug|x86.ActiveCfg = Debug|Win32 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug|x86.Build.0 = Debug|Win32 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release|ARM64.ActiveCfg = Release|ARM64 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release|ARM64.Build.0 = Release|ARM64 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release|x64.ActiveCfg = Release|x64 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release|x64.Build.0 = Release|x64 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release|x86.ActiveCfg = Release|Win32 - {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release|x86.Build.0 = Release|Win32 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug|ARM64.Build.0 = Debug|ARM64 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug|x64.ActiveCfg = Debug|x64 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug|x64.Build.0 = Debug|x64 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug|x86.ActiveCfg = Debug|Win32 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug|x86.Build.0 = Debug|Win32 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release|ARM64.ActiveCfg = Release|ARM64 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release|ARM64.Build.0 = Release|ARM64 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release|x64.ActiveCfg = Release|x64 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release|x64.Build.0 = Release|x64 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release|x86.ActiveCfg = Release|Win32 - {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release|x86.Build.0 = Release|Win32 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug|ARM64.Build.0 = Debug|ARM64 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug|x64.ActiveCfg = Debug|x64 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug|x64.Build.0 = Debug|x64 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug|x86.ActiveCfg = Debug|Win32 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug|x86.Build.0 = Debug|Win32 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release|ARM64.ActiveCfg = Release|ARM64 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release|ARM64.Build.0 = Release|ARM64 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release|x64.ActiveCfg = Release|x64 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release|x64.Build.0 = Release|x64 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release|x86.ActiveCfg = Release|Win32 - {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release|x86.Build.0 = Release|Win32 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug|ARM64.Build.0 = Debug|ARM64 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug|x64.ActiveCfg = Debug|x64 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug|x64.Build.0 = Debug|x64 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug|x86.ActiveCfg = Debug|Win32 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug|x86.Build.0 = Debug|Win32 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release|ARM64.ActiveCfg = Release|ARM64 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release|ARM64.Build.0 = Release|ARM64 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release|x64.ActiveCfg = Release|x64 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release|x64.Build.0 = Release|x64 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release|x86.ActiveCfg = Release|Win32 - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release|x86.Build.0 = Release|Win32 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug|ARM64.Build.0 = Debug|ARM64 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug|x64.ActiveCfg = Debug|x64 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug|x64.Build.0 = Debug|x64 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug|x86.ActiveCfg = Debug|Win32 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug|x86.Build.0 = Debug|Win32 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Release|ARM64.ActiveCfg = Release|ARM64 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Release|ARM64.Build.0 = Release|ARM64 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Release|x64.ActiveCfg = Release|x64 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Release|x64.Build.0 = Release|x64 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Release|x86.ActiveCfg = Release|Win32 - {7467E9AE-844F-444D-8A3F-17397544BA21}.Release|x86.Build.0 = Release|Win32 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug|ARM64.Build.0 = Debug|ARM64 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug|x64.ActiveCfg = Debug|x64 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug|x64.Build.0 = Debug|x64 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug|x86.ActiveCfg = Debug|Win32 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug|x86.Build.0 = Debug|Win32 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Release|ARM64.ActiveCfg = Release|ARM64 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Release|ARM64.Build.0 = Release|ARM64 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Release|x64.ActiveCfg = Release|x64 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Release|x64.Build.0 = Release|x64 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Release|x86.ActiveCfg = Release|Win32 - {497FDF54-9762-4048-A833-61CC3980A0FB}.Release|x86.Build.0 = Release|Win32 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug|ARM64.Build.0 = Debug|ARM64 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug|x64.ActiveCfg = Debug|x64 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug|x64.Build.0 = Debug|x64 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug|x86.ActiveCfg = Debug|Win32 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug|x86.Build.0 = Debug|Win32 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release|ARM64.ActiveCfg = Release|ARM64 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release|ARM64.Build.0 = Release|ARM64 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release|x64.ActiveCfg = Release|x64 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release|x64.Build.0 = Release|x64 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release|x86.ActiveCfg = Release|Win32 - {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release|x86.Build.0 = Release|Win32 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug|ARM64.Build.0 = Debug|ARM64 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug|x64.ActiveCfg = Debug|x64 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug|x64.Build.0 = Debug|x64 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug|x86.ActiveCfg = Debug|Win32 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug|x86.Build.0 = Debug|Win32 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release|ARM64.ActiveCfg = Release|ARM64 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release|ARM64.Build.0 = Release|ARM64 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release|x64.ActiveCfg = Release|x64 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release|x64.Build.0 = Release|x64 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release|x86.ActiveCfg = Release|Win32 - {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release|x86.Build.0 = Release|Win32 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug|ARM64.Build.0 = Debug|ARM64 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug|x64.ActiveCfg = Debug|x64 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug|x64.Build.0 = Debug|x64 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug|x86.ActiveCfg = Debug|Win32 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug|x86.Build.0 = Debug|Win32 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release|ARM64.ActiveCfg = Release|ARM64 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release|ARM64.Build.0 = Release|ARM64 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release|x64.ActiveCfg = Release|x64 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release|x64.Build.0 = Release|x64 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release|x86.ActiveCfg = Release|Win32 - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release|x86.Build.0 = Release|Win32 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug|ARM64.Build.0 = Debug|ARM64 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug|x64.ActiveCfg = Debug|x64 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug|x64.Build.0 = Debug|x64 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug|x86.ActiveCfg = Debug|Win32 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug|x86.Build.0 = Debug|Win32 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release|ARM64.ActiveCfg = Release|ARM64 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release|ARM64.Build.0 = Release|ARM64 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release|x64.ActiveCfg = Release|x64 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release|x64.Build.0 = Release|x64 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release|x86.ActiveCfg = Release|Win32 - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release|x86.Build.0 = Release|Win32 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug|ARM64.Build.0 = Debug|ARM64 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug|x64.ActiveCfg = Debug|x64 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug|x64.Build.0 = Debug|x64 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug|x86.ActiveCfg = Debug|Win32 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug|x86.Build.0 = Debug|Win32 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release|ARM64.ActiveCfg = Release|ARM64 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release|ARM64.Build.0 = Release|ARM64 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release|x64.ActiveCfg = Release|x64 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release|x64.Build.0 = Release|x64 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release|x86.ActiveCfg = Release|Win32 - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release|x86.Build.0 = Release|Win32 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug|ARM64.Build.0 = Debug|ARM64 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug|x64.ActiveCfg = Debug|x64 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug|x64.Build.0 = Debug|x64 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug|x86.ActiveCfg = Debug|Win32 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug|x86.Build.0 = Debug|Win32 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release|ARM64.ActiveCfg = Release|ARM64 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release|ARM64.Build.0 = Release|ARM64 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release|x64.ActiveCfg = Release|x64 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release|x64.Build.0 = Release|x64 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release|x86.ActiveCfg = Release|Win32 - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release|x86.Build.0 = Release|Win32 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug|ARM64.Build.0 = Debug|ARM64 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug|x64.ActiveCfg = Debug|x64 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug|x64.Build.0 = Debug|x64 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug|x86.ActiveCfg = Debug|Win32 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug|x86.Build.0 = Debug|Win32 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release|ARM64.ActiveCfg = Release|ARM64 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release|ARM64.Build.0 = Release|ARM64 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release|x64.ActiveCfg = Release|x64 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release|x64.Build.0 = Release|x64 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release|x86.ActiveCfg = Release|Win32 - {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release|x86.Build.0 = Release|Win32 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug|ARM64.Build.0 = Debug|ARM64 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug|x64.ActiveCfg = Debug|x64 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug|x64.Build.0 = Debug|x64 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug|x86.ActiveCfg = Debug|Win32 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug|x86.Build.0 = Debug|Win32 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release|ARM64.ActiveCfg = Release|ARM64 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release|ARM64.Build.0 = Release|ARM64 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release|x64.ActiveCfg = Release|x64 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release|x64.Build.0 = Release|x64 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release|x86.ActiveCfg = Release|Win32 - {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release|x86.Build.0 = Release|Win32 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug|ARM64.Build.0 = Debug|ARM64 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug|x64.ActiveCfg = Debug|x64 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug|x64.Build.0 = Debug|x64 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug|x86.ActiveCfg = Debug|Win32 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug|x86.Build.0 = Debug|Win32 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release|ARM64.ActiveCfg = Release|ARM64 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release|ARM64.Build.0 = Release|ARM64 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release|x64.ActiveCfg = Release|x64 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release|x64.Build.0 = Release|x64 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release|x86.ActiveCfg = Release|Win32 - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release|x86.Build.0 = Release|Win32 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug|ARM64.Build.0 = Debug|ARM64 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug|x64.ActiveCfg = Debug|x64 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug|x64.Build.0 = Debug|x64 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug|x86.ActiveCfg = Debug|Win32 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug|x86.Build.0 = Debug|Win32 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release|ARM64.ActiveCfg = Release|ARM64 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release|ARM64.Build.0 = Release|ARM64 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release|x64.ActiveCfg = Release|x64 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release|x64.Build.0 = Release|x64 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release|x86.ActiveCfg = Release|Win32 - {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release|x86.Build.0 = Release|Win32 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug|ARM64.Build.0 = Debug|ARM64 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug|x64.ActiveCfg = Debug|x64 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug|x64.Build.0 = Debug|x64 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug|x86.ActiveCfg = Debug|Win32 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug|x86.Build.0 = Debug|Win32 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release|ARM64.ActiveCfg = Release|ARM64 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release|ARM64.Build.0 = Release|ARM64 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release|x64.ActiveCfg = Release|x64 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release|x64.Build.0 = Release|x64 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release|x86.ActiveCfg = Release|Win32 - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release|x86.Build.0 = Release|Win32 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug|ARM64.Build.0 = Debug|ARM64 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug|x64.ActiveCfg = Debug|x64 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug|x64.Build.0 = Debug|x64 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug|x86.ActiveCfg = Debug|Win32 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug|x86.Build.0 = Debug|Win32 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release|ARM64.ActiveCfg = Release|ARM64 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release|ARM64.Build.0 = Release|ARM64 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release|x64.ActiveCfg = Release|x64 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release|x64.Build.0 = Release|x64 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release|x86.ActiveCfg = Release|Win32 - {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release|x86.Build.0 = Release|Win32 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug|ARM64.Build.0 = Debug|ARM64 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug|x64.ActiveCfg = Debug|x64 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug|x64.Build.0 = Debug|x64 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug|x86.ActiveCfg = Debug|Win32 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug|x86.Build.0 = Debug|Win32 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release|ARM64.ActiveCfg = Release|ARM64 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release|ARM64.Build.0 = Release|ARM64 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release|x64.ActiveCfg = Release|x64 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release|x64.Build.0 = Release|x64 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release|x86.ActiveCfg = Release|Win32 - {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release|x86.Build.0 = Release|Win32 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug|ARM64.Build.0 = Debug|ARM64 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug|x64.ActiveCfg = Debug|x64 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug|x64.Build.0 = Debug|x64 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug|x86.ActiveCfg = Debug|Win32 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug|x86.Build.0 = Debug|Win32 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release|ARM64.ActiveCfg = Release|ARM64 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release|ARM64.Build.0 = Release|ARM64 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release|x64.ActiveCfg = Release|x64 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release|x64.Build.0 = Release|x64 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release|x86.ActiveCfg = Release|Win32 - {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release|x86.Build.0 = Release|Win32 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug|ARM64.Build.0 = Debug|ARM64 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug|x64.ActiveCfg = Debug|x64 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug|x64.Build.0 = Debug|x64 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug|x86.ActiveCfg = Debug|Win32 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug|x86.Build.0 = Debug|Win32 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release|ARM64.ActiveCfg = Release|ARM64 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release|ARM64.Build.0 = Release|ARM64 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release|x64.ActiveCfg = Release|x64 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release|x64.Build.0 = Release|x64 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release|x86.ActiveCfg = Release|Win32 - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release|x86.Build.0 = Release|Win32 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug|ARM64.Build.0 = Debug|ARM64 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug|x64.ActiveCfg = Debug|x64 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug|x64.Build.0 = Debug|x64 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug|x86.ActiveCfg = Debug|Win32 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug|x86.Build.0 = Debug|Win32 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Release|ARM64.ActiveCfg = Release|ARM64 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Release|ARM64.Build.0 = Release|ARM64 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Release|x64.ActiveCfg = Release|x64 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Release|x64.Build.0 = Release|x64 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Release|x86.ActiveCfg = Release|Win32 - {6C897101-BE52-4387-8AA2-062123A76BA1}.Release|x86.Build.0 = Release|Win32 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug|ARM64.Build.0 = Debug|ARM64 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug|x64.ActiveCfg = Debug|x64 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug|x64.Build.0 = Debug|x64 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug|x86.ActiveCfg = Debug|Win32 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug|x86.Build.0 = Debug|Win32 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release|ARM64.ActiveCfg = Release|ARM64 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release|ARM64.Build.0 = Release|ARM64 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release|x64.ActiveCfg = Release|x64 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release|x64.Build.0 = Release|x64 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release|x86.ActiveCfg = Release|Win32 - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release|x86.Build.0 = Release|Win32 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug|ARM64.Build.0 = Debug|ARM64 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug|x64.ActiveCfg = Debug|x64 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug|x64.Build.0 = Debug|x64 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug|x86.ActiveCfg = Debug|Win32 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug|x86.Build.0 = Debug|Win32 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release|ARM64.ActiveCfg = Release|ARM64 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release|ARM64.Build.0 = Release|ARM64 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release|x64.ActiveCfg = Release|x64 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release|x64.Build.0 = Release|x64 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release|x86.ActiveCfg = Release|Win32 - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release|x86.Build.0 = Release|Win32 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug|ARM64.Build.0 = Debug|ARM64 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug|x64.ActiveCfg = Debug|x64 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug|x64.Build.0 = Debug|x64 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug|x86.ActiveCfg = Debug|Win32 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug|x86.Build.0 = Debug|Win32 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release|ARM64.ActiveCfg = Release|ARM64 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release|ARM64.Build.0 = Release|ARM64 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release|x64.ActiveCfg = Release|x64 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release|x64.Build.0 = Release|x64 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release|x86.ActiveCfg = Release|Win32 - {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release|x86.Build.0 = Release|Win32 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug|ARM64.Build.0 = Debug|ARM64 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug|x64.ActiveCfg = Debug|x64 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug|x64.Build.0 = Debug|x64 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug|x86.ActiveCfg = Debug|Win32 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug|x86.Build.0 = Debug|Win32 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release|ARM64.ActiveCfg = Release|ARM64 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release|ARM64.Build.0 = Release|ARM64 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release|x64.ActiveCfg = Release|x64 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release|x64.Build.0 = Release|x64 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release|x86.ActiveCfg = Release|Win32 - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release|x86.Build.0 = Release|Win32 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug|ARM64.Build.0 = Debug|ARM64 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug|x64.ActiveCfg = Debug|x64 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug|x64.Build.0 = Debug|x64 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug|x86.ActiveCfg = Debug|Win32 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug|x86.Build.0 = Debug|Win32 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release|ARM64.ActiveCfg = Release|ARM64 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release|ARM64.Build.0 = Release|ARM64 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release|x64.ActiveCfg = Release|x64 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release|x64.Build.0 = Release|x64 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release|x86.ActiveCfg = Release|Win32 - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release|x86.Build.0 = Release|Win32 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug|ARM64.Build.0 = Debug|ARM64 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug|x64.ActiveCfg = Debug|x64 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug|x64.Build.0 = Debug|x64 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug|x86.ActiveCfg = Debug|Win32 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug|x86.Build.0 = Debug|Win32 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release|ARM64.ActiveCfg = Release|ARM64 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release|ARM64.Build.0 = Release|ARM64 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release|x64.ActiveCfg = Release|x64 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release|x64.Build.0 = Release|x64 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release|x86.ActiveCfg = Release|Win32 - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release|x86.Build.0 = Release|Win32 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug|ARM64.Build.0 = Debug|ARM64 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug|x64.ActiveCfg = Debug|x64 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug|x64.Build.0 = Debug|x64 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug|x86.ActiveCfg = Debug|Win32 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug|x86.Build.0 = Debug|Win32 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release|ARM64.ActiveCfg = Release|ARM64 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release|ARM64.Build.0 = Release|ARM64 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release|x64.ActiveCfg = Release|x64 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release|x64.Build.0 = Release|x64 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release|x86.ActiveCfg = Release|Win32 - {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release|x86.Build.0 = Release|Win32 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug|ARM64.Build.0 = Debug|ARM64 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug|x64.ActiveCfg = Debug|x64 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug|x64.Build.0 = Debug|x64 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug|x86.ActiveCfg = Debug|Win32 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug|x86.Build.0 = Debug|Win32 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Release|ARM64.ActiveCfg = Release|ARM64 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Release|ARM64.Build.0 = Release|ARM64 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Release|x64.ActiveCfg = Release|x64 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Release|x64.Build.0 = Release|x64 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Release|x86.ActiveCfg = Release|Win32 - {071E64F3-1396-4A97-97CA-98CAC059B168}.Release|x86.Build.0 = Release|Win32 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug|ARM64.Build.0 = Debug|ARM64 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug|x64.ActiveCfg = Debug|x64 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug|x64.Build.0 = Debug|x64 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug|x86.ActiveCfg = Debug|Win32 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug|x86.Build.0 = Debug|Win32 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release|ARM64.ActiveCfg = Release|ARM64 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release|ARM64.Build.0 = Release|ARM64 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release|x64.ActiveCfg = Release|x64 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release|x64.Build.0 = Release|x64 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release|x86.ActiveCfg = Release|Win32 - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release|x86.Build.0 = Release|Win32 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug|ARM64.Build.0 = Debug|ARM64 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug|x64.ActiveCfg = Debug|x64 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug|x64.Build.0 = Debug|x64 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug|x86.ActiveCfg = Debug|Win32 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug|x86.Build.0 = Debug|Win32 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Release|ARM64.ActiveCfg = Release|ARM64 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Release|ARM64.Build.0 = Release|ARM64 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Release|x64.ActiveCfg = Release|x64 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Release|x64.Build.0 = Release|x64 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Release|x86.ActiveCfg = Release|Win32 - {1F4722E7-F78E-413F-A106-D3490211EA57}.Release|x86.Build.0 = Release|Win32 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug|ARM64.Build.0 = Debug|ARM64 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug|x64.ActiveCfg = Debug|x64 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug|x64.Build.0 = Debug|x64 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug|x86.ActiveCfg = Debug|Win32 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug|x86.Build.0 = Debug|Win32 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release|ARM64.ActiveCfg = Release|ARM64 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release|ARM64.Build.0 = Release|ARM64 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release|x64.ActiveCfg = Release|x64 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release|x64.Build.0 = Release|x64 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release|x86.ActiveCfg = Release|Win32 - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release|x86.Build.0 = Release|Win32 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug|ARM64.Build.0 = Debug|ARM64 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug|x64.ActiveCfg = Debug|x64 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug|x64.Build.0 = Debug|x64 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug|x86.ActiveCfg = Debug|Win32 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug|x86.Build.0 = Debug|Win32 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|ARM64.ActiveCfg = Release|ARM64 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|ARM64.Build.0 = Release|ARM64 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|x64.ActiveCfg = Release|x64 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|x64.Build.0 = Release|x64 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|x86.ActiveCfg = Release|Win32 - {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|x86.Build.0 = Release|Win32 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug|ARM64.Build.0 = Debug|ARM64 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug|x64.ActiveCfg = Debug|x64 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug|x64.Build.0 = Debug|x64 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug|x86.ActiveCfg = Debug|Win32 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug|x86.Build.0 = Debug|Win32 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release|ARM64.ActiveCfg = Release|ARM64 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release|ARM64.Build.0 = Release|ARM64 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release|x64.ActiveCfg = Release|x64 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release|x64.Build.0 = Release|x64 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release|x86.ActiveCfg = Release|Win32 - {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release|x86.Build.0 = Release|Win32 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug|ARM64.Build.0 = Debug|ARM64 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug|x64.ActiveCfg = Debug|x64 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug|x64.Build.0 = Debug|x64 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug|x86.ActiveCfg = Debug|Win32 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug|x86.Build.0 = Debug|Win32 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|ARM64.ActiveCfg = Release|ARM64 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|ARM64.Build.0 = Release|ARM64 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|x64.ActiveCfg = Release|x64 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|x64.Build.0 = Release|x64 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|x86.ActiveCfg = Release|Win32 - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - 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} - {0C2D2F82-AE67-400C-B19C-8C9B957B132A} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {E6784F91-4E4E-4956-A079-73FAB1AC7BE6} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} - {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} - {93A1F656-0D29-4C5E-B140-11F23FF5D6AB} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} - {F81C5819-85B6-4D2E-B6DC-104A7634461B} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} - {66CC5B13-881A-412F-8C51-746622A91C5A} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {557138B0-7BE2-4392-B2E2-B45734031A62} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {9EED87BB-527F-4D05-9384-6D16CFD627A8} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {6D1CA2F1-7FCA-4249-9220-075C2DF4F965} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {946A1700-C7AA-46F0-AEF2-67C98B5722AC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {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} - {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {A643BB06-735D-47F3-BFE7-B6D3C36F7097} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {B332DCA8-3599-4A99-917A-82261BDC27AC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {59089B0C-AAB4-4532-B294-44DEAE7178B7} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {C298876B-6C12-4EA4-903B-33450BCD9884} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {83F586FA-C801-4979-ACCA-006BD628CC88} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {FF2970AE-E2E9-405F-B321-D523A1BD44A0} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {79417CE2-FEEB-42F0-BC53-62D5267B19B1} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {AFDDE100-2D36-4749-817D-12E54C56312F} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {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} - {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} - {F3412853-2B6A-4334-8CF2-B796CDAE0850} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {D03F2C82-9553-4AFA-8F49-9234009122B6} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {A53CCF42-A972-478F-9336-0F618B3EC06A} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {870723DD-945A-4136-B65B-4AF3BF85369C} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {EA6488AD-445B-4835-87FB-EBC9E2EDAF97} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {3AD868E6-8355-4F29-B5ED-7DE94AD786E7} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {2B78CF0A-5403-45E2-99BD-493F1679BCDB} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {0AB968E0-E993-45CE-8875-7453C96DF583} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {25923141-9859-4AFE-8168-0DF78322FC63} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {7E855020-7FA4-482D-B510-2E709354FE8B} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {9782E0C8-2BD3-4F67-B420-21CF19CA2435} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {9F4135E3-9814-452C-9B35-0EFBCD792B49} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {C45343E6-DAB6-4F3A-A00A-8BED71A098BE} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {B19DD336-538E-4091-A559-EAA717FEC899} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {0BF60202-43F7-48E9-8717-D31E56FA5BE0} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {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} - {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} - {56FB0A45-145F-4EAE-B2C8-E5833E682D8F} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} - {2BB0C1D4-9298-45AC-B244-67A99769A292} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} - {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} - {81064BCE-EEC1-43B0-9912-F05F2B54B11A} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} - {31B41997-3890-45E3-93FE-C57B363E9C0D} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} - {D550AB93-DF31-4B76-873F-F075018352F4} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} - {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} - {F90FCDC5-EE14-4B89-96DB-4392E28F34AF} = {278D8859-20B1-428F-8448-064F46E1F021} - {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79} = {278D8859-20B1-428F-8448-064F46E1F021} - {56E68E37-B3FC-4799-91AF-0CA10B6D55A5} = {278D8859-20B1-428F-8448-064F46E1F021} - {03E7018C-44A2-4C46-9CE7-F2A135A2692B} = {278D8859-20B1-428F-8448-064F46E1F021} - {F3F6FE4D-9D9E-451A-B0BA-81456104B672} = {278D8859-20B1-428F-8448-064F46E1F021} - {C27794B5-1293-4EA7-BC0E-0F18E6325539} = {278D8859-20B1-428F-8448-064F46E1F021} - {02F41059-12A2-4A96-8D77-07EFE4B108FD} = {278D8859-20B1-428F-8448-064F46E1F021} - {B774E0B9-9514-4E88-975F-4EB6C3B8D519} = {278D8859-20B1-428F-8448-064F46E1F021} - {D91367C2-2189-4859-A7FE-D2CAB84FA15C} = {278D8859-20B1-428F-8448-064F46E1F021} - {33459B4E-1839-4856-BF6B-22480D11FE31} = {278D8859-20B1-428F-8448-064F46E1F021} - {48871156-181A-475A-BD8D-200086A09675} = {278D8859-20B1-428F-8448-064F46E1F021} - {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1} = {278D8859-20B1-428F-8448-064F46E1F021} - {1C49E35A-2838-49D9-9D5F-4B8134960EF6} = {278D8859-20B1-428F-8448-064F46E1F021} - {F91142E2-A999-47F0-9E74-38C1E2930EBE} = {278D8859-20B1-428F-8448-064F46E1F021} - {1EDD4BCF-345C-4065-8CBD-7285224293C3} = {278D8859-20B1-428F-8448-064F46E1F021} - {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {CF3755C4-937D-4ABF-B7B3-95140808717F} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {D34939FE-8873-4C53-8D6C-74DED78EA3C4} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {D408A730-363A-4ABF-BCEF-5D63DCC66042} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {52FB7463-C128-42AF-A02F-78F48473EA9A} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {7381D91E-5C72-48F0-AAB4-95C9B10D7484} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {D36EC43E-B31F-4CF4-8285-93A7A9D90189} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {274C0319-7E1E-4188-936B-8DF3331230B3} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {600C3D4F-0670-4DB4-B30F-520A729053B5} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {11F33A39-74B7-4018-B5F9-CC285A673A8F} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {A6F5E35E-B4A7-41B3-853A-75558E6E0715} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {291B4975-8EFF-4C7C-8AF3-44A77B8491B8} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {FDE6080B-E203-4066-910D-AD0302566008} = {E9D708A5-9C1F-4B84-A795-C5F191801762} - {E1B6D565-9D7C-46B7-9202-ECF54974DE50} = {E9D708A5-9C1F-4B84-A795-C5F191801762} - {C8765523-58F8-4C8E-9914-693396F6F0FF} = {E9D708A5-9C1F-4B84-A795-C5F191801762} - {2F1B955B-275E-4D8E-8864-06FEC44D7912} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {F2DB2E59-76BF-4D81-859A-AFC289C046C0} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} - {3FE7E9B6-49AC-4246-A789-28DB4644567B} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {191A5289-BA65-4638-A215-C521F0187313} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {8B1AF423-00F1-4924-AC54-F77D402D2AC9} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {658A1B85-554E-4A5D-973A-FFE592CDD5F2} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {07CA51AD-72AE-46A2-AAED-DC3E3F807976} = {E9D708A5-9C1F-4B84-A795-C5F191801762} - {27B110CC-43C0-400A-89D9-245E681647D7} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} - {1DE84812-E143-4C4B-A61D-9267AAD55401} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {4A87569C-4BD3-4113-B4B9-573D65B3D3F8} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} - {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} - {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {A4B0D971-3CD6-41C9-8AB2-055D25A33373} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} - {15CDD310-6980-42A6-8082-3A6B7730D13F} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {71DB4284-5B1C-4E86-9AF5-B91542D44A6F} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {88DE5AD6-0074-4A5A-BE22-C840153E35D5} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {A546E75A-5242-46E6-9A9E-6C91554EAB84} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {DF25E545-00FF-4E64-844C-7DF98991F901} = {278D8859-20B1-428F-8448-064F46E1F021} - {703BE7BA-5B99-4F70-806D-3A259F6A991E} = {278D8859-20B1-428F-8448-064F46E1F021} - {FAFEE2F9-24B0-4AF1-B512-433E9590033F} = {278D8859-20B1-428F-8448-064F46E1F021} - {8245DAD9-D402-4D5C-8F45-32229CD3B263} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {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} - {0981CA28-E4A5-4DF1-987F-A41D09131EFC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {6777EC3C-077C-42FC-B4AD-B799CE55CCE4} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} - {A61DAD9C-271C-4E95-81AA-DB4CD58564D4} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {49C67F03-1A56-4F96-B278-39B66EC93678} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {D496308F-3C3C-40B3-A3ED-EA327D244B3E} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} - {3B27F358-2679-4F38-B297-17B536F580BB} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {718FCBD0-591D-448C-B7D5-9F1CA8544E7B} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {19CA0070-B4B2-4394-90B7-D0C259AA35BA} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {9DB1F875-6E65-4195-B23F-ED8095C0B99C} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} - {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {8E132D5A-2C00-48D0-8747-97E41356F26F} = {278D8859-20B1-428F-8448-064F46E1F021} - {A4662163-83E7-4309-8CAA-B0BF13655FE6} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20} = {278D8859-20B1-428F-8448-064F46E1F021} - {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC} = {E9D708A5-9C1F-4B84-A795-C5F191801762} - {0C442799-B09C-4CD1-9538-711B6E85E9BF} = {278D8859-20B1-428F-8448-064F46E1F021} - {DFB40A10-F8B7-412A-BCC3-5EE49294D816} = {278D8859-20B1-428F-8448-064F46E1F021} - {BB58A5FB-1A35-4471-86D0-A5189EC541B3} = {278D8859-20B1-428F-8448-064F46E1F021} - {61997220-5383-4AE5-ABD4-5F45AE1B0F2A} = {278D8859-20B1-428F-8448-064F46E1F021} - {7467E9AE-844F-444D-8A3F-17397544BA21} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {497FDF54-9762-4048-A833-61CC3980A0FB} = {278D8859-20B1-428F-8448-064F46E1F021} - {29B00F47-BE91-4A1F-B87D-B1302F038316} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} - {124935CC-73BB-489E-92E8-4F922A85DB5D} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F} = {278D8859-20B1-428F-8448-064F46E1F021} - {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2} = {278D8859-20B1-428F-8448-064F46E1F021} - {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91} = {278D8859-20B1-428F-8448-064F46E1F021} - {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {9DE2FC01-A839-4F89-8319-9071D4C54821} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {2F578155-D51F-4C03-AB7F-5C5122CA46CC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {1C829D1A-892C-451C-AF0B-AC65C85F5CC6} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {84DE22BB-C25F-425C-A7FE-0120CF107B83} = {278D8859-20B1-428F-8448-064F46E1F021} - {98152EDD-7E28-4FA3-89D8-B636ED5D5F65} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {B7FDD40F-DDA4-468E-9C40-EEB175964A26} = {278D8859-20B1-428F-8448-064F46E1F021} - {028F0967-B253-45DA-B1C4-FACCE45D0D8D} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {666346D7-C84B-498D-AE17-53B20C62DB1A} = {278D8859-20B1-428F-8448-064F46E1F021} - {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {6C897101-BE52-4387-8AA2-062123A76BA1} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} - {30011884-25EE-42C9-BB15-888CAFB1AA6E} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {32FE2658-1D70-442E-8672-0AC5C6F0BD7B} = {278D8859-20B1-428F-8448-064F46E1F021} - {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F} = {278D8859-20B1-428F-8448-064F46E1F021} - {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {0653AFAF-5578-4C02-AF29-0C873E7634AE} = {278D8859-20B1-428F-8448-064F46E1F021} - {071E64F3-1396-4A97-97CA-98CAC059B168} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC} = {278D8859-20B1-428F-8448-064F46E1F021} - {1F4722E7-F78E-413F-A106-D3490211EA57} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} - {0A0FC982-6E31-401F-BA77-3C5E8AB02C68} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {DC163251-16C3-4B72-B965-ACDBA0F02BD1} = {278D8859-20B1-428F-8448-064F46E1F021} - {D35D2FDA-B53F-4F70-81CA-24D95812B89C} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31912.275 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "raylib", "raylib\raylib.vcxproj", "{E89D61AC-55DE-4482-AFD4-DF7242EBC859}" +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}") = "others", "others", "{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_animation", "examples\textures_sprite_animation.vcxproj", "{C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_srcrec_dstrec", "examples\textures_srcrec_dstrec.vcxproj", "{103B292B-049B-4B15-85A1-9F902840DB2C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_image_drawing", "examples\textures_image_drawing.vcxproj", "{0C2D2F82-AE67-400C-B19C-8C9B957B132A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_module_playing", "examples\audio_module_playing.vcxproj", "{E6784F91-4E4E-4956-A079-73FAB1AC7BE6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_music_stream", "examples\audio_music_stream.vcxproj", "{BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_raw_stream", "examples\audio_raw_stream.vcxproj", "{93A1F656-0D29-4C5E-B140-11F23FF5D6AB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_sound_loading", "examples\audio_sound_loading.vcxproj", "{F81C5819-85B6-4D2E-B6DC-104A7634461B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_2d_camera", "examples\core_2d_camera.vcxproj", "{66CC5B13-881A-412F-8C51-746622A91C5A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_2d_camera_platformer", "examples\core_2d_camera_platformer.vcxproj", "{CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_camera_first_person", "examples\core_3d_camera_first_person.vcxproj", "{557138B0-7BE2-4392-B2E2-B45734031A62}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_camera_free", "examples\core_3d_camera_free.vcxproj", "{9EED87BB-527F-4D05-9384-6D16CFD627A8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_camera_mode", "examples\core_3d_camera_mode.vcxproj", "{6D1CA2F1-7FCA-4249-9220-075C2DF4F965}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_camera_split_screen", "examples\core_3d_camera_split_screen.vcxproj", "{946A1700-C7AA-46F0-AEF2-67C98B5722AC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_picking", "examples\core_3d_picking.vcxproj", "{FD193822-3D5C-4161-A147-884C2ABDE483}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_custom_logging", "examples\core_custom_logging.vcxproj", "{20AD0AC9-9159-4744-99CC-6AC5779D6B87}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_drop_files", "examples\core_drop_files.vcxproj", "{0199E349-0701-40BC-8A7F-06A54FFA3E7C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_highdpi_demo", "examples\core_highdpi_demo.vcxproj", "{BCB71111-8505-4B35-8CEF-EC6115DC9D4D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_gamepad", "examples\core_input_gamepad.vcxproj", "{8F19E3DA-8929-4000-87B5-3CA6929636CC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_gestures", "examples\core_input_gestures.vcxproj", "{51A00565-5787-4911-9CC0-28403AA4909D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_keys", "examples\core_input_keys.vcxproj", "{92B64AE7-D773-4F05-89F1-CE59BBF4F053}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_mouse", "examples\core_input_mouse.vcxproj", "{A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_multitouch", "examples\core_input_multitouch.vcxproj", "{A643BB06-735D-47F3-BFE7-B6D3C36F7097}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_random_sequence", "examples\core_random_sequence.vcxproj", "{6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_random_values", "examples\core_random_values.vcxproj", "{B332DCA8-3599-4A99-917A-82261BDC27AC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_scissor_test", "examples\core_scissor_test.vcxproj", "{59089B0C-AAB4-4532-B294-44DEAE7178B7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_storage_values", "examples\core_storage_values.vcxproj", "{C298876B-6C12-4EA4-903B-33450BCD9884}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_vr_simulator", "examples\core_vr_simulator.vcxproj", "{83F586FA-C801-4979-ACCA-006BD628CC88}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_window_flags", "examples\core_window_flags.vcxproj", "{86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_window_letterbox", "examples\core_window_letterbox.vcxproj", "{FF2970AE-E2E9-405F-B321-D523A1BD44A0}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_world_screen", "examples\core_world_screen.vcxproj", "{79417CE2-FEEB-42F0-BC53-62D5267B19B1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_playing", "examples\models_animation_playing.vcxproj", "{AFDDE100-2D36-4749-817D-12E54C56312F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_billboard_rendering", "examples\models_billboard_rendering.vcxproj", "{B7812167-50FB-4934-996F-DF6FE4CBBFDF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_box_collisions", "examples\models_box_collisions.vcxproj", "{39DB56C7-05F8-492C-A8D4-F19E40FECB59}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_cubicmap_rendering", "examples\models_cubicmap_rendering.vcxproj", "{82F3D34B-8DB2-4C6A-98B1-132245DD9D99}" +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}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_heightmap_rendering", "examples\models_heightmap_rendering.vcxproj", "{0859A973-E4FE-4688-8D16-0253163FDE24}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_loading", "examples\models_loading.vcxproj", "{F3412853-2B6A-4334-8CF2-B796CDAE0850}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_mesh_generation", "examples\models_mesh_generation.vcxproj", "{BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_mesh_picking", "examples\models_mesh_picking.vcxproj", "{D03F2C82-9553-4AFA-8F49-9234009122B6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_orthographic_projection", "examples\models_orthographic_projection.vcxproj", "{FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_rlgl_solar_system", "examples\models_rlgl_solar_system.vcxproj", "{A53CCF42-A972-478F-9336-0F618B3EC06A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_skybox_rendering", "examples\models_skybox_rendering.vcxproj", "{0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_waving_cubes", "examples\models_waving_cubes.vcxproj", "{870723DD-945A-4136-B65B-4AF3BF85369C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_yaw_pitch_roll", "examples\models_yaw_pitch_roll.vcxproj", "{EA6488AD-445B-4835-87FB-EBC9E2EDAF97}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_to_image", "examples\textures_to_image.vcxproj", "{E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_sprite_explosion", "examples\textures_sprite_explosion.vcxproj", "{472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_sprite_button", "examples\textures_sprite_button.vcxproj", "{589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_raw_data", "examples\textures_raw_data.vcxproj", "{3AD868E6-8355-4F29-B5ED-7DE94AD786E7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_particles_blending", "examples\textures_particles_blending.vcxproj", "{2B78CF0A-5403-45E2-99BD-493F1679BCDB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_npatch_drawing", "examples\textures_npatch_drawing.vcxproj", "{0AB968E0-E993-45CE-8875-7453C96DF583}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_mouse_painting", "examples\textures_mouse_painting.vcxproj", "{25923141-9859-4AFE-8168-0DF78322FC63}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_logo_raylib", "examples\textures_logo_raylib.vcxproj", "{7E855020-7FA4-482D-B510-2E709354FE8B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_image_text", "examples\textures_image_text.vcxproj", "{9782E0C8-2BD3-4F67-B420-21CF19CA2435}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_image_processing", "examples\textures_image_processing.vcxproj", "{9F4135E3-9814-452C-9B35-0EFBCD792B49}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_image_loading", "examples\textures_image_loading.vcxproj", "{C45343E6-DAB6-4F3A-A00A-8BED71A098BE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_image_generation", "examples\textures_image_generation.vcxproj", "{B19DD336-538E-4091-A559-EAA717FEC899}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_tiled_drawing", "examples\textures_tiled_drawing.vcxproj", "{0BF60202-43F7-48E9-8717-D31E56FA5BE0}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_bunnymark", "examples\textures_bunnymark.vcxproj", "{4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_blend_modes", "examples\textures_blend_modes.vcxproj", "{6D75CD88-1A03-4955-B8C7-ACFC3742154F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_background_scrolling", "examples\textures_background_scrolling.vcxproj", "{8DD0EB7E-668E-452D-91D7-906C64A9C8AC}" +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_emojis", "examples\text_unicode_emojis.vcxproj", "{1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_rectangle_bounds", "examples\text_rectangle_bounds.vcxproj", "{25BCB876-B60A-499B-9046-E9801CFD7780}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_sprite_fonts", "examples\text_sprite_fonts.vcxproj", "{56FB0A45-145F-4EAE-B2C8-E5833E682D8F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_input_box", "examples\text_input_box.vcxproj", "{2BB0C1D4-9298-45AC-B244-67A99769A292}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_format_text", "examples\text_format_text.vcxproj", "{99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_font_spritefont", "examples\text_font_spritefont.vcxproj", "{81064BCE-EEC1-43B0-9912-F05F2B54B11A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_font_sdf", "examples\text_font_sdf.vcxproj", "{31B41997-3890-45E3-93FE-C57B363E9C0D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_font_loading", "examples\text_font_loading.vcxproj", "{D550AB93-DF31-4B76-873F-F075018352F4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_font_filters", "examples\text_font_filters.vcxproj", "{8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_rectangle_scaling", "examples\shapes_rectangle_scaling.vcxproj", "{F90FCDC5-EE14-4B89-96DB-4392E28F34AF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_logo_raylib_anim", "examples\shapes_logo_raylib_anim.vcxproj", "{93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_logo_raylib", "examples\shapes_logo_raylib.vcxproj", "{56E68E37-B3FC-4799-91AF-0CA10B6D55A5}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_lines_bezier", "examples\shapes_lines_bezier.vcxproj", "{03E7018C-44A2-4C46-9CE7-F2A135A2692B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_following_eyes", "examples\shapes_following_eyes.vcxproj", "{F3F6FE4D-9D9E-451A-B0BA-81456104B672}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_basic_shapes", "examples\shapes_basic_shapes.vcxproj", "{C27794B5-1293-4EA7-BC0E-0F18E6325539}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_bouncing_ball", "examples\shapes_bouncing_ball.vcxproj", "{02F41059-12A2-4A96-8D77-07EFE4B108FD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_collision_area", "examples\shapes_collision_area.vcxproj", "{B774E0B9-9514-4E88-975F-4EB6C3B8D519}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_colors_palette", "examples\shapes_colors_palette.vcxproj", "{D91367C2-2189-4859-A7FE-D2CAB84FA15C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_circle_sector_drawing", "examples\shapes_circle_sector_drawing.vcxproj", "{33459B4E-1839-4856-BF6B-22480D11FE31}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_rounded_rectangle_drawing", "examples\shapes_rounded_rectangle_drawing.vcxproj", "{48871156-181A-475A-BD8D-200086A09675}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_ring_drawing", "examples\shapes_ring_drawing.vcxproj", "{C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_easings_ball", "examples\shapes_easings_ball.vcxproj", "{1C49E35A-2838-49D9-9D5F-4B8134960EF6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_easings_box", "examples\shapes_easings_box.vcxproj", "{F91142E2-A999-47F0-9E74-38C1E2930EBE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_easings_rectangles", "examples\shapes_easings_rectangles.vcxproj", "{1EDD4BCF-345C-4065-8CBD-7285224293C3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_basic_lighting", "examples\shaders_basic_lighting.vcxproj", "{A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_custom_uniform", "examples\shaders_custom_uniform.vcxproj", "{B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_eratosthenes_sieve", "examples\shaders_eratosthenes_sieve.vcxproj", "{D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_fog_rendering", "examples\shaders_fog_rendering.vcxproj", "{4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_hot_reloading", "examples\shaders_hot_reloading.vcxproj", "{CF3755C4-937D-4ABF-B7B3-95140808717F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_julia_set", "examples\shaders_julia_set.vcxproj", "{D34939FE-8873-4C53-8D6C-74DED78EA3C4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_model_shader", "examples\shaders_model_shader.vcxproj", "{D408A730-363A-4ABF-BCEF-5D63DCC66042}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_multi_sample2d", "examples\shaders_multi_sample2d.vcxproj", "{F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_palette_switch", "examples\shaders_palette_switch.vcxproj", "{52FB7463-C128-42AF-A02F-78F48473EA9A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_postprocessing", "examples\shaders_postprocessing.vcxproj", "{7381D91E-5C72-48F0-AAB4-95C9B10D7484}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_raymarching_rendering", "examples\shaders_raymarching_rendering.vcxproj", "{D36EC43E-B31F-4CF4-8285-93A7A9D90189}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_mesh_instancing", "examples\shaders_mesh_instancing.vcxproj", "{274C0319-7E1E-4188-936B-8DF3331230B3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_shapes_textures", "examples\shaders_shapes_textures.vcxproj", "{41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_simple_mask", "examples\shaders_simple_mask.vcxproj", "{600C3D4F-0670-4DB4-B30F-520A729053B5}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_spotlight_rendering", "examples\shaders_spotlight_rendering.vcxproj", "{11F33A39-74B7-4018-B5F9-CC285A673A8F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_texture_rendering", "examples\shaders_texture_rendering.vcxproj", "{A6F5E35E-B4A7-41B3-853A-75558E6E0715}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_texture_waves", "examples\shaders_texture_waves.vcxproj", "{291B4975-8EFF-4C7C-8AF3-44A77B8491B8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "embedded_files_loading", "examples\embedded_files_loading.vcxproj", "{FDE6080B-E203-4066-910D-AD0302566008}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "easings_testbed", "examples\easings_testbed.vcxproj", "{E1B6D565-9D7C-46B7-9202-ECF54974DE50}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rlgl_standalone", "examples\rlgl_standalone.vcxproj", "{C8765523-58F8-4C8E-9914-693396F6F0FF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_loading_vox", "examples\models_loading_vox.vcxproj", "{2F1B955B-275E-4D8E-8864-06FEC44D7912}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_loading_gltf", "examples\models_loading_gltf.vcxproj", "{F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_codepoints_loading", "examples\text_codepoints_loading.vcxproj", "{F2DB2E59-76BF-4D81-859A-AFC289C046C0}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_window_should_close", "examples\core_window_should_close.vcxproj", "{3FE7E9B6-49AC-4246-A789-28DB4644567B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_fog_of_war", "examples\textures_fog_of_war.vcxproj", "{EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_gif_player", "examples\textures_gif_player.vcxproj", "{191A5289-BA65-4638-A215-C521F0187313}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_2d_camera_mouse_zoom", "examples\core_2d_camera_mouse_zoom.vcxproj", "{3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_basic_screen_manager", "examples\core_basic_screen_manager.vcxproj", "{8B1AF423-00F1-4924-AC54-F77D402D2AC9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_custom_frame_control", "examples\core_custom_frame_control.vcxproj", "{658A1B85-554E-4A5D-973A-FFE592CDD5F2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rlgl_compute_shader", "examples\rlgl_compute_shader.vcxproj", "{07CA51AD-72AE-46A2-AAED-DC3E3F807976}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_3d_drawing", "examples\text_3d_drawing.vcxproj", "{27B110CC-43C0-400A-89D9-245E681647D7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_polygon_drawing", "examples\textures_polygon_drawing.vcxproj", "{1DE84812-E143-4C4B-A61D-9267AAD55401}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_stream_effects", "examples\audio_stream_effects.vcxproj", "{4A87569C-4BD3-4113-B4B9-573D65B3D3F8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_textured_curve", "examples\textures_textured_curve.vcxproj", "{769FF0C1-4424-4FA3-BC44-D7A7DA312A06}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_loading_m3d", "examples\models_loading_m3d.vcxproj", "{6D9E00D8-2893-45E4-9363-3F7F61D416BD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_depth_writing", "examples\shaders_depth_writing.vcxproj", "{70B35F59-AFC2-4D8F-8833-5314D2047A81}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_depth_rendering", "examples\shaders_depth_rendering.vcxproj", "{DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_hybrid_rendering", "examples\shaders_hybrid_rendering.vcxproj", "{3755E9F4-CB48-4EC3-B561-3B85964EBDEF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_sound_multi", "examples\audio_sound_multi.vcxproj", "{F81C5819-85B4-4D2E-B6DC-104A7634461B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_2d_camera_split_screen", "examples\core_2d_camera_split_screen.vcxproj", "{CC62F7DB-D089-4677-8575-CAB7A7815C43}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_automation_events", "examples\core_automation_events.vcxproj", "{7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_mixed_processor", "examples\audio_mixed_processor.vcxproj", "{A4B0D971-3CD6-41C9-8AB2-055D25A33373}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_mouse_wheel", "examples\core_input_mouse_wheel.vcxproj", "{15CDD310-6980-42A6-8082-3A6B7730D13F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_smooth_pixelperfect", "examples\core_smooth_pixelperfect.vcxproj", "{71DB4284-5B1C-4E86-9AF5-B91542D44A6F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_textured_cube", "examples\models_textured_cube.vcxproj", "{4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_deferred_rendering", "examples\shaders_deferred_rendering.vcxproj", "{88DE5AD6-0074-4A5A-BE22-C840153E35D5}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_texture_outline", "examples\shaders_texture_outline.vcxproj", "{A546E75A-5242-46E6-9A9E-6C91554EAB84}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_texture_tiling", "examples\shaders_texture_tiling.vcxproj", "{EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_splines_drawing", "examples\shapes_splines_drawing.vcxproj", "{DF25E545-00FF-4E64-844C-7DF98991F901}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_top_down_lights", "examples\shapes_top_down_lights.vcxproj", "{703BE7BA-5B99-4F70-806D-3A259F6A991E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_rectangle_advanced", "examples\shapes_rectangle_advanced.vcxproj", "{FAFEE2F9-24B0-4AF1-B512-433E9590033F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_gpu_skinning", "examples\models_animation_gpu_skinning.vcxproj", "{8245DAD9-D402-4D5C-8F45-32229CD3B263}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_shadowmap_rendering", "examples\shaders_shadowmap_rendering.vcxproj", "{41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_bone_socket", "examples\models_bone_socket.vcxproj", "{3A7FE53D-35F7-49DC-9C9A-A5204A53523F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_vertex_displacement", "examples\shaders_vertex_displacement.vcxproj", "{CCA63A76-D9FC-4130-9F67-4D97F9770D53}" +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_rendering", "examples\shaders_lightmap_rendering.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 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_virtual_controls", "examples\core_input_virtual_controls.vcxproj", "{0981CA28-E4A5-4DF1-987F-A41D09131EFC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_camera_fps", "examples\core_3d_camera_fps.vcxproj", "{6B1A933E-71B8-4C1F-9E79-02D98830E671}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_normalmap_rendering", "examples\shaders_normalmap_rendering.vcxproj", "{6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_unicode_ranges", "examples\text_unicode_ranges.vcxproj", "{6777EC3C-077C-42FC-B4AD-B799CE55CCE4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_gestures_testbed", "examples\core_input_gestures_testbed.vcxproj", "{A61DAD9C-271C-4E95-81AA-DB4CD58564D4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_render_texture", "examples\core_render_texture.vcxproj", "{49C67F03-1A56-4F96-B278-39B66EC93678}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_inline_styling", "examples\text_inline_styling.vcxproj", "{D496308F-3C3C-40B3-A3ED-EA327D244B3E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_undo_redo", "examples\core_undo_redo.vcxproj", "{3B27F358-2679-4F38-B297-17B536F580BB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_actions", "examples\core_input_actions.vcxproj", "{718FCBD0-591D-448C-B7D5-9F1CA8544E7B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_delta_time", "examples\core_delta_time.vcxproj", "{19CA0070-B4B2-4394-90B7-D0C259AA35BA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_bullet_hell", "examples\shapes_bullet_hell.vcxproj", "{2CCCD9E4-9058-4291-BD89-39C979F0CA1E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_vector_angle", "examples\shapes_vector_angle.vcxproj", "{9DB1F875-6E65-4195-B23F-ED8095C0B99C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_basic_voxel", "examples\models_basic_voxel.vcxproj", "{52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_dashed_line", "examples\shapes_dashed_line.vcxproj", "{8E132D5A-2C00-48D0-8747-97E41356F26F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_rotating_cube", "examples\models_rotating_cube.vcxproj", "{A4662163-83E7-4309-8CAA-B0BF13655FE6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_ascii_rendering", "examples\shaders_ascii_rendering.vcxproj", "{5F4B766F-DD52-4B53-B6C3-BC7611E17F20}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_monitor_detector", "examples\core_monitor_detector.vcxproj", "{FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "web_basic_window", "examples\web_basic_window.vcxproj", "{A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_kaleidoscope", "examples\shapes_kaleidoscope.vcxproj", "{0C442799-B09C-4CD1-9538-711B6E85E9BF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_recursive_tree", "examples\shapes_recursive_tree.vcxproj", "{DFB40A10-F8B7-412A-BCC3-5EE49294D816}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_triangle_strip", "examples\shapes_triangle_strip.vcxproj", "{BB58A5FB-1A35-4471-86D0-A5189EC541B3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_pie_chart", "examples\shapes_pie_chart.vcxproj", "{61997220-5383-4AE5-ABD4-5F45AE1B0F2A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_directory_files", "examples\core_directory_files.vcxproj", "{7467E9AE-844F-444D-8A3F-17397544BA21}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_simple_particles", "examples\shapes_simple_particles.vcxproj", "{497FDF54-9762-4048-A833-61CC3980A0FB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_words_alignment", "examples\text_words_alignment.vcxproj", "{29B00F47-BE91-4A1F-B87D-B1302F038316}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_clipboard_text", "examples\core_clipboard_text.vcxproj", "{124935CC-73BB-489E-92E8-4F922A85DB5D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_clock_of_clocks", "examples\shapes_clock_of_clocks.vcxproj", "{AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_mouse_trail", "examples\shapes_mouse_trail.vcxproj", "{0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_starfield_effect", "examples\shapes_starfield_effect.vcxproj", "{EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_highdpi_testbed", "examples\core_highdpi_testbed.vcxproj", "{1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_screen_recording", "examples\core_screen_recording.vcxproj", "{9DE2FC01-A839-4F89-8319-9071D4C54821}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_text_file_loading", "examples\core_text_file_loading.vcxproj", "{2F578155-D51F-4C03-AB7F-5C5122CA46CC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_mandelbrot_set", "examples\shaders_mandelbrot_set.vcxproj", "{1C829D1A-892C-451C-AF0B-AC65C85F5CC6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_math_angle_rotation", "examples\shapes_math_angle_rotation.vcxproj", "{84DE22BB-C25F-425C-A7FE-0120CF107B83}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_color_correction", "examples\shaders_color_correction.vcxproj", "{98152EDD-7E28-4FA3-89D8-B636ED5D5F65}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_math_sine_cosine", "examples\shapes_math_sine_cosine.vcxproj", "{B7FDD40F-DDA4-468E-9C40-EEB175964A26}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_decals", "examples\models_decals.vcxproj", "{028F0967-B253-45DA-B1C4-FACCE45D0D8D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_lines_drawing", "examples\shapes_lines_drawing.vcxproj", "{666346D7-C84B-498D-AE17-53B20C62DB1A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_viewport_scaling", "examples\core_viewport_scaling.vcxproj", "{AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_compute_hash", "examples\core_compute_hash.vcxproj", "{6C897101-BE52-4387-8AA2-062123A76BA1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_screen_buffer", "examples\textures_screen_buffer.vcxproj", "{4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_spectrum_visualizer", "examples\audio_spectrum_visualizer.vcxproj", "{2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_directional_billboard", "examples\models_directional_billboard.vcxproj", "{30011884-25EE-42C9-BB15-888CAFB1AA6E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_rlgl_color_wheel", "examples\shapes_rlgl_color_wheel.vcxproj", "{32FE2658-1D70-442E-8672-0AC5C6F0BD7B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_rlgl_triangle", "examples\shapes_rlgl_triangle.vcxproj", "{842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_sprite_stacking", "examples\textures_sprite_stacking.vcxproj", "{FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_ball_physics", "examples\shapes_ball_physics.vcxproj", "{0653AFAF-5578-4C02-AF29-0C873E7634AE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_game_of_life", "examples\shaders_game_of_life.vcxproj", "{071E64F3-1396-4A97-97CA-98CAC059B168}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_penrose_tile", "examples\shapes_penrose_tile.vcxproj", "{7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_strings_management", "examples\text_strings_management.vcxproj", "{1F4722E7-F78E-413F-A106-D3490211EA57}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_cellular_automata", "examples\textures_cellular_automata.vcxproj", "{0A0FC982-6E31-401F-BA77-3C5E8AB02C68}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_hilbert_curve", "examples\shapes_hilbert_curve.vcxproj", "{DC163251-16C3-4B72-B965-ACDBA0F02BD1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_keyboard_testbed", "examples\core_keyboard_testbed.vcxproj", "{D35D2FDA-B53F-4F70-81CA-24D95812B89C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_framebuffer_rendering", "examples\textures_framebuffer_rendering.vcxproj", "{F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug.DLL|ARM64 = Debug.DLL|ARM64 + Debug.DLL|x64 = Debug.DLL|x64 + Debug.DLL|x86 = Debug.DLL|x86 + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release.DLL|ARM64 = Release.DLL|ARM64 + Release.DLL|x64 = Release.DLL|x64 + Release.DLL|x86 = Release.DLL|x86 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug|ARM64.Build.0 = Debug|ARM64 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug|x64.ActiveCfg = Debug|x64 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug|x64.Build.0 = Debug|x64 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug|x86.ActiveCfg = Debug|Win32 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Debug|x86.Build.0 = Debug|Win32 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release|ARM64.ActiveCfg = Release|ARM64 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release|ARM64.Build.0 = Release|ARM64 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release|x64.ActiveCfg = Release|x64 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release|x64.Build.0 = Release|x64 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release|x86.ActiveCfg = Release|Win32 + {E89D61AC-55DE-4482-AFD4-DF7242EBC859}.Release|x86.Build.0 = Release|Win32 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug|ARM64.Build.0 = Debug|ARM64 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug|x64.ActiveCfg = Debug|x64 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug|x64.Build.0 = Debug|x64 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug|x86.ActiveCfg = Debug|Win32 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Debug|x86.Build.0 = Debug|Win32 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release|ARM64.ActiveCfg = Release|ARM64 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release|ARM64.Build.0 = Release|ARM64 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release|x64.ActiveCfg = Release|x64 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release|x64.Build.0 = Release|x64 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release|x86.ActiveCfg = Release|Win32 + {0981CA98-E4A5-4DF1-987F-A41D09131EFC}.Release|x86.Build.0 = Release|Win32 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug|ARM64.Build.0 = Debug|ARM64 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug|x64.ActiveCfg = Debug|x64 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug|x64.Build.0 = Debug|x64 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug|x86.ActiveCfg = Debug|Win32 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Debug|x86.Build.0 = Debug|Win32 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release|ARM64.ActiveCfg = Release|ARM64 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release|ARM64.Build.0 = Release|ARM64 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release|x64.ActiveCfg = Release|x64 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release|x64.Build.0 = Release|x64 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release|x86.ActiveCfg = Release|Win32 + {C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}.Release|x86.Build.0 = Release|Win32 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug|ARM64.Build.0 = Debug|ARM64 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug|x64.ActiveCfg = Debug|x64 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug|x64.Build.0 = Debug|x64 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug|x86.ActiveCfg = Debug|Win32 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Debug|x86.Build.0 = Debug|Win32 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Release|ARM64.ActiveCfg = Release|ARM64 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Release|ARM64.Build.0 = Release|ARM64 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Release|x64.ActiveCfg = Release|x64 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Release|x64.Build.0 = Release|x64 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Release|x86.ActiveCfg = Release|Win32 + {103B292B-049B-4B15-85A1-9F902840DB2C}.Release|x86.Build.0 = Release|Win32 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug|ARM64.Build.0 = Debug|ARM64 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug|x64.ActiveCfg = Debug|x64 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug|x64.Build.0 = Debug|x64 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug|x86.ActiveCfg = Debug|Win32 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Debug|x86.Build.0 = Debug|Win32 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release|ARM64.ActiveCfg = Release|ARM64 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release|ARM64.Build.0 = Release|ARM64 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release|x64.ActiveCfg = Release|x64 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release|x64.Build.0 = Release|x64 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release|x86.ActiveCfg = Release|Win32 + {0C2D2F82-AE67-400C-B19C-8C9B957B132A}.Release|x86.Build.0 = Release|Win32 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug|ARM64.Build.0 = Debug|ARM64 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug|x64.ActiveCfg = Debug|x64 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug|x64.Build.0 = Debug|x64 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug|x86.ActiveCfg = Debug|Win32 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Debug|x86.Build.0 = Debug|Win32 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release|ARM64.ActiveCfg = Release|ARM64 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release|ARM64.Build.0 = Release|ARM64 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release|x64.ActiveCfg = Release|x64 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release|x64.Build.0 = Release|x64 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release|x86.ActiveCfg = Release|Win32 + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release|x86.Build.0 = Release|Win32 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug|ARM64.Build.0 = Debug|ARM64 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug|x64.ActiveCfg = Debug|x64 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug|x64.Build.0 = Debug|x64 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug|x86.ActiveCfg = Debug|Win32 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug|x86.Build.0 = Debug|Win32 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release|ARM64.ActiveCfg = Release|ARM64 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release|ARM64.Build.0 = Release|ARM64 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release|x64.ActiveCfg = Release|x64 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release|x64.Build.0 = Release|x64 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release|x86.ActiveCfg = Release|Win32 + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Release|x86.Build.0 = Release|Win32 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug|ARM64.Build.0 = Debug|ARM64 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug|x64.ActiveCfg = Debug|x64 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug|x64.Build.0 = Debug|x64 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug|x86.ActiveCfg = Debug|Win32 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Debug|x86.Build.0 = Debug|Win32 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release|ARM64.ActiveCfg = Release|ARM64 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release|ARM64.Build.0 = Release|ARM64 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release|x64.ActiveCfg = Release|x64 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release|x64.Build.0 = Release|x64 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release|x86.ActiveCfg = Release|Win32 + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB}.Release|x86.Build.0 = Release|Win32 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug|ARM64.Build.0 = Debug|ARM64 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug|x64.ActiveCfg = Debug|x64 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug|x64.Build.0 = Debug|x64 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug|x86.ActiveCfg = Debug|Win32 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Debug|x86.Build.0 = Debug|Win32 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release|ARM64.ActiveCfg = Release|ARM64 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release|ARM64.Build.0 = Release|ARM64 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release|x64.ActiveCfg = Release|x64 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release|x64.Build.0 = Release|x64 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release|x86.ActiveCfg = Release|Win32 + {F81C5819-85B6-4D2E-B6DC-104A7634461B}.Release|x86.Build.0 = Release|Win32 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug|ARM64.Build.0 = Debug|ARM64 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug|x64.ActiveCfg = Debug|x64 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug|x64.Build.0 = Debug|x64 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug|x86.ActiveCfg = Debug|Win32 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Debug|x86.Build.0 = Debug|Win32 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Release|ARM64.ActiveCfg = Release|ARM64 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Release|ARM64.Build.0 = Release|ARM64 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Release|x64.ActiveCfg = Release|x64 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Release|x64.Build.0 = Release|x64 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Release|x86.ActiveCfg = Release|Win32 + {66CC5B13-881A-412F-8C51-746622A91C5A}.Release|x86.Build.0 = Release|Win32 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug|ARM64.Build.0 = Debug|ARM64 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug|x64.ActiveCfg = Debug|x64 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug|x64.Build.0 = Debug|x64 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug|x86.ActiveCfg = Debug|Win32 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Debug|x86.Build.0 = Debug|Win32 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release|ARM64.ActiveCfg = Release|ARM64 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release|ARM64.Build.0 = Release|ARM64 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release|x64.ActiveCfg = Release|x64 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release|x64.Build.0 = Release|x64 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release|x86.ActiveCfg = Release|Win32 + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B}.Release|x86.Build.0 = Release|Win32 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug|ARM64.Build.0 = Debug|ARM64 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug|x64.ActiveCfg = Debug|x64 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug|x64.Build.0 = Debug|x64 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug|x86.ActiveCfg = Debug|Win32 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Debug|x86.Build.0 = Debug|Win32 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Release|ARM64.ActiveCfg = Release|ARM64 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Release|ARM64.Build.0 = Release|ARM64 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Release|x64.ActiveCfg = Release|x64 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Release|x64.Build.0 = Release|x64 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Release|x86.ActiveCfg = Release|Win32 + {557138B0-7BE2-4392-B2E2-B45734031A62}.Release|x86.Build.0 = Release|Win32 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug|ARM64.Build.0 = Debug|ARM64 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug|x64.ActiveCfg = Debug|x64 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug|x64.Build.0 = Debug|x64 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug|x86.ActiveCfg = Debug|Win32 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Debug|x86.Build.0 = Debug|Win32 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release|ARM64.ActiveCfg = Release|ARM64 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release|ARM64.Build.0 = Release|ARM64 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release|x64.ActiveCfg = Release|x64 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release|x64.Build.0 = Release|x64 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release|x86.ActiveCfg = Release|Win32 + {9EED87BB-527F-4D05-9384-6D16CFD627A8}.Release|x86.Build.0 = Release|Win32 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug|ARM64.Build.0 = Debug|ARM64 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug|x64.ActiveCfg = Debug|x64 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug|x64.Build.0 = Debug|x64 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug|x86.ActiveCfg = Debug|Win32 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Debug|x86.Build.0 = Debug|Win32 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release|ARM64.ActiveCfg = Release|ARM64 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release|ARM64.Build.0 = Release|ARM64 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release|x64.ActiveCfg = Release|x64 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release|x64.Build.0 = Release|x64 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release|x86.ActiveCfg = Release|Win32 + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release|x86.Build.0 = Release|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|ARM64.Build.0 = Debug|ARM64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x64.ActiveCfg = Debug|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x64.Build.0 = Debug|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x86.ActiveCfg = Debug|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x86.Build.0 = Debug|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|ARM64.ActiveCfg = Release|ARM64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|ARM64.Build.0 = Release|ARM64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x64.ActiveCfg = Release|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x64.Build.0 = Release|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x86.ActiveCfg = Release|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x86.Build.0 = Release|Win32 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug|ARM64.Build.0 = Debug|ARM64 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug|x64.ActiveCfg = Debug|x64 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug|x64.Build.0 = Debug|x64 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug|x86.ActiveCfg = Debug|Win32 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug|x86.Build.0 = Debug|Win32 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Release|ARM64.ActiveCfg = Release|ARM64 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Release|ARM64.Build.0 = Release|ARM64 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Release|x64.ActiveCfg = Release|x64 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Release|x64.Build.0 = Release|x64 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Release|x86.ActiveCfg = Release|Win32 + {FD193822-3D5C-4161-A147-884C2ABDE483}.Release|x86.Build.0 = Release|Win32 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug|ARM64.Build.0 = Debug|ARM64 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug|x64.ActiveCfg = Debug|x64 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug|x64.Build.0 = Debug|x64 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug|x86.ActiveCfg = Debug|Win32 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Debug|x86.Build.0 = Debug|Win32 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release|ARM64.ActiveCfg = Release|ARM64 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release|ARM64.Build.0 = Release|ARM64 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release|x64.ActiveCfg = Release|x64 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release|x64.Build.0 = Release|x64 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release|x86.ActiveCfg = Release|Win32 + {20AD0AC9-9159-4744-99CC-6AC5779D6B87}.Release|x86.Build.0 = Release|Win32 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug|ARM64.Build.0 = Debug|ARM64 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug|x64.ActiveCfg = Debug|x64 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug|x64.Build.0 = Debug|x64 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug|x86.ActiveCfg = Debug|Win32 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Debug|x86.Build.0 = Debug|Win32 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release|ARM64.ActiveCfg = Release|ARM64 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release|ARM64.Build.0 = Release|ARM64 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release|x64.ActiveCfg = Release|x64 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release|x64.Build.0 = Release|x64 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release|x86.ActiveCfg = Release|Win32 + {0199E349-0701-40BC-8A7F-06A54FFA3E7C}.Release|x86.Build.0 = Release|Win32 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug|ARM64.Build.0 = Debug|ARM64 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug|x64.ActiveCfg = Debug|x64 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug|x64.Build.0 = Debug|x64 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug|x86.ActiveCfg = Debug|Win32 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Debug|x86.Build.0 = Debug|Win32 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release|ARM64.ActiveCfg = Release|ARM64 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release|ARM64.Build.0 = Release|ARM64 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release|x64.ActiveCfg = Release|x64 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release|x64.Build.0 = Release|x64 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release|x86.ActiveCfg = Release|Win32 + {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}.Release|x86.Build.0 = Release|Win32 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug|ARM64.Build.0 = Debug|ARM64 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug|x64.ActiveCfg = Debug|x64 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug|x64.Build.0 = Debug|x64 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug|x86.ActiveCfg = Debug|Win32 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Debug|x86.Build.0 = Debug|Win32 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release|ARM64.ActiveCfg = Release|ARM64 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release|ARM64.Build.0 = Release|ARM64 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release|x64.ActiveCfg = Release|x64 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release|x64.Build.0 = Release|x64 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release|x86.ActiveCfg = Release|Win32 + {8F19E3DA-8929-4000-87B5-3CA6929636CC}.Release|x86.Build.0 = Release|Win32 + {51A00565-5787-4911-9CC0-28403AA4909D}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {51A00565-5787-4911-9CC0-28403AA4909D}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {51A00565-5787-4911-9CC0-28403AA4909D}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {51A00565-5787-4911-9CC0-28403AA4909D}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {51A00565-5787-4911-9CC0-28403AA4909D}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {51A00565-5787-4911-9CC0-28403AA4909D}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {51A00565-5787-4911-9CC0-28403AA4909D}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {51A00565-5787-4911-9CC0-28403AA4909D}.Debug|ARM64.Build.0 = Debug|ARM64 + {51A00565-5787-4911-9CC0-28403AA4909D}.Debug|x64.ActiveCfg = Debug|x64 + {51A00565-5787-4911-9CC0-28403AA4909D}.Debug|x64.Build.0 = Debug|x64 + {51A00565-5787-4911-9CC0-28403AA4909D}.Debug|x86.ActiveCfg = Debug|Win32 + {51A00565-5787-4911-9CC0-28403AA4909D}.Debug|x86.Build.0 = Debug|Win32 + {51A00565-5787-4911-9CC0-28403AA4909D}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {51A00565-5787-4911-9CC0-28403AA4909D}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {51A00565-5787-4911-9CC0-28403AA4909D}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {51A00565-5787-4911-9CC0-28403AA4909D}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {51A00565-5787-4911-9CC0-28403AA4909D}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {51A00565-5787-4911-9CC0-28403AA4909D}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {51A00565-5787-4911-9CC0-28403AA4909D}.Release|ARM64.ActiveCfg = Release|ARM64 + {51A00565-5787-4911-9CC0-28403AA4909D}.Release|ARM64.Build.0 = Release|ARM64 + {51A00565-5787-4911-9CC0-28403AA4909D}.Release|x64.ActiveCfg = Release|x64 + {51A00565-5787-4911-9CC0-28403AA4909D}.Release|x64.Build.0 = Release|x64 + {51A00565-5787-4911-9CC0-28403AA4909D}.Release|x86.ActiveCfg = Release|Win32 + {51A00565-5787-4911-9CC0-28403AA4909D}.Release|x86.Build.0 = Release|Win32 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug|ARM64.Build.0 = Debug|ARM64 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug|x64.ActiveCfg = Debug|x64 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug|x64.Build.0 = Debug|x64 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug|x86.ActiveCfg = Debug|Win32 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Debug|x86.Build.0 = Debug|Win32 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release|ARM64.ActiveCfg = Release|ARM64 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release|ARM64.Build.0 = Release|ARM64 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release|x64.ActiveCfg = Release|x64 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release|x64.Build.0 = Release|x64 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release|x86.ActiveCfg = Release|Win32 + {92B64AE7-D773-4F05-89F1-CE59BBF4F053}.Release|x86.Build.0 = Release|Win32 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug|ARM64.Build.0 = Debug|ARM64 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug|x64.ActiveCfg = Debug|x64 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug|x64.Build.0 = Debug|x64 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug|x86.ActiveCfg = Debug|Win32 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Debug|x86.Build.0 = Debug|Win32 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release|ARM64.ActiveCfg = Release|ARM64 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release|ARM64.Build.0 = Release|ARM64 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release|x64.ActiveCfg = Release|x64 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release|x64.Build.0 = Release|x64 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release|x86.ActiveCfg = Release|Win32 + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3}.Release|x86.Build.0 = Release|Win32 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug|ARM64.Build.0 = Debug|ARM64 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug|x64.ActiveCfg = Debug|x64 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug|x64.Build.0 = Debug|x64 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug|x86.ActiveCfg = Debug|Win32 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Debug|x86.Build.0 = Debug|Win32 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release|ARM64.ActiveCfg = Release|ARM64 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release|ARM64.Build.0 = Release|ARM64 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release|x64.ActiveCfg = Release|x64 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release|x64.Build.0 = Release|x64 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release|x86.ActiveCfg = Release|Win32 + {A643BB06-735D-47F3-BFE7-B6D3C36F7097}.Release|x86.Build.0 = Release|Win32 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug|ARM64.Build.0 = Debug|ARM64 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug|x64.ActiveCfg = Debug|x64 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug|x64.Build.0 = Debug|x64 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug|x86.ActiveCfg = Debug|Win32 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Debug|x86.Build.0 = Debug|Win32 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release|ARM64.ActiveCfg = Release|ARM64 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release|ARM64.Build.0 = Release|ARM64 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release|x64.ActiveCfg = Release|x64 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release|x64.Build.0 = Release|x64 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release|x86.ActiveCfg = Release|Win32 + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A}.Release|x86.Build.0 = Release|Win32 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug|ARM64.Build.0 = Debug|ARM64 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug|x64.ActiveCfg = Debug|x64 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug|x64.Build.0 = Debug|x64 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug|x86.ActiveCfg = Debug|Win32 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Debug|x86.Build.0 = Debug|Win32 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release|ARM64.ActiveCfg = Release|ARM64 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release|ARM64.Build.0 = Release|ARM64 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release|x64.ActiveCfg = Release|x64 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release|x64.Build.0 = Release|x64 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release|x86.ActiveCfg = Release|Win32 + {B332DCA8-3599-4A99-917A-82261BDC27AC}.Release|x86.Build.0 = Release|Win32 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug|ARM64.Build.0 = Debug|ARM64 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug|x64.ActiveCfg = Debug|x64 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug|x64.Build.0 = Debug|x64 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug|x86.ActiveCfg = Debug|Win32 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Debug|x86.Build.0 = Debug|Win32 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release|ARM64.ActiveCfg = Release|ARM64 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release|ARM64.Build.0 = Release|ARM64 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release|x64.ActiveCfg = Release|x64 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release|x64.Build.0 = Release|x64 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release|x86.ActiveCfg = Release|Win32 + {59089B0C-AAB4-4532-B294-44DEAE7178B7}.Release|x86.Build.0 = Release|Win32 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug|ARM64.Build.0 = Debug|ARM64 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug|x64.ActiveCfg = Debug|x64 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug|x64.Build.0 = Debug|x64 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug|x86.ActiveCfg = Debug|Win32 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Debug|x86.Build.0 = Debug|Win32 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Release|ARM64.ActiveCfg = Release|ARM64 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Release|ARM64.Build.0 = Release|ARM64 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Release|x64.ActiveCfg = Release|x64 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Release|x64.Build.0 = Release|x64 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Release|x86.ActiveCfg = Release|Win32 + {C298876B-6C12-4EA4-903B-33450BCD9884}.Release|x86.Build.0 = Release|Win32 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug|ARM64.Build.0 = Debug|ARM64 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug|x64.ActiveCfg = Debug|x64 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug|x64.Build.0 = Debug|x64 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug|x86.ActiveCfg = Debug|Win32 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Debug|x86.Build.0 = Debug|Win32 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Release|ARM64.ActiveCfg = Release|ARM64 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Release|ARM64.Build.0 = Release|ARM64 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Release|x64.ActiveCfg = Release|x64 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Release|x64.Build.0 = Release|x64 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Release|x86.ActiveCfg = Release|Win32 + {83F586FA-C801-4979-ACCA-006BD628CC88}.Release|x86.Build.0 = Release|Win32 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug|ARM64.Build.0 = Debug|ARM64 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug|x64.ActiveCfg = Debug|x64 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug|x64.Build.0 = Debug|x64 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug|x86.ActiveCfg = Debug|Win32 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Debug|x86.Build.0 = Debug|Win32 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release|ARM64.ActiveCfg = Release|ARM64 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release|ARM64.Build.0 = Release|ARM64 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release|x64.ActiveCfg = Release|x64 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release|x64.Build.0 = Release|x64 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release|x86.ActiveCfg = Release|Win32 + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22}.Release|x86.Build.0 = Release|Win32 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug|ARM64.Build.0 = Debug|ARM64 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug|x64.ActiveCfg = Debug|x64 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug|x64.Build.0 = Debug|x64 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug|x86.ActiveCfg = Debug|Win32 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Debug|x86.Build.0 = Debug|Win32 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release|ARM64.ActiveCfg = Release|ARM64 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release|ARM64.Build.0 = Release|ARM64 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release|x64.ActiveCfg = Release|x64 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release|x64.Build.0 = Release|x64 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release|x86.ActiveCfg = Release|Win32 + {FF2970AE-E2E9-405F-B321-D523A1BD44A0}.Release|x86.Build.0 = Release|Win32 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug|ARM64.Build.0 = Debug|ARM64 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug|x64.ActiveCfg = Debug|x64 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug|x64.Build.0 = Debug|x64 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug|x86.ActiveCfg = Debug|Win32 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Debug|x86.Build.0 = Debug|Win32 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release|ARM64.ActiveCfg = Release|ARM64 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release|ARM64.Build.0 = Release|ARM64 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release|x64.ActiveCfg = Release|x64 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release|x64.Build.0 = Release|x64 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release|x86.ActiveCfg = Release|Win32 + {79417CE2-FEEB-42F0-BC53-62D5267B19B1}.Release|x86.Build.0 = Release|Win32 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug|ARM64.Build.0 = Debug|ARM64 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug|x64.ActiveCfg = Debug|x64 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug|x64.Build.0 = Debug|x64 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug|x86.ActiveCfg = Debug|Win32 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Debug|x86.Build.0 = Debug|Win32 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Release|ARM64.ActiveCfg = Release|ARM64 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Release|ARM64.Build.0 = Release|ARM64 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Release|x64.ActiveCfg = Release|x64 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Release|x64.Build.0 = Release|x64 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Release|x86.ActiveCfg = Release|Win32 + {AFDDE100-2D36-4749-817D-12E54C56312F}.Release|x86.Build.0 = Release|Win32 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug|ARM64.Build.0 = Debug|ARM64 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug|x64.ActiveCfg = Debug|x64 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug|x64.Build.0 = Debug|x64 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug|x86.ActiveCfg = Debug|Win32 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Debug|x86.Build.0 = Debug|Win32 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release|ARM64.ActiveCfg = Release|ARM64 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release|ARM64.Build.0 = Release|ARM64 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release|x64.ActiveCfg = Release|x64 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release|x64.Build.0 = Release|x64 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release|x86.ActiveCfg = Release|Win32 + {B7812167-50FB-4934-996F-DF6FE4CBBFDF}.Release|x86.Build.0 = Release|Win32 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug|ARM64.Build.0 = Debug|ARM64 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug|x64.ActiveCfg = Debug|x64 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug|x64.Build.0 = Debug|x64 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug|x86.ActiveCfg = Debug|Win32 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Debug|x86.Build.0 = Debug|Win32 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release|ARM64.ActiveCfg = Release|ARM64 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release|ARM64.Build.0 = Release|ARM64 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release|x64.ActiveCfg = Release|x64 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release|x64.Build.0 = Release|x64 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release|x86.ActiveCfg = Release|Win32 + {39DB56C7-05F8-492C-A8D4-F19E40FECB59}.Release|x86.Build.0 = Release|Win32 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug|ARM64.Build.0 = Debug|ARM64 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug|x64.ActiveCfg = Debug|x64 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug|x64.Build.0 = Debug|x64 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug|x86.ActiveCfg = Debug|Win32 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Debug|x86.Build.0 = Debug|Win32 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release|ARM64.ActiveCfg = Release|ARM64 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release|ARM64.Build.0 = Release|ARM64 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release|x64.ActiveCfg = Release|x64 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release|x64.Build.0 = Release|x64 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release|x86.ActiveCfg = Release|Win32 + {82F3D34B-8DB2-4C6A-98B1-132245DD9D99}.Release|x86.Build.0 = Release|Win32 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug|ARM64.Build.0 = Debug|ARM64 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug|x64.ActiveCfg = Debug|x64 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug|x64.Build.0 = Debug|x64 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug|x86.ActiveCfg = Debug|Win32 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Debug|x86.Build.0 = Debug|Win32 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release|ARM64.ActiveCfg = Release|ARM64 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release|ARM64.Build.0 = Release|ARM64 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release|x64.ActiveCfg = Release|x64 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release|x64.Build.0 = Release|x64 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release|x86.ActiveCfg = Release|Win32 + {CBD6C0F8-8200-4E9A-9D7C-6505A2AA4A62}.Release|x86.Build.0 = Release|Win32 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug|ARM64.Build.0 = Debug|ARM64 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug|x64.ActiveCfg = Debug|x64 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug|x64.Build.0 = Debug|x64 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug|x86.ActiveCfg = Debug|Win32 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Debug|x86.Build.0 = Debug|Win32 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release|ARM64.ActiveCfg = Release|ARM64 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release|ARM64.Build.0 = Release|ARM64 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release|x64.ActiveCfg = Release|x64 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release|x64.Build.0 = Release|x64 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release|x86.ActiveCfg = Release|Win32 + {14BA7F98-02CC-4648-9236-676BFF9458AF}.Release|x86.Build.0 = Release|Win32 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug|ARM64.Build.0 = Debug|ARM64 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug|x64.ActiveCfg = Debug|x64 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug|x64.Build.0 = Debug|x64 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug|x86.ActiveCfg = Debug|Win32 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Debug|x86.Build.0 = Debug|Win32 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Release|ARM64.ActiveCfg = Release|ARM64 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Release|ARM64.Build.0 = Release|ARM64 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Release|x64.ActiveCfg = Release|x64 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Release|x64.Build.0 = Release|x64 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Release|x86.ActiveCfg = Release|Win32 + {0859A973-E4FE-4688-8D16-0253163FDE24}.Release|x86.Build.0 = Release|Win32 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug|ARM64.Build.0 = Debug|ARM64 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug|x64.ActiveCfg = Debug|x64 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug|x64.Build.0 = Debug|x64 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug|x86.ActiveCfg = Debug|Win32 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Debug|x86.Build.0 = Debug|Win32 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release|ARM64.ActiveCfg = Release|ARM64 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release|ARM64.Build.0 = Release|ARM64 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release|x64.ActiveCfg = Release|x64 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release|x64.Build.0 = Release|x64 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release|x86.ActiveCfg = Release|Win32 + {F3412853-2B6A-4334-8CF2-B796CDAE0850}.Release|x86.Build.0 = Release|Win32 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug|ARM64.Build.0 = Debug|ARM64 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug|x64.ActiveCfg = Debug|x64 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug|x64.Build.0 = Debug|x64 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug|x86.ActiveCfg = Debug|Win32 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Debug|x86.Build.0 = Debug|Win32 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release|ARM64.ActiveCfg = Release|ARM64 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release|ARM64.Build.0 = Release|ARM64 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release|x64.ActiveCfg = Release|x64 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release|x64.Build.0 = Release|x64 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release|x86.ActiveCfg = Release|Win32 + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912}.Release|x86.Build.0 = Release|Win32 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug|ARM64.Build.0 = Debug|ARM64 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug|x64.ActiveCfg = Debug|x64 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug|x64.Build.0 = Debug|x64 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug|x86.ActiveCfg = Debug|Win32 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Debug|x86.Build.0 = Debug|Win32 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release|ARM64.ActiveCfg = Release|ARM64 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release|ARM64.Build.0 = Release|ARM64 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release|x64.ActiveCfg = Release|x64 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release|x64.Build.0 = Release|x64 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release|x86.ActiveCfg = Release|Win32 + {D03F2C82-9553-4AFA-8F49-9234009122B6}.Release|x86.Build.0 = Release|Win32 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug|ARM64.Build.0 = Debug|ARM64 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug|x64.ActiveCfg = Debug|x64 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug|x64.Build.0 = Debug|x64 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug|x86.ActiveCfg = Debug|Win32 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Debug|x86.Build.0 = Debug|Win32 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release|ARM64.ActiveCfg = Release|ARM64 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release|ARM64.Build.0 = Release|ARM64 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release|x64.ActiveCfg = Release|x64 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release|x64.Build.0 = Release|x64 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release|x86.ActiveCfg = Release|Win32 + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3}.Release|x86.Build.0 = Release|Win32 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug|ARM64.Build.0 = Debug|ARM64 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug|x64.ActiveCfg = Debug|x64 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug|x64.Build.0 = Debug|x64 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug|x86.ActiveCfg = Debug|Win32 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Debug|x86.Build.0 = Debug|Win32 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release|ARM64.ActiveCfg = Release|ARM64 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release|ARM64.Build.0 = Release|ARM64 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release|x64.ActiveCfg = Release|x64 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release|x64.Build.0 = Release|x64 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release|x86.ActiveCfg = Release|Win32 + {A53CCF42-A972-478F-9336-0F618B3EC06A}.Release|x86.Build.0 = Release|Win32 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug|ARM64.Build.0 = Debug|ARM64 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug|x64.ActiveCfg = Debug|x64 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug|x64.Build.0 = Debug|x64 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug|x86.ActiveCfg = Debug|Win32 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Debug|x86.Build.0 = Debug|Win32 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release|ARM64.ActiveCfg = Release|ARM64 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release|ARM64.Build.0 = Release|ARM64 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release|x64.ActiveCfg = Release|x64 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release|x64.Build.0 = Release|x64 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release|x86.ActiveCfg = Release|Win32 + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA}.Release|x86.Build.0 = Release|Win32 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug|ARM64.Build.0 = Debug|ARM64 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug|x64.ActiveCfg = Debug|x64 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug|x64.Build.0 = Debug|x64 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug|x86.ActiveCfg = Debug|Win32 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Debug|x86.Build.0 = Debug|Win32 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Release|ARM64.ActiveCfg = Release|ARM64 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Release|ARM64.Build.0 = Release|ARM64 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Release|x64.ActiveCfg = Release|x64 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Release|x64.Build.0 = Release|x64 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Release|x86.ActiveCfg = Release|Win32 + {870723DD-945A-4136-B65B-4AF3BF85369C}.Release|x86.Build.0 = Release|Win32 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug|ARM64.Build.0 = Debug|ARM64 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug|x64.ActiveCfg = Debug|x64 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug|x64.Build.0 = Debug|x64 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug|x86.ActiveCfg = Debug|Win32 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Debug|x86.Build.0 = Debug|Win32 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release|ARM64.ActiveCfg = Release|ARM64 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release|ARM64.Build.0 = Release|ARM64 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release|x64.ActiveCfg = Release|x64 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release|x64.Build.0 = Release|x64 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release|x86.ActiveCfg = Release|Win32 + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97}.Release|x86.Build.0 = Release|Win32 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug|ARM64.Build.0 = Debug|ARM64 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug|x64.ActiveCfg = Debug|x64 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug|x64.Build.0 = Debug|x64 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug|x86.ActiveCfg = Debug|Win32 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Debug|x86.Build.0 = Debug|Win32 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release|ARM64.ActiveCfg = Release|ARM64 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release|ARM64.Build.0 = Release|ARM64 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release|x64.ActiveCfg = Release|x64 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release|x64.Build.0 = Release|x64 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release|x86.ActiveCfg = Release|Win32 + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0}.Release|x86.Build.0 = Release|Win32 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug|ARM64.Build.0 = Debug|ARM64 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug|x64.ActiveCfg = Debug|x64 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug|x64.Build.0 = Debug|x64 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug|x86.ActiveCfg = Debug|Win32 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Debug|x86.Build.0 = Debug|Win32 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release|ARM64.ActiveCfg = Release|ARM64 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release|ARM64.Build.0 = Release|ARM64 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release|x64.ActiveCfg = Release|x64 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release|x64.Build.0 = Release|x64 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release|x86.ActiveCfg = Release|Win32 + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4}.Release|x86.Build.0 = Release|Win32 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug|ARM64.Build.0 = Debug|ARM64 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug|x64.ActiveCfg = Debug|x64 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug|x64.Build.0 = Debug|x64 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug|x86.ActiveCfg = Debug|Win32 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Debug|x86.Build.0 = Debug|Win32 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release|ARM64.ActiveCfg = Release|ARM64 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release|ARM64.Build.0 = Release|ARM64 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release|x64.ActiveCfg = Release|x64 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release|x64.Build.0 = Release|x64 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release|x86.ActiveCfg = Release|Win32 + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E}.Release|x86.Build.0 = Release|Win32 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug|ARM64.Build.0 = Debug|ARM64 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug|x64.ActiveCfg = Debug|x64 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug|x64.Build.0 = Debug|x64 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug|x86.ActiveCfg = Debug|Win32 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Debug|x86.Build.0 = Debug|Win32 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release|ARM64.ActiveCfg = Release|ARM64 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release|ARM64.Build.0 = Release|ARM64 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release|x64.ActiveCfg = Release|x64 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release|x64.Build.0 = Release|x64 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release|x86.ActiveCfg = Release|Win32 + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7}.Release|x86.Build.0 = Release|Win32 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug|ARM64.Build.0 = Debug|ARM64 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug|x64.ActiveCfg = Debug|x64 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug|x64.Build.0 = Debug|x64 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug|x86.ActiveCfg = Debug|Win32 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Debug|x86.Build.0 = Debug|Win32 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release|ARM64.ActiveCfg = Release|ARM64 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release|ARM64.Build.0 = Release|ARM64 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release|x64.ActiveCfg = Release|x64 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release|x64.Build.0 = Release|x64 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release|x86.ActiveCfg = Release|Win32 + {2B78CF0A-5403-45E2-99BD-493F1679BCDB}.Release|x86.Build.0 = Release|Win32 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug|ARM64.Build.0 = Debug|ARM64 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug|x64.ActiveCfg = Debug|x64 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug|x64.Build.0 = Debug|x64 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug|x86.ActiveCfg = Debug|Win32 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Debug|x86.Build.0 = Debug|Win32 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Release|ARM64.ActiveCfg = Release|ARM64 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Release|ARM64.Build.0 = Release|ARM64 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Release|x64.ActiveCfg = Release|x64 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Release|x64.Build.0 = Release|x64 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Release|x86.ActiveCfg = Release|Win32 + {0AB968E0-E993-45CE-8875-7453C96DF583}.Release|x86.Build.0 = Release|Win32 + {25923141-9859-4AFE-8168-0DF78322FC63}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {25923141-9859-4AFE-8168-0DF78322FC63}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {25923141-9859-4AFE-8168-0DF78322FC63}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {25923141-9859-4AFE-8168-0DF78322FC63}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {25923141-9859-4AFE-8168-0DF78322FC63}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {25923141-9859-4AFE-8168-0DF78322FC63}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {25923141-9859-4AFE-8168-0DF78322FC63}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {25923141-9859-4AFE-8168-0DF78322FC63}.Debug|ARM64.Build.0 = Debug|ARM64 + {25923141-9859-4AFE-8168-0DF78322FC63}.Debug|x64.ActiveCfg = Debug|x64 + {25923141-9859-4AFE-8168-0DF78322FC63}.Debug|x64.Build.0 = Debug|x64 + {25923141-9859-4AFE-8168-0DF78322FC63}.Debug|x86.ActiveCfg = Debug|Win32 + {25923141-9859-4AFE-8168-0DF78322FC63}.Debug|x86.Build.0 = Debug|Win32 + {25923141-9859-4AFE-8168-0DF78322FC63}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {25923141-9859-4AFE-8168-0DF78322FC63}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {25923141-9859-4AFE-8168-0DF78322FC63}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {25923141-9859-4AFE-8168-0DF78322FC63}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {25923141-9859-4AFE-8168-0DF78322FC63}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {25923141-9859-4AFE-8168-0DF78322FC63}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {25923141-9859-4AFE-8168-0DF78322FC63}.Release|ARM64.ActiveCfg = Release|ARM64 + {25923141-9859-4AFE-8168-0DF78322FC63}.Release|ARM64.Build.0 = Release|ARM64 + {25923141-9859-4AFE-8168-0DF78322FC63}.Release|x64.ActiveCfg = Release|x64 + {25923141-9859-4AFE-8168-0DF78322FC63}.Release|x64.Build.0 = Release|x64 + {25923141-9859-4AFE-8168-0DF78322FC63}.Release|x86.ActiveCfg = Release|Win32 + {25923141-9859-4AFE-8168-0DF78322FC63}.Release|x86.Build.0 = Release|Win32 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug|ARM64.Build.0 = Debug|ARM64 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug|x64.ActiveCfg = Debug|x64 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug|x64.Build.0 = Debug|x64 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug|x86.ActiveCfg = Debug|Win32 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Debug|x86.Build.0 = Debug|Win32 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Release|ARM64.ActiveCfg = Release|ARM64 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Release|ARM64.Build.0 = Release|ARM64 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Release|x64.ActiveCfg = Release|x64 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Release|x64.Build.0 = Release|x64 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Release|x86.ActiveCfg = Release|Win32 + {7E855020-7FA4-482D-B510-2E709354FE8B}.Release|x86.Build.0 = Release|Win32 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug|ARM64.Build.0 = Debug|ARM64 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug|x64.ActiveCfg = Debug|x64 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug|x64.Build.0 = Debug|x64 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug|x86.ActiveCfg = Debug|Win32 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Debug|x86.Build.0 = Debug|Win32 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release|ARM64.ActiveCfg = Release|ARM64 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release|ARM64.Build.0 = Release|ARM64 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release|x64.ActiveCfg = Release|x64 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release|x64.Build.0 = Release|x64 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release|x86.ActiveCfg = Release|Win32 + {9782E0C8-2BD3-4F67-B420-21CF19CA2435}.Release|x86.Build.0 = Release|Win32 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug|ARM64.Build.0 = Debug|ARM64 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug|x64.ActiveCfg = Debug|x64 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug|x64.Build.0 = Debug|x64 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug|x86.ActiveCfg = Debug|Win32 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Debug|x86.Build.0 = Debug|Win32 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release|ARM64.ActiveCfg = Release|ARM64 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release|ARM64.Build.0 = Release|ARM64 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release|x64.ActiveCfg = Release|x64 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release|x64.Build.0 = Release|x64 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release|x86.ActiveCfg = Release|Win32 + {9F4135E3-9814-452C-9B35-0EFBCD792B49}.Release|x86.Build.0 = Release|Win32 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug|ARM64.Build.0 = Debug|ARM64 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug|x64.ActiveCfg = Debug|x64 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug|x64.Build.0 = Debug|x64 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug|x86.ActiveCfg = Debug|Win32 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Debug|x86.Build.0 = Debug|Win32 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release|ARM64.ActiveCfg = Release|ARM64 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release|ARM64.Build.0 = Release|ARM64 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release|x64.ActiveCfg = Release|x64 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release|x64.Build.0 = Release|x64 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release|x86.ActiveCfg = Release|Win32 + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE}.Release|x86.Build.0 = Release|Win32 + {B19DD336-538E-4091-A559-EAA717FEC899}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {B19DD336-538E-4091-A559-EAA717FEC899}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {B19DD336-538E-4091-A559-EAA717FEC899}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {B19DD336-538E-4091-A559-EAA717FEC899}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {B19DD336-538E-4091-A559-EAA717FEC899}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {B19DD336-538E-4091-A559-EAA717FEC899}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {B19DD336-538E-4091-A559-EAA717FEC899}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {B19DD336-538E-4091-A559-EAA717FEC899}.Debug|ARM64.Build.0 = Debug|ARM64 + {B19DD336-538E-4091-A559-EAA717FEC899}.Debug|x64.ActiveCfg = Debug|x64 + {B19DD336-538E-4091-A559-EAA717FEC899}.Debug|x64.Build.0 = Debug|x64 + {B19DD336-538E-4091-A559-EAA717FEC899}.Debug|x86.ActiveCfg = Debug|Win32 + {B19DD336-538E-4091-A559-EAA717FEC899}.Debug|x86.Build.0 = Debug|Win32 + {B19DD336-538E-4091-A559-EAA717FEC899}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {B19DD336-538E-4091-A559-EAA717FEC899}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {B19DD336-538E-4091-A559-EAA717FEC899}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {B19DD336-538E-4091-A559-EAA717FEC899}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {B19DD336-538E-4091-A559-EAA717FEC899}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {B19DD336-538E-4091-A559-EAA717FEC899}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {B19DD336-538E-4091-A559-EAA717FEC899}.Release|ARM64.ActiveCfg = Release|ARM64 + {B19DD336-538E-4091-A559-EAA717FEC899}.Release|ARM64.Build.0 = Release|ARM64 + {B19DD336-538E-4091-A559-EAA717FEC899}.Release|x64.ActiveCfg = Release|x64 + {B19DD336-538E-4091-A559-EAA717FEC899}.Release|x64.Build.0 = Release|x64 + {B19DD336-538E-4091-A559-EAA717FEC899}.Release|x86.ActiveCfg = Release|Win32 + {B19DD336-538E-4091-A559-EAA717FEC899}.Release|x86.Build.0 = Release|Win32 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug|ARM64.Build.0 = Debug|ARM64 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug|x64.ActiveCfg = Debug|x64 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug|x64.Build.0 = Debug|x64 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug|x86.ActiveCfg = Debug|Win32 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Debug|x86.Build.0 = Debug|Win32 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release|ARM64.ActiveCfg = Release|ARM64 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release|ARM64.Build.0 = Release|ARM64 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release|x64.ActiveCfg = Release|x64 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release|x64.Build.0 = Release|x64 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release|x86.ActiveCfg = Release|Win32 + {0BF60202-43F7-48E9-8717-D31E56FA5BE0}.Release|x86.Build.0 = Release|Win32 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug|ARM64.Build.0 = Debug|ARM64 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug|x64.ActiveCfg = Debug|x64 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug|x64.Build.0 = Debug|x64 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug|x86.ActiveCfg = Debug|Win32 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Debug|x86.Build.0 = Debug|Win32 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release|ARM64.ActiveCfg = Release|ARM64 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release|ARM64.Build.0 = Release|ARM64 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release|x64.ActiveCfg = Release|x64 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release|x64.Build.0 = Release|x64 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release|x86.ActiveCfg = Release|Win32 + {4E863E5B-0B95-43BE-8D4F-B9EB6C394FEC}.Release|x86.Build.0 = Release|Win32 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug|ARM64.Build.0 = Debug|ARM64 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug|x64.ActiveCfg = Debug|x64 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug|x64.Build.0 = Debug|x64 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug|x86.ActiveCfg = Debug|Win32 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Debug|x86.Build.0 = Debug|Win32 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release|ARM64.ActiveCfg = Release|ARM64 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release|ARM64.Build.0 = Release|ARM64 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release|x64.ActiveCfg = Release|x64 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release|x64.Build.0 = Release|x64 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release|x86.ActiveCfg = Release|Win32 + {6D75CD88-1A03-4955-B8C7-ACFC3742154F}.Release|x86.Build.0 = Release|Win32 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug|ARM64.Build.0 = Debug|ARM64 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug|x64.ActiveCfg = Debug|x64 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug|x64.Build.0 = Debug|x64 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug|x86.ActiveCfg = Debug|Win32 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Debug|x86.Build.0 = Debug|Win32 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release|ARM64.ActiveCfg = Release|ARM64 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release|ARM64.Build.0 = Release|ARM64 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release|x64.ActiveCfg = Release|x64 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release|x64.Build.0 = Release|x64 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release|x86.ActiveCfg = Release|Win32 + {8DD0EB7E-668E-452D-91D7-906C64A9C8AC}.Release|x86.Build.0 = Release|Win32 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug|ARM64.Build.0 = Debug|ARM64 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug|x64.ActiveCfg = Debug|x64 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug|x64.Build.0 = Debug|x64 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug|x86.ActiveCfg = Debug|Win32 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Debug|x86.Build.0 = Debug|Win32 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release|ARM64.ActiveCfg = Release|ARM64 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release|ARM64.Build.0 = Release|ARM64 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release|x64.ActiveCfg = Release|x64 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release|x64.Build.0 = Release|x64 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release|x86.ActiveCfg = Release|Win32 + {F6FD9C75-AAA7-48C9-B19D-FD37C8FB9B7E}.Release|x86.Build.0 = Release|Win32 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug|ARM64.Build.0 = Debug|ARM64 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug|x64.ActiveCfg = Debug|x64 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug|x64.Build.0 = Debug|x64 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug|x86.ActiveCfg = Debug|Win32 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Debug|x86.Build.0 = Debug|Win32 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release|ARM64.ActiveCfg = Release|ARM64 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release|ARM64.Build.0 = Release|ARM64 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release|x64.ActiveCfg = Release|x64 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release|x64.Build.0 = Release|x64 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release|x86.ActiveCfg = Release|Win32 + {1FE8758D-7E8A-41F3-9B6D-FD50E9A2A03D}.Release|x86.Build.0 = Release|Win32 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug|ARM64.Build.0 = Debug|ARM64 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug|x64.ActiveCfg = Debug|x64 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug|x64.Build.0 = Debug|x64 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug|x86.ActiveCfg = Debug|Win32 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Debug|x86.Build.0 = Debug|Win32 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Release|ARM64.ActiveCfg = Release|ARM64 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Release|ARM64.Build.0 = Release|ARM64 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Release|x64.ActiveCfg = Release|x64 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Release|x64.Build.0 = Release|x64 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Release|x86.ActiveCfg = Release|Win32 + {25BCB876-B60A-499B-9046-E9801CFD7780}.Release|x86.Build.0 = Release|Win32 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug|ARM64.Build.0 = Debug|ARM64 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug|x64.ActiveCfg = Debug|x64 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug|x64.Build.0 = Debug|x64 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug|x86.ActiveCfg = Debug|Win32 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Debug|x86.Build.0 = Debug|Win32 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release|ARM64.ActiveCfg = Release|ARM64 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release|ARM64.Build.0 = Release|ARM64 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release|x64.ActiveCfg = Release|x64 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release|x64.Build.0 = Release|x64 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release|x86.ActiveCfg = Release|Win32 + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F}.Release|x86.Build.0 = Release|Win32 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug|ARM64.Build.0 = Debug|ARM64 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug|x64.ActiveCfg = Debug|x64 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug|x64.Build.0 = Debug|x64 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug|x86.ActiveCfg = Debug|Win32 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Debug|x86.Build.0 = Debug|Win32 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release|ARM64.ActiveCfg = Release|ARM64 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release|ARM64.Build.0 = Release|ARM64 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release|x64.ActiveCfg = Release|x64 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release|x64.Build.0 = Release|x64 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release|x86.ActiveCfg = Release|Win32 + {2BB0C1D4-9298-45AC-B244-67A99769A292}.Release|x86.Build.0 = Release|Win32 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug|ARM64.Build.0 = Debug|ARM64 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug|x64.ActiveCfg = Debug|x64 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug|x64.Build.0 = Debug|x64 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug|x86.ActiveCfg = Debug|Win32 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Debug|x86.Build.0 = Debug|Win32 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release|ARM64.ActiveCfg = Release|ARM64 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release|ARM64.Build.0 = Release|ARM64 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release|x64.ActiveCfg = Release|x64 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release|x64.Build.0 = Release|x64 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release|x86.ActiveCfg = Release|Win32 + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB}.Release|x86.Build.0 = Release|Win32 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug|ARM64.Build.0 = Debug|ARM64 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug|x64.ActiveCfg = Debug|x64 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug|x64.Build.0 = Debug|x64 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug|x86.ActiveCfg = Debug|Win32 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Debug|x86.Build.0 = Debug|Win32 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release|ARM64.ActiveCfg = Release|ARM64 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release|ARM64.Build.0 = Release|ARM64 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release|x64.ActiveCfg = Release|x64 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release|x64.Build.0 = Release|x64 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release|x86.ActiveCfg = Release|Win32 + {81064BCE-EEC1-43B0-9912-F05F2B54B11A}.Release|x86.Build.0 = Release|Win32 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug|ARM64.Build.0 = Debug|ARM64 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug|x64.ActiveCfg = Debug|x64 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug|x64.Build.0 = Debug|x64 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug|x86.ActiveCfg = Debug|Win32 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Debug|x86.Build.0 = Debug|Win32 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release|ARM64.ActiveCfg = Release|ARM64 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release|ARM64.Build.0 = Release|ARM64 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release|x64.ActiveCfg = Release|x64 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release|x64.Build.0 = Release|x64 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release|x86.ActiveCfg = Release|Win32 + {31B41997-3890-45E3-93FE-C57B363E9C0D}.Release|x86.Build.0 = Release|Win32 + {D550AB93-DF31-4B76-873F-F075018352F4}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {D550AB93-DF31-4B76-873F-F075018352F4}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {D550AB93-DF31-4B76-873F-F075018352F4}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {D550AB93-DF31-4B76-873F-F075018352F4}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {D550AB93-DF31-4B76-873F-F075018352F4}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {D550AB93-DF31-4B76-873F-F075018352F4}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {D550AB93-DF31-4B76-873F-F075018352F4}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D550AB93-DF31-4B76-873F-F075018352F4}.Debug|ARM64.Build.0 = Debug|ARM64 + {D550AB93-DF31-4B76-873F-F075018352F4}.Debug|x64.ActiveCfg = Debug|x64 + {D550AB93-DF31-4B76-873F-F075018352F4}.Debug|x64.Build.0 = Debug|x64 + {D550AB93-DF31-4B76-873F-F075018352F4}.Debug|x86.ActiveCfg = Debug|Win32 + {D550AB93-DF31-4B76-873F-F075018352F4}.Debug|x86.Build.0 = Debug|Win32 + {D550AB93-DF31-4B76-873F-F075018352F4}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {D550AB93-DF31-4B76-873F-F075018352F4}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {D550AB93-DF31-4B76-873F-F075018352F4}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {D550AB93-DF31-4B76-873F-F075018352F4}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {D550AB93-DF31-4B76-873F-F075018352F4}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {D550AB93-DF31-4B76-873F-F075018352F4}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {D550AB93-DF31-4B76-873F-F075018352F4}.Release|ARM64.ActiveCfg = Release|ARM64 + {D550AB93-DF31-4B76-873F-F075018352F4}.Release|ARM64.Build.0 = Release|ARM64 + {D550AB93-DF31-4B76-873F-F075018352F4}.Release|x64.ActiveCfg = Release|x64 + {D550AB93-DF31-4B76-873F-F075018352F4}.Release|x64.Build.0 = Release|x64 + {D550AB93-DF31-4B76-873F-F075018352F4}.Release|x86.ActiveCfg = Release|Win32 + {D550AB93-DF31-4B76-873F-F075018352F4}.Release|x86.Build.0 = Release|Win32 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug|ARM64.Build.0 = Debug|ARM64 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug|x64.ActiveCfg = Debug|x64 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug|x64.Build.0 = Debug|x64 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug|x86.ActiveCfg = Debug|Win32 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Debug|x86.Build.0 = Debug|Win32 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release|ARM64.ActiveCfg = Release|ARM64 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release|ARM64.Build.0 = Release|ARM64 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release|x64.ActiveCfg = Release|x64 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release|x64.Build.0 = Release|x64 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release|x86.ActiveCfg = Release|Win32 + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A}.Release|x86.Build.0 = Release|Win32 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug|ARM64.Build.0 = Debug|ARM64 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug|x64.ActiveCfg = Debug|x64 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug|x64.Build.0 = Debug|x64 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug|x86.ActiveCfg = Debug|Win32 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Debug|x86.Build.0 = Debug|Win32 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release|ARM64.ActiveCfg = Release|ARM64 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release|ARM64.Build.0 = Release|ARM64 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release|x64.ActiveCfg = Release|x64 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release|x64.Build.0 = Release|x64 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release|x86.ActiveCfg = Release|Win32 + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF}.Release|x86.Build.0 = Release|Win32 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug|ARM64.Build.0 = Debug|ARM64 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug|x64.ActiveCfg = Debug|x64 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug|x64.Build.0 = Debug|x64 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug|x86.ActiveCfg = Debug|Win32 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Debug|x86.Build.0 = Debug|Win32 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release|ARM64.ActiveCfg = Release|ARM64 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release|ARM64.Build.0 = Release|ARM64 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release|x64.ActiveCfg = Release|x64 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release|x64.Build.0 = Release|x64 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release|x86.ActiveCfg = Release|Win32 + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79}.Release|x86.Build.0 = Release|Win32 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug|ARM64.Build.0 = Debug|ARM64 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug|x64.ActiveCfg = Debug|x64 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug|x64.Build.0 = Debug|x64 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug|x86.ActiveCfg = Debug|Win32 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Debug|x86.Build.0 = Debug|Win32 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release|ARM64.ActiveCfg = Release|ARM64 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release|ARM64.Build.0 = Release|ARM64 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release|x64.ActiveCfg = Release|x64 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release|x64.Build.0 = Release|x64 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release|x86.ActiveCfg = Release|Win32 + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5}.Release|x86.Build.0 = Release|Win32 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug|ARM64.Build.0 = Debug|ARM64 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug|x64.ActiveCfg = Debug|x64 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug|x64.Build.0 = Debug|x64 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug|x86.ActiveCfg = Debug|Win32 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Debug|x86.Build.0 = Debug|Win32 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release|ARM64.ActiveCfg = Release|ARM64 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release|ARM64.Build.0 = Release|ARM64 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release|x64.ActiveCfg = Release|x64 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release|x64.Build.0 = Release|x64 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release|x86.ActiveCfg = Release|Win32 + {03E7018C-44A2-4C46-9CE7-F2A135A2692B}.Release|x86.Build.0 = Release|Win32 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug|ARM64.Build.0 = Debug|ARM64 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug|x64.ActiveCfg = Debug|x64 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug|x64.Build.0 = Debug|x64 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug|x86.ActiveCfg = Debug|Win32 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Debug|x86.Build.0 = Debug|Win32 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release|ARM64.ActiveCfg = Release|ARM64 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release|ARM64.Build.0 = Release|ARM64 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release|x64.ActiveCfg = Release|x64 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release|x64.Build.0 = Release|x64 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release|x86.ActiveCfg = Release|Win32 + {F3F6FE4D-9D9E-451A-B0BA-81456104B672}.Release|x86.Build.0 = Release|Win32 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug|ARM64.Build.0 = Debug|ARM64 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug|x64.ActiveCfg = Debug|x64 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug|x64.Build.0 = Debug|x64 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug|x86.ActiveCfg = Debug|Win32 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Debug|x86.Build.0 = Debug|Win32 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release|ARM64.ActiveCfg = Release|ARM64 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release|ARM64.Build.0 = Release|ARM64 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release|x64.ActiveCfg = Release|x64 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release|x64.Build.0 = Release|x64 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release|x86.ActiveCfg = Release|Win32 + {C27794B5-1293-4EA7-BC0E-0F18E6325539}.Release|x86.Build.0 = Release|Win32 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug|ARM64.Build.0 = Debug|ARM64 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug|x64.ActiveCfg = Debug|x64 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug|x64.Build.0 = Debug|x64 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug|x86.ActiveCfg = Debug|Win32 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Debug|x86.Build.0 = Debug|Win32 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release|ARM64.ActiveCfg = Release|ARM64 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release|ARM64.Build.0 = Release|ARM64 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release|x64.ActiveCfg = Release|x64 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release|x64.Build.0 = Release|x64 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release|x86.ActiveCfg = Release|Win32 + {02F41059-12A2-4A96-8D77-07EFE4B108FD}.Release|x86.Build.0 = Release|Win32 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug|ARM64.Build.0 = Debug|ARM64 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug|x64.ActiveCfg = Debug|x64 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug|x64.Build.0 = Debug|x64 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug|x86.ActiveCfg = Debug|Win32 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Debug|x86.Build.0 = Debug|Win32 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release|ARM64.ActiveCfg = Release|ARM64 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release|ARM64.Build.0 = Release|ARM64 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release|x64.ActiveCfg = Release|x64 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release|x64.Build.0 = Release|x64 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release|x86.ActiveCfg = Release|Win32 + {B774E0B9-9514-4E88-975F-4EB6C3B8D519}.Release|x86.Build.0 = Release|Win32 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug|ARM64.Build.0 = Debug|ARM64 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug|x64.ActiveCfg = Debug|x64 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug|x64.Build.0 = Debug|x64 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug|x86.ActiveCfg = Debug|Win32 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Debug|x86.Build.0 = Debug|Win32 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release|ARM64.ActiveCfg = Release|ARM64 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release|ARM64.Build.0 = Release|ARM64 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release|x64.ActiveCfg = Release|x64 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release|x64.Build.0 = Release|x64 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release|x86.ActiveCfg = Release|Win32 + {D91367C2-2189-4859-A7FE-D2CAB84FA15C}.Release|x86.Build.0 = Release|Win32 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug|ARM64.Build.0 = Debug|ARM64 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug|x64.ActiveCfg = Debug|x64 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug|x64.Build.0 = Debug|x64 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug|x86.ActiveCfg = Debug|Win32 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Debug|x86.Build.0 = Debug|Win32 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Release|ARM64.ActiveCfg = Release|ARM64 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Release|ARM64.Build.0 = Release|ARM64 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Release|x64.ActiveCfg = Release|x64 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Release|x64.Build.0 = Release|x64 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Release|x86.ActiveCfg = Release|Win32 + {33459B4E-1839-4856-BF6B-22480D11FE31}.Release|x86.Build.0 = Release|Win32 + {48871156-181A-475A-BD8D-200086A09675}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {48871156-181A-475A-BD8D-200086A09675}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {48871156-181A-475A-BD8D-200086A09675}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {48871156-181A-475A-BD8D-200086A09675}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {48871156-181A-475A-BD8D-200086A09675}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {48871156-181A-475A-BD8D-200086A09675}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {48871156-181A-475A-BD8D-200086A09675}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {48871156-181A-475A-BD8D-200086A09675}.Debug|ARM64.Build.0 = Debug|ARM64 + {48871156-181A-475A-BD8D-200086A09675}.Debug|x64.ActiveCfg = Debug|x64 + {48871156-181A-475A-BD8D-200086A09675}.Debug|x64.Build.0 = Debug|x64 + {48871156-181A-475A-BD8D-200086A09675}.Debug|x86.ActiveCfg = Debug|Win32 + {48871156-181A-475A-BD8D-200086A09675}.Debug|x86.Build.0 = Debug|Win32 + {48871156-181A-475A-BD8D-200086A09675}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {48871156-181A-475A-BD8D-200086A09675}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {48871156-181A-475A-BD8D-200086A09675}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {48871156-181A-475A-BD8D-200086A09675}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {48871156-181A-475A-BD8D-200086A09675}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {48871156-181A-475A-BD8D-200086A09675}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {48871156-181A-475A-BD8D-200086A09675}.Release|ARM64.ActiveCfg = Release|ARM64 + {48871156-181A-475A-BD8D-200086A09675}.Release|ARM64.Build.0 = Release|ARM64 + {48871156-181A-475A-BD8D-200086A09675}.Release|x64.ActiveCfg = Release|x64 + {48871156-181A-475A-BD8D-200086A09675}.Release|x64.Build.0 = Release|x64 + {48871156-181A-475A-BD8D-200086A09675}.Release|x86.ActiveCfg = Release|Win32 + {48871156-181A-475A-BD8D-200086A09675}.Release|x86.Build.0 = Release|Win32 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug|ARM64.Build.0 = Debug|ARM64 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug|x64.ActiveCfg = Debug|x64 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug|x64.Build.0 = Debug|x64 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug|x86.ActiveCfg = Debug|Win32 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Debug|x86.Build.0 = Debug|Win32 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release|ARM64.ActiveCfg = Release|ARM64 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release|ARM64.Build.0 = Release|ARM64 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release|x64.ActiveCfg = Release|x64 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release|x64.Build.0 = Release|x64 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release|x86.ActiveCfg = Release|Win32 + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1}.Release|x86.Build.0 = Release|Win32 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug|ARM64.Build.0 = Debug|ARM64 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug|x64.ActiveCfg = Debug|x64 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug|x64.Build.0 = Debug|x64 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug|x86.ActiveCfg = Debug|Win32 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Debug|x86.Build.0 = Debug|Win32 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release|ARM64.ActiveCfg = Release|ARM64 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release|ARM64.Build.0 = Release|ARM64 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release|x64.ActiveCfg = Release|x64 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release|x64.Build.0 = Release|x64 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release|x86.ActiveCfg = Release|Win32 + {1C49E35A-2838-49D9-9D5F-4B8134960EF6}.Release|x86.Build.0 = Release|Win32 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug|ARM64.Build.0 = Debug|ARM64 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug|x64.ActiveCfg = Debug|x64 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug|x64.Build.0 = Debug|x64 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug|x86.ActiveCfg = Debug|Win32 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Debug|x86.Build.0 = Debug|Win32 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release|ARM64.ActiveCfg = Release|ARM64 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release|ARM64.Build.0 = Release|ARM64 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release|x64.ActiveCfg = Release|x64 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release|x64.Build.0 = Release|x64 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release|x86.ActiveCfg = Release|Win32 + {F91142E2-A999-47F0-9E74-38C1E2930EBE}.Release|x86.Build.0 = Release|Win32 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug|ARM64.Build.0 = Debug|ARM64 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug|x64.ActiveCfg = Debug|x64 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug|x64.Build.0 = Debug|x64 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug|x86.ActiveCfg = Debug|Win32 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Debug|x86.Build.0 = Debug|Win32 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release|ARM64.ActiveCfg = Release|ARM64 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release|ARM64.Build.0 = Release|ARM64 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release|x64.ActiveCfg = Release|x64 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release|x64.Build.0 = Release|x64 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release|x86.ActiveCfg = Release|Win32 + {1EDD4BCF-345C-4065-8CBD-7285224293C3}.Release|x86.Build.0 = Release|Win32 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug|ARM64.Build.0 = Debug|ARM64 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug|x64.ActiveCfg = Debug|x64 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug|x64.Build.0 = Debug|x64 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug|x86.ActiveCfg = Debug|Win32 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Debug|x86.Build.0 = Debug|Win32 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release|ARM64.ActiveCfg = Release|ARM64 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release|ARM64.Build.0 = Release|ARM64 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release|x64.ActiveCfg = Release|x64 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release|x64.Build.0 = Release|x64 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release|x86.ActiveCfg = Release|Win32 + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA}.Release|x86.Build.0 = Release|Win32 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug|ARM64.Build.0 = Debug|ARM64 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug|x64.ActiveCfg = Debug|x64 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug|x64.Build.0 = Debug|x64 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug|x86.ActiveCfg = Debug|Win32 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Debug|x86.Build.0 = Debug|Win32 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release|ARM64.ActiveCfg = Release|ARM64 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release|ARM64.Build.0 = Release|ARM64 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release|x64.ActiveCfg = Release|x64 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release|x64.Build.0 = Release|x64 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release|x86.ActiveCfg = Release|Win32 + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870}.Release|x86.Build.0 = Release|Win32 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug|ARM64.Build.0 = Debug|ARM64 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug|x64.ActiveCfg = Debug|x64 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug|x64.Build.0 = Debug|x64 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug|x86.ActiveCfg = Debug|Win32 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Debug|x86.Build.0 = Debug|Win32 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release|ARM64.ActiveCfg = Release|ARM64 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release|ARM64.Build.0 = Release|ARM64 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release|x64.ActiveCfg = Release|x64 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release|x64.Build.0 = Release|x64 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release|x86.ActiveCfg = Release|Win32 + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1}.Release|x86.Build.0 = Release|Win32 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug|ARM64.Build.0 = Debug|ARM64 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug|x64.ActiveCfg = Debug|x64 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug|x64.Build.0 = Debug|x64 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug|x86.ActiveCfg = Debug|Win32 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Debug|x86.Build.0 = Debug|Win32 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release|ARM64.ActiveCfg = Release|ARM64 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release|ARM64.Build.0 = Release|ARM64 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release|x64.ActiveCfg = Release|x64 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release|x64.Build.0 = Release|x64 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release|x86.ActiveCfg = Release|Win32 + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF}.Release|x86.Build.0 = Release|Win32 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug|ARM64.Build.0 = Debug|ARM64 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug|x64.ActiveCfg = Debug|x64 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug|x64.Build.0 = Debug|x64 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug|x86.ActiveCfg = Debug|Win32 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Debug|x86.Build.0 = Debug|Win32 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release|ARM64.ActiveCfg = Release|ARM64 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release|ARM64.Build.0 = Release|ARM64 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release|x64.ActiveCfg = Release|x64 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release|x64.Build.0 = Release|x64 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release|x86.ActiveCfg = Release|Win32 + {CF3755C4-937D-4ABF-B7B3-95140808717F}.Release|x86.Build.0 = Release|Win32 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug|ARM64.Build.0 = Debug|ARM64 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug|x64.ActiveCfg = Debug|x64 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug|x64.Build.0 = Debug|x64 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug|x86.ActiveCfg = Debug|Win32 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Debug|x86.Build.0 = Debug|Win32 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release|ARM64.ActiveCfg = Release|ARM64 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release|ARM64.Build.0 = Release|ARM64 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release|x64.ActiveCfg = Release|x64 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release|x64.Build.0 = Release|x64 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release|x86.ActiveCfg = Release|Win32 + {D34939FE-8873-4C53-8D6C-74DED78EA3C4}.Release|x86.Build.0 = Release|Win32 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug|ARM64.Build.0 = Debug|ARM64 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug|x64.ActiveCfg = Debug|x64 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug|x64.Build.0 = Debug|x64 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug|x86.ActiveCfg = Debug|Win32 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Debug|x86.Build.0 = Debug|Win32 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release|ARM64.ActiveCfg = Release|ARM64 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release|ARM64.Build.0 = Release|ARM64 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release|x64.ActiveCfg = Release|x64 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release|x64.Build.0 = Release|x64 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release|x86.ActiveCfg = Release|Win32 + {D408A730-363A-4ABF-BCEF-5D63DCC66042}.Release|x86.Build.0 = Release|Win32 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug|ARM64.Build.0 = Debug|ARM64 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug|x64.ActiveCfg = Debug|x64 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug|x64.Build.0 = Debug|x64 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug|x86.ActiveCfg = Debug|Win32 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Debug|x86.Build.0 = Debug|Win32 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release|ARM64.ActiveCfg = Release|ARM64 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release|ARM64.Build.0 = Release|ARM64 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release|x64.ActiveCfg = Release|x64 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release|x64.Build.0 = Release|x64 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release|x86.ActiveCfg = Release|Win32 + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED}.Release|x86.Build.0 = Release|Win32 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug|ARM64.Build.0 = Debug|ARM64 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug|x64.ActiveCfg = Debug|x64 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug|x64.Build.0 = Debug|x64 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug|x86.ActiveCfg = Debug|Win32 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Debug|x86.Build.0 = Debug|Win32 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release|ARM64.ActiveCfg = Release|ARM64 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release|ARM64.Build.0 = Release|ARM64 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release|x64.ActiveCfg = Release|x64 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release|x64.Build.0 = Release|x64 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release|x86.ActiveCfg = Release|Win32 + {52FB7463-C128-42AF-A02F-78F48473EA9A}.Release|x86.Build.0 = Release|Win32 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug|ARM64.Build.0 = Debug|ARM64 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug|x64.ActiveCfg = Debug|x64 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug|x64.Build.0 = Debug|x64 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug|x86.ActiveCfg = Debug|Win32 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Debug|x86.Build.0 = Debug|Win32 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release|ARM64.ActiveCfg = Release|ARM64 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release|ARM64.Build.0 = Release|ARM64 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release|x64.ActiveCfg = Release|x64 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release|x64.Build.0 = Release|x64 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release|x86.ActiveCfg = Release|Win32 + {7381D91E-5C72-48F0-AAB4-95C9B10D7484}.Release|x86.Build.0 = Release|Win32 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug|ARM64.Build.0 = Debug|ARM64 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug|x64.ActiveCfg = Debug|x64 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug|x64.Build.0 = Debug|x64 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug|x86.ActiveCfg = Debug|Win32 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Debug|x86.Build.0 = Debug|Win32 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release|ARM64.ActiveCfg = Release|ARM64 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release|ARM64.Build.0 = Release|ARM64 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release|x64.ActiveCfg = Release|x64 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release|x64.Build.0 = Release|x64 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release|x86.ActiveCfg = Release|Win32 + {D36EC43E-B31F-4CF4-8285-93A7A9D90189}.Release|x86.Build.0 = Release|Win32 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug|ARM64.Build.0 = Debug|ARM64 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug|x64.ActiveCfg = Debug|x64 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug|x64.Build.0 = Debug|x64 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug|x86.ActiveCfg = Debug|Win32 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Debug|x86.Build.0 = Debug|Win32 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Release|ARM64.ActiveCfg = Release|ARM64 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Release|ARM64.Build.0 = Release|ARM64 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Release|x64.ActiveCfg = Release|x64 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Release|x64.Build.0 = Release|x64 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Release|x86.ActiveCfg = Release|Win32 + {274C0319-7E1E-4188-936B-8DF3331230B3}.Release|x86.Build.0 = Release|Win32 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug|ARM64.Build.0 = Debug|ARM64 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug|x64.ActiveCfg = Debug|x64 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug|x64.Build.0 = Debug|x64 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug|x86.ActiveCfg = Debug|Win32 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Debug|x86.Build.0 = Debug|Win32 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release|ARM64.ActiveCfg = Release|ARM64 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release|ARM64.Build.0 = Release|ARM64 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release|x64.ActiveCfg = Release|x64 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release|x64.Build.0 = Release|x64 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release|x86.ActiveCfg = Release|Win32 + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629}.Release|x86.Build.0 = Release|Win32 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug|ARM64.Build.0 = Debug|ARM64 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug|x64.ActiveCfg = Debug|x64 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug|x64.Build.0 = Debug|x64 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug|x86.ActiveCfg = Debug|Win32 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Debug|x86.Build.0 = Debug|Win32 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release|ARM64.ActiveCfg = Release|ARM64 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release|ARM64.Build.0 = Release|ARM64 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release|x64.ActiveCfg = Release|x64 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release|x64.Build.0 = Release|x64 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release|x86.ActiveCfg = Release|Win32 + {600C3D4F-0670-4DB4-B30F-520A729053B5}.Release|x86.Build.0 = Release|Win32 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug|ARM64.Build.0 = Debug|ARM64 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug|x64.ActiveCfg = Debug|x64 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug|x64.Build.0 = Debug|x64 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug|x86.ActiveCfg = Debug|Win32 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Debug|x86.Build.0 = Debug|Win32 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release|ARM64.ActiveCfg = Release|ARM64 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release|ARM64.Build.0 = Release|ARM64 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release|x64.ActiveCfg = Release|x64 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release|x64.Build.0 = Release|x64 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release|x86.ActiveCfg = Release|Win32 + {11F33A39-74B7-4018-B5F9-CC285A673A8F}.Release|x86.Build.0 = Release|Win32 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug|ARM64.Build.0 = Debug|ARM64 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug|x64.ActiveCfg = Debug|x64 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug|x64.Build.0 = Debug|x64 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug|x86.ActiveCfg = Debug|Win32 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Debug|x86.Build.0 = Debug|Win32 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release|ARM64.ActiveCfg = Release|ARM64 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release|ARM64.Build.0 = Release|ARM64 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release|x64.ActiveCfg = Release|x64 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release|x64.Build.0 = Release|x64 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release|x86.ActiveCfg = Release|Win32 + {A6F5E35E-B4A7-41B3-853A-75558E6E0715}.Release|x86.Build.0 = Release|Win32 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug|ARM64.Build.0 = Debug|ARM64 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug|x64.ActiveCfg = Debug|x64 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug|x64.Build.0 = Debug|x64 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug|x86.ActiveCfg = Debug|Win32 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Debug|x86.Build.0 = Debug|Win32 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release|ARM64.ActiveCfg = Release|ARM64 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release|ARM64.Build.0 = Release|ARM64 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release|x64.ActiveCfg = Release|x64 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release|x64.Build.0 = Release|x64 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release|x86.ActiveCfg = Release|Win32 + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release|x86.Build.0 = Release|Win32 + {FDE6080B-E203-4066-910D-AD0302566008}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {FDE6080B-E203-4066-910D-AD0302566008}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {FDE6080B-E203-4066-910D-AD0302566008}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {FDE6080B-E203-4066-910D-AD0302566008}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {FDE6080B-E203-4066-910D-AD0302566008}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {FDE6080B-E203-4066-910D-AD0302566008}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {FDE6080B-E203-4066-910D-AD0302566008}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FDE6080B-E203-4066-910D-AD0302566008}.Debug|ARM64.Build.0 = Debug|ARM64 + {FDE6080B-E203-4066-910D-AD0302566008}.Debug|x64.ActiveCfg = Debug|x64 + {FDE6080B-E203-4066-910D-AD0302566008}.Debug|x64.Build.0 = Debug|x64 + {FDE6080B-E203-4066-910D-AD0302566008}.Debug|x86.ActiveCfg = Debug|Win32 + {FDE6080B-E203-4066-910D-AD0302566008}.Debug|x86.Build.0 = Debug|Win32 + {FDE6080B-E203-4066-910D-AD0302566008}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {FDE6080B-E203-4066-910D-AD0302566008}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {FDE6080B-E203-4066-910D-AD0302566008}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {FDE6080B-E203-4066-910D-AD0302566008}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {FDE6080B-E203-4066-910D-AD0302566008}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {FDE6080B-E203-4066-910D-AD0302566008}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {FDE6080B-E203-4066-910D-AD0302566008}.Release|ARM64.ActiveCfg = Release|ARM64 + {FDE6080B-E203-4066-910D-AD0302566008}.Release|ARM64.Build.0 = Release|ARM64 + {FDE6080B-E203-4066-910D-AD0302566008}.Release|x64.ActiveCfg = Release|x64 + {FDE6080B-E203-4066-910D-AD0302566008}.Release|x64.Build.0 = Release|x64 + {FDE6080B-E203-4066-910D-AD0302566008}.Release|x86.ActiveCfg = Release|Win32 + {FDE6080B-E203-4066-910D-AD0302566008}.Release|x86.Build.0 = Release|Win32 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug|ARM64.Build.0 = Debug|ARM64 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug|x64.ActiveCfg = Debug|x64 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug|x64.Build.0 = Debug|x64 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug|x86.ActiveCfg = Debug|Win32 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug|x86.Build.0 = Debug|Win32 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release|ARM64.ActiveCfg = Release|ARM64 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release|ARM64.Build.0 = Release|ARM64 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release|x64.ActiveCfg = Release|x64 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release|x64.Build.0 = Release|x64 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release|x86.ActiveCfg = Release|Win32 + {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release|x86.Build.0 = Release|Win32 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug|ARM64.Build.0 = Debug|ARM64 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug|x64.ActiveCfg = Debug|x64 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug|x64.Build.0 = Debug|x64 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug|x86.ActiveCfg = Debug|Win32 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug|x86.Build.0 = Debug|Win32 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|ARM64.ActiveCfg = Release|ARM64 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|ARM64.Build.0 = Release|ARM64 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|x64.ActiveCfg = Release|x64 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|x64.Build.0 = Release|x64 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|x86.ActiveCfg = Release|Win32 + {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|x86.Build.0 = Release|Win32 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug|ARM64.Build.0 = Debug|ARM64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug|x64.ActiveCfg = Debug|x64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug|x64.Build.0 = Debug|x64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug|x86.ActiveCfg = Debug|Win32 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug|x86.Build.0 = Debug|Win32 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release|ARM64.ActiveCfg = Release|ARM64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release|ARM64.Build.0 = Release|ARM64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release|x64.ActiveCfg = Release|x64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release|x64.Build.0 = Release|x64 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release|x86.ActiveCfg = Release|Win32 + {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Release|x86.Build.0 = Release|Win32 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug|ARM64.Build.0 = Debug|ARM64 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug|x64.ActiveCfg = Debug|x64 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug|x64.Build.0 = Debug|x64 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug|x86.ActiveCfg = Debug|Win32 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Debug|x86.Build.0 = Debug|Win32 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release|ARM64.ActiveCfg = Release|ARM64 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release|ARM64.Build.0 = Release|ARM64 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release|x64.ActiveCfg = Release|x64 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release|x64.Build.0 = Release|x64 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release|x86.ActiveCfg = Release|Win32 + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}.Release|x86.Build.0 = Release|Win32 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug|ARM64.Build.0 = Debug|ARM64 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug|x64.ActiveCfg = Debug|x64 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug|x64.Build.0 = Debug|x64 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug|x86.ActiveCfg = Debug|Win32 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Debug|x86.Build.0 = Debug|Win32 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release|ARM64.ActiveCfg = Release|ARM64 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release|ARM64.Build.0 = Release|ARM64 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release|x64.ActiveCfg = Release|x64 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release|x64.Build.0 = Release|x64 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release|x86.ActiveCfg = Release|Win32 + {F2DB2E59-76BF-4D81-859A-AFC289C046C0}.Release|x86.Build.0 = Release|Win32 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug|ARM64.Build.0 = Debug|ARM64 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug|x64.ActiveCfg = Debug|x64 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug|x64.Build.0 = Debug|x64 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug|x86.ActiveCfg = Debug|Win32 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Debug|x86.Build.0 = Debug|Win32 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release|ARM64.ActiveCfg = Release|ARM64 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release|ARM64.Build.0 = Release|ARM64 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release|x64.ActiveCfg = Release|x64 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release|x64.Build.0 = Release|x64 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release|x86.ActiveCfg = Release|Win32 + {3FE7E9B6-49AC-4246-A789-28DB4644567B}.Release|x86.Build.0 = Release|Win32 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug|ARM64.Build.0 = Debug|ARM64 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug|x64.ActiveCfg = Debug|x64 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug|x64.Build.0 = Debug|x64 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug|x86.ActiveCfg = Debug|Win32 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Debug|x86.Build.0 = Debug|Win32 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release|ARM64.ActiveCfg = Release|ARM64 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release|ARM64.Build.0 = Release|ARM64 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release|x64.ActiveCfg = Release|x64 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release|x64.Build.0 = Release|x64 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release|x86.ActiveCfg = Release|Win32 + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0}.Release|x86.Build.0 = Release|Win32 + {191A5289-BA65-4638-A215-C521F0187313}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {191A5289-BA65-4638-A215-C521F0187313}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {191A5289-BA65-4638-A215-C521F0187313}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {191A5289-BA65-4638-A215-C521F0187313}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {191A5289-BA65-4638-A215-C521F0187313}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {191A5289-BA65-4638-A215-C521F0187313}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {191A5289-BA65-4638-A215-C521F0187313}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {191A5289-BA65-4638-A215-C521F0187313}.Debug|ARM64.Build.0 = Debug|ARM64 + {191A5289-BA65-4638-A215-C521F0187313}.Debug|x64.ActiveCfg = Debug|x64 + {191A5289-BA65-4638-A215-C521F0187313}.Debug|x64.Build.0 = Debug|x64 + {191A5289-BA65-4638-A215-C521F0187313}.Debug|x86.ActiveCfg = Debug|Win32 + {191A5289-BA65-4638-A215-C521F0187313}.Debug|x86.Build.0 = Debug|Win32 + {191A5289-BA65-4638-A215-C521F0187313}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {191A5289-BA65-4638-A215-C521F0187313}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {191A5289-BA65-4638-A215-C521F0187313}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {191A5289-BA65-4638-A215-C521F0187313}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {191A5289-BA65-4638-A215-C521F0187313}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {191A5289-BA65-4638-A215-C521F0187313}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {191A5289-BA65-4638-A215-C521F0187313}.Release|ARM64.ActiveCfg = Release|ARM64 + {191A5289-BA65-4638-A215-C521F0187313}.Release|ARM64.Build.0 = Release|ARM64 + {191A5289-BA65-4638-A215-C521F0187313}.Release|x64.ActiveCfg = Release|x64 + {191A5289-BA65-4638-A215-C521F0187313}.Release|x64.Build.0 = Release|x64 + {191A5289-BA65-4638-A215-C521F0187313}.Release|x86.ActiveCfg = Release|Win32 + {191A5289-BA65-4638-A215-C521F0187313}.Release|x86.Build.0 = Release|Win32 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug|ARM64.Build.0 = Debug|ARM64 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug|x64.ActiveCfg = Debug|x64 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug|x64.Build.0 = Debug|x64 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug|x86.ActiveCfg = Debug|Win32 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Debug|x86.Build.0 = Debug|Win32 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release|ARM64.ActiveCfg = Release|ARM64 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release|ARM64.Build.0 = Release|ARM64 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release|x64.ActiveCfg = Release|x64 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release|x64.Build.0 = Release|x64 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release|x86.ActiveCfg = Release|Win32 + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359}.Release|x86.Build.0 = Release|Win32 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug|ARM64.Build.0 = Debug|ARM64 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug|x64.ActiveCfg = Debug|x64 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug|x64.Build.0 = Debug|x64 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug|x86.ActiveCfg = Debug|Win32 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Debug|x86.Build.0 = Debug|Win32 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release|ARM64.ActiveCfg = Release|ARM64 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release|ARM64.Build.0 = Release|ARM64 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release|x64.ActiveCfg = Release|x64 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release|x64.Build.0 = Release|x64 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release|x86.ActiveCfg = Release|Win32 + {8B1AF423-00F1-4924-AC54-F77D402D2AC9}.Release|x86.Build.0 = Release|Win32 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug|ARM64.Build.0 = Debug|ARM64 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug|x64.ActiveCfg = Debug|x64 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug|x64.Build.0 = Debug|x64 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug|x86.ActiveCfg = Debug|Win32 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Debug|x86.Build.0 = Debug|Win32 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release|ARM64.ActiveCfg = Release|ARM64 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release|ARM64.Build.0 = Release|ARM64 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release|x64.ActiveCfg = Release|x64 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release|x64.Build.0 = Release|x64 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release|x86.ActiveCfg = Release|Win32 + {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release|x86.Build.0 = Release|Win32 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug|ARM64.Build.0 = Debug|ARM64 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug|x64.ActiveCfg = Debug|x64 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug|x64.Build.0 = Debug|x64 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug|x86.ActiveCfg = Debug|Win32 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug|x86.Build.0 = Debug|Win32 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release|ARM64.ActiveCfg = Release|ARM64 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release|ARM64.Build.0 = Release|ARM64 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release|x64.ActiveCfg = Release|x64 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release|x64.Build.0 = Release|x64 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release|x86.ActiveCfg = Release|Win32 + {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release|x86.Build.0 = Release|Win32 + {27B110CC-43C0-400A-89D9-245E681647D7}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {27B110CC-43C0-400A-89D9-245E681647D7}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {27B110CC-43C0-400A-89D9-245E681647D7}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {27B110CC-43C0-400A-89D9-245E681647D7}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {27B110CC-43C0-400A-89D9-245E681647D7}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {27B110CC-43C0-400A-89D9-245E681647D7}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {27B110CC-43C0-400A-89D9-245E681647D7}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {27B110CC-43C0-400A-89D9-245E681647D7}.Debug|ARM64.Build.0 = Debug|ARM64 + {27B110CC-43C0-400A-89D9-245E681647D7}.Debug|x64.ActiveCfg = Debug|x64 + {27B110CC-43C0-400A-89D9-245E681647D7}.Debug|x64.Build.0 = Debug|x64 + {27B110CC-43C0-400A-89D9-245E681647D7}.Debug|x86.ActiveCfg = Debug|Win32 + {27B110CC-43C0-400A-89D9-245E681647D7}.Debug|x86.Build.0 = Debug|Win32 + {27B110CC-43C0-400A-89D9-245E681647D7}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {27B110CC-43C0-400A-89D9-245E681647D7}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {27B110CC-43C0-400A-89D9-245E681647D7}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {27B110CC-43C0-400A-89D9-245E681647D7}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {27B110CC-43C0-400A-89D9-245E681647D7}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {27B110CC-43C0-400A-89D9-245E681647D7}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {27B110CC-43C0-400A-89D9-245E681647D7}.Release|ARM64.ActiveCfg = Release|ARM64 + {27B110CC-43C0-400A-89D9-245E681647D7}.Release|ARM64.Build.0 = Release|ARM64 + {27B110CC-43C0-400A-89D9-245E681647D7}.Release|x64.ActiveCfg = Release|x64 + {27B110CC-43C0-400A-89D9-245E681647D7}.Release|x64.Build.0 = Release|x64 + {27B110CC-43C0-400A-89D9-245E681647D7}.Release|x86.ActiveCfg = Release|Win32 + {27B110CC-43C0-400A-89D9-245E681647D7}.Release|x86.Build.0 = Release|Win32 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug|ARM64.Build.0 = Debug|ARM64 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug|x64.ActiveCfg = Debug|x64 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug|x64.Build.0 = Debug|x64 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug|x86.ActiveCfg = Debug|Win32 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Debug|x86.Build.0 = Debug|Win32 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release|ARM64.ActiveCfg = Release|ARM64 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release|ARM64.Build.0 = Release|ARM64 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release|x64.ActiveCfg = Release|x64 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release|x64.Build.0 = Release|x64 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release|x86.ActiveCfg = Release|Win32 + {1DE84812-E143-4C4B-A61D-9267AAD55401}.Release|x86.Build.0 = Release|Win32 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug|ARM64.Build.0 = Debug|ARM64 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug|x64.ActiveCfg = Debug|x64 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug|x64.Build.0 = Debug|x64 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug|x86.ActiveCfg = Debug|Win32 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Debug|x86.Build.0 = Debug|Win32 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release|ARM64.ActiveCfg = Release|ARM64 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release|ARM64.Build.0 = Release|ARM64 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release|x64.ActiveCfg = Release|x64 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release|x64.Build.0 = Release|x64 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release|x86.ActiveCfg = Release|Win32 + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release|x86.Build.0 = Release|Win32 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug|ARM64.Build.0 = Debug|ARM64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug|x64.ActiveCfg = Debug|x64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug|x64.Build.0 = Debug|x64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug|x86.ActiveCfg = Debug|Win32 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug|x86.Build.0 = Debug|Win32 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release|ARM64.ActiveCfg = Release|ARM64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release|ARM64.Build.0 = Release|ARM64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release|x64.ActiveCfg = Release|x64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release|x64.Build.0 = Release|x64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release|x86.ActiveCfg = Release|Win32 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release|x86.Build.0 = Release|Win32 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug|ARM64.Build.0 = Debug|ARM64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug|x64.ActiveCfg = Debug|x64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug|x64.Build.0 = Debug|x64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug|x86.ActiveCfg = Debug|Win32 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug|x86.Build.0 = Debug|Win32 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release|ARM64.ActiveCfg = Release|ARM64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release|ARM64.Build.0 = Release|ARM64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release|x64.ActiveCfg = Release|x64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release|x64.Build.0 = Release|x64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release|x86.ActiveCfg = Release|Win32 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release|x86.Build.0 = Release|Win32 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug|ARM64.Build.0 = Debug|ARM64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug|x64.ActiveCfg = Debug|x64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug|x64.Build.0 = Debug|x64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug|x86.ActiveCfg = Debug|Win32 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug|x86.Build.0 = Debug|Win32 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release|ARM64.ActiveCfg = Release|ARM64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release|ARM64.Build.0 = Release|ARM64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release|x64.ActiveCfg = Release|x64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release|x64.Build.0 = Release|x64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release|x86.ActiveCfg = Release|Win32 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release|x86.Build.0 = Release|Win32 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug|ARM64.Build.0 = Debug|ARM64 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug|x64.ActiveCfg = Debug|x64 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug|x64.Build.0 = Debug|x64 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug|x86.ActiveCfg = Debug|Win32 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Debug|x86.Build.0 = Debug|Win32 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release|ARM64.ActiveCfg = Release|ARM64 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release|ARM64.Build.0 = Release|ARM64 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release|x64.ActiveCfg = Release|x64 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release|x64.Build.0 = Release|x64 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release|x86.ActiveCfg = Release|Win32 + {DFDE29A7-4F54-455D-B20B-D2BF79D3B3F7}.Release|x86.Build.0 = Release|Win32 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug|ARM64.Build.0 = Debug|ARM64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug|x64.ActiveCfg = Debug|x64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug|x64.Build.0 = Debug|x64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug|x86.ActiveCfg = Debug|Win32 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug|x86.Build.0 = Debug|Win32 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|ARM64.ActiveCfg = Release|ARM64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|ARM64.Build.0 = Release|ARM64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|x64.ActiveCfg = Release|x64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|x64.Build.0 = Release|x64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|x86.ActiveCfg = Release|Win32 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|x86.Build.0 = Release|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug|ARM64.Build.0 = Debug|ARM64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug|x64.ActiveCfg = Debug|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug|x64.Build.0 = Debug|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug|x86.ActiveCfg = Debug|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug|x86.Build.0 = Debug|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|ARM64.ActiveCfg = Release|ARM64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|ARM64.Build.0 = Release|ARM64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x64.ActiveCfg = Release|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x64.Build.0 = Release|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x86.ActiveCfg = Release|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x86.Build.0 = Release|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug|ARM64.Build.0 = Debug|ARM64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug|x64.ActiveCfg = Debug|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug|x64.Build.0 = Debug|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug|x86.ActiveCfg = Debug|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug|x86.Build.0 = Debug|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|ARM64.ActiveCfg = Release|ARM64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|ARM64.Build.0 = Release|ARM64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x64.ActiveCfg = Release|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x64.Build.0 = Release|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x86.ActiveCfg = Release|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x86.Build.0 = Release|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|ARM64.Build.0 = Debug|ARM64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x64.ActiveCfg = Debug|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x64.Build.0 = Debug|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x86.ActiveCfg = Debug|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x86.Build.0 = Debug|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|ARM64.ActiveCfg = Release|ARM64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|ARM64.Build.0 = Release|ARM64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x64.ActiveCfg = Release|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x64.Build.0 = Release|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x86.ActiveCfg = Release|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x86.Build.0 = Release|Win32 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug|ARM64.Build.0 = Debug|ARM64 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug|x64.ActiveCfg = Debug|x64 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug|x64.Build.0 = Debug|x64 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug|x86.ActiveCfg = Debug|Win32 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Debug|x86.Build.0 = Debug|Win32 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release|ARM64.ActiveCfg = Release|ARM64 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release|ARM64.Build.0 = Release|ARM64 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release|x64.ActiveCfg = Release|x64 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release|x64.Build.0 = Release|x64 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release|x86.ActiveCfg = Release|Win32 + {A4B0D971-3CD6-41C9-8AB2-055D25A33373}.Release|x86.Build.0 = Release|Win32 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug|ARM64.Build.0 = Debug|ARM64 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug|x64.ActiveCfg = Debug|x64 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug|x64.Build.0 = Debug|x64 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug|x86.ActiveCfg = Debug|Win32 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Debug|x86.Build.0 = Debug|Win32 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release|ARM64.ActiveCfg = Release|ARM64 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release|ARM64.Build.0 = Release|ARM64 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release|x64.ActiveCfg = Release|x64 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release|x64.Build.0 = Release|x64 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release|x86.ActiveCfg = Release|Win32 + {15CDD310-6980-42A6-8082-3A6B7730D13F}.Release|x86.Build.0 = Release|Win32 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug|ARM64.Build.0 = Debug|ARM64 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug|x64.ActiveCfg = Debug|x64 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug|x64.Build.0 = Debug|x64 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug|x86.ActiveCfg = Debug|Win32 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Debug|x86.Build.0 = Debug|Win32 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release|ARM64.ActiveCfg = Release|ARM64 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release|ARM64.Build.0 = Release|ARM64 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release|x64.ActiveCfg = Release|x64 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release|x64.Build.0 = Release|x64 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release|x86.ActiveCfg = Release|Win32 + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F}.Release|x86.Build.0 = Release|Win32 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug|ARM64.Build.0 = Debug|ARM64 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug|x64.ActiveCfg = Debug|x64 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug|x64.Build.0 = Debug|x64 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug|x86.ActiveCfg = Debug|Win32 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Debug|x86.Build.0 = Debug|Win32 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release|ARM64.ActiveCfg = Release|ARM64 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release|ARM64.Build.0 = Release|ARM64 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release|x64.ActiveCfg = Release|x64 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release|x64.Build.0 = Release|x64 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release|x86.ActiveCfg = Release|Win32 + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7}.Release|x86.Build.0 = Release|Win32 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug|ARM64.Build.0 = Debug|ARM64 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug|x64.ActiveCfg = Debug|x64 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug|x64.Build.0 = Debug|x64 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug|x86.ActiveCfg = Debug|Win32 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Debug|x86.Build.0 = Debug|Win32 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release|ARM64.ActiveCfg = Release|ARM64 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release|ARM64.Build.0 = Release|ARM64 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release|x64.ActiveCfg = Release|x64 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release|x64.Build.0 = Release|x64 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release|x86.ActiveCfg = Release|Win32 + {88DE5AD6-0074-4A5A-BE22-C840153E35D5}.Release|x86.Build.0 = Release|Win32 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug|ARM64.Build.0 = Debug|ARM64 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug|x64.ActiveCfg = Debug|x64 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug|x64.Build.0 = Debug|x64 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug|x86.ActiveCfg = Debug|Win32 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Debug|x86.Build.0 = Debug|Win32 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release|ARM64.ActiveCfg = Release|ARM64 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release|ARM64.Build.0 = Release|ARM64 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release|x64.ActiveCfg = Release|x64 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release|x64.Build.0 = Release|x64 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release|x86.ActiveCfg = Release|Win32 + {A546E75A-5242-46E6-9A9E-6C91554EAB84}.Release|x86.Build.0 = Release|Win32 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug|ARM64.Build.0 = Debug|ARM64 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug|x64.ActiveCfg = Debug|x64 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug|x64.Build.0 = Debug|x64 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug|x86.ActiveCfg = Debug|Win32 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Debug|x86.Build.0 = Debug|Win32 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release|ARM64.ActiveCfg = Release|ARM64 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release|ARM64.Build.0 = Release|ARM64 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release|x64.ActiveCfg = Release|x64 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release|x64.Build.0 = Release|x64 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release|x86.ActiveCfg = Release|Win32 + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD}.Release|x86.Build.0 = Release|Win32 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug|ARM64.Build.0 = Debug|ARM64 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug|x64.ActiveCfg = Debug|x64 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug|x64.Build.0 = Debug|x64 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug|x86.ActiveCfg = Debug|Win32 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Debug|x86.Build.0 = Debug|Win32 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Release|ARM64.ActiveCfg = Release|ARM64 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Release|ARM64.Build.0 = Release|ARM64 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Release|x64.ActiveCfg = Release|x64 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Release|x64.Build.0 = Release|x64 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Release|x86.ActiveCfg = Release|Win32 + {DF25E545-00FF-4E64-844C-7DF98991F901}.Release|x86.Build.0 = Release|Win32 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug|ARM64.Build.0 = Debug|ARM64 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug|x64.ActiveCfg = Debug|x64 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug|x64.Build.0 = Debug|x64 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug|x86.ActiveCfg = Debug|Win32 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Debug|x86.Build.0 = Debug|Win32 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release|ARM64.ActiveCfg = Release|ARM64 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release|ARM64.Build.0 = Release|ARM64 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release|x64.ActiveCfg = Release|x64 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release|x64.Build.0 = Release|x64 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release|x86.ActiveCfg = Release|Win32 + {703BE7BA-5B99-4F70-806D-3A259F6A991E}.Release|x86.Build.0 = Release|Win32 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug|ARM64.Build.0 = Debug|ARM64 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug|x64.ActiveCfg = Debug|x64 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug|x64.Build.0 = Debug|x64 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug|x86.ActiveCfg = Debug|Win32 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Debug|x86.Build.0 = Debug|Win32 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release|ARM64.ActiveCfg = Release|ARM64 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release|ARM64.Build.0 = Release|ARM64 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release|x64.ActiveCfg = Release|x64 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release|x64.Build.0 = Release|x64 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release|x86.ActiveCfg = Release|Win32 + {FAFEE2F9-24B0-4AF1-B512-433E9590033F}.Release|x86.Build.0 = Release|Win32 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug|ARM64.Build.0 = Debug|ARM64 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug|x64.ActiveCfg = Debug|x64 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug|x64.Build.0 = Debug|x64 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug|x86.ActiveCfg = Debug|Win32 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Debug|x86.Build.0 = Debug|Win32 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release|ARM64.ActiveCfg = Release|ARM64 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release|ARM64.Build.0 = Release|ARM64 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release|x64.ActiveCfg = Release|x64 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release|x64.Build.0 = Release|x64 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release|x86.ActiveCfg = Release|Win32 + {8245DAD9-D402-4D5C-8F45-32229CD3B263}.Release|x86.Build.0 = Release|Win32 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug|ARM64.Build.0 = Debug|ARM64 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug|x64.ActiveCfg = Debug|x64 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug|x64.Build.0 = Debug|x64 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug|x86.ActiveCfg = Debug|Win32 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Debug|x86.Build.0 = Debug|Win32 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release|ARM64.ActiveCfg = Release|ARM64 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release|ARM64.Build.0 = Release|ARM64 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release|x64.ActiveCfg = Release|x64 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release|x64.Build.0 = Release|x64 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release|x86.ActiveCfg = Release|Win32 + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629}.Release|x86.Build.0 = Release|Win32 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug|ARM64.Build.0 = Debug|ARM64 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug|x64.ActiveCfg = Debug|x64 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug|x64.Build.0 = Debug|x64 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug|x86.ActiveCfg = Debug|Win32 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Debug|x86.Build.0 = Debug|Win32 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release|ARM64.ActiveCfg = Release|ARM64 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release|ARM64.Build.0 = Release|ARM64 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release|x64.ActiveCfg = Release|x64 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release|x64.Build.0 = Release|x64 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release|x86.ActiveCfg = Release|Win32 + {3A7FE53D-35F7-49DC-9C9A-A5204A53523F}.Release|x86.Build.0 = Release|Win32 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug|ARM64.Build.0 = Debug|ARM64 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug|x64.ActiveCfg = Debug|x64 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug|x64.Build.0 = Debug|x64 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug|x86.ActiveCfg = Debug|Win32 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Debug|x86.Build.0 = Debug|Win32 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release|ARM64.ActiveCfg = Release|ARM64 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release|ARM64.Build.0 = Release|ARM64 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release|x64.ActiveCfg = Release|x64 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release|x64.Build.0 = Release|x64 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release|x86.ActiveCfg = Release|Win32 + {CCA63A76-D9FC-4130-9F67-4D97F9770D53}.Release|x86.Build.0 = Release|Win32 + {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 + {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 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug|ARM64.Build.0 = Debug|ARM64 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug|x64.ActiveCfg = Debug|x64 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug|x64.Build.0 = Debug|x64 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug|x86.ActiveCfg = Debug|Win32 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Debug|x86.Build.0 = Debug|Win32 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release|ARM64.ActiveCfg = Release|ARM64 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release|ARM64.Build.0 = Release|ARM64 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release|x64.ActiveCfg = Release|x64 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release|x64.Build.0 = Release|x64 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release|x86.ActiveCfg = Release|Win32 + {0981CA28-E4A5-4DF1-987F-A41D09131EFC}.Release|x86.Build.0 = Release|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.Build.0 = Debug|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.ActiveCfg = Debug|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.Build.0 = Debug|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.ActiveCfg = Debug|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.Build.0 = Debug|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.ActiveCfg = Release|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.Build.0 = Release|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.ActiveCfg = Release|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.Build.0 = Release|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.ActiveCfg = Release|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.Build.0 = Release|Win32 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug|ARM64.Build.0 = Debug|ARM64 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug|x64.ActiveCfg = Debug|x64 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug|x64.Build.0 = Debug|x64 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug|x86.ActiveCfg = Debug|Win32 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Debug|x86.Build.0 = Debug|Win32 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release|ARM64.ActiveCfg = Release|ARM64 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release|ARM64.Build.0 = Release|ARM64 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release|x64.ActiveCfg = Release|x64 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release|x64.Build.0 = Release|x64 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release|x86.ActiveCfg = Release|Win32 + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release|x86.Build.0 = Release|Win32 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug|ARM64.Build.0 = Debug|ARM64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug|x64.ActiveCfg = Debug|x64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug|x64.Build.0 = Debug|x64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug|x86.ActiveCfg = Debug|Win32 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug|x86.Build.0 = Debug|Win32 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release|ARM64.ActiveCfg = Release|ARM64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release|ARM64.Build.0 = Release|ARM64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release|x64.ActiveCfg = Release|x64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release|x64.Build.0 = Release|x64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release|x86.ActiveCfg = Release|Win32 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release|x86.Build.0 = Release|Win32 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug|ARM64.Build.0 = Debug|ARM64 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug|x64.ActiveCfg = Debug|x64 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug|x64.Build.0 = Debug|x64 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug|x86.ActiveCfg = Debug|Win32 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Debug|x86.Build.0 = Debug|Win32 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release|ARM64.ActiveCfg = Release|ARM64 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release|ARM64.Build.0 = Release|ARM64 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release|x64.ActiveCfg = Release|x64 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release|x64.Build.0 = Release|x64 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release|x86.ActiveCfg = Release|Win32 + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4}.Release|x86.Build.0 = Release|Win32 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug|ARM64.Build.0 = Debug|ARM64 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug|x64.ActiveCfg = Debug|x64 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug|x64.Build.0 = Debug|x64 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug|x86.ActiveCfg = Debug|Win32 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Debug|x86.Build.0 = Debug|Win32 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Release|ARM64.ActiveCfg = Release|ARM64 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Release|ARM64.Build.0 = Release|ARM64 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Release|x64.ActiveCfg = Release|x64 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Release|x64.Build.0 = Release|x64 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Release|x86.ActiveCfg = Release|Win32 + {49C67F03-1A56-4F96-B278-39B66EC93678}.Release|x86.Build.0 = Release|Win32 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug|ARM64.Build.0 = Debug|ARM64 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug|x64.ActiveCfg = Debug|x64 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug|x64.Build.0 = Debug|x64 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug|x86.ActiveCfg = Debug|Win32 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Debug|x86.Build.0 = Debug|Win32 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release|ARM64.ActiveCfg = Release|ARM64 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release|ARM64.Build.0 = Release|ARM64 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release|x64.ActiveCfg = Release|x64 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release|x64.Build.0 = Release|x64 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release|x86.ActiveCfg = Release|Win32 + {D496308F-3C3C-40B3-A3ED-EA327D244B3E}.Release|x86.Build.0 = Release|Win32 + {3B27F358-2679-4F38-B297-17B536F580BB}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {3B27F358-2679-4F38-B297-17B536F580BB}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {3B27F358-2679-4F38-B297-17B536F580BB}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {3B27F358-2679-4F38-B297-17B536F580BB}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {3B27F358-2679-4F38-B297-17B536F580BB}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {3B27F358-2679-4F38-B297-17B536F580BB}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {3B27F358-2679-4F38-B297-17B536F580BB}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {3B27F358-2679-4F38-B297-17B536F580BB}.Debug|ARM64.Build.0 = Debug|ARM64 + {3B27F358-2679-4F38-B297-17B536F580BB}.Debug|x64.ActiveCfg = Debug|x64 + {3B27F358-2679-4F38-B297-17B536F580BB}.Debug|x64.Build.0 = Debug|x64 + {3B27F358-2679-4F38-B297-17B536F580BB}.Debug|x86.ActiveCfg = Debug|Win32 + {3B27F358-2679-4F38-B297-17B536F580BB}.Debug|x86.Build.0 = Debug|Win32 + {3B27F358-2679-4F38-B297-17B536F580BB}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {3B27F358-2679-4F38-B297-17B536F580BB}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {3B27F358-2679-4F38-B297-17B536F580BB}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {3B27F358-2679-4F38-B297-17B536F580BB}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {3B27F358-2679-4F38-B297-17B536F580BB}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {3B27F358-2679-4F38-B297-17B536F580BB}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {3B27F358-2679-4F38-B297-17B536F580BB}.Release|ARM64.ActiveCfg = Release|ARM64 + {3B27F358-2679-4F38-B297-17B536F580BB}.Release|ARM64.Build.0 = Release|ARM64 + {3B27F358-2679-4F38-B297-17B536F580BB}.Release|x64.ActiveCfg = Release|x64 + {3B27F358-2679-4F38-B297-17B536F580BB}.Release|x64.Build.0 = Release|x64 + {3B27F358-2679-4F38-B297-17B536F580BB}.Release|x86.ActiveCfg = Release|Win32 + {3B27F358-2679-4F38-B297-17B536F580BB}.Release|x86.Build.0 = Release|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|ARM64.Build.0 = Debug|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|x64.ActiveCfg = Debug|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|x64.Build.0 = Debug|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|x86.ActiveCfg = Debug|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|x86.Build.0 = Debug|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|ARM64.ActiveCfg = Release|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|ARM64.Build.0 = Release|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|x64.ActiveCfg = Release|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|x64.Build.0 = Release|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|x86.ActiveCfg = Release|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|x86.Build.0 = Release|Win32 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug|ARM64.Build.0 = Debug|ARM64 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug|x64.ActiveCfg = Debug|x64 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug|x64.Build.0 = Debug|x64 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug|x86.ActiveCfg = Debug|Win32 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Debug|x86.Build.0 = Debug|Win32 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release|ARM64.ActiveCfg = Release|ARM64 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release|ARM64.Build.0 = Release|ARM64 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release|x64.ActiveCfg = Release|x64 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release|x64.Build.0 = Release|x64 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release|x86.ActiveCfg = Release|Win32 + {19CA0070-B4B2-4394-90B7-D0C259AA35BA}.Release|x86.Build.0 = Release|Win32 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|ARM64.Build.0 = Debug|ARM64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|x64.ActiveCfg = Debug|x64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|x64.Build.0 = Debug|x64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|x86.ActiveCfg = Debug|Win32 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|x86.Build.0 = Debug|Win32 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|ARM64.ActiveCfg = Release|ARM64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|ARM64.Build.0 = Release|ARM64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|x64.ActiveCfg = Release|x64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|x64.Build.0 = Release|x64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|x86.ActiveCfg = Release|Win32 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|x86.Build.0 = Release|Win32 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug|ARM64.Build.0 = Debug|ARM64 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug|x64.ActiveCfg = Debug|x64 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug|x64.Build.0 = Debug|x64 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug|x86.ActiveCfg = Debug|Win32 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Debug|x86.Build.0 = Debug|Win32 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release|ARM64.ActiveCfg = Release|ARM64 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release|ARM64.Build.0 = Release|ARM64 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release|x64.ActiveCfg = Release|x64 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release|x64.Build.0 = Release|x64 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release|x86.ActiveCfg = Release|Win32 + {9DB1F875-6E65-4195-B23F-ED8095C0B99C}.Release|x86.Build.0 = Release|Win32 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug|ARM64.Build.0 = Debug|ARM64 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug|x64.ActiveCfg = Debug|x64 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug|x64.Build.0 = Debug|x64 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug|x86.ActiveCfg = Debug|Win32 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Debug|x86.Build.0 = Debug|Win32 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release|ARM64.ActiveCfg = Release|ARM64 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release|ARM64.Build.0 = Release|ARM64 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release|x64.ActiveCfg = Release|x64 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release|x64.Build.0 = Release|x64 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release|x86.ActiveCfg = Release|Win32 + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F}.Release|x86.Build.0 = Release|Win32 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug|ARM64.Build.0 = Debug|ARM64 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug|x64.ActiveCfg = Debug|x64 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug|x64.Build.0 = Debug|x64 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug|x86.ActiveCfg = Debug|Win32 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Debug|x86.Build.0 = Debug|Win32 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release|ARM64.ActiveCfg = Release|ARM64 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release|ARM64.Build.0 = Release|ARM64 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release|x64.ActiveCfg = Release|x64 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release|x64.Build.0 = Release|x64 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release|x86.ActiveCfg = Release|Win32 + {8E132D5A-2C00-48D0-8747-97E41356F26F}.Release|x86.Build.0 = Release|Win32 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug|ARM64.Build.0 = Debug|ARM64 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug|x64.ActiveCfg = Debug|x64 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug|x64.Build.0 = Debug|x64 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug|x86.ActiveCfg = Debug|Win32 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Debug|x86.Build.0 = Debug|Win32 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release|ARM64.ActiveCfg = Release|ARM64 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release|ARM64.Build.0 = Release|ARM64 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release|x64.ActiveCfg = Release|x64 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release|x64.Build.0 = Release|x64 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release|x86.ActiveCfg = Release|Win32 + {A4662163-83E7-4309-8CAA-B0BF13655FE6}.Release|x86.Build.0 = Release|Win32 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug|ARM64.Build.0 = Debug|ARM64 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug|x64.ActiveCfg = Debug|x64 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug|x64.Build.0 = Debug|x64 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug|x86.ActiveCfg = Debug|Win32 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Debug|x86.Build.0 = Debug|Win32 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release|ARM64.ActiveCfg = Release|ARM64 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release|ARM64.Build.0 = Release|ARM64 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release|x64.ActiveCfg = Release|x64 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release|x64.Build.0 = Release|x64 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release|x86.ActiveCfg = Release|Win32 + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20}.Release|x86.Build.0 = Release|Win32 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug|ARM64.Build.0 = Debug|ARM64 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug|x64.ActiveCfg = Debug|x64 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug|x64.Build.0 = Debug|x64 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug|x86.ActiveCfg = Debug|Win32 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Debug|x86.Build.0 = Debug|Win32 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release|ARM64.ActiveCfg = Release|ARM64 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release|ARM64.Build.0 = Release|ARM64 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release|x64.ActiveCfg = Release|x64 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release|x64.Build.0 = Release|x64 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release|x86.ActiveCfg = Release|Win32 + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release|x86.Build.0 = Release|Win32 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug|ARM64.Build.0 = Debug|ARM64 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug|x64.ActiveCfg = Debug|x64 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug|x64.Build.0 = Debug|x64 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug|x86.ActiveCfg = Debug|Win32 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug|x86.Build.0 = Debug|Win32 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release|ARM64.ActiveCfg = Release|ARM64 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release|ARM64.Build.0 = Release|ARM64 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release|x64.ActiveCfg = Release|x64 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release|x64.Build.0 = Release|x64 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release|x86.ActiveCfg = Release|Win32 + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release|x86.Build.0 = Release|Win32 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug|ARM64.Build.0 = Debug|ARM64 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug|x64.ActiveCfg = Debug|x64 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug|x64.Build.0 = Debug|x64 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug|x86.ActiveCfg = Debug|Win32 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug|x86.Build.0 = Debug|Win32 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release|ARM64.ActiveCfg = Release|ARM64 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release|ARM64.Build.0 = Release|ARM64 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release|x64.ActiveCfg = Release|x64 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release|x64.Build.0 = Release|x64 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release|x86.ActiveCfg = Release|Win32 + {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Release|x86.Build.0 = Release|Win32 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug|ARM64.Build.0 = Debug|ARM64 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug|x64.ActiveCfg = Debug|x64 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug|x64.Build.0 = Debug|x64 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug|x86.ActiveCfg = Debug|Win32 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Debug|x86.Build.0 = Debug|Win32 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release|ARM64.ActiveCfg = Release|ARM64 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release|ARM64.Build.0 = Release|ARM64 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release|x64.ActiveCfg = Release|x64 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release|x64.Build.0 = Release|x64 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release|x86.ActiveCfg = Release|Win32 + {DFB40A10-F8B7-412A-BCC3-5EE49294D816}.Release|x86.Build.0 = Release|Win32 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug|ARM64.Build.0 = Debug|ARM64 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug|x64.ActiveCfg = Debug|x64 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug|x64.Build.0 = Debug|x64 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug|x86.ActiveCfg = Debug|Win32 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Debug|x86.Build.0 = Debug|Win32 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release|ARM64.ActiveCfg = Release|ARM64 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release|ARM64.Build.0 = Release|ARM64 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release|x64.ActiveCfg = Release|x64 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release|x64.Build.0 = Release|x64 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release|x86.ActiveCfg = Release|Win32 + {BB58A5FB-1A35-4471-86D0-A5189EC541B3}.Release|x86.Build.0 = Release|Win32 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug|ARM64.Build.0 = Debug|ARM64 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug|x64.ActiveCfg = Debug|x64 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug|x64.Build.0 = Debug|x64 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug|x86.ActiveCfg = Debug|Win32 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Debug|x86.Build.0 = Debug|Win32 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release|ARM64.ActiveCfg = Release|ARM64 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release|ARM64.Build.0 = Release|ARM64 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release|x64.ActiveCfg = Release|x64 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release|x64.Build.0 = Release|x64 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release|x86.ActiveCfg = Release|Win32 + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A}.Release|x86.Build.0 = Release|Win32 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug|ARM64.Build.0 = Debug|ARM64 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug|x64.ActiveCfg = Debug|x64 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug|x64.Build.0 = Debug|x64 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug|x86.ActiveCfg = Debug|Win32 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Debug|x86.Build.0 = Debug|Win32 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Release|ARM64.ActiveCfg = Release|ARM64 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Release|ARM64.Build.0 = Release|ARM64 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Release|x64.ActiveCfg = Release|x64 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Release|x64.Build.0 = Release|x64 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Release|x86.ActiveCfg = Release|Win32 + {7467E9AE-844F-444D-8A3F-17397544BA21}.Release|x86.Build.0 = Release|Win32 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug|ARM64.Build.0 = Debug|ARM64 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug|x64.ActiveCfg = Debug|x64 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug|x64.Build.0 = Debug|x64 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug|x86.ActiveCfg = Debug|Win32 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Debug|x86.Build.0 = Debug|Win32 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Release|ARM64.ActiveCfg = Release|ARM64 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Release|ARM64.Build.0 = Release|ARM64 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Release|x64.ActiveCfg = Release|x64 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Release|x64.Build.0 = Release|x64 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Release|x86.ActiveCfg = Release|Win32 + {497FDF54-9762-4048-A833-61CC3980A0FB}.Release|x86.Build.0 = Release|Win32 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug|ARM64.Build.0 = Debug|ARM64 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug|x64.ActiveCfg = Debug|x64 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug|x64.Build.0 = Debug|x64 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug|x86.ActiveCfg = Debug|Win32 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Debug|x86.Build.0 = Debug|Win32 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release|ARM64.ActiveCfg = Release|ARM64 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release|ARM64.Build.0 = Release|ARM64 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release|x64.ActiveCfg = Release|x64 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release|x64.Build.0 = Release|x64 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release|x86.ActiveCfg = Release|Win32 + {29B00F47-BE91-4A1F-B87D-B1302F038316}.Release|x86.Build.0 = Release|Win32 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug|ARM64.Build.0 = Debug|ARM64 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug|x64.ActiveCfg = Debug|x64 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug|x64.Build.0 = Debug|x64 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug|x86.ActiveCfg = Debug|Win32 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Debug|x86.Build.0 = Debug|Win32 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release|ARM64.ActiveCfg = Release|ARM64 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release|ARM64.Build.0 = Release|ARM64 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release|x64.ActiveCfg = Release|x64 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release|x64.Build.0 = Release|x64 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release|x86.ActiveCfg = Release|Win32 + {124935CC-73BB-489E-92E8-4F922A85DB5D}.Release|x86.Build.0 = Release|Win32 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug|ARM64.Build.0 = Debug|ARM64 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug|x64.ActiveCfg = Debug|x64 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug|x64.Build.0 = Debug|x64 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug|x86.ActiveCfg = Debug|Win32 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Debug|x86.Build.0 = Debug|Win32 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release|ARM64.ActiveCfg = Release|ARM64 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release|ARM64.Build.0 = Release|ARM64 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release|x64.ActiveCfg = Release|x64 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release|x64.Build.0 = Release|x64 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release|x86.ActiveCfg = Release|Win32 + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F}.Release|x86.Build.0 = Release|Win32 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug|ARM64.Build.0 = Debug|ARM64 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug|x64.ActiveCfg = Debug|x64 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug|x64.Build.0 = Debug|x64 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug|x86.ActiveCfg = Debug|Win32 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Debug|x86.Build.0 = Debug|Win32 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release|ARM64.ActiveCfg = Release|ARM64 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release|ARM64.Build.0 = Release|ARM64 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release|x64.ActiveCfg = Release|x64 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release|x64.Build.0 = Release|x64 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release|x86.ActiveCfg = Release|Win32 + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2}.Release|x86.Build.0 = Release|Win32 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug|ARM64.Build.0 = Debug|ARM64 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug|x64.ActiveCfg = Debug|x64 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug|x64.Build.0 = Debug|x64 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug|x86.ActiveCfg = Debug|Win32 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Debug|x86.Build.0 = Debug|Win32 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release|ARM64.ActiveCfg = Release|ARM64 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release|ARM64.Build.0 = Release|ARM64 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release|x64.ActiveCfg = Release|x64 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release|x64.Build.0 = Release|x64 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release|x86.ActiveCfg = Release|Win32 + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91}.Release|x86.Build.0 = Release|Win32 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug|ARM64.Build.0 = Debug|ARM64 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug|x64.ActiveCfg = Debug|x64 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug|x64.Build.0 = Debug|x64 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug|x86.ActiveCfg = Debug|Win32 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Debug|x86.Build.0 = Debug|Win32 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release|ARM64.ActiveCfg = Release|ARM64 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release|ARM64.Build.0 = Release|ARM64 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release|x64.ActiveCfg = Release|x64 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release|x64.Build.0 = Release|x64 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release|x86.ActiveCfg = Release|Win32 + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F}.Release|x86.Build.0 = Release|Win32 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug|ARM64.Build.0 = Debug|ARM64 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug|x64.ActiveCfg = Debug|x64 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug|x64.Build.0 = Debug|x64 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug|x86.ActiveCfg = Debug|Win32 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Debug|x86.Build.0 = Debug|Win32 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release|ARM64.ActiveCfg = Release|ARM64 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release|ARM64.Build.0 = Release|ARM64 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release|x64.ActiveCfg = Release|x64 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release|x64.Build.0 = Release|x64 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release|x86.ActiveCfg = Release|Win32 + {9DE2FC01-A839-4F89-8319-9071D4C54821}.Release|x86.Build.0 = Release|Win32 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug|ARM64.Build.0 = Debug|ARM64 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug|x64.ActiveCfg = Debug|x64 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug|x64.Build.0 = Debug|x64 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug|x86.ActiveCfg = Debug|Win32 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Debug|x86.Build.0 = Debug|Win32 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release|ARM64.ActiveCfg = Release|ARM64 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release|ARM64.Build.0 = Release|ARM64 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release|x64.ActiveCfg = Release|x64 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release|x64.Build.0 = Release|x64 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release|x86.ActiveCfg = Release|Win32 + {2F578155-D51F-4C03-AB7F-5C5122CA46CC}.Release|x86.Build.0 = Release|Win32 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug|ARM64.Build.0 = Debug|ARM64 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug|x64.ActiveCfg = Debug|x64 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug|x64.Build.0 = Debug|x64 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug|x86.ActiveCfg = Debug|Win32 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Debug|x86.Build.0 = Debug|Win32 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release|ARM64.ActiveCfg = Release|ARM64 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release|ARM64.Build.0 = Release|ARM64 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release|x64.ActiveCfg = Release|x64 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release|x64.Build.0 = Release|x64 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release|x86.ActiveCfg = Release|Win32 + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6}.Release|x86.Build.0 = Release|Win32 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug|ARM64.Build.0 = Debug|ARM64 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug|x64.ActiveCfg = Debug|x64 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug|x64.Build.0 = Debug|x64 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug|x86.ActiveCfg = Debug|Win32 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Debug|x86.Build.0 = Debug|Win32 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release|ARM64.ActiveCfg = Release|ARM64 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release|ARM64.Build.0 = Release|ARM64 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release|x64.ActiveCfg = Release|x64 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release|x64.Build.0 = Release|x64 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release|x86.ActiveCfg = Release|Win32 + {84DE22BB-C25F-425C-A7FE-0120CF107B83}.Release|x86.Build.0 = Release|Win32 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug|ARM64.Build.0 = Debug|ARM64 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug|x64.ActiveCfg = Debug|x64 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug|x64.Build.0 = Debug|x64 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug|x86.ActiveCfg = Debug|Win32 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Debug|x86.Build.0 = Debug|Win32 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release|ARM64.ActiveCfg = Release|ARM64 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release|ARM64.Build.0 = Release|ARM64 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release|x64.ActiveCfg = Release|x64 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release|x64.Build.0 = Release|x64 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release|x86.ActiveCfg = Release|Win32 + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65}.Release|x86.Build.0 = Release|Win32 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug|ARM64.Build.0 = Debug|ARM64 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug|x64.ActiveCfg = Debug|x64 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug|x64.Build.0 = Debug|x64 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug|x86.ActiveCfg = Debug|Win32 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Debug|x86.Build.0 = Debug|Win32 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release|ARM64.ActiveCfg = Release|ARM64 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release|ARM64.Build.0 = Release|ARM64 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release|x64.ActiveCfg = Release|x64 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release|x64.Build.0 = Release|x64 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release|x86.ActiveCfg = Release|Win32 + {B7FDD40F-DDA4-468E-9C40-EEB175964A26}.Release|x86.Build.0 = Release|Win32 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug|ARM64.Build.0 = Debug|ARM64 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug|x64.ActiveCfg = Debug|x64 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug|x64.Build.0 = Debug|x64 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug|x86.ActiveCfg = Debug|Win32 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Debug|x86.Build.0 = Debug|Win32 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release|ARM64.ActiveCfg = Release|ARM64 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release|ARM64.Build.0 = Release|ARM64 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release|x64.ActiveCfg = Release|x64 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release|x64.Build.0 = Release|x64 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release|x86.ActiveCfg = Release|Win32 + {028F0967-B253-45DA-B1C4-FACCE45D0D8D}.Release|x86.Build.0 = Release|Win32 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug|ARM64.Build.0 = Debug|ARM64 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug|x64.ActiveCfg = Debug|x64 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug|x64.Build.0 = Debug|x64 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug|x86.ActiveCfg = Debug|Win32 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Debug|x86.Build.0 = Debug|Win32 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release|ARM64.ActiveCfg = Release|ARM64 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release|ARM64.Build.0 = Release|ARM64 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release|x64.ActiveCfg = Release|x64 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release|x64.Build.0 = Release|x64 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release|x86.ActiveCfg = Release|Win32 + {666346D7-C84B-498D-AE17-53B20C62DB1A}.Release|x86.Build.0 = Release|Win32 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug|ARM64.Build.0 = Debug|ARM64 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug|x64.ActiveCfg = Debug|x64 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug|x64.Build.0 = Debug|x64 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug|x86.ActiveCfg = Debug|Win32 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug|x86.Build.0 = Debug|Win32 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release|ARM64.ActiveCfg = Release|ARM64 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release|ARM64.Build.0 = Release|ARM64 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release|x64.ActiveCfg = Release|x64 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release|x64.Build.0 = Release|x64 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release|x86.ActiveCfg = Release|Win32 + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release|x86.Build.0 = Release|Win32 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug|ARM64.Build.0 = Debug|ARM64 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug|x64.ActiveCfg = Debug|x64 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug|x64.Build.0 = Debug|x64 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug|x86.ActiveCfg = Debug|Win32 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug|x86.Build.0 = Debug|Win32 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Release|ARM64.ActiveCfg = Release|ARM64 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Release|ARM64.Build.0 = Release|ARM64 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Release|x64.ActiveCfg = Release|x64 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Release|x64.Build.0 = Release|x64 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Release|x86.ActiveCfg = Release|Win32 + {6C897101-BE52-4387-8AA2-062123A76BA1}.Release|x86.Build.0 = Release|Win32 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug|ARM64.Build.0 = Debug|ARM64 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug|x64.ActiveCfg = Debug|x64 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug|x64.Build.0 = Debug|x64 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug|x86.ActiveCfg = Debug|Win32 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug|x86.Build.0 = Debug|Win32 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release|ARM64.ActiveCfg = Release|ARM64 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release|ARM64.Build.0 = Release|ARM64 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release|x64.ActiveCfg = Release|x64 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release|x64.Build.0 = Release|x64 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release|x86.ActiveCfg = Release|Win32 + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release|x86.Build.0 = Release|Win32 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug|ARM64.Build.0 = Debug|ARM64 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug|x64.ActiveCfg = Debug|x64 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug|x64.Build.0 = Debug|x64 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug|x86.ActiveCfg = Debug|Win32 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug|x86.Build.0 = Debug|Win32 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release|ARM64.ActiveCfg = Release|ARM64 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release|ARM64.Build.0 = Release|ARM64 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release|x64.ActiveCfg = Release|x64 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release|x64.Build.0 = Release|x64 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release|x86.ActiveCfg = Release|Win32 + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release|x86.Build.0 = Release|Win32 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug|ARM64.Build.0 = Debug|ARM64 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug|x64.ActiveCfg = Debug|x64 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug|x64.Build.0 = Debug|x64 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug|x86.ActiveCfg = Debug|Win32 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug|x86.Build.0 = Debug|Win32 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release|ARM64.ActiveCfg = Release|ARM64 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release|ARM64.Build.0 = Release|ARM64 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release|x64.ActiveCfg = Release|x64 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release|x64.Build.0 = Release|x64 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release|x86.ActiveCfg = Release|Win32 + {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release|x86.Build.0 = Release|Win32 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug|ARM64.Build.0 = Debug|ARM64 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug|x64.ActiveCfg = Debug|x64 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug|x64.Build.0 = Debug|x64 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug|x86.ActiveCfg = Debug|Win32 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug|x86.Build.0 = Debug|Win32 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release|ARM64.ActiveCfg = Release|ARM64 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release|ARM64.Build.0 = Release|ARM64 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release|x64.ActiveCfg = Release|x64 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release|x64.Build.0 = Release|x64 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release|x86.ActiveCfg = Release|Win32 + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release|x86.Build.0 = Release|Win32 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug|ARM64.Build.0 = Debug|ARM64 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug|x64.ActiveCfg = Debug|x64 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug|x64.Build.0 = Debug|x64 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug|x86.ActiveCfg = Debug|Win32 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug|x86.Build.0 = Debug|Win32 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release|ARM64.ActiveCfg = Release|ARM64 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release|ARM64.Build.0 = Release|ARM64 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release|x64.ActiveCfg = Release|x64 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release|x64.Build.0 = Release|x64 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release|x86.ActiveCfg = Release|Win32 + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release|x86.Build.0 = Release|Win32 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug|ARM64.Build.0 = Debug|ARM64 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug|x64.ActiveCfg = Debug|x64 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug|x64.Build.0 = Debug|x64 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug|x86.ActiveCfg = Debug|Win32 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug|x86.Build.0 = Debug|Win32 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release|ARM64.ActiveCfg = Release|ARM64 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release|ARM64.Build.0 = Release|ARM64 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release|x64.ActiveCfg = Release|x64 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release|x64.Build.0 = Release|x64 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release|x86.ActiveCfg = Release|Win32 + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release|x86.Build.0 = Release|Win32 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug|ARM64.Build.0 = Debug|ARM64 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug|x64.ActiveCfg = Debug|x64 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug|x64.Build.0 = Debug|x64 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug|x86.ActiveCfg = Debug|Win32 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug|x86.Build.0 = Debug|Win32 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release|ARM64.ActiveCfg = Release|ARM64 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release|ARM64.Build.0 = Release|ARM64 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release|x64.ActiveCfg = Release|x64 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release|x64.Build.0 = Release|x64 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release|x86.ActiveCfg = Release|Win32 + {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release|x86.Build.0 = Release|Win32 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug|ARM64.Build.0 = Debug|ARM64 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug|x64.ActiveCfg = Debug|x64 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug|x64.Build.0 = Debug|x64 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug|x86.ActiveCfg = Debug|Win32 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug|x86.Build.0 = Debug|Win32 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Release|ARM64.ActiveCfg = Release|ARM64 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Release|ARM64.Build.0 = Release|ARM64 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Release|x64.ActiveCfg = Release|x64 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Release|x64.Build.0 = Release|x64 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Release|x86.ActiveCfg = Release|Win32 + {071E64F3-1396-4A97-97CA-98CAC059B168}.Release|x86.Build.0 = Release|Win32 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug|ARM64.Build.0 = Debug|ARM64 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug|x64.ActiveCfg = Debug|x64 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug|x64.Build.0 = Debug|x64 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug|x86.ActiveCfg = Debug|Win32 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug|x86.Build.0 = Debug|Win32 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release|ARM64.ActiveCfg = Release|ARM64 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release|ARM64.Build.0 = Release|ARM64 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release|x64.ActiveCfg = Release|x64 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release|x64.Build.0 = Release|x64 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release|x86.ActiveCfg = Release|Win32 + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release|x86.Build.0 = Release|Win32 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug|ARM64.Build.0 = Debug|ARM64 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug|x64.ActiveCfg = Debug|x64 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug|x64.Build.0 = Debug|x64 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug|x86.ActiveCfg = Debug|Win32 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug|x86.Build.0 = Debug|Win32 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Release|ARM64.ActiveCfg = Release|ARM64 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Release|ARM64.Build.0 = Release|ARM64 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Release|x64.ActiveCfg = Release|x64 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Release|x64.Build.0 = Release|x64 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Release|x86.ActiveCfg = Release|Win32 + {1F4722E7-F78E-413F-A106-D3490211EA57}.Release|x86.Build.0 = Release|Win32 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug|ARM64.Build.0 = Debug|ARM64 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug|x64.ActiveCfg = Debug|x64 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug|x64.Build.0 = Debug|x64 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug|x86.ActiveCfg = Debug|Win32 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug|x86.Build.0 = Debug|Win32 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release|ARM64.ActiveCfg = Release|ARM64 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release|ARM64.Build.0 = Release|ARM64 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release|x64.ActiveCfg = Release|x64 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release|x64.Build.0 = Release|x64 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release|x86.ActiveCfg = Release|Win32 + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release|x86.Build.0 = Release|Win32 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug|ARM64.Build.0 = Debug|ARM64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug|x64.ActiveCfg = Debug|x64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug|x64.Build.0 = Debug|x64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug|x86.ActiveCfg = Debug|Win32 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug|x86.Build.0 = Debug|Win32 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|ARM64.ActiveCfg = Release|ARM64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|ARM64.Build.0 = Release|ARM64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|x64.ActiveCfg = Release|x64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|x64.Build.0 = Release|x64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|x86.ActiveCfg = Release|Win32 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|x86.Build.0 = Release|Win32 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug|ARM64.Build.0 = Debug|ARM64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug|x64.ActiveCfg = Debug|x64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug|x64.Build.0 = Debug|x64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug|x86.ActiveCfg = Debug|Win32 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug|x86.Build.0 = Debug|Win32 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release|ARM64.ActiveCfg = Release|ARM64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release|ARM64.Build.0 = Release|ARM64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release|x64.ActiveCfg = Release|x64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release|x64.Build.0 = Release|x64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release|x86.ActiveCfg = Release|Win32 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release|x86.Build.0 = Release|Win32 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug|ARM64.Build.0 = Debug|ARM64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug|x64.ActiveCfg = Debug|x64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug|x64.Build.0 = Debug|x64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug|x86.ActiveCfg = Debug|Win32 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug|x86.Build.0 = Debug|Win32 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|ARM64.ActiveCfg = Release|ARM64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|ARM64.Build.0 = Release|ARM64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|x64.ActiveCfg = Release|x64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|x64.Build.0 = Release|x64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|x86.ActiveCfg = Release|Win32 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + 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} + {0C2D2F82-AE67-400C-B19C-8C9B957B132A} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {E6784F91-4E4E-4956-A079-73FAB1AC7BE6} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} + {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} + {93A1F656-0D29-4C5E-B140-11F23FF5D6AB} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} + {F81C5819-85B6-4D2E-B6DC-104A7634461B} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} + {66CC5B13-881A-412F-8C51-746622A91C5A} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {CB75B7C9-4E00-43B8-B2A9-9ACB4FC40F9B} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {557138B0-7BE2-4392-B2E2-B45734031A62} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {9EED87BB-527F-4D05-9384-6D16CFD627A8} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {6D1CA2F1-7FCA-4249-9220-075C2DF4F965} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {946A1700-C7AA-46F0-AEF2-67C98B5722AC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {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} + {A2BA5E5C-FDB9-4939-B0B5-2B753A5E33D3} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {A643BB06-735D-47F3-BFE7-B6D3C36F7097} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {6B8BAAF1-75C7-4C68-80B8-0E2A9EABBD9A} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {B332DCA8-3599-4A99-917A-82261BDC27AC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {59089B0C-AAB4-4532-B294-44DEAE7178B7} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {C298876B-6C12-4EA4-903B-33450BCD9884} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {83F586FA-C801-4979-ACCA-006BD628CC88} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {86CBE96B-F5FE-483C-BA4A-DC9B1D43AF22} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {FF2970AE-E2E9-405F-B321-D523A1BD44A0} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {79417CE2-FEEB-42F0-BC53-62D5267B19B1} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {AFDDE100-2D36-4749-817D-12E54C56312F} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {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} + {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} + {F3412853-2B6A-4334-8CF2-B796CDAE0850} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {BE097E8F-B6F3-45DC-8A27-E0EBC31AB912} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {D03F2C82-9553-4AFA-8F49-9234009122B6} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {FE232CA5-6C0D-4ADF-9A21-775D4DC048D3} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {A53CCF42-A972-478F-9336-0F618B3EC06A} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {0037A3CD-4F50-48B2-9AC3-5A0D1D16D2CA} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {870723DD-945A-4136-B65B-4AF3BF85369C} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {EA6488AD-445B-4835-87FB-EBC9E2EDAF97} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {E07B6DBE-3358-4BA0-AABF-CDD8F96AECF0} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {472BCBDC-62E0-441D-B2FD-0EE0FC6CEEB4} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {589C8E9B-0BB3-4D6D-A70C-0A28E469F20E} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {3AD868E6-8355-4F29-B5ED-7DE94AD786E7} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {2B78CF0A-5403-45E2-99BD-493F1679BCDB} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {0AB968E0-E993-45CE-8875-7453C96DF583} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {25923141-9859-4AFE-8168-0DF78322FC63} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {7E855020-7FA4-482D-B510-2E709354FE8B} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {9782E0C8-2BD3-4F67-B420-21CF19CA2435} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {9F4135E3-9814-452C-9B35-0EFBCD792B49} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {C45343E6-DAB6-4F3A-A00A-8BED71A098BE} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {B19DD336-538E-4091-A559-EAA717FEC899} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {0BF60202-43F7-48E9-8717-D31E56FA5BE0} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {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} + {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} + {56FB0A45-145F-4EAE-B2C8-E5833E682D8F} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} + {2BB0C1D4-9298-45AC-B244-67A99769A292} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} + {99A40FC5-9DB0-4B80-8D97-867EF00FA2CB} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} + {81064BCE-EEC1-43B0-9912-F05F2B54B11A} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} + {31B41997-3890-45E3-93FE-C57B363E9C0D} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} + {D550AB93-DF31-4B76-873F-F075018352F4} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} + {8CF3F7BA-4C99-43EB-B4F1-7CA346817D0A} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} + {F90FCDC5-EE14-4B89-96DB-4392E28F34AF} = {278D8859-20B1-428F-8448-064F46E1F021} + {93A864C9-93B7-4E5C-ACE7-E8FC5F9EFF79} = {278D8859-20B1-428F-8448-064F46E1F021} + {56E68E37-B3FC-4799-91AF-0CA10B6D55A5} = {278D8859-20B1-428F-8448-064F46E1F021} + {03E7018C-44A2-4C46-9CE7-F2A135A2692B} = {278D8859-20B1-428F-8448-064F46E1F021} + {F3F6FE4D-9D9E-451A-B0BA-81456104B672} = {278D8859-20B1-428F-8448-064F46E1F021} + {C27794B5-1293-4EA7-BC0E-0F18E6325539} = {278D8859-20B1-428F-8448-064F46E1F021} + {02F41059-12A2-4A96-8D77-07EFE4B108FD} = {278D8859-20B1-428F-8448-064F46E1F021} + {B774E0B9-9514-4E88-975F-4EB6C3B8D519} = {278D8859-20B1-428F-8448-064F46E1F021} + {D91367C2-2189-4859-A7FE-D2CAB84FA15C} = {278D8859-20B1-428F-8448-064F46E1F021} + {33459B4E-1839-4856-BF6B-22480D11FE31} = {278D8859-20B1-428F-8448-064F46E1F021} + {48871156-181A-475A-BD8D-200086A09675} = {278D8859-20B1-428F-8448-064F46E1F021} + {C4416DA1-9E62-46BA-9CD3-F8963C79E1A1} = {278D8859-20B1-428F-8448-064F46E1F021} + {1C49E35A-2838-49D9-9D5F-4B8134960EF6} = {278D8859-20B1-428F-8448-064F46E1F021} + {F91142E2-A999-47F0-9E74-38C1E2930EBE} = {278D8859-20B1-428F-8448-064F46E1F021} + {1EDD4BCF-345C-4065-8CBD-7285224293C3} = {278D8859-20B1-428F-8448-064F46E1F021} + {A6B2A11B-0669-4AF5-A025-8DD02DBBE5EA} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {B176BB4A-CA31-4E2A-B790-3EA0ED2EE870} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {D08AA2A0-2F94-4BF5-B42D-E92450F03FD1} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {4A7D0ECA-D7CC-4E66-B741-C92E9C1B42FF} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {CF3755C4-937D-4ABF-B7B3-95140808717F} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {D34939FE-8873-4C53-8D6C-74DED78EA3C4} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {D408A730-363A-4ABF-BCEF-5D63DCC66042} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {F532AFBC-9E62-4A89-BB99-1044E4B2D8ED} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {52FB7463-C128-42AF-A02F-78F48473EA9A} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {7381D91E-5C72-48F0-AAB4-95C9B10D7484} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {D36EC43E-B31F-4CF4-8285-93A7A9D90189} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {274C0319-7E1E-4188-936B-8DF3331230B3} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {41BBCC10-CFDE-48A1-B2E0-A0EC6A668629} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {600C3D4F-0670-4DB4-B30F-520A729053B5} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {11F33A39-74B7-4018-B5F9-CC285A673A8F} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {A6F5E35E-B4A7-41B3-853A-75558E6E0715} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {291B4975-8EFF-4C7C-8AF3-44A77B8491B8} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {FDE6080B-E203-4066-910D-AD0302566008} = {E9D708A5-9C1F-4B84-A795-C5F191801762} + {E1B6D565-9D7C-46B7-9202-ECF54974DE50} = {E9D708A5-9C1F-4B84-A795-C5F191801762} + {C8765523-58F8-4C8E-9914-693396F6F0FF} = {E9D708A5-9C1F-4B84-A795-C5F191801762} + {2F1B955B-275E-4D8E-8864-06FEC44D7912} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {F2DB2E59-76BF-4D81-859A-AFC289C046C0} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} + {3FE7E9B6-49AC-4246-A789-28DB4644567B} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {EBBBF4A0-2DA2-4DE6-B4FE-C6654A2417A0} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {191A5289-BA65-4638-A215-C521F0187313} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {8B1AF423-00F1-4924-AC54-F77D402D2AC9} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {658A1B85-554E-4A5D-973A-FFE592CDD5F2} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {07CA51AD-72AE-46A2-AAED-DC3E3F807976} = {E9D708A5-9C1F-4B84-A795-C5F191801762} + {27B110CC-43C0-400A-89D9-245E681647D7} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} + {1DE84812-E143-4C4B-A61D-9267AAD55401} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {4A87569C-4BD3-4113-B4B9-573D65B3D3F8} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} + {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} + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {A4B0D971-3CD6-41C9-8AB2-055D25A33373} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} + {15CDD310-6980-42A6-8082-3A6B7730D13F} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {71DB4284-5B1C-4E86-9AF5-B91542D44A6F} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {4B39E5FC-0A96-4057-9AA5-8D5A52880DA7} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {88DE5AD6-0074-4A5A-BE22-C840153E35D5} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {A546E75A-5242-46E6-9A9E-6C91554EAB84} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {EFA150D4-F93B-4D7D-A69C-9E8B4663BECD} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {DF25E545-00FF-4E64-844C-7DF98991F901} = {278D8859-20B1-428F-8448-064F46E1F021} + {703BE7BA-5B99-4F70-806D-3A259F6A991E} = {278D8859-20B1-428F-8448-064F46E1F021} + {FAFEE2F9-24B0-4AF1-B512-433E9590033F} = {278D8859-20B1-428F-8448-064F46E1F021} + {8245DAD9-D402-4D5C-8F45-32229CD3B263} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {41BBCC10-6FDE-48A1-B2E0-A0EC6A668629} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {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} + {0981CA28-E4A5-4DF1-987F-A41D09131EFC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} + {A61DAD9C-271C-4E95-81AA-DB4CD58564D4} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {49C67F03-1A56-4F96-B278-39B66EC93678} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {D496308F-3C3C-40B3-A3ED-EA327D244B3E} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} + {3B27F358-2679-4F38-B297-17B536F580BB} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {19CA0070-B4B2-4394-90B7-D0C259AA35BA} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {9DB1F875-6E65-4195-B23F-ED8095C0B99C} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} + {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {8E132D5A-2C00-48D0-8747-97E41356F26F} = {278D8859-20B1-428F-8448-064F46E1F021} + {A4662163-83E7-4309-8CAA-B0BF13655FE6} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20} = {278D8859-20B1-428F-8448-064F46E1F021} + {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC} = {E9D708A5-9C1F-4B84-A795-C5F191801762} + {0C442799-B09C-4CD1-9538-711B6E85E9BF} = {278D8859-20B1-428F-8448-064F46E1F021} + {DFB40A10-F8B7-412A-BCC3-5EE49294D816} = {278D8859-20B1-428F-8448-064F46E1F021} + {BB58A5FB-1A35-4471-86D0-A5189EC541B3} = {278D8859-20B1-428F-8448-064F46E1F021} + {61997220-5383-4AE5-ABD4-5F45AE1B0F2A} = {278D8859-20B1-428F-8448-064F46E1F021} + {7467E9AE-844F-444D-8A3F-17397544BA21} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {497FDF54-9762-4048-A833-61CC3980A0FB} = {278D8859-20B1-428F-8448-064F46E1F021} + {29B00F47-BE91-4A1F-B87D-B1302F038316} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} + {124935CC-73BB-489E-92E8-4F922A85DB5D} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {AC215730-2B5F-4498-B7F5-5DB80AEFCA5F} = {278D8859-20B1-428F-8448-064F46E1F021} + {0835E6BF-0170-4E99-A55C-E06E1EF4C3B2} = {278D8859-20B1-428F-8448-064F46E1F021} + {EA4AD5A7-DB95-43C0-9A67-2D94146BCF91} = {278D8859-20B1-428F-8448-064F46E1F021} + {1ACC8236-EF4E-44B0-BD0C-AB1D95D5890F} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {9DE2FC01-A839-4F89-8319-9071D4C54821} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {2F578155-D51F-4C03-AB7F-5C5122CA46CC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {1C829D1A-892C-451C-AF0B-AC65C85F5CC6} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {84DE22BB-C25F-425C-A7FE-0120CF107B83} = {278D8859-20B1-428F-8448-064F46E1F021} + {98152EDD-7E28-4FA3-89D8-B636ED5D5F65} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {B7FDD40F-DDA4-468E-9C40-EEB175964A26} = {278D8859-20B1-428F-8448-064F46E1F021} + {028F0967-B253-45DA-B1C4-FACCE45D0D8D} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {666346D7-C84B-498D-AE17-53B20C62DB1A} = {278D8859-20B1-428F-8448-064F46E1F021} + {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {6C897101-BE52-4387-8AA2-062123A76BA1} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} + {30011884-25EE-42C9-BB15-888CAFB1AA6E} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {32FE2658-1D70-442E-8672-0AC5C6F0BD7B} = {278D8859-20B1-428F-8448-064F46E1F021} + {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F} = {278D8859-20B1-428F-8448-064F46E1F021} + {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {0653AFAF-5578-4C02-AF29-0C873E7634AE} = {278D8859-20B1-428F-8448-064F46E1F021} + {071E64F3-1396-4A97-97CA-98CAC059B168} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC} = {278D8859-20B1-428F-8448-064F46E1F021} + {1F4722E7-F78E-413F-A106-D3490211EA57} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} + {0A0FC982-6E31-401F-BA77-3C5E8AB02C68} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {DC163251-16C3-4B72-B965-ACDBA0F02BD1} = {278D8859-20B1-428F-8448-064F46E1F021} + {D35D2FDA-B53F-4F70-81CA-24D95812B89C} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} + EndGlobalSection +EndGlobal diff --git a/src/raylib.h b/src/raylib.h index f9c504680..06138ceca 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1621,6 +1621,8 @@ RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount); // Load model animations from file RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose (CPU) RLAPI void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame); // Update model animation mesh bone matrices (GPU skinning) +RLAPI void UpdateModelAnimationBonesLerp(Model model, ModelAnimation animA, int frameA, ModelAnimation animB, int frameB, float value); // Update model animation mesh bone matrices with interpolation between two poses(GPU skinning) +RLAPI void UpdateModelVertsToCurrentBones(Model model); // Update model vertices according to mesh bone matrices (CPU) RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount); // Unload animation array data RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match diff --git a/src/rmodels.c b/src/rmodels.c index 0ee103212..b90b6a53f 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2346,12 +2346,70 @@ void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame) } } -// at least 2x speed up vs the old method -// Update model animated vertex data (positions and normals) for a given frame -// NOTE: Updated data is uploaded to GPU -void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) +// Update model animated bones transform matrices by interpolating between two different given frames of different ModelAnimation(could be same too) +// NOTE: Updated data is not uploaded to GPU but kept at model.meshes[i].boneMatrices[boneId], +// to be uploaded to shader at drawing, in case GPU skinning is enabled +void UpdateModelAnimationBonesLerp(Model model, ModelAnimation animA, int frameA, ModelAnimation animB, int frameB, float value) { - UpdateModelAnimationBones(model,anim,frame); + if ((animA.frameCount > 0) && (animA.bones != NULL) && (animA.framePoses != NULL) && + (animB.frameCount > 0) && (animB.bones != NULL) && (animB.framePoses != NULL) && + (value >= 0.0f) && (value <= 1.0f)) + { + frameA = frameA % animA.frameCount; + frameB = frameB % animB.frameCount; + + for (int i = 0; i < model.meshCount; i++) + { + if (model.meshes[i].boneMatrices) + { + assert(model.meshes[i].boneCount == animA.boneCount); + assert(model.meshes[i].boneCount == animB.boneCount); + + for (int boneId = 0; boneId < model.meshes[i].boneCount; boneId++) + { + Vector3 inTranslation = model.bindPose[boneId].translation; + Quaternion inRotation = model.bindPose[boneId].rotation; + Vector3 inScale = model.bindPose[boneId].scale; + + Vector3 outATranslation = animA.framePoses[frameA][boneId].translation; + Quaternion outARotation = animA.framePoses[frameA][boneId].rotation; + Vector3 outAScale = animA.framePoses[frameA][boneId].scale; + + Vector3 outBTranslation = animB.framePoses[frameB][boneId].translation; + Quaternion outBRotation = animB.framePoses[frameB][boneId].rotation; + Vector3 outBScale = animB.framePoses[frameB][boneId].scale; + + Vector3 outTranslation = Vector3Lerp(outATranslation, outBTranslation, value); + Quaternion outRotation = QuaternionSlerp(outARotation, outBRotation, value); + Vector3 outScale = Vector3Lerp(outAScale, outBScale, value); + + Vector3 invTranslation = Vector3RotateByQuaternion(Vector3Negate(inTranslation), QuaternionInvert(inRotation)); + Quaternion invRotation = QuaternionInvert(inRotation); + Vector3 invScale = Vector3Divide((Vector3){ 1.0f, 1.0f, 1.0f }, inScale); + + Vector3 boneTranslation = Vector3Add( + Vector3RotateByQuaternion(Vector3Multiply(outScale, invTranslation), + outRotation), outTranslation); + Quaternion boneRotation = QuaternionMultiply(outRotation, invRotation); + Vector3 boneScale = Vector3Multiply(outScale, invScale); + + Matrix boneMatrix = MatrixMultiply(MatrixMultiply( + QuaternionToMatrix(boneRotation), + MatrixTranslate(boneTranslation.x, boneTranslation.y, boneTranslation.z)), + MatrixScale(boneScale.x, boneScale.y, boneScale.z)); + + model.meshes[i].boneMatrices[boneId] = boneMatrix; + } + } + } + } +} + +// Update model vertex data (positions and normals) from mesh bone data +// NOTE: Updated data is uploaded to GPU +void UpdateModelVertsToCurrentBones(Model model) +{ + //UpdateModelAnimationBones(model, anim, frame); // TODO: Review for (int m = 0; m < model.meshCount; m++) { @@ -2416,6 +2474,15 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) } } +// at least 2x speed up vs the old method +// Update model animated vertex data (positions and normals) for a given frame +// NOTE: Updated data is uploaded to GPU +void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) +{ + UpdateModelAnimationBones(model,anim,frame); + UpdateModelVertsToCurrentBones(model); +} + // Unload animation array data void UnloadModelAnimations(ModelAnimation *animations, int animCount) { diff --git a/tools/rlparser/output/raylib_api.json b/tools/rlparser/output/raylib_api.json index 96875fe87..f102abb1d 100644 --- a/tools/rlparser/output/raylib_api.json +++ b/tools/rlparser/output/raylib_api.json @@ -11436,6 +11436,48 @@ } ] }, + { + "name": "UpdateModelAnimationBonesLerp", + "description": "Update model animation mesh bone matrices with interpolation between two poses(GPU skinning)", + "returnType": "void", + "params": [ + { + "type": "Model", + "name": "model" + }, + { + "type": "ModelAnimation", + "name": "animA" + }, + { + "type": "int", + "name": "frameA" + }, + { + "type": "ModelAnimation", + "name": "animB" + }, + { + "type": "int", + "name": "frameB" + }, + { + "type": "float", + "name": "value" + } + ] + }, + { + "name": "UpdateModelVertsToCurrentBones", + "description": "Update model vertices according to mesh bone matrices (CPU)", + "returnType": "void", + "params": [ + { + "type": "Model", + "name": "model" + } + ] + }, { "name": "UnloadModelAnimation", "description": "Unload animation data", diff --git a/tools/rlparser/output/raylib_api.lua b/tools/rlparser/output/raylib_api.lua index a1f79cbe6..3707896bb 100644 --- a/tools/rlparser/output/raylib_api.lua +++ b/tools/rlparser/output/raylib_api.lua @@ -7812,6 +7812,27 @@ return { {type = "int", name = "frame"} } }, + { + name = "UpdateModelAnimationBonesLerp", + description = "Update model animation mesh bone matrices with interpolation between two poses(GPU skinning)", + returnType = "void", + params = { + {type = "Model", name = "model"}, + {type = "ModelAnimation", name = "animA"}, + {type = "int", name = "frameA"}, + {type = "ModelAnimation", name = "animB"}, + {type = "int", name = "frameB"}, + {type = "float", name = "value"} + } + }, + { + name = "UpdateModelVertsToCurrentBones", + description = "Update model vertices according to mesh bone matrices (CPU)", + returnType = "void", + params = { + {type = "Model", name = "model"} + } + }, { name = "UnloadModelAnimation", description = "Unload animation data", diff --git a/tools/rlparser/output/raylib_api.txt b/tools/rlparser/output/raylib_api.txt index e7ff4c98b..9c25843f5 100644 --- a/tools/rlparser/output/raylib_api.txt +++ b/tools/rlparser/output/raylib_api.txt @@ -992,7 +992,7 @@ Callback 006: AudioCallback() (2 input parameters) Param[1]: bufferData (type: void *) Param[2]: frames (type: unsigned int) -Functions found: 599 +Functions found: 601 Function 001: InitWindow() (3 input parameters) Name: InitWindow @@ -4354,24 +4354,39 @@ Function 522: UpdateModelAnimationBones() (3 input parameters) Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: int) -Function 523: UnloadModelAnimation() (1 input parameters) +Function 523: UpdateModelAnimationBonesLerp() (6 input parameters) + Name: UpdateModelAnimationBonesLerp + Return type: void + Description: Update model animation mesh bone matrices with interpolation between two poses(GPU skinning) + Param[1]: model (type: Model) + Param[2]: animA (type: ModelAnimation) + Param[3]: frameA (type: int) + Param[4]: animB (type: ModelAnimation) + Param[5]: frameB (type: int) + Param[6]: value (type: float) +Function 524: UpdateModelVertsToCurrentBones() (1 input parameters) + Name: UpdateModelVertsToCurrentBones + Return type: void + Description: Update model vertices according to mesh bone matrices (CPU) + Param[1]: model (type: Model) +Function 525: UnloadModelAnimation() (1 input parameters) Name: UnloadModelAnimation Return type: void Description: Unload animation data Param[1]: anim (type: ModelAnimation) -Function 524: UnloadModelAnimations() (2 input parameters) +Function 526: 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 525: IsModelAnimationValid() (2 input parameters) +Function 527: 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 526: CheckCollisionSpheres() (4 input parameters) +Function 528: CheckCollisionSpheres() (4 input parameters) Name: CheckCollisionSpheres Return type: bool Description: Check collision between two spheres @@ -4379,40 +4394,40 @@ Function 526: CheckCollisionSpheres() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector3) Param[4]: radius2 (type: float) -Function 527: CheckCollisionBoxes() (2 input parameters) +Function 529: 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 528: CheckCollisionBoxSphere() (3 input parameters) +Function 530: 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 529: GetRayCollisionSphere() (3 input parameters) +Function 531: 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 530: GetRayCollisionBox() (2 input parameters) +Function 532: 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 531: GetRayCollisionMesh() (3 input parameters) +Function 533: 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 532: GetRayCollisionTriangle() (4 input parameters) +Function 534: GetRayCollisionTriangle() (4 input parameters) Name: GetRayCollisionTriangle Return type: RayCollision Description: Get collision info between ray and triangle @@ -4420,7 +4435,7 @@ Function 532: GetRayCollisionTriangle() (4 input parameters) Param[2]: p1 (type: Vector3) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) -Function 533: GetRayCollisionQuad() (5 input parameters) +Function 535: GetRayCollisionQuad() (5 input parameters) Name: GetRayCollisionQuad Return type: RayCollision Description: Get collision info between ray and quad @@ -4429,158 +4444,158 @@ Function 533: GetRayCollisionQuad() (5 input parameters) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) Param[5]: p4 (type: Vector3) -Function 534: InitAudioDevice() (0 input parameters) +Function 536: InitAudioDevice() (0 input parameters) Name: InitAudioDevice Return type: void Description: Initialize audio device and context No input parameters -Function 535: CloseAudioDevice() (0 input parameters) +Function 537: CloseAudioDevice() (0 input parameters) Name: CloseAudioDevice Return type: void Description: Close the audio device and context No input parameters -Function 536: IsAudioDeviceReady() (0 input parameters) +Function 538: IsAudioDeviceReady() (0 input parameters) Name: IsAudioDeviceReady Return type: bool Description: Check if audio device has been initialized successfully No input parameters -Function 537: SetMasterVolume() (1 input parameters) +Function 539: SetMasterVolume() (1 input parameters) Name: SetMasterVolume Return type: void Description: Set master volume (listener) Param[1]: volume (type: float) -Function 538: GetMasterVolume() (0 input parameters) +Function 540: GetMasterVolume() (0 input parameters) Name: GetMasterVolume Return type: float Description: Get master volume (listener) No input parameters -Function 539: LoadWave() (1 input parameters) +Function 541: LoadWave() (1 input parameters) Name: LoadWave Return type: Wave Description: Load wave data from file Param[1]: fileName (type: const char *) -Function 540: LoadWaveFromMemory() (3 input parameters) +Function 542: 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 541: IsWaveValid() (1 input parameters) +Function 543: 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 542: LoadSound() (1 input parameters) +Function 544: LoadSound() (1 input parameters) Name: LoadSound Return type: Sound Description: Load sound from file Param[1]: fileName (type: const char *) -Function 543: LoadSoundFromWave() (1 input parameters) +Function 545: LoadSoundFromWave() (1 input parameters) Name: LoadSoundFromWave Return type: Sound Description: Load sound from wave data Param[1]: wave (type: Wave) -Function 544: LoadSoundAlias() (1 input parameters) +Function 546: 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 545: IsSoundValid() (1 input parameters) +Function 547: 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 546: UpdateSound() (3 input parameters) +Function 548: UpdateSound() (3 input parameters) Name: UpdateSound Return type: void Description: Update sound buffer with new data (default data format: 32 bit float, stereo) Param[1]: sound (type: Sound) Param[2]: data (type: const void *) Param[3]: sampleCount (type: int) -Function 547: UnloadWave() (1 input parameters) +Function 549: UnloadWave() (1 input parameters) Name: UnloadWave Return type: void Description: Unload wave data Param[1]: wave (type: Wave) -Function 548: UnloadSound() (1 input parameters) +Function 550: UnloadSound() (1 input parameters) Name: UnloadSound Return type: void Description: Unload sound Param[1]: sound (type: Sound) -Function 549: UnloadSoundAlias() (1 input parameters) +Function 551: 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 550: ExportWave() (2 input parameters) +Function 552: 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 551: ExportWaveAsCode() (2 input parameters) +Function 553: 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 552: PlaySound() (1 input parameters) +Function 554: PlaySound() (1 input parameters) Name: PlaySound Return type: void Description: Play a sound Param[1]: sound (type: Sound) -Function 553: StopSound() (1 input parameters) +Function 555: StopSound() (1 input parameters) Name: StopSound Return type: void Description: Stop playing a sound Param[1]: sound (type: Sound) -Function 554: PauseSound() (1 input parameters) +Function 556: PauseSound() (1 input parameters) Name: PauseSound Return type: void Description: Pause a sound Param[1]: sound (type: Sound) -Function 555: ResumeSound() (1 input parameters) +Function 557: ResumeSound() (1 input parameters) Name: ResumeSound Return type: void Description: Resume a paused sound Param[1]: sound (type: Sound) -Function 556: IsSoundPlaying() (1 input parameters) +Function 558: IsSoundPlaying() (1 input parameters) Name: IsSoundPlaying Return type: bool Description: Check if a sound is currently playing Param[1]: sound (type: Sound) -Function 557: SetSoundVolume() (2 input parameters) +Function 559: 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 558: SetSoundPitch() (2 input parameters) +Function 560: 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 559: SetSoundPan() (2 input parameters) +Function 561: SetSoundPan() (2 input parameters) Name: SetSoundPan Return type: void Description: Set pan for a sound (-1.0 left, 0.0 center, 1.0 right) Param[1]: sound (type: Sound) Param[2]: pan (type: float) -Function 560: WaveCopy() (1 input parameters) +Function 562: WaveCopy() (1 input parameters) Name: WaveCopy Return type: Wave Description: Copy a wave to a new wave Param[1]: wave (type: Wave) -Function 561: WaveCrop() (3 input parameters) +Function 563: 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 562: WaveFormat() (4 input parameters) +Function 564: WaveFormat() (4 input parameters) Name: WaveFormat Return type: void Description: Convert wave data to desired format @@ -4588,203 +4603,203 @@ Function 562: WaveFormat() (4 input parameters) Param[2]: sampleRate (type: int) Param[3]: sampleSize (type: int) Param[4]: channels (type: int) -Function 563: LoadWaveSamples() (1 input parameters) +Function 565: 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 564: UnloadWaveSamples() (1 input parameters) +Function 566: UnloadWaveSamples() (1 input parameters) Name: UnloadWaveSamples Return type: void Description: Unload samples data loaded with LoadWaveSamples() Param[1]: samples (type: float *) -Function 565: LoadMusicStream() (1 input parameters) +Function 567: LoadMusicStream() (1 input parameters) Name: LoadMusicStream Return type: Music Description: Load music stream from file Param[1]: fileName (type: const char *) -Function 566: LoadMusicStreamFromMemory() (3 input parameters) +Function 568: 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 567: IsMusicValid() (1 input parameters) +Function 569: 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 568: UnloadMusicStream() (1 input parameters) +Function 570: UnloadMusicStream() (1 input parameters) Name: UnloadMusicStream Return type: void Description: Unload music stream Param[1]: music (type: Music) -Function 569: PlayMusicStream() (1 input parameters) +Function 571: PlayMusicStream() (1 input parameters) Name: PlayMusicStream Return type: void Description: Start music playing Param[1]: music (type: Music) -Function 570: IsMusicStreamPlaying() (1 input parameters) +Function 572: IsMusicStreamPlaying() (1 input parameters) Name: IsMusicStreamPlaying Return type: bool Description: Check if music is playing Param[1]: music (type: Music) -Function 571: UpdateMusicStream() (1 input parameters) +Function 573: UpdateMusicStream() (1 input parameters) Name: UpdateMusicStream Return type: void Description: Updates buffers for music streaming Param[1]: music (type: Music) -Function 572: StopMusicStream() (1 input parameters) +Function 574: StopMusicStream() (1 input parameters) Name: StopMusicStream Return type: void Description: Stop music playing Param[1]: music (type: Music) -Function 573: PauseMusicStream() (1 input parameters) +Function 575: PauseMusicStream() (1 input parameters) Name: PauseMusicStream Return type: void Description: Pause music playing Param[1]: music (type: Music) -Function 574: ResumeMusicStream() (1 input parameters) +Function 576: ResumeMusicStream() (1 input parameters) Name: ResumeMusicStream Return type: void Description: Resume playing paused music Param[1]: music (type: Music) -Function 575: SeekMusicStream() (2 input parameters) +Function 577: 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 576: SetMusicVolume() (2 input parameters) +Function 578: 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 577: SetMusicPitch() (2 input parameters) +Function 579: 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 578: SetMusicPan() (2 input parameters) +Function 580: SetMusicPan() (2 input parameters) Name: SetMusicPan Return type: void Description: Set pan for a music (-1.0 left, 0.0 center, 1.0 right) Param[1]: music (type: Music) Param[2]: pan (type: float) -Function 579: GetMusicTimeLength() (1 input parameters) +Function 581: GetMusicTimeLength() (1 input parameters) Name: GetMusicTimeLength Return type: float Description: Get music time length (in seconds) Param[1]: music (type: Music) -Function 580: GetMusicTimePlayed() (1 input parameters) +Function 582: GetMusicTimePlayed() (1 input parameters) Name: GetMusicTimePlayed Return type: float Description: Get current music time played (in seconds) Param[1]: music (type: Music) -Function 581: LoadAudioStream() (3 input parameters) +Function 583: 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 582: IsAudioStreamValid() (1 input parameters) +Function 584: 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 583: UnloadAudioStream() (1 input parameters) +Function 585: UnloadAudioStream() (1 input parameters) Name: UnloadAudioStream Return type: void Description: Unload audio stream and free memory Param[1]: stream (type: AudioStream) -Function 584: UpdateAudioStream() (3 input parameters) +Function 586: 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 585: IsAudioStreamProcessed() (1 input parameters) +Function 587: IsAudioStreamProcessed() (1 input parameters) Name: IsAudioStreamProcessed Return type: bool Description: Check if any audio stream buffers requires refill Param[1]: stream (type: AudioStream) -Function 586: PlayAudioStream() (1 input parameters) +Function 588: PlayAudioStream() (1 input parameters) Name: PlayAudioStream Return type: void Description: Play audio stream Param[1]: stream (type: AudioStream) -Function 587: PauseAudioStream() (1 input parameters) +Function 589: PauseAudioStream() (1 input parameters) Name: PauseAudioStream Return type: void Description: Pause audio stream Param[1]: stream (type: AudioStream) -Function 588: ResumeAudioStream() (1 input parameters) +Function 590: ResumeAudioStream() (1 input parameters) Name: ResumeAudioStream Return type: void Description: Resume audio stream Param[1]: stream (type: AudioStream) -Function 589: IsAudioStreamPlaying() (1 input parameters) +Function 591: IsAudioStreamPlaying() (1 input parameters) Name: IsAudioStreamPlaying Return type: bool Description: Check if audio stream is playing Param[1]: stream (type: AudioStream) -Function 590: StopAudioStream() (1 input parameters) +Function 592: StopAudioStream() (1 input parameters) Name: StopAudioStream Return type: void Description: Stop audio stream Param[1]: stream (type: AudioStream) -Function 591: SetAudioStreamVolume() (2 input parameters) +Function 593: 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 592: SetAudioStreamPitch() (2 input parameters) +Function 594: 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 593: SetAudioStreamPan() (2 input parameters) +Function 595: 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 594: SetAudioStreamBufferSizeDefault() (1 input parameters) +Function 596: SetAudioStreamBufferSizeDefault() (1 input parameters) Name: SetAudioStreamBufferSizeDefault Return type: void Description: Default size for new audio streams Param[1]: size (type: int) -Function 595: SetAudioStreamCallback() (2 input parameters) +Function 597: 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 596: AttachAudioStreamProcessor() (2 input parameters) +Function 598: 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 597: DetachAudioStreamProcessor() (2 input parameters) +Function 599: 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 598: AttachAudioMixedProcessor() (1 input parameters) +Function 600: 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 599: DetachAudioMixedProcessor() (1 input parameters) +Function 601: DetachAudioMixedProcessor() (1 input parameters) Name: DetachAudioMixedProcessor Return type: void Description: Detach audio stream processor from the entire audio pipeline diff --git a/tools/rlparser/output/raylib_api.xml b/tools/rlparser/output/raylib_api.xml index 8734f405d..d3314a5d1 100644 --- a/tools/rlparser/output/raylib_api.xml +++ b/tools/rlparser/output/raylib_api.xml @@ -678,7 +678,7 @@ - + @@ -2918,6 +2918,17 @@ + + + + + + + + + + + From 71607db6672ae843e2f9c541f4fd0f260d80ea0a Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Feb 2026 16:46:30 +0100 Subject: [PATCH 031/319] Moved easings example to shapes --- examples/others/reasings.h | 263 ------------------ .../shapes_easings_testbed.c} | 39 ++- .../shapes_easings_testbed.png} | Bin projects/VS2022/raylib.sln | 4 +- 4 files changed, 27 insertions(+), 279 deletions(-) delete mode 100644 examples/others/reasings.h rename examples/{others/easings_testbed.c => shapes/shapes_easings_testbed.c} (87%) rename examples/{others/easings_testbed.png => shapes/shapes_easings_testbed.png} (100%) diff --git a/examples/others/reasings.h b/examples/others/reasings.h deleted file mode 100644 index c3ee1169f..000000000 --- a/examples/others/reasings.h +++ /dev/null @@ -1,263 +0,0 @@ -/******************************************************************************************* -* -* reasings - raylib easings library, based on Robert Penner library -* -* Useful easing functions for values animation -* -* This header uses: -* #define REASINGS_STATIC_INLINE // Inlines all functions code, so it runs faster. -* // This requires lots of memory on system. -* How to use: -* The four inputs t,b,c,d are defined as follows: -* t = current time (in any unit measure, but same unit as duration) -* b = starting value to interpolate -* c = the total change in value of b that needs to occur -* d = total time it should take to complete (duration) -* -* Example: -* -* int currentTime = 0; -* int duration = 100; -* float startPositionX = 0.0f; -* float finalPositionX = 30.0f; -* float currentPositionX = startPositionX; -* -* while (currentPositionX < finalPositionX) -* { -* currentPositionX = EaseSineIn(currentTime, startPositionX, finalPositionX - startPositionX, duration); -* currentTime++; -* } -* -* A port of Robert Penner's easing equations to C (http://robertpenner.com/easing/) -* -* Robert Penner License -* --------------------------------------------------------------------------------- -* Open source under the BSD License. -* -* Copyright (c) 2001 Robert Penner. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without modification, -* are permitted provided that the following conditions are met: -* -* - Redistributions of source code must retain the above copyright notice, -* this list of conditions and the following disclaimer. -* - Redistributions in binary form must reproduce the above copyright notice, -* this list of conditions and the following disclaimer in the documentation -* and/or other materials provided with the distribution. -* - Neither the name of the author nor the names of contributors may be used -* to endorse or promote products derived from this software without specific -* prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED -* OF THE POSSIBILITY OF SUCH DAMAGE. -* --------------------------------------------------------------------------------- -* -* Copyright (c) 2015-2024 Ramon Santamaria (@raysan5) -* -* This software is provided "as-is", without any express or implied warranty. In no event -* will the authors be held liable for any damages arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, including commercial -* applications, and to alter it and redistribute it freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not claim that you -* wrote the original software. If you use this software in a product, an acknowledgment -* in the product documentation would be appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be misrepresented -* as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -**********************************************************************************************/ - -#ifndef REASINGS_H -#define REASINGS_H - -#define REASINGS_STATIC_INLINE // NOTE: By default, compile functions as static inline - -#if defined(REASINGS_STATIC_INLINE) - #define EASEDEF static inline -#else - #define EASEDEF extern -#endif - -#include // Required for: sinf(), cosf(), sqrtf(), powf() - -#ifndef PI - #define PI 3.14159265358979323846f //Required as PI is not always defined in math.h -#endif - -#if defined(__cplusplus) -extern "C" { // Prevents name mangling of functions -#endif - -// Linear Easing functions -EASEDEF float EaseLinearNone(float t, float b, float c, float d) { return (c*t/d + b); } // Ease: Linear -EASEDEF float EaseLinearIn(float t, float b, float c, float d) { return (c*t/d + b); } // Ease: Linear In -EASEDEF float EaseLinearOut(float t, float b, float c, float d) { return (c*t/d + b); } // Ease: Linear Out -EASEDEF float EaseLinearInOut(float t, float b, float c, float d) { return (c*t/d + b); } // Ease: Linear In Out - -// Sine Easing functions -EASEDEF float EaseSineIn(float t, float b, float c, float d) { return (-c*cosf(t/d*(PI/2.0f)) + c + b); } // Ease: Sine In -EASEDEF float EaseSineOut(float t, float b, float c, float d) { return (c*sinf(t/d*(PI/2.0f)) + b); } // Ease: Sine Out -EASEDEF float EaseSineInOut(float t, float b, float c, float d) { return (-c/2.0f*(cosf(PI*t/d) - 1.0f) + b); } // Ease: Sine In Out - -// Circular Easing functions -EASEDEF float EaseCircIn(float t, float b, float c, float d) { t /= d; return (-c*(sqrtf(1.0f - t*t) - 1.0f) + b); } // Ease: Circular In -EASEDEF float EaseCircOut(float t, float b, float c, float d) { t = t/d - 1.0f; return (c*sqrtf(1.0f - t*t) + b); } // Ease: Circular Out -EASEDEF float EaseCircInOut(float t, float b, float c, float d) // Ease: Circular In Out -{ - if ((t/=d/2.0f) < 1.0f) return (-c/2.0f*(sqrtf(1.0f - t*t) - 1.0f) + b); - t -= 2.0f; return (c/2.0f*(sqrtf(1.0f - t*t) + 1.0f) + b); -} - -// Cubic Easing functions -EASEDEF float EaseCubicIn(float t, float b, float c, float d) { t /= d; return (c*t*t*t + b); } // Ease: Cubic In -EASEDEF float EaseCubicOut(float t, float b, float c, float d) { t = t/d - 1.0f; return (c*(t*t*t + 1.0f) + b); } // Ease: Cubic Out -EASEDEF float EaseCubicInOut(float t, float b, float c, float d) // Ease: Cubic In Out -{ - if ((t/=d/2.0f) < 1.0f) return (c/2.0f*t*t*t + b); - t -= 2.0f; return (c/2.0f*(t*t*t + 2.0f) + b); -} - -// Quadratic Easing functions -EASEDEF float EaseQuadIn(float t, float b, float c, float d) { t /= d; return (c*t*t + b); } // Ease: Quadratic In -EASEDEF float EaseQuadOut(float t, float b, float c, float d) { t /= d; return (-c*t*(t - 2.0f) + b); } // Ease: Quadratic Out -EASEDEF float EaseQuadInOut(float t, float b, float c, float d) // Ease: Quadratic In Out -{ - if ((t/=d/2) < 1) return (((c/2)*(t*t)) + b); - return (-c/2.0f*(((t - 1.0f)*(t - 3.0f)) - 1.0f) + b); -} - -// Exponential Easing functions -EASEDEF float EaseExpoIn(float t, float b, float c, float d) { return (t == 0.0f) ? b : (c*powf(2.0f, 10.0f*(t/d - 1.0f)) + b); } // Ease: Exponential In -EASEDEF float EaseExpoOut(float t, float b, float c, float d) { return (t == d) ? (b + c) : (c*(-powf(2.0f, -10.0f*t/d) + 1.0f) + b); } // Ease: Exponential Out -EASEDEF float EaseExpoInOut(float t, float b, float c, float d) // Ease: Exponential In Out -{ - if (t == 0.0f) return b; - if (t == d) return (b + c); - if ((t/=d/2.0f) < 1.0f) return (c/2.0f*powf(2.0f, 10.0f*(t - 1.0f)) + b); - - return (c/2.0f*(-powf(2.0f, -10.0f*(t - 1.0f)) + 2.0f) + b); -} - -// Back Easing functions -EASEDEF float EaseBackIn(float t, float b, float c, float d) // Ease: Back In -{ - float s = 1.70158f; - float postFix = t/=d; - return (c*(postFix)*t*((s + 1.0f)*t - s) + b); -} - -EASEDEF float EaseBackOut(float t, float b, float c, float d) // Ease: Back Out -{ - float s = 1.70158f; - t = t/d - 1.0f; - return (c*(t*t*((s + 1.0f)*t + s) + 1.0f) + b); -} - -EASEDEF float EaseBackInOut(float t, float b, float c, float d) // Ease: Back In Out -{ - float s = 1.70158f; - if ((t/=d/2.0f) < 1.0f) - { - s *= 1.525f; - return (c/2.0f*(t*t*((s + 1.0f)*t - s)) + b); - } - - float postFix = t-=2.0f; - s *= 1.525f; - return (c/2.0f*((postFix)*t*((s + 1.0f)*t + s) + 2.0f) + b); -} - -// Bounce Easing functions -EASEDEF float EaseBounceOut(float t, float b, float c, float d) // Ease: Bounce Out -{ - if ((t/=d) < (1.0f/2.75f)) - { - return (c*(7.5625f*t*t) + b); - } - else if (t < (2.0f/2.75f)) - { - float postFix = t-=(1.5f/2.75f); - return (c*(7.5625f*(postFix)*t + 0.75f) + b); - } - else if (t < (2.5/2.75)) - { - float postFix = t-=(2.25f/2.75f); - return (c*(7.5625f*(postFix)*t + 0.9375f) + b); - } - else - { - float postFix = t-=(2.625f/2.75f); - return (c*(7.5625f*(postFix)*t + 0.984375f) + b); - } -} - -EASEDEF float EaseBounceIn(float t, float b, float c, float d) { return (c - EaseBounceOut(d - t, 0.0f, c, d) + b); } // Ease: Bounce In -EASEDEF float EaseBounceInOut(float t, float b, float c, float d) // Ease: Bounce In Out -{ - if (t < d/2.0f) return (EaseBounceIn(t*2.0f, 0.0f, c, d)*0.5f + b); - else return (EaseBounceOut(t*2.0f - d, 0.0f, c, d)*0.5f + c*0.5f + b); -} - -// Elastic Easing functions -EASEDEF float EaseElasticIn(float t, float b, float c, float d) // Ease: Elastic In -{ - if (t == 0.0f) return b; - if ((t/=d) == 1.0f) return (b + c); - - float p = d*0.3f; - float a = c; - float s = p/4.0f; - float postFix = a*powf(2.0f, 10.0f*(t-=1.0f)); - - return (-(postFix*sinf((t*d-s)*(2.0f*PI)/p )) + b); -} - -EASEDEF float EaseElasticOut(float t, float b, float c, float d) // Ease: Elastic Out -{ - if (t == 0.0f) return b; - if ((t/=d) == 1.0f) return (b + c); - - float p = d*0.3f; - float a = c; - float s = p/4.0f; - - return (a*powf(2.0f,-10.0f*t)*sinf((t*d-s)*(2.0f*PI)/p) + c + b); -} - -EASEDEF float EaseElasticInOut(float t, float b, float c, float d) // Ease: Elastic In Out -{ - if (t == 0.0f) return b; - if ((t/=d/2.0f) == 2.0f) return (b + c); - - float p = d*(0.3f*1.5f); - float a = c; - float s = p/4.0f; - - if (t < 1.0f) - { - float postFix = a*powf(2.0f, 10.0f*(t-=1.0f)); - return -0.5f*(postFix*sinf((t*d-s)*(2.0f*PI)/p)) + b; - } - - float postFix = a*powf(2.0f, -10.0f*(t-=1.0f)); - - return (postFix*sinf((t*d-s)*(2.0f*PI)/p)*0.5f + c + b); -} - -#if defined(__cplusplus) -} -#endif - -#endif // REASINGS_H diff --git a/examples/others/easings_testbed.c b/examples/shapes/shapes_easings_testbed.c similarity index 87% rename from examples/others/easings_testbed.c rename to examples/shapes/shapes_easings_testbed.c index fa4a599ee..a3ff23a7c 100644 --- a/examples/others/easings_testbed.c +++ b/examples/shapes/shapes_easings_testbed.c @@ -1,11 +1,11 @@ /******************************************************************************************* * -* raylib [others] example - easings testbed -* -* Example originally created with raylib 2.5, last time updated with raylib 2.5 +* raylib [shapes] example - easings testbed * * Example complexity rating: [★★★☆] 3/4 * +* Example originally created with raylib 2.5, last time updated with raylib 2.5 +* * Example contributed by Juan Miguel López (@flashback-fx) and reviewed by Ramon Santamaria (@raysan5) * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, @@ -26,6 +26,9 @@ #define D_MIN 1.0f #define D_MAX 10000.0f +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- // Easing types enum EasingTypes { EASE_LINEAR_NONE = 0, @@ -60,13 +63,13 @@ enum EasingTypes { EASING_NONE = NUM_EASING_TYPES }; -static float NoEase(float t, float b, float c, float d); // NoEase function declaration, function used when "no easing" is selected for any axis - -// Easing functions reference data -static const struct { +typedef struct EasingFuncs { const char *name; float (*func)(float, float, float, float); -} Easings[] = { +} EasingFuncs; + +// Easing functions reference data +static const EasingFuncs easings[] = { [EASE_LINEAR_NONE] = { .name = "EaseLinearNone", .func = EaseLinearNone }, [EASE_LINEAR_IN] = { .name = "EaseLinearIn", .func = EaseLinearIn }, [EASE_LINEAR_OUT] = { .name = "EaseLinearOut", .func = EaseLinearOut }, @@ -98,6 +101,12 @@ static const struct { [EASING_NONE] = { .name = "None", .func = NoEase }, }; +//------------------------------------------------------------------------------------ +// Module Functions Declaration +//------------------------------------------------------------------------------------ +// Function used when "no easing" is selected for any axis +static float NoEase(float t, float b, float c, float d); + //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -108,7 +117,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [others] example - easings testbed"); + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - easings testbed"); Vector2 ballPosition = { 100.0f, 100.0f }; @@ -157,11 +166,11 @@ int main(void) } // Change d (duration) value - if (IsKeyPressed(KEY_W) && d < D_MAX - D_STEP) d += D_STEP; - else if (IsKeyPressed(KEY_Q) && d > D_MIN + D_STEP) d -= D_STEP; + if (IsKeyPressed(KEY_W) && (d < D_MAX - D_STEP)) d += D_STEP; + else if (IsKeyPressed(KEY_Q) && (d > D_MIN + D_STEP)) d -= D_STEP; - if (IsKeyDown(KEY_S) && d < D_MAX - D_STEP_FINE) d += D_STEP_FINE; - else if (IsKeyDown(KEY_A) && d > D_MIN + D_STEP_FINE) d -= D_STEP_FINE; + if (IsKeyDown(KEY_S) && (d < D_MAX - D_STEP_FINE)) d += D_STEP_FINE; + else if (IsKeyDown(KEY_A) && (d > D_MIN + D_STEP_FINE)) d -= D_STEP_FINE; // Play, pause and restart controls if (IsKeyPressed(KEY_SPACE) || IsKeyPressed(KEY_T) || @@ -220,7 +229,9 @@ int main(void) return 0; } - +//------------------------------------------------------------------------------------ +// Module Functions Declaration +//------------------------------------------------------------------------------------ // NoEase function, used when "no easing" is selected for any axis // It just ignores all parameters besides b static float NoEase(float t, float b, float c, float d) diff --git a/examples/others/easings_testbed.png b/examples/shapes/shapes_easings_testbed.png similarity index 100% rename from examples/others/easings_testbed.png rename to examples/shapes/shapes_easings_testbed.png diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 8483dc563..740d1bcbe 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -5619,8 +5619,8 @@ Global {3B27F358-2679-4F38-B297-17B536F580BB} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {718FCBD0-591D-448C-B7D5-9F1CA8544E7B} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {19CA0070-B4B2-4394-90B7-D0C259AA35BA} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} - {9DB1F875-6E65-4195-B23F-ED8095C0B99C} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E} = {278D8859-20B1-428F-8448-064F46E1F021} + {9DB1F875-6E65-4195-B23F-ED8095C0B99C} = {278D8859-20B1-428F-8448-064F46E1F021} {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {8E132D5A-2C00-48D0-8747-97E41356F26F} = {278D8859-20B1-428F-8448-064F46E1F021} {A4662163-83E7-4309-8CAA-B0BF13655FE6} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} From 98c773491179984e3de53a929fd31b467589a738 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Feb 2026 16:46:48 +0100 Subject: [PATCH 032/319] REVIEWED: `models_basic_voxel` example --- examples/models/models_basic_voxel.c | 13 +++++++++---- examples/models/models_basic_voxel.png | Bin 349583 -> 29035 bytes 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/examples/models/models_basic_voxel.c b/examples/models/models_basic_voxel.c index a70da1822..61313ecef 100644 --- a/examples/models/models_basic_voxel.c +++ b/examples/models/models_basic_voxel.c @@ -16,6 +16,7 @@ ********************************************************************************************/ #include "raylib.h" + #include "raymath.h" #define WORLD_SIZE 8 // Size of our voxel world (8x8x8 cubes) @@ -32,7 +33,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [models] example - basic voxel"); - DisableCursor(); // Lock mouse to window center + DisableCursor(); // Lock mouse to window center // Define the camera to look into our 3d world (first person) Camera3D camera = { 0 }; @@ -59,7 +60,7 @@ int main(void) } } } - + SetTargetFPS(60); //-------------------------------------------------------------------------------------- @@ -74,7 +75,7 @@ int main(void) if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { // Cast a ray from the screen center (where crosshair would be) - Vector2 screenCenter = { screenWidth/2.0f, screenHeight/2.0f }; + Vector2 screenCenter = { GetScreenWidth()/2.0f, GetScreenHeight()/2.0f }; Ray ray = GetMouseRay(screenCenter, camera); // Check ray collision with all voxels @@ -132,8 +133,11 @@ int main(void) } } } - + EndMode3D(); + + // Draw reference point for raycasting to delete blocks + DrawCircle(GetRenderWidth()/2, GetScreenHeight()/2, 4, RED); DrawText("Left-click a voxel to remove it!", 10, 10, 20, DARKGRAY); DrawText("WASD to move, mouse to look around", 10, 35, 10, GRAY); @@ -145,6 +149,7 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- UnloadModel(cubeModel); + CloseWindow(); //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_basic_voxel.png b/examples/models/models_basic_voxel.png index 8bbcaf8dae98aa01624392f07822e36f2f44a53d..0666ec8c8dd1b5c043ecfe6afffcb6fe5d4494ae 100644 GIT binary patch literal 29035 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%0A^Vr^v4`8hs~>J7}}@qJobzeWh`LrrU%B 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 literal 349583 zcmeFZi$9b9A3v;AR2cb`B$iY6@IjF{PMwq4hKtv;Xc?|a|(UvNJj_uh{^cJ11Ay|3$fA6~EL>-l=V7kAFe zLVClt4Ps(q(x)tsUl0?M*dZpiCPQ)^yr+KMvzuaKt86^Y&Ci`OH{Ws2AM57n?J6d= zH_0cl?mw%Y{E>*?cb4YhvG|KzkV8byvyw1%U37HJ@)55%s&6g94mW1Ugf1; z_fC(VYm!41U(0tKYrSo8puF1G5J{*f3lVlc_ushs9M!~p%RbQdtK+>-<){CkHBU0N zbKaiN*cz9ADjGYNwIaX8rp;!`ezM<#mVecEo!k7%{-3@)Y^~S2O|7r?uc$K4ANjN< zW&K#o`BS<)yWlga+0oW#t;)IICW7M&)6*V$SaQz?+nqhIe(Zjc{>WI4kwL2IMbfQ9 zg+aSm#kli1`SH&L_@qM-Tb?D|0?D2FrK&Pr2FKLR?;qa%V@+ds%j3VU?N0c5_?i>( z<5WF1P*77-Q3=QGXgaxIZ@6j(`}m6L5i_VKNdXBpWIQx70-uA@a z+99gf=>`^e+jQqn(S`o|`g5PI!Jhx;O1A?3c`Wz=wM4&YX=@(R`fuIvsV1VmM&~?( zUA?ay_w<3=488_RTif7}$?p^XUqAhyOa9NPcK>s#fzILoJ@tQn`hT8!DZtg=9P0yL z^fv1Mbl5*9|L>3goM@sYdiMWmi$4whdoSG5s0}7s|Lrx@hWle(LbxNhdmgvG2!F!E zO!QhU8en_>e8StSOFpfX3~mz>J1TbS__2$@t7boWCf+`Ud!ty6FITv9^n8A!%+;gk zR3Q7RwBl}WO@5XSa0Fd;&a0KYv_DH0dphg(UR@XE zbY9`y(Ty7CjviAzuhd%`Uj6{R9AVwiny$Z7O-Ie2SG)XdcxjFsGK>jA|I#;63($#+ zjyxp0?#t1wlHyJ~r2hXe|KDzd&vn|qSEEp$OrtmW^|)uWYZcTJD(@{r?)G=o>@bpAHVelm_i0bsx!1eLdXlmk&&vrXG)z+BJ|=UCT;$YU zua;o6GYuI1eNgQ^oeye{R4(9XK&aKky4nd{P#q+E7~Q5bLVm_Y1|M_KdYy-TdFEGa zHsN9>mzX90AySL?0|E79)7`fkyq21Fk?~}G%TJD8l`$6;Rr}0MW>d2^m8zm&ByrW@ z7~!^tnpIZ7fI-Dxn|$>w-kT^Ss0)>VG=K(_Lf*s9!v{*CDZ!ZGyUw*_hj#gKLts*n z#;VK;n9|2niyCf^pclcU<%EyGgiviB83MKNErbtxO3h3I`m5Y;Ej^$MGFi(;WNMa8 zX8ihk(M*x}Dwh5tq4SY=nVg`qs)U%l?m}L7yU$FWEX56AB3OC4a%HT%ip+*k0pzvCyx`IRDM2B6$a^Ht-cFWKgg{Y z&Ewg)Rr*`>Zy?@nGi4oreaozAUrAQrK!)tM-b=QoUt`m;rfuuXBX2D@IW^f%KEj72 zTYOMc1}hiWVJs)ljZ7q+lj-gdMBRID@U#kFD81k~#%L zUzPBxzIzCZdlJ%sD?owj)PDqsgYA8%9gQbyZaG-^D?sv4y3n0b?eu8}PWl79E4o1S z_BbG_##?dxFPo|-)aWH8{~X%>5==|7`08q?gL zAE&f}i_k+Yo~fLEtAgu3Mm111tEI)aSqBP@j+GV1%nivR5{;TGg`P?}Y)8vQF3b4gz6&Ce}O( z{t+bYX#1ykI>0>i=deFjih2vDDZ8_M<8HXSqBB0rCGZPgGWPT5!8wvI(y(!B*Qv}K z!>GTj^W-z>9vVos6FGNe*gPdRuG1sg|Ep|U?rGF3XJm}Z^M;@0?&uCg1eXjqems=M zv;F8Iz>+FmpjI%QWdYp!%FClimO6J5bv1t+xcQ6~gtowwrw5-452Hn+`ugw$JnD1! z2d=@2H#RCf?4fn1>O)9r#h!<9?l;IS{5#!NP!LwbcwcIzzCfG{^2Fb)^?CSB;p zqlkOA1@jM(!aTv&8E1_`ldC7M39#fd0xVn#uico!w09RKK7ult*}zkzd3gThQ;W8i zcxWyy4ss+xxX#^D;!dMtif@(PbW$JIN#B`DaoO=`pO` zu=TRQ&@$dK?ge+~`zYh`Wc+r4IrK;(6K$%{DNR2uuofiLHW;4o-^hH9r5MdzR;F1B ztl>+H1EHE3N}Z`9BN-kS@Tx$8c7$4n>wRz3kObE*5zz!KIT+|?Xfe@D^vWjsaYwBN zuUb#WTYFrr8eBU6^65Zw28jh5%fi7Ap;dZ|py9Y1X@nHo zAb?x^y0z4FyYCHykZ-Y856eQHklS)(ZhRobRQq`#m3=nd>|ea8&V|jx`iPj1iG*k< zOQLE~wb|gKE7&o0u@56ih`5)?yEAPO6XA9(0^@a=%sv_WtP>vUaZu*_Qjzn)t9ji$ z1Xd<>xs0H6wMd0W^5_xf@G)SrP@PY1CyS;+6t*4yni`VGlE}F?Xt)|R(SS4K9U_`E zu;S>_W(|kgB~ZJ;Jw(XvCte}j7frNCm*yJ$d3%nZAwcbl9 zn!;TUCn?buJ+cIsQRVTqlYL6#)aq$xeeM{Vr3E`$iG-~X@8za{dPw-N0J&a(-c2x-tB?5SM}e+%?jAA3dS{Q{3fqYv7%;lDD1j z_LDq&eM15v`1b9~Ge65>aF#Zg84nEi`gNP;bG<*;*Y<*K=WQ6=V+x(`WPZA? zkR>#xcTPJW5IqAnPb=5!00Ub?j)lACz1J@Irl}cdgG+4Ii!AGh@>7EHaIMyV2TuO{ zBHKHOwH!!H6WuEA;RNhW;f-Nt*otC_`~3_OpEcPk0FO~q(x8kN8N%EYXyTK@aXy~(?J)TqN6E|IS+ z(l%Z&Wif8_UA7?_Nh_4Dl-J_A^?_k-#51#@&+>#aAFpmtR0zm9;y3hO{2ja49Vumh zS^agc+R24_bik`lBz>KHlc1fHPvRmkAD=fK2g0a{LU%qGiN;Zn%qffvRTF zLke4@z4p)(zb(wl(CO0?26?&4a&Nwb)Rji1 zO-u^Dv!22Zf+|8n3MlP);wP;Zeh><1p=${}*Q+e@+GeHF$izjSJqj=}L;({D^)q|~ zzjpq9bW0bMWRZrz&G29I*M&;DQf76mCLVqQ3gnUYN#WN&$CV(?~`GK-qG=@B>zOEBr@^CsIsm?+{WI<)72`WeQ{W%N5^@kF}RPI|KgY9iS(q6-E;? zNdB-13**QTs0$Aqg%Z5i8f`pdkz4%AcOQXGTITZnV4BW6F$8MotLHQ(vxGf08^|O) zsv|f+kE5J!aDhxtVec*_N`yn&UZ-w&xqd!XbU9nQPkZ!+THRRv+0+wLuhf>--s?Jj zVoOGRw|2>yPh)rWVB_L_fhcihGh~-nDhbRUm0subT7{{KvT7ALK^pa%uEuOM*EH34 zEqPJyH)?f@Sf?o~c!Y}i$R&>0){FCFel)-!;@N|DySJSB;(t@PPf3GIF+e zE&Yp5ndVBb{aR{Z(KLN584=^Nxxa|U*BR-pWFy0wHs(Rk$-JbkWamLUg4gf!fKr3(Wxth!>@$f9v5n`P94}xG`TIy7|>g! zkJT)ACm5L9PsLlev}JC+QITIh-cu3PAYsfER;e^_Mb06gl#j++3#`?~)*$%hP}6_Q z@9q)JqY(t`HPJ*!gtUGmtgKnO_58YNb(XSrL@%ARyoS@wkHHNCEjE*94pynjya{0~ zIh=vaFnMRJKy=rf#)NCZ&lw;D-4Cq&^v2f?yPz+`)HMVRr&De$==h3d-kRV zSa~Zv-1ye&x=){u!ntySG&6?Vd*U6a!yOHdvtyP!za4)@utr^AH-jI~Hl9P8^5d0@ z%ABi|1gAg^VtZ{__1VMP#y zT&*AaOQ#*4_FA1;hCnCEdrGhi8Eq+oG;~1+{^&DgLtese;z!Zo`^xjG(BtktA$&Xb zrW3hP+b$IMxgpJ%w=a|LiMI5a*8zz}+~_fGdI6_tp*udm2Z^x3U_4 zvp&S(uO|nruI)Ko@$Q^Oac#UrxQi5KyF#lh#y+~&0%>t~ZvT@56~=>udZi0K6_DX+0nZv6p|IXKI?4bjrjC>4!cm^%5g>J62KShX^?NTv_FuGVjcVwq zdn{HMefp4CUDD*%!~^PY$}`I9VM?9B=^mvK1#ur zvO{LX1V0p6i<8>dqZd+tuZGB#1}(>o)jgJtxagb0Aep}*MW`M6#HZ#%*^|&Xlwu4W z3k|+QJKiM&Ow0PYF&vFT(iC43_?s1B{Sij64zqbdnZ2wpo;bf!6?2 zo6?#!-}p=((z)J%`C((pEM4o7#!8>|>-9M<%P<26l5A!C`AT@d-;HSmM6faE8o1l2>PoW?7qIk`e`u4+i{5GB)L4 zly$m~?PRv=d<*mn)QU$a)Xjg8flLwY5)HU-IV(ip*DjzBTkAXIBGjNme^>+;s1D%* zdrL;dXGJx7LY6q$KH3oI7-$iVxs?cb4nb)f89PM|#cqnFzC85DxEa&2!c{r>LIWTw z;I9$LG)Rf^>ud(PO8;0OjacD3{ zU}S<*t#DCiG)UMum}@U@gkJEGf`}1U0m4n_3c7-0%cWomD02f!fc?Lyh%f*QpJy|T z=SjMng7!$njtBJ!KfyY`FPG5J3#(3~Auu6GVp)hbGB(f?Dkjg)?~=I*WlKwPO@2`s z6GY6;V}W5%$t*8kS8{7~1+~caMAO!-GGCu{=04M(a19=KpWJc6e0t4o?U&8wX@0mE zRU=kI^%)NuMg8_;`I|ld3~6Ti%^Z97eiZv%Y-`3t6W~YP)_++pam>HiOKEww&0R1Ef2&}$lhrDE2dYJ|f-^`7JPzgjA1o#u79}*6% zbPlu`TLqn)GQCT$xI{;;u)$U7Z-OY0XM+1ZC)1&9jh<;{Z|Fc=#&xkTJM9${<@N3M zB+66QpV;Q~YTuJ9sRnnYSzKx3o`w!=x2Dxq<;4bx`g^2p#D8;^yfiOf&M+x5Hn|3T^#!P2i{lGOV8tOLOCVRM4NMhk@iFskL>{y({7R;J&b4yD zI3(pTV05dWZ8khOu-tXMt=anR7_G zr$E;!!^6$Uup{WkV`~*vrMt-#LCW{1@>iP;*l+7yCWkc9_pw^YPKLrF1(MfQO_umr56{w2n zF%ftWkq3q?LISw~v5C|PppxA&$0bAdQWeP0eUBsxRvVcY1aUvSz7}=8PpRG_N2< z98x8kfrhQP7Kr9!I1rOXeM82GDARw7GZF0q4bIY$|MEj3K50H;mK5NveNR29hK9Etwyf>LVAxU0+joS2y9qgriW6;9j+a(9KAb#iYyyM_-!K1#2a%z;7lMH76=Zliz^* zxs<wh)d!^jj->5o4h?T16aX*$tv#Q*%E0K?R=3J zx~{`d2-rQ3EM>1%&sXe=_fiMlo2&)on?%5XKm*(Z_VTR*G|$3hjtm@P_nm|*A{heP zJ6WmYU%*TA8Zi++j?9i=O&62Kz~L;^G`PHo_;d36xXR!oM<%mtn9U;l+}|lmkZXQgYQ6qoI%|8?|Y2Q!}^X9 z8tbrY6ZK=ThN=~EOMOgZ%}`oKXa4#F@#%DN@LAg9=DH)U+Bgt)C!Rm zP9)}BrPBhPOgj63^ie4WT0oN_SMWU*s{SP6Wo_D8N)z9GeOcKib_d4al2ow*et8G~o`yi37rVT< zfk~c`<`d|q3r7};eOJ^7N7286>%|owvFydS0rjE zdOy}PxbEMGef8t+JJYq|VA62rB zVX1%0b*}y;BpYv9-n0-C_DhG`I~WhGvK^F;7=|WVYLiYD<@wYDyqDfW6cW}KnyARWc^iqZ4*niRS+j&5^502@Re+pBzJ9w4E zK|?r|kj}z(LLrbLj|yxbPxgdLt!JGt85Fkj1xltfs`bJI?00g3;=0ndzRuh_Azvtezbd)+l-afK zo~@b>Rpc%B)5c5<`*l>#Id?gf>*n|q*O+lN3$}HY*a4Izo=@6LK`|T!*FXZTKVK;VUK>AM zxFU!OPb!~_N;rPGuCilbYYlMaHn{q<_m@ixLQ-#uK1YF_SnN5m=ekvv=z$k!nCw>?Y(moMzlY#m~ z$V)uEW*FM)G@B1Qsr5;&%>HhmcoxlVN@YV8Q5^Z^d9VK3+@AYZR(=asog?f|?>2sq z++8+*Bw-c3SZ2#!b+VDw;jglc(mP$*87pv(?L9{QibkajJMBR<9F30GMW6l;&9eN% zhT4iUC!#mB6~(E`DMp!D|8_?KnOGI^HNMuLL^ZT`VV zUh=PP<*Yd7yO`AM;{MH+e`fodaP6YpPAjg}b?+U$=KE`(HD3jpoa@MAL2{^2 z*23hC*upt&{0d2u=s&C31BGy%g_Q{7vBEFVe(oYt&^V3+^>ra#C=`>*PGwhm{Klz+ z4LmhaA50DK{DTTo0LZ0-`)g|CM@jW^-$$VCIFuGSzwo+w1WuBPjPq}5D+BEhhf(!# zB1GQAm>VM8GXkUq)C>bAHTnKvwlzn>!Ka-C%)}SN(Vw2&OpB`LLSp7Zt_@FJT9e(f zoE8M0K)meSiDw&4CQlM}5f2KBkupXq0t=kV-#~E&whc_XxTOFGZ_3lPyYU3B-%QYSa zF5!nhCa-Y<9`0Wr7n}}jIO4Dw zsnn}j13jw*-b95(Ezg{gW*Sd&oKbz5xEuL>?e3xqJv05Q^ z{Dwop&8=Wq?dZ)j?S!6Uo>r=ey=0<=8}36tLt>%fYK5N%yq>`k3+vUiErOC_f1X*)+dLfUlRW;ZEwyhiw ztS5P;uROpyF2DJ%YN{vEu0%^OJo}Z-#thB$718wNGdJJYHkX@J(?z|4NWQ z$$$U0M?OpnDYeL3Ii!jU;Rd{O=4#aRh^H2!^4$4*6{sl9FO^XG=NyeP`C@5j!F(?; zGk~5u*aP@>m&%OYTA)8f3y9xUnp!#K(@y|edK2a__&X(+?g+un1Tf*apafcuNYN#T zFq~*E!13$ZS~vv`J8KbZE!t4vOFd?}Lup4GCIq;uP7FR}R^Z~z6=a6g%+lwx{(f82 zLx;|~{>{vBL}ld_6;HFUY=MJvINt)F!?Qr9%%KBQf1>HDC=c@Gk={e21KPLRve0i2 zSeIEVX&Cva8%eqjoS0%-e+Z?_?cdHu%8Z!i9#0;XLMR|%s`>Y;z8e5-5u5ef<>na@#qY~bpILmE=Z@YoR!?AeSu`yazjQRa-5;4Stc{Nt zdO?%Ltfg|!yPt>XWAz(dzjsEAM=1!h#H%EshtcvT23iir6H3U=^x;nEW#$5+&0hF8 zBN>ZtvFBoLLC#ic&U{3M8;D!-9#qH-c(Ro&7^=p?9D-0I0DJ`f6rL7fp)7-?Y*Bpn z`DmuXuMZPSg_8|`Y;R9tx+@B3gKaP&L}w}cb<*=f@@{IdhPnz2Ov1Jbv;vl12$5cq z8;|r$diHnd%%i96Qmb(Gx3?8ePH2^a`BC6bh$My5>#_+_srAu6j#R8yC+h92*c{Gy zjQyCBj7AVwoCQO_ZW0U-7g#<482>c@C0A`=$un8`_zLa1{LV8QBpw`=IqH6Vo8RZS z>^u~%yHNg~&P>3uCbKu&RS{=Y7WY}CG**7vOue_GcFrNXFsjtinLEYS$k5pSjV6zz z*<)k1)XSl;#+<_RFU^`hm#6!61Y`y}u;_y~^jlO`&E6sZ)O@+OYq0#KfOXA>I7-L3AsBRn0F74sl+6>AO121JY3$*4+IvSFSm(h$1wLM@$XAv}| zF@v5v+O*)AKCiZ!8elm26j&^Gj%YJ_exo_Tjo~gI!!d51xubnfrgl>5>i1l~*_QR8 zxRUQWG|G7lKgIC;)YwqPeXPU51>R8GLA~P#X*BtrG_P!$4SDe@LV1zk<9S87uL!zv zzCd~CbvQH6aY>_6t;Uc##79WJF|=K0l`4zcTJDu(7~c@ODD( zK;Uy|w$Rz94y9MMJkSaUnb#$9IiDrOi+=>xocvzsa||gc76Kj?R_*;N_&zZCakF*LyF#an#CppM zLMM`&kLTem!^{(8e%_yN*iN(0$qg!z#3<2hBCCSS&S$Ek!jHoUXwsx@390;=Hl#3Q zp0<;2JO+;`9_6#9&^)EiqPe=J5vc_I=DyRQv`VlP6g|DW`-TrYZg=#4a!G^91LOk{ zyiFE1vnpyE4hM+C*&$)Ssro#+^g|+y>=EH_BK$|_h!BYQUWt>Oflkx@L-6O&&xYsz z%s{)NZrH%yE@ggr(RxU>fAJL=IUteBga&?i3Jl{ZQ(J~(uqqWz9L(&H8%NEjRYLV{ zrwN!)Su%DF#wKM7=>tlAs*XdJ6pqH1N-G=UjE%zfh5Wp%@S7(rY^Hn9Z>Icuap+J( zrdK=;InXWwfcYlIL9N#&eK{y-2K7leo^Ga)LD`Q3G&|;b7%{G zPsc$1A}`2>$^lI;Bl*7+oyW;4B~rB&<>gBF88{fs)Y10NVGM?M28dP1@|KdSXZlyl zq24qChG%OXM%#AbkrZ?CJpNnxBVL8AQlB_Nncc2#a=}dE(JO;PWyHv+V2Q@M7Z{n3 z;wM!6kTWj|O~34??7a96#c2#WZ=l$%fKi^=RLGqU)3&_w^u9GbqLSS+L%2^(Y`4`-XkS1_lyiox>h}7acF;DgFMIn-Z$MN4VPvj&As-T`Zb187r$+Sqa`#pEDyQH!V8IBzKj~%m^Fwm@x=VcPGL&Z_1JaeMK zm3#xAXuh}7TTws(hiOCL@0lq|Y^FgfL%=(jMjz(GEUP*n=tO7K{sXW?t@0T$ zAq*0*;1&wI_Cw1QJ;h2G4hMGui;A!dR1(SDdiaH)8!QM>>Z7(ox;Y`T;oChKL2Li^ z&WENuhk$+KFS8hgL_Q&GsNVgQGj||;8HDvDlzi^{U+=LY9(t_IW&{Yp`tn&PzjeK7w&x{!FPl!0W_V@_WUK-2(@*< zxxDwF{_kwpZ+e9Zvr0%#knSUZ-P$24@H?pm1BVvA?FeZq9Gv=bU4>~?(nC+^ z)Q4q&G#~>p+ubffSM-Z63wU5M_0YZMBh+4Hc!rU{^m}miHkk;lK+>tokSnv>{xFo0 z;LJ*h=tu|R7s`(;0cGYUW-8Gl;NZ_=D^I| zk2o(orWd3z)^Ah~MxpcLYk6>u_KYt`2C%^QOA4xAK-+7j0lHAL;CjanbI69?KCnVw z8T#rdzz6_Q7|gx6>{vf@S_HbBP&)>J%3m)R#%eA(>KXzEm%&U?Smf0b-dt4h_a7p? zi0=k9q9=r;^bI`0ctfbcTNwOrV)>yL>W*GcgMPj2b?$&oS3G{%(VTIC9A5#u0_))r zVgEWfLi_b|B5I!|N0xEn(XUsaS504?Owium6TPa(_gWxxxfR(I%6dRwC_xA=8*50&Y?um_ z(}CQ*+CwhT&-7tLqT%wvWlX=FXqymL5fCNF075YtEKDaUpO69ZON9|6=uXgIT>AF- zs={w>G!x0O0UkzqNr8u%)1Z+k7ch9iz*cyU;e}gKAA6=^mI#rjk= zKBni!fP(jIbVBE~h1}Ej!J>$L6X?!3%qqkv6g(VTaj$g|cJw`nkiM1z2cqadl0y+g zks9VLa<06rXCh{qyEfdslPbKj3b|fE^2f9;EJoe?Gh#xRO|^KY?yHK-s#J~p;7mC( zpsJt9Z^L$X(mU#N zwhuc&%;boa1eqJ@Fv+k{$+FP`QLN%8EOd?DObxWRsOcTfg{JRd!(!DaIx=ru8@lP| z%Z}MGebRBT^~!cWabjW6;F)dp> z<^r=kC!(>?wV{~sV9ds?c1{-avB012^jtP~#T-xZL8jJK)(Tgdn-t0Rw2k&uj4o%~ zgKn*yq4Rui86`;fYyX!U8pspn*PZT|*0J2#(!Y#j27yr0SV$q!)DUX5=p+dryk#^l zOZ;b)P*UC0qOQd#1ak6!v}8NMsd$8!B~VW}c1C_D4(>EI{_{q`=d+H^6Uib$b}|`R zQ6Mdhb^2{^&Mhn9)o;MJ$8sGMN_@fm_}F9cm=`KkQsjvyCmhU(N(@^=TzgIw4B`>JEyMR5* zl0`at?``OARH;r$ayQPCUhK-{-gu0K8EdcMl-c7o{;d)C&4js zMBc@n4|^`hl@Gs`!`%!~7E^y5GHt9awJ&dS;9c^&-8F}C-R50midG+QZZw@a^wXOz zi%F+>0kMcJ)f+rrVy(06n&@NB^_=_IVb_H@v;k#jfd;K@QL6w);J)^PiZxI$uA1or z<{md~@ieEUDBY*tH&Ud<8#a;j-ooUcZ{jFc8CoErS%37hf6_6;P*5bqPc=+rVbO@u zy8-COEP%Y;VM6GRohKRsy73}G&jC>yBNX!)CTIdclLvHba^$a}bR(z;ng0#wmzsO2 z>3-3i#%{mw_|;^na{3~iq_=A9-{Sgx3v-M<|K@#^%w1E)g_HKJ=4a#;!qo9!2?A$~ zLwyMMcfgihAzhJF7C0*&+Avo*C)Y2VdDY!R$@qJ%=NC9;37U@XRJrWbrFbar@AuB) zmsrZ%mt?=Q(W!8jhYA$8Gp7t%B$L_mWGb1O%I_2yfOD(Amlzo-`g08=%{4}v! zv0ee;s1hw}9L-wz?z7(p&-f0ueV8kOdToe_?%odGNYz)jh!jvhLCXDgKfM7!*jdoR7E8Q9wLia!avY{ycbp%#rLvni6C-Tb>z@{8D1>ERyD+# zk0gFbMss+T3LXR7>+|r3pISBOKYZl#miwfi4Ar4WvO>{!P&y5jjHy$l=Ef`JLA z!iZZKW}R{k@FFX)v0f(RNPcL89GrD82R36sFMb&sL8_22VaM7mcdeH;d9eEDvh+?-fzV%$*ERX~cmGLX`y8Zk*~YHWz{o0VdI z`1r)y>}z-44Qi`>w6!T;YY*;0!q^aY2-TnvmIh*16FEccX#C+n(fOnR(+8~xv9S64 zT{tiJ8&-fJAOa&6Y8n zHwXDV-BgcyGO<|u2k^;TsXj;QR>SLBzggLwkwi2(Nkk-AG2b(|{jy;u&Rl<|8q1z7 zW@sQsDvVY+odOGBUZ`oZee@?OvN^6ez1etPuZ=B!ZZ_K5kztFU?OsROV-+8$)1dxP z^JP`c#i9q?mvY;111^?_=j@N`t;l5UO0Ou;W9)dDr^1_VAq4kYSZ231rug|OyX+a3 z#Tc6l-cWvC#?S8U%bPo`Xl5co;G)hr02dDL51&^=;Z!WM7c%puseHdYugCf zRERmA-H-!k#io0~%89??VdEmy*QoVTn^=Xpb)Z(EupF9(Qy*B=OX1CKtNt?Xgxb^) zz$zk-e^EznA2NtZkl}46Rtc2UFUGQFqcMEzVK~Z>2E8)~aK7x{3{e=eHR3#+6(r4~ zYldKzf(UQHJb$hGgkX@U&y2VHmu~#MZlKg+X9zTnrsfgRutI7cQ^OOavCGnz5i_%H zU`Q?dqcMV5yTh4|Ui_>=OICq74gAcGrt;J93aA22&*(BfV(uL%VbY%^kiVN9dx`>! zntc)k*TuDw_@PToo?vZy#Y+xGDY_4is>41PUSM_~+Yj17-ExHB#>%s_^P13gY34q( zY*LZ52XkES=74k%y*=o@=bMQ;^Biz)O4WQ3`3LI+sqweV^ zN9L0v3KduW(K46n)|J4DD{59LtdSA!rWcMR%$%0|vqA=TJ89y-m8MrzVWCXv9#|5H z34s>d8}KdsW`81SO6XO-tcBLXe1|Ib4Ti_~v}~Vr!0?vyt*0II$e)NyT~NBaG94W{ zg6_Ju$PllcoW9_eUiP;SBMm)^_H64q@h<$^l>Dm$m&;G6xSGU<(h`^nozEa7gQQn% z%T!<^O_}R`3d8qcrHG-6@tUErFL5n-e%~baALbm2o9K;9#8a+Fd|R~>UJ9odEc@-u zHgAuixRaKK)L-UYLe9u-Tl|c+;&;~<ncq-#6UTEu27x`)}PS8wst<^KB+n6qhu5t%z|SxJ+}8o<_YeNi}>IY;ZwS5 zgbkzU^XoGavqYa8@0iNn>B#){rlSM6~rBfUYO485E&8v>+i^V%sogIRUyZm{OXtO z9h>lwQmc)hS@5t>G9E)Pj>y+xw|}0o-_8k6M0<}Ot9W_C zcyPN^++%~=tu|ziISOZKEE|IpYc=nxUw8M@yEYf*WxNVXuRBk#O4*`p_vOaM9#+ve;S&Poq$Vyqe~AIZ1J5wy;z;3UUS%Il^x0<3jyUXm3{uEC$9wxdl zqWmmLMz_`{1)$UtVL6ev=GPNk;`^{EwUS&m!KXiuxqt*SgJRMEp^8YSb|h6Ov1bJ* zir%uR+7DpCN!@Z2bSoU|;@G=v-M10Jk5dKz8gV$SVWgoH-wbtDJudwxdqov7Ycn;J z+zKf4&BBi&s*oy;8ZVy~m}6$1!9rcv7GKCVheI_Pva~eh(c~?9i(yNd=5DDH_|`_4 zz++pjQTA)x%E8o3^c!$wSt1+{dL=vfdC22r7u#)9b#A}2bITH{lW6H%NF1WhqA7IM zdMM_)Ed6|pD-0@RcI8W(CaybtF0MjDrsVkdz+CgJsI##K3%%wE1f6g7aYH55I*W1= znN?9HFChm%|&@Up3e2mi#NirE^IENy`i zIK4CdbMvhQcjr~j4}9Qu2c{$+^ZNPNDmz$fcNx~%5yOs&Ot)x!i`3=X9C)3XkPz;C z2FO|d9e9WaI_ysQaJ=^$slbaKezEi;?lqI|8wQO9B;kIMuJ5Hu_sra}#>>@K5A3)` zCRR}4wS8qWFx=Nao5p_c9%cuOKEpaP*E8VNk>*65zi%X3eBjPJvwo{wn|q}hlOA0b z@2e7V;CaQWPxozm8oZ!~USV>mt?>){EE*U#LvR4_QI`Jl5qm$RQ6s;in*zKFW<@?6 zG5Ka-mig@QDyP^r)t0+1U-+)NE8$q+j#Jr%;bv{0k9Do8U*+pB-P3!Ctao0adkZeZ zg|dnqPl8EJ8Ri)qi41K)ifN{uDC;%*YU3MWQ2}qdw^puM^F}yrg5?pf!>b%#qUv4Q zV|8HWIDV7As*LBMBEmE4k2L!|G3 z7Rp0k01`?Easx&G>Hse-`2?$y|7qs`F6(RlxIF(6ni8T#@Z_tAZ13ao7*1hOZ}WRT zt9j+oS|9>6DTHPogoDjj9K(7GiGY$j>10>H{Qt$+cf~c8Zt)I+0)tXjR7z-y;z-0o zC!wfd0aQdpDJm*bLy?*Wf)o*gqN0LCMM0#A(n}&WASKeP1VZQ~gb>n`yP0$5+;gAq zdE^H?WPjgY>)+PO0b3W9(VOmvf`uS*uXNS>C*t+nF4KOUEL2$ls8(QuIu5=2<*~rL z_#HpLdZ|3HaRAu=Nn3#(%l1>g2dE6%q-`bxgpM-e?TzjRS=RVM@0KfUr?vIw_$iQ# zfe6QP&9A7K-Dh8D*`)0my;uG6tgiayHUkV|TTxD%mGVWuVCPn?PbxEIz{pCn3OOX; ztS0%Ieh0Pe9<JeXm`;TrVd4Co0V!LlSMt`2v7j7E9!0F7(=Pfo?s-HXQzkh9$p5Im7ob$=x zI}t0*#%2H&>F>bQyygG>t02K1DEfrkicgS}6sU4G3%-J5j>Dg4NP)6|$s~iNcXAVp;t8Huy-)VbAlkg6wgUY%!TH5e6DZ%WCu$2ZD9l zoDMSQCiv13yGSM=D(3Tz_Bvrnp}$o*HkH63%Uwl~Z{89w858*6!OQ6D;upwsR}srT zNvQz4;X}f(sF4KZ!_h68lBqFQdTd7ZCHF7CJ33^i6|!FzDgMhs^P{f*Uq0Q3*iCJN z+q@RktPs9Y=vuQ8ZDMuo?TbYz3WRT0`(E@zyhP!q5)u7zEa2t2Q9+JrZl1!F&$5>< z(;V$lJ!$^%aXJ>=H3br~a!b!QdoW38^sFgJjdHX{b$h^M>+$71v|f8ESi`4w{%W^{ zQs~_|1MS}@zc$~vef?9?r89yAF=5k>t6e^bAFpebLYwD+;REOa(Z#Nzy89$Y*t};Qsdbz@?@7NP4Gzx_wvBHY8!VS^~SH< zG5Bt(`_R=6Z97y&f2KrPp-PVxg6ewk`-&_U5Ca~QKJ+5UyGX};y^BY_3MUSe6`t9f zl}OGL_CO-V51g1&JN-~Cx$-^5=tqv%;zb}}qxQ-|%Z>5Y{78+`9!v4YY5>zn?G3uFB89O(0Py5q!;r)>Vfxis=fq?M2!`$KWR(EzmI~dO;6jhS* z2wkRh@)Yy8K^1qUM!RAx=sZ&visG?i+ig^JMJ46H8a1a-|I4bUTvJhAkEkLhfp)y| z0mUfPH>k^K_JVHp+=mq-?$USdWiU8MCXg;<(lBWDm;55*Vr1}|XrWH}Q}>or@Y+GO zQ?=T9U3_se|If%V{6)q0dXxyE2QIYGlYJ4SG+ri~GI=9Km&}(5q?OuVM_P`)cU|>L zTL$D0^k-{`@thNUMnU7b=9~uE1LH}0v`sm7DDZ5?U<7^tKy-HJhBiq_eg(&uxT*C&ZNpl;9R;(@>emGkp+eqSu0cTB z25hoAoEuy?h^N+3>sCfdf(^pH0m3NVX_=PN2;aK`t+gV9FKdh!hYKy|%pNnf2P$;m z-`?v*uXB*Y9`lpG>_+p6NcI`6_F&B&Ug)*YiWJ65N08yd)2g>|fgwHzafWekqxq~` z9!$~m+jZ%4pJUFCw401LXv;>vRJvSrvk`JX-Pm;Gede)k2?*S$&(aJ`SuR;-nHA#1 zXjyIkp{s!t{R?szP!caK-*2xbk3s@JJ1kxj(3jaXkYEyo;_QNMco3_W%SY{M1e`@} zgNh`=ZY-)@PW0*jv@Fc`u?SARtwIxAIq`5s;~O&2J7{vNqA9BcgIo&6a{6*v*i_!x z0cL4@jXd>CZK~#SUBdKO?=`zIF7}Ic6;o$uLUpUFU#j(+_S1}Cd&w0A(&%fL6I*8X zK&D3uSxv~#QV`~u()5i4d*n#WJA*jt?^MO-01#@_z04h$^=0KIla6-i(*4-Bg*~JI+fE8vd~=}b zOmjscfYxmB)ki3xi23vGoTXgIvKP@CZ*jDWT|;_1w*HoSVZ`Z84K(lEw9WstJ#8aN zL|222PnAKl8f&1PoH=V~WDvs%x5k!1&RMy83wN&w{gx-}zXi#B2=}xaL99HfHOUJ* z@DlE1`8G0-@K#D@dhT}2+P+!c_)>ix9HU>GvUeO>R^WwfFFXu+&uS{UG^zt(Ki7}aYiZ`0J>Mtf{ub(i7*dJnQftTz>H^p}k%-Jt9O z!-w0mXp~&j>0uIY#z33omyW3*p}6a4;}3iOUPSf(t1>?Obt}^u1gZKHi8ukH z7=4C2Gv#GK3Tmee+HNneC7Qwiw31-L|rT)6m^+-0r(F0040VRIaniD;Vc#l z&3z_?!>YPUyQr5Xsz8(DpT=q7GN<+$KM%xTW+YcEb!|&e94k!laQ&Hw!C8)3y8sAr zZ(zH(NvF10I~Oz(4J6>=FN&AwhSoV9!~P0Q0^gg?T~Hvs8e%1j>}7b|O* z@Q$FFNV(x;X5)~1+T>|C?1Sx%q8)4X#T#z}=03zr3$?DemNaSm0mEk+s>Va*nX;yq zTtMJ1>s)xhxIZW4SFirHR{L^%1`p3)C>AX2uOI~yKJkY8GyJRbxL6D_5Eb{otX`Z5 z(EEY)sG~ePe}HE+KnL1PoRC`xtDD4eSGY2TFF<}6=8>@%tKkM@1NB49l~XD`bAiN$ z80q&+@Pb!{tU9)OPVO1ZEy=%r@a3|F%=?s(!?@5YlEtqnnn2vOB#t%kKGS$yuHTsK zBWm{3PfFcy|EYtd%3o3ZoeA$fQvWCybG&?gnoV*)-(J~;4GPRZve1X10sAOwuZ!16MBG#de&fx3{Z7~}Bbbm8z_;MJ^tRf-{PO^meiFM0) zOpCGPaO<8AH*}Y4?P-FhAZliJ7NhQL)OkJFz_CRrTBF^*`0^g!?l z&C=%b2$8(3jVA4;oZI`9=DZ1$|=!vi^e~iFHdaaLT?n%9P`a@jAf=9gZoRLIa z-_!6At&zyNwSdG~t^3_a9{T%<2Z~=dx$+KY82b_sEWP&*4bfj_9E+{~d9(-@itWYa z@Nx`lX~{Bm{PI!VYTycM8;}5;Q7?We>3{j?w4Hb5i-AHiutdo%>weOMPP#zSb?MSQ zq+V-w0XbvvTdv<}bb`InWg0hARSK~>hh>e6Ct`4X?NWXd^4ETn;O6=FzEhS~8|UXT zPucu6M0pw~*19J&GiX~vai4eg>0AZ748SEc*?YL^dA?NKoiyux+i8-Av_IC`$@ZAu ze&@+OqG?-8_GmPv^*dpHStA-A+wYT`a>A}X&#e{2yR#Z*$57m5Hh?m0|EJf$IyQO( zHgf>L4<6czE&=rv2P}``S&ZW>gq7#u>&(V;hteYF2`WkH!A;#KJ6)5e$7C+GSV3;L z@}-#e3|tErwEo$O?~Ot9j14AOBiFoLM^7|4Rpb%tgdYk93<{B>R^0pX`ezlay^-sO zMh!~jGPcFKh`)cf1*M{9LX0|prY<1+?Se-dXTd1yW1@o2$0|L2kdm}z+3XJ*8dN5= z4dd6iEi$(6H0Fd~k?}5))Tsrv@{Cl|k+>A)VYKfRAaA1b_WLM(#;KOP3j-BCl)^FN zl>FxeOb;A?=-U6Zht>L_2yQF;YrL6nLxmcwhB1H+7jj9$^EK0j|7qp*Ecc zi7RD7$GPEyjc@0*H_Vh3r3l ztnj?iskE!DRtpod#UWos=dCU*tbMl6=-!nsn-|jmNp-r#U!-zZG#&5ixY%ngDv_ThmEN`&Xqx$q1e>f?&cr zUQp~`h|e(tP<8j2+*Z13TxFQfxSZT&JKzSqKUUEGTdJA@r&=jb`sa*%0W2L$?kzJQ zviUqBp7K^UVoqb|4EDq{St<7dGUUd<6^!9@g8u#N5Mb3%$X}XARB2_8Rb2AB4x@6+-J3K?%toFsC}oe{l5k(2w=UDj&AsJ8PsVV4Ot6G6`)WtMK04E=wI$IZ^| znZaE~X`T@J>))_DC;*DSae*(F`J73p+Qi~N+M3OIGi@ic=57BAgz9&ia~t|8-`aIE zGQ-9r(uACJG&{_|4Gj!Byu#j>Q3Nqonv-NLSKN6ovm?ifSJeP|mQJWqqgQ&qm-bq> zg*){E75*igoaRo&H|0JHY5G_+9XIjbnhT^g*Q@HJBW8Ty3HJL`nkugWQ_IMm7+zV@ z4(FQ5d%xm(&yo|~9=E&yx@q`Q&vQLPx9YO{+H!ZVmj$?{;R-;L&qA_b?>aw!u^9c?c$yyb8fl08kVo6pHO z0N^Uu_Ul;Q4`)wXRD#UNbr$6~&>TXxx3*D63DgV(Lok&lL{aO~4pVXPwdUQbvAiEs zc39po1!AV@_eb`qCa*MWvyZvUqZN>&?1ddNb^>mh%!MKz2bl&gUsaG3|KQM0lkuaJ<9| zBSONETfnJcH}+2_yDo3fi?L|%>ZA-hydk{6N?-3Pl${1OifGbQ)f+LL5uvLG1#I`d z-NF2BV8YJf`?i0%mc*O+QvLz|HagqXpqzQY=)YDv&+QHG%T_%(nMr5o7jay;(W zmCy;!TE*7vhAe4o`Hj`=qPiT^w!<+JvWa|*rK(p*t71&;_4xEa+OO>GuA`O-N7fT! zFgyJ6F)4vRy;T;}CvE~tVwLg8e!om0$3sr=((A0~Fg}UTuS@|&`CnE$yJb1*V|O6= z`C<9ftyX&($}Y>7U_+rjseZ2+O<^|=Ry?U>J%@K20CtdZY~T07BgV}q>=Nom{Aqc) zM@B!O&{qAUS7iVN-wS-VkM9oj4Mhkw(LS^_w+2ZfRalz zg9Y8h@&OS-sj|cdg%yl%R~GmEMnO&Gb}v z;m9ClaJ))*<1m{v+SqOgT2%-dw9J$t)q!bQ0K#Ydn%0ng4~SdPQf&+YuHE?ZWV2>b zIKir#jOiE6AcQ7jk{@GKs85$LA_|tcm50h@W4GmLX(7!VXv#Td9Tcr5O)h_%swq@P#EzV?b&;a8>bM7rGv!e zv5{}9<5XAg}T=o4H<34tRus>M(ld}tnA?*<<|Ndw=u0GrcLJ@ zy0VKnNAlY>L9Qd0KP@<;6Wke0>_C)qpu|ShOSv7bns_L60FhsanhNMuRQ6CzMYLa@Oz+0g-%?+h zu1#LzZlk0DVA0>r+!UWTDzI&MuzhVu>!DrZyNA~~V-Eu$rUE2k=*CULP+TgVdXGSh-##}ElQ zEAVb_*A?H|SgVTh$y5LwETrxxT|_KsoJ>wxe&1m{Fi1uOF?dEV)+Wpa;=kq=05xF4zU{@)KwSux+}YL4b<6% zPvXs%Zq?urrAQacwaH0iT<4e0*nf_r;YZN+rRkV4TlawNU^OaUC}^Yd$EimKOEf=+ z^y#V{Znj4*IB0{>?Dkyes;JD%fj6Jp787m5e8D)cUcV%M%w4tMshez>$p1X^uY0$# z79Z9+{SKLZJIJ5M3J#G5*dRN#KkY+OSL3opYchM7B3k+O2Br$mQG>++BmdSezGo$+qUCCuN?Rv0t!HL^6Z^B;B zsGO!0x-NB$?7$wquWch;Vv`|$=$X+{?MIu~a;+wWuc7lE)7In3K$hwiS>f{FS*PWT z@$sgM6?3~qM7g(RAKTlwuKluVB4qi!0~}hcvFXOA(w?&=3I^YXYB!))-eHmp6Zb{r$aMDA^`N zAXtqc+#1&P?I1*;yl89i`?*lk2=k5-|F?SZ5Tk3!aJY?}wb*V}-9G@2B~qc?HuYP-Dzx_J_gn}t^&-{Hrd zMGIzEi0!(Z_U)dTfoGVPTMY!PyMGCwGs_huNEM(w*T510YpqVICVc)=x?R@#N9_X}N*LRWolc@gh7N1;|G<6=_ zO3^_orI`3}XAJOF)95A-Di<&0R-Axe@O*JHz9iRd*-HUs<;*_EZMJEN7AF2|d=p)a z<`K}DsPpMnYwNktx(v({rP^=O>;u#QzwO){>N{1JDHGWl9Uq@1MfOV-x}#wQo-5Zy zxAm-G8iqGPdqNQhQC{7=k99n<*5b>^Rf6yX!Tp#ttK&5FMSfAzY1H?ic>8<0B`=*} z9ij+H8Nr|UuTM+?6qq>yeo+F8Z&X5M|0D_dBv}2mX8exXgQ0|h0lP&^Bum-QgZS<( z4}2SC!Pu1RjY-LNFCv1o7qnFW15#q6vs?b~9L=rxehVN1N2o|5oP~u!M^xQDR4LK~ zE#Tsa_XPE?==xc!UV6wY{A>PAY7VfDy6>4z$7pW|_?9LYn9#iw(<7B5?m?R#8& zq(m}#6zqEw^K~4Qh*7<5_P|deq!ma3#+(TB^*!bLi+g0)dO^mj8mzX`fQ)5F9 zPZYm3rx0l&K0i`gdpCs9q+OWu{_e<`+{ckalpBfVp9ValnsSSlHT-KN<(AY(wmJc+ zUAd6NY|(9&$~=4AN=R|dT!#mtYxWT(T0F|tRp1I2L3Xkg3)})Nl9}Z~aW-gh-Sdaw zcRMkwcBU#6DKhuxX)3=1nI>cj;8#EExxY{hgZ$x~yIS972U0Fj0GI!R-F@GP=!&Cf zJW==lY>Xd69Oc!{n!(->nujs~C0n=i)9)mA9(m=q@0W`jnQ#cF8)H{gz3|0rrI`1# z_yMROBbv_y{^LMR>iG^5uah*k<|GAKdy09G9%zD0H7ON(qBlI#icyUGx4$R15;p(j zfg%R+4zg2sew@D&R=n;xm|hp9JoP%MMX_$~bmP4Tpand$j=HStk0xuQKGgj9e9pzP zl%&55u_T7HuJP}tixeeT8NHCnDOe)SRff@3A`W>HXtzWv|i z10tYdFj@G)K#?kMQl+_I+FdX$nSS7BfB-6F6S2HG%-Z<4l+T=ua8;%zNjoU?`^;yi z4}Qbv#=xN|fhKfKvp31&fvoOx0U0d#P_yUWSmhr|e=g4Rsc7MDH^R(`^wUtm6cl$i za0%W7auaIoeR$NJb z+%}!=jw|fgj;fjh9t5$*{lHO7K*i$ir=&gzKC_`Bj{VN-!m1SO#=yG9A2l8T`Q_R+CUuXrlvJ7N8;q~LthjdMzx#fQ zmT2wZYEtO33OOYVzvx;~VaK}&M+{HO$HvzvSgxF_^#KTe{1r25aEEq5(IMyjG8pnAiCjVJO>sfV?4tSe59^cDbRpFIKk!?^ z*+;q^{hrK|21eFk{*|b;W)&12S>WHvjV8Q>u!+JcqckaEbQYt{FAQqPHlS7l^x4O0 zwxIkW>D=_zGQmr~BP74f63VnN3qBBhwt6)5quW1SJ4Ekj?Wvo^Q=(3)%~CeFXvUkt z+Lon9liCnkVp}ehKW`T=B(^rkZ)wRGU(Y#0@}As3a~BMMt@Ha+D^)0<+OxKe1Gr9? zqlOFzGCW;Ix3X6KvZ0)8=yjHD_Vus|#Z(uKhQPX4Uqactbx2P#!(L8m(3Sy@SxpbUqTKIb!Gynx|Lcf&>6D&*a{BZDhL z+i*tB`+fKueE|gvTeTF`V2CND3Pf*Nd3FmsXRqQ(tKr416o0OMrm%DoBV=J}q@cRv zq}4bB-`8o3XWkV)(oZJ0Qa)7K?+wPbcSSVZ4d4rNtVs2z575gvDc(sTM6l}W5x2YD zud=`2=qkg{2h3&sxY2Zc8YEbauYq;SIbEw58aaC{h8j*V$stvawpU-H7OmKJPr0okwWj$V0%RSs!RX;Sn<#MR}|8*IxUx;*ZcsWqOSM-g*=v<&O2>8KZinV3}I+uTRN6M?`aV+Yv21v z;fko${D#kh?p=@+nxXw+al(qU=}B=*o;R$`0AgrWMcG>QDkv?$7hK223x!Y3(;n)U&< zEbo>Kd`S1(6N0W_q?kW4(SB%WYpM?8xdulNGkZ=W8JSwM60@E)@!4$2K-i=W(aV$= zCAu*( z!#V5Qlw!r^6c6I#dun5Z(|}f;c9DPm>=loR|)u48A2V9kH6d#rO-C#pt1zoqQu@ecty#(mq*G&2I z_U@*%L|)Ho$|_F80aR8s4^Qzm6N#v53V5@f91MN0zh|TPtX&PDv=rZnQ=xbgdf02R z%CfUr_iuy|XK#)VniYfyzt5|M{78orr%dF=;k9A;t4+I9k389-{H1nEg?cTZXXk4= z?iw>V4LiK)R)?C&XGE?y#oO`R^Z&$O`~`{n^2Lnq$c>HC%3XWxRC{|6_51K1Uk=@` zi%UIZAMumiW{>)|ydxXaG)*-PxXd!@0z0m@-u{U8i;dcu_ryX?LM&QMR_x)AvuDh= z$oG6SE8Dtdtz?-KRC7^%RZ}9{B~ff2;<51=#=?a2tKip|rziDi{i-S66L$*Sms>8T z84p#qYI@#LS$d&x+cs10WV&8TQyaQ${u_9q?>Z$&*dz;+=6&7Un-kN=St!?k#yc9@ zsqWjdqdL&wv1<3AQk3dj^Iw|!vGx~q&bx3u{r$bw2R!|dYv&=iJ?;Jit4W%}kAWDk z>31#qfIYP*`cq@NuXh0LL^IAP;t8zxE^_TUNzSBX1wTO8P)v@heK`ZG;$f|xJ5Ury zVHoHl&x~~U#Qx9@rVOm6GCYK<`qXGG32ammK41`TGIM7{i5riOm?ih)%eYTn;B^B& zESZ%rG7AINDjh$8bAR~cUysLJ)^LYJfrcC_iif1aNvlXC;{Kn`^{r=**i$AO+jfF% zyFBDa71Tm4wn&PJ#F<~{w>H_pkQSlgGlAHw1ADn@VZX)a&FU?{2=fk2I%`5{Z2>Uc z>x*PY)UTqaolsi`FTJ)h{{~e>N?J(BIogX~dW8;Njn*d#w?(%e%Hz1Ezc}d1urR8k zSrpZ*R8N+CW<0o_JR7T{{pgGNDiYvWs&721_Qo-jxvPg+@x6KD86ixB?#u?_)zla6pP{v(5oo$lZbk2!<78R8&z|V0A}SJ> z|2^x(vL3m+({yInzpgqRNMrHHo1*!}MyTPm4NQW1m-7W`A9<|1(inK7P}1};kp@X? zRi}d0uzs7M8!#JH(aQBG5*WwL|4-2EQNlt|kOVQfBwy)h@b|98iHR$a~wj z{MUuInz*z~DwjP38&_9DUWs*)8m;fxAEtq6*k>6nhpyUfr=d5Pk}MUu)j!T<=ykS7 z;T*w6rJ2JDzD1>{DMX{6X+8rT1M8JRPI@C}Hu?q)#3N^1x<(&-ZnN3J7%%|q#cP~E zUelPPgo!S=cqkaUOZe`qsq4?!Vl#7d=ZRq1_D8aw(jhGayr>ivbgWV6x&*SM#UJVG zg5>Ayi&buNCUTnI<+BSt`1jmKXs2Q5f$su>naDgWSKjpW_=cm*4VN&6051vM??mOk z9>v#&ca!+gy2u`mA2bXV_!}?(;q3eDE-af+vm4C$G8vh7P~^^ftx z#`|?~|2&Zt%`N#hVyZ;;Ya-%aP)+K7#+?iM9XK+j>Yot2@h>u25j-9AtOh@xz(4ye z1oV*!8V`Pxrf}`aIU&nw{e@{itiSS*NSoJQ8#~AyYgSl0k=H?7;+W%~?PQrtL}g#L zmldo6;Cr?5oS*Dg*=>$l><-TrPKuF4gFC<9UB$)Fwbe`K5qfd8@ibGbTqbW_}r2KQ{kVR+W_0@!HkEqiqB7FRKtzGlQQPh<3 zmY|jKxjYh_r?NTJI9B+Y3uq8e69ljTlb~ zIKz{PW%cj*^)AsQ>}-Cd3st zb3ZRvWZN=zQGj()-RHQ5&KvXWnc5!RF@N_?7x(sWqAkv+>{(7eXPSE(pc?ql;gigM z_wZ{vHD`rM(l5SFd7kI*kGDs2dXp*uzr`x93QDp$#^4WPb*HH+c7JdMYO|XJWU}VS zYfavjyaU4b$ri$Wv^CJ%{;u8sYP=NnA9w|I>z|S~hP%K5QRlOw;R+98)yxt< zLunn;5nr;dt)Fu}UR;)JMI5Uws45`k#7u}1vq9M<3&WVenGz7PZR!y;G8SP2%I)H` z9MdVoF-kjC{Q^Qiv`gMECyAN=@kh3jLPE~mW35j}JeqIp65DLgRhi#X;lspMs9l$>X56C=+e9(J3#M+ARh4 zM58dhxoS6hPf#8*b^SRJ8G3a=MgW*`!5{Pv_l{4TU;f^b%iE$|X`qNwC=;AnnR&f& zW)0g_LNy##+GV>5IW}9^RHC_XGSuF(V1Z*H6Zvb^(6;q}#I}S#iy|AIR-Q8t>Ur#t z!k~JpcK_7WRu?t&`-E6T0tq$(&jz z6vY~KXWGHoaG8^1Q0K+If0>u>yOy;m*&4Z)I)*{5XDbp>8{gCKs=Xy~fW+nC1=K<= z%6sm{wDFc_a1nOHwMYBoY%gU&!f5yFg#C*T|N41ulCV}^={pXnFf0-sz@eV5icRv%$SxPHo9j!oR=yLh=&@F4Gk8SPEy*c%J4*N3vF zN-|_`I;zzf+71zdIdaE}e||9ruB-3KP(0hB?O*^(K*!=IN*9bLtx&Dc9PxjZ9}=lb zt{bLrikDlp36Ya0v@uX%z077OkKlPXf6`Wia)*i17+#?nj{*=)-G&D$B5h&&1e{`F zY%0N5 zw5&C${^xoCukYh1`=6B;%ppY#Nm#>vs^#LEL4fwTw^Qz0(Myzx23>InN$ZeKQ7?Jioal;$!K#}%#K&IjPFx*UOca?AEdG3fj0{Ed$Mr{={MK?dQgtra+{W{ z;3BPssT%vk=MCjchFANich+Od!3K$Tdg+3>w)P!z5{uvOt-Mhp2#+r1+ooHZUS7=j zPE5iwI+v?Q3W}3CnUneK1W#M{4rHKSF{5xQp)(>wS4}M*95D7gszdV{UE->n&C^X7 zy;+LmlYdx9+CanvB_M8z>zz}}$%IcEpe!ax9^Gqz>|NbsIu*`SB{5nL!Ezpq;ly?KZU!0N6WwW=G40_(`=+R`RHzv=H(n zGlyJr{BT4P%3({85T}_w0>~G|dyMLsTz$jW$y0Ji}a|-d)kzc@Jmy(DEsCvuSYl20P;(2Mb zKfp4uUYQ!qr<1Z@GM7MHc>YYQ>A!i(<^$H2j9xKOQ&-znxpS+Vwf||eS}8^Ss+Z?< z8;WlduyWm4^v?XWjuy_q+ZgsbIANlwB>Xva_HzZr@|bHNq( zL*Xiz>Qc+8&|U7fzqAVaMW2ng`s~mkO=;KsCRm{gR?KmMfBvftss)V686FfFhK{GC z9m+gFv6=@g3bd*X5ZM5Vmg*si=;p8w>X|{&Q9J>lS?#|794Wer#Rqiy={Idd;#FK^ zs-3TW03rujv7Z#^{0KLI9CT$Y&ms&3U!3Z^^JYwbxY6GX{xxVP{g9rct(GTJBpdtl zOIj9EhShV}48Q&f{QLAmKXv}NcJ39U!t1BE$h(rP(Bz_)!Z%Fj7i~444pG(BsOCo1 zjjxkrz2_lRfSIH^JaW|Euhym)2BSMdM#a9m64~&b>bXEPShGV48M*rp2l`_dTLfh#APc;%weLST8mIw= zDH0DbH<7-JPlXfbwHoq{T5D4q_C0O1z#RQqb#xY#R&;pw1JroNWlgUm?{tC74u+QM z@{SBWibY3KXYE|4Rl&*rNlzH-^5FHE;_8tMq1@79wgK3v@bq5~ZQqu}-0A{pjl~Vs z?x;Q+)N8ni^BQdV;~jY4gC_M3*EE9B4~cUb(gyVzpFoj|$DKOr6Tpyh^Pta-DU~g8 zDdWI*`^}K!JgmRwX;(!0I%2R91;m7|y2)aDhW4zry4q+ZP4gPMluuZCA;bIgvajpE zB_ZTRLu=H!mlN2H?OXYKx!q@+Ki#ZELURk6WPngdJmE>_xo`GIF|^5w>ze;O3_H;+ z{PY~c8(EG|PT`OjQloHO4r<2AeUn#r3P-Y?vT)TsPiMRnD=U4IKe z>Q7AfCxHV~CnVr8O+^@*D?AJ4_5`1Y^gZSM!0>mhVPk_JR=6nr9RY;&tJ5DRYQ`Bw(ONSkBp5L%6)(dpQSakVh(jyCSMp~6CdM49MQp$%cc*$_ z*ISc{B|1^CM0|<)_?DYhBQK_*iYCGI8`zVIp$}{r9!1ofzUV&V1!>b~0Y#~e2ihDB-X+V)?iEQlx(qwPsosl_ zqnB($oM~<0SoT!piAU8&Z}C&%RbE0O*9Qy~^%5A)4%ny4IN6a0zb38!GH6v4HR_g| zO74wwL!VinIeNwCqTBc+)LowG<%bcaK8ZlsZ_7)eR)2l1Cov}r^% z^<iv)G)PPFkwCK-yc~%q4w|zf(#?QMhsu zTDjV9D#7`7h8uzWYWY+L_yoxj7TKHe(rlgiB`a#F&5#rIZZdD@pc{NSiwc_lcv&dX zX#|kTIvVwoTQ6D&Oa0k&#LZlX+-yg{|Dt zi5~LU6vP=Eg=eM%m09B|?Uy85ji^Z>Py=?=wmc$uhw`Zn?r@#^UzFdQjuzE;Z$6kr z`YAc9tcbLKo<&l#|y8`*3%;Ts$k7Q8B=j@3H2X6@d*n+j{3~SKTuWJ34zuDAkA$wI1T;+{# zxr*j|%L(RRYm#GEmgCZGfE+y?2$hgzqHHSDg z`gEle#{|>OXkM-m7T7yYFz)PR#THjF$H_ai!pM}JB)@yXF4-F(-Oy6z5C;c#W zXZZ!$w8jYH0}C|p-BRpU*sAsadz02gj*_NZm_3E^0u<$hJs%#nOXTd*=ET^fm@PI} zm}0!_0iqOf)#|hjSzSM^P04aO%uw?BBf^}e z<$yv&e^+95Jd`)tPF-!fDrqwyzF#-IE!#mErE~U)^$V>RZfbWC5;Ki%iKS>>^E(pw-@u)nI{eS?DBao(fS9fa}d$Fn&64Asid}_)OKRO{Y^~}(%j~b$%{#4B9^l&et zeTP(~E_}}(W;>97DMe!yRViyZ*Fk21e@4PBJ(0+c+55^qh;`3XzvwEdiq|^S)e?o# z+}~*0!bWsc|FW%_wLc7WlXn|_r{e~m1rF|6O5{gt{|=)q%#MczG8~U{YqA;4A~1NI za|@;GYtKq{f{0DVla(h$)o@9VNPQTw=zld^LtBZ}Yh3_~p}P6cZ#GzR1u!+3zGyrt zIzeTnQ1{pk2QFMtpQ)oFsbnB(yir8mQ-HbNmo6utCNq{l7CP#eG>F?MYp2@gmkVkt zSzHz{xySE$M7Xv&fahFv_~|e@Y!8MQqjeMXV%=hM)*Hc|YrOmIspPBL1;^s+k+krJ z!P=RV?PX~(6TG!_Jc1UrVK(t6W{y(&ydmtwnrrf(m{XL}d%J8zRbRBjK4n4CxHsF# z@z&`A;0>bT@E5lRZqeTqvFo1PwMSHTY1&3mJk08TN}iEO*IECOEjj;Q`#AfNu4>hv z-R)+@-xk~gznz;T1!wL-EO~%xCb+o#RK+b%>H;}lFFF7sWE283leCZ?cdzw)ZVkk8 zM7!tS&{P1IeN~uX1c=!;7Zx`#WwAEoI)~|9#o^7mYf@`ZiZ#uz^4GRt?rwOB)g7+A z%HG?JMvo7fX`^&z2239Z_7e}_4%58s;UlXFJlIjnJOi8;5{M$OcSjuJiDNQ9GC zu}{MNN2ZzbSM-tY1>7nxe^o3ONRJ`wnaTr9v(RxSLUDCdfqxPE-e^N9BK9z5rKo{8 zu^Ar6~0SqBiON@p7 zBZw_VsVK_>&Cx|nO_N+~-)37WPy4RsDMDKGm{!O{D`aY@2IV03 z6BH-mbIQyp?=I5!Psq?Te#y}cbrKXhP!=}8DV@LE_kT!x^Khv5_kTQ^GEx{S+b|gA zoKw*xW1q2wj)OWXM7F7vok6xCODM~bQ)I1lNX9Zr$ewA2YzZ^6jb$1#w!tvQ_Io*> z_xics@9*`wuHRp{Uh}%f>wZ4(`+47w`*A;Bc24yHXX9Dz1zncH4v>8pnI@8VuRkXW zURtF1St8@Ui1>NuKq9>taKjJs*lg81yQUml2bE zA|sixvEf@*yIT)Fl~*mYwGMCGvuawLRNm=gDlz7hflG_q%agR7!ThVk^N&SdbMO0E zi-|3#iI@kIY?S^HItC5)y-X5UkWh8Z317Z7fZ1P^r#nK%-G4q6?Htp;O6vGyB=vxR zf_xEUBu*8__K{C;-8r+r<#W@maK^0dkpv^b-zJeQaWsK)ak*+Uk?^9MdK>E0R&AZ4 zPr+XgsB798IsW{QDWOXbzAsz&Dmf6oKgnEq(X=JFd$*N* zwf5P2Xz;PEC4>lmzQrmJtT!p`%Fwi@+o8Ao7&9fNL!s< ziNZR5o$PW>ar_N5tDu#}Bh)7RX=6HXQ8gE{VPe=)J4tRgY6TbUt9YLhr19v8JkR3C zVquuPtqpADjdBt01sn~$z0Uj1t@6$%S#@JSg?$o%sjwttlC{U~FuBfd-DbEjeF_v2 z@8JX;%&b$Ta+ko=3EO6%C{Z%j6x{ggv8b`(N6-xd;KMLjHE!9js3dHMp~Nt6nt#)d z$9yN2`0T*$d2TbN^m6Arxp>9hyJL&JCJjv<5!rdWThok!gqweFR8~mwr@G{TCt^qU z0NopSHX~+akCu@Bu(M11oA*&ztPTI;xjhEM(+enok)Udobmqx3s1*Mm$edAhVy{iL z7<%-Vb><$?D2?Mkbf^?AZ#C>#ng1`2_*X0r68#B)h+iE6Dd|3PI1($h1un=juhQWB zz7%y2@wB%}>kDF9R0{5K!HGIs+B0N38`i!eT2PKO!G4ZRYt=&Hh60vuC1)B%ngpy8 zN-g3g!z$1wAhd_Ac*7?3w(gEh;zgw87yd4nDdbERxuM-NA^T2Jw%GIA*T>EVGK zHK5QYO@H5z^aB8zn;jFL;4vw>+WC;}-e<6SVG-(tMN}U|i9KRcC1507F2ZJM%Fylf zd-ON*J^jb8577lTw$|+DdUWU!D{pawRhBz{Zd2!UfUZAxdrf9ynAc4SjV5n;kh zNs2fCkF07o^n35 zOx3{df*2uk8zCKMs>iFZhO7?QsOAl}z4P&&qK!>ACPw1uvNgNY8H=`CoZR`aK<_3T zyVoRZbcB3L0UyC_kg%#>c=Ji4n(YDeFaOjOv2W@0@!ggD#p_!fI=z9L*fUziopD>R z9of4fJVWrNk_5hI!Z0{oSRxiKQG`(ZJK7Z893*OcL4P^eAi-3wNt<&yX84DD#8c7K z6@+f*VFh^+kx|jq_;{~#0)IZ6mZ9Jnk0$C>Q^Gh$yb*b89c2eEBHv&bih1Ix5<@kERJd*XF~R?Nu00oTzIfsOA})Mu3WnZ_7NOJD_c&YZ zVk?|GdoR&a4m3BSZ+-lR&0ERA<(Dlzv-z?iwa`sI| zVsAW=S4^{dEb{whSxj#A_lj%f$@RYHlfbo*P>f?(33d()=X`JKOiZrLfkaRNQD=Kv zuck==^KH*{SlHidZw^U;Y>s!=jg%26FI zf7JDMC&-~mwFrMT?!3=0kL*X*pV2od`Voq^0V-#72*;`nIUNtUyaE0t8ozFRxmKQ%)=Va3B&tNNL>irgBVbAeaLeFZEZ3G@B@` zR0!@1X}?t;c%byzQ+aVC;c$=b_fRT1HD9QOE^#jC6;Lj?;;?6bZ*?G);`4-Z*@XEc zH%hIb9&_Lz7H;V*tVR1u2kaj%^k1JAh+3M8pwEJBiKRfe`kAKa^zkQJ^bt9M?gx+^ z3|cfPGs|wNm58oc6{0v_vaaRoU7ig zhsn~8ULTT5wc9`IMjwm)XSn$9&)Qt9KYp3cP2T_GYvcaq--o|cS9ip?KF0bZXCT3u zpAWofwHYfWLrxXN!2A-lxiYMl4rh%qY3?kou+G&^-1P9y5{g{?Lw>C}TI;1{WI(^Z z6}q#%D5bZa{L*MnCUWC+z|{l;%4?w6?9(^hjB#3Oj{4Mla+W+>*empiQ(+V7!+-Y~ zEm?fOK7VIchjizGKP@tvUz)y-QPt|V%MXNEsi;gsOPvHieo)?8GMJ696Q9}{sq}ES zrv&>e@Xi@tdK>9aJedN8-BFe=zvb7VjQ+Os4=&r^y!3zX=l?mE#unAj zR6!N!xIH$C^i1&ZxGo@rU?NrhsHT)1*3Z8&nTn0P^z6ojut>Ok&nU}r{rZT1#~ClF z|BGKoH@9DSt{csH|6WMYELc)Y#)JxnK6)jiO-o8Zni!{i3a*iyCSP-G3wFPrFUd?| zl+fGFgjdoi!GXRyR1(d>OM5jXADSLP=(R5=I~N>;hsKVzdEusde-v3+AK6Lb)=@r) zc&0x94U0;@x66jBW&@`%>BuS~Iz7_iM#j7?b zcCsmjXH9zsr{6R(nkn1X^l+;49|5EcNiV8`TT}VEBi&MelG~nh-}5Kqf6Fue%OB}U z`#?%0vppb8Chwa{yG{a9OI%ChC$TvK5fP6;#6i3tK%8FTp9mS2Cj%6W;Q)!=_)*8s zE@{0DcRBw|tNcP~(oX*{ynxg7r)m@rp;5j0K2Pg=#nOc(N?dBZCY9>hcVolO>bTHv z!l(I|&k;;qdd&wrQx#AKbviMGHNu27)*d8tr4k1;s`3M}c?kt`-*`#N35 zl>kRLfVEXvB^e`1647`hlR>^sP`8u8;AlyU6A*&eVLn0dKbr6Vk2p4N48H_6lWXDm z44pue8pnUF8Fyh^Q#xq}AIkK=SOV>k6@>CZ7)c$wO7Kvbr@F+JAj%OI>LfMS7?#01 z8CB&gEbE>RMwd4C%f1{_50RHBeIe(xa;Ng1JD?vqPuWUge6uw>XX8mS8Fjv)M!{QL zd_HjGYfQsyQ&XsnGP#~xT=|}L`#Ntxa#@}Sp&a={8FR-4Rz+ZCL*Jn^j_KgiGOD8S zo5s^nbeoy+>53nKlP@d2{mLZcgf*5Pm=&MoUT3UN$$su!+l6l z84p&~k8`TXWqI-hWdvyO*yv#=liW)0z~gjfganz7v_|fG7!+q09uLy^G;*ZkXEWeo z2z<2gB$A&C#+XOMW0EBWY8=ME%fAX=lkr=-1sf{0vJ3!YJw#0%r#O`wt|KDaEI_F@ z)MUkO7cb2h^5O@UUrhA>6rvxRJcrBY`-Q0a{tR|_Sf2t?^jJ+mG6y*s^r#|(iU}e5 z@09n@*ifdi3y#S%mw$Wh9r3oMuxoKoPAC|uvKr{wae{Iar={$futD(g;ie@@@3A-Q z-ZXA!;%z4hZaDwnjz8`^*76dP9q66j@9U`19d~TL?AzJK$KL!unQ>pVrr7r75JsKm zg>*vsy!u?cwxweLd~G_SOW`g6*UiOOH3Idhte(bKE#!Q6`z;*PfmDm$8XE<-ZB`>d zO7j0F7Zp!#1}e<3Q4hZAtEu>>C>flHp|7;b5n`4`+L1BhIxIlgn zSP}zcoC&A8dF$agEheZ7VO>{Q6O^nGuI4!nJaJG-y)<5A1zithrt`_cKymQy);~+A z|JNC$ED;6d;nequZtsN}K`6l#!6*%bFNk>6s8x*0;;V9u+L35kM1JkVC#@g7RkoJ= zN|Un&zhH_`AFl&j_JvUeoT*mrvHb=gE>`=FCe5h0x7=Ta;9>`Oylc4|*{rV_8#jv| zo)mBrcAoOSf3n?Eomiv078APq;p4D&~u%}Bm_z2lQ@_RB;a9sW2 z^i8;Grn{+?r7A6@>-?ZCoKY!2U7JtH-(a`qHxv{#YQFn%uDpu<9jJ$r_CPSH|I1DW z5-TG3C_j*9kkp0`xbZthw@u6u7ACH&x&RA<`lJ~w)wo|wul_K0FDPF_l)=K#u7n>6 z!sz7!qs&&Vv>u+nA=Qmxm{1bBGKg=SO?EMUZ56RnV>8)H+i80+mXk!XE3qgNy2yOA z?}MO8_PtjJBd(5swOv64-g&_W#1uXIo_f-c)-HHHCR-SZDfJ?7_Ns$sidIN!R=nIp zL<;e7JV}vjmyuTQ(3xC~Dp2q0N$7`)rqW&l;rIU*m6YI1uYI=2FSY?y*)-FN2)8=< zg>e%`nuGbw)w~Ohf7XhO2qCyFl(TN<^E9V@&yhc~oZT#Qur*m(B05M?Fl{EGN~Liv z3g2*6dbqGdE7a9Jr(Ob=YGa?R@+Y;N-I-k3*l^jY7#o;Q86q*pJNWpU%}~MRP?Ivs+&5Urs(BdJz6coW+&RDM?BZT0D{q^{{N&KCAUPnBs z#oHwmCnt~Wm>ip_LDJTuJBYEp@WixY;rV0qKU?enIg8qBx&w~;Xz~6Z8d00Yu?ilV zM@>{}#7#i17BU6M?InYFScZdm(AEj2l1#Sy52qEo7>9%cHX~i+qjx0lTZ}{c&YCeU zw6ZYd;gq5VqLbr7h?u1H$G{pNVZ#U^tKaUtdRXf!oUQ2OWn1^W){n!jL;$4$HT%dC zIHXpIO)xr*HBs?MvvF>)8{&AW#E3+2Oy0iFLti|`U?!T}H{`(`QKhh5Yu|Bx($=;kZzFts3U8eWFTL#Zrncf|sv<1IH6jo=jI5uPC>Ob&@$7_{xZB1+3mly>c8d6Lq_haX&7r8hMnNK=J`~2@ z6NV8@?eUV(719-~2V-gerv9f16IUeJZYhiN^U(#pDHdbEn1xpT5nV{_JG-LdryH3i zw4O{WvctOEg zn>H6XzlkNM^Avc7?+=LIO)LpJA(EQlJkTZdRwErs4I;txY)Y*NeqJ9+ggr<+l7Ect zumIbU=`t$hH#47{7T8r#RxGfb25}Lg%8PhRzLDFEYyzZY%f2VK{VTBYZ#HU<00XL4 zOQL^%THM)O9O=1Lyj#6X*$tF$x}|^ee>PKv0AIw@u~1fN*y?3alzfF8pYV;*&9QaI z9G!c@{Ez9G(FN?s847|VFys0OGl!D&MHi)~LM_5QAEnFUYJMpr+6|{^t}^8F($o!e zkW{M6c{x2pxqu{c)eljheC9_ylv$Ylb6-POc zZ4)7pSkcz{L#$urUDYx+-ZVAl-G7G)Tr~w+AM^omn@~Muv8Hw1u+QYbR2XXb=A$2p z0;fPX@ontu(>u`e%{1?5z+kcWM?{{@#59M3_D0h`0p#*3&B&d4W%=(R;}6D7Y8VNU z-|v1o<@>X!GOv|b@l-%j7^Y$zxh>f{;fBK^Tw5uq{&sN}S`#lTQS*VM$3PAM45 zIzYmzqAV{X_MfwIH)xbrEJox8xJ)i>s;YZi%W)k(3W^E@$HqFuTY4HetMljFk6pjB z`#$P^xIx``a?QB;xPARlfc!B#IQLGv+E?-n{91c~PqwhuxI;oOOb-QyBf~{K&Xyy1 zCDl%*bDizhxMu}wswfAZbt{kQ{-%Y(n|TKBmV%hF?$B;~uczdetR*Ze5ocaK@BRi& zIsZAwyaH4f=Nc1W1W=%Hqd#dMka6{<&3?&F)Z*6{y|?zDI~aTcl-;0t9p;3+C?;L5 z>JfUmXGFcW*a|i0=%szOi)#nV)|>DVUIinkq!i?PFyqyH|0T<5{$}8u9gD$V{}&5j z(!KpfGz{i%l4I}dK3hL?!^sW=Ob2fs&Y?UIl2DSLFs~U8lZ2Q-EYE)|cx@V=dwJmi zjC*u7X>gS$k&BvLRZmF+R@=XIxIcWbv}Z)_VYTuc0b-&ut5C+d;O4Vd3=1s9TPg^! z!KiKkILE#FE>CrH(d8hYX8HChc|*^2C$H{v5tr0>BtpE}Q(wkTTeYTNsq+S8l{W}@ z5$r=#pM6y)j9NYaUPPtgdk2xS z;5GnPQu@zEu+-v@zo@zP;z9Sx;`7L^h}WfwO56 zCOKJhIhFPSzz%QN!_Xej=y{zkvaX^mj45GyNO$?t%zzXt)U2W%rj!XMWfvf4PPQjI zirSr;VZ-yZ_W7qv4pjX(doQ_3AHi>2iWhddg2w~2(4G<9F@09H3`~VOXB4@9^UV4; zhl5S1?y3UZGgJO_9`$UshZi+?^HZWkym%_qGzAE0|LN>3+RxW+60}mcyQ5=8DSVq) zsvx@T?s3VHXj~g_bC?HLh4L@ADOj~wM>hiQ!XV5>W@;_*svYWR)>!64+`J`Wovf? zG+A9^M2yqICmV#j0bJ^AsYy!>wnk8yN?$#@nFkJ_RiXmCbOC&U;crb6ho9}vc5idj z-6!qjbDtx{R(FJ%Ojam#c6}NOZby5IVYh1E8ObZI^+2;Xaw}D_QcjqJ%806h{rh!TDi1kEh< zU*5Nmf*E9XC=?q-hSf1{j^7ZS=bH1s>~nm0aeDBb`G}qPSX^z;=AAbu+8 z$4)x`@GQ?&^!%Ln@4mgoK7A)%cS3+d*miUBc|-%`nD5pB(FQ#*l(-h%1!UUuLNcx> zoRoqLuJ;GNTra&t<{>)6!;AZ}`eZ2fZeL2WR>ND3S+(884dJ#p!~a<2H47Ta$4&{> zx!lzNa%1@l1%3s24Ume9n{?DNm^v&iN*`9?iq;&}QZ%`s=Mj9g1|E=rUG+UbBL!hD z8xc4UVv3DVA~9@b3C{Nz$$3*b3U4i0E|&+Sap9*s`C!JXOv-KOj=v|vc*&?Ip=+Kfb;Z$#Py~ zRWcP1`aT;r&sM>vN$9wu!b>}HP)U?(9;+ka3VK@X0n@<8iRwlOTCqz^+mURKU)^EI zFEkA;w^l$YAjV?O3gP!I48j)I-{!vV#a4j;T-54o?c6fstDkd$9L^c=Y$y zGYXz^0FDB7!p7sX!h{tnmdDe~g27bQZ#nW>Gdd!=iqHjtIaBoYp(~q-7uVMZJ&F;X zcT+z`U!R|WXk0VRzO_dLpz@|-ESxJ8$o#s`6+7oUWTMAL0+rBOqN|PoOL&6c!DKt> zPvM3az=V`ANKyM0??Yju5MJ3BM~=mMW~Nf^mDVPRJ^iM(NFyDiQI! zx@5X!z^Dnjwmx^P&h~e@=JuMbSCCV@YwFEPm*1*HgQ$*#(Vwu*tp}eKiUh%whYDLw zy2?d%MjW;YF=*6Wl#pJuFhT_BAH0$Y?!_qPz-7R5+7%20Rb$aMrSzvUgZUv#MW}j+o`+ALLY_AFvqoA* zAgA@(h6YRRa|y&qWim5Ns>7^lXgFZW#hBnTk`lzQIwW^xBvl3(bk*p*fzh$Uv%uuk zPuTFz%h+`-P;>HR$M+8w8Lp=bR{kVfyrw+3M@)1GdVa(WL$FK5iQ#H=bE}%C&^t^ZhPr8jLAZMRDtHdq-?bby)*EHj*8_OEOW|#n?w!Q)&HyY9NHA zrW_|!H}Zs&?GDtdWDhsv1Pa+qcvh|2)Qem|Nm-pr3F^u}X*YE)bEz*Q&Cqeyj^zd? z97wB`m!2vd0fi!yU@KWg^45zrgoUVScfc?C_;IXa+32tWtDhN1qWJ7oE@Df(J`Kj5|}1SYJ;?Q1ey(|viOQTHa! zEAaQkCPRAZy0iV2Cta3)?0hyx)8e3UXscIs~SFKm@vnQSQQ%y&VctF(QUg_SGdn7-={=FyT|Tw9F2(II!Y}4acIPN%URurC4U? z#7VErG?OsVq`tzY2&9!G^31okq3cP^-hADeQhQ3FCKk_kWBaory!T}XV zo8xaO=_l8xHAj=XLfiF54p-M+9#4TD?5!!&n2iI9l1rWj<8mPGkXG3m_!_uv(MQ4o z;*hBOI93X-tuklE`LSKWQf?P0E>0nKbE;8Dt`yZ*bGG!`6{C9809^|OCi)t!x^y9B z_fBlcoFnzbQ)mKvaOM-FVFyUAY6XH%&aN9C-kIBvXwsUExTkw<>+aH_zTZEcG>a>5 zKR{DFALC3b@_xM69ZzB$1sgO=v{(uYYHB!am-;065gI}m#X;e=!}%e}X|^Q1?M34) zQH&H3{y46)msr?H9Qu@P8=EsYmzMXYoRb~8bV9B!Vs`_0`^nb_I*avcASLjHf8Q{F zE8?M|QKBhR`VXK|l$l>c89NRaSFJad6ny^#Q{)OnQ}q<>1fsSf_rnS7bmRaZ1xsDnMCUdv12%c5W9b%m`wqwyFd5rw=eE%)uA4CluJ`Lk}PcN}MC zvek)T#99Yb0oKTT-7p!GCyAZGCyx+Loa%ihk*jLy%KmhFEAlR#FC<07Rjy^|(!+n* zE&tp)g93n6HuqJU$VPh{(L?}Ai|=Vjs^_cP0@Q@&K{Ie0BGy%ee_ejxwBP#ZJ=Gz& z?Y6L44FuE(a*4%8oiZ3t8@H6?-0gEwp|~Cn)jDB?7W2pfy^5GA?)jdRHsnCMW+~xL_ah57=XjL{_^X77&c{9a6y6~5Bk$@^p^}&J&hI5KlzR+-^ z;g?KXTeR4rrx!+Jk09JJm9uPkJ~Hcjw8P5DcR%FG{gC}ys}vJeYBjC#V8kKhJjw6k zYMPLfswY=*Hq+Ya`7?WZZO9v#A6Prg*TEcKZgc|DJblbroiFdc$kqSkiy4 z>J6Ot%(CJn2u$u{G7kgC7gU9x^2~Sco*e&MQze#s1)6{FDEpHN06ry<#uP|MKTacT>q9$Nm^mYb%s8n zD+kK8@3~IK#49Vt5oZU~IsWC`R^>F2-Cwu>_Vv+rv+8M>+Ce9ln$yqG#ILr0AQPGp zM~ywdFzqVu%2WzwQ2|X>kZi4knwvNoQ;pK5I`Eg|v#CIrpaeVm^)yRT^$S-qiR$*Q zQO{KwrgRcXOCq$F$jTa?4xJ}FEO*s~gHEW5&H@?n^ze{i8<3Rg#;O0W5aYP^He;TD z0+2)+tm;8oA>fJN>x-Ui$3TF$4H}Fqr6rdv*!?Y+xd!(gxN36*bQ7p}mE|{t z&9or=y6QiflB9K_g;fp!N0VWEZkmwy0X=wha(e2LIv6QvMI|{K_1wYHo{YeY0OE@F zrd#&~ITO%WJa2(dR{hx4csijdxoXoDVAN;~ycG`GYQH6CI$P)DFTZx%G7NKrEt#{v z*?M#HTW|JYG~t=r^^ri%U8V~>>3(bBDKqaB-Jt|!Wf>C(H42P~s?98*;Hx;#Y!qPX zl5CBX>s)?Gq>1O_C?0Y^o-31x#>ReN-tK}r#ioT*Uuz{)tDqKIc^2yZ6@@$8iIqBl zty>>QKiCGhjr!%^t;K(?d@n3_9|BUop3k}6D4N2)7k(phYPDtb!8OIyuo?+*b+MG9 zAL?>qU+c0FV1j9L5$b*9;tlsa(bT11d`~-cPMBr3PE{sq?(DZq8ivZ1NXvYxW1Me~Qm{ogIDG~~ZdobRv z-c>PDP2v`~y|P)Zy=`3aqWbuUCx+31FLRBnoDqD!r zq$boxPJjK=T*9B{C-BFX)ZV@T^ZyMOwT->sS+W;(`W^wrL0_m_Ww{nOwmvInjGfS> zH&QF|IGJE6J~7zz*-(Ei;ifF;8oc?QzMQv70NU@cQh#0;8oU zU2$W~D(2{UruVg`#>VVc2^YqjotYbIh6{H4vk~3Jf(g_&@K6wJ(qTB>BfPT(+FSHE z8Ngj6KIV)#zd~#CZui?bEE#>-MdF=r`O$7<{(D;ECL1N1>b89>bz2|`VAKJ&!KzlK zw;IVa1}0p>t~~3R)zdu{i`msa#P<#UI8f9!SzkdOXvZWDA83kc&ORG2%8s^4%j~z3 z*%?ZDkZlluRyj^Rh~}kD==HnGU&y>IY-6O0n{i93lI-ewosU>`#+imM-RgQ!gOt^$RHrECaAk|-6_)N7Y`LGc~C;D*G?Q}b@u&_OC4DR z_K9oguohDp?5nlJE=kX|&(PsWZxBPhUaFb{riMEoc(>kWN5RbQ6!GXWt3kJIKI{Yq zrJZ&=7F7_jEf?5O8R}HID8G*%vsjJTymGWU*T;RE`xn6zdBOSEMDR6qCjDqDDOV4) zET?4r%(Hd+kHITOP!t#i zCgqMC(N>v#UKz*8t70P#=IV<1brOdY0S@jeRn0WnfO5fdLy^g?pi&EsaNDIXU_mTr z>m^G37{rr&$+SfzJ1oFUZ;SBI+llxkLU_<(q0sA6HnFVhBJ}yHOtZHNlW8PSd|OYW zynMq}+$|S5T1CB$TF#bmigI~(5We#`VYRf>B140g{>NZApRI`O$2&eiBEBeeK(=wt zJaU;;a72fv?F#R)m*ER(Ot#nIdnQ8AUy;*d1^+g>du2B)b*y{tR~o%r%@dlRPhEURB$GcmM1hUt zVL6ZdfXT`)t=0Vy8CSiPw{cA_N+Q6Fnd6qI=dw;`uXqd$(16PVKn7DnYfdngY3jDE z{ZgY_npug`s+fqkz;(rxmQQCviv4u5jz!Mae?0%XLgq_UGI0(+I57c;Qg9va6X+=YvK1TzMCU|Y8%#<1G;jgUA ziCP(oh_)Y*Yy8&oVlU#mKVL`iD>->Iu+HQBB&z>Jo zM|jCZF?8C&P|oJ`YgR5JEPX7d*#Sfz^TDhYbNiV8^oT;fW(pFr)BpSyhUbCHA zd|j@M+uS;T^NhcXX;J}-BO1WX?vNhbs~#roS!qJW+8av#7z%T+uysUx5aGNgK`xx3ycWT3wc#zj!LkyZi6+KFV8(eW{;YI7+QZ|eo44+l zkJW$I0_aBG7+N)OlMP&W5*%FgxoHB>vcGPGyD?P`TYD1Ss@>vkeGKgC-q(4i8ytM} z*3I;pgz}Goroe$#O_T7N9Q%Uer<~VOjbsBL4G!8OOgBo_y6a+_H5wQGEY5g-u1JGb z8DxoDIjcB0Qn~3*GK~x`UL49v!qI*10;&hiG637EVtza2PucmF-8n&WC&PgToVCaC|LA2ss8=9{;W*>EqBYhqR=5nrw8XvVN{vKvv>f2Q1@aE@o%j-=xa>M}N98t|# z+TvxJj-_o;2B1A7I=Hs!H@uM|{}Bk+o)nfr2HietGZP? zj(L|2%LQQW3|S|vS{5FnP!uTJ%jy`B44+7`r8yE*={GG_xo_Y5Q1_@iK1>5!8<(zI zmfHh%ts`a0CAC3ag0{*h1Ex;?VO)^vYE8)Pt563eS7E~;+c$B&mt(dV(XT3lvQ;kX zvYunS<@lUSyJgI9fF{6dQ9f#>_%80DDLd8kU1QjJe8Y?zG|bfIQ?G7)F zD>}92j4%w|rN@|~abG6PEz3S%=#2@<%x=|9nrSa~$`)3)D^OO3Hzsg?bV!;k`ws9( zZ5A%pgr1@Ls=SCg6QRZ5gM`98pOTP;DQR)_Em!gA42CpvvbXkUq>JTNl8bTAElVsU zM!3Zrx6M{N2y^HA&^TD?BOBQDPDtP1J~0hyCfkKC4-HR_IUA7ARh)VKP%@@Va+N?# zlW2)c(G2hbvcEmJ2HS$MtyYNNj=Gv3>D{BJFgU$9Z_VOtkSC|9G9C2cqBR<2oz=LD ziu75WqHKkQ!O(%D>wUndFdNfkt1dJXeLMd{6EPgSjy_HwrtqXH*Ebhe*4K#P7wzv53L#6T9A|O`190%7-r7a8Dh$fBkl5;j3k^=yE2;LZFIsZ%M+&C=U@6uTVkxVCh^J6~ zbkD7d@h@{n(*pvwMDK^kuH3;pKe!)mSI2EM0eOSUD3{C3{2{PF?!|+@Hoj=>ew`|m z>qo9I8NbtphZ!ev=K0DF>L67n6I$>cl3N5!Y2kHp{}!pjr0v8m`MMrFq^(5O31AU< z$ueguLcW#j6)2BMQU~34h9uUiqF1zAx-S@z3eULKCl5aNBY08Cc1AA|t{DMS2aAh@ zT~W<0!gnb-4rZkoRnoNvEl{31Dk*_eYw$RFMif_k(NZpIHrc)Jg~-Z~8eBW9-%3ZT zG^UI4=5a=CkZ1pSG|OY?fH&jg+Hp($Ws}(0kvQm@z%|@Xt=>B9HD@Divp*EqNaWUX z$jj?%#oHYA_J_qL!1kM5#O~M_#p=fT_A2lx+nrjr^$LCi|E*}j=QMp}8~+Jdt_bqn z_Ri+k-jdmd1D4mgy|~HxYaOlUalm>JBMO<>_CBW@t6@Qr?M*W59nROOzTtbOi#L*i z`x~SR?-s6UJpKD>bJz`$`97UZ_#SN_NLB6?R+ZTD>ACX^Ds(mFByxS`jxVb0sv%O2 zrc>Eio|?*HKU4<;F!4P)u3A|>66jp|@M}b$osK>_;VSIJPiY;}+F3p&n%P-}nNp-` z1}i)yjg>z;80imJKQ1eQaGZV zS$+ddE8#Q-oHg>RzUa8!^r=Dxt{szI54>|QTkO0B22%^P8%zN@>$FiTQvrBYpCRoM80w%{|LE z;9)C^^SG`4K)Mz1Dg5_*8MOLt=58i#c89#&HM+aEE-bgsvA-6Tvb<+@z{~#bDjEZ0 zG?_%;1MZE$4wwwvbXm{Xs)H;#sp_E`aQ9Zn27vqN*)CbP-6JvIHg|Y?YYlw)=K2PC zYm-X=nuVGTKt$ASSV!vU3#y^z3NC~RGaNwf^L$BsTb*4#$aempZs76OndnYY$t^~CDEGZho|+nmai5hUBD z6mDjnMMiDsLtzCII!dB}<)cEL0G5NKv+#N``S!ODGYRK3o&rs2@IoiWtYNbjxwEzAy(MQ_S&EEY! zad)$dx07%E-RW@Qi}Y!kmBpz_VLjF2URMCnfLIC&f7TLR803C&Q({o)359^ znz)k$-ceW9c8A2+(r~kNnE9Po4*@8v_v1;EqOmlGS*bMllCI~4sIOmotgrQ0il-gB!`5+9C7^FEPPsce{WfG zqteN)P5XHOqEH65TfYd2jJhOKsc~4j-DNtv|2OOcdnhOphyI^9w#l=mS+7`e@`rlf zZ;RB?Yd0v9lsQbWMKWv!R=K|Y8z)~Htr6+uR!_;P;L zPg!Z%qn*(9;jjf5}8k-!IxISTID}@XJ1yMSij7ay3`|h+C$<{Yt99HiY{Ps zQ;vE@1gY$g*_y8}tgl5%|B(5ZYqHcwzCO+izK9H8GEFLxx!|Mc?^{bQOwkIZGBGQn z_tf**qPR|gzm?Y+lOoi@{J=X`lozolU&rHpli@tGrfb0sbF`rIG<6U|D`&&&{$HU= zU1QgFD--vNNjw1apcUk4a3m+fg5%`0y;K@Mx;V0hVPn=1pd#|#8L}Bs$g@h$&u%K$ zcs~}@P`kk-!#EL}q1a7WV$;{Ph^FmO??_x@1n$ojU?Fb6f_8Ce3@3E=uXt0t7jJ6U zRdWA=l9Zq)q>YsFOKHa7w#vY!%3(m#MK;w}PC~^Y^?s~s6ew!ZrTdeEp_Lwr*WNI` zUjzQ|Ly`0Y2tbm<;oue=n|o`kaR6>uw%K23hE zOKGWn<;4eEOb%-N z$CG9r8dPLwL)VHeVL_YwdqE~s0S!oZ%vxZr%{O5k`kO3PfS%*n9De7>Uh6a6vTQKJ1UUf)Wy?5(-U z+IxZ(4B!b_(e=7g-Y4!26=%Gg^Hoaq0*~Nd$-%=Py+_Ih9}ge5|I)D`scge|?Sz#L zR@!=#tUhwMNcl&r?o1|aj1iGwT9HoIq=F;3sr&18k_fd+y$2&1j=gCDvT&_;mjzk` z5UQcfvM!6$hH`=AwysePy~y+T1GJxR(3 zrmN-Y&=rSK=A6MtfLQv0J8QI~z1x*2p_)fYn`#?QZQW^h_ue6L0W32KScCouL;W&S z&)My)*``L`g#AB52-x!fe+UT&IyE&|AOBxwJ~x?QMas7H%mkE$q7ZM{H|I}|${pud zP!JLN#?U(h_FbCY+E~dLiUeXwC3~GwMz=Z07UFv+~Xn(NVY6r!lA;GUZBE20xV!mwyeDX zz0#=c+5eg|>SMiljGs(;mE=(M6LD6F3=)ueIm%?6tm-=-k>tR5>o>WN$Pe$O^|#(; zU$B|#`uP%d{kAn;NUt%)OS|9I__cWwNq*b>cd>F=euzTGc3NLO7g24q-fDKZz%U~0 z+!&9f)5ORBc&hE*h=Kje_X>2y>`Kpka8fEzRP5#SO_&|rmP@%vT~qM}Ws5S0WzVkQ zzw69yYE?BA0Grn57t~&qA#Tf61F0uvPOlNWu5qor+NW-8Bpf@nxy@k}bkk}1AM$uS z$zi&~rtL1}%&u({W=(Dv@RLoKsXiL4gtqA>3lw^61Ar7a8wOeSP**W6?$Xoz^_@n^ zldyME-ouahBbAgTs~^mKYingO0iKwiOm>$_OtUda9+uVtdIgAS-<3BUzXgy5FQzmX zkBq5k9iuvMf$(rXe=Hb&df zGZTWOmQQfSeVL_!A4j|1d#TX{(iB>Pqn7Jnx>J$+@dx@rdZhVysbuPXHeHhp9J%2YwYja7|1 zp=sZPL;`1T&6`^`z=pFkJn;pMC3J}kTQ$0xt3?O9{Y#vZ==)XibZIAcYhdvW`Ze42 z&B$#q6@w!-?f-odPi)%yuDbZ=BHcGkHMqm2hpwPQmx}0#Tv);Wf5d%jSd!`5cAH5j zb2Ckvd#Gt9E6Yu0p(3=VnQ>-iQDfm=QX84l#7!p3F2~Xmm71p9%e0!ZL`5@2Q$Z!f z3598cT5eDgnM6e-1O(xGY}R_;cdhsP_xXKb>HgreMWo3UPDO7 zC4(1}vI3*e$z#odCwM8Z&YFM>79A@&>JLWl71M7RgXJ z=8{Z2OQaM~RmN5YRfXFsQo-3&4mF(GVF>MThD%SLvDREP>+4hy-wMu1h`#juGFUZ`wqy1UQbn&o$WRQ?j==}|(}O%2XuVLNU2N71fv{i3Le zG)fy;Nw-a&&uT;?0n~la*Y7wbDdp{!?ei_=9DA4#UKkSWV__9NE}6%sRd3wEZ7n*1 z(hH^GVntXH}0Q`WS=!RvcTi()`=Nja0&z#TGJbgwujqDOFsLf{y{1)L$3N^N7s zYXu8v1$U-NNmUSL)fpE^;5H+00*=au2PacU)u}wVOud&W(+%k=gu`M)q`U!7=@-cv zRQN$-l<}>y2?a*0aS8ewqoBZ8BWl#teOS_wDMBoy0{Dcdfs1gQ? zz_wnOBTR`ov3*YW??!uVhb2ovu()|P!*Z~jEY~DSiUsx9S>3ZQD&ycP7ygu&UFtuu z6WRquZetw;XQx?0-f>$}*}}mQ_*x+i<$u*@ke_0+iyg}w4f=ufkBVK{h9gjq;3A1&!q1_>R%h---J{S7NV5w_q!Yf?YTxv zi#b~O*wYzL{R~(1239%$`Lvejd0D>W444;fYrjJhh-kBT zw8r1qDiDl{n)GN*qb64MRx8lJmUQjSEXt^HOw_boE}?P+^SV~aUpt#HyTYHDQ&iQd zLzvuYxq4UtE+f%NbK19bN~6yIAaTGb?I8`wL-B(~bJA8)B4KG6t-7Qy56C(>uC@Mg z{9!jaWc$EU+|D>JBs1+o+!NhkDl}k*Zc>EjSS%-%4W5b}1b(m% z{@fo)wP_cy7b4mp)bb;rL=f2rXvJ0F?=lnIZLh=Ku%k@WM&KQxk?sn)oD`YI?8`E9rh`4~EZ5 zw%jlbu7R(-*1_8;^{b0>R7?WSepghxJ^HWp;0}meb#LU#p-f=E>wNsELO-%xKR5~4 z94X&x7+S^|-e}(v`5OM@KfS^@q#l(4jksV2evm0jhf@_OGU+_C!@z}6N2PlW4Z4cV zd!4%$k~TO%h}D<~c)L>Im&)93d?ymLXCsW3eBa{qix2*wix2Q%MaDz{ZnmJkK&Y!+ zJ2i|QOy$XJJTAX?qF&a|_Zp;P&n=!Gc^*Ay_pXuhGYO3eQ<`L_zRe+4n&fo~oJ@|Nfk$dSDbe^+}S->)F&I zFlA}A;>%*E{eIG_Zdnp0bR{NWmg{Zl%}u!1;68fb!V%=j;)?o_VTYHON8Hvn9*jZ3 z0{e5%Mfzl(Yzc9y5M_u~LJ6)kL))vi)Qo7(%%c$Zqct6s#Wu{;m5}7j2K&HrBtq*k zC%)D=v6tl^Mery#LJc>Ud)6%8H+(#>nktHTP$3wxV$ohTjjP7>C`VQzd88aTIh~$c zH@FB`^hcD$zj-WHDHAhgp&gByC~25JRwzKF>#HcZJ zh1;Xn%}c!u@d`vgW6(GxZPb->;FnlI7;C!?^Yl)0CpldhdqZiViHYTfvZYe?oh$ID^7?wD^=TrZwM=Qu|d7`vx^4M7#7-G+WnR6%5lGr#G zlOJUz?lmFBu{f`UEw*0Sp$%NcPXJ-*ZE1nD%3*05I)?K0^>EAku}_cS+GCP0!6Ix) z@x&0^Rt7=KA&0izDt-4UDg)WWnqKtDO=rIPtaVfU)=g!Io29o!ObE5(n~3hxYtiR_ zX5mAm9^9pzs*a{*$QfMpQVHR+Zu_x}yGwyJP$_o2dtn&&OgSuzo=fYeErlIvw~f*# zjV6uv*joj*WOi~j?$pZ{vDIMq@`z{+?wnpQN>5kY;pPo>lGQv4zISybUQ6eYW(`Z3 z)Nt0|QtCcKgBngt-%3hY@i)%mFTl}w^uIz9X;wF{f~6VJDzKwS-bNK@=p4AWD9RX1 z70jn@H4d#D?*I~VOL!Jx93tAq45wA)=kpPdrr9k(P6%XBN`Ra|XnJ>(z+cbJELY6| zDIv||21dOi=$9kUXg@m3TK{0ltd`_n==o^N@q7-8 z+g+QC8-ZZFFI$Bcgtpx{olQHz*%Qa9U`;;{m-VD~y7%XG(Z771YsW&Y|Mcarg%g** zJ7btpqAk{NGN*dhI}%l5;VnB7-11UwtZ3zZE|r76dY$VN2wn6yx3qGl!5tV=I7F4F- ziu=HzMzy;M7@^WW>z*^rfN!Xey(~GMz}@{5q%bdp{QI#hvABj9q^HJ)D_HoH z7JVV~yyqCm4+ zBXt;>fZ*Xz>|p`J8~VTMrmA^)%~-7J&4=g%a2k4?K3~37wIBEl5ldiWnhU#m^ zLJebrSnX1#pAp1bYFDdA^n*(bG4P9J(u`(I&?N0JHtHJ<^!`<+qt6-h<+DWKRTzqV zWgd^i3#iIn9%lN;ti~*{=%)OV*Dr5uGAfmS>Y-09WxE!>xA}4Cq0t_~e9eFmsyfZL z1;q@U94Bnhzx5FSR|r^aBc(&*4orWJ9O#Qvl`-n${Bsgq>qe4tu21_BfEXej8k^N7 zWj^Qpa6EQjqBl2dOAs6dQQ$4svYqdeuk*NDD(O{EGez=KQN_1uD>pBBwoV7m$ajLH z2yueRKgyPAJ16C#zsr3XM?Q~y($n^Fi+>RQ!Z!25gaYxE-=*Er+5@6Bi5x|GPPi#H zhM#Q1xFkxc1;rg(NJ<&lKKYgo6F;F;IYmgqO7IlJ_*9xGGp9f{6a}eCl3e1#S2SXZ z$ngFZ!t3&He?$YBfl(okysa~8R~ODOZn_OTEQ(5ztMgZjp4FxR2CN}kuOra=S$94Z zLNf;bj6ago}*D3{k5s|w{S5x6lO94Lp4Z}ndKQ2kp2@RE53@Tw)vn4Hhxs`WsziwVt&H!W;K zdf5&C+Y$lQmj;q=ZBjt;?Yh&1Hiq(h7>(Z%Bg1eFkFh zPyHFT@tmbNqCNG9e}g|aVW(Sakx5eg`~>rirZ0YKh{t~8vYo8thQs;&6*D{PG_Q+z zHIvJ`-3!@i*V(=2u+>a2r$(g6UhsSqPXnF)9*L|~9oIvOfn$_rAfj_?!2A1m^;rK6 zsT?qyLb*3?R;(BR z&w-2mc)-E}>869_WpF0-khTJm@D59@3}JSR0gFoam#_FAGB&NuX*yVq2C6SpojR8x zH|{mo7~_?zJ{+$DUeYE4UNtPnvHV0Jkx~jZYyV;c$S-N6sTA|6JP#_!xJU#==B8O| zoNN}<{y5k5-1%Vu89I8M(U2+fx{P;}XC`J;tcwG|je4ammvTnsx6L^0{$_fI#irWn z&*S#=wPL;Y5p`+51{Vrvd$u(a8qSa3k~g3|@(~vhX^N{1cfQog*wFDzWBzHV;x-u^ zpp8*I=RTA89`Z9IwYVkwa5R$>vO37qP0;)*zoEfc*~C-3zS^Ddjx=xGm>U@DcJd^% z^szgg92}5OZ*f9oA8e;xr3CdcySBMmg>UEy!f2wW8oYa4#^e|GB1sDhF1Lya#ahV{ zz*glBuBOyfW-Lzm-FFQ$ua88~;A21umbKhtbm&C&RMoFIA}Nv))`Y4-t6m045HEqR zIzO>Ufz|^peI{CqgHt5KvN<4DzEzHiQkAG-Q6caPnemYr2Oq3d8w87jW!Aw}&t)|m zftTp!fLHl5)osc-g+GaZuQMJw{%F<7l*c9`Z+_@7t4@u7VL0@iV7AuM5u?k@xGq;T zN;@km8}DeQUt+L2`k;m2u4Qqum;?i5`+EIs5@+$1rc8ODF=YwDF5`qq z%EZ{`8yVAA;Ae`;u~WldnkFh%8`3cSjq0Sco#!3;>6MuwwvYIUG`kjI@K!^{-dkWQ zKL^7$v8MABW#0kESYx}xNTK6o`n=r}@T_eW&gol2{So&%5M5^S9j3}f96GP&?{lpWuL@rL0 zInHz2L)-3?oMKW8gNlv47>jKyb^{Gn#?3YN-T^rxALzFK`%k;mhDzLci>s&T^D*E~ zPv9T;zi)u4jXU4Dmu1ENU7xu!A`;X6K!RAC1wN4HLu_1(O}jtjd6OHf8+&6f304`m zfBG}R`TD!pl)k#%ma;5YQTcsnszY+?%D00lW_6`*0qx*73fD_ce*K8~K_^+Gww&}VL;S?MyZ1)6fz zhM*c{nAM#}YaMf_6q}&IHQ&SdX?7dMn>S&slvJSN8ig;T)^Xv&xYo!%1%joZC6y>F@itocG8VzDdEVo+1s)p=Fox|0jC4XRq} z(yk2Sz6lH>w|5sy{eU6&=#SnMQC6I9KOl^iuJ^+bzD#JMo zX;D5j!WDUQul!5vfaa~UpVmg&G2_lBO0Jp4^-rX~(@Zur+cK%1TG%M1TDR(XWV%9gP3#!&aBdCkckNN@g zK;@T0P>gj3L!ZmG4~9A zcv+D~Rq+?glY55Wj=0t}4dMgd(reeT3fq;}b_D%+fgf6<0_qv1(zDpjHaLyP-8X#> zbM))v<=3I=G=yLL? z(o^XG(N0!S=K2F`O7VLWajkI&WmQhey;-pq`{pK8@jpa6p2_=NjNwS4C&bOwU<_nv zk<1Atpm9DpxUgZrolYO`tO&223eV1M>t02f zlYF>KF<5P0`qn2!sN^DJ zWx+M&nKdp@V}|)Q;|o*|z**%w>a`IyKoNkF~B;niju?CI(m-0(OJbNtmO zFUKff^_Sa2&3WkKSHklJuV!b4qVzGs;I;@_amlfcu7=Fy99LGRc0iBSmfCV;?E{5H z2m%sRIb*%a>Qz*Y6SM^1kU5{A@=&Po$@8Rbnj5tRKX!f*Umv2a5TAm!H}yQ*!hyk< zu-!wFXOrh70P^9UQ}r~)d~A)?SlG&yhRoNu9mgtJW0bjL#Pwa1F~VurLEupS8uh1K zUd=n;?EN|6_4NpNWtuRc4sn=Bs8MeG_A{r0>wD9E`>6jjD6g7jHIfv5h$Qzq{Wip{ z(OzPH>Hy)_D_(UdwYsHuPmHH-1M&FLFVv@0+B%aPl^CY>0pf7!t>UI9rMh~P3w#dw zy5mQS%97TQGVZmsL{60nS60mSThP?e&wY z=>$QNw+oGF$I@$RAkdk(irR#JoU~OU=ww`Rb5oScA6xXSIdGzJsUp7S0TK=++4?pY zHKuv1&Nwb}Lj6PR6ivrJ%VXDNEX}j&#MVV|h&Eb$YZIG0{u=XRFL&~Zm8*MHvLQt~ z*wPr_AENIoffIFbA~2U`yjX+M>l+t_%(qG7|1HD-{-nX%6laO0y2J;KC&vS`?5~yY ze!pMrm7s@;hV=v;RK!cYVZ-u9xQb+-k~RNeXRvGZ%dk3U}eM|@?>-uy-^LUAR+ zXUiieCr&tLDlWyBUGL16KXTE{C>vsA4s|UyjH140v@Zlc@hASQ8Q^n*6N?A*9-!t$ zZa?>ZtWgRkY9`{1M=t}8bG8I=m>CppaQe4o1SB7;4RPa|jP~4jG1geK4RPBRt@jYa z3Udmc79$Q3-=-%6nl5I=?)S@+Kt)QBUrc-t*CJoKn2M$QeIg!77 ztItW0T?qyY8z>kRc%EGEmpHfnK}5%ah-bI{(B}{{L&f@}XuyFABX;&YMPSjo%tyI7 z?LOYb0TeYVBE}_zB|64^N9eJOq*`| z$~_y?Tyw{ry9KcJwQBdB3@2%( z!p~7+2bzqFS%eDtrs8vU_Ep};<1>==TFvGfk%kOe-+QE@;omdSsvkXECA;oDUwJ_8 zKO`2tmr;45BU$zb$Bz7#mA{VKu+HnUJ|D$@vh62^+p5l5wnF*j?^kbARuDXJ?i&^~uk_*z4+!DyluMijMxcdzSqyyzhCi)tn#i!eMNJd%5yzs-B{A*=a#wR_Kecs*)h)ZEhv?Y9k%fG_&E3LgLP~r*QY4 zue}dOI_p#*#bo!Zwk(|BBY}BO_Ur6Z6L_RW+9M(A_`1B`rGi}78DGDbiuJ+U!xPH&Dxjq}164V~6A}n5y^)c&P zvnXk&y?d{<@xUKYLWm#H)=sfN6wzRB-zoFFuj zg$%nuA`PlvI&b41(C#~_7yh&_7h#SddftK8*c5WWcUS?Hp(pXeUKcm_ky5TGXk}3w zUEA`~)j09=oxp%xZ@6(CXN~rbytx?vth#HlyzSQr90I4vNsRjSZbWi}<{J0eoN!mO z`lq&K?oziYYcL<|0$uB9cJOm(L{V5~49eLmBDoznobQjMxxeM3jv<*Hh(md8Mawld zxy4YN>J?W@(th+C>sro@|Gi^8%p&dqbO`-qd_DDhz&7~5tE!e#_be1gi!Ts>4KsWy zqlB4J@f;axMw9!N4?s%)9mGk3gRJN?!R~sjzRy6ZZE~l?6Jlwx+e*Yp=7MhG` zKKPI8DFe_D^aD$F!z4{45#Kf)%&5J92=tteATSr7b-({iGcJ~-BjF$&_AR)MoCeMS zNi|cQG>lDrWNu-u4mgF^G=lI<89=@K-u8vp!T;#1R})e0Y8uLYT{0xC>BR3aq!RsJ z4SNF;f;cq%?#I>>&P$1Y+(D-z~U!GtD2j5q~o*ZtN9-)rZLYgBvnS z1$Oy%O0wBCPgh`t1a zg|H76J*k5(oPXRho|LEXe(2XM-*|F#c?;Ku>T7QqVX>Z59a;EvGX5BZsfwCwBj28Q zLAJ@63!&!qA=?Q}HARTS%VZ6}U3f>nt@+=vc3`!F3pbi!{nb>=Ggf7~_Dw*|$&7Ynd&%W4lK@c2ZKdPlW6*!8$4&IeLX6PwHO+W-nJBt({1aC9v3Jm*}&J?;12VQoi; zH1C5VX)Xz=9!};5X^413O7!I8{b}M>$N>Da5Q2=^($u|KDZ$(>6Ftw9(CyI?+VJaj z@1nXJIwr<><6DHVsN$9|+AZL6KNQ*(U=!3bIFi!VJ>H) zFYg^~JDLdx1AHtOke_~CrH%a;mhewV0jpdMy#1t&?O|)D9TikAtwOypp*~DaFe%*a zDFZ^%s+((C*PbC8y924W6Vz|!NZ%F2tQxC!i1uI z7K)|;fbenGO^x!h(8$AF@Iw9cVUR9R6!+!mIqt0CJiE4m;LZfX#u4&Y~En9hB3){q)Wql{FY_AxX3c8 z|G(61!>fT7BaLLK%J}0lrN*pSy`WZmuzOhXo^3$Qg7sZ(fi8wwzTzP}<(JqC%k@db z!5S90OFxAwlCPuBG4RPNRIeHmE&uG6mNr!U}KX}0+2MH+`i6diae=~<gICGMGgrJpl)-V5ASs@D|G^Z48VwX>xK^h=d>bb35b1%{@$X(~m zC!R{l_d4RborlYyI$<}j=+a9`yfS#ni+bA2Y)Zi8ShEpV^chl>c4P%f1J82VF+sUjWs0nx6vI zjRwUrZ>z^E-yF4r48+UAGZ3R{Z@SVAr5x^9Nd&m}ZJ5Ehb_^nmq{$^M0AwUI(V!ED z=2D({eBycQ+yYvF?uqFajYUXEV2Zs(wyR5ELr#WAjql`AV4>+w5>p@WXH3fL^xSE` zWcGfva@zp0wrRN(+|>=qm9XEmj`iIdIZ9y80Gx+c_z)O#FIIJh9_*%j67X4%`h*jQQjt!Too8j3QQ7xrUS6o$;FcgP?}?3|%op*OK;MexE$WG6J2 zwKB{aB`0vxnZb)|tNE$Y(^4iuC}_%1OTZ($7PfDI2?s^tG~M0wh0+-6Ob2tPh1Bgs zT*e2kc8{LI$}r$-Qtdi=n%!j7>=ytwj?1O1{6{YhDhk%okF8dG&4+x|84m#tYPM+Y z6dW9G*;rz-efo3Yk~2JaDXzCy6x6P7nexj1o6>23If9=)v;YZdnp3{vkp}f`%Xnc! zX0?0Idu~;(w-FK**j{ootUmd9sM@v2&zj-OetOiOu1F&tcDqCOL9U2nIHHPX_y+jd z$lrK_zwn-Zs~s>4Q@5Aa&lj;vj6-8~mIGJIw;%}a>xv>MPiyrXL*)uEVZ~!@pyNN5xdrmUoO46AWdn9kA1Q2i`Pa^@et^xK&ez|?gQRv5PtelUv z&{X=-ebw(aJ|Nchx49d~zsa)Nz3_->7z-P<>c}3n)}4~8yw5?>?N>YSai7&BmRjvM z0vK-LwOM;k7n1$W+x9zIVnZd0je9G9j}$1~L}O)YzI4kCjJ!ioK&tHo`X^)3w!R~M z-#dI#DmXj6v!Wk)MiYWdwRO^Ci~YgmVNf&T=pycUcJLAZcqE~9Nf@%CIu)3RUyAO( z`o6$5!L=Py(*hbG<~h%k(^zrGwBf2NxmUfjyG1KCLPj@_c1i6UArJpZjx!RgAN78< z2T%$!H!11I$hbtMqA~5*S6JIH?P8Zw_9BY!`!Jg)0^Q-bxb+Npk1b?A8jyQnYBNQzE=4r z-ud9zQX*;+_jC><)=iQ#iZDuACyu&(Y8bChm9bnG%0&9nJZ70sDgFicr))j4Q8M}`g z_G)pc)Ugmu2`GR7XlteWN!-TCCB-kn7HAdQX?PQ|-36RqMI|@!T}K^Jt+unsQr8es;)>! z;fjA2`ZOOZVn_wm7)~%jVRBh@d9d;-Aw5&=((e8Xy)b|({Las3DG5N5jyWrcXRBK> zYf2T!Hp3Zl8V2ISnK1tOZ`!;z=1Bs|7}?Ts-h+LrU_SZSjHoCgQd_i0wumG@l%c%jH(Qe~_<(YfYjDt_IfE?W*1Fvu8K>?bche#^RBy!L)e z>-CmmA+%~$q-RHpSb&49BGw;CZnSeH558rioN^svu-)Tcg6s03sAbo>oQRHGS!J%Q zJN7C&{&u_tI#Pbkg_u@$%z5}|VWulW-P~;pZ?-MJeHv9~dfZu6f${FL3jI3zQg;d_ z{@cH`UjHwF-fkoT-*MSy)1Y>!Z}>#fzs>@P+FJ-*ZtuYdZDlk))Jq_*o};GHMdM=rq%AG5;{P`NBPu3ikNg^jSu`MmFUNmDwV|wGn(YGP;^-1%#ez% z`tgRv6{|}@WO1WdnkKy>qAhB58RnbRwkQQ3a0s|HOF1iTn+bN;N48AX;-UMC3~`$)(Uz!|OEr@su}<5dSxg_Bv6Z)rmy|~oeu7mc>nd^#0#q(e;3`pk zZzZCtJ%<5F$8KD8pD)_)ze@)GU->1F+F)1n*ov1x-+oqvR~Af+u$(A9-y^cWbOpP{ zc?Z19iq<-s_?ER@Q#LVty__oMf(!MEn&j5W7z@?&tFip)FGueZGLQprq7u_Swyq*H z0J_r**MZ7V@hLC=Nms%1$!3&Kw^DTO=E#N+^40#TkpAZw03QLW>-M~lQMdFGwr_fa zo}Zv1cRh{@Y0+?Km{0z^nsc4Z^2L_3Te3|NYQS0Upokesfqg;l7S`e%baKtgl-^+InAndkSFE%H zME#2XwWb0C5bGaMl0g6}G~nFzIsKhD@+ALZLtakazJ?(e!>eCT{p!g6|p5V-)-*BTod4XXo z5(rO#;jbKTaZwfg7!ToWqCUD-JEy2$e5Og--*_+cyRyXrVyUO%>McPv1JLCG!YjWV z3F5L<$@FHIPug?5Tg+~M5;@>75SA*bO%wZ_De|3A2E#3h*?wP!O~}45nBZ`3q@m2q z8|fG;nq>qK-Tw9&h=%Rxp&CCP9obmOszgQ+#Lby@PEZ=WL4V#LU9C=U|5KK?!I0D@ za}=pnxosFzi;uu%wt~nsK=p8&gwos3?~Upha3;<9R1P%ilWHj(Zt7en641vF;u7_t z9r0o-j$Do3g~&)(j7nIp@<$73sE~DOG()Yl2ZkOfa%lw#_^9fU<5NL@A^w0EGN<4Z zj5huHXzd?g?eY-WRnoH=4?0uv{o=Dq(Obrn@POJG`0cQD@!F{i)%o(;zZm+zlz1!H z*Cd0)5Vyx9zyGX5Ua_#asJP)k{>6hwH^vHT7W4|IVX`|Oie%>8Sy<9*XfK|lI)ILi zW@Cl&p6J6(P5U($;SH1V2<&Uj@l$U3xxm%gR^NbvxLeTB$;Fu+m5bF|64l~1;U|@$ zoJ-}OT@ydN)>ItPUyJP9W5Sx$lnC!zh?Zkc&@x@;lURdC@hP%R-y~*4YbIuH!JVk0 zHxHRmg#Q8SzVs`JEwnaECvuUk3AMDZ5>6dU{_}`)%S_h_cU3DNg3<$X3c0!=LxFY$ zZ_6blOGLJ$96xd}J&lXP6;`SC!Xspnn#uH9we#%QZBpch6e;jAdx`vWL~l2RF?)U zNK!3YkgwDIj`P_D=Hc0wS3*Fu9ec6JcY{$_>WsnCwSjTOC8rXB1X z+~Xl%4C6$Zj^Thog#lSh-w43gTwX__q9(VWU{ z-@vp#5zlUid0!XpaC3=B8?=Kd9pZ`c8PgEq6FLG1>B&6;E-6NHB-t;50E0H1PT)M_ z09RB;1rkzs66TQvb#zLolcpLqyDr4ID?>G>`}WL^!4TUl26{63>DhvaHQ`QZ+lHOk zkk*f`ZDJ6s<$ap{%&!{n>=<(%*9vQZbCbWbwOik-vV6B$y_sq6(1ZE$A=l&v@_|X< ziZ8cuy7Tps))dY7D+vW|(1<^vr~uVFmq9e0dUJSk)=`I6h$ES3Kf_xk?ksm=uaOC) z0IJ~PTZK+rBWTn#;nf^@%gP8aQ4Lskp^XIRrsu2%D|xs&9}PTe^qoi_d9#v^m$qs0 z7(HNSs-kV+!>ahnfu%tdh_utt52zb3ivxfM+Bv}}!22nWX$0chNF8Xrah2;)(g&GU zQu&X2pr^7jimXo0{~*nf199n%Jn1Q^{J(W|qh0Sw&XkgC-1@%q-w{#1DC^7#aFz!yG?sSS5n9{tKfpT6Rz~~qGRdKb zw+4?kqakzRhsXW%YGbO`vr+vil4ossqHReMGh38@bDo&&Vu@u=@=Yp`OufYy6?4gH zhqNREaE%FT(N>WPOeCR2y>Ok0E}0R2K`<;}>~!YqCZDt14d&@v=~1#m){+xKR!J%{ zGFuj@nWKOba!dz|@vKI9{61qP>C%{1-c6n016~uUY12!9=YMVgwj`}!piT39Sgi9I ziIn{nsM7x}P>t_f%?jG&yO?*f74NY$=0;;iyU?n`v>_pt zZs6(>9$z^IFxZbp6Bmt;4Si6eK`V6!q*pWPhYj<#2bDLczeak2y=>yPp$oe)#9u^X zI|joFZzC!@B9aGweHpcvsR3oVAOsURknCCo23nEB0;Q$3y zE~MPc{HZ0`T1+lNfY@up`(`G1R@pQa;46CMk%jHy=lzo! ztOV=uWTOAf&&-iV?N02z3`9bowYoT(;w@-9ZBoubL$I!EeJ z&vr?u z9O+H3V#O*BY>V_6H(Ug2ZpZcC!gEx+YcO~ePv`>T$?>*=LKz;YD7hq2`+}b!QtCCP z;U_6_<@iAdltx!6pX7m2(t!#%DPF-(uk|Bkv>EI4&LojC60fC!QHHhw%2gJPXlNvY z-=g*DwYbsTfj1fR)eL3&m_jjX!`j5ssZ;S_GKtQj%7+DG7-qFU|<9*#}ZeUyX zNq0VHj>kDihb#Da&{nGbi_F|6G-Oqd7e2AjL*Gg@Xy&x9Z^;udKXCdhs;vYwp2zWh zt-9y(s2JAcUZRbLvmNx{r_EuU^S-Q8xPh-D+-^R#hH2|Li>)5A1d?NL+H{AK#JlH+J;QeG%2+2yMQf*15&(nFj;_D-2iOgI^`)E}l)+@Xs0Zs-UUS7psZP<-B`3CCwI_2&?sxc5Qr8~<&V>Ji z83I#oZL|x~&ebM+-j#JTQ;(x?X40H{a2 zCF>~Hh0Bbpk$3 zeTQF!`ZHrvXq$_?M!+P^L@uUs<`u)Ch$jNczJ#behAZ41v{4PFl z&7{{;B_X<$DgjB)5Bk5X92Ixr=fNu(Z+4|}XLcT!g?L6^P;iW?|v zol-ro^)@)UVu7MX?U;JGo>yz<-=`;{0a>8MH@w|BR_R{%&i9IGm5sg?wfw%Yn$$E6pf0wQI1QKS#MZ1Ff(TPUBKgk zE#;X&kqBSrX->g^vQhje@4m2ml{Qtn~7HLk7gIDmmkAGwtjt8JA z&|*I1`DW#{N85X5{pfZL_0tDq`r9>gCBdHzjH_Jshg(OErZiB&&G`YQ! z&>X2=g)_79?MW63)u^ZlVLcMac*}Ka{(xqj6Ix!ufu6qz&KRXdX}Ux0p-k^PBnf*0 zP-fBBPH(w!%Fgkx#_wMw;df>SlFBi8q;|%^Rio7Y52K8!UtRAfg^LD%I1XqAk!K7# z_0OU)Hcq{2lBAWL${$?Mta+!MF2gK;^qclt=+DBvRjuEz{|aqL0(2^v@gKLLAFL|^ zm|n7=9EM@axipDk^Mbt6S3K8(Rz zcb&z`Pj0@UcQi3Fr*igPYH{m$Z3Q>X^XV}1y}NfqRJ_~^Rv4D0?JzkbMAtH)yZ(Du ztP#%A&9{+!2i%DUdwjxwu&97C5SUzMYkM?R83?6CU)3N17@s*Nk8!Fc)tVoBtd*bt zdtd1~W%|x9;+!`CA=zLlShM&8v+dn7p!#}5sRWSa8E*C*K&n=rg`f|<9Pj%}C+HYh zI@cLGXu7q?l-}5q3NcKdoCKxXD;8@twCz+I21;BZ?nYC#)By^MNjg!#Fc&S(Vc4P- zEWZw25Y)AhrMt$#uoh2OwWO+|F-^}}ys?lS*R0(pQ_4e&CsTN~>6?q5o62J+gO>UX^<<+9W5%MxqbMqBl%O>T9-r0B;@Rw6T7>TUbadnA37#`4>& z?Poi@TlAFwAs77I>ECycrra7^mnFs@QB7bnp5R(8rTk7PZ0Ie#W<5| zNs@)M=G^#)=j2(Zd7y(N7&@-DiY(Twx80JO)sh^ifDCS`A)t$EmivpUH*G@yp!yWf zSGno^B_WE5=Eqo3g@Mmnx|SO{Ar|8V>zL9J%gyXdKDypuQvV#}m!yOX4|oc4+pd!u zi_!L9uLw<9HV2t{S*t?~2zlJ!cUbIx=MOzm{*UG5kI@BN?)U&de47TYAkHDS+x%FzBXs3u6IRnZ*-;Use8(S4*K^L zZijd6Xc2Jsg97D;xjj+4QjtNEXUwlW1%`gGCO7ChXTjV&veT5lI{MDyDF7wZ$g|nr z^NF!@o&gPvoCeIAkdboEjmND))SrK00|Wf!!F)bM(baJN$>Ql;N|$)CSbnWc_v|z{ zSLUB+7(TDa2qAo=&s?@tYo*#6`#NA#rL; z=RbA(g&q-KRBxNB{GGX2)3W7@?K7>JTAuh&d$_h5m8kBnxSBin>iXmo=ZviF^h!;X zxx4Ds!uJt1y52~^q_7F2d3dR#lTH{ma)B{GmclmK%s@>(=&>e!J4MFAH0%)sQ@soGv45*47sqAWR2aux`_b1d$IX}0GPaC zu)Hm`lVm!}@b&`^c)%&)N3{12&#w-!8V=o36gRzxkl7`IUj{Q<5{VJ65gBu03jy5+ zUz-xrip8Xm9>_rg9J z{4+!Yc=E|ClS1lpnOJEd%wPq=<9W;>Zn{efSFYOmaAWGmb!xw#XBx~ zq7ei32%SdUJWZq5EA;1YFKj2*M~7bF2i^_b%Utr_5ESI$214$Q_-Wyb=5`YbHdIAp zqrnIw1eU`M9c?NSW5vPn{L-YFiE&_V-)n4U{QW5~KPn18%FdYEP$;;^EC;yY7!yj3 zuZ5p+Wp%(W>6x2ER?UK(5<4GkDq`%23 zy4$J8Dff@P3QB+10fg&Er=6Ka|G2z;>cepH@(No~Tv5y|u00R}ZDuWYT>}QS#j`== zd_m0ysqg0(w|Y?#=S3#m0x5G35bV2swPZRMwF#*9Absuq`aY86Gb!nn3b^1Z z@}1bZb*k2BN%kXV2byudR&o9jsvOhQBCG}_5IVcx|1@bO3X4eeHs7r9AnrsK7B_|@ zH!NxmSo3Wx?Yk-}b0Ic_`|rExn=J0Gg;q-^wO{|C@k{@QyElzXI&b^_Yn*IznKE-t z+s!nW$X%d0o3h5zDa|DpTvAI-L~}#Hno2TDGfUi=#?mntG;&J?Q^^vA$`H*3aU&5$ zMOIPopUrjN*V)|nb^o9LANi=)tJlHtJJ$E{`JS9j(wmtjFwhxAQ|Kp1J8PI4FiHWs zNBTL3o9W-|WA3^l5B*jg%7>}f7v)XN$nMJ+H=^mgPEtdRNzd>r$VdjIVO9V!#%!DPxCY` zA99!X4SWA~?qCEf_2r9;%Ro0XNxxU>2@U-MN|bN~%*usW0D1s^i&R2QysTwD zNCL_q1=MnDI&tm<*ses`XBHk<9l(A$@$Mgvbbds8gKuXGg4}~rh;ScY;e4E3xiq8! zd~oii%oM>25!j376PAW$0EGr<%d8Be?JJ;lzYZ@e?Sax*S_l5K79_)TaUZ3!XcC6G^hWgvNz|4tNl|90~gm3bC@@o{n@I z%mmI${>-;f?ae7|i)^8WF!&4eom)f5y(XsNLS}SgLm`(+VKh9wV(L&>Lb4ru$7B1q z&tELE4}(nGUbj!f**8ep)6`)B6uo95`u|Z#?*T+aNQ>}CbgL1_Vir_aX9iMEKk;YL z)Ct$b2AknH(Wh0fFJQfwH0JUN*S!3{*Ji~xRwrre7W}`Q>!n_7!B(QSEC5j7$}~ca z_?PhNY=9_m)0N|&dOYtFqz~1x)@q=Fu`+cp#PLiyEI#uPkR;`Y~>iQx+(k(BVsgKYAheY6nuUP z=8WC4col7ERECJR@br$frbxy=1CkBfZjp5{$z>Y*4-#awWXyc>Kv+y&6#7xMZ2Ddp z#m$bi5A{B?$h{B48LMty-0W2tpPwY!d^%~{s*EWNbNTWQ8+o~p)BydK*qS97{zqn; zU^fl$j;QMN`1x?AF&D=K!}La36N`S`>e~b+J!IXt2~6&ewFwMaGQe8t1rFIJ>hq*` z`yVtsENO1|RiRFfojw|JbKBO`wugv`?VxCXb{l(V8#P2#4G+2xBEEeBZBpN8A}Ev_ z4f~SWFAPHpa^74tszieSr7^ARq!RAU*qGccVbn~TYP=pR;pHtS&Rj%juS32kKzAj9wfBk?Qi8qVjEJt3^KXbDqxMI1RZ+o^kw ze_?V$+UzLGU0ss8!h=Y&_+Q_1I5}}bxwiA2^CJy^S4Z@Ug}J1NEs`IfHFV6cj>&ed z0SJoG!46&j!7SE^3>`q)^G|LOK=x$^ea$t4=_5QlcMvAb+YJFp&kvAKLZq#(Nn%{x z!eA{Yu!6DK%Xqa@r`RIgyI}gG6zp}AzhTz8JpRE?m#>}H-ZOH~MyL3WU9CoR#5+X= z^5jhX8Gm3Y6Vjl!qK<8J-g%GMt9UQW3EX^wG`~V8y0g8-{_flT;+={_K;ngm-9M3X6e_UqQm2Lrn92WH8@7qwg? z`}{7!9WPTpyL^5GXfxKzp^j;5B~!=sWQ9*aP5n(T396f}b`s{+AkqE<8KBGsuz7w{<3=(7C z{iOYi7sXAEXp+46Xj2|-zTB6{1d-Cs0}&y76RW;-ZgQKxFQT`dR7d75tSl*l+}!J-*-XM6HRTsZyay zP{q#sR0Fr^+>I{IWl)%~B8)8ZpL5fwl+9mszi(P7ry1eV=54>mnMIDL+T8~Y&xux7 zhJY0@x*V6j&+8vK-8cOQWnE6*1fSd^og7#~dPi%|RGy#4^nF|e#OBnw5M*1G6wYxe zh1i>V-dvIwB)k_VhBX6~VHjM_7Yk=YHc8{a!D7oD0L&=a2*AG`W3@@vg^H-)LtRbn z_ur2^=ojC$h<}9(r2SsxsRGmW2O{i?kB5CDqK_E4Y>Y!y84-{HleX5m{PIEU(kDA( zwKmTOGAf`$WF|FD8%yWfjomhP{!}ItUJo|~Dn*HPftE_*V1Oq$_WzS;{N3vFbdY+R zSH7ydG>U?_1P1P~H3$)9sCgxg6-bIl1<)%my&B6kbu*pupBI!}4XO}@vJDNKG^N4? zeZ_~6G&z&}y7wP}`P;ZvoR}!+u3MVAt`qvI@!XRR>8YNMskm#RYe+%0mbGu$~7C*4~%_8UrEwe zEk`2~3V`PZ7Up%7vU*5^`>wM!IGOG%0OCz)>%`uKRnrP*qry##OD0cDtZsjul4n@@ z6w|!Ptpsd>z(__5817qDy~d_jd=~i#8wr=Zl%=-Pd~xnX@5#{mV((gw1NzPz0CeS? zhnOmC-7dM|t+a9aU$m4GlE$8mAwn<8{5@u(JxWv+5ZTM^U`jU)wYO=nd#mr9Fl$H)x2RRO3`L`p?WyH+J|UY8XeZfcKU z1kPI!JeoZbo@_=)y}i*&5@rocT#*atKpZjz=B(eQ?|&;CY;^|?@x_TZL30gwr@E)7 z8i9k9eE2bRJ|?7pv0F*kf&V!>=I=2Qu(upxkpYn9xfLXCxf_lV+`-F%(I{*=@~?S9 zN8s)dw}9ABH|Y7#pYDNr@PZpj-JOgQ&CLKOwP=p#iqfn4h@n&7fj7tF z%*|_~V%cWBQRNm#(kmL+lKJ@0cLi&@JKJm47hIYqHHBI;-63Ig?YfSOWI$ok?LG^V zq~M?l?1Lk@R+o(1HMb=O+Rc{A+Ij2#&4*LUoq-hH_Fyd zk5y$x9L2-d!|=Ql`0#uJo0FvK;{RV}5&ZT?lE!qf;zQsN!RxmcKeKYz=AS}i62piu>LaLEt?L8QK088Cs!qtM%1*_DJCJbAMb%*EPs3H zXPv+16Y|wczLWavmrmrGhyxwU6w(b468BU<@}zh~$pej0##rzN1f!R4c7{-JK3N0&goYo;DG{*)(&ml zzwI;r0usGv%AV8d#HaZ=Da~4G*$pW+Y1tj9x1G2*Hi_dh;0)BnIzJVd4UN|wEz8zc zK0VV`8WwT+PN2}ek~kR2z*aa}luz6tz9@}Di2ow3%*U#F2;B-7t6S9pA`?82N}Zn4 zvzA8uzW~46`&#bZnMQ@1?(E=#+TBmh^Hxp8L3c=8%so(CtH=Pv<> zSa-Inr}E;ua%HB{X@G~JdLJlHxaXl+rkNHK*`Tu9-#*Gear9lf1|YDV#-F(Jz(sdh z_uZ~uw1?4jI1GZakn&P@4Wzyw+ZskMN4&tB%s&YULJSae*;4+i)3ZO6Q(7lo%7Z5W ziJcHahVQ?APcA*Pvfy9P=z$5Tl~MdWO%3z2o8yJbeA%ddEiSt$IZ?)K61DRO(P=Y5 zFf>N7XcGB$$T8xsmSUlW>tn2%i4v@72uqI!PJF5{=FsFPTfQ?=Q#i2wNg#`@Sf`u+ z+ml?3=03p7hfC6>+NAVf^*4g}qhnv?J1$Q8fbZPHt9CTe=^UT@m{FgK*Z~6_rlenw zrL>#wnkxxA52)Nd0~BhoW9Cf%NrOpnu!*Q^;Fvd6%CxwX|?69XJqR!t-i2*&5wmnTn`IWg{(>i11vK$;uol~rau<1qrw24fWrNIRyz47mQ0NXIn9)&eTM^|L7%oD{>AKX zn&*y*>hUWtwCX@fYka0=f|8}Y)6|3j88sy6was8^;1;(-+^Tn#Y#Oyb9B|~IdvuvP z5fBHo#stS)ZLbTjp#_jOgh(F<*!3@e@stgu9HyozD)_C?sp zyM?wajZ?<@<>;El-l}a*&XkdF(<4YPpbPNJcWa`NJ8OKg#>x+{<4b~bRE@buz+xHt z*2)9A{|!nx>2yK5PF~zRLWntAli$8;q)6 zci}{PTgcoRk~XTO2=}H@$|u@m>P>_d6RoBG%VPZ``FtK8BfARBvo*i}9Ivk}V?VJ* zIILBanoN-7fcL_pGldP1Zo~|l6DGMtDO82>N2RaOp_2*j&J|zhnh$J{#eFD%3dwhn zIqS#0WZcAoI1AKbpa*$HHqBQ#u(C7{Tqfc*vvR66zj)fEV&Ajmr}T_MD7gSQcFRa( zSZil6-Tc)tPDa?Bi>fMEhG#_6Qi+Ct=aCXw*tx`HRa}1kXw}i|`uwr#UL$BzNB-6B zJ1#{{YpZHRoWXy|v8~Z#BmWaU7N3b*CsHaw>oQ6m%(L5FHzt#FCpy`6Z-E~#ImHME zZo6rhR^y%>uYEe1KwF={?;y5wH~wt{&M(vD@vL5ysJY@ZP2=vc;+X&R zsujs&lwt$0(*dEm3h!@SO$PdsxEt;pO-R<4(mzo0g}M&$zymP%O?^qT_Y`Wg8ErWI zgB9gDl8WS!e+!`P@eSP4`&&W)J8kCqdD3WnD}Fm~$4PVAI%Pihyg!TRVV3 zZCT{wx0p_`jxTf3$qyfECeHi^a}|1AvNUG?SDDgwjE3e*G*OqMTD0ZAkPnnnNcvf*CNxwbdSduiQgC0#GGZItX`(trtd zT-b-43JXamC&Zd(r=ihinAYR#vqm_%k+jY$(BwuX=;-~3kc0YgdFcE_7AcV$=QhxJ zFVF6xejy_;pKHcJX0n>wF+my2hv!mH*q`07@6|oVbxf(KJhk3?Z;T;o!9R5(+P}Ad z#j=w7SpO6z(c%RjR#?v{Qn}A)@DBha;H&E;{(hdEP|LGC43}#@yigu6;ZAQc@6qsH zj@{^;B%5vIuY6q9_QU)b76qJ4{YTS6bsfR4OyPYgRwsRnW$=<|ped<{o2ezR^D5w+ zif&-)57@FE|AT-pow2nDM|H1?fQC*K2OVaX9ES?$C99dhsiPRsj0(FNe+8ZuFc}aN zapP@=_VzCt)R5r|qz1<&;IjKDiN?r;!z#7X)(1Crh#oNk(Xs0I+>`9!d5=bev*O2f zldrHLv``NL#mC1SJQjYCfmUT62%b@K>qAoYlgXD*b=tqHdLn)advn*T(X6T1o!7Zd zwJW!c!y{7q;qVeQ~%-y$o$<0o3zGygxAZGU_v z26U1ptn-K--N;UGt(C$l1ZG~y25y1NY4dkM7zj}(rYlO)3rY$G#usztUqk_nbRC0m zb6q7^AJ_dvrS#Dg#u`=R3XS(DLo!soyq%g*e9sNT(Iy+`FUCL3^>D{@&rNUwbVpHhHFv2HMdJ_3VU;yjf4H!arLsMreg9ELeCeJ$6ftfznWEKh3dj`cxX%>72YK$ul%^n7BE6jBif8AEX*ZU}374zK6sYUV9L@+5z!cD0xB(Il-K<^9G%MWR3{ijLl*Gy7R zpFLdwEwM6AU-X7B!r)D`yh?n)Ra9Gx*<7Mh7kQQ3@j+sG^CpHf6@=jr$$yzUcH&*0 zREyr@=eM+_cNu;>cgiE!s5?GC#pc%&$7MqD)VqVuN<+V&SHV+MGSgj&IZoy%O71|F zl^~!*4HB|9n2&?gw`l~gAxMHZyJdzX?SRxm`XnT9;jtNK;2rV9g9%j zCiLPvf~Q1t?Sf1*JAr5Db-ZHA#C)}Sh`}g(hQzb;T?V=sqxFC?nelxkzyZw6l)n9m z^i~9#n8A>^XEcuTS8CFwW~nW_$#;dAtAf3`5Ac!|{i&=K55&{$AALw{&tG^exrZwYzyTxq(?xw;J2lRAu*I><#D}N9Cc>v;9V4ox zSyP`Km#f-YIJHT(!7AacglZ8JeH||T}iis52NZt8vNnS`v2)A zewx;Y6+k!axtVruEP>);5SWl45WXzzxGs-eLBQBZrC;T4HSKq5K9~J`s96$=VE3o|9N{OvLbE#Mdlfgj4S#asQMFTSe0noeDHB_}FQTA;=G!y2RZWlI6E zk#UMmv`1RU-nI|%j~)iG}e+Ug5Cy@F6WfJ%FhD{uR4ST+&EOJ zTT8o+P_~%L@~HV5Kn#gKC_>t5^(OgkgJ^8h3ErL--F)|X+|Eo_vh+d>QVamaOYskd1iMQ0CjoN7lJia?MCpdX*3D3L$)2H;e?QqN&#MQR#AdaP0xFyVq zJv?rM^(2%`vzgUP`46U3lFDLZ>ah?^XVu)$ z#yuPRc%0B`&u0+c6a%rdWM>|dQKtdZXJ_HiP{J#LtP6j(0lcF0#^Bw9u!_k8R@O2Acg0D5(H(Bws`YOgH*0Kl__F zdW0t|y&Iy-8PBayu8qF!e00xhGwFAs7xXUhZcF(0p~i15)Qn)`{^-?T>h4rHFYK=n zo#HiZUh4mRuJxu?c9M&Ve6Cp72AX>aOYP%Ugz z{5+Ro1bZW^tI<}md_~O5(A=BL`p|vZn3$CXB3lb`7QW!7xYL&?PjGq>^SsdH zmLFBDTvmQZHr^4v4YZxOyA2fb`efIYuK{0U5$!qSMiEeA4(0Yfh?lT0mMjn8F{-Bt zdtf2+?KaJNeO>o?{-1-!;0d#>a-q<5EL7r7g+nr4^gV$-#(`?aKEF!Was8Q&#hhP(_gznAc2HpZ`(6< zsYJCtv(0CbHT|dYt&cPHEBk-SlK_$V8L@7BM+afWml!RiY&Um{>vn4n_Gf%l?O)=Q zxz#gL^ISfy;J3#P#57MHR&n(g-3$H8aYhs+p=QtvAArwl9O)R|z>TI;4;IU=Prr5z z6T3Su%flOQSGWn=d$Ato;6LV4;dIkF?c}?Wo9x&|SbB~r6ebN{y`=CCo&?-eX^@g* z{!#AzcWpzj8WH2&;O}U8)nqy&@MPDtq&j=Ge%3xU-KOC()t=ypu@w(^kq2CJSa4vI zb>eAaG0@!~sx6@biCuDKvYPabGh!~(?FK|>6>r5-Bim6g5*&$#EK~cs;hl!2XKng< zyzpNNWt$r>ng&}aTM1h`ii0)H*tZ7~a-1IdV9-_D5~{R+sOFblk_VKjVo73G-&m3y zH9@o)h?DcEsD%Oe4xswO7(LqY#T15l@Yyec=JSim(61c$*U9x-of9xFV*_Kqd{OVQ zPX%D}+ueoZE=0`dXKKKEeMF`jnoM-w`+{B^A;ckH)^O7G5#gKVKOTg7i}%eeOfaUY z&&NtuR)85j<|pMn4*y*iuT{rs)Rxs1deW{s=|&!E*nZjeC6=rzomTEo0Etfcnk*k9 zj5#L#tB3Xcs2S7_C%ZgbOOOu5)}ee4=W>8;za!eVhv{QBLZ>w+iUGyfm23XF8OMHn z8fTeZl=u9@5y8&Jcv~|0z}4PXlUh%8eE)CJhPJD|sm}QY>6f>rpL4R1P6gXUDVs4u zm@ZOBuyFsWaA5)C%XBc6wAAM}D_IOaA2}5++~yk>RDy*YQ2K?W0rt?L4a1sMx)uuc zqYLHB<@HZ^YnTz(xg2yJ+wzks-jz(n8WF5zzKu-*Wn_F0@P-d=YgzP(k9ukLWhC3S z(_cp^b8Tg(5=7x5^jCF|Bk$i@0QG4l_6_|MrpAyA3+R0P{OZ*IVJ<*;N6ee-?irZB z?(#XB10R0eJoN-&ycC_ZOMm+6xjI~Xi=5~Esi!{AcT)UiXfFPj9dwN1LkCn;kKmft z+*2d`?{cBq!z5Tj@cE_Cf73?Xu8v;RJC$4bxt#7}zZ`q!D7hC1&_)O7+;HdCK%-M+ zTBQF09!=C-*aUVeR8NjK-=9akh@Pj3CLR5ad(QL$w2>Ka$+Pgu4uCc)-}L%{_r}!n zg~oN|%^5uHufGRWj#a0u;YN0)i22Y*Rqb!a12bKqj?H^-HS5OyP`0t%;2Unl-Wk{I z7H7mX&?+PIt3-XF*t)?IdmE(s01M^KEp@J*n8!+!fLd2|)EPQMICpj5e;3eeMUg`Q zFiopVyd;>Mnu&jH$hDbg=Dn=Xb_NvU?^zcw+cc-8OK%rXI|lh-Voika2P+BDbbkI! z>=m_`>x%ZkUR!V3&}kJPFaqpK66OKy2++YIS9%Dx5p|;X7!7$pzv(3kvhDV4pHtro z5Jw%Opy)jmjg5J#COST6<7ol;Dd2J*Wx&7XDE~dFqetlFZ85WxlxS{ROF+ z`z*`3TX*JFm4(rLD>aeS1`nCT_7;aUpy-@YS9I!oS;vp9rCB6`Rdx=LklEYn0;>*0 zA=lYeYQ|nTE8MqlNw00E%6&y#RG;w6iP%b&L}3>4r69$>B+l8^5U=NC!!Wc|io0T# z+wESpk^ots@;2k&edxCjt{dp`;bQsZgW7K%Tw=S(tg zO8XnyOBN8$c)?P5;Z%nlY>>)()MaJ3%Q+>93IflPhgH6 zP(Q&E6ii&=CF=4Zp6{H$=p3505iUX&z;3|1eAOCx5d;DwLTa0t2#AOF*jR`^vZPl+YB-0UsphJY(33@lvT>vwW4VzF@sA-y z6);py?eZsoMNP$$z?famwV~HddTsE4m|?Stu<7yj;icg(508xOdWEt?_&n3JOqdyM z{7aJ)*?1q6p;j3^KP7EF9#zx+_4yl^kWt0WGcmC1; z;07in?jAPEFEg^gEtufkoVlyf+q~T~KYJInD+pq`K3`0N|L&YWxo+0hOLHotc3{E> zH%fV*(vlqRbHUd<^7Z59O99@HmtVy8=E7G4L|{BAb~IIth@o7BHgOlK?_>@obQEzw z<<~bVIMi09B9guAm+|o7ri2<%Pe;bg|KKyA`1)PWRwsAeBoF@)gVh2;&3=5{@+QI< zXYuxAf_+H|IIj7K!m>#oE3hsoe3W#1J%Olo9J4t(GkS;OilhT7(|HK3#E@ZS>+xD! z%uUS$kH+s?+zGqofV>6L?2hVdJrtbdbm2klN>pB0e~N9eH9KVp!59NEZ+=9DbofrX z@B=pMcZGh2S=#Ar%Ur(fv06$30Xmy!kQd2?S;$AU$a|_iIW2#*O*(R`1sfCJ>uUn9 z!Pa=GA2!Z=+t@dJd?lA|SSjsm;0 z0dPY?(Ne7a(2kfCd3W9`D{w_MtG(p_DGkmQ+VpV}?6D>GaZM>VrMqI)K!Hh{6q{Cn zP0~+iogZ}gwajJ6ZEwh_7qq&9wQpxpB4qr2B6hWs zEMA$!DG*i~;Wm_{s%XrwuTLuj@@S$z#@kB&SVsDu>mvXPKRe$Tjqf)*tr3-GF9dJ0 zF)L%yRB`0tHvgj33AGRcCcXeS-3cpS?4 z?9m&_*0Hem-!=1Qf6sp!L#*J}S!pGCf9mCtOJt{J0tLt(0p^8>z4Wq>XP-_*>h*Ll zo6or>VTbesZ=qO9@#lc~J;_r~-VreJ1&H}*p}$_>Cg8zTr!IHnkVYG2AAoke5L?@O zfw1tkeeO~Am}LBbalN?0hZfdB|tftG^jwk0hc~4zPnvv(g_xjY-Wa(3?*R&UFKUuyu2po`9bq<#wx3 zO?=TmP``|=&I64HlRw;?NARP3_$~r%3>pD)P97n*X@Zz%^%{QWv$DTxH!C`UD0-UE z%$`q6(2U=qI4N8`^ePGQF~PU4C?fv#E@w5NL@B<4_S3>VzU_LXSV_)SO^Rpd>BC4U zY1_Y6mo6zr1s@1DP?mIgYHPn#v&3+A>NJCm<0pT$8@BbO%$%rK{9PXPf@xsl&tHB% zAU$o)wuJufF4w~qJQ!9ow2$?OTnsc|_p}X(BdG)PqIy|SQL1xYCRp4Yv;_Y-Z>n57 z@`3t(a_W4>F5*^=K+_K2o`rXXUb=R=jt|Um$mhh9p$fJ&vCZG;Bg@1vagRTPplO*p z9#|3rA32nKgiN#+>KvV{D{(i-pLOj@0#rFCP%?zM<#!GQwC(H1XY_}g_QWSv(oPN= zBvoZ{L{@~^Or~`KTC?62Filda^CSu;Xo5^md?k}7b1?F=_)LlbzkWO#IZEeL0^P<& zf-f0!ZJji!_vqKKz95z`*d-RU1JanY%@6`x;qi-xRc)pyA1T>Af6<-ltje}bdpotg z0S7-XT%gqXT@q5A`d;AEA5`V3WsJ@wI)Nvl8&+ldwLJD+RhXua5>)>^zTli`$ny~* z4fa4WsAWLCtUC6o^5;Hd&O1ZgkXbh^MEK;HBiLu$mgLtJE&WOAVQkxuC{4zL};Z1N~Iy?QEX8mQw*#*`UM-sx(4>|?`dSjQ?=4n9oK0@4n;$hYrg|vA{ z2}>Vr*TvG0;;V@VM)Qv}kRlj^8~_BQF^XG>EK!?qeY$8G&^G+ zTlS?&+$WEP{27d96g6ij5nyHnV5dl4|Ig0j{aW3)IyiZ*b|5;&WWiCI=}`Ddrae?BV`JfP}STZ`?j0^hOT%o?LMs%mvV!kGY$U1a$a7UGfk!4;k~gUfjeuiQiF5B8IqtZUSuTjQ-&IrjD^qK z@dbC!M?>3Y_gJ+XpR&LD*CEy7`hdRH8Ay09;89^o03CDh&UgNl_74-)%2W1%BJDdy zJ8B0OWQa+RhxUEFeG#S@h?R%?dX6WdM5U3v;C$4`mxpi|!?IAF;14_r3 zyV8_|^qj2)n!4LTY6^~flivl#?PNVflp5UU6%6zrX;^6W_}@v4|Rei~HMirRIsa zny^TN!!Qc3ipca%NF17TlQ2GxQf=Busnf@+{x3xUg z{1&K~E~v3ZQQfNSU9?>I_Q;}^HAbt06Tf`)*?VoEiUgV#$qt2OS}x0dZ6!ocSr1hM zMk7wna_OKD=+11G`x}H-yU}1StTs>QzgVNbZWNEV#u}9?Vy|DYZ%nxFyw&GD z;mK8pBueC2pho$#Y*|nV(H&cOt<+s4XJD^2tf5A=phPiX(fjs$YbCqzFkkd-7hmws zxS>js=Xy!vh3aX%)@dqt=yi#kV$9bN)XE9mq2GN-J?eU*h=;4ck3#w*XZZ`r$}hFM zT;~qsU*;dMZ!UY{Jh<_~!p65@*yHWJf4n`sH{VH;I`t@4^ZhKg|0FNNt=#*^+Xpn< zgaz+op!B(7`}VfJgA#fGZ*VF4$So zq}9S{o5b=4M+Ixd1n@YM(IhhM>2sTO??w+{` zO3mEg1{+9lkDbr5=_xdFda#g%{4`V5n5i3+58tsK|H#Dj)SKpn2K9HoE|kuF{p=X? zS}>jvJe^vBWsRKk{qX}Z=F|ibBZzrMX_}@eG|LD(qz9~RlT++=7PQ>h^>(-{J5u}f zPFR8Z!W$;>p229NP8V<_2Zggp=&6kez?FQyw^8Od8#Mqn3a%7cMZ%GSSV?0Jpued^ zfVB;euNf&i;1AYCtK~wZllS0+4@dmL=VgMuF+B-*fi5wkXTC(sOb{=g9ZD6dmjjbq zSl!pjlfl0P8|?JAf>sN*Mele`aaRF^c5k&WF1)DrHh_uDUSzWJJC1)a>HV|JMHScs z?qB1K)OwVzmX_Gvb$9TN_kU%|JOgk>r{?pAEcCtV!-v$9`%tl|M;f#1UmD0m)|RiP z7j(@YH2j$Kz|iBw#JqalXP2UI>4XQs8AZGdjCliOx|1At1F&u)8kk&1U0RT!JF9_L z>3V!-XsFBjW^Q5W*F5Sw-e=Mkf$7z}7D!TX2%qa+nE0PF^oJOZ(5cznPNm zzpbScPDTh1z(-Y*$n$ZyFLOv-xJob35!EO~?~4B_{TzBkxFe9Tv7V8k&}P%<6V3^Ed4yT&2F^M2gzoUd)C$~) zDT7ta9;{7tg$;We&Xq@lLd|~fJpb_X9|7&(1f4V~ldxi=JjZSI6EQj5vhxLI`4h+f zn)b$LY`CR5sYmWO!h8?M8T6XCgKh86{;b;b+r#E)L==Na)#%Bgofkfbt@>uU=LR{QtQnX;Y^O~VMhl)PysnvzzZnRoF2(zry;suK=hQ^% z(-#pDM=z3ZuV3>iSnJ@p_fZ8v5w!d2x(3qkHO|K^-OY(tt^0m!Z^!|r|2xgG+vsi( z7;u-aef?mfa_vI@W+fy?jxv+_e_~AVyMi+oy6lV{#dJ6Lz&{Hx=XGN6mdpkR5X1;W^V83y!dBBfwRwuMFaXIJ+F)JB+ zowx&}(FiCUS$=x>u+TIw!BIMCGUcCg>r9`myECNO8>Fky6^TQ3h7}hYfc03P`&CD!!Ow%l+&gE^k~=Egu{Rs1=A zwcRP}0FkapG0d^~wMWCK)Xu%GuWIkl;jO1S5yR7WJb_0OKQUMv)21aqpB27f{c)Z& z%nSVjlmid&nzpuWepGkUh1M5@q-9`}&7zg)Yk2wdBNZb6Jcn}J35VQY6CUXx`zwP_ApWJow%jmv2ic-t)KZc zb2ai{PW9pXr#C>%A~voR#8>9_#l?141%>S6`Is1+Kg-XZeKUiY^4S~JHeyiqb*Ukn zi!%abeCf|q_o1TtjX5abT~;>eZ`Db2r@ynSYiW`dH1OK)Nzvu-#e%1CuWBQlrYpi4F&j*uAB!MKrUXrC0!3ZXvNy~&j^0Ym#<$%Y#J4n1&>`9bA0OJ_FRly;*$y4vK8)875CHJu|8 z*g>zcaAdCRV?wEcVO_Mvy)jkb++8x8w~KTs>QXp*?@K#dgD7$S;=;)1R1Jsu1u+se zF~?Bz0VgRe(19ZhfQ&f$T`z3@trx}*G+3R3&Wvo&-?9J;{<

D)bQt8=^%55 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¯=oYv~c#@#(mswRYT? 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

nx+G@icFY4x~P!mlg=U-u#vOWz< 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

*k;B!N<*xC7{1c z_X`oB!{;>vzgbkB%Uv8k%L@{y9_zngv;qMQkA*-gN+q`h&&-n{;NP~bE_Qcp=`sHY Dw%LXw diff --git a/examples/models/models_loading_m3d.c b/examples/models/models_loading_m3d.c index 452a21610..06911bf53 100644 --- a/examples/models/models_loading_m3d.c +++ b/examples/models/models_loading_m3d.c @@ -21,6 +21,8 @@ #include "raylib.h" +static void DrawModelSkeleton(ModelSkeleton skeleton, ModelAnimPose pose, float scale, Color color); + //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -41,22 +43,17 @@ int main(void) camera.fovy = 45.0f; // Camera field-of-view Y camera.projection = CAMERA_PERSPECTIVE; // Camera projection type - Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position - - char modelFileName[128] = "resources/models/m3d/cesium_man.m3d"; - bool drawMesh = 1; - bool drawSkeleton = 1; - bool animPlaying = false; // Store anim state, what to draw - // Load model - Model model = LoadModel(modelFileName); // Load the bind-pose model mesh and basic data + Model model = LoadModel("resources/models/m3d/cesium_man.m3d"); // Load the animated model mesh and basic data + Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position - // Load animations - int animsCount = 0; - int animFrameCounter = 0, animId = 0; - ModelAnimation *anims = LoadModelAnimations(modelFileName, &animsCount); // Load skeletal animation data + // Load animation data + int animCount = 0; + ModelAnimation *anims = LoadModelAnimations("resources/models/m3d/cesium_man.m3d", &animCount); - DisableCursor(); // Limit cursor to relative movement inside the window + // Animation playing variables + unsigned int animIndex = 0; // Current animation playing + float animCurrentFrame = 0.0f; // Current animation frame (supporting interpolated frames) SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -66,38 +63,16 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera, CAMERA_FIRST_PERSON); + UpdateCamera(&camera, CAMERA_ORBITAL); - if (animsCount) - { - // Play animation when spacebar is held down (or step one frame with N) - if (IsKeyDown(KEY_SPACE) || IsKeyPressed(KEY_N)) - { - animFrameCounter++; + // Select current animation + if (IsKeyPressed(KEY_RIGHT)) animIndex = (animIndex + 1)%animCount; + else if (IsKeyPressed(KEY_LEFT)) animIndex = (animIndex + animCount - 1)%animCount; - if (animFrameCounter >= anims[animId].frameCount) animFrameCounter = 0; - - UpdateModelAnimation(model, anims[animId], animFrameCounter); - animPlaying = true; - } - - // Select animation by pressing C - if (IsKeyPressed(KEY_C)) - { - animFrameCounter = 0; - animId++; - - if (animId >= (int)animsCount) animId = 0; - UpdateModelAnimation(model, anims[animId], 0); - animPlaying = true; - } - } - - // Toggle skeleton drawing - if (IsKeyPressed(KEY_B)) drawSkeleton ^= 1; - - // Toggle mesh drawing - if (IsKeyPressed(KEY_M)) drawMesh ^= 1; + // Update model animation + animCurrentFrame += 1.0f; + if (animCurrentFrame >= anims[animIndex].keyframeCount) animCurrentFrame = 0.0f; + UpdateModelAnimation(model, anims[animIndex], animCurrentFrame); //---------------------------------------------------------------------------------- // Draw @@ -109,52 +84,19 @@ int main(void) BeginMode3D(camera); // Draw 3d model with texture - if (drawMesh) DrawModel(model, position, 1.0f, WHITE); - - // Draw the animated skeleton - if (drawSkeleton) + if (!IsKeyDown(KEY_SPACE)) DrawModel(model, position, 1.0f, WHITE); + else { - // Loop to (boneCount - 1) because the last one is a special "no bone" bone, - // needed to workaround buggy models - // 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() - // 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) - { - // Display the bind-pose skeleton - DrawCube(model.bindPose[i].translation, 0.04f, 0.04f, 0.04f, RED); - - if (model.bones[i].parent >= 0) - { - DrawLine3D(model.bindPose[i].translation, - model.bindPose[model.bones[i].parent].translation, RED); - } - } - else - { - // Display the frame-pose skeleton - DrawCube(anims[animId].framePoses[animFrameCounter][i].translation, 0.05f, 0.05f, 0.05f, RED); - - if (anims[animId].bones[i].parent >= 0) - { - DrawLine3D(anims[animId].framePoses[animFrameCounter][i].translation, - anims[animId].framePoses[animFrameCounter][anims[animId].bones[i].parent].translation, RED); - } - } - } + // Draw the animated skeleton + DrawModelSkeleton(model.skeleton, anims[animIndex].keyframePoses[(int)animCurrentFrame], 1.0f, RED); } - DrawGrid(10, 1.0f); // Draw a grid + DrawGrid(10, 1.0f); EndMode3D(); - DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, GetScreenHeight() - 80, 10, MAROON); - DrawText("PRESS N to STEP ONE ANIMATION FRAME", 10, GetScreenHeight() - 60, 10, DARKGRAY); - DrawText("PRESS C to CYCLE THROUGH ANIMATIONS", 10, GetScreenHeight() - 40, 10, DARKGRAY); - DrawText("PRESS M to toggle MESH, B to toggle SKELETON DRAWING", 10, GetScreenHeight() - 20, 10, DARKGRAY); + DrawText(TextFormat("Current animation: %s", anims[animIndex].name), 10, 10, 20, LIGHTGRAY); + DrawText("Press SPACE to draw skeleton", 10, 40, 20, MAROON); DrawText("(c) CesiumMan model by KhronosGroup", GetScreenWidth() - 210, GetScreenHeight() - 20, 10, GRAY); EndDrawing(); @@ -163,14 +105,28 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - - // Unload model animations data - UnloadModelAnimations(anims, animsCount); - - UnloadModel(model); // Unload model + UnloadModelAnimations(anims, animCount); // Unload model animations data + UnloadModel(model); // Unload model CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; } + +// Draw model skeleton +static void DrawModelSkeleton(ModelSkeleton skeleton, ModelAnimPose pose, float scale, Color color) +{ + // Loop to (boneCount - 1) because the last one is a special "no bone" bone, + // needed to workaround buggy models without a -1, a cube is always drawn at the origin + for (int i = 0; i < skeleton.boneCount - 1; i++) + { + // Display the frame-pose skeleton + DrawCube(pose[i].translation, scale*0.05f, scale*0.05f, scale*0.05f, color); + + if (skeleton.bones[i].parent >= 0) + { + DrawLine3D(pose[i].translation, pose[skeleton.bones[i].parent].translation, color); + } + } +} \ No newline at end of file diff --git a/examples/models/models_loading_m3d.png b/examples/models/models_loading_m3d.png index 9220419ad1c9124b887dc019f0ecdf776eac1f31..101831c60eddfc7b8042660bc502e9dc7c4dfb32 100644 GIT binary patch literal 22930 zcmd?RdpwkT_cuPqG^UI(I%p0C)2NixV4TLG$+TxgB(*!}u!kH5DQO%MHKNgB#-VgD zrP!CmE^XD+-ANHi+2lehRFsNJ36;*jb&Yzy&-1(Q`}z0x$M2tBdz2J<}U`r~lA<~W?rbrKQ&GI335Ee^MQ`MkMv7ALG;^Y!!lS6dYe zxePqQ{xAQ?NIFHRpQ%9#N~QhBe{>SJS4j1Od8H~vg_NZH5C3qLaRSk}%K!W$+)$>U zg3`0lxo-bW48gysv_83V%K85)!=(`!PXAXKFcF&ni+mtL|A+{S|34K$$2h25_Gutw zPtxBfPPj~J)}L{hc=<Es&uBS6 zI=Op)V?dm2Q&5a)WB|Q`@79-&ZRfFK#A##!kJoxl!D)K~=G+ zJvr_A_JFPhz5k5=C-1;K*9nxO%+>Gg0u&klW|9(k6&Rex`TwRbdP$(q8g%1p#@=Op}ha9H||#tiHsP``#tmD_V1_Yjn)K_i#f70>i=ING+GdR^Y}M# z*O5w>|9yLF*%h`7lmA`Yf_0?MSCWIdxhbj}$B0vAGd};%uN*4^?b?=apSSXw{&yS0 z{XuH{bDT|^4c%wsuZwMk5)Ficf&aO#f-U%GEzxaTgl3Jc+Wgnw6Vh=gD{C*`*J69x zr+vwsNqt$&nN_RI3To3oZ7n;txANu8fRHHxUk(I3&(tIo#CAC^W)7zwOXan38UM@q z^Jfr5ALYuT{9ityU~^5FuZBM;l?`fT=+j?+=CcJcdTEV2>`ORbswSV#a@n(BT>`}+ z8OhzEH3x}@MAKz3I(}u|UbLTHpZjYu!#}O+S5zjgbJ;xet+Pr|6n1r-1h)6Ua|3_g%BWmULWK6vONB73*S8D5zUU-taGTz^% z+|T8T<+NF(hX-sDgRNt|*Z8|kF>edtlj@-tv2jtYl4_uCBhJ zf3t2ubm8xxCyl41T{oJt-nVO7OwP-x-IRzMdgV`bmJTm+Nk)TijX&Q|BTYRxH1^P* zjEpU&hrRT~!;HU0emEIMBIp^o{nIj&8i-aT|jgtZr)6#UcEJoR<{I=bz* zQj&RW+R+xH#0XW-IG3jRFKewp2w7oXOM@WU5V~ zdVGXM^TyHlqI2)&$2!pa4l0drXfLgrMj-Z)Nv9+4my|C}YpG>TkGvoFebT-1>X)3@ zxy%C>FNnHJ_I;AF*5?#?2izQ25S_Mt_NiQI%*TUSjK8b@3Ojr+<;k%h^Clqn6ZQ$^ zO_Nt5-n*rHEySRf(Dzz36wvUrf9 zQ;?F8OwCQlJ-zq$A3rihBYKjO$iXB(K0o8sR@uA>$w7N_5}2zZ>=SO&R&KE2d)o&X zYC&70l{T7&Yon=rCR@JVA+?RAKx<~kA~{P8no;7czO$zG1J|XlswHE!%Wt8ipp;sv zTI;>}5hQYJ&6rD;OGy|MTQr0%2X_m6 zn_nw7#pOf^}*OGdyok zS7W#-?8Oo%tuXmoj98QkTI+($V_uPmQv{^0=gp+k6y7xDP{V{7OEYHfGb#NO1WACL z0+=-f{GCKf$#yt2=Y%C|hUz++LF~10J;)s5l%AdA^;)%m74kbK>i?ieEM6$4edFwC zJ!^re7PZ~z6>PUIZT`n|TCuq9GCF+|kDypLV#kj_lpx6ClbkMalpQtCq-!FgtF6vG z4bghZcnp;<};+Ui^9eL;6HkW3g(>5Yk zh)RLm=OHl{qatS6kax`x?Y${$x!G{g&bCbvJYT&(eizO&$&8yXIz&O(g5@JS+$bew zQSrJDZb{6W^N_Qg?#b^eTPtXN!A+-%8O05i(&K60?;6G&Z2oht3GAZ7G?7q#VPp?; zP-?|kd{xO5 zO6Eyq17+u6b}O46t{OspM~Cvwd{d_*&+bcgqny&Uo0Dv~n@TG=OK-`zQsCrb+eYHJ zPaq#JW2oX^zjGhUDCLx{QJFSlVN%f^P0~6B-_)PY4d`#0b&0qlESUEueB4LjF&oDw zW?oLscfU7fsv5Tqr!!9|3B9In%3#fD$NP}9Ak$9KTSZbD|4B}D_m!^7Rb{L*Tsf9T z4xG*SF|_kXrAb3-6fU^fae_!MTEyJ^3q@6c7J|ur!GhA7O6f5tUXxCu>%z;rUXg=h z{Z&tdlKg>gDT8)!F+YHqxu2ptK?~o)GmAjN3kE+3qWM*UJQn$HnPA=;qZI+PRi?JX z&3fmaMro>SE<80^nrjgAUb=-9<>b#9@yQ)es1-~7D#>QnWg=e>W|So3ipQv>hck<| zcjXy-s{ROmkHlVY^kE{pUWma>EKV@;BT0V#JrzS4OZZh&(Xo3?MPg_1683g0AGt>$ z?`@`1!&MVS>#BkQR$-CmFlGh29L+3Zt70iGe#LWK5GI#wLo{*n|@Tr zO&B{Uh$V|$EK;utkXRFgnls@VHQSvac_$9n3*xb`Kr#3bVnmn2HbLq2NM6?@4~b=l zZHB|#-UHk8Hh!k<3XgZ%?^gSc>&IzE!UOw@W+WD6q8YdPrv(BoS9q?>(9CzFO}|pG zzdn5+&KF+F z8}kEQ?HD!gL$noDtncx%@IZD;F{3Xjd-gcq*sw+7X8q7adS6awgOF32y31rN^4=zy zKLue?$5d_N6;Re1CR!TrLH3y@H~kYG5v?kszbwklrXE39l!2yyJR2f87n|qIkDy1r zUHC5((>^)X=*We!EYDXxR|$$ay2KS-&H>A8$kVxtr{6;xYAP%abu~)Nq6AG-`p6&8 zm^{CRejvZ+mvZ-ts>a73%!m_YT@Te%)^gY4%Jo}N(bGmRerm~SXA{O%aut=lJ%R4y zqI;-c5p!{N+wx-z+UQYB;hs*m+{#8iUio2h`X5%?+P1%z241h=b}$cVX6T7r{t`OA zCo3*6`=ZI;hwKgx4oi=(=`TEro+s#5l^xch_XTs6_!(@R_rz)*SFE3&eJHK?K`k?i zOL;4sn>oyi;!(cwc#ZEzPm6sleHthcN-WN~DP(%Vnrx?l#aZLQhYdsYmo77S$hLe&U4w;U~9m z-xk%~gGfqB={lGqMslyRxoU-Ikx2<#P%N~84B@k=mMxffJXq6gW7|CTRAP8p{CuL1 zUYc5m^m2zX&O;Pe;2Xhq4~Wim!1r&*of18fUZVRrw!FzwDte@({B&Hw2c!90WM;3V zZS0oZQMU~D%RKH;NN!?7ahOmf5*7Y-XRC&5)_3L4bX>_7g@}nq40MtVbh?M;yp-IM z)@CShmIUd-k1EA$>E)eA*n+*3TFGr1=GPpd!CWNM-iB7Ykj%gq^u0FGH@ z*Bohn)iE+NYA)r}?X8wC`cOr8 zvf%;XTY9yNM-DKzskUPCK4Gb4s$GqYCUho zX9r!yX1!=ls!M`;Usb^=T3P5ydg=G0j*`VmWGZ)!T?%n>6v?}}SwoRJs zG`H8Hs_mcmee*PIMtSzptd1_er+wRMnrq&k%17r}eRA_-`?4PyRW6B^Zwr(0j-J7X zI4Nc5U!JVelf}&one|9@`qQ3H+?z5+eWs?bJEJ9u-*!HK{qf*pO^DdVd|A^C&t?1V z1JK5;IYEVvNup+xzNU^IPltD^muLecyf>N`m3?5nSnmiayuK`OZxmC@m_xn9Ke1I*ER+1VXk4Dz(~7V)1&&8B^eI;CHT@ifC{l>;b+ zq`Se*JL|7AXKWRf%kHOk@c14T-0n)h+zIr)wd99|4l5bEI2S8j#`{0^+;OzPf3+QX z4R_D0KfB7j!Zf-b9?uKo(=tDNri(0SYdH~!s=VtEIsJA|pOj(J79M?pH$L5KOCYUM zVj=T_O6M88kWSxcoIQKb<^eK-3nMjNzRvVX=Q!F>&WpZF*Q`%CexsnN#Kb3EIGYu8 z;-26tBf?6oXrEUK>$4p+zBoGYLU0=DuKP`h+aFiFT$+(FBeN)YC7aur_ZSU1#<7YD zI>FSlHE8_v3d=f*xRgXb&r7$GDPB!5!b0?*?$rlb1$!=t^=}kRz;`>4#m!HpUQ-{E(->n$*j1WTvk1phSjX9s)xRGW3Cvhf$bgUlT72AExw~?$j`D)RUbby970k#ww zsna{PHU>G%Lc;eMvjywgH&{?4nZp2DX2of!jDOeym63ba50O=S*MWvZ(xQytjafGBdmP*>V2T^_aINql4hWXW9nb&mN1{M3ku-b^$=x z0W}

ebZKLmHB9^30@~=QN01YLMP&u1slY;#ASu*(raLlg(S^G{eRGKA(|KGcDf1 zdWbzIP(LN5hnnetOJ5LBc#ljfb-AIt+#z%S{3ed6>F z>5xy#VtJ(Bp7gA(t)1CTTrpX^F2pV1cY_RSeu!S$Bi3D%*eN|V3AQ$SiP)bL&3lH< zf@djBPxWr7l?fEy1PBXC<&$-m$Cf5KI*6@YU#8(o`R0ztW4YuZTRF;eR(tnvuMiYw zqP_!Cb)f~J*bV2_LhL-f{bBwqIXPW0c}jLh++4+iK!?Mksgf=9hZ^_c<`T(2;vkxne*YPG%UEkXkmcN zqj-~;WbLtz7nN~R@z`(YTlB*}(sY6|f!J(5DDgQY>y;~WmK(&JeGIGaw7wv+nm{Bj zqbcL&693?{cCx75%V?uhbvUP&JFevCBE1{Ck3A}AI>IFP#ry>b&V`W^avptF;|^)^-+qhMzMeJilDfW~6g*D{rfzs^*dBTV6&{bHU?j^f!GD0p~uB zjuVtECwt9A>TINk!bL8VS_~ee6uYRVS3NuIlC2gD$xa^mea<31@-qr4u|ipNx3amB zsnYM*VAIB?^I3bcJA4t*W85)a+cr;T(&(+S<;I&f*aqxqo^4%qJxLb5EiNx?sEn1T zL$@}gDX27Sj%VgcH10v?{7xjw?)5tluf&j!Yg>P8J!pd%q*oqloHNtP%4!X5)Iq2Z z1cs*;+wd>VTb`8Fv!o0v=FN+=Xo{tJYPZ`Q?rJ40@ z;P?i6WPCw020QCVBb*~?PZfJ=s-fuCY;dG){2@J_diAs|pCta$Z`zmO=ct?$9tS&K zooK_c9;eJ0B4@7qAWbFMo0d#4g@DxdA`{hkC($w~PUIs4iJCWszAKe+uF?ZVkU@l+ zWJeRb8$FhksvS7a>1>ZzH5Hy0CnV?F)k?z%c0^HCuylMo!W!H3G+cIfbX4;P5Mzhq zCq)Dif;U&0z&*^Isk`t&^HYD1ELK%-@C+~8F3)w1syM!4%R9FKy{8#f=yJo)g>L~a zbEQPe_J3^opP|OCn24_`m}5uXl7DmTjHEbaoQPVIc){_u*o9SfjcnHYen@&V^=fz$ zEKGSjr_z>b%Rgh=_8`Bec0e+FrNrDSG9ccycIP#;f%Kt)%hBDP!MqP$6Gj58QjfQqPx}xD{%GF4DAFKN zQ6ZTYCjPjId`dPONVhr9s4>r(c@Jr&a)mc3tL-3zqWT*|sI9%$GXDf7o$c1LG+eoh z1#;Fj)vgbFo**~v$O9}=`Yl=F5WoNur%YdG?b%6H8$#EEt_N&qUF@Z;I6|}j4D1MZ zJ1((}cu~bTyA=~PuL09%naQN0KN)$uOyI4NB=Sv)UgAm>R=dr*N>`x|GSDHL7NLAk zOp@lQ4KHXbW|)-5h@Y*Peazl!2kPd;Ync9{PNnGCEH0Pq3aOX-Wv zAf8%SOVUYsDF>3Gpo%O}zD4CvOjJuJL#-c&`?$yyLJxO+iBd{%mdUXn&;`hYFCCFqq-P3kYq z19ZZA5>))*ItKGkI0wL%mXZ`6$XXEO%HVT>Od)25BG0}_40QrD^MDT|&TIv(FQ$Cy zI(Sg*G6|V5?wp6DSapFiSD1Cb%=C30^}F2{ksW0;SkyoblG{vNDV02JBT41Cq(t>h0i_a(2DkWfi4_7^nOnnh))_J0FjBeme6jrx1|xGxwK z5*JI_6wMFu)r1?63Z%cK0i)?fB2F=@a7ihF!NaS zQavdPpL(%0W%Zqu>p_L*MwJ)=!9aB_aB6#XgXIKO1u}sOeTs-R!NtSOmfnh1c7B{V zMAJNHL z8QZ#E$|c_A_gkJE0`Hb}&e9bHy-1S91#5&J>qz9|DfMXLp6KbcZ0MFX5M%Xg(Tv*u zQ_&)Q|0hr5%N(xm=u7Kdr1uI6Ha_WidSwUuSXj7y8=c;#q>jUL3Jh%8-c-*Y&G{2D zgZ$40Y$wR4!;2jd1HIc%W4PPd=lJGGo?%CC5bsXs9XsTGKZHy-g6MPSGtWEYs!qXO zbtd;c)`8U0CANr4x2>jKkhuelQ9AA#l1CY+isii)tpgyHCwF@z4)_x584n4Rnffmr z6D+D@U!634jpRvKK4TSV@MX$dD{?rNc!YFN}E z0r9toJXT-o_0yEV@qXj+NJvNHJfM5NS5)8i z>(v-~>IKW@uIX+SAPBof^##4ca~&LO%~%e1eGL!M1iB0jG)X@>Iz3&hIbyc*o$WHc zPvMVr=y?w%v{B2T{S=&bCDJ`gdkm4b4k_mc6LtSSen9Nowzw+9Q3;uA;Y<(u2y})4Z zmV#n{PM2-kxPOX(h*sn)o=9&vtN$i#$IVb>omom~Nf9_symw>UeLhoIuIu4@P*qgj zH4cxk7PM7AlYVn^^&Xw5dAk$f450QClfb<_P3O@2qLt6BV}x=)WXy`=x(8O(=4E#^ z>CmHO8PBcP0<|#Ha=}Ch(VSW97hW#ib`}iBl3{&8ZxTV|Rp1bylkt33L^^JUPf6JC ze!;wAj%^O`(VHT9>j4@gQLLt>I_b?dysecUIJdX*>Up{Q^x_4U_Q5HWRRDX&+TG_ zxIau88pnc6(MKt%0z%dJEr_ZB<+(Isq23(%w3c_&U@^2KG>-gNHtKuDEPU-IDHevW zD&~^NHQSNJ*h zbZgJp6X+Gkm5?V5gH>^^z+MWrj|^lm&p_4lAPc43loFvA@S2Ug&3wtD97gFiXp7@m z4+*y+12C9q@R4AIAvd6WqT*ubdQO`!X1XCep$|(+#}%VPM#bb-BOSEG=}a>3hpOmm zwh2Bp@NI36dnW4YL<5Hy-74jqEMdz_Ikx;d8+EN17~i1L#^=i}JC~-Wv^3OWLMFj1 z!>~}^nm33p&qT*N9US4xP1AuZk8=`@d+%wW6Jb{6ZD}$(b}oiB2A@C&Vj{lf=OY+A=7NX_tNm+C7kb#9zNAJoE*EF0+v zIq3O7x36x_F0tr9h-%7L;AT={jf<0Gj88?g@+Q#hp|3AE1a<0B6)bnLNU3yU z*g=`A9xd`Bna2yHr+NDA0Eh!!_`Iow_!Ijwxj~$BjS~Ci5V1)~9Pm+BcUbITJ*;7x!UXu#tR#Du1@z?|7>5@joiB z?^Nd*c9)q&;Z)CWWE{&Gn8D2D5=4n9c93g%d&zgHf}?Y2YY#Qv z8hsC2+gGO!p~Uj8Gm@%{e9G<^>0Yr;LZMZhPprdWiikcPdY^A8=@iloQ&C+p>gwD9 zQRto>ri@K{zye_$u4rO3bcsx=C#PXsMz*5?`m7m_hAq2U1!br7s>y9OcctD1k=|(0 z;N+{?dm4v;_%)yX1OsmRcPpv*?!|Aa>TJXQISA(nlr{Y_31?oT%Ld*)$GN06<4e zALWvU7S(G&9nus&l`VK9ogMQ+xBE?Hv=Q67YCRCEysebyR9f%*o6^nj-@v*kHZW+c z4fzYYGiKH?tUbe7_?*S97?IKZ7x0e+ED0%h)Q(Y3@57Z>4nhhYSL=NXDc2k0DW@t<0gcQt_ z3{1QLwu@}7L+w4^-I2VNSA4Y%SeNm9Q6e~hxnPSllX8;s_zEA4K4^oBny$AsO_d>2 zIjn==;l-@hefUoPDZW@H$a)&4v9neG5f1wqtMgtM$Gb*3`W3qkzrk7Er3h6pOOr%Q zhhY!7`conhe0ds&OLHT6cd+hQ-ImXV2&(g-Any-Ay{X*qBD=f&e-XsTAB&mxK)LT0 zz3FMPdRcTDopTg*_4bAMK` z*R9Q{(io4VdZ*4_rKAi{8k`t?^2TYaMRpa^XQN>DaZF^w@$D%z8?zak1ri&V!!DD| zXJ-Igwf_b3Y`75yBT`4Fa2%~Y@s98y)yIj;Xv5ScYQr?5C%G?^_W+Vo#ztCLYGjOtCc|4Y@UL`@;Vy(R6znf+Yh{|Kv?-^4^{OzDa5YkMS((ubK)G3XUeTVMw8PQx|vS&VFN zkU)&iAbndCRO~PwmL@#jvF+Gtu+K_*81n3mlqrXLniXvB)*?y*!UHJA`{WPsmz_v5 z*&toi)wA%5@Wc#eR2liyFrzO)d45ze*jb;~6OWj=Q03`=N4JF*!$j*d&u=v5sb>|E z-W|#`@PUUCELI)dvq_c}rtuNfQE5>dgA5-Uq0nqnWf;%IX8WwXZU@C!{7%q)XhiRm zXB}B0W=8$L@>2d1;VbV;06@@S+otUVSHF=1AA_IY}Ab8%!XZArbiK72gLrl z-2TzKUc&I9o~3T@J5fGH@aZUE&Dcfiu-)AUbrZ-p^J*I zvbkgU5^^QmB_J9`BEs3+J)%#dI=v^&H4-O!q=w^hsA;WcQ02B#3<%)di)e$CE?e{2 z#juvMCdjB7Dap?iUv=>bt5247z+II2ob`Qjo%E5Q`dv@V2~;h!*k0jR|3w!B#*_s! zFM`Wdn(w~;9?Q3lZxSk2&gScT938v9Fis>2a}!0jWSioJC98ZsQfhlFp{WY17}8HE zSjo=ZKOqOk-{S1}wJ=9T{d`~xr(n>%?H#T3cd-zrp2*J=bDqP}0iCUdn{>_a!eiS^ z`(%EzO0kBrw<#EH#->ONH`a)S@?k1%ZQq8!usX{Gc@W4W5ZA-#O~`UiV9x$WB|%{G z0b{#>1{5o45)bHth-RQJfr!1hY6b@p~PH71!`-^8y1m;`tO1?^!hBAOeQ)6ICf~X zPU5+DJE4#RG`9B}OdZ_;3gVc1TQBXPM*E6$wWCui_d`sCR{+oflvr9>F%3%DRD5dbVCB}($I?rIwB;(YN8Ke%bIXGOY_228 z+a`3nnIm6ji_U5X>v4%B3G=SWswpq}Ek*`ZZp(EepA2XkUPUniF8*=I?8#p$MuIt3a3_!^DL9Ewi2ZHUY` z7T7AtqF6nIsmvJkl9rCU_=PVD6B7Tpo)YVxwMta_fVz^UfAAA`)7o`3>tInFse)*2 zVq#LEW5e%sR&%t<^m_vaH(oTZdSuUQrY(WoojSFg8Zo@L*@zwHum-c?T|kp9{Umsk ze-rBz!;8sBse%V-?w7_#^1^}h>!;`h{Qed0)IGG$Gkj=Vvr$1X{VujQ<40}Boesg< z6Fo%2Bn+ENdPdF%+=b_G4RsP#4jBy!@_dg;|EkO^<#?2SVx5jy6#WnW?hVUwB7Z4BE=TCWzmV3aM5bEd~nJTCRU+4c!VQC5fI|aXKr(` z4tyn(EOKkfxX9{DBR?K1%hxj&9gwP88??@g3)3TXpF@3XrDMT9DUF1be>x-pQDl$S541DfkI1@%0j5K?!hF2VqLGZ$-r~$`aZZG zR=rHb3DfQ@Ha3xYf8V}+DY{!_d-=WL(V7jj17grg1VwYcJU5r_v_k}|8N3a;zDhNZ zAQHM?qsNvZx8fnlO~fM;CWS7`NP9qyXpqZ2qGj|}Y02DrDs8l`4z@*`LLp9xNz{ir zI?nN$aklEU5P#25X{BFD>5rq|>`-bz!t>&=2;$Zf=dp$g8jHXvt%t}1Vqs z22{tti34z%0EUk&AKUtgG5sbfGi}=ji#S)wyD=+{jCztuA*&p8p-}qH7BrDbGmuGP z9jGT$8%M|~sIOkE|BdKKb5-@^Wf%OfLp>t#Za|ZlBF{cZAA&xhrm~0^2r^i!cG;_? z+LWTqC`6}(kPp-}dG^Y)p42J@ z(A5~tcmv(Zua0d-1ySyRPO>ImMV@_;j)OcS9fuE&6$pIkP`>)?;b7R|Sjz5i1sz$8 z1{O8qQxv$z!z=b}@A6kCVCeDuW>``pTM!363A!VT12CR~xKwF)BLrj1u7?9>;!&RD zaOqB}LrAN16}Rm>9jh}XL*q}Pi(x>T03||8Wb_oH#_;6`av6C5yT*mXD1S6M{H{Eb zZ?Ap@Zy2EFONoTwZ$17e(O@SN(0p2?^u|j+Mlb7>_dd}9i9ym4i0tH*CdyVi5>|Jg3ond%_$Au;3r)rC;pq@zQhjRmV*gJWPZ zG(~(O%l9n1*3L9v^c_3$vRr>m3Z#RAeH4-2CD9;E_~t7rdce8I+XO+3ZMKOS>mp74 z>=#j3$)8ZRJPqBOFVYW%9zGxkb-3>N4api)MtwVmuW+~Z79Y~l8P(G>qgHx`p#=VD zfTy0B-fX+ClCN)jiLm>_r?rr(`lr{4UKOF2!YvfIlYa~DLb{q7%(FAt59l<6;j_Dk z%{?s2&|5P#7&hjzE5>eJsXL2)j?i3X%wywT$(1uJ89pDYroiOnJG5Fki;#67r1Cz( zb^xygdvG!3aFT;AFC}zu41R|m#_}ASkhRonVKR|0UVwO?7TJw)gAHPA=9Oo|j|G8_ z_e1Bv;9KzlLoB<6Fiu+Aw;9oW^a0B@ej)ODuY-|seu;Y+o z#nfQgpqp+L1`f^`K(YGjGRlLl4X(W!Ycujz8Ri0OnkF7+RSF|`j-UHuJ&1p$dc0g) zE4``P^bU9IE&414d1eBIao#&KA2Xp1p{EU0*oc{t(nAF|M~j<{+~92u3dyVB{CFJk zuapJFoNY;8`#m9d&iog{`Y}KnROccpvs(JNFSoq3s@4nKzOL9fU149KwayUw!9 zd`YJbwj|!vE1;l)`WCY6lF6!d_5lu9wW!xCYDOh67nLl>(8DDb4>s>lJ2g$un?ydh zu(zwMJKQ4H14Ec~`~Vm{V)50dfJvmP<@uZR2;*T~${fkX12t%T5D8zyO|Ouwx7^eA zPGI<|^8D-Y3-KNp{q>Ah4z2{XdZ~eoMBW8ai;BhDTpidn{hR0zHwXBr0KuL_&yELP6+a{UJIJkTAXVmwmbdDpC8D%`n^jBn(WjQ$= zqA{x#J+mH8f=SC=)ecZ}7itE3VKX)*K-{n$`yJ!|@atd@S0Y_Wc|jqa@+B2CYluQM z69C)(WZ^W}{g>#F7X)sM7&)#3yRvW}n&#L9Q_IXlRpq+dI!3d*?^T}348Z^!-Dxq9 zL*Gq(F6St4CF97lqwub1kZ{;y2=$26G%^fyYOPy`u2v=%$^@SC{?NT^Lf&aW1k&Xt06r7APsFDqoME z6aFO`%U?V3&?3xMq16C^S|A@QDk~NsFlGrWim#SLOSMY6P@We}HzKpxfoG zoj%*K(q%G0N|<#o%gY~SOJAPEJDI+kxK-V*iY;MPy|Ph<~@yv z0O@pOG&m+O#rut+Hv9p)J5DZNEbja8365h1E*?;3%up`jVDQTW_X#h|dNnQAh_2e& zLan&>iH7m>*6>?2o11v6tI;c2bI^n)vop`Pg!IaG56sAzq+G&=3$A#`bBsQ9B}^oh ze$%SZ238#nzOp&AzeeBs!yEE#EQ}MjPBN)4MDE@Mp!1e$em>iEirAPgq8E2GkAsRk&DRoiZG)oii;Ggy=s6V}$9>9@Xxw=XMs=x3G@9tcTIN*RH5 z$N>||kMS;2*yA~n)pn-@%FeIRw84GZEGV4}3c62`18$D7=P$9Va)oZj!=-isyP#fZ z1Or-b^tpyz+TNLZN!QjNq7H1pUsxF<#JLf6^zNXxOU?da86LbQ4vP~F0?>yIKM%^} zpzf<0*Wj^gW5>Wp>Dw)uGAOhldymIg&zO4sUV_<}JVQU2>Vdxfb?sdRaF^r4*{a6K z_h|lGxqMrk)2thezFV2bWuQm~zEFX4t~;afx~XeyjMcD7P;JA`ttBbXA1gkv1oFN# z(D^VQDCrQ5pY~n(B52vv-^DI=)gDO^0DT3-#nE*=4_Zyf!gl4GP~gY;HUS4mPFlDS z6oc>1bfc8C0jvRb zO?$#LKfR&{$NkQ;p{9Nn{-ui_bG3jw&KAm5>#wkfV7ibC2hN;C8*WOkj-W(9vVC^1 z3!u(sPkKyl1%vq06j(Uz#gIq-^?)k-kw2>*bKU3V*mmpqgWoJ6j1AJgd&)Eh*3rqB z57?aG8qlK^f!s=^QO>e2e8s&?8|m_BHGB*>7QHOJGj4A1CeE{rhB&2hf>yV}$pi%? z*XT#hwraqAn3Q28W#OD>DW?CJ?E!}H%(3_i7^xBMuG5-ZfeBt1;t-6H8h1u6=R<4$ z5ELOEbEYHWPF3Nferfi|p}8y2o^pJH0fdX|wWuF@InitND6$v2J7`)YLF3=mKplv=R&|<1|Z>ORAV`Q{I6%-7s`*McBjUE&>e3pysEpcRP20EH;E_I zIsypiN7f1APr%}G_^+eHJ76TFVHf$s`Tnfea_UPrjOID}a(m1_^p6zG^AfI zdg37uu%jb&XGDuSe%<9j$ZG$WFkF7Lh?SM{24ws6f9R_Na`@!M|K}}J2+`*v^;Udg z9(o&oh7f7_zCvVospqhD$;S;VkeNg2Ws`(3ob>dC^yP*Pw$9nzo z7clI)eCtoOL38O2eHYfu%K2qsEzrnsKbu;b=etlZxLC$-L=v$|^4o~tCc??aFo|q^ zEp!&K#4uu|O>wU!%Sz@hfg5I>hCYWf^o!Ptv9@!8E*EQ5>W$p5ZA^wTz$pzHl171d%9_Kq*k6u4m#K%*V)h7-=irh1rC?BBANYs14akW zJ+yECIHP__ZS>K;ihcQF2tdn|wMudVr#i9_o7Jk{`%|lOQc7jxl4Bpb2|szx9Z8}6 zp1a``TdmY#L@xN4z{*e~py@<|-9P0L0Ng*fsKh>SPV=G0bT~AC>FJ$TPX)k%4YowBbPGpoWNU6J&=mNjvS)}z+J5IxrL3z!@+>( zm=Il18^Uv-z$mN-dQG8xerIGqAaI`Z@p^rdR44WSAMd3FKo64nBqSuHa|Z)k=;@ye z8eX?Fc3`h9F}+gi{Yp)9eKGINm;G)76v*9{x7gX zUq`zl!7F&^jOLkX;;J~_4{huvLqE2p?;q?V7fHW9a-j=@OsX|)dS81UvXKYB;xX|L zfHQEoYO2-~fn5PS0^>H9I(B^zkI+;(sx!5(hI&)k{oxt3jM;8AzR)t6Jx8RzINC4>kvo+D0bBM^#3f3fvq8luFR7oUn&d5V! zK&+=WF;BMePKmuQVYaA){i$?^TQ}I$_~#{PntgyJNLmZ)t?7{--zeMrSz4kEngVDK zpEuf&-h_P-#`V`X2Ic~>+pK!n?6g=7^q4|C$1Jbv6hI*M1uS0KKR^Ev>m4 zdwbHa@6jv+HY?CsJp;zF8X;6O;gGLXU z_}u6Qc+&<4^G2kNsswsO-n|W~`ha2Bz($}fYo1DPA6o(zd_+}r4DQT@JBb$VkFfpU zKXOK$1E>-J1zl^^FplAasSQML&zLWtu zr3=WuKKG`l34ltGBpYZj@9u#2T!C^FT1@<2*y!FUUOA=?DCZr_df&NRB1CEyk7QdL z5Ql2w^BTC5+KIGnug%KmCG%L|q5Z&5H0W*G;0z0|a^;=>x=M2tKX+CF> za0pHc(CqnFmYFb)P0DNfDh*;;uLk=20~Q`4c>4^V!IjsLUI-dQp!v_gC>|%q+E8Eb2ow z7{w1=_x24?cCio55|4W;&4#d^14R1MwY&)`K*TTztZoHoi-t(~MRn;N7u3!R(|*2# zTmtr*rJ8a-8!QuYaWlw6PJ#gKUTmfZ0us$vWlrfvpU8Lw?BhMqk6Ucl1QHCosr4ZG zeFu$(B0dMPX>3c!%qcx+5L~%K8xf>lpsBee<@0p31sr$?cHsjf>iTAkD)AjjVHz-v zCoXCyVBzfvS+lr=TgufzPHN~x$>ZS3pYm7T&uEa7B@HZ}`^b&wQj!E@SojFdfl{;@ z_F!rEWx^ID}|BY<}dNT_Q|3JTX8cMDjigx>Nh4_ozj*x5u7 zFxSk-2OnoKGo}oqlQtHDc*1aAeWn7}=ON*3RTV*=G{-zXXvo`M z`*t`M3bL&NZ3gnxov>!iSitM{0b3QWUHn!8F%QQYv1$SeUc7QyvR#Dt% z@uG)P5*72M`#|E(eX$oMa|7m6Zo-!0R7J1AA)x!s!qI^4!T2Xp`|6h zL2pn|UI6p&Ps2%vQ~&BqVCY#7RB2)5I;>;SD63!wDE-u0TL?g3mf z_zr6!3)}~S2^1)AGm1L0;rktZCsS1KA$plN#3H$CH2yT$f;KpEd>pQGg`o2xhBp}- zD~$TXAZdOk=DzE-?pq6bAl6BrK(NH-a}TwM7Y)K!PoV7Y-0=*O`qt?OYlN z1qI_OEVRHqmD|zr?tp+at1$Dae+bcE8cO$>o(l6J5DUXMK>h?8{%zM5oyG-uR+n{u|c;cE#`da)+@MjOO;_MY7$y9Q~>Y#7P9 zG=2@Q52B!yB!}nCp!w(t?O}8LQfEB}3lds7szUS8>qn1_#LY+A)ir{ED``ICfOr?R zFG!u$iV=JJCxcEHcuP=F16@slP zQi};diPm+@Q;=KQvP9lA5^EINo1nA?wX@ZsqRk(W+`K)IyZI?<5%dr{V5G>hdfdS>3 z_`KAvduWU{UD}56%CQk87c=41dKJWucLRaEvnu2MG*E{TS1r&vwWDVgs)*l zjl<5eY-F;)2CGz49M{6kTyJL4ZcNUU%AZk@V-5PZ=4qPlVYzw$%4tO)OlA!sw_yAO z9$5y1K9H<=^G;wGgM<}8BBY6&sD4w=zUu^oZ?`T2pe%SM> zVlu5hjq@*o8K`Kt#GFYX5+!O@;l+u0}0JAmn(VmM;Ag z_!Yem4b&hIbkIiDd>cHHTlWIV-OxS013}V}eOh=}5e+D)G6x-+5BT_eydn-hJ0K@l zeRWq)OOI!3$7~8m@H3>B{p*qNLkHpYFUa3y&=-s3{f)gI^6{=sypj>{{E;eHz2E+l zMI^5r8#B=Ec_8Co2%=kM6(l||MY%+%T@KWTBE40jW><`?d`!-iVWUko02lXCzSXiy zJC=1)ZJji!*k=LbMH%U@B=iz0PY6Z z89%!-{BjgX{LC9_}KQ#3QBuPcsN-PAiHzn|N4X^Hi z(|JmVi&_(da6SO>&VYWkM5^htO?ry8KDT-OK$WfulQwIvn?%KFLjZ)~_Yl#qTAG#@V%k;`r75-j>b)C$n{?6$CTo(d=48Ri{0)fD-&%!3GFcCV>sNC9 zc*iyooj&;nwr~d4!3N{!L?iRG(E>~!&IDsD1qBaBAmCQ$twL(_Sk&D^((RkHMb;9f zIXF^6gWrI0KO1QD%toU2W-p4tv>t73qee@ZpO6V1(!Kxdk?~+<&8d!CMC}%w6GK3< z+Xt)wl}rGYKZ#;@m~6du`yw*CUna`~}PLG?ouDD(0sywLo`Z zbANTabVYFYzDd|u_w0e>&w|=(!aeC3Es_R5EPxDlmYvG^4yMC7Yk_6i(m6Dd-RyTe zwJ9<1=N?LiA?iGE=WgJ)eWzVKb_*MrnW6)Dfh}-iE8h&#>Sz4aHy3DEY~TexRS~az z-4u)k$SBAV(3IDBFnlKcy37_<28q@ShQdEXoP)IKl?nv5ln@bw#mCT$pPNFv2ysZS zapoIwI}@2)hc#&p@*!##;6`Vl&z2x`?Ps1~SAYc_kH7yi_0gl%(VgQbPJBwX2x?JQ zQ%$47l+UgF04Kl)v2?)OhqeXEsv~*ApXLtfCqEq*^APuPS?8;S{8u-QnYPZEvvk3( z2i)?)OFOgNW9m6MF`iLts4;r(o4mfseP+*BF6k1kYd}H+{+}%he8OXh`?FSy<4Yrmq`v1{j#JB}LnF1$S`?sjBGtIYZxXO4-T zdvZiw^I1Z%_0mkvG_|T(4%@mX^#RwWwIqu4ToI4}PMyvA1}^u&_MYGb*=yFoI!kcj z62-RDbuqoMMQ@$nXDl#RxoEAgP+78jg7l>yQjd3>QFF65{CDiyg5zR+M|KH@%RDI3 zuet%;V`e-%q&`sS|K5!1DFsU*DK!GyC+!LY@=z=%7^9`_VH)!=ns3?b3XS4Z(dZ1iKegkjXNwt(j6a^ zj_QMETZ|;qfzuYi;Ui#scO$r6502yuN-Qfv_cuuOZeUYg*QUDZ_sfeXmONWwu9|3n zp|?I_ah<~5#=P$d?^7OhgLdm?MA`rk-~r`3CnRTuavU&Q_FbsZMt`F$M~<=V5fh7* zcQuw?vAK}&?BU^d!zQ`uJCAf7mU?_*u33q<$JXX)mMvEbjx3X#FZtw%@X|sD&x&5T z!`4x9!aR)&YfRpQyv>AUei_G?M=vuJ-t#ivw7#JHomuoZaB#U{J+twwV&E9(+X5+I z-t>)S0d4{S&gTMih}s)i4goeJS{e5Fv3{DkSJ{+}t@wHKEn}C(JA3wk+nK(WF2F^a zk|#mA3fM0J)#hLVSfwi$a(;M`3)~qp0h&EHE&}&ba!aD{`zz5@c=!c%q}c>ru1n&?jgZs-D*Q7>D8Jqw=JGoW@)3(`TAQ-C9L zyo4Yxoh`cDEj-hmy<%V#D48^6E_ zbtg#U8|?c&MT0BA5*%2Depa;vHbj9n`rQe#OkXrgKs6jEuiFS;dNjx qbJ*`$yI~%1!uqGg*~L>@7#NgyTfX(bcJK%T5O})!xvX` literal 25881 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 l%o%Vu0{0rQ56G!+dTcYHd8`V{|m|TrIG*u diff --git a/examples/models/models_loading_vox.c b/examples/models/models_loading_vox.c index 06dc651d7..04b203ad9 100644 --- a/examples/models/models_loading_vox.c +++ b/examples/models/models_loading_vox.c @@ -25,9 +25,9 @@ #include "rlights.h" #if defined(PLATFORM_DESKTOP) -#define GLSL_VERSION 330 + #define GLSL_VERSION 330 #else // PLATFORM_ANDROID, PLATFORM_WEB -#define GLSL_VERSION 100 + #define GLSL_VERSION 100 #endif //------------------------------------------------------------------------------------ @@ -130,6 +130,7 @@ int main(void) camerarot.y = 0; } + // Update camere movement, custom controls UpdateCameraPro(&camera, (Vector3){ (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP))*0.1f - (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN))*0.1f, // Move forward-backward (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT))*0.1f - (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT))*0.1f, // Move right-left @@ -173,7 +174,7 @@ int main(void) DrawText("- MOUSE LEFT BUTTON: CYCLE VOX MODELS", 20, 50, 10, BLUE); DrawText("- MOUSE MIDDLE BUTTON: ZOOM OR ROTATE CAMERA", 20, 70, 10, BLUE); DrawText("- UP-DOWN-LEFT-RIGHT KEYS: MOVE CAMERA", 20, 90, 10, BLUE); - DrawText(TextFormat("Model file: %s", GetFileName(voxFileNames[currentModel])), 10, 10, 20, GRAY); + DrawText(TextFormat("VOX model file: %s", GetFileName(voxFileNames[currentModel])), 10, 10, 20, GRAY); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/examples/models/models_loading_vox.png b/examples/models/models_loading_vox.png index 417d887e4a7f9798833c6d112c9277dd522a0d8d..2bae0ab3e49ac54573f95ba86a1e5fd3ce4f463a 100644 GIT binary patch literal 37121 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 diff --git a/projects/VS2022/examples/models_animation_timming.vcxproj b/projects/VS2022/examples/models_animation_timing.vcxproj similarity index 99% rename from projects/VS2022/examples/models_animation_timming.vcxproj rename to projects/VS2022/examples/models_animation_timing.vcxproj index a18123bbc..9a1034925 100644 --- a/projects/VS2022/examples/models_animation_timming.vcxproj +++ b/projects/VS2022/examples/models_animation_timing.vcxproj @@ -55,7 +55,7 @@ Win32Proj models_animation_timming 10.0 - models_animation_timming + models_animation_timing @@ -553,7 +553,7 @@ - + diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 51268e037..02fe4b197 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -435,7 +435,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_blending", EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_window_web", "examples\core_window_web.vcxproj", "{4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_timming", "examples\models_animation_timming.vcxproj", "{89D5A0E9-683C-465C-BF85-A880865175C8}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_timing", "examples\models_animation_timing.vcxproj", "{89D5A0E9-683C-465C-BF85-A880865175C8}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/config.h b/src/config.h index 3beaeeceb..5fb4f5545 100644 --- a/src/config.h +++ b/src/config.h @@ -131,13 +131,13 @@ // Module: rlgl - Configuration values //------------------------------------------------------------------------------------ #if !defined(EXTERNAL_CONFIG_FLAGS) +//#define SUPPORT_GPU_SKINNING 1 // GPU skinning, comment if your GPU does not support more than 8 VBOs + // Enable OpenGL Debug Context (only available on OpenGL 4.3) -//#define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT 1 +//#define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT 1 // OpenGL debug context requested // Show OpenGL extensions and capabilities detailed logs on init -//#define RLGL_SHOW_GL_DETAILS_INFO 1 - -#define RL_SUPPORT_MESH_GPU_SKINNING 1 // GPU skinning, comment if your GPU does not support more than 8 VBOs +//#define RLGL_SHOW_GL_DETAILS_INFO 1 // Show OpenGL detailed info on initialization (limits and extensions) #endif //#define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 4096 // Default internal render batch elements limits @@ -149,8 +149,8 @@ #define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported -#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 +#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 @@ -160,27 +160,31 @@ #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT 4 #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 5 #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES 6 -#if defined(RL_SUPPORT_MESH_GPU_SKINNING) - #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS 7 +#if defined(SUPPORT_GPU_SKINNING) + #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES 7 #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8 #endif -#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCE_TX 9 +#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORMS 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 +// Default shader vertex attribute/uniform names to set location points +// NOTE: When a new shader is loaded, locations are tried to be set for convenience, +// if the following names are found in the shader, if not, it's up to the user to set locations #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 +#define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES "vertexBoneIndices" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES +#define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix #define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView)) -#define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color) +#define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (tint color, multiplied by texture color) +#define RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES "boneMatrices" // bone matrices #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0) #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) @@ -278,10 +282,11 @@ //------------------------------------------------------------------------------------ #define MAX_MATERIAL_MAPS 12 // Maximum number of shader maps supported -#ifdef RL_SUPPORT_MESH_GPU_SKINNING -#define MAX_MESH_VERTEX_BUFFERS 9 // Maximum vertex buffers (VBO) per mesh +#ifdef SUPPORT_GPU_SKINNING + // NOTE: Two additional vertex buffers required to store bone indices and bone weights + #define MAX_MESH_VERTEX_BUFFERS 9 // Maximum vertex buffers (VBO) per mesh #else -#define MAX_MESH_VERTEX_BUFFERS 7 // Maximum vertex buffers (VBO) per mesh + #define MAX_MESH_VERTEX_BUFFERS 7 // Maximum vertex buffers (VBO) per mesh #endif //------------------------------------------------------------------------------------ diff --git a/src/raylib.h b/src/raylib.h index 06138ceca..bd64bbe6f 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -353,13 +353,15 @@ typedef struct Mesh { unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) unsigned short *indices; // Vertex indices (in case vertex data comes indexed) - // Animation vertex data + // Skin data for animation + int boneCount; // Number of bones (MAX: 256 bones) + unsigned char *boneIndices; // Vertex bone indices, up to 4 bones influence by vertex (skinning) (shader-location = 6) + float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7) + + // Runtime animation vertex data (CPU skinning) + // NOTE: In case of GPU skinning, not used, pointers are NULL float *animVertices; // Animated vertex positions (after bones transformations) float *animNormals; // Animated normals (after bones transformations) - unsigned char *boneIds; // Vertex bone ids, max 255 bone ids, up to 4 bones influence by vertex (skinning) (shader-location = 6) - float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7) - Matrix *boneMatrices; // Bones animated transformation matrices - int boneCount; // Number of bones // OpenGL identifiers unsigned int vaoId; // OpenGL Vertex Array Object id @@ -393,12 +395,22 @@ typedef struct Transform { Vector3 scale; // Scale } Transform; +// Anim pose, an array of Transform[] +typedef Transform *ModelAnimPose; + // Bone, skeletal animation bone typedef struct BoneInfo { char name[32]; // Bone name int parent; // Bone parent } BoneInfo; +// Skeleton, animation bones hierarchy +typedef struct ModelSkeleton { + int boneCount; // Number of bones + BoneInfo *bones; // Bones information (skeleton) + ModelAnimPose bindPose; // Bones base transformation (Transform[]) +} ModelSkeleton; + // Model, meshes, materials and animation data typedef struct Model { Matrix transform; // Local transform matrix @@ -410,18 +422,20 @@ typedef struct Model { int *meshMaterial; // Mesh material number // Animation data - int boneCount; // Number of bones - BoneInfo *bones; // Bones information (skeleton) - Transform *bindPose; // Bones base transformation (pose) + ModelSkeleton skeleton; // Skeleton for animation + + // Runtime animation data (CPU/GPU skinning) + ModelAnimPose currentPose; // Current animation pose (Transform[]) + Matrix *boneMatrices; // Bones animated transformation matrices } Model; -// ModelAnimation +// ModelAnimation, contains a full animation sequence typedef struct ModelAnimation { char name[32]; // Animation name - int boneCount; // Number of bones - int frameCount; // Number of animation frames - BoneInfo *bones; // Bones information (skeleton) - Transform **framePoses; // Poses array by frame + + int boneCount; // Number of bones (per pose) + int keyframeCount; // Number of animation key frames + ModelAnimPose *keyframePoses; // Animation sequence keyframe poses [keyframe][pose] } ModelAnimation; // Ray, ray for raycasting @@ -768,6 +782,8 @@ typedef enum { #define MATERIAL_MAP_SPECULAR MATERIAL_MAP_METALNESS // Shader location index +// NOTE: Some locations are tried to be set automatically on shader loading, +// but only if default attributes/uniforms names are found, check config.h for names typedef enum { SHADER_LOC_VERTEX_POSITION = 0, // Shader location: vertex attribute: position SHADER_LOC_VERTEX_TEXCOORD01, // Shader location: vertex attribute: texcoord01 @@ -790,15 +806,15 @@ typedef enum { SHADER_LOC_MAP_ROUGHNESS, // Shader location: sampler2d texture: roughness SHADER_LOC_MAP_OCCLUSION, // Shader location: sampler2d texture: occlusion SHADER_LOC_MAP_EMISSION, // Shader location: sampler2d texture: emission - SHADER_LOC_MAP_HEIGHT, // Shader location: sampler2d texture: height + SHADER_LOC_MAP_HEIGHT, // Shader location: sampler2d texture: heightmap SHADER_LOC_MAP_CUBEMAP, // Shader location: samplerCube texture: cubemap SHADER_LOC_MAP_IRRADIANCE, // Shader location: samplerCube texture: irradiance SHADER_LOC_MAP_PREFILTER, // Shader location: samplerCube texture: prefilter SHADER_LOC_MAP_BRDF, // Shader location: sampler2d texture: brdf - SHADER_LOC_VERTEX_BONEIDS, // Shader location: vertex attribute: boneIds - SHADER_LOC_VERTEX_BONEWEIGHTS, // Shader location: vertex attribute: boneWeights - SHADER_LOC_BONE_MATRICES, // Shader location: array of matrices uniform: boneMatrices - SHADER_LOC_VERTEX_INSTANCE_TX // Shader location: vertex attribute: instanceTransform + SHADER_LOC_VERTEX_BONEIDS, // Shader location: vertex attribute: bone indices + SHADER_LOC_VERTEX_BONEWEIGHTS, // Shader location: vertex attribute: bone weights + SHADER_LOC_MATRIX_BONETRANSFORMS, // Shader location: matrix attribute: bone transforms (animation) + SHADER_LOC_VERTEX_INSTANCETRANSFORMS // Shader location: vertex attribute: instance transforms } ShaderLocationIndex; #define SHADER_LOC_MAP_DIFFUSE SHADER_LOC_MAP_ALBEDO @@ -1619,11 +1635,8 @@ RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); // Model animations loading/unloading functions RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount); // Load model animations from file -RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose (CPU) -RLAPI void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame); // Update model animation mesh bone matrices (GPU skinning) -RLAPI void UpdateModelAnimationBonesLerp(Model model, ModelAnimation animA, int frameA, ModelAnimation animB, int frameB, float value); // Update model animation mesh bone matrices with interpolation between two poses(GPU skinning) -RLAPI void UpdateModelVertsToCurrentBones(Model model); // Update model vertices according to mesh bone matrices (CPU) -RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data +RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, float frame); // Update model animation pose (vertex buffers and bone matrices) +RLAPI void UpdateModelAnimationEx(Model model, ModelAnimation animA, float frameA, ModelAnimation animB, float frameB, float blend); // Update model animation pose, blending two animations RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount); // Unload animation array data RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match diff --git a/src/rcore.c b/src/rcore.c index c29fb1e19..f1481890b 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1286,7 +1286,7 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode) // - vertex color location = 3 // - vertex tangent location = 4 // - vertex texcoord2 location = 5 - // - vertex boneIds location = 6 + // - vertex boneIndices location = 6 // - vertex boneWeights location = 7 // NOTE: If any location is not found, loc point becomes -1 @@ -1303,9 +1303,9 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode) shader.locs[SHADER_LOC_VERTEX_NORMAL] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL); shader.locs[SHADER_LOC_VERTEX_TANGENT] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT); shader.locs[SHADER_LOC_VERTEX_COLOR] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR); - shader.locs[SHADER_LOC_VERTEX_BONEIDS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS); + shader.locs[SHADER_LOC_VERTEX_BONEIDS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES); shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS); - shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX); + shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORMS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORMS); // Get handles to GLSL uniform locations (vertex shader) shader.locs[SHADER_LOC_MATRIX_MVP] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MVP); @@ -1313,7 +1313,7 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode) shader.locs[SHADER_LOC_MATRIX_PROJECTION] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION); shader.locs[SHADER_LOC_MATRIX_MODEL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL); shader.locs[SHADER_LOC_MATRIX_NORMAL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL); - shader.locs[SHADER_LOC_BONE_MATRICES] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES); + shader.locs[SHADER_LOC_MATRIX_BONETRANSFORMS] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES); // Get handles to GLSL uniform locations (fragment shader) shader.locs[SHADER_LOC_COLOR_DIFFUSE] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR); diff --git a/src/rlgl.h b/src/rlgl.h index 0cde3e6eb..67817a3c9 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -65,7 +65,7 @@ * #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR * #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT * #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS "vertexBoneIds" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES "vertexBoneIndices" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES * #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS * #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix * #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix @@ -73,7 +73,7 @@ * #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix * #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView))) * #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color) -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES "boneMatrices" // bone matrices +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES "boneMatrices" // bone matrices * #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0) * #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) @@ -345,16 +345,16 @@ #ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES 6 #endif -#ifdef RL_SUPPORT_MESH_GPU_SKINNING -#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS - #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS 7 +#ifdef SUPPORT_GPU_SKINNING + #ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES + #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES 7 + #endif + #ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS + #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8 + #endif #endif -#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS - #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8 -#endif -#endif -#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCE_TX - #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCE_TX 9 +#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORMS + #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORMS 9 #endif //---------------------------------------------------------------------------------- @@ -992,31 +992,34 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad // Default shader vertex attribute names to set location points #ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION - #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION + #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION #endif #ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD - #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD + #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD #endif #ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL - #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL + #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL #endif #ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR - #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR + #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR #endif #ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT - #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT + #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT #endif #ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 - #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 + #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 #endif -#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS - #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS "vertexBoneIds" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS +#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES + #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES "vertexBoneIndices" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES #endif #ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS - #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS + #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS #endif -#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX - #define RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX "instanceTransform" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX +#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES + #define RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES "boneMatrices" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEMATRICES +#endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORMS + #define RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORMS "instanceTransform" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORMS #endif #ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_MVP @@ -1037,9 +1040,6 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color) #endif -#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES - #define RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES "boneMatrices" // bone matrices -#endif #ifndef RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0) #endif @@ -1049,6 +1049,9 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #ifndef RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2) #endif +#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES + #define RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES "boneMatrices" // bone matrices (required for GPU skinning) +#endif //---------------------------------------------------------------------------------- // Module Types and Structures Definition @@ -4339,10 +4342,9 @@ unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId) glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR); glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT); glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2); - glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCE_TX, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX); - -#ifdef RL_SUPPORT_MESH_GPU_SKINNING - glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS); + glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORMS, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORMS); +#ifdef SUPPORT_GPU_SKINNING + glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES); glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS); #endif diff --git a/src/rmodels.c b/src/rmodels.c index b90b6a53f..1649fb8f3 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -167,6 +167,9 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou static void ProcessMaterialsOBJ(Material *rayMaterials, tinyobj_material_t *materials, int materialCount); // Process obj materials #endif +// Update model vertex data (positions and normals) +static void UpdateModelAnimationVertexBuffers(Model model); + //---------------------------------------------------------------------------------- // Module Functions Definition //---------------------------------------------------------------------------------- @@ -1182,7 +1185,7 @@ bool IsModelValid(Model model) if ((model.meshes[i].tangents != NULL) && (model.meshes[i].vboId[4] == 0)) { result = false; break; } // Vertex tangents buffer not uploaded to GPU if ((model.meshes[i].texcoords2 != NULL) && (model.meshes[i].vboId[5] == 0)) { result = false; break; } // Vertex texcoords2 buffer not uploaded to GPU if ((model.meshes[i].indices != NULL) && (model.meshes[i].vboId[6] == 0)) { result = false; break; } // Vertex indices buffer not uploaded to GPU - if ((model.meshes[i].boneIds != NULL) && (model.meshes[i].vboId[7] == 0)) { result = false; break; } // Vertex boneIds buffer not uploaded to GPU + if ((model.meshes[i].boneIndices != NULL) && (model.meshes[i].vboId[7] == 0)) { result = false; break; } // Vertex boneIndices buffer not uploaded to GPU if ((model.meshes[i].boneWeights != NULL) && (model.meshes[i].vboId[8] == 0)) { result = false; break; } // Vertex boneWeights buffer not uploaded to GPU // NOTE: Some OpenGL versions do not support VAO, so we don't check it @@ -1212,8 +1215,8 @@ void UnloadModel(Model model) RL_FREE(model.meshMaterial); // Unload animation data - RL_FREE(model.bones); - RL_FREE(model.bindPose); + RL_FREE(model.skeleton.bones); + RL_FREE(model.skeleton.bindPose); TRACELOG(LOG_INFO, "MODEL: Unloaded model (and meshes) from RAM and VRAM"); } @@ -1273,9 +1276,8 @@ void UploadMesh(Mesh *mesh, bool dynamic) mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT] = 0; // Vertex buffer: tangents mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2] = 0; // Vertex buffer: texcoords2 mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES] = 0; // Vertex buffer: indices - -#ifdef RL_SUPPORT_MESH_GPU_SKINNING - mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS] = 0; // Vertex buffer: boneIds +#ifdef SUPPORT_GPU_SKINNING + mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES] = 0; // Vertex buffer: boneIndices mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS] = 0; // Vertex buffer: boneWeights #endif @@ -1375,21 +1377,21 @@ void UploadMesh(Mesh *mesh, bool dynamic) rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2); } -#ifdef RL_SUPPORT_MESH_GPU_SKINNING - if (mesh->boneIds != NULL) +#ifdef SUPPORT_GPU_SKINNING + if (mesh->boneIndices != NULL) { - // Enable vertex attribute: boneIds (shader-location = 7) - mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS] = rlLoadVertexBuffer(mesh->boneIds, mesh->vertexCount*4*sizeof(unsigned char), dynamic); - rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, 4, RL_UNSIGNED_BYTE, 0, 0, 0); - rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS); + // Enable vertex attribute: boneIndices (shader-location = 7) + mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES] = rlLoadVertexBuffer(mesh->boneIndices, mesh->vertexCount*4*sizeof(unsigned char), dynamic); + rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES, 4, RL_UNSIGNED_BYTE, 0, 0, 0); + rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES); } else { - // Default vertex attribute: boneIds + // Default vertex attribute: boneIndices // WARNING: Default value provided to shader if location available float value[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; - rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, value, SHADER_ATTRIB_VEC4, 4); - rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS); + rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES, value, SHADER_ATTRIB_VEC4, 4); + rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES); } if (mesh->boneWeights != NULL) @@ -1436,17 +1438,17 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) #define GL_COLOR_ARRAY 0x8076 #define GL_TEXTURE_COORD_ARRAY 0x8078 - if (mesh.texcoords && material.maps[MATERIAL_MAP_DIFFUSE].texture.id > 0) rlEnableTexture(material.maps[MATERIAL_MAP_DIFFUSE].texture.id); + if ((mesh.texcoords != NULL) && (material.maps[MATERIAL_MAP_DIFFUSE].texture.id > 0)) rlEnableTexture(material.maps[MATERIAL_MAP_DIFFUSE].texture.id); - if (mesh.animVertices) rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.animVertices); + if (mesh.animVertices != NULL) rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.animVertices); else rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.vertices); if (mesh.texcoords) rlEnableStatePointer(GL_TEXTURE_COORD_ARRAY, mesh.texcoords); - if (mesh.animNormals) rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.animNormals); - else if (mesh.normals) rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.normals); + if (mesh.animNormals != NULL) rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.animNormals); + else if (mesh.normals != NULL) rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.normals); - if (mesh.colors) rlEnableStatePointer(GL_COLOR_ARRAY, mesh.colors); + if (mesh.colors != NULL) rlEnableStatePointer(GL_COLOR_ARRAY, mesh.colors); rlPushMatrix(); rlMultMatrixf(MatrixToFloat(transform)); @@ -1528,14 +1530,6 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) // Upload model normal matrix (if locations available) if (material.shader.locs[SHADER_LOC_MATRIX_NORMAL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_NORMAL], MatrixTranspose(MatrixInvert(matModel))); - -#ifdef RL_SUPPORT_MESH_GPU_SKINNING - // Upload Bone Transforms - if ((material.shader.locs[SHADER_LOC_BONE_MATRICES] != -1) && mesh.boneMatrices) - { - rlSetUniformMatrices(material.shader.locs[SHADER_LOC_BONE_MATRICES], mesh.boneMatrices, mesh.boneCount); - } -#endif //----------------------------------------------------- // Bind active texture maps (if available) @@ -1615,11 +1609,11 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]); } -#ifdef RL_SUPPORT_MESH_GPU_SKINNING +#ifdef SUPPORT_GPU_SKINNING // Bind mesh VBO data: vertex bone ids (shader-location = 6, if available) if (material.shader.locs[SHADER_LOC_VERTEX_BONEIDS] != -1) { - rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS]); + rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES]); rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS], 4, RL_UNSIGNED_BYTE, 0, 0, 0); rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS]); } @@ -1743,7 +1737,7 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i if (material.shader.locs[SHADER_LOC_MATRIX_PROJECTION] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_PROJECTION], matProjection); // Create instances buffer - instanceTransforms = (float16 *)RL_MALLOC(instances*sizeof(float16)); + instanceTransforms = (float16 *)RL_CALLOC(instances, sizeof(float16)); // Fill buffer with instances transformations as float16 arrays for (int i = 0; i < instances; i++) instanceTransforms[i] = MatrixToFloatV(transforms[i]); @@ -1757,14 +1751,14 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i // no faster, since all the transform matrices are transferred anyway instancesVboId = rlLoadVertexBuffer(instanceTransforms, instances*sizeof(float16), false); - // Instances transformation matrices are sent to shader attribute location: SHADER_LOC_VERTEX_INSTANCE_TX - if (material.shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] != -1) + // Instances transformation matrices are sent to shader attribute location: SHADER_LOC_VERTEX_INSTANCETRANSFORMS + if (material.shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORMS] != -1) { for (unsigned int i = 0; i < 4; i++) { - rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] + i); - rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] + i, 4, RL_FLOAT, 0, sizeof(Matrix), i*sizeof(Vector4)); - rlSetVertexAttributeDivisor(material.shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] + i, 1); + rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORMS] + i); + rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORMS] + i, 4, RL_FLOAT, 0, sizeof(Matrix), i*sizeof(Vector4)); + rlSetVertexAttributeDivisor(material.shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORMS] + i, 1); } } @@ -1777,15 +1771,6 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i // Upload model normal matrix (if locations available) if (material.shader.locs[SHADER_LOC_MATRIX_NORMAL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_NORMAL], MatrixTranspose(MatrixInvert(matModel))); - -#ifdef RL_SUPPORT_MESH_GPU_SKINNING - // Upload Bone Transforms - if ((material.shader.locs[SHADER_LOC_BONE_MATRICES] != -1) && mesh.boneMatrices) - { - rlSetUniformMatrices(material.shader.locs[SHADER_LOC_BONE_MATRICES], mesh.boneMatrices, mesh.boneCount); - } -#endif - //----------------------------------------------------- // Bind active texture maps (if available) @@ -1863,11 +1848,11 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]); } -#ifdef RL_SUPPORT_MESH_GPU_SKINNING +#ifdef SUPPORT_GPU_SKINNING // Bind mesh VBO data: vertex bone ids (shader-location = 6, if available) if (material.shader.locs[SHADER_LOC_VERTEX_BONEIDS] != -1) { - rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS]); + rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES]); rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS], 4, RL_UNSIGNED_BYTE, 0, 0, 0); rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS]); } @@ -1946,6 +1931,7 @@ void UnloadMesh(Mesh mesh) if (mesh.vboId != NULL) for (int i = 0; i < MAX_MESH_VERTEX_BUFFERS; i++) rlUnloadVertexBuffer(mesh.vboId[i]); RL_FREE(mesh.vboId); + // Unload mesh vertex buffers RL_FREE(mesh.vertices); RL_FREE(mesh.texcoords); RL_FREE(mesh.normals); @@ -1954,11 +1940,13 @@ void UnloadMesh(Mesh mesh) RL_FREE(mesh.texcoords2); RL_FREE(mesh.indices); + // Unload mesh skin animation data + RL_FREE(mesh.boneWeights); + RL_FREE(mesh.boneIndices); + + // Unload mesh runtime CPU skinning data RL_FREE(mesh.animVertices); RL_FREE(mesh.animNormals); - RL_FREE(mesh.boneWeights); - RL_FREE(mesh.boneIds); - RL_FREE(mesh.boneMatrices); } // Export mesh data to file @@ -2189,7 +2177,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 = (Material *)RL_MALLOC(count*sizeof(Material)); + materials = (Material *)RL_CALLOC(count, sizeof(Material)); ProcessMaterialsOBJ(materials, mats, count); tinyobj_materials_free(mats, count); @@ -2228,10 +2216,11 @@ bool IsMaterialValid(Material material) { bool result = false; - if ((material.maps != NULL) && // Validate material contain some map + if ((material.maps != NULL) && // Validate material contain some map (material.shader.id > 0)) result = true; // Validate material shader is valid - // TODO: Check if available maps contain loaded textures + // NOTE: Checking if available maps contain loaded textures does not determine if + // a material is valid, there can be maps without a texture assigned, only properties return result; } @@ -2287,146 +2276,205 @@ ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount) return animations; } -// Update model animated bones transform matrices for a given frame -// NOTE: Updated data is not uploaded to GPU but kept at model.meshes[i].boneMatrices[boneId], -// to be uploaded to shader at drawing, in case GPU skinning is enabled -void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame) +// Update model animation data (vertex buffers / bone matrices) for a specific pose +// NOTE 1: Request frame could be fractional, using a lerp interpolation between two frames +// NOTE 2: Updated vertex animation data is uploaded to GPU in case of CPU skinning, +// for GPU skinning, bone matrices are uploaded to shader on DrawModelEx() +void UpdateModelAnimation(Model model, ModelAnimation anim, float frame) { - if ((anim.frameCount > 0) && (anim.bones != NULL) && (anim.framePoses != NULL)) + if (model.boneMatrices == NULL) return; + + //UpdateModelAnimationEx(model, anim, frame, anim, frame, 0.0f); + + // Update model animated bones transform matrices for a given frame + if ((anim.keyframeCount > 0) && (model.skeleton.bones != NULL) && (anim.keyframePoses != NULL)) { - if (frame >= anim.frameCount) frame = frame%anim.frameCount; + // Get frame and blending from frame factor required + int currentFrame = (int)frame; + int nextFrame = currentFrame + 1; + float blend = frame - currentFrame; + blend = Clamp(blend, 0.0f, 1.0f); + if (currentFrame >= anim.keyframeCount) currentFrame = currentFrame%anim.keyframeCount; + if (nextFrame >= anim.keyframeCount) nextFrame = nextFrame%anim.keyframeCount; - // Get first mesh which have bones - int firstMeshWithBones = -1; + Matrix bindPoseMatrix = { 0 }; + Matrix currentPoseMatrix = { 0 }; - for (int i = 0; i < model.meshCount; i++) + // Update all bones and bone matrices of model + for (int boneIndex = 0; boneIndex < model.skeleton.boneCount; boneIndex++) { - if (model.meshes[i].boneMatrices) - { - if (firstMeshWithBones == -1) - { - firstMeshWithBones = i; - break; - } - } + // Compute interpolated pose between current and next frame + // NOTE: Storing animation frame data in model.currentPose + model.currentPose[boneIndex].translation = Vector3Lerp( + anim.keyframePoses[currentFrame][boneIndex].translation, + anim.keyframePoses[nextFrame][boneIndex].translation, blend); + model.currentPose[boneIndex].rotation = QuaternionSlerp( + anim.keyframePoses[currentFrame][boneIndex].rotation, + anim.keyframePoses[nextFrame][boneIndex].rotation, blend); + model.currentPose[boneIndex].scale = Vector3Lerp( + anim.keyframePoses[currentFrame][boneIndex].scale, + anim.keyframePoses[nextFrame][boneIndex].scale, blend); + + // Compute runtime bone matrix from model current pose + //----------------------------------------------------------------------------------- + Transform *bindPoseTransform = &model.skeleton.bindPose[boneIndex]; + bindPoseMatrix = MatrixMultiply( + MatrixMultiply(MatrixScale(bindPoseTransform->scale.x, bindPoseTransform->scale.y, bindPoseTransform->scale.z), + QuaternionToMatrix(bindPoseTransform->rotation)), + MatrixTranslate(bindPoseTransform->translation.x, bindPoseTransform->translation.y, bindPoseTransform->translation.z)); + + Transform *currentPoseTransform = &model.currentPose[boneIndex]; + currentPoseMatrix = MatrixMultiply( + MatrixMultiply(MatrixScale(currentPoseTransform->scale.x, currentPoseTransform->scale.y, currentPoseTransform->scale.z), + QuaternionToMatrix(currentPoseTransform->rotation)), + MatrixTranslate(currentPoseTransform->translation.x, currentPoseTransform->translation.y, currentPoseTransform->translation.z)); + + model.boneMatrices[boneIndex] = MatrixMultiply(MatrixInvert(bindPoseMatrix), currentPoseMatrix); + //----------------------------------------------------------------------------------- } - if (firstMeshWithBones != -1) - { - // Update all bones and boneMatrices of first mesh with bones - for (int boneId = 0; boneId < anim.boneCount; boneId++) - { - Transform *bindTransform = &model.bindPose[boneId]; - Matrix bindMatrix = MatrixMultiply(MatrixMultiply( - MatrixScale(bindTransform->scale.x, bindTransform->scale.y, bindTransform->scale.z), - QuaternionToMatrix(bindTransform->rotation)), - MatrixTranslate(bindTransform->translation.x, bindTransform->translation.y, bindTransform->translation.z)); - - Transform *targetTransform = &anim.framePoses[frame][boneId]; - Matrix targetMatrix = MatrixMultiply(MatrixMultiply( - MatrixScale(targetTransform->scale.x, targetTransform->scale.y, targetTransform->scale.z), - QuaternionToMatrix(targetTransform->rotation)), - MatrixTranslate(targetTransform->translation.x, targetTransform->translation.y, targetTransform->translation.z)); - - model.meshes[firstMeshWithBones].boneMatrices[boneId] = MatrixMultiply(MatrixInvert(bindMatrix), targetMatrix); - } - - // Update remaining meshes with bones - // NOTE: Using deep copy because shallow copy results in double free with 'UnloadModel()' - for (int i = firstMeshWithBones + 1; i < model.meshCount; i++) - { - if (model.meshes[i].boneMatrices) - { - memcpy(model.meshes[i].boneMatrices, - model.meshes[firstMeshWithBones].boneMatrices, - model.meshes[i].boneCount*sizeof(model.meshes[i].boneMatrices[0])); - } - } - } + // CPU skinning, updates CPU buffers and uploads them to GPU + // NOTE: On GPU skinning not supported, use CPU skinning + UpdateModelAnimationVertexBuffers(model); } } -// Update model animated bones transform matrices by interpolating between two different given frames of different ModelAnimation(could be same too) -// NOTE: Updated data is not uploaded to GPU but kept at model.meshes[i].boneMatrices[boneId], -// to be uploaded to shader at drawing, in case GPU skinning is enabled -void UpdateModelAnimationBonesLerp(Model model, ModelAnimation animA, int frameA, ModelAnimation animB, int frameB, float value) +// Update model animation data (vertex buffers / bone matrices) for a specific pose, +// defined by two different animations at specific frames blended together +// NOTE 1: Request frames could be fractional, using a lerp interpolation between two frames +// NOTE 2: Updated vertex animation data is uploaded to GPU in case of CPU skinning, +// for GPU skinning, bone matrices are uploaded to shader on DrawModelEx() +void UpdateModelAnimationEx(Model model, ModelAnimation animA, float frameA, ModelAnimation animB, float frameB, float blend) { - if ((animA.frameCount > 0) && (animA.bones != NULL) && (animA.framePoses != NULL) && - (animB.frameCount > 0) && (animB.bones != NULL) && (animB.framePoses != NULL) && - (value >= 0.0f) && (value <= 1.0f)) + if (model.boneMatrices == NULL) return; + + if ((animA.keyframeCount > 0) && (animA.keyframePoses != NULL) && + (animB.keyframeCount > 0) && (animB.keyframePoses != NULL) && + (blend >= 0.0f) && (blend <= 1.0f)) { - frameA = frameA % animA.frameCount; - frameB = frameB % animB.frameCount; + // Inter-frame interpolation values for first animation + int currentFrameA = (int)frameA%animA.keyframeCount; + int nextFrameA = currentFrameA + 1; + float blendA = frameA - currentFrameA; + blendA = Clamp(blendA, 0.0f, 1.0f); + if (currentFrameA >= animA.keyframeCount) currentFrameA = currentFrameA%animA.keyframeCount; + if (nextFrameA >= animA.keyframeCount) nextFrameA = nextFrameA%animA.keyframeCount; - for (int i = 0; i < model.meshCount; i++) + // Inter-frame interpolation values for second animation + int currentFrameB = (int)frameB%animB.keyframeCount; + int nextFrameB = currentFrameB + 1; + float blendB = frameB - currentFrameB; + blendB = Clamp(blendB, 0.0f, 1.0f); + if (currentFrameB >= animB.keyframeCount) currentFrameB = currentFrameB%animB.keyframeCount; + if (nextFrameB >= animB.keyframeCount) nextFrameB = nextFrameB%animB.keyframeCount; + + Matrix bindPoseMatrix = { 0 }; + Matrix currentPoseMatrix = { 0 }; + + for (int boneIndex = 0; boneIndex < model.skeleton.boneCount; boneIndex++) { - if (model.meshes[i].boneMatrices) - { - assert(model.meshes[i].boneCount == animA.boneCount); - assert(model.meshes[i].boneCount == animB.boneCount); + // Get frame-interpolation for first animation + Vector3 frameATranslation = Vector3Lerp( + animA.keyframePoses[currentFrameA][boneIndex].translation, + animA.keyframePoses[nextFrameA][boneIndex].translation, blendA); + Quaternion frameARotation = QuaternionSlerp( + animA.keyframePoses[currentFrameA][boneIndex].rotation, + animA.keyframePoses[nextFrameA][boneIndex].rotation, blendA); + Vector3 frameAScale = Vector3Lerp( + animA.keyframePoses[currentFrameA][boneIndex].scale, + animA.keyframePoses[nextFrameA][boneIndex].scale, blendA); - for (int boneId = 0; boneId < model.meshes[i].boneCount; boneId++) - { - Vector3 inTranslation = model.bindPose[boneId].translation; - Quaternion inRotation = model.bindPose[boneId].rotation; - Vector3 inScale = model.bindPose[boneId].scale; + // Get frame-interpolation for second animation + Vector3 frameBTranslation = Vector3Lerp( + animB.keyframePoses[currentFrameB][boneIndex].translation, + animB.keyframePoses[nextFrameB][boneIndex].translation, blendB); + Quaternion frameBRotation = QuaternionSlerp( + animB.keyframePoses[currentFrameB][boneIndex].rotation, + animB.keyframePoses[nextFrameB][boneIndex].rotation, blendB); + Vector3 frameBScale = Vector3Lerp( + animB.keyframePoses[currentFrameB][boneIndex].scale, + animB.keyframePoses[nextFrameB][boneIndex].scale, blendB); + + // Compute interpolated pose between both animations frames + // NOTE: Storing animation frame data in model.currentPose + model.currentPose[boneIndex].translation = Vector3Lerp(frameATranslation, frameBTranslation, blend); + model.currentPose[boneIndex].rotation = QuaternionSlerp(frameARotation, frameBRotation, blend); + model.currentPose[boneIndex].scale = Vector3Lerp(frameAScale, frameBScale, blend); - Vector3 outATranslation = animA.framePoses[frameA][boneId].translation; - Quaternion outARotation = animA.framePoses[frameA][boneId].rotation; - Vector3 outAScale = animA.framePoses[frameA][boneId].scale; + // Compute runtime bone matrix from model current pose + //----------------------------------------------------------------------------------- + Transform *bindPoseTransform = &model.skeleton.bindPose[boneIndex]; + bindPoseMatrix = MatrixMultiply( + MatrixMultiply(MatrixScale(bindPoseTransform->scale.x, bindPoseTransform->scale.y, bindPoseTransform->scale.z), + QuaternionToMatrix(bindPoseTransform->rotation)), + MatrixTranslate(bindPoseTransform->translation.x, bindPoseTransform->translation.y, bindPoseTransform->translation.z)); - Vector3 outBTranslation = animB.framePoses[frameB][boneId].translation; - Quaternion outBRotation = animB.framePoses[frameB][boneId].rotation; - Vector3 outBScale = animB.framePoses[frameB][boneId].scale; + Transform *currentPoseTransform = &model.currentPose[boneIndex]; + currentPoseMatrix = MatrixMultiply( + MatrixMultiply(MatrixScale(currentPoseTransform->scale.x, currentPoseTransform->scale.y, currentPoseTransform->scale.z), + QuaternionToMatrix(currentPoseTransform->rotation)), + MatrixTranslate(currentPoseTransform->translation.x, currentPoseTransform->translation.y, currentPoseTransform->translation.z)); - Vector3 outTranslation = Vector3Lerp(outATranslation, outBTranslation, value); - Quaternion outRotation = QuaternionSlerp(outARotation, outBRotation, value); - Vector3 outScale = Vector3Lerp(outAScale, outBScale, value); + model.boneMatrices[boneIndex] = MatrixMultiply(MatrixInvert(bindPoseMatrix), currentPoseMatrix); + //----------------------------------------------------------------------------------- - Vector3 invTranslation = Vector3RotateByQuaternion(Vector3Negate(inTranslation), QuaternionInvert(inRotation)); - Quaternion invRotation = QuaternionInvert(inRotation); - Vector3 invScale = Vector3Divide((Vector3){ 1.0f, 1.0f, 1.0f }, inScale); + /* + Vector3 outATranslation = animA.keyframePoses[currentFrameA][boneIndex].translation; + Quaternion outARotation = animA.keyframePoses[currentFrameA][boneIndex].rotation; + Vector3 outAScale = animA.keyframePoses[currentFrameA][boneIndex].scale; - Vector3 boneTranslation = Vector3Add( - Vector3RotateByQuaternion(Vector3Multiply(outScale, invTranslation), - outRotation), outTranslation); - Quaternion boneRotation = QuaternionMultiply(outRotation, invRotation); - Vector3 boneScale = Vector3Multiply(outScale, invScale); + Vector3 outBTranslation = animB.keyframePoses[currentFrameB][boneIndex].translation; + Quaternion outBRotation = animB.keyframePoses[currentFrameB][boneIndex].rotation; + Vector3 outBScale = animB.keyframePoses[currentFrameB][boneIndex].scale; - Matrix boneMatrix = MatrixMultiply(MatrixMultiply( - QuaternionToMatrix(boneRotation), - MatrixTranslate(boneTranslation.x, boneTranslation.y, boneTranslation.z)), - MatrixScale(boneScale.x, boneScale.y, boneScale.z)); + // Invert bind pose transformation + Vector3 invBindTranslation = Vector3RotateByQuaternion( + Vector3Negate(model.skeleton.bindPose[boneIndex].translation), + QuaternionInvert(model.skeleton.bindPose[boneIndex].rotation)); + Quaternion invBindRotation = QuaternionInvert(model.skeleton.bindPose[boneIndex].rotation); + Vector3 invBindScale = Vector3Divide((Vector3){ 1.0f, 1.0f, 1.0f }, model.skeleton.bindPose[boneIndex].scale); - model.meshes[i].boneMatrices[boneId] = boneMatrix; - } - } + Vector3 boneTranslation = Vector3Add(Vector3RotateByQuaternion( + Vector3Multiply(model.currentPose[boneIndex].scale, invBindTranslation), + model.currentPose[boneIndex].rotation), + model.currentPose[boneIndex].translation); + Quaternion boneRotation = QuaternionMultiply(model.currentPose[boneIndex].rotation, invBindRotation); + Vector3 boneScale = Vector3Multiply(model.currentPose[boneIndex].scale, invBindScale); + + model.boneMatrices[boneIndex] = MatrixMultiply( + MatrixMultiply(QuaternionToMatrix(boneRotation), + MatrixTranslate(boneTranslation.x, boneTranslation.y, boneTranslation.z)), + MatrixScale(boneScale.x, boneScale.y, boneScale.z)); + */ } + + // CPU skinning, updates CPU buffers and uploads them to GPU (if available) + // NOTE: Fallback in case GPU skinning is not supported or enabled + UpdateModelAnimationVertexBuffers(model); } } -// Update model vertex data (positions and normals) from mesh bone data -// NOTE: Updated data is uploaded to GPU -void UpdateModelVertsToCurrentBones(Model model) +// Update model vertex animation buffers (positions and normals) +// NOTE: Required for CPU skinning, uploads animated vertex buffers to GPU +static void UpdateModelAnimationVertexBuffers(Model model) { - //UpdateModelAnimationBones(model, anim, frame); // TODO: Review - for (int m = 0; m < model.meshCount; m++) { Mesh mesh = model.meshes[m]; Vector3 animVertex = { 0 }; Vector3 animNormal = { 0 }; - const int vValues = mesh.vertexCount*3; + const int vertexValuesCount = mesh.vertexCount*3; - int boneId = 0; + int boneIndex = 0; int boneCounter = 0; float boneWeight = 0.0f; - bool updated = false; // Flag to check when anim vertex information is updated + bool bufferUpdateRequired = false; // Flag to check when anim vertex information is updated - // Skip if missing bone data, causes segfault without on some models - if ((mesh.boneWeights == NULL) || (mesh.boneIds == NULL)) continue; + // Skip if missing bone data or missing anim buffers initialization + if ((mesh.boneWeights == NULL) || (mesh.boneIndices == NULL) || + (mesh.animVertices == NULL) || (mesh.animNormals == NULL)) continue; - for (int vCounter = 0; vCounter < vValues; vCounter += 3) + for (int vCounter = 0; vCounter < vertexValuesCount; vCounter += 3) { mesh.animVertices[vCounter] = 0; mesh.animVertices[vCounter + 1] = 0; @@ -2442,23 +2490,23 @@ void UpdateModelVertsToCurrentBones(Model model) for (int j = 0; j < 4; j++, boneCounter++) { boneWeight = mesh.boneWeights[boneCounter]; - boneId = mesh.boneIds[boneCounter]; + boneIndex = mesh.boneIndices[boneCounter]; // Early stop when no transformation will be applied if (boneWeight == 0.0f) continue; animVertex = (Vector3){ mesh.vertices[vCounter], mesh.vertices[vCounter + 1], mesh.vertices[vCounter + 2] }; - animVertex = Vector3Transform(animVertex, model.meshes[m].boneMatrices[boneId]); + animVertex = Vector3Transform(animVertex, model.boneMatrices[boneIndex]); mesh.animVertices[vCounter] += animVertex.x*boneWeight; - mesh.animVertices[vCounter+1] += animVertex.y*boneWeight; - mesh.animVertices[vCounter+2] += animVertex.z*boneWeight; - updated = true; + mesh.animVertices[vCounter + 1] += animVertex.y*boneWeight; + mesh.animVertices[vCounter + 2] += animVertex.z*boneWeight; + bufferUpdateRequired = true; // Normals processing // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals) if ((mesh.normals != NULL) && (mesh.animNormals != NULL )) { animNormal = (Vector3){ mesh.normals[vCounter], mesh.normals[vCounter + 1], mesh.normals[vCounter + 2] }; - animNormal = Vector3Transform(animNormal, MatrixTranspose(MatrixInvert(model.meshes[m].boneMatrices[boneId]))); + animNormal = Vector3Transform(animNormal, MatrixTranspose(MatrixInvert(model.boneMatrices[boneIndex]))); mesh.animNormals[vCounter] += animNormal.x*boneWeight; mesh.animNormals[vCounter + 1] += animNormal.y*boneWeight; mesh.animNormals[vCounter + 2] += animNormal.z*boneWeight; @@ -2466,53 +2514,36 @@ void UpdateModelVertsToCurrentBones(Model model) } } - if (updated) + if (bufferUpdateRequired) { - rlUpdateVertexBuffer(mesh.vboId[0], mesh.animVertices, mesh.vertexCount*3*sizeof(float), 0); // Update vertex position - if (mesh.normals != NULL) rlUpdateVertexBuffer(mesh.vboId[2], mesh.animNormals, mesh.vertexCount*3*sizeof(float), 0); // Update vertex normals + // Update GPU vertex buffers with updated data (position + normals) + rlUpdateVertexBuffer(mesh.vboId[SHADER_LOC_VERTEX_POSITION], mesh.animVertices, mesh.vertexCount*3*sizeof(float), 0); + if (mesh.normals != NULL) rlUpdateVertexBuffer(mesh.vboId[SHADER_LOC_VERTEX_NORMAL], mesh.animNormals, mesh.vertexCount*3*sizeof(float), 0); } } } -// at least 2x speed up vs the old method -// Update model animated vertex data (positions and normals) for a given frame -// NOTE: Updated data is uploaded to GPU -void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) -{ - UpdateModelAnimationBones(model,anim,frame); - UpdateModelVertsToCurrentBones(model); -} - // Unload animation array data void UnloadModelAnimations(ModelAnimation *animations, int animCount) { - for (int i = 0; i < animCount; i++) UnloadModelAnimation(animations[i]); + for (int a = 0; a < animCount; a++) + { + for (int i = 0; i < animations[a].keyframeCount; i++) + RL_FREE(animations[a].keyframePoses[i]); + + RL_FREE(animations[a].keyframePoses); + } + RL_FREE(animations); } -// Unload animation data -void UnloadModelAnimation(ModelAnimation anim) -{ - for (int i = 0; i < anim.frameCount; i++) RL_FREE(anim.framePoses[i]); - - RL_FREE(anim.bones); - RL_FREE(anim.framePoses); -} - // Check model animation skeleton match // NOTE: Only number of bones and parent connections are checked bool IsModelAnimationValid(Model model, ModelAnimation anim) { int result = true; - if (model.boneCount != anim.boneCount) result = false; - else - { - for (int i = 0; i < model.boneCount; i++) - { - if (model.bones[i].parent != anim.bones[i].parent) { result = false; break; } - } - } + if (model.skeleton.boneCount != anim.boneCount) result = false; return result; } @@ -3883,17 +3914,31 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota for (int i = 0; i < model.meshCount; i++) { - Color color = model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color; + Material mat = model.materials[model.meshMaterial[i]]; + Color colDiffuse = mat.maps[MATERIAL_MAP_DIFFUSE].color; - Color colorTint = WHITE; - colorTint.r = (unsigned char)(((int)color.r*(int)tint.r)/255); - colorTint.g = (unsigned char)(((int)color.g*(int)tint.g)/255); - colorTint.b = (unsigned char)(((int)color.b*(int)tint.b)/255); - colorTint.a = (unsigned char)(((int)color.a*(int)tint.a)/255); + // Applying color tint directly to material diffuse map, + // because is comes as an input paramter to the function + Color colTinted = { 0 }; + colTinted.r = (unsigned char)(((int)colDiffuse.r*(int)tint.r)/255); + colTinted.g = (unsigned char)(((int)colDiffuse.g*(int)tint.g)/255); + colTinted.b = (unsigned char)(((int)colDiffuse.b*(int)tint.b)/255); + colTinted.a = (unsigned char)(((int)colDiffuse.a*(int)tint.a)/255); - model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = colorTint; - DrawMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform); - model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = color; + mat.maps[MATERIAL_MAP_DIFFUSE].color = colTinted; + + // Upload runtime bone transforms matrices, to compute skinning on the shader (GPU-skinning) + // NOTE: Required location must be found and Mesh bones indices and weights must be also uploaded to shader + if ((mat.shader.locs[SHADER_LOC_MATRIX_BONETRANSFORMS] != -1) && (model.boneMatrices != NULL)) + { + rlEnableShader(mat.shader.id); // Enable shader to set bone transform matrices + rlSetUniformMatrices(mat.shader.locs[SHADER_LOC_MATRIX_BONETRANSFORMS], model.boneMatrices, model.skeleton.boneCount); + } + + DrawMesh(model.meshes[i], mat, model.transform); + + // Restore material diffuse map color (before tint applied) + mat.maps[MATERIAL_MAP_DIFFUSE].color = colDiffuse; } } @@ -4344,7 +4389,7 @@ static void BuildPoseFromParentJoints(BoneInfo *bones, int boneCount, Transform { if (bones[i].parent > i) { - TRACELOG(LOG_WARNING, "Assumes bones are toplogically sorted, but bone %d has parent %d. Skipping.", i, bones[i].parent); + TRACELOG(LOG_WARNING, "Skipping bone not topologically sorted: Bone %d has parent %d", i, bones[i].parent); continue; } transforms[i].rotation = QuaternionMultiply(transforms[bones[i].parent].rotation, transforms[i].rotation); @@ -4762,11 +4807,11 @@ static Model LoadIQM(const char *fileName) for (int i = 0; i < model.meshCount; i++) { - //fseek(iqmFile, iqmHeader->ofs_text + imesh[i].name, SEEK_SET); + //fseek(iqmFile, iqmHeader->ofs_text + imesh[a].name, SEEK_SET); //fread(name, sizeof(char), MESH_NAME_LENGTH, iqmFile); memcpy(name, fileDataPtr + iqmHeader->ofs_text + imesh[i].name, MESH_NAME_LENGTH*sizeof(char)); - //fseek(iqmFile, iqmHeader->ofs_text + imesh[i].material, SEEK_SET); + //fseek(iqmFile, iqmHeader->ofs_text + imesh[a].material, SEEK_SET); //fread(material, sizeof(char), MATERIAL_NAME_LENGTH, iqmFile); memcpy(material, fileDataPtr + iqmHeader->ofs_text + imesh[i].material, MATERIAL_NAME_LENGTH*sizeof(char)); @@ -4783,16 +4828,18 @@ static Model LoadIQM(const char *fileName) 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 = (unsigned char *)RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(unsigned char)); // Up-to 4 bones supported! + model.meshes[i].boneIndices = (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 = (unsigned short *)RL_CALLOC(model.meshes[i].triangleCount*3, sizeof(unsigned short)); +#if !defined(SUPPORT_GPU_SKINNING) // 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 = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); model.meshes[i].animNormals = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); +#endif } // Triangles data processing @@ -4830,7 +4877,7 @@ static Model LoadIQM(const char *fileName) case IQM_POSITION: { vertex = (float *)RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float)); - //fseek(iqmFile, va[i].offset, SEEK_SET); + //fseek(iqmFile, va[a].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)); @@ -4840,7 +4887,7 @@ static Model LoadIQM(const char *fileName) for (unsigned int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++) { model.meshes[m].vertices[vCounter] = vertex[i]; - model.meshes[m].animVertices[vCounter] = vertex[i]; + if (model.meshes[m].animVertices != NULL) model.meshes[m].animVertices[vCounter] = vertex[i]; vCounter++; } } @@ -4848,7 +4895,7 @@ static Model LoadIQM(const char *fileName) case IQM_NORMAL: { normal = (float *)RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float)); - //fseek(iqmFile, va[i].offset, SEEK_SET); + //fseek(iqmFile, va[a].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)); @@ -4858,7 +4905,7 @@ static Model LoadIQM(const char *fileName) for (unsigned int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++) { model.meshes[m].normals[vCounter] = normal[i]; - model.meshes[m].animNormals[vCounter] = normal[i]; + if (model.meshes[m].animNormals != NULL) model.meshes[m].animNormals[vCounter] = normal[i]; vCounter++; } } @@ -4866,7 +4913,7 @@ static Model LoadIQM(const char *fileName) case IQM_TEXCOORD: { text = (float *)RL_MALLOC(iqmHeader->num_vertexes*2*sizeof(float)); - //fseek(iqmFile, va[i].offset, SEEK_SET); + //fseek(iqmFile, va[a].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)); @@ -4883,7 +4930,7 @@ static Model LoadIQM(const char *fileName) case IQM_BLENDINDEXES: { blendi = (char *)RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(char)); - //fseek(iqmFile, va[i].offset, SEEK_SET); + //fseek(iqmFile, va[a].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)); @@ -4892,7 +4939,7 @@ static Model LoadIQM(const char *fileName) int boneCounter = 0; for (unsigned int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++) { - model.meshes[m].boneIds[boneCounter] = blendi[i]; + model.meshes[m].boneIndices[boneCounter] = blendi[i]; boneCounter++; } } @@ -4900,7 +4947,7 @@ static Model LoadIQM(const char *fileName) case IQM_BLENDWEIGHTS: { blendw = (unsigned char *)RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char)); - //fseek(iqmFile, va[i].offset, SEEK_SET); + //fseek(iqmFile, va[a].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)); @@ -4917,7 +4964,7 @@ static Model LoadIQM(const char *fileName) case IQM_COLOR: { color = (unsigned char *)RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char)); - //fseek(iqmFile, va[i].offset, SEEK_SET); + //fseek(iqmFile, va[a].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)); @@ -4942,45 +4989,39 @@ static Model LoadIQM(const char *fileName) //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 = (BoneInfo *)RL_MALLOC(iqmHeader->num_joints*sizeof(BoneInfo)); - model.bindPose = (Transform *)RL_MALLOC(iqmHeader->num_joints*sizeof(Transform)); + model.skeleton.boneCount = iqmHeader->num_joints; + model.skeleton.bones = (BoneInfo *)RL_CALLOC(iqmHeader->num_joints, sizeof(BoneInfo)); + model.skeleton.bindPose = (Transform *)RL_CALLOC(iqmHeader->num_joints, sizeof(Transform)); for (unsigned int i = 0; i < iqmHeader->num_joints; i++) { // Bones - model.bones[i].parent = ijoint[i].parent; - //fseek(iqmFile, iqmHeader->ofs_text + ijoint[i].name, SEEK_SET); - //fread(model.bones[i].name, sizeof(char), BONE_NAME_LENGTH, iqmFile); - memcpy(model.bones[i].name, fileDataPtr + iqmHeader->ofs_text + ijoint[i].name, BONE_NAME_LENGTH*sizeof(char)); + model.skeleton.bones[i].parent = ijoint[i].parent; + //fseek(iqmFile, iqmHeader->ofs_text + ijoint[a].name, SEEK_SET); + //fread(model.bones[a].name, sizeof(char), BONE_NAME_LENGTH, iqmFile); + memcpy(model.skeleton.bones[i].name, fileDataPtr + iqmHeader->ofs_text + ijoint[i].name, BONE_NAME_LENGTH*sizeof(char)); // Bind pose (base pose) - model.bindPose[i].translation.x = ijoint[i].translate[0]; - model.bindPose[i].translation.y = ijoint[i].translate[1]; - model.bindPose[i].translation.z = ijoint[i].translate[2]; + model.skeleton.bindPose[i].translation.x = ijoint[i].translate[0]; + model.skeleton.bindPose[i].translation.y = ijoint[i].translate[1]; + model.skeleton.bindPose[i].translation.z = ijoint[i].translate[2]; - model.bindPose[i].rotation.x = ijoint[i].rotate[0]; - model.bindPose[i].rotation.y = ijoint[i].rotate[1]; - model.bindPose[i].rotation.z = ijoint[i].rotate[2]; - model.bindPose[i].rotation.w = ijoint[i].rotate[3]; + model.skeleton.bindPose[i].rotation.x = ijoint[i].rotate[0]; + model.skeleton.bindPose[i].rotation.y = ijoint[i].rotate[1]; + model.skeleton.bindPose[i].rotation.z = ijoint[i].rotate[2]; + model.skeleton.bindPose[i].rotation.w = ijoint[i].rotate[3]; - model.bindPose[i].scale.x = ijoint[i].scale[0]; - model.bindPose[i].scale.y = ijoint[i].scale[1]; - model.bindPose[i].scale.z = ijoint[i].scale[2]; + model.skeleton.bindPose[i].scale.x = ijoint[i].scale[0]; + model.skeleton.bindPose[i].scale.y = ijoint[i].scale[1]; + model.skeleton.bindPose[i].scale.z = ijoint[i].scale[2]; } - BuildPoseFromParentJoints(model.bones, model.boneCount, model.bindPose); + BuildPoseFromParentJoints(model.skeleton.bones, model.skeleton.boneCount, model.skeleton.bindPose); - for (int i = 0; i < model.meshCount; i++) - { - model.meshes[i].boneCount = model.boneCount; - model.meshes[i].boneMatrices = (Matrix *)RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix)); - - for (int j = 0; j < model.meshes[i].boneCount; j++) - { - model.meshes[i].boneMatrices[j] = MatrixIdentity(); - } - } + // Initialize runtime animation data: current pose and bone matrices + model.currentPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform)); + model.boneMatrices = (Matrix *)RL_CALLOC(model.skeleton.boneCount, sizeof(Matrix)); + for (int j = 0; j < model.skeleton.boneCount; j++) model.boneMatrices[j] = MatrixIdentity(); UnloadFileData(fileData); @@ -5078,37 +5119,42 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou //fread(anim, sizeof(IQMAnim), iqmHeader->num_anims, iqmFile); memcpy(anim, fileDataPtr + iqmHeader->ofs_anims, iqmHeader->num_anims*sizeof(IQMAnim)); - ModelAnimation *animations = (ModelAnimation *)RL_MALLOC(iqmHeader->num_anims*sizeof(ModelAnimation)); + ModelAnimation *animations = (ModelAnimation *)RL_CALLOC(iqmHeader->num_anims, sizeof(ModelAnimation)); // frameposes - unsigned short *framedata = (unsigned short *)RL_MALLOC(iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short)); + unsigned short *framedata = (unsigned short *)RL_CALLOC(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 = (IQMJoint *)RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint)); + IQMJoint *joints = (IQMJoint *)RL_CALLOC(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 = (BoneInfo *)RL_MALLOC(iqmHeader->num_poses*sizeof(BoneInfo)); - animations[a].framePoses = (Transform **)RL_MALLOC(anim[a].num_frames*sizeof(Transform *)); + BoneInfo *bones = (BoneInfo *)RL_CALLOC(iqmHeader->num_poses, sizeof(BoneInfo)); + + animations[a].keyframeCount = anim[a].num_frames; + animations[a].keyframePoses = (Transform **)RL_CALLOC(anim[a].num_frames, sizeof(Transform *)); memcpy(animations[a].name, fileDataPtr + iqmHeader->ofs_text + anim[a].name, 32); - TRACELOG(LOG_INFO, "IQM Anim %s", animations[a].name); - //animations[a].framerate = anim.framerate; // TODO: Use animation framerate data? + // TODO: Use animation framerate data? + //animations[a].framerate = anim.framerate; + + TRACELOG(LOG_INFO, "MODEL: [%s] Loaded animation: %s | Frames: %d | Framerate: %f", fileName, animations[a].name, animations[a].keyframeCount, anim[a].framerate); for (unsigned int j = 0; j < iqmHeader->num_poses; j++) { - // If animations and skeleton are in the same file, copy bone names to anim - if (iqmHeader->num_joints > 0) memcpy(animations[a].bones[j].name, fileDataPtr + iqmHeader->ofs_text + joints[j].name, BONE_NAME_LENGTH*sizeof(char)); - else memcpy(animations[a].bones[j].name, "ANIMJOINTNAME", 13); // Default bone name otherwise - animations[a].bones[j].parent = poses[j].parent; + bones[j].parent = poses[j].parent; + + // NOTE: No need to store bones names, bones only required to generate keyframe poses + //if (iqmHeader->num_joints > 0) memcpy(bones[j].name, fileDataPtr + iqmHeader->ofs_text + joints[j].name, BONE_NAME_LENGTH*sizeof(char)); + //else memcpy(bones[j].name, "ANIMJOINTNAME", 13); // Default bone name otherwise } - for (unsigned int j = 0; j < anim[a].num_frames; j++) animations[a].framePoses[j] = (Transform *)RL_MALLOC(iqmHeader->num_poses*sizeof(Transform)); + for (unsigned int j = 0; j < anim[a].num_frames; j++) + animations[a].keyframePoses[j] = (Transform *)RL_MALLOC(iqmHeader->num_poses*sizeof(Transform)); int dcounter = anim[a].first_frame*iqmHeader->num_framechannels; @@ -5116,87 +5162,87 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou { for (unsigned int i = 0; i < iqmHeader->num_poses; i++) { - animations[a].framePoses[frame][i].translation.x = poses[i].channeloffset[0]; + animations[a].keyframePoses[frame][i].translation.x = poses[i].channeloffset[0]; if (poses[i].mask & 0x01) { - animations[a].framePoses[frame][i].translation.x += framedata[dcounter]*poses[i].channelscale[0]; + animations[a].keyframePoses[frame][i].translation.x += framedata[dcounter]*poses[i].channelscale[0]; dcounter++; } - animations[a].framePoses[frame][i].translation.y = poses[i].channeloffset[1]; + animations[a].keyframePoses[frame][i].translation.y = poses[i].channeloffset[1]; if (poses[i].mask & 0x02) { - animations[a].framePoses[frame][i].translation.y += framedata[dcounter]*poses[i].channelscale[1]; + animations[a].keyframePoses[frame][i].translation.y += framedata[dcounter]*poses[i].channelscale[1]; dcounter++; } - animations[a].framePoses[frame][i].translation.z = poses[i].channeloffset[2]; + animations[a].keyframePoses[frame][i].translation.z = poses[i].channeloffset[2]; if (poses[i].mask & 0x04) { - animations[a].framePoses[frame][i].translation.z += framedata[dcounter]*poses[i].channelscale[2]; + animations[a].keyframePoses[frame][i].translation.z += framedata[dcounter]*poses[i].channelscale[2]; dcounter++; } - animations[a].framePoses[frame][i].rotation.x = poses[i].channeloffset[3]; + animations[a].keyframePoses[frame][i].rotation.x = poses[i].channeloffset[3]; if (poses[i].mask & 0x08) { - animations[a].framePoses[frame][i].rotation.x += framedata[dcounter]*poses[i].channelscale[3]; + animations[a].keyframePoses[frame][i].rotation.x += framedata[dcounter]*poses[i].channelscale[3]; dcounter++; } - animations[a].framePoses[frame][i].rotation.y = poses[i].channeloffset[4]; + animations[a].keyframePoses[frame][i].rotation.y = poses[i].channeloffset[4]; if (poses[i].mask & 0x10) { - animations[a].framePoses[frame][i].rotation.y += framedata[dcounter]*poses[i].channelscale[4]; + animations[a].keyframePoses[frame][i].rotation.y += framedata[dcounter]*poses[i].channelscale[4]; dcounter++; } - animations[a].framePoses[frame][i].rotation.z = poses[i].channeloffset[5]; + animations[a].keyframePoses[frame][i].rotation.z = poses[i].channeloffset[5]; if (poses[i].mask & 0x20) { - animations[a].framePoses[frame][i].rotation.z += framedata[dcounter]*poses[i].channelscale[5]; + animations[a].keyframePoses[frame][i].rotation.z += framedata[dcounter]*poses[i].channelscale[5]; dcounter++; } - animations[a].framePoses[frame][i].rotation.w = poses[i].channeloffset[6]; + animations[a].keyframePoses[frame][i].rotation.w = poses[i].channeloffset[6]; if (poses[i].mask & 0x40) { - animations[a].framePoses[frame][i].rotation.w += framedata[dcounter]*poses[i].channelscale[6]; + animations[a].keyframePoses[frame][i].rotation.w += framedata[dcounter]*poses[i].channelscale[6]; dcounter++; } - animations[a].framePoses[frame][i].scale.x = poses[i].channeloffset[7]; + animations[a].keyframePoses[frame][i].scale.x = poses[i].channeloffset[7]; if (poses[i].mask & 0x80) { - animations[a].framePoses[frame][i].scale.x += framedata[dcounter]*poses[i].channelscale[7]; + animations[a].keyframePoses[frame][i].scale.x += framedata[dcounter]*poses[i].channelscale[7]; dcounter++; } - animations[a].framePoses[frame][i].scale.y = poses[i].channeloffset[8]; + animations[a].keyframePoses[frame][i].scale.y = poses[i].channeloffset[8]; if (poses[i].mask & 0x100) { - animations[a].framePoses[frame][i].scale.y += framedata[dcounter]*poses[i].channelscale[8]; + animations[a].keyframePoses[frame][i].scale.y += framedata[dcounter]*poses[i].channelscale[8]; dcounter++; } - animations[a].framePoses[frame][i].scale.z = poses[i].channeloffset[9]; + animations[a].keyframePoses[frame][i].scale.z = poses[i].channeloffset[9]; if (poses[i].mask & 0x200) { - animations[a].framePoses[frame][i].scale.z += framedata[dcounter]*poses[i].channelscale[9]; + animations[a].keyframePoses[frame][i].scale.z += framedata[dcounter]*poses[i].channelscale[9]; dcounter++; } - animations[a].framePoses[frame][i].rotation = QuaternionNormalize(animations[a].framePoses[frame][i].rotation); + animations[a].keyframePoses[frame][i].rotation = QuaternionNormalize(animations[a].keyframePoses[frame][i].rotation); } } @@ -5205,15 +5251,17 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou { for (int i = 0; i < animations[a].boneCount; i++) { - if (animations[a].bones[i].parent >= 0) + if (bones[i].parent >= 0) { - animations[a].framePoses[frame][i].rotation = QuaternionMultiply(animations[a].framePoses[frame][animations[a].bones[i].parent].rotation, animations[a].framePoses[frame][i].rotation); - animations[a].framePoses[frame][i].translation = Vector3RotateByQuaternion(animations[a].framePoses[frame][i].translation, animations[a].framePoses[frame][animations[a].bones[i].parent].rotation); - animations[a].framePoses[frame][i].translation = Vector3Add(animations[a].framePoses[frame][i].translation, animations[a].framePoses[frame][animations[a].bones[i].parent].translation); - animations[a].framePoses[frame][i].scale = Vector3Multiply(animations[a].framePoses[frame][i].scale, animations[a].framePoses[frame][animations[a].bones[i].parent].scale); + animations[a].keyframePoses[frame][i].rotation = QuaternionMultiply(animations[a].keyframePoses[frame][bones[i].parent].rotation, animations[a].keyframePoses[frame][i].rotation); + animations[a].keyframePoses[frame][i].translation = Vector3RotateByQuaternion(animations[a].keyframePoses[frame][i].translation, animations[a].keyframePoses[frame][bones[i].parent].rotation); + animations[a].keyframePoses[frame][i].translation = Vector3Add(animations[a].keyframePoses[frame][i].translation, animations[a].keyframePoses[frame][bones[i].parent].translation); + animations[a].keyframePoses[frame][i].scale = Vector3Multiply(animations[a].keyframePoses[frame][i].scale, animations[a].keyframePoses[frame][bones[i].parent].scale); } } } + + RL_FREE(bones); } UnloadFileData(fileData); @@ -5375,7 +5423,7 @@ static Model LoadGLTF(const char *fileName) - Supports basic animations - Transforms, including parent-child relations, are applied on the mesh data, but the hierarchy is not kept (as it can't be represented) - - Mesh instances in the glTF file (i.e. same mesh linked from multiple nodes) + - Mesh instances in the glTF file (a.e. same mesh linked from multiple nodes) are turned into separate raylib Meshes RESTRICTIONS: @@ -5604,7 +5652,7 @@ static Model LoadGLTF(const char *fileName) // NOTE: 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) - // - 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 + // - Any glTF mesh linked from more than one Node (a.e. instancing) is turned into multiple Mesh's, as each Node will have its own transform applied // // WARNING: The code below disregards the scenes defined in the file, all nodes are used //---------------------------------------------------------------------------------------------------- @@ -6092,15 +6140,15 @@ static Model LoadGLTF(const char *fileName) // LIMITATIONS: // - Only supports 1 armature per file, and skips loading it if there are multiple armatures // - Only supports linear interpolation (default method in Blender when checked "Always Sample Animations" when exporting a GLTF file) - // - Only supports translation/rotation/scale animation channel.path, weights not considered (i.e. morph targets) + // - Only supports translation/rotation/scale animation channel.path, weights not considered (a.e. morph targets) //---------------------------------------------------------------------------------------------------- if (data->skins_count > 0) { cgltf_skin skin = data->skins[0]; - model.bones = LoadBoneInfoGLTF(skin, &model.boneCount); - model.bindPose = (Transform *)RL_MALLOC(model.boneCount*sizeof(Transform)); + model.skeleton.bones = LoadBoneInfoGLTF(skin, &model.skeleton.boneCount); + model.skeleton.bindPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform)); - for (int i = 0; i < model.boneCount; i++) + for (int i = 0; i < model.skeleton.boneCount; i++) { cgltf_node *node = skin.joints[i]; cgltf_float worldTransform[16]; @@ -6111,7 +6159,11 @@ static Model LoadGLTF(const char *fileName) worldTransform[2], worldTransform[6], worldTransform[10], worldTransform[14], worldTransform[3], worldTransform[7], worldTransform[11], worldTransform[15] }; - MatrixDecompose(worldMatrix, &(model.bindPose[i].translation), &(model.bindPose[i].rotation), &(model.bindPose[i].scale)); + + MatrixDecompose(worldMatrix, + &(model.skeleton.bindPose[i].translation), + &(model.skeleton.bindPose[i].rotation), + &(model.skeleton.bindPose[i].scale)); } if (data->skins_count > 1) TRACELOG(LOG_WARNING, "MODEL: [%s] can only load one skin (armature) per model, but gltf skins_count == %i", fileName, data->skins_count); @@ -6143,7 +6195,7 @@ static Model LoadGLTF(const char *fileName) // NOTE: JOINTS_n can only be vec4 and u8/u16 // SPECS: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes-overview - // WARNING: raylib only supports model.meshes[].boneIds as u8 (unsigned char), + // WARNING: raylib only supports model.meshes[].boneIndices as u8 (unsigned char), // if data is provided in any other format, it is converted to supported format but // it could imply data loss (a warning message is issued in that case) @@ -6151,16 +6203,16 @@ 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 = (unsigned char *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char)); + // Init raylib mesh boneIndices to copy glTF attribute data + model.meshes[meshIndex].boneIndices = (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) + LOAD_ATTRIBUTE(attribute, 4, unsigned char, model.meshes[meshIndex].boneIndices) } else if (attribute->component_type == cgltf_component_type_r_16u) { - // Init raylib mesh boneIds to copy glTF attribute data - model.meshes[meshIndex].boneIds = (unsigned char *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char)); + // Init raylib mesh boneIndices to copy glTF attribute data + model.meshes[meshIndex].boneIndices = (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 = (unsigned short *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned short)); @@ -6177,7 +6229,7 @@ static Model LoadGLTF(const char *fileName) } // Despite the possible overflow, we convert data to unsigned char - model.meshes[meshIndex].boneIds[b] = (unsigned char)temp[b]; + model.meshes[meshIndex].boneIndices[b] = (unsigned char)temp[b]; } RL_FREE(temp); @@ -6243,7 +6295,7 @@ static Model LoadGLTF(const char *fileName) if (data->skins_count > 0 && !hasJoints && node->parent != NULL && node->parent->mesh == NULL) { int parentBoneId = -1; - for (int joint = 0; joint < model.boneCount; joint++) + for (int joint = 0; joint < model.skeleton.boneCount; joint++) { if (data->skins[0].joints[joint] == node->parent) { @@ -6254,38 +6306,34 @@ static Model LoadGLTF(const char *fileName) if (parentBoneId >= 0) { - model.meshes[meshIndex].boneIds = (unsigned char *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char)); + model.meshes[meshIndex].boneIndices = (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) { - model.meshes[meshIndex].boneIds[vertexIndex] = (unsigned char)parentBoneId; + model.meshes[meshIndex].boneIndices[vertexIndex] = (unsigned char)parentBoneId; model.meshes[meshIndex].boneWeights[vertexIndex] = 1.0f; } } } - // Animated vertex data +#if !defined(SUPPORT_GPU_SKINNING) + // Animated vertex data (CPU skinning) 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 = (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)); - } - - // Bone Transform Matrices - model.meshes[meshIndex].boneCount = model.boneCount; - model.meshes[meshIndex].boneMatrices = (Matrix *)RL_CALLOC(model.meshes[meshIndex].boneCount, sizeof(Matrix)); - - for (int j = 0; j < model.meshes[meshIndex].boneCount; j++) - { - model.meshes[meshIndex].boneMatrices[j] = MatrixIdentity(); - } + if (model.meshes[meshIndex].normals != NULL) memcpy(model.meshes[meshIndex].animNormals, model.meshes[meshIndex].normals, model.meshes[meshIndex].vertexCount*3*sizeof(float)); +#endif + model.meshes[meshIndex].boneCount = model.skeleton.boneCount; meshIndex++; // Move to next mesh } } + + // Initialize runtime animation data: current pose and bone matrices + model.currentPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform)); + model.boneMatrices = (Matrix *)RL_CALLOC(model.skeleton.boneCount, sizeof(Matrix)); + for (int j = 0; j < model.skeleton.boneCount; j++) model.boneMatrices[j] = MatrixIdentity(); //---------------------------------------------------------------------------------------------------- // Free all cgltf loaded data @@ -6484,11 +6532,11 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo }; MatrixDecompose(worldMatrix, &worldTransform.translation, &worldTransform.rotation, &worldTransform.scale); - for (unsigned int i = 0; i < data->animations_count; i++) + for (unsigned int a = 0; a < data->animations_count; a++) { - animations[i].bones = LoadBoneInfoGLTF(skin, &animations[i].boneCount); + BoneInfo *bones = LoadBoneInfoGLTF(skin, &animations[a].boneCount); - cgltf_animation animData = data->animations[i]; + cgltf_animation animData = data->animations[a]; struct Channels { cgltf_animation_channel *translate; @@ -6497,7 +6545,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo cgltf_interpolation_type interpolationType; }; - struct Channels *boneChannels = (struct Channels *)RL_CALLOC(animations[i].boneCount, sizeof(struct Channels)); + struct Channels *boneChannels = (struct Channels *)RL_CALLOC(animations[a].boneCount, sizeof(struct Channels)); float animDuration = 0.0f; for (unsigned int j = 0; j < animData.channels_count; j++) @@ -6514,11 +6562,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo } } - if (boneIndex == -1) - { - // Animation channel for a node not in the armature - continue; - } + if (boneIndex == -1) continue; // Animation channel for a node not in the skeleton boneChannels[boneIndex].interpolationType = animData.channels[j].sampler->interpolation; @@ -6538,34 +6582,34 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo } else { - TRACELOG(LOG_WARNING, "MODEL: [%s] Unsupported target_path on channel %d's sampler for animation %d. Skipping.", fileName, j, i); + TRACELOG(LOG_WARNING, "MODEL: [%s] Unsupported target_path on channel %d's sampler for animation %d. Skipping.", fileName, j, a); } } else TRACELOG(LOG_WARNING, "MODEL: [%s] Invalid interpolation curve encountered for GLTF animation.", fileName); - float t = 0.0f; - cgltf_bool r = cgltf_accessor_read_float(channel.sampler->input, channel.sampler->input->count - 1, &t, 1); + float time = 0.0f; + cgltf_bool result = cgltf_accessor_read_float(channel.sampler->input, channel.sampler->input->count - 1, &time, 1); - if (!r) + if (!result) { TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load input time", fileName); continue; } - animDuration = (t > animDuration)? t : animDuration; + animDuration = (time > animDuration)? time : animDuration; } - if (animData.name != NULL) strncpy(animations[i].name, animData.name, sizeof(animations[i].name) - 1); + if (animData.name != NULL) strncpy(animations[a].name, animData.name, sizeof(animations[a].name) - 1); - animations[i].frameCount = (int)(animDuration*GLTF_FRAMERATE) + 1; - animations[i].framePoses = (Transform **)RL_MALLOC(animations[i].frameCount*sizeof(Transform *)); + animations[a].keyframeCount = (int)(animDuration*GLTF_FRAMERATE) + 1; + animations[a].keyframePoses = (Transform **)RL_CALLOC(animations[a].keyframeCount, sizeof(Transform *)); - for (int j = 0; j < animations[i].frameCount; j++) + for (int j = 0; j < animations[a].keyframeCount; j++) { - animations[i].framePoses[j] = (Transform *)RL_MALLOC(animations[i].boneCount*sizeof(Transform)); + animations[a].keyframePoses[j] = (Transform *)RL_CALLOC(animations[a].boneCount, sizeof(Transform)); float time = (float)j / GLTF_FRAMERATE; - for (int k = 0; k < animations[i].boneCount; k++) + for (int k = 0; k < animations[a].boneCount; k++) { Vector3 translation = {skin.joints[k]->translation[0], skin.joints[k]->translation[1], skin.joints[k]->translation[2]}; Quaternion rotation = {skin.joints[k]->rotation[0], skin.joints[k]->rotation[1], skin.joints[k]->rotation[2], skin.joints[k]->rotation[3]}; @@ -6575,7 +6619,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo { if (!GetPoseAtTimeGLTF(boneChannels[k].interpolationType, boneChannels[k].translate->sampler->input, boneChannels[k].translate->sampler->output, time, &translation)) { - TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load translate pose data for bone %s", fileName, animations[i].bones[k].name); + TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load translate pose data for bone %s", fileName, bones[k].name); } } @@ -6583,7 +6627,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo { if (!GetPoseAtTimeGLTF(boneChannels[k].interpolationType, boneChannels[k].rotate->sampler->input, boneChannels[k].rotate->sampler->output, time, &rotation)) { - TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load rotate pose data for bone %s", fileName, animations[i].bones[k].name); + TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load rotate pose data for bone %s", fileName, bones[k].name); } } @@ -6591,40 +6635,43 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo { if (!GetPoseAtTimeGLTF(boneChannels[k].interpolationType, boneChannels[k].scale->sampler->input, boneChannels[k].scale->sampler->output, time, &scale)) { - TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load scale pose data for bone %s", fileName, animations[i].bones[k].name); + TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load scale pose data for bone %s", fileName, bones[k].name); } } - animations[i].framePoses[j][k] = (Transform){ + animations[a].keyframePoses[j][k] = (Transform){ .translation = translation, .rotation = rotation, .scale = scale }; } - Transform *root = &animations[i].framePoses[j][0]; + Transform *root = &animations[a].keyframePoses[j][0]; root->rotation = QuaternionMultiply(worldTransform.rotation, root->rotation); root->scale = Vector3Multiply(root->scale, worldTransform.scale); root->translation = Vector3Multiply(root->translation, worldTransform.scale); root->translation = Vector3RotateByQuaternion(root->translation, worldTransform.rotation); root->translation = Vector3Add(root->translation, worldTransform.translation); - BuildPoseFromParentJoints(animations[i].bones, animations[i].boneCount, animations[i].framePoses[j]); + BuildPoseFromParentJoints(bones, animations[a].boneCount, animations[a].keyframePoses[j]); } - TRACELOG(LOG_INFO, "MODEL: [%s] Loaded animation: %s (%d frames, %fs)", fileName, (animData.name != NULL)? animData.name : "NULL", animations[i].frameCount, animDuration); + TRACELOG(LOG_INFO, "MODEL: [%s] Loaded animation: %s | Frames: %d | Duration: %fs", fileName, (animData.name != NULL)? animData.name : "NULL", animations[a].keyframeCount, animDuration); RL_FREE(boneChannels); + RL_FREE(bones); } } if (data->skins_count > 1) { - TRACELOG(LOG_WARNING, "MODEL: [%s] expected exactly one skin to load animation data from, but found %i", fileName, data->skins_count); + TRACELOG(LOG_WARNING, "MODEL: [%s] Expected one unique skin to load animation data from, but found %i", fileName, data->skins_count); } cgltf_free(data); } + UnloadFileData(fileData); + return animations; } #endif @@ -6795,13 +6842,13 @@ static Model LoadM3D(const char *fileName) // WARNING: Sorting is not needed, valid M3D model files should already be sorted // Just keeping the sorting function for reference (Check PR #3363 #3385) /* - for (i = 1; i < m3d->numface; i++) + for (a = 1; a < m3d->numface; a++) { - if (m3d->face[i-1].materialid <= m3d->face[i].materialid) continue; + if (m3d->face[a-1].materialid <= m3d->face[a].materialid) continue; - // face[i-1] > face[i]. slide face[i] lower - m3df_t slider = m3d->face[i]; - j = i-1; + // face[a-1] > face[a]. slide face[a] lower + m3df_t slider = m3d->face[a]; + j = a-1; do { // face[j] > slider, face[j+1] is svailable vacant gap @@ -6875,10 +6922,12 @@ static Model LoadM3D(const char *fileName) if (m3d->numbone && m3d->numskin) { - model.meshes[k].boneIds = (unsigned char *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); + model.meshes[k].boneIndices = (unsigned char *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); model.meshes[k].boneWeights = (float *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(float)); +#if !defined(SUPPORT_GPU_SKINNING) model.meshes[k].animVertices = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float)); model.meshes[k].animNormals = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float)); +#endif } model.meshMaterial[k] = mi + 1; @@ -6942,7 +6991,7 @@ static Model LoadM3D(const char *fileName) { for (j = 0; j < 4; j++) { - model.meshes[k].boneIds[l*12 + n*4 + j] = m3d->skin[skinid].boneid[j]; + model.meshes[k].boneIndices[l*12 + n*4 + j] = m3d->skin[skinid].boneid[j]; model.meshes[k].boneWeights[l*12 + n*4 + j] = m3d->skin[skinid].weight[j]; } } @@ -6950,7 +6999,7 @@ static Model LoadM3D(const char *fileName) { // raylib does not handle boneless meshes with skeletal animations, so // we put all vertices without a bone into a special "no bone" bone - model.meshes[k].boneIds[l*12 + n*4] = m3d->numbone; + model.meshes[k].boneIndices[l*12 + n*4] = m3d->numbone; model.meshes[k].boneWeights[l*12 + n*4] = 1.0f; } } @@ -7031,47 +7080,47 @@ static Model LoadM3D(const char *fileName) // Load bones if (m3d->numbone) { - model.boneCount = m3d->numbone + 1; - model.bones = (BoneInfo *)RL_CALLOC(model.boneCount, sizeof(BoneInfo)); - model.bindPose = (Transform *)RL_CALLOC(model.boneCount, sizeof(Transform)); + model.skeleton.boneCount = m3d->numbone + 1; + model.skeleton.bones = (BoneInfo *)RL_CALLOC(model.skeleton.boneCount, sizeof(BoneInfo)); + model.skeleton.bindPose = (Transform *)RL_CALLOC(model.skeleton.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) - 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; - model.bindPose[i].rotation.x = m3d->vertex[m3d->bone[i].ori].x; - model.bindPose[i].rotation.y = m3d->vertex[m3d->bone[i].ori].y; - model.bindPose[i].rotation.z = m3d->vertex[m3d->bone[i].ori].z; - model.bindPose[i].rotation.w = m3d->vertex[m3d->bone[i].ori].w; + model.skeleton.bones[i].parent = m3d->bone[i].parent; + strncpy(model.skeleton.bones[i].name, m3d->bone[i].name, sizeof(model.skeleton.bones[i].name) - 1); + model.skeleton.bindPose[i].translation.x = m3d->vertex[m3d->bone[i].pos].x*m3d->scale; + model.skeleton.bindPose[i].translation.y = m3d->vertex[m3d->bone[i].pos].y*m3d->scale; + model.skeleton.bindPose[i].translation.z = m3d->vertex[m3d->bone[i].pos].z*m3d->scale; + model.skeleton.bindPose[i].rotation.x = m3d->vertex[m3d->bone[i].ori].x; + model.skeleton.bindPose[i].rotation.y = m3d->vertex[m3d->bone[i].ori].y; + model.skeleton.bindPose[i].rotation.z = m3d->vertex[m3d->bone[i].ori].z; + model.skeleton.bindPose[i].rotation.w = m3d->vertex[m3d->bone[i].ori].w; - // TODO: If the orientation quaternion is not normalized, then that's encoding scaling - model.bindPose[i].rotation = QuaternionNormalize(model.bindPose[i].rotation); - model.bindPose[i].scale.x = model.bindPose[i].scale.y = model.bindPose[i].scale.z = 1.0f; + // NOTE: If the orientation quaternion is not normalized, then that's encoding scaling + model.skeleton.bindPose[i].rotation = QuaternionNormalize(model.skeleton.bindPose[i].rotation); + model.skeleton.bindPose[i].scale.x = model.skeleton.bindPose[i].scale.y = model.skeleton.bindPose[i].scale.z = 1.0f; // Child bones are stored in parent bone relative space, convert that into model space - if (model.bones[i].parent >= 0) + if (model.skeleton.bones[i].parent >= 0) { - model.bindPose[i].rotation = QuaternionMultiply(model.bindPose[model.bones[i].parent].rotation, model.bindPose[i].rotation); - model.bindPose[i].translation = Vector3RotateByQuaternion(model.bindPose[i].translation, model.bindPose[model.bones[i].parent].rotation); - model.bindPose[i].translation = Vector3Add(model.bindPose[i].translation, model.bindPose[model.bones[i].parent].translation); - model.bindPose[i].scale = Vector3Multiply(model.bindPose[i].scale, model.bindPose[model.bones[i].parent].scale); + model.skeleton.bindPose[i].rotation = QuaternionMultiply(model.skeleton.bindPose[model.skeleton.bones[i].parent].rotation, model.skeleton.bindPose[i].rotation); + model.skeleton.bindPose[i].translation = Vector3RotateByQuaternion(model.skeleton.bindPose[i].translation, model.skeleton.bindPose[model.skeleton.bones[i].parent].rotation); + model.skeleton.bindPose[i].translation = Vector3Add(model.skeleton.bindPose[i].translation, model.skeleton.bindPose[model.skeleton.bones[i].parent].translation); + model.skeleton.bindPose[i].scale = Vector3Multiply(model.skeleton.bindPose[i].scale, model.skeleton.bindPose[model.skeleton.bones[i].parent].scale); } } // Add a special "no bone" bone - model.bones[i].parent = -1; - memcpy(model.bones[i].name, "NO BONE", 7); - model.bindPose[i].translation.x = 0.0f; - model.bindPose[i].translation.y = 0.0f; - model.bindPose[i].translation.z = 0.0f; - model.bindPose[i].rotation.x = 0.0f; - model.bindPose[i].rotation.y = 0.0f; - model.bindPose[i].rotation.z = 0.0f; - model.bindPose[i].rotation.w = 1.0f; - model.bindPose[i].scale.x = model.bindPose[i].scale.y = model.bindPose[i].scale.z = 1.0f; + model.skeleton.bones[i].parent = -1; + memcpy(model.skeleton.bones[i].name, "NO BONE", 7); + model.skeleton.bindPose[i].translation.x = 0.0f; + model.skeleton.bindPose[i].translation.y = 0.0f; + model.skeleton.bindPose[i].translation.z = 0.0f; + model.skeleton.bindPose[i].rotation.x = 0.0f; + model.skeleton.bindPose[i].rotation.y = 0.0f; + model.skeleton.bindPose[i].rotation.z = 0.0f; + model.skeleton.bindPose[i].rotation.w = 1.0f; + model.skeleton.bindPose[i].scale.x = model.skeleton.bindPose[i].scale.y = model.skeleton.bindPose[i].scale.z = 1.0f; } // Load bone-pose default mesh into animation vertices. These will be updated when UpdateModelAnimation gets @@ -7080,16 +7129,19 @@ static Model LoadM3D(const char *fileName) { for (i = 0; i < model.meshCount; i++) { + model.meshes[i].boneCount = model.skeleton.boneCount; + +#if !defined(SUPPORT_GPU_SKINNING) + // Initialize vertex buffers for CPU skinning memcpy(model.meshes[i].animVertices, model.meshes[i].vertices, model.meshes[i].vertexCount*3*sizeof(float)); 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 = (Matrix *)RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix)); - for (j = 0; j < model.meshes[i].boneCount; j++) - { - model.meshes[i].boneMatrices[j] = MatrixIdentity(); - } +#endif } + + // Initialize runtime animation data: current pose and bone matrices + model.currentPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform)); + model.boneMatrices = (Matrix *)RL_CALLOC(model.skeleton.boneCount, sizeof(Matrix)); + for (int j = 0; j < model.skeleton.boneCount; j++) model.boneMatrices[j] = MatrixIdentity(); } m3d_free(m3d); @@ -7123,8 +7175,7 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou UnloadFileData(fileData); return NULL; } - else TRACELOG(LOG_INFO, "MODEL: [%s] M3D data loaded successfully: %i animations, %i bones, %i skins", fileName, - m3d->numaction, m3d->numbone, m3d->numskin); + else TRACELOG(LOG_INFO, "MODEL: [%s] M3D data loaded successfully: %i animations, %i bones, %i skins", fileName, m3d->numaction, m3d->numbone, m3d->numskin); // No animation or bones, exit out. skins are not required because some people use one animation for N models if (!m3d->numaction || !m3d->numbone) @@ -7139,29 +7190,30 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou 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 = (BoneInfo *)RL_MALLOC((m3d->numbone + 1)*sizeof(BoneInfo)); - animations[a].framePoses = (Transform **)RL_MALLOC(animations[a].frameCount*sizeof(Transform *)); + BoneInfo *bones = (BoneInfo *)RL_CALLOC((m3d->numbone + 1), sizeof(BoneInfo)); + + animations[a].keyframeCount = m3d->action[a].durationmsec/M3D_ANIMDELAY; + animations[a].keyframePoses = (Transform **)RL_CALLOC(animations[a].keyframeCount, 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); + TRACELOG(LOG_INFO, "MODEL: [%s] Loaded animation: %s | Frames: %d | Duration: %fs", fileName, animations[a].name, animations[a].keyframeCount, m3d->action[a].durationmsec); 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) - 1); + bones[i].parent = m3d->bone[i].parent; + strncpy(bones[i].name, m3d->bone[i].name, sizeof(bones[i].name) - 1); } // A special, never transformed "no bone" bone, used for boneless vertices - animations[a].bones[i].parent = -1; - memcpy(animations[a].bones[i].name, "NO BONE", 7); + bones[i].parent = -1; + memcpy(bones[i].name, "NO BONE", 7); // M3D stores frames at arbitrary intervals with sparse skeletons. We need full skeletons at // regular intervals, so let the M3D SDK do the heavy lifting and calculate interpolated bones - for (i = 0; i < animations[a].frameCount; i++) + for (i = 0; i < animations[a].keyframeCount; i++) { - animations[a].framePoses[i] = (Transform *)RL_MALLOC((m3d->numbone + 1)*sizeof(Transform)); + animations[a].keyframePoses[i] = (Transform *)RL_CALLOC((m3d->numbone + 1), sizeof(Transform)); m3db_t *pose = m3d_pose(m3d, a, i*M3D_ANIMDELAY); @@ -7169,38 +7221,43 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou { for (j = 0; j < (int)m3d->numbone; j++) { - animations[a].framePoses[i][j].translation.x = m3d->vertex[pose[j].pos].x*m3d->scale; - animations[a].framePoses[i][j].translation.y = m3d->vertex[pose[j].pos].y*m3d->scale; - animations[a].framePoses[i][j].translation.z = m3d->vertex[pose[j].pos].z*m3d->scale; - animations[a].framePoses[i][j].rotation.x = m3d->vertex[pose[j].ori].x; - animations[a].framePoses[i][j].rotation.y = m3d->vertex[pose[j].ori].y; - animations[a].framePoses[i][j].rotation.z = m3d->vertex[pose[j].ori].z; - animations[a].framePoses[i][j].rotation.w = m3d->vertex[pose[j].ori].w; - animations[a].framePoses[i][j].rotation = QuaternionNormalize(animations[a].framePoses[i][j].rotation); - animations[a].framePoses[i][j].scale.x = animations[a].framePoses[i][j].scale.y = animations[a].framePoses[i][j].scale.z = 1.0f; + animations[a].keyframePoses[i][j].translation.x = m3d->vertex[pose[j].pos].x*m3d->scale; + animations[a].keyframePoses[i][j].translation.y = m3d->vertex[pose[j].pos].y*m3d->scale; + animations[a].keyframePoses[i][j].translation.z = m3d->vertex[pose[j].pos].z*m3d->scale; + animations[a].keyframePoses[i][j].rotation.x = m3d->vertex[pose[j].ori].x; + animations[a].keyframePoses[i][j].rotation.y = m3d->vertex[pose[j].ori].y; + animations[a].keyframePoses[i][j].rotation.z = m3d->vertex[pose[j].ori].z; + animations[a].keyframePoses[i][j].rotation.w = m3d->vertex[pose[j].ori].w; + animations[a].keyframePoses[i][j].rotation = QuaternionNormalize(animations[a].keyframePoses[i][j].rotation); + animations[a].keyframePoses[i][j].scale.x = animations[a].keyframePoses[i][j].scale.y = animations[a].keyframePoses[i][j].scale.z = 1.0f; // Child bones are stored in parent bone relative space, convert that into model space - if (animations[a].bones[j].parent >= 0) + if (bones[j].parent >= 0) { - animations[a].framePoses[i][j].rotation = QuaternionMultiply(animations[a].framePoses[i][animations[a].bones[j].parent].rotation, animations[a].framePoses[i][j].rotation); - animations[a].framePoses[i][j].translation = Vector3RotateByQuaternion(animations[a].framePoses[i][j].translation, animations[a].framePoses[i][animations[a].bones[j].parent].rotation); - animations[a].framePoses[i][j].translation = Vector3Add(animations[a].framePoses[i][j].translation, animations[a].framePoses[i][animations[a].bones[j].parent].translation); - animations[a].framePoses[i][j].scale = Vector3Multiply(animations[a].framePoses[i][j].scale, animations[a].framePoses[i][animations[a].bones[j].parent].scale); + animations[a].keyframePoses[i][j].rotation = QuaternionMultiply(animations[a].keyframePoses[i][bones[j].parent].rotation, animations[a].keyframePoses[i][j].rotation); + animations[a].keyframePoses[i][j].translation = Vector3RotateByQuaternion(animations[a].keyframePoses[i][j].translation, animations[a].keyframePoses[i][bones[j].parent].rotation); + animations[a].keyframePoses[i][j].translation = Vector3Add(animations[a].keyframePoses[i][j].translation, animations[a].keyframePoses[i][bones[j].parent].translation); + animations[a].keyframePoses[i][j].scale = Vector3Multiply(animations[a].keyframePoses[i][j].scale, animations[a].keyframePoses[i][bones[j].parent].scale); } } // Default transform for the "no bone" bone - animations[a].framePoses[i][j].translation.x = 0.0f; - animations[a].framePoses[i][j].translation.y = 0.0f; - animations[a].framePoses[i][j].translation.z = 0.0f; - animations[a].framePoses[i][j].rotation.x = 0.0f; - animations[a].framePoses[i][j].rotation.y = 0.0f; - animations[a].framePoses[i][j].rotation.z = 0.0f; - animations[a].framePoses[i][j].rotation.w = 1.0f; - animations[a].framePoses[i][j].scale.x = animations[a].framePoses[i][j].scale.y = animations[a].framePoses[i][j].scale.z = 1.0f; + animations[a].keyframePoses[i][j].translation.x = 0.0f; + animations[a].keyframePoses[i][j].translation.y = 0.0f; + animations[a].keyframePoses[i][j].translation.z = 0.0f; + animations[a].keyframePoses[i][j].rotation.x = 0.0f; + animations[a].keyframePoses[i][j].rotation.y = 0.0f; + animations[a].keyframePoses[i][j].rotation.z = 0.0f; + animations[a].keyframePoses[i][j].rotation.w = 1.0f; + animations[a].keyframePoses[i][j].scale.x = 1.0f; + animations[a].keyframePoses[i][j].scale.y = 1.0f; + animations[a].keyframePoses[i][j].scale.z = 1.0f; + RL_FREE(pose); } } + + RL_FREE(bones); } m3d_free(m3d); From 149062f715d2dac9361a889fa5615f9037bccef9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 00:19:18 +0000 Subject: [PATCH 072/319] rlparser: update raylib_api.* by CI --- tools/rlparser/output/raylib_api.json | 167 +++++++--------- tools/rlparser/output/raylib_api.lua | 152 +++++++-------- tools/rlparser/output/raylib_api.txt | 265 +++++++++++++------------- tools/rlparser/output/raylib_api.xml | 69 +++---- 4 files changed, 301 insertions(+), 352 deletions(-) diff --git a/tools/rlparser/output/raylib_api.json b/tools/rlparser/output/raylib_api.json index f102abb1d..3f56434cd 100644 --- a/tools/rlparser/output/raylib_api.json +++ b/tools/rlparser/output/raylib_api.json @@ -837,6 +837,21 @@ "name": "indices", "description": "Vertex indices (in case vertex data comes indexed)" }, + { + "type": "int", + "name": "boneCount", + "description": "Number of bones (MAX: 256 bones)" + }, + { + "type": "unsigned char *", + "name": "boneIndices", + "description": "Vertex bone indices, up to 4 bones influence by vertex (skinning) (shader-location = 6)" + }, + { + "type": "float *", + "name": "boneWeights", + "description": "Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7)" + }, { "type": "float *", "name": "animVertices", @@ -847,26 +862,6 @@ "name": "animNormals", "description": "Animated normals (after bones transformations)" }, - { - "type": "unsigned char *", - "name": "boneIds", - "description": "Vertex bone ids, max 255 bone ids, up to 4 bones influence by vertex (skinning) (shader-location = 6)" - }, - { - "type": "float *", - "name": "boneWeights", - "description": "Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7)" - }, - { - "type": "Matrix *", - "name": "boneMatrices", - "description": "Bones animated transformation matrices" - }, - { - "type": "int", - "name": "boneCount", - "description": "Number of bones" - }, { "type": "unsigned int", "name": "vaoId", @@ -974,6 +969,27 @@ } ] }, + { + "name": "ModelSkeleton", + "description": "Skeleton, animation bones hierarchy", + "fields": [ + { + "type": "int", + "name": "boneCount", + "description": "Number of bones" + }, + { + "type": "BoneInfo *", + "name": "bones", + "description": "Bones information (skeleton)" + }, + { + "type": "ModelAnimPose", + "name": "bindPose", + "description": "Bones base transformation (Transform[])" + } + ] + }, { "name": "Model", "description": "Model, meshes, materials and animation data", @@ -1009,25 +1025,25 @@ "description": "Mesh material number" }, { - "type": "int", - "name": "boneCount", - "description": "Number of bones" + "type": "ModelSkeleton", + "name": "skeleton", + "description": "Skeleton for animation" }, { - "type": "BoneInfo *", - "name": "bones", - "description": "Bones information (skeleton)" + "type": "ModelAnimPose", + "name": "currentPose", + "description": "Current animation pose (Transform[])" }, { - "type": "Transform *", - "name": "bindPose", - "description": "Bones base transformation (pose)" + "type": "Matrix *", + "name": "boneMatrices", + "description": "Bones animated transformation matrices" } ] }, { "name": "ModelAnimation", - "description": "ModelAnimation", + "description": "ModelAnimation, contains a full animation sequence", "fields": [ { "type": "char[32]", @@ -1037,22 +1053,17 @@ { "type": "int", "name": "boneCount", - "description": "Number of bones" + "description": "Number of bones (per pose)" }, { "type": "int", - "name": "frameCount", - "description": "Number of animation frames" + "name": "keyframeCount", + "description": "Number of animation key frames" }, { - "type": "BoneInfo *", - "name": "bones", - "description": "Bones information (skeleton)" - }, - { - "type": "Transform **", - "name": "framePoses", - "description": "Poses array by frame" + "type": "ModelAnimPose *", + "name": "keyframePoses", + "description": "Animation sequence keyframe poses [keyframe][pose]" } ] }, @@ -1404,6 +1415,11 @@ "type": "Camera3D", "name": "Camera", "description": "Camera type fallback, defaults to Camera3D" + }, + { + "type": "Transform", + "name": "*ModelAnimPose", + "description": "Anim pose, an array of Transform[]" } ], "enums": [ @@ -2502,7 +2518,7 @@ { "name": "SHADER_LOC_MAP_HEIGHT", "value": 21, - "description": "Shader location: sampler2d texture: height" + "description": "Shader location: sampler2d texture: heightmap" }, { "name": "SHADER_LOC_MAP_CUBEMAP", @@ -2527,22 +2543,22 @@ { "name": "SHADER_LOC_VERTEX_BONEIDS", "value": 26, - "description": "Shader location: vertex attribute: boneIds" + "description": "Shader location: vertex attribute: bone indices" }, { "name": "SHADER_LOC_VERTEX_BONEWEIGHTS", "value": 27, - "description": "Shader location: vertex attribute: boneWeights" + "description": "Shader location: vertex attribute: bone weights" }, { - "name": "SHADER_LOC_BONE_MATRICES", + "name": "SHADER_LOC_MATRIX_BONETRANSFORMS", "value": 28, - "description": "Shader location: array of matrices uniform: boneMatrices" + "description": "Shader location: matrix attribute: bone transforms (animation)" }, { - "name": "SHADER_LOC_VERTEX_INSTANCE_TX", + "name": "SHADER_LOC_VERTEX_INSTANCETRANSFORMS", "value": 29, - "description": "Shader location: vertex attribute: instanceTransform" + "description": "Shader location: vertex attribute: instance transforms" } ] }, @@ -11400,7 +11416,7 @@ }, { "name": "UpdateModelAnimation", - "description": "Update model animation pose (CPU)", + "description": "Update model animation pose (vertex buffers and bone matrices)", "returnType": "void", "params": [ { @@ -11412,33 +11428,14 @@ "name": "anim" }, { - "type": "int", + "type": "float", "name": "frame" } ] }, { - "name": "UpdateModelAnimationBones", - "description": "Update model animation mesh bone matrices (GPU skinning)", - "returnType": "void", - "params": [ - { - "type": "Model", - "name": "model" - }, - { - "type": "ModelAnimation", - "name": "anim" - }, - { - "type": "int", - "name": "frame" - } - ] - }, - { - "name": "UpdateModelAnimationBonesLerp", - "description": "Update model animation mesh bone matrices with interpolation between two poses(GPU skinning)", + "name": "UpdateModelAnimationEx", + "description": "Update model animation pose, blending two animations", "returnType": "void", "params": [ { @@ -11450,7 +11447,7 @@ "name": "animA" }, { - "type": "int", + "type": "float", "name": "frameA" }, { @@ -11458,34 +11455,12 @@ "name": "animB" }, { - "type": "int", + "type": "float", "name": "frameB" }, { "type": "float", - "name": "value" - } - ] - }, - { - "name": "UpdateModelVertsToCurrentBones", - "description": "Update model vertices according to mesh bone matrices (CPU)", - "returnType": "void", - "params": [ - { - "type": "Model", - "name": "model" - } - ] - }, - { - "name": "UnloadModelAnimation", - "description": "Unload animation data", - "returnType": "void", - "params": [ - { - "type": "ModelAnimation", - "name": "anim" + "name": "blend" } ] }, diff --git a/tools/rlparser/output/raylib_api.lua b/tools/rlparser/output/raylib_api.lua index 3707896bb..85edb02f1 100644 --- a/tools/rlparser/output/raylib_api.lua +++ b/tools/rlparser/output/raylib_api.lua @@ -837,6 +837,21 @@ return { name = "indices", description = "Vertex indices (in case vertex data comes indexed)" }, + { + type = "int", + name = "boneCount", + description = "Number of bones (MAX: 256 bones)" + }, + { + type = "unsigned char *", + name = "boneIndices", + description = "Vertex bone indices, up to 4 bones influence by vertex (skinning) (shader-location = 6)" + }, + { + type = "float *", + name = "boneWeights", + description = "Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7)" + }, { type = "float *", name = "animVertices", @@ -847,26 +862,6 @@ return { name = "animNormals", description = "Animated normals (after bones transformations)" }, - { - type = "unsigned char *", - name = "boneIds", - description = "Vertex bone ids, max 255 bone ids, up to 4 bones influence by vertex (skinning) (shader-location = 6)" - }, - { - type = "float *", - name = "boneWeights", - description = "Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7)" - }, - { - type = "Matrix *", - name = "boneMatrices", - description = "Bones animated transformation matrices" - }, - { - type = "int", - name = "boneCount", - description = "Number of bones" - }, { type = "unsigned int", name = "vaoId", @@ -974,6 +969,27 @@ return { } } }, + { + name = "ModelSkeleton", + description = "Skeleton, animation bones hierarchy", + fields = { + { + type = "int", + name = "boneCount", + description = "Number of bones" + }, + { + type = "BoneInfo *", + name = "bones", + description = "Bones information (skeleton)" + }, + { + type = "ModelAnimPose", + name = "bindPose", + description = "Bones base transformation (Transform[])" + } + } + }, { name = "Model", description = "Model, meshes, materials and animation data", @@ -1009,25 +1025,25 @@ return { description = "Mesh material number" }, { - type = "int", - name = "boneCount", - description = "Number of bones" + type = "ModelSkeleton", + name = "skeleton", + description = "Skeleton for animation" }, { - type = "BoneInfo *", - name = "bones", - description = "Bones information (skeleton)" + type = "ModelAnimPose", + name = "currentPose", + description = "Current animation pose (Transform[])" }, { - type = "Transform *", - name = "bindPose", - description = "Bones base transformation (pose)" + type = "Matrix *", + name = "boneMatrices", + description = "Bones animated transformation matrices" } } }, { name = "ModelAnimation", - description = "ModelAnimation", + description = "ModelAnimation, contains a full animation sequence", fields = { { type = "char[32]", @@ -1037,22 +1053,17 @@ return { { type = "int", name = "boneCount", - description = "Number of bones" + description = "Number of bones (per pose)" }, { type = "int", - name = "frameCount", - description = "Number of animation frames" + name = "keyframeCount", + description = "Number of animation key frames" }, { - type = "BoneInfo *", - name = "bones", - description = "Bones information (skeleton)" - }, - { - type = "Transform **", - name = "framePoses", - description = "Poses array by frame" + type = "ModelAnimPose *", + name = "keyframePoses", + description = "Animation sequence keyframe poses [keyframe][pose]" } } }, @@ -1404,6 +1415,11 @@ return { type = "Camera3D", name = "Camera", description = "Camera type fallback, defaults to Camera3D" + }, + { + type = "Transform", + name = "*ModelAnimPose", + description = "Anim pose, an array of Transform[]" } }, enums = { @@ -2502,7 +2518,7 @@ return { { name = "SHADER_LOC_MAP_HEIGHT", value = 21, - description = "Shader location: sampler2d texture: height" + description = "Shader location: sampler2d texture: heightmap" }, { name = "SHADER_LOC_MAP_CUBEMAP", @@ -2527,22 +2543,22 @@ return { { name = "SHADER_LOC_VERTEX_BONEIDS", value = 26, - description = "Shader location: vertex attribute: boneIds" + description = "Shader location: vertex attribute: bone indices" }, { name = "SHADER_LOC_VERTEX_BONEWEIGHTS", value = 27, - description = "Shader location: vertex attribute: boneWeights" + description = "Shader location: vertex attribute: bone weights" }, { - name = "SHADER_LOC_BONE_MATRICES", + name = "SHADER_LOC_MATRIX_BONETRANSFORMS", value = 28, - description = "Shader location: array of matrices uniform: boneMatrices" + description = "Shader location: matrix attribute: bone transforms (animation)" }, { - name = "SHADER_LOC_VERTEX_INSTANCE_TX", + name = "SHADER_LOC_VERTEX_INSTANCETRANSFORMS", value = 29, - description = "Shader location: vertex attribute: instanceTransform" + description = "Shader location: vertex attribute: instance transforms" } } }, @@ -7794,51 +7810,25 @@ return { }, { name = "UpdateModelAnimation", - description = "Update model animation pose (CPU)", + description = "Update model animation pose (vertex buffers and bone matrices)", returnType = "void", params = { {type = "Model", name = "model"}, {type = "ModelAnimation", name = "anim"}, - {type = "int", name = "frame"} + {type = "float", name = "frame"} } }, { - name = "UpdateModelAnimationBones", - description = "Update model animation mesh bone matrices (GPU skinning)", - returnType = "void", - params = { - {type = "Model", name = "model"}, - {type = "ModelAnimation", name = "anim"}, - {type = "int", name = "frame"} - } - }, - { - name = "UpdateModelAnimationBonesLerp", - description = "Update model animation mesh bone matrices with interpolation between two poses(GPU skinning)", + name = "UpdateModelAnimationEx", + description = "Update model animation pose, blending two animations", returnType = "void", params = { {type = "Model", name = "model"}, {type = "ModelAnimation", name = "animA"}, - {type = "int", name = "frameA"}, + {type = "float", name = "frameA"}, {type = "ModelAnimation", name = "animB"}, - {type = "int", name = "frameB"}, - {type = "float", name = "value"} - } - }, - { - name = "UpdateModelVertsToCurrentBones", - description = "Update model vertices according to mesh bone matrices (CPU)", - returnType = "void", - params = { - {type = "Model", name = "model"} - } - }, - { - name = "UnloadModelAnimation", - description = "Unload animation data", - returnType = "void", - params = { - {type = "ModelAnimation", name = "anim"} + {type = "float", name = "frameB"}, + {type = "float", name = "blend"} } }, { diff --git a/tools/rlparser/output/raylib_api.txt b/tools/rlparser/output/raylib_api.txt index 9c25843f5..da57f2cfd 100644 --- a/tools/rlparser/output/raylib_api.txt +++ b/tools/rlparser/output/raylib_api.txt @@ -287,7 +287,7 @@ Define 057: GetMouseRay Value: GetScreenToWorldRay Description: Compatibility hack for previous raylib versions -Structures found: 34 +Structures found: 35 Struct 01: Vector2 (2 fields) Name: Vector2 @@ -403,7 +403,7 @@ Struct 14: Camera2D (4 fields) Field[2]: Vector2 target // Camera target (world space target point that is mapped to screen space offset) Field[3]: float rotation // Camera rotation in degrees (pivots around target) Field[4]: float zoom // Camera zoom (scaling around target), must not be set to 0, set to 1.0f for no scale -Struct 15: Mesh (17 fields) +Struct 15: Mesh (16 fields) Name: Mesh Description: Mesh, vertex data and vao/vbo Field[1]: int vertexCount // Number of vertices stored in arrays @@ -415,14 +415,13 @@ Struct 15: Mesh (17 fields) Field[7]: float * tangents // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4) Field[8]: unsigned char * colors // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) Field[9]: unsigned short * indices // Vertex indices (in case vertex data comes indexed) - Field[10]: float * animVertices // Animated vertex positions (after bones transformations) - Field[11]: float * animNormals // Animated normals (after bones transformations) - Field[12]: unsigned char * boneIds // Vertex bone ids, max 255 bone ids, up to 4 bones influence by vertex (skinning) (shader-location = 6) - Field[13]: float * boneWeights // Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7) - Field[14]: Matrix * boneMatrices // Bones animated transformation matrices - Field[15]: int boneCount // Number of bones - Field[16]: unsigned int vaoId // OpenGL Vertex Array Object id - Field[17]: unsigned int * vboId // OpenGL Vertex Buffer Objects id (default vertex data) + Field[10]: int boneCount // Number of bones (MAX: 256 bones) + Field[11]: unsigned char * boneIndices // Vertex bone indices, up to 4 bones influence by vertex (skinning) (shader-location = 6) + Field[12]: float * boneWeights // Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7) + Field[13]: float * animVertices // Animated vertex positions (after bones transformations) + Field[14]: float * animNormals // Animated normals (after bones transformations) + Field[15]: unsigned int vaoId // OpenGL Vertex Array Object id + Field[16]: unsigned int * vboId // OpenGL Vertex Buffer Objects id (default vertex data) Struct 16: Shader (2 fields) Name: Shader Description: Shader @@ -451,7 +450,13 @@ Struct 20: BoneInfo (2 fields) Description: Bone, skeletal animation bone Field[1]: char[32] name // Bone name Field[2]: int parent // Bone parent -Struct 21: Model (9 fields) +Struct 21: ModelSkeleton (3 fields) + Name: ModelSkeleton + Description: Skeleton, animation bones hierarchy + Field[1]: int boneCount // Number of bones + Field[2]: BoneInfo * bones // Bones information (skeleton) + Field[3]: ModelAnimPose bindPose // Bones base transformation (Transform[]) +Struct 22: Model (9 fields) Name: Model Description: Model, meshes, materials and animation data Field[1]: Matrix transform // Local transform matrix @@ -460,35 +465,34 @@ Struct 21: Model (9 fields) Field[4]: Mesh * meshes // Meshes array Field[5]: Material * materials // Materials array Field[6]: int * meshMaterial // Mesh material number - Field[7]: int boneCount // Number of bones - Field[8]: BoneInfo * bones // Bones information (skeleton) - Field[9]: Transform * bindPose // Bones base transformation (pose) -Struct 22: ModelAnimation (5 fields) + Field[7]: ModelSkeleton skeleton // Skeleton for animation + Field[8]: ModelAnimPose currentPose // Current animation pose (Transform[]) + Field[9]: Matrix * boneMatrices // Bones animated transformation matrices +Struct 23: ModelAnimation (4 fields) Name: ModelAnimation - Description: ModelAnimation + Description: ModelAnimation, contains a full animation sequence Field[1]: char[32] name // Animation name - Field[2]: int boneCount // Number of bones - Field[3]: int frameCount // Number of animation frames - Field[4]: BoneInfo * bones // Bones information (skeleton) - Field[5]: Transform ** framePoses // Poses array by frame -Struct 23: Ray (2 fields) + Field[2]: int boneCount // Number of bones (per pose) + Field[3]: int keyframeCount // Number of animation key frames + Field[4]: ModelAnimPose * keyframePoses // Animation sequence keyframe poses [keyframe][pose] +Struct 24: Ray (2 fields) Name: Ray Description: Ray, ray for raycasting Field[1]: Vector3 position // Ray position (origin) Field[2]: Vector3 direction // Ray direction (normalized) -Struct 24: RayCollision (4 fields) +Struct 25: RayCollision (4 fields) Name: RayCollision Description: RayCollision, ray hit information Field[1]: bool hit // Did the ray hit something? Field[2]: float distance // Distance to the nearest hit Field[3]: Vector3 point // Point of the nearest hit Field[4]: Vector3 normal // Surface normal of hit -Struct 25: BoundingBox (2 fields) +Struct 26: BoundingBox (2 fields) Name: BoundingBox Description: BoundingBox Field[1]: Vector3 min // Minimum vertex box-corner Field[2]: Vector3 max // Maximum vertex box-corner -Struct 26: Wave (5 fields) +Struct 27: Wave (5 fields) Name: Wave Description: Wave, audio wave data Field[1]: unsigned int frameCount // Total number of frames (considering channels) @@ -496,7 +500,7 @@ Struct 26: Wave (5 fields) Field[3]: unsigned int sampleSize // Bit depth (bits per sample): 8, 16, 32 (24 not supported) Field[4]: unsigned int channels // Number of channels (1-mono, 2-stereo, ...) Field[5]: void * data // Buffer data pointer -Struct 27: AudioStream (5 fields) +Struct 28: AudioStream (5 fields) Name: AudioStream Description: AudioStream, custom audio stream Field[1]: rAudioBuffer * buffer // Pointer to internal data used by the audio system @@ -504,12 +508,12 @@ Struct 27: AudioStream (5 fields) Field[3]: unsigned int sampleRate // Frequency (samples per second) Field[4]: unsigned int sampleSize // Bit depth (bits per sample): 8, 16, 32 (24 not supported) Field[5]: unsigned int channels // Number of channels (1-mono, 2-stereo, ...) -Struct 28: Sound (2 fields) +Struct 29: Sound (2 fields) Name: Sound Description: Sound Field[1]: AudioStream stream // Audio stream Field[2]: unsigned int frameCount // Total number of frames (considering channels) -Struct 29: Music (5 fields) +Struct 30: Music (5 fields) Name: Music Description: Music, audio stream, anything longer than ~10 seconds should be streamed Field[1]: AudioStream stream // Audio stream @@ -517,7 +521,7 @@ Struct 29: Music (5 fields) Field[3]: bool looping // Music looping enable Field[4]: int ctxType // Type of music context (audio filetype) Field[5]: void * ctxData // Audio context data, depends on type -Struct 30: VrDeviceInfo (9 fields) +Struct 31: VrDeviceInfo (9 fields) Name: VrDeviceInfo Description: VrDeviceInfo, Head-Mounted-Display device parameters Field[1]: int hResolution // Horizontal resolution in pixels @@ -529,7 +533,7 @@ Struct 30: VrDeviceInfo (9 fields) Field[7]: float interpupillaryDistance // IPD (distance between pupils) in meters Field[8]: float[4] lensDistortionValues // Lens distortion constant parameters Field[9]: float[4] chromaAbCorrection // Chromatic aberration correction parameters -Struct 31: VrStereoConfig (8 fields) +Struct 32: VrStereoConfig (8 fields) Name: VrStereoConfig Description: VrStereoConfig, VR stereo rendering configuration for simulator Field[1]: Matrix[2] projection // VR projection matrices (per eye) @@ -540,25 +544,25 @@ Struct 31: VrStereoConfig (8 fields) Field[6]: float[2] rightScreenCenter // VR right screen center Field[7]: float[2] scale // VR distortion scale Field[8]: float[2] scaleIn // VR distortion scale in -Struct 32: FilePathList (2 fields) +Struct 33: FilePathList (2 fields) Name: FilePathList Description: File path list Field[1]: unsigned int count // Filepaths entries count Field[2]: char ** paths // Filepaths entries -Struct 33: AutomationEvent (3 fields) +Struct 34: AutomationEvent (3 fields) Name: AutomationEvent Description: Automation event Field[1]: unsigned int frame // Event frame Field[2]: unsigned int type // Event type (AutomationEventType) Field[3]: int[4] params // Event parameters (if required) -Struct 34: AutomationEventList (3 fields) +Struct 35: AutomationEventList (3 fields) Name: AutomationEventList Description: Automation event list Field[1]: unsigned int capacity // Events max entries (MAX_AUTOMATION_EVENTS) Field[2]: unsigned int count // Events entries count Field[3]: AutomationEvent * events // Events entries -Aliases found: 5 +Aliases found: 6 Alias 001: Quaternion Type: Vector4 @@ -580,6 +584,10 @@ Alias 005: Camera Type: Camera3D Name: Camera Description: Camera type fallback, defaults to Camera3D +Alias 006: *ModelAnimPose + Type: Transform + Name: *ModelAnimPose + Description: Anim pose, an array of Transform[] Enums found: 21 @@ -825,8 +833,8 @@ Enum 09: ShaderLocationIndex (30 values) Value[SHADER_LOC_MAP_BRDF]: 25 Value[SHADER_LOC_VERTEX_BONEIDS]: 26 Value[SHADER_LOC_VERTEX_BONEWEIGHTS]: 27 - Value[SHADER_LOC_BONE_MATRICES]: 28 - Value[SHADER_LOC_VERTEX_INSTANCE_TX]: 29 + Value[SHADER_LOC_MATRIX_BONETRANSFORMS]: 28 + Value[SHADER_LOC_VERTEX_INSTANCETRANSFORMS]: 29 Enum 10: ShaderUniformDataType (13 values) Name: ShaderUniformDataType Description: Shader uniform data type @@ -992,7 +1000,7 @@ Callback 006: AudioCallback() (2 input parameters) Param[1]: bufferData (type: void *) Param[2]: frames (type: unsigned int) -Functions found: 601 +Functions found: 598 Function 001: InitWindow() (3 input parameters) Name: InitWindow @@ -4343,50 +4351,33 @@ Function 520: LoadModelAnimations() (2 input parameters) Function 521: UpdateModelAnimation() (3 input parameters) Name: UpdateModelAnimation Return type: void - Description: Update model animation pose (CPU) + Description: Update model animation pose (vertex buffers and bone matrices) Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) - Param[3]: frame (type: int) -Function 522: UpdateModelAnimationBones() (3 input parameters) - Name: UpdateModelAnimationBones + Param[3]: frame (type: float) +Function 522: UpdateModelAnimationEx() (6 input parameters) + Name: UpdateModelAnimationEx 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 523: UpdateModelAnimationBonesLerp() (6 input parameters) - Name: UpdateModelAnimationBonesLerp - Return type: void - Description: Update model animation mesh bone matrices with interpolation between two poses(GPU skinning) + Description: Update model animation pose, blending two animations Param[1]: model (type: Model) Param[2]: animA (type: ModelAnimation) - Param[3]: frameA (type: int) + Param[3]: frameA (type: float) Param[4]: animB (type: ModelAnimation) - Param[5]: frameB (type: int) - Param[6]: value (type: float) -Function 524: UpdateModelVertsToCurrentBones() (1 input parameters) - Name: UpdateModelVertsToCurrentBones - Return type: void - Description: Update model vertices according to mesh bone matrices (CPU) - Param[1]: model (type: Model) -Function 525: UnloadModelAnimation() (1 input parameters) - Name: UnloadModelAnimation - Return type: void - Description: Unload animation data - Param[1]: anim (type: ModelAnimation) -Function 526: UnloadModelAnimations() (2 input parameters) + Param[5]: frameB (type: float) + Param[6]: blend (type: float) +Function 523: 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 527: IsModelAnimationValid() (2 input parameters) +Function 524: 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 528: CheckCollisionSpheres() (4 input parameters) +Function 525: CheckCollisionSpheres() (4 input parameters) Name: CheckCollisionSpheres Return type: bool Description: Check collision between two spheres @@ -4394,40 +4385,40 @@ Function 528: CheckCollisionSpheres() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector3) Param[4]: radius2 (type: float) -Function 529: CheckCollisionBoxes() (2 input parameters) +Function 526: 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 530: CheckCollisionBoxSphere() (3 input parameters) +Function 527: 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 531: GetRayCollisionSphere() (3 input parameters) +Function 528: 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 532: GetRayCollisionBox() (2 input parameters) +Function 529: 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 533: GetRayCollisionMesh() (3 input parameters) +Function 530: 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 534: GetRayCollisionTriangle() (4 input parameters) +Function 531: GetRayCollisionTriangle() (4 input parameters) Name: GetRayCollisionTriangle Return type: RayCollision Description: Get collision info between ray and triangle @@ -4435,7 +4426,7 @@ Function 534: GetRayCollisionTriangle() (4 input parameters) Param[2]: p1 (type: Vector3) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) -Function 535: GetRayCollisionQuad() (5 input parameters) +Function 532: GetRayCollisionQuad() (5 input parameters) Name: GetRayCollisionQuad Return type: RayCollision Description: Get collision info between ray and quad @@ -4444,158 +4435,158 @@ Function 535: GetRayCollisionQuad() (5 input parameters) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) Param[5]: p4 (type: Vector3) -Function 536: InitAudioDevice() (0 input parameters) +Function 533: InitAudioDevice() (0 input parameters) Name: InitAudioDevice Return type: void Description: Initialize audio device and context No input parameters -Function 537: CloseAudioDevice() (0 input parameters) +Function 534: CloseAudioDevice() (0 input parameters) Name: CloseAudioDevice Return type: void Description: Close the audio device and context No input parameters -Function 538: IsAudioDeviceReady() (0 input parameters) +Function 535: IsAudioDeviceReady() (0 input parameters) Name: IsAudioDeviceReady Return type: bool Description: Check if audio device has been initialized successfully No input parameters -Function 539: SetMasterVolume() (1 input parameters) +Function 536: SetMasterVolume() (1 input parameters) Name: SetMasterVolume Return type: void Description: Set master volume (listener) Param[1]: volume (type: float) -Function 540: GetMasterVolume() (0 input parameters) +Function 537: GetMasterVolume() (0 input parameters) Name: GetMasterVolume Return type: float Description: Get master volume (listener) No input parameters -Function 541: LoadWave() (1 input parameters) +Function 538: LoadWave() (1 input parameters) Name: LoadWave Return type: Wave Description: Load wave data from file Param[1]: fileName (type: const char *) -Function 542: LoadWaveFromMemory() (3 input parameters) +Function 539: 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 543: IsWaveValid() (1 input parameters) +Function 540: 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 544: LoadSound() (1 input parameters) +Function 541: LoadSound() (1 input parameters) Name: LoadSound Return type: Sound Description: Load sound from file Param[1]: fileName (type: const char *) -Function 545: LoadSoundFromWave() (1 input parameters) +Function 542: LoadSoundFromWave() (1 input parameters) Name: LoadSoundFromWave Return type: Sound Description: Load sound from wave data Param[1]: wave (type: Wave) -Function 546: LoadSoundAlias() (1 input parameters) +Function 543: 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 547: IsSoundValid() (1 input parameters) +Function 544: 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 548: UpdateSound() (3 input parameters) +Function 545: UpdateSound() (3 input parameters) Name: UpdateSound Return type: void Description: Update sound buffer with new data (default data format: 32 bit float, stereo) Param[1]: sound (type: Sound) Param[2]: data (type: const void *) Param[3]: sampleCount (type: int) -Function 549: UnloadWave() (1 input parameters) +Function 546: UnloadWave() (1 input parameters) Name: UnloadWave Return type: void Description: Unload wave data Param[1]: wave (type: Wave) -Function 550: UnloadSound() (1 input parameters) +Function 547: UnloadSound() (1 input parameters) Name: UnloadSound Return type: void Description: Unload sound Param[1]: sound (type: Sound) -Function 551: UnloadSoundAlias() (1 input parameters) +Function 548: 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 552: ExportWave() (2 input parameters) +Function 549: 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 553: ExportWaveAsCode() (2 input parameters) +Function 550: 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 554: PlaySound() (1 input parameters) +Function 551: PlaySound() (1 input parameters) Name: PlaySound Return type: void Description: Play a sound Param[1]: sound (type: Sound) -Function 555: StopSound() (1 input parameters) +Function 552: StopSound() (1 input parameters) Name: StopSound Return type: void Description: Stop playing a sound Param[1]: sound (type: Sound) -Function 556: PauseSound() (1 input parameters) +Function 553: PauseSound() (1 input parameters) Name: PauseSound Return type: void Description: Pause a sound Param[1]: sound (type: Sound) -Function 557: ResumeSound() (1 input parameters) +Function 554: ResumeSound() (1 input parameters) Name: ResumeSound Return type: void Description: Resume a paused sound Param[1]: sound (type: Sound) -Function 558: IsSoundPlaying() (1 input parameters) +Function 555: IsSoundPlaying() (1 input parameters) Name: IsSoundPlaying Return type: bool Description: Check if a sound is currently playing Param[1]: sound (type: Sound) -Function 559: SetSoundVolume() (2 input parameters) +Function 556: 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 560: SetSoundPitch() (2 input parameters) +Function 557: 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 561: SetSoundPan() (2 input parameters) +Function 558: SetSoundPan() (2 input parameters) Name: SetSoundPan Return type: void Description: Set pan for a sound (-1.0 left, 0.0 center, 1.0 right) Param[1]: sound (type: Sound) Param[2]: pan (type: float) -Function 562: WaveCopy() (1 input parameters) +Function 559: WaveCopy() (1 input parameters) Name: WaveCopy Return type: Wave Description: Copy a wave to a new wave Param[1]: wave (type: Wave) -Function 563: WaveCrop() (3 input parameters) +Function 560: 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 564: WaveFormat() (4 input parameters) +Function 561: WaveFormat() (4 input parameters) Name: WaveFormat Return type: void Description: Convert wave data to desired format @@ -4603,203 +4594,203 @@ Function 564: WaveFormat() (4 input parameters) Param[2]: sampleRate (type: int) Param[3]: sampleSize (type: int) Param[4]: channels (type: int) -Function 565: LoadWaveSamples() (1 input parameters) +Function 562: 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 566: UnloadWaveSamples() (1 input parameters) +Function 563: UnloadWaveSamples() (1 input parameters) Name: UnloadWaveSamples Return type: void Description: Unload samples data loaded with LoadWaveSamples() Param[1]: samples (type: float *) -Function 567: LoadMusicStream() (1 input parameters) +Function 564: LoadMusicStream() (1 input parameters) Name: LoadMusicStream Return type: Music Description: Load music stream from file Param[1]: fileName (type: const char *) -Function 568: LoadMusicStreamFromMemory() (3 input parameters) +Function 565: 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 569: IsMusicValid() (1 input parameters) +Function 566: 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 570: UnloadMusicStream() (1 input parameters) +Function 567: UnloadMusicStream() (1 input parameters) Name: UnloadMusicStream Return type: void Description: Unload music stream Param[1]: music (type: Music) -Function 571: PlayMusicStream() (1 input parameters) +Function 568: PlayMusicStream() (1 input parameters) Name: PlayMusicStream Return type: void Description: Start music playing Param[1]: music (type: Music) -Function 572: IsMusicStreamPlaying() (1 input parameters) +Function 569: IsMusicStreamPlaying() (1 input parameters) Name: IsMusicStreamPlaying Return type: bool Description: Check if music is playing Param[1]: music (type: Music) -Function 573: UpdateMusicStream() (1 input parameters) +Function 570: UpdateMusicStream() (1 input parameters) Name: UpdateMusicStream Return type: void Description: Updates buffers for music streaming Param[1]: music (type: Music) -Function 574: StopMusicStream() (1 input parameters) +Function 571: StopMusicStream() (1 input parameters) Name: StopMusicStream Return type: void Description: Stop music playing Param[1]: music (type: Music) -Function 575: PauseMusicStream() (1 input parameters) +Function 572: PauseMusicStream() (1 input parameters) Name: PauseMusicStream Return type: void Description: Pause music playing Param[1]: music (type: Music) -Function 576: ResumeMusicStream() (1 input parameters) +Function 573: ResumeMusicStream() (1 input parameters) Name: ResumeMusicStream Return type: void Description: Resume playing paused music Param[1]: music (type: Music) -Function 577: SeekMusicStream() (2 input parameters) +Function 574: 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 578: SetMusicVolume() (2 input parameters) +Function 575: 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 579: SetMusicPitch() (2 input parameters) +Function 576: 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 580: SetMusicPan() (2 input parameters) +Function 577: SetMusicPan() (2 input parameters) Name: SetMusicPan Return type: void Description: Set pan for a music (-1.0 left, 0.0 center, 1.0 right) Param[1]: music (type: Music) Param[2]: pan (type: float) -Function 581: GetMusicTimeLength() (1 input parameters) +Function 578: GetMusicTimeLength() (1 input parameters) Name: GetMusicTimeLength Return type: float Description: Get music time length (in seconds) Param[1]: music (type: Music) -Function 582: GetMusicTimePlayed() (1 input parameters) +Function 579: GetMusicTimePlayed() (1 input parameters) Name: GetMusicTimePlayed Return type: float Description: Get current music time played (in seconds) Param[1]: music (type: Music) -Function 583: LoadAudioStream() (3 input parameters) +Function 580: 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 584: IsAudioStreamValid() (1 input parameters) +Function 581: 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 585: UnloadAudioStream() (1 input parameters) +Function 582: UnloadAudioStream() (1 input parameters) Name: UnloadAudioStream Return type: void Description: Unload audio stream and free memory Param[1]: stream (type: AudioStream) -Function 586: UpdateAudioStream() (3 input parameters) +Function 583: 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 587: IsAudioStreamProcessed() (1 input parameters) +Function 584: IsAudioStreamProcessed() (1 input parameters) Name: IsAudioStreamProcessed Return type: bool Description: Check if any audio stream buffers requires refill Param[1]: stream (type: AudioStream) -Function 588: PlayAudioStream() (1 input parameters) +Function 585: PlayAudioStream() (1 input parameters) Name: PlayAudioStream Return type: void Description: Play audio stream Param[1]: stream (type: AudioStream) -Function 589: PauseAudioStream() (1 input parameters) +Function 586: PauseAudioStream() (1 input parameters) Name: PauseAudioStream Return type: void Description: Pause audio stream Param[1]: stream (type: AudioStream) -Function 590: ResumeAudioStream() (1 input parameters) +Function 587: ResumeAudioStream() (1 input parameters) Name: ResumeAudioStream Return type: void Description: Resume audio stream Param[1]: stream (type: AudioStream) -Function 591: IsAudioStreamPlaying() (1 input parameters) +Function 588: IsAudioStreamPlaying() (1 input parameters) Name: IsAudioStreamPlaying Return type: bool Description: Check if audio stream is playing Param[1]: stream (type: AudioStream) -Function 592: StopAudioStream() (1 input parameters) +Function 589: StopAudioStream() (1 input parameters) Name: StopAudioStream Return type: void Description: Stop audio stream Param[1]: stream (type: AudioStream) -Function 593: SetAudioStreamVolume() (2 input parameters) +Function 590: 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 594: SetAudioStreamPitch() (2 input parameters) +Function 591: 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 595: SetAudioStreamPan() (2 input parameters) +Function 592: 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 596: SetAudioStreamBufferSizeDefault() (1 input parameters) +Function 593: SetAudioStreamBufferSizeDefault() (1 input parameters) Name: SetAudioStreamBufferSizeDefault Return type: void Description: Default size for new audio streams Param[1]: size (type: int) -Function 597: SetAudioStreamCallback() (2 input parameters) +Function 594: 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 598: AttachAudioStreamProcessor() (2 input parameters) +Function 595: 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 599: DetachAudioStreamProcessor() (2 input parameters) +Function 596: 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 600: AttachAudioMixedProcessor() (1 input parameters) +Function 597: 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 601: DetachAudioMixedProcessor() (1 input parameters) +Function 598: DetachAudioMixedProcessor() (1 input parameters) Name: DetachAudioMixedProcessor Return type: void Description: Detach audio stream processor from the entire audio pipeline diff --git a/tools/rlparser/output/raylib_api.xml b/tools/rlparser/output/raylib_api.xml index d3314a5d1..6c83dc5df 100644 --- a/tools/rlparser/output/raylib_api.xml +++ b/tools/rlparser/output/raylib_api.xml @@ -59,7 +59,7 @@ - + @@ -160,7 +160,7 @@ - + @@ -170,12 +170,11 @@ + + + - - - - @@ -202,6 +201,11 @@ + + + + + @@ -209,16 +213,15 @@ - - - + + + - + - - - - + + + @@ -295,12 +298,13 @@ - + + @@ -528,15 +532,15 @@ - + - - - - + + + + @@ -678,7 +682,7 @@ - + @@ -2908,29 +2912,18 @@ - + - + - - - - - - + - + - - - - - - - - + + From ade81248c37dcff0b4453d6aa015c5e0737dd578 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 24 Feb 2026 01:26:07 +0100 Subject: [PATCH 073/319] REVIEWED: Right timing --- examples/Makefile | 2 +- examples/Makefile.Web | 4 ++-- examples/models/models_animation_blending.c | 4 ++-- src/platforms/rcore_desktop_glfw.c | 2 +- src/platforms/rcore_desktop_win32.c | 2 +- src/platforms/rcore_drm.c | 2 +- src/platforms/rcore_memory.c | 2 +- src/rcore.c | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 47e1fb47e..ff989a9dd 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -667,7 +667,7 @@ MODELS = \ models/models_animation_blend_custom \ models/models_animation_blending \ models/models_animation_gpu_skinning \ - models/models_animation_timming \ + models/models_animation_timing \ models/models_basic_voxel \ models/models_billboard_rendering \ models/models_bone_socket \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index b355bc63e..1fa797bf1 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -645,7 +645,7 @@ MODELS = \ models/models_animation_blend_custom \ models/models_animation_blending \ models/models_animation_gpu_skinning \ - models/models_animation_timming \ + models/models_animation_timing \ models/models_basic_voxel \ models/models_billboard_rendering \ models/models_bone_socket \ @@ -1218,7 +1218,7 @@ models/models_animation_gpu_skinning: models/models_animation_gpu_skinning.c --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_animation_timming: models/models_animation_timming.c +models/models_animation_timing: models/models_animation_timing.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file models/resources/models/gltf/robot.glb@resources/models/gltf/robot.glb diff --git a/examples/models/models_animation_blending.c b/examples/models/models_animation_blending.c index a5d3d4b1d..46577a353 100644 --- a/examples/models/models_animation_blending.c +++ b/examples/models/models_animation_blending.c @@ -199,8 +199,8 @@ int main(void) } // Update progress bars values with current frame for each animation - float animFrameProgress0 = animCurrentFrame0; - float animFrameProgress1 = animCurrentFrame1; + animFrameProgress0 = animCurrentFrame0; + animFrameProgress1 = animCurrentFrame1; //---------------------------------------------------------------------------------- // Draw diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index af3e6b6c1..b79d5d629 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1782,7 +1782,7 @@ int InitPlatform(void) } //---------------------------------------------------------------------------- - // Initialize timming system + // Initialize timing system //---------------------------------------------------------------------------- InitTimer(); //---------------------------------------------------------------------------- diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index 8cb9645d2..48b644a72 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -1655,7 +1655,7 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > GLSL: %s", "NOT SUPPORTED"); } - // Initialize timming system + // Initialize timing system //---------------------------------------------------------------------------- LARGE_INTEGER time = { 0 }; QueryPerformanceCounter(&time); diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 85db9b4e1..619a42256 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -1613,7 +1613,7 @@ int InitPlatform(void) //---------------------------------------------------------------------------- // Initialize timing system //---------------------------------------------------------------------------- - // NOTE: timming system must be initialized before the input events system + // NOTE: timing system must be initialized before the input events system InitTimer(); //---------------------------------------------------------------------------- diff --git a/src/platforms/rcore_memory.c b/src/platforms/rcore_memory.c index 1d69f5ed3..a6fa46ba3 100644 --- a/src/platforms/rcore_memory.c +++ b/src/platforms/rcore_memory.c @@ -55,7 +55,7 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -// Platform-specific required data for timming (Win32) +// Platform-specific required data for timing (Win32) #if defined(_WIN32) typedef struct _LARGE_INTEGER { int64_t QuadPart; } LARGE_INTEGER; __declspec(dllimport) int __stdcall QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount); diff --git a/src/rcore.c b/src/rcore.c index f1481890b..1090cfc9c 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1593,7 +1593,7 @@ Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera) } //---------------------------------------------------------------------------------- -// Module Functions Definition: Timming +// Module Functions Definition: Timing //---------------------------------------------------------------------------------- // NOTE: Functions with a platform-specific implementation on rcore_.c From 7f0cedba63ca5f2d30c40d20fb98849670cb7e1e Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 24 Feb 2026 01:31:40 +0100 Subject: [PATCH 074/319] Update shaders_shadowmap_rendering.c --- examples/shaders/shaders_shadowmap_rendering.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/shaders/shaders_shadowmap_rendering.c b/examples/shaders/shaders_shadowmap_rendering.c index a739cdd93..9bf84eae7 100644 --- a/examples/shaders/shaders_shadowmap_rendering.c +++ b/examples/shaders/shaders_shadowmap_rendering.c @@ -79,8 +79,9 @@ int main(void) cube.materials[0].shader = shadowShader; Model robot = LoadModel("resources/models/robot.glb"); for (int i = 0; i < robot.materialCount; i++) robot.materials[i].shader = shadowShader; + int animCount = 0; - ModelAnimation *robotAnimations = LoadModelAnimations("resources/models/robot.glb", &animCount); + ModelAnimation *anims = LoadModelAnimations("resources/models/robot.glb", &animCount); RenderTexture2D shadowMap = LoadShadowmapRenderTexture(SHADOWMAP_RESOLUTION, SHADOWMAP_RESOLUTION); @@ -115,8 +116,8 @@ int main(void) UpdateCamera(&camera, CAMERA_ORBITAL); frameCounter++; - frameCounter %= (robotAnimations[0].frameCount); - UpdateModelAnimation(robot, robotAnimations[0], frameCounter); + frameCounter %= (anims[0].keyframeCount); + UpdateModelAnimation(robot, anims[0], frameCounter); // Move light with arrow keys const float cameraSpeed = 0.05f; @@ -190,7 +191,7 @@ int main(void) UnloadShader(shadowShader); UnloadModel(cube); UnloadModel(robot); - UnloadModelAnimations(robotAnimations, animCount); + UnloadModelAnimations(anims, animCount); UnloadShadowmapRenderTexture(shadowMap); CloseWindow(); // Close window and OpenGL context From ace4d77bfae692d923f28e7cc9027ae5dcc99a51 Mon Sep 17 00:00:00 2001 From: BadRAM Date: Tue, 24 Feb 2026 02:26:30 -0800 Subject: [PATCH 075/319] New example: textures_magnifying_glass (#5587) --- examples/textures/textures_magnifying_glass.c | 134 ++++++++++++++++++ .../textures/textures_magnifying_glass.png | Bin 0 -> 310987 bytes 2 files changed, 134 insertions(+) create mode 100644 examples/textures/textures_magnifying_glass.c create mode 100644 examples/textures/textures_magnifying_glass.png diff --git a/examples/textures/textures_magnifying_glass.c b/examples/textures/textures_magnifying_glass.c new file mode 100644 index 000000000..12cd0cdee --- /dev/null +++ b/examples/textures/textures_magnifying_glass.c @@ -0,0 +1,134 @@ +/******************************************************************************************* +* +* raylib textures example - magnifying glass +* +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 5.6, last time updated with raylib 5.6 +* +* Example contributed by Luke Vaughan (@badram) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2026 Luke Vaughan (@badram) +* +********************************************************************************************/ + +#include "raylib.h" +#include "rlgl.h" // for rlSetBlendFactorsSeparate() + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [textures] example - magnifying glass"); + + Texture2D bunny = LoadTexture("resources/raybunny.png"); + Texture2D parrots = LoadTexture("resources/parrots.png"); + + // Use image draw to generate a mask texture instead of loading it from a file. + Image circle = GenImageColor(256, 256, BLANK); + ImageDrawCircle(&circle, 128, 128, 128, WHITE); + Texture2D mask = LoadTextureFromImage(circle); // Copy the mask image from RAM to VRAM + UnloadImage(circle); // Unload the image from RAM + + RenderTexture2D magnifiedWorld = LoadRenderTexture(256, 256); + + Camera2D camera = { 0 }; + // Set magnifying glass zoom + camera.zoom = 2; + // Offset by half the size of the magnifying glass to counteract drawing the texture centered on the mouse position + camera.offset = (Vector2){128, 128}; + + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + Vector2 mPos = GetMousePosition(); + camera.target = mPos; + + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + // Draw the normal version of the world + DrawTexture(parrots, 144, 33, WHITE); + DrawText("Use the magnifying glass to find hidden bunnies!", 154, 6, 20, BLACK); + + // Render to a the magnifying glass + BeginTextureMode(magnifiedWorld); + ClearBackground(RAYWHITE); + + BeginMode2D(camera); + // Draw the same things in the magnified world as were in the normal version + DrawTexture(parrots, 144, 33, WHITE); + DrawText("Use the magnifying glass to find hidden bunnies!", 154, 6, 20, BLACK); + + // Draw bunnies only in the magnified world. + // BLEND_MULTIPLIED lets them take on the color of the image below them. + BeginBlendMode(BLEND_MULTIPLIED); + DrawTexture(bunny, 250, 350, WHITE); + DrawTexture(bunny, 500, 100, WHITE); + DrawTexture(bunny, 420, 300, WHITE); + DrawTexture(bunny, 650, 10, WHITE); + EndBlendMode(); + EndMode2D(); + + // Mask the magnifying glass view texture to a circle + // To make the mask affect only alpha, a CUSTOM blend mode is used with SEPARATE color/alpha functions + BeginBlendMode(BLEND_CUSTOM_SEPARATE); + // C: Color, A: Alpha, s: source (texture to draw), d: destination (texture drawn to) + // glSrcRGB: RL_ZERO - Cs * 0 = 0 - discard source rgb because we don't want to draw our texture's colors at all + // glDstRGB: RL_ONE - Cd * 1 = Cd - use destination colors unmodified + // glSrcAlpha: RL_ONE - As * 1 = As - use source alpha unmodified + // glDstAlpha: RL_ZERO - Ad * 0 = 0 - discard destination alpha + // glEqRGB: RL_FUNC_ADD - Cs(0) + Cd = Cd - destination color is unmodified + // glEqAlpha: RL_FUNC_ADD - As + Ad(0) = As - destination alpha is set to source alpha + rlSetBlendFactorsSeparate(RL_ZERO, RL_ONE, RL_ONE, RL_ZERO, RL_FUNC_ADD, RL_FUNC_ADD); + DrawTexture(mask, 0, 0, WHITE); + EndBlendMode(); + EndTextureMode(); + + // Draw magnifiedWorld to screen, centered on cursor + DrawTextureRec(magnifiedWorld.texture, (Rectangle){0, 0, 256, -256}, (Vector2){mPos.x - 128, mPos.y - 128}, WHITE); + + // Draw the outer ring of the magnifying glass + DrawRing(mPos, 126, 130, 0, 360, 64, BLACK); + + // Draw floating specular highlight on the glass + float rx = mPos.x/800; + float ry = mPos.y/800; + DrawCircle((int)(mPos.x - 64*rx) - 32, (int)(mPos.y - 64*ry) - 32, 4, ColorAlpha(WHITE, 0.5)); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadTexture(parrots); + UnloadTexture(bunny); + UnloadTexture(mask); + UnloadRenderTexture(magnifiedWorld); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/textures/textures_magnifying_glass.png b/examples/textures/textures_magnifying_glass.png new file mode 100644 index 0000000000000000000000000000000000000000..bc8b02aa303ab7f4787a7b87310a0b328adcd411 GIT binary patch literal 310987 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°_d!EZy0yh7HlP|P0Zy9zD6IZ>ECNPlX3<| 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%0AX@vB&^9<{LFGr6_Or1EwGFDUH6* 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 Y#q4?BsD1BWZZBJCUo`5(@;{Sm_VqJ1 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

~XJg1JOopX9!i7dt?&khg}I$s^qJur|fS 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 BM;!nF literal 0 HcmV?d00001 From 2c5e7f8db6bf276e9a60e93a34689d2fef84671c Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 2 Mar 2026 12:42:55 +0100 Subject: [PATCH 103/319] REVIEWED: example: `textures_clipboard_image` --- examples/textures/textures_clipboard_image.c | 57 ++++++++------------ 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/examples/textures/textures_clipboard_image.c b/examples/textures/textures_clipboard_image.c index 5cf87c1c2..aa955da2e 100644 --- a/examples/textures/textures_clipboard_image.c +++ b/examples/textures/textures_clipboard_image.c @@ -4,26 +4,25 @@ * * Example complexity rating: [★☆☆☆] 1/4 * -* Example originally created with raylib 5.5, last time updated with raylib 5.6 +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by Maicon Santana (@maiconpintoabreu) and reviewed by Ramon Santamaria (@raysan5) * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software * -* Copyright (c) 2026-2026 Maicon Santana (@maiconpintoabreu) +* Copyright (c) 2026 Maicon Santana (@maiconpintoabreu) * ********************************************************************************************/ -#define RCORE_PLATFORM_RGFW #include "raylib.h" -#define MAX_IAMGE_COLLECTION_AMOUNT 1000 +#define MAX_TEXTURE_COLLECTION 20 -typedef struct ImageCollection { +typedef struct TextureCollection { Texture2D texture; Vector2 position; -} ImageCollection; +} TextureCollection; //------------------------------------------------------------------------------------ // Program main entry point @@ -37,7 +36,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [textures] example - clipboard_image"); - ImageCollection collection[MAX_IAMGE_COLLECTION_AMOUNT] = {0}; + TextureCollection collection[MAX_TEXTURE_COLLECTION] = { 0 }; int currentCollectionIndex = 0; SetTargetFPS(60); @@ -48,26 +47,19 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - // TODO: Update variables / Implement example logic at this point - //---------------------------------------------------------------------------------- - - if(IsKeyPressed(KEY_R)) + if (IsKeyPressed(KEY_R)) // Reset image collection { + // Unload textures to avoid memory leaks + for (int i = 0; i < MAX_TEXTURE_COLLECTION; i++) UnloadTexture(collection[i].texture); + currentCollectionIndex = 0; - - // Unload Texture to avoid Memory leak - for (int i=0;i Date: Mon, 2 Mar 2026 13:16:43 +0100 Subject: [PATCH 104/319] REVIEWED: Fullscreen request #5601 Tested on Windows with Edge and Chrome browsers, the other options do not work: - `Module.canvas.requestFullscreen(false, false)` - FAIL - `Module.requestFullscreen(false, false)` - FAIL - `Module.requestFullscreen()` - FAIL Tested with latest Emscripten/emsdk 5.0.2 --- src/shell.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shell.html b/src/shell.html index e5e26a3b1..f1bf066d8 100644 --- a/src/shell.html +++ b/src/shell.html @@ -179,7 +179,7 @@ jwE50AGjLCVuS8Yt4H7OgZLKK5EKOsLviEWJSL/+0uMi7gLUSBseYwqEbXvSHCec1CJvZPyHCmYQffaB - + From 3bea7f518d73e61fe1d9227263b5fd85f0f37da1 Mon Sep 17 00:00:00 2001 From: Lam Wei Lun Date: Mon, 2 Mar 2026 22:46:15 +0800 Subject: [PATCH 105/319] Added MatrixUnit and MatrixMultiplyValue (#5613) --- src/raymath.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/raymath.h b/src/raymath.h index ab1be1409..b0b8dd199 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -1765,6 +1765,18 @@ RMAPI Matrix MatrixMultiply(Matrix left, Matrix right) return result; } +RMAPI Matrix MatrixMultiplyValue(Matrix left, float value) +{ + Matrix result = { + left.m0 * value, left.m4 * value, left.m8 * value, left.m12 * value, + left.m1 * value, left.m5 * value, left.m9 * value, left.m13 * value, + left.m2 * value, left.m6 * value, left.m10 * value, left.m14 * value, + left.m3 * value, left.m7 * value, left.m11 * value, left.m15 * value + }; + + return result; +} + // Get translation matrix RMAPI Matrix MatrixTranslate(float x, float y, float z) { @@ -3079,6 +3091,11 @@ inline const Quaternion& operator *= (Quaternion& lhs, const Matrix& rhs) } // Matrix operators +static constexpr Matrix MatrixUnit = { 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 }; + inline Matrix operator + (const Matrix& lhs, const Matrix& rhs) { return MatrixAdd(lhs, rhs); @@ -3111,6 +3128,18 @@ inline const Matrix& operator *= (Matrix& lhs, const Matrix& rhs) lhs = MatrixMultiply(lhs, rhs); return lhs; } + +inline Matrix operator * (const Matrix& lhs, const float value) +{ + return MatrixMultiplyValue(lhs, value); +} + +inline const Matrix& operator *= (Matrix& lhs, const float value) +{ + lhs = MatrixMultiplyValue(lhs, value); + return lhs; +} + //------------------------------------------------------------------------------- #endif // C++ operators From ea926779022694af2e759d386549bcc47a79272e Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 2 Mar 2026 15:56:59 +0100 Subject: [PATCH 106/319] REVIEWED: `TRACELOG()` macro logic --- src/config.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/config.h b/src/config.h index 7bedd4f4c..26e8c6fef 100644 --- a/src/config.h +++ b/src/config.h @@ -124,10 +124,11 @@ #define SUPPORT_CLIPBOARD_IMAGE 1 #endif -#if SUPPORT_TRACELOG - #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) -#else +#if !defined(SUPPORT_TRACELOG) || !SUPPORT_TRACELOG + // Not defined or disabled #define TRACELOG(level, ...) (void)0 +#else + #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) #endif // rcore: Configuration values From 416da9aca6c5800d46dcac3b40b6b8b132b9cea9 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 2 Mar 2026 16:01:00 +0100 Subject: [PATCH 107/319] Revert "REVIEWED: `TRACELOG()` macro logic" This reverts commit ea926779022694af2e759d386549bcc47a79272e. --- src/config.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/config.h b/src/config.h index 26e8c6fef..7bedd4f4c 100644 --- a/src/config.h +++ b/src/config.h @@ -124,11 +124,10 @@ #define SUPPORT_CLIPBOARD_IMAGE 1 #endif -#if !defined(SUPPORT_TRACELOG) || !SUPPORT_TRACELOG - // Not defined or disabled - #define TRACELOG(level, ...) (void)0 -#else +#if SUPPORT_TRACELOG #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) +#else + #define TRACELOG(level, ...) (void)0 #endif // rcore: Configuration values From 23c8ee855de828a39145b37d36333f398e3e7fdd Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 2 Mar 2026 16:05:00 +0100 Subject: [PATCH 108/319] Update config.h --- src/config.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/config.h b/src/config.h index 7bedd4f4c..ae7002953 100644 --- a/src/config.h +++ b/src/config.h @@ -124,12 +124,6 @@ #define SUPPORT_CLIPBOARD_IMAGE 1 #endif -#if SUPPORT_TRACELOG - #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) -#else - #define TRACELOG(level, ...) (void)0 -#endif - // rcore: Configuration values // NOTE: Below values are alread defined inside [rcore.c] so there is no need to be // redefined here, in case it must be done, just uncomment the required line and update @@ -365,7 +359,12 @@ //#define AUDIO_DEVICE_PERIOD_SIZE_IN_FRAMES 0 // Device period size (controls latency, 0 defaults to 10ms) //#define MAX_AUDIO_BUFFER_POOL_CHANNELS 16 // Maximum number of audio pool channels //------------------------------------------------------------------------------------ - #endif // !EXTERNAL_CONFIG_FLAGS +#if SUPPORT_TRACELOG + #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) +#else + #define TRACELOG(level, ...) (void)0 +#endif + #endif // CONFIG_H From bf830c3f7be1c3a450d96b68418f523fa0801c78 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 2 Mar 2026 16:07:56 +0100 Subject: [PATCH 109/319] Update config.h --- src/config.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/config.h b/src/config.h index ae7002953..9e0b6d8ea 100644 --- a/src/config.h +++ b/src/config.h @@ -361,6 +361,8 @@ //------------------------------------------------------------------------------------ #endif // !EXTERNAL_CONFIG_FLAGS +// NOTE: Following macro depends on config flag that can +// be externally defined, so, it needs to be outside EXTERNAL_CONFIG_FLAGS #if SUPPORT_TRACELOG #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) #else From 8e705b19e47645092a15b85b4bd7793aa097f3d6 Mon Sep 17 00:00:00 2001 From: Dmitry Mozgin Date: Tue, 3 Mar 2026 11:11:41 +0300 Subject: [PATCH 110/319] Update LibraryConfigurations.cmake (#5617) Typo in find_dependency --- cmake/LibraryConfigurations.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index 5ccd74485..2fe05827a 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -143,7 +143,7 @@ elseif ("${PLATFORM}" MATCHES "SDL") message(STATUS "Found SDL2 via find_package()") set(PLATFORM_CPP "PLATFORM_DESKTOP_SDL") set(LIBS_PUBLIC SDL2::SDL2) - set(RAYLIB_DEPENDENCIES "${RAYLIB_DEPENDENCIES}\nfind_dependency(SDL3 REQUIRED)") + set(RAYLIB_DEPENDENCIES "${RAYLIB_DEPENDENCIES}\nfind_dependency(SDL2 REQUIRED)") add_compile_definitions(USING_SDL2_PACKAGE) endif() endif() From 9ae34d2c4bdaa8735b4527fde26d3bec78a80499 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 3 Mar 2026 11:13:33 +0100 Subject: [PATCH 111/319] Update ROADMAP.md --- ROADMAP.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index a49cdbfd7..b9392fa66 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -16,10 +16,9 @@ _Current version of raylib is complete and functional but there is always room f **raylib 5.x** - [ ] `rcore`: Support additional platforms: iOS, consoles? - [x] `rcore_web`: Avoid GLFW dependency, functionality can be directly implemented using emscripten SDK - - [ ] `rlgl`: Review GLSL shaders naming conventions for consistency + - [x] `rlgl`: Review GLSL shaders naming conventions for consistency - [ ] `textures`: Improve compressed textures support, loading and saving - - [ ] `rmodels`: Improve 3d objects loading, specially animations (obj, gltf) - - [ ] `raudio`: Implement miniaudio high-level provided features + - [x] `rmodels`: Improve 3d objects loading, specially animations (obj, gltf) - [x] `examples`: Review all examples, add more and better code explanations - [x] Software renderer backend? Maybe using `Image` provided API From b68dbaa8af5c5d7b783f09c3b54daafdd77231b9 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Tue, 3 Mar 2026 11:55:50 -0600 Subject: [PATCH 112/319] [tools/rexm] Update nextCatIndex (#5616) * removed +1 offset * update from PR feedback --- 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 30ef6411f..011102ce1 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -1040,7 +1040,7 @@ int main(int argc, char *argv[]) // Find position to add new example on list, just before the following category // Category order: core, shapes, textures, text, models, shaders, audio int exListNextCatIndex = -1; - if (nextCatIndex != -1) exListNextCatIndex = TextFindIndex(exList, exCategories[nextCatIndex]); + if (nextCatIndex != -1) exListNextCatIndex = TextFindIndex(exList, TextFormat("\n%s", exCategories[nextCatIndex])) + 1; else exListNextCatIndex = exListLen; // EOF strncpy(exListUpdated, exList, exListNextCatIndex); From 37a852a7aeb96df84c89f1bc3bb0ad7bada828e2 Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Tue, 3 Mar 2026 17:57:34 +0000 Subject: [PATCH 113/319] [web] Add clipboard image implementation for web (#5614) * Add clipboard image implementation for web * Making sure that are not malloc empty string or image --- src/platforms/rcore_web.c | 101 +++++++++++++++++++++----- src/platforms/rcore_web_emscripten.c | 103 +++++++++++++++++++++------ 2 files changed, 164 insertions(+), 40 deletions(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 5fd91eff6..6b9c55534 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -801,35 +801,98 @@ void SetClipboardText(const char *text) else EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, text); } +// Async EM_JS to be able to await clickboard read asynchronous function +EM_ASYNC_JS(void, RequestClipboardData, (void), { + if (navigator.clipboard && window.isSecureContext) { + let items = await navigator.clipboard.read(); + for (const item of items) { + + // Check if this item contains plain text or image + if (item.types.includes("text/plain")) { + const blob = await item.getType("text/plain"); + const text = await blob.text(); + window._lastClipboardString = text; + } + else if (item.types.find(t => t.startsWith("image/"))) { + const blob = await item.getType(item.types.find(t => t.startsWith("image/"))); + const bitmap = await createImageBitmap(blob); + + const canvas = document.createElement('canvas'); + canvas.width = bitmap.width; + canvas.height = bitmap.height; + const ctx = canvas.getContext('2d'); + ctx.drawImage(bitmap, 0, 0); + + const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height).data; + + // Store image and data for the Fetch function + window._lastImgWidth = canvas.width; + window._lastImgHeight = canvas.height; + window._lastImgData = imgData; + } + } + } else { + console.warn("Clipboard read() requires HTTPS/Localhost"); + } +}); + +// Returns the string created by RequestClipboardData from JS memory to Emscripten C memory +EM_JS(char*, GetLastPastedText, (void), { + var str = window._lastClipboardString || ""; + var length = lengthBytesUTF8(str) + 1; + if (length > 1) { + var ptr = _malloc(length); + stringToUTF8(str, ptr, length); + return ptr; + } + return 0; +}); + +// Returns the image created by RequestClipboardData from JS memory to Emscripten C memory +EM_JS(unsigned char*, GetLastPastedImage, (int* width, int* height), { + if (window._lastImgData) { + const data = window._lastImgData; + if (data.length > 0) { + const ptr = _malloc(data.length); + HEAPU8.set(data, ptr); + + // Set the width and height via the pointers passed from C + // HEAP32 handles the 4-byte integers + if (width) setValue(width, window._lastImgWidth, 'i32'); + if (height) setValue(height, window._lastImgHeight, 'i32'); + + // Clear the JS buffer so we don't fetch the same image twice + window._lastImgData = null; + + return ptr; + } + } + return 0; +}); + // Get clipboard text content // NOTE: returned string is allocated and freed by GLFW const char *GetClipboardText(void) { -/* - // Accessing clipboard data from browser is tricky due to security reasons - // The method to use is navigator.clipboard.readText() but this is an asynchronous method - // that will return at some moment after the function is called with the required data - emscripten_run_script_string("navigator.clipboard.readText() \ - .then(text => { document.getElementById('clipboard').innerText = text; console.log('Pasted content: ', text); }) \ - .catch(err => { console.error('Failed to read clipboard contents: ', err); });" - ); - - // The main issue is getting that data, one approach could be using ASYNCIFY and wait - // for the data but it requires adding Asyncify emscripten library on compilation - - // Another approach could be just copy the data in a HTML text field and try to retrieve it - // later on if available... and clean it for future accesses -*/ - return NULL; + RequestClipboardData(); + return GetLastPastedText(); } // Get clipboard image Image GetClipboardImage(void) { Image image = { 0 }; - - TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform"); - + int w = 0, h = 0; + RequestClipboardData(); + unsigned char* data = GetLastPastedImage(&w, &h); + if (data != NULL) { + image.data = data; + image.width = w; + image.height = h; + image.mipmaps = 1; + image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + } + return image; } diff --git a/src/platforms/rcore_web_emscripten.c b/src/platforms/rcore_web_emscripten.c index 995881233..dcee45865 100644 --- a/src/platforms/rcore_web_emscripten.c +++ b/src/platforms/rcore_web_emscripten.c @@ -779,37 +779,98 @@ void SetClipboardText(const char *text) else EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, text); } +// Async EM_JS to be able to await clickboard read asynchronous function +EM_ASYNC_JS(void, RequestClipboardData, (void), { + if (navigator.clipboard && window.isSecureContext) { + let items = await navigator.clipboard.read(); + for (const item of items) { + + // Check if this item contains plain text or image + if (item.types.includes("text/plain")) { + const blob = await item.getType("text/plain"); + const text = await blob.text(); + window._lastClipboardString = text; + } + else if (item.types.find(t => t.startsWith("image/"))) { + const blob = await item.getType(item.types.find(t => t.startsWith("image/"))); + const bitmap = await createImageBitmap(blob); + + const canvas = document.createElement('canvas'); + canvas.width = bitmap.width; + canvas.height = bitmap.height; + const ctx = canvas.getContext('2d'); + ctx.drawImage(bitmap, 0, 0); + + const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height).data; + + // Store image and data for the Fetch function + window._lastImgWidth = canvas.width; + window._lastImgHeight = canvas.height; + window._lastImgData = imgData; + } + } + } else { + console.warn("Clipboard read() requires HTTPS/Localhost"); + } +}); + +// Returns the string created by RequestClipboardData from JS memory to Emscripten C memory +EM_JS(char*, GetLastPastedText, (void), { + var str = window._lastClipboardString || ""; + var length = lengthBytesUTF8(str) + 1; + if (length > 1) { + var ptr = _malloc(length); + stringToUTF8(str, ptr, length); + return ptr; + } + return 0; +}); + +// Returns the image created by RequestClipboardData from JS memory to Emscripten C memory +EM_JS(unsigned char*, GetLastPastedImage, (int* width, int* height), { + if (window._lastImgData) { + const data = window._lastImgData; + if (data.length > 0) { + const ptr = _malloc(data.length); + HEAPU8.set(data, ptr); + + // Set the width and height via the pointers passed from C + // HEAP32 handles the 4-byte integers + if (width) setValue(width, window._lastImgWidth, 'i32'); + if (height) setValue(height, window._lastImgHeight, 'i32'); + + // Clear the JS buffer so we don't fetch the same image twice + window._lastImgData = null; + + return ptr; + } + } + return 0; +}); + // Get clipboard text content // NOTE: returned string is allocated and freed by GLFW const char *GetClipboardText(void) { -/* - // Accessing clipboard data from browser is tricky due to security reasons - // The method to use is navigator.clipboard.readText() but this is an asynchronous method - // that will return at some moment after the function is called with the required data - emscripten_run_script_string("navigator.clipboard.readText() \ - .then(text => { document.getElementById('clipboard').innerText = text; console.log('Pasted content: ', text); }) \ - .catch(err => { console.error('Failed to read clipboard contents: ', err); });" - ); - - // The main issue is getting that data, one approach could be using ASYNCIFY and wait - // for the data but it requires adding Asyncify emscripten library on compilation - - // Another approach could be just copy the data in a HTML text field and try to retrieve it - // later on if available... and clean it for future accesses -*/ - return NULL; + RequestClipboardData(); + return GetLastPastedText(); } // Get clipboard image Image GetClipboardImage(void) { Image image = { 0 }; - - // NOTE: In theory, the new navigator.clipboard.read() can be used to return arbitrary data from clipboard (2024) - // REF: https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/read - TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform"); - + int w = 0, h = 0; + RequestClipboardData(); + unsigned char* data = GetLastPastedImage(&w, &h); + if (data != NULL) { + image.data = data; + image.width = w; + image.height = h; + image.mipmaps = 1; + image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + } + return image; } From 70a1a57a12845d2dd57d22a656a6aa5a61900bd7 Mon Sep 17 00:00:00 2001 From: Dmitry Mozgin Date: Tue, 3 Mar 2026 21:04:06 +0300 Subject: [PATCH 114/319] Remove unnecessary define from raylib_opengl_interop.c (#5618) --- examples/others/raylib_opengl_interop.c | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/others/raylib_opengl_interop.c b/examples/others/raylib_opengl_interop.c index 935eb87f8..540a623ab 100644 --- a/examples/others/raylib_opengl_interop.c +++ b/examples/others/raylib_opengl_interop.c @@ -30,7 +30,6 @@ #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL) #if defined(GRAPHICS_API_OPENGL_ES2) - #define GLAD_GLES2_IMPLEMENTATION #include "glad_gles2.h" // Required for: OpenGL functionality #define glGenVertexArrays glGenVertexArraysOES #define glBindVertexArray glBindVertexArrayOES From b4746469d4b88b9a06f448c8b559031b9acd3873 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 3 Mar 2026 22:40:34 +0100 Subject: [PATCH 115/319] Formating review, using imperative mode in comments --- src/config.h | 8 ++-- src/external/win32_clipboard.h | 18 ++++----- src/platforms/rcore_desktop_glfw.c | 12 +++--- src/platforms/rcore_desktop_rgfw.c | 39 +++++++++---------- src/platforms/rcore_desktop_sdl.c | 34 ++++++++--------- src/platforms/rcore_desktop_win32.c | 10 ++--- src/platforms/rcore_drm.c | 6 +-- src/platforms/rcore_template.c | 6 +-- src/platforms/rcore_web.c | 2 +- src/raudio.c | 33 ++++++++-------- src/raylib.h | 6 +-- src/raymath.h | 22 +++++------ src/rcamera.h | 8 ++-- src/rcore.c | 58 ++++++++++++++--------------- src/rlgl.h | 9 +++-- src/rmodels.c | 26 ++++++------- src/rshapes.c | 4 +- src/rtext.c | 24 ++++++------ 18 files changed, 164 insertions(+), 161 deletions(-) diff --git a/src/config.h b/src/config.h index 9e0b6d8ea..4dab32c36 100644 --- a/src/config.h +++ b/src/config.h @@ -60,7 +60,7 @@ #define SUPPORT_TRACELOG 1 #endif #ifndef SUPPORT_CAMERA_SYSTEM - // Camera module is included (rcamera.h) and multiple predefined + // Camera module is included (rcamera.h) and multiple predefined // cameras are available: free, 1st/3rd person, orbital #define SUPPORT_CAMERA_SYSTEM 1 #endif @@ -90,7 +90,7 @@ #define SUPPORT_BUSY_WAIT_LOOP 0 // Disabled by default #endif #if !SUPPORT_PARTIALBUSY_WAIT_LOOP && !SUPPORT_BUSY_WAIT_LOOP - // Use a partial-busy wait loop, in this case frame sleeps for most of the time, + // Use a partial-busy wait loop, in this case frame sleeps for most of the time, // but then runs a busy loop at the end for accuracy #define SUPPORT_PARTIALBUSY_WAIT_LOOP 1 #endif @@ -267,7 +267,7 @@ #ifndef SUPPORT_IMAGE_EXPORT // Support image export functionality (.png, .bmp, .tga, .jpg, .qoi) - // NOTE: Image export requires stb_image_write.h library + // NOTE: Image export requires stb_image_write.h library #define SUPPORT_IMAGE_EXPORT 1 #endif #ifndef SUPPORT_IMAGE_GENERATION @@ -319,7 +319,7 @@ #endif #ifndef SUPPORT_GPU_SKINNING // GPU skinning disabled by default, some GPUs do not support more than 8 VBOs - #define SUPPORT_GPU_SKINNING 0 + #define SUPPORT_GPU_SKINNING 0 #endif //------------------------------------------------------------------------------------ diff --git a/src/external/win32_clipboard.h b/src/external/win32_clipboard.h index c85b3882a..3beb4c8b1 100644 --- a/src/external/win32_clipboard.h +++ b/src/external/win32_clipboard.h @@ -181,7 +181,7 @@ typedef struct tagRGBQUAD { #endif #ifndef BI_ALPHABITFIELDS -// Bitmap not compressed and that the color table consists of four DWORD color masks, +// Bitmap not compressed and that the color table consists of four DWORD color masks, // that specify the red, green, blue, and alpha components of each pixel #define BI_ALPHABITFIELDS 0x0006 #endif @@ -216,7 +216,7 @@ static int GetPixelDataOffset(BITMAPINFOHEADER bih); // Get pixel data offset fr unsigned char *Win32GetClipboardImageData(int *width, int *height, unsigned long long int *dataSize) { unsigned char *bmpData = NULL; - + if (OpenClipboardRetrying(NULL)) { HGLOBAL clipHandle = (HGLOBAL)GetClipboardData(CF_DIB); @@ -231,7 +231,7 @@ unsigned char *Win32GetClipboardImageData(int *width, int *height, unsigned long if (clipDataSize >= sizeof(BITMAPINFOHEADER)) { int pixelOffset = GetPixelDataOffset(*bmpInfoHeader); - + // Create the bytes for a correct BMP file and copy the data to a pointer //------------------------------------------------------------------------ BITMAPFILEHEADER bmpFileHeader = { 0 }; @@ -245,10 +245,10 @@ unsigned char *Win32GetClipboardImageData(int *width, int *height, unsigned long bmpData = (unsigned char *)RL_MALLOC(sizeof(bmpFileHeader) + clipDataSize); memcpy(bmpData, &bmpFileHeader, sizeof(bmpFileHeader)); // Add BMP file header data memcpy(bmpData + sizeof(bmpFileHeader), bmpInfoHeader, clipDataSize); // Add BMP info header data - + GlobalUnlock(clipHandle); CloseClipboard(); - + TRACELOG(LOG_INFO, "Clipboad image acquired successfully"); //------------------------------------------------------------------------ } @@ -259,14 +259,14 @@ unsigned char *Win32GetClipboardImageData(int *width, int *height, unsigned long CloseClipboard(); } } - else + else { TRACELOG(LOG_WARNING, "Clipboard data failed to be locked"); GlobalUnlock(clipHandle); CloseClipboard(); } } - else + else { TRACELOG(LOG_WARNING, "Clipboard data is not an image"); CloseClipboard(); @@ -286,7 +286,7 @@ static BOOL OpenClipboardRetrying(HWND hWnd) { static const int maxTries = 20; static const int sleepTimeMS = 60; - + for (int i = 0; i < maxTries; i++) { // Might be being hold by another process @@ -295,7 +295,7 @@ static BOOL OpenClipboardRetrying(HWND hWnd) Sleep(sleepTimeMS); } - + return false; } diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 19603d8d3..08e26ee03 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1522,7 +1522,7 @@ int InitPlatform(void) glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE); #endif #if defined(_GLFW_WAYLAND) && !defined(_GLFW_X11) - // GLFW 3.4+ defaults GLFW_SCALE_FRAMEBUFFER to TRUE, + // GLFW 3.4+ defaults GLFW_SCALE_FRAMEBUFFER to TRUE, // causing framebuffer/window size mismatch on Wayland with display scaling glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE); #endif @@ -1727,12 +1727,12 @@ int InitPlatform(void) #if !defined(__APPLE__) if (glfwGetPlatform() == GLFW_PLATFORM_WAYLAND) { - // On Wayland, GLFW_SCALE_FRAMEBUFFER handles scaling; read actual framebuffer size + // On Wayland, GLFW_SCALE_FRAMEBUFFER handles scaling; read actual framebuffer size // instead of resizing the window (which would double-scale) int fbWidth = 0; int fbHeight = 0; glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight); - + CORE.Window.render.width = fbWidth; CORE.Window.render.height = fbHeight; } @@ -1751,7 +1751,7 @@ int InitPlatform(void) // Current active framebuffer size is main framebuffer size CORE.Window.currentFbo = CORE.Window.render; - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully %s", + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully %s", FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)? "(HighDPI)" : ""); TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); @@ -1935,14 +1935,14 @@ static void FramebufferSizeCallback(GLFWwindow *window, int width, int height) int winWidth = 0; int winHeight = 0; glfwGetWindowSize(platform.handle, &winWidth, &winHeight); - + if ((winWidth != width) || (winHeight != height)) { CORE.Window.screen.width = winWidth; CORE.Window.screen.height = winHeight; float scaleX = (float)width/winWidth; float scaleY = (float)height/winHeight; - + CORE.Window.screenScale = MatrixScale(scaleX, scaleY, 1.0f); } } diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index f4f388998..316a8abbb 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -479,7 +479,7 @@ void ToggleFullscreen(void) if (!FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) { FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE); - // Store previous window position (in case we exit fullscreen) + // Store previous window position (in case of exiting fullscreen) Vector2 currentPosition = GetWindowPosition(); CORE.Window.previousPosition.x = currentPosition.x; CORE.Window.previousPosition.y = currentPosition.y; @@ -493,7 +493,7 @@ void ToggleFullscreen(void) { FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); - // we update the window position right away + // Update the window position right away CORE.Window.position = CORE.Window.previousPosition; RGFW_window_setFullscreen(platform.window, 0); RGFW_window_move(platform.window, CORE.Window.position.x, CORE.Window.position.y); @@ -534,7 +534,7 @@ void ToggleBorderlessWindowed(void) { FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE); RGFW_window_setBorder(platform.window, 1); - + CORE.Window.position = CORE.Window.previousPosition; RGFW_window_resize(platform.window, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); @@ -821,7 +821,7 @@ void SetWindowSize(int width, int height) CORE.Window.screen.width = width; CORE.Window.screen.height = height; } - + RGFW_window_resize(platform.window, CORE.Window.screen.width, CORE.Window.screen.height); } @@ -957,17 +957,17 @@ Vector2 GetWindowScaleDPI(void) else monitor = RGFW_getPrimaryMonitor(); #if defined(__APPLE__) - // apple does < 1.0f scaling, example: 0.66f, 0.5f - // we want to convert this to be consistent - return (Vector2){ 1.0f / monitor->scaleX, 1.0f / monitor->scaleX }; + // Apple does < 1.0f scaling, example: 0.66f, 0.5f + // it needs to be convert to be consistent + return (Vector2){ 1.0f/monitor->scaleX, 1.0f/monitor->scaleX }; #else - // linux and windows do >= 1.0f scaling, example: 1.0f, 1.25f, 2.0f + // Linux and Windows do >= 1.0f scaling, example: 1.0f, 1.25f, 2.0f return (Vector2){ monitor->scaleX, monitor->scaleX }; #endif } -// Not part of raylib. Mac has a different pixel ratio for retina displays -// and we want to be able to handle it +// Get monitor pixel ratio +// WARNING: Function not used, neither exposed by raylib float GetMonitorPixelRatio(void) { RGFW_monitor *monitor = NULL; @@ -1009,7 +1009,7 @@ const char *GetClipboardText(void) Image GetClipboardImage(void) { Image image = { 0 }; - + #if SUPPORT_CLIPBOARD_IMAGE && SUPPORT_MODULE_RTEXTURES #if defined(_WIN32) @@ -1283,8 +1283,8 @@ void PollInputEvents(void) { if (CORE.Window.dropFileCount == 0) { - // When a new file is dropped, we reserve a fixed number of slots for all possible dropped files - // at the moment we limit the number of drops at once to 1024 files but this behaviour should probably be reviewed + // When a new file is dropped, reserve a fixed number of slots for all possible dropped files + // at the moment limiting the number of drops at once to 1024 files but this behaviour should probably be reviewed // TODO: Pointers should probably be reallocated for any new file added... CORE.Window.dropFilepaths = (char **)RL_CALLOC(1024, sizeof(char *)); @@ -1331,19 +1331,20 @@ void PollInputEvents(void) CORE.Window.currentFbo.width = CORE.Window.render.width; CORE.Window.currentFbo.height = CORE.Window.render.height; #elif defined(PLATFORM_WEB_RGFW) - // do nothing for web return; #else SetupViewport(platform.window->w, platform.window->h); - // if we are doing automatic DPI scaling, then the "screen" size is divided by the window scale + + // Consider content scaling if required if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) { Vector2 scaleDpi = GetWindowScaleDPI(); CORE.Window.screen.width = (int)(platform.window->w/scaleDpi.x); CORE.Window.screen.height = (int)(platform.window->h/scaleDpi.y); CORE.Window.screenScale = MatrixScale(scaleDpi.x, scaleDpi.y, 1.0f); - // mouse scale doesnt seem needed - // SetMouseScale(1.0f/scaleDpi.x, 1.0f/scaleDpi.y); + + // Mouse scale does not seem to be needed + //SetMouseScale(1.0f/scaleDpi.x, 1.0f/scaleDpi.y); } else { @@ -1403,7 +1404,7 @@ void PollInputEvents(void) case RGFW_keyChar: { - // NOTE: event.text.text data comes an UTF-8 text sequence but we register codepoints (int) + // NOTE: event.text.text data comes an UTF-8 text sequence but registering codepoints (int) // Check if there is space available in the queue if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) { @@ -1664,7 +1665,7 @@ int InitPlatform(void) //---------------------------------------------------------------------------- - // If everything work as expected, we can continue + // If everything work as expected, continue CORE.Window.position.x = platform.window->x; CORE.Window.position.y = platform.window->y; CORE.Window.render.width = CORE.Window.screen.width; diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index b20fded9c..06c33026f 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -250,10 +250,10 @@ 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 #if defined(USING_VERSION_SDL3) -// SDL3 Migration: +// SDL3 migration: // SDL_WINDOW_FULLSCREEN_DESKTOP has been removed, // SDL_GetWindowFullscreenMode() can be called // to see whether an exclusive fullscreen mode will be used @@ -265,10 +265,10 @@ static const int CursorsLUT[] = { #define SDL_ENABLE true // 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 (|) +#define SDL_INIT_TIMER 0x0 // It's a flag, so no problem in setting it to zero to be used 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 -#define SDL_WINDOW_SHOWN 0x0 // It's a flag, so no problem in setting it to zero if we use in a bitor (|) +#define SDL_WINDOW_SHOWN 0x0 // It's a flag, so no problem in setting it to zero to be used in a bitor (|) // SDL3 Migration: Renamed // IMPORTANT: Might need to call SDL_CleanupEvent somewhere see :https://github.com/libsdl-org/SDL/issues/3540#issuecomment-1793449852 @@ -414,13 +414,14 @@ int SDL_GetNumTouchFingers(SDL_TouchID touchID) #else // SDL2 fallback -// Since SDL2 doesn't have this function we leave a stub +// Since SDL2 doesn't have this function, leaving a stub // SDL_GetClipboardData function is available since SDL 3.1.3. (e.g. SDL3) void *SDL_GetClipboardData(const char *mime_type, size_t *size) { TRACELOG(LOG_WARNING, "SDL: Getting clipboard data that is not text not available in SDL2"); - // We could possibly implement it ourselves in this case for some easier platforms + // TODO: Implement getting clipboard data + return NULL; } #endif // USING_VERSION_SDL3 @@ -573,8 +574,6 @@ void SetWindowState(unsigned int flags) } if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED)) { - // NOTE: To be able to implement this part it seems that we should - // do it ourselves, via 'windows.h', 'X11/Xlib.h' or even 'Cocoa.h' TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_SDL"); } if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST)) @@ -856,8 +855,8 @@ void SetWindowMonitor(int monitor) // ending up positioned partly outside the target display // NOTE 2: The workaround for that is, previously to moving the window, // setting the window size to the target display size, so they match - // NOTE 3: It wasn't done here because we can't assume changing the window size automatically - // is acceptable behavior by the user + // NOTE 3: It wasn't done here because it can not bee assumed that changing + // the window size automatically 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; @@ -1261,7 +1260,7 @@ void DisableCursor(void) void SwapScreenBuffer(void) { #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) - // NOTE: We use a preprocessor condition here because rlCopyFramebuffer() is only declared for software rendering + // NOTE: Using a preprocessor condition here because rlCopyFramebuffer() is only declared for software rendering SDL_Surface *surface = SDL_GetWindowSurface(platform.window); rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, surface->pixels); SDL_UpdateWindowSurface(platform.window); @@ -1442,8 +1441,8 @@ void PollInputEvents(void) { if (CORE.Window.dropFileCount == 0) { - // When a new file is dropped, we reserve a fixed number of slots for all possible dropped files - // at the moment we limit the number of drops at once to 1024 files but this behaviour should probably be reviewed + // When a new file is dropped, reserve a fixed number of slots for all possible dropped files + // at the moment limit the number of drops at once to 1024 files but this behaviour should probably be reviewed // TODO: Pointers should probably be reallocated for any new file added... CORE.Window.dropFilepaths = (char **)RL_CALLOC(1024, sizeof(char *)); @@ -1497,7 +1496,8 @@ void PollInputEvents(void) const int width = event.window.data1; const int height = event.window.data2; SetupViewport(width, height); - // if we are doing automatic DPI scaling, then the "screen" size is divided by the window scale + + // Consider content scaling if required if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) { CORE.Window.screen.width = (int)(width/GetWindowScaleDPI().x); @@ -1622,7 +1622,7 @@ void PollInputEvents(void) case SDL_TEXTINPUT: { - // NOTE: event.text.text data comes an UTF-8 text sequence but we register codepoints (int) + // NOTE: event.text.text data comes an UTF-8 text sequence but register codepoints (int) // Check if there is space available in the queue if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) @@ -1885,7 +1885,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, normalizing it to raylib's -1.0 to 1.0f range float value = event.jaxis.value/(float)32767; CORE.Input.Gamepad.axisState[i][axis] = value; @@ -2032,7 +2032,7 @@ int InitPlatform(void) platform.window = SDL_CreateWindow(CORE.Window.title, CORE.Window.screen.width, CORE.Window.screen.height, flags); - // NOTE: SDL3 no longer enables text input by default, + // NOTE: SDL3 no longer enables text input by default, // it is needed to be enabled manually to keep GetCharPressed() working // REF: https://github.com/libsdl-org/SDL/commit/72fc6f86e5d605a3787222bc7dc18c5379047f4a const char *enableOSK = SDL_GetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD); diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index 48b644a72..89ae22ec0 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -995,7 +995,7 @@ void SetWindowMaxSize(int width, int height) CORE.Window.screenMax.width = width; CORE.Window.screenMax.height = height; - + SetWindowSize(platform.appScreenWidth, platform.appScreenHeight); } @@ -1771,7 +1771,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara SIZE maxWindowSize = CalcWindowSize(96, maxClientSize, style); SIZE minClientSize = { CORE.Window.screenMin.width, CORE.Window.screenMin.height }; SIZE minWindowSize = CalcWindowSize(96, minClientSize, style); - + LPMINMAXINFO lpmmi = (LPMINMAXINFO) lparam; lpmmi->ptMaxSize.x = maxWindowSize.cx; lpmmi->ptMaxSize.y = maxWindowSize.cy; @@ -1865,7 +1865,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara case WM_SIZE: { // WARNING: Don't trust the docs, they say this message can not be obtained if not calling DefWindowProc() - // in response to WM_WINDOWPOSCHANGED but looks like when a window is created, + // in response to WM_WINDOWPOSCHANGED but looks like when a window is created, // this message can be obtained without getting WM_WINDOWPOSCHANGED HandleWindowResize(hwnd, &platform.appScreenWidth, &platform.appScreenHeight); } break; @@ -1880,7 +1880,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara SIZE *inoutSize = (SIZE *)lparam; UINT newDpi = (UINT)wparam; // TODO: WARNING: Converting from WPARAM = UINT_PTR - // For the following flag changes, a window resize event should be posted, + // For the following flag changes, a window resize event should be posted, // TODO: Should it be done after dpi changes? if (CORE.Window.flags & FLAG_WINDOW_MINIMIZED) return TRUE; if (CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) return TRUE; @@ -2188,7 +2188,7 @@ static unsigned SanitizeFlags(int mode, unsigned flags) // the state continues to change // // This design takes care of many odd corner cases. For example, in case of restoring -// a window that was previously maximized AND minimized and those two flags need to be removed, +// a window that was previously maximized AND minimized and those two flags need to be removed, // ShowWindow with SW_RESTORE twice need to bee actually calleed. Another example is // wheen having a maximized window, if the undecorated flag is modified then the window style // needs to be updated, but updating the style would mean the window size would change diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 3fbea09ba..43ffac8d1 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -58,7 +58,7 @@ #include // Linux: Joystick support library // WARNING: Both 'linux/input.h' and 'raylib.h' define KEY_F12 -// To avoid conflict with the capturing code in rcore.c we undefine the macro KEY_F12, +// To avoid conflict with the capturing code in rcore.c, undefine the macro KEY_F12, // so the enum KEY_F12 from raylib is used #undef KEY_F12 @@ -97,7 +97,7 @@ #define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events -// Actually biggest key is KEY_CNT but we only really map the keys up to KEY_ALS_TOGGLE +// Actually biggest key is KEY_CNT but only mapping keys up to KEY_ALS_TOGGLE #define KEYMAP_SIZE KEY_ALS_TOGGLE //---------------------------------------------------------------------------------- @@ -1428,7 +1428,7 @@ int InitPlatform(void) if ((eglClientExtensions != NULL) && (strstr(eglClientExtensions, "EGL_EXT_platform_base") != NULL)) { PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); - + if (eglGetPlatformDisplayEXT != NULL) platform.device = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, platform.gbmDevice, NULL); } diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 45c150942..758c20179 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -341,7 +341,7 @@ void SwapScreenBuffer(void) double GetTime(void) { double time = 0.0; - + struct timespec ts = { 0 }; clock_gettime(CLOCK_MONOTONIC, &ts); unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; @@ -459,7 +459,7 @@ int InitPlatform(void) if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) { // TODO: Enable MSAA - + TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4"); } @@ -489,7 +489,7 @@ int InitPlatform(void) } //---------------------------------------------------------------------------- - // If everything work as expected, we can continue + // If everything worked as expected, continue CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; CORE.Window.currentFbo.width = CORE.Window.render.width; diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 5fd91eff6..4e0a05250 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -1255,7 +1255,7 @@ int InitPlatform(void) // Remember center for switchinging from fullscreen to window if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) { - // If screen width/height equal to the display, it's not possible to + // If screen width/height equal to the display, it's not possible to // calculate the window position for toggling full-screened/windowed CORE.Window.position.x = CORE.Window.display.width/4; CORE.Window.position.y = CORE.Window.display.height/4; diff --git a/src/raudio.c b/src/raudio.c index afe0a7814..888973ce7 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -495,8 +495,9 @@ void InitAudioDevice(void) return; } - // Mixing happens on a separate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may - // want to look at something a bit smarter later on to keep everything real-time, if that's necessary + // Mixing happens on a separate thread which means synchronization is needed + // A mutex is used here to make things simple, but may want to look at something + // a bit smarter later on to keep everything real-time, if that's necessary if (ma_mutex_init(&AUDIO.System.lock) != MA_SUCCESS) { TRACELOG(LOG_WARNING, "AUDIO: Failed to create mutex for mixing"); @@ -822,7 +823,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int wave.channels = wav.channels; wave.data = (short *)RL_MALLOC((size_t)wave.frameCount*wave.channels*sizeof(short)); - // NOTE: We are forcing conversion to 16bit sample size on reading + // NOTE: Forcing conversion to 16bit sample size on reading drwav_read_pcm_frames_s16(&wav, wave.frameCount, (drwav_int16 *)wave.data); } else TRACELOG(LOG_WARNING, "WAVE: Failed to load WAV data"); @@ -845,7 +846,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int wave.frameCount = (unsigned int)stb_vorbis_stream_length_in_samples(oggData); // NOTE: It returns frames! wave.data = (short *)RL_MALLOC(wave.frameCount*wave.channels*sizeof(short)); - // NOTE: Get the number of samples to process (be careful! we ask for number of shorts, not bytes!) + // NOTE: Get the number of samples to process (be careful! asking for number of shorts, not bytes!) stb_vorbis_get_samples_short_interleaved(oggData, info.channels, (short *)wave.data, wave.frameCount*wave.channels); stb_vorbis_close(oggData); } @@ -858,7 +859,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int drmp3_config config = { 0 }; unsigned long long int totalFrameCount = 0; - // NOTE: We are forcing conversion to 32bit float sample size on reading + // NOTE: Forcing conversion to 32bit float sample size on reading wave.data = drmp3_open_memory_and_read_pcm_frames_f32(fileData, dataSize, &config, &totalFrameCount, NULL); wave.sampleSize = 32; @@ -896,7 +897,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int { unsigned long long int totalFrameCount = 0; - // NOTE: We are forcing conversion to 16bit sample size on reading + // NOTE: Forcing conversion to 16bit sample size on reading wave.data = drflac_open_memory_and_read_pcm_frames_s16(fileData, dataSize, &wave.channels, &wave.sampleRate, &totalFrameCount, NULL); wave.sampleSize = 16; @@ -933,7 +934,7 @@ Sound LoadSound(const char *fileName) Sound sound = LoadSoundFromWave(wave); - UnloadWave(wave); // Sound is loaded, we can unload wave + UnloadWave(wave); // Sound is loaded, wave can be unloaded return sound; } @@ -2475,20 +2476,20 @@ static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, // Reads audio data from an AudioBuffer object in device format, returned data will be in a format appropriate for mixing static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, float *framesOut, ma_uint32 frameCount) { - // NOTE: Continuously converting data from the AudioBuffer's internal format to the mixing format, - // which should be defined by the output format of the data converter. - // This is done until frameCount frames have been output. + // NOTE: Continuously converting data from the AudioBuffer's internal format to the mixing format, + // which should be defined by the output format of the data converter. + // This is done until frameCount frames have been output. ma_uint32 bpf = ma_get_bytes_per_frame(audioBuffer->converter.formatIn, audioBuffer->converter.channelsIn); ma_uint8 inputBuffer[4096] = { 0 }; ma_uint32 inputBufferFrameCap = sizeof(inputBuffer)/bpf; - + ma_uint32 totalOutputFramesProcessed = 0; while (totalOutputFramesProcessed < frameCount) { float *runningFramesOut = framesOut + (totalOutputFramesProcessed*audioBuffer->converter.channelsOut); ma_uint64 outputFramesToProcessThisIteration = frameCount - totalOutputFramesProcessed; //ma_uint64 inputFramesToProcessThisIteration = 0; - + // Process any residual input frames from the previous read first. if (audioBuffer->converterResidualCount > 0) { @@ -2656,7 +2657,7 @@ static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 fr const float localVolume = buffer->volume; const ma_uint32 channels = AUDIO.System.device.playback.channels; - if (channels == 2) // We consider panning + if (channels == 2) // Consider panning { const float right = (buffer->pan + 1.0f)/2.0f; // Normalize: [-1..1] -> [0..1] const float left = 1.0f - right; @@ -2676,7 +2677,7 @@ static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 fr frameIn += 2; } } - else // We do not consider panning + else // Do not consider panning { for (ma_uint32 frame = 0; frame < frameCount; frame++) { @@ -2831,7 +2832,7 @@ static const char *GetFileNameWithoutExt(const char *filePath) { if (fileName[i] == '.') { - // NOTE: We break on first '.' found + // NOTE: Break on first '.' found fileName[i] = '\0'; break; } @@ -2862,7 +2863,7 @@ static unsigned char *LoadFileData(const char *fileName, int *dataSize) { data = (unsigned char *)RL_MALLOC(size*sizeof(unsigned char)); - // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements] + // NOTE: fread() returns number of read elements instead of bytes, so reading [1 byte, size elements] unsigned int count = (unsigned int)fread(data, sizeof(unsigned char), size, file); *dataSize = count; diff --git a/src/raylib.h b/src/raylib.h index e542ad517..d996e298b 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -26,7 +26,7 @@ * - One default RenderBatch is loaded on rlglInit()->rlLoadRenderBatch() [rlgl] (OpenGL 3.3 or ES2) * * DEPENDENCIES: -* [rcore] Depends on the selected platform backend, check rcore.c header for details +* [rcore] Depends on the selected platform backend, check rcore.c header for details * [rlgl] glad/glad_gles2 (David Herberth - github.com/Dav1dde/glad) for OpenGL extensions loading * [raudio] miniaudio (David Reid - github.com/mackron/miniaudio) for audio device/context management * @@ -39,7 +39,7 @@ * [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG) * [rtextures] stb_image_resize2 (Sean Barret) for image resizing algorithms * [rtextures] stb_perlin (Sean Barret) for Perlin Noise image generation -* [rtextures] rl_gputex (Ramon Santamaria) for GPU-compressed texture formats +* [rtextures] rl_gputex (Ramon Santamaria) for GPU-compressed texture formats * [rtext] stb_truetype (Sean Barret) for ttf fonts loading * [rtext] stb_rect_pack (Sean Barret) for rectangles packing * [rmodels] par_shapes (Philip Rideout) for parametric 3d shapes generation @@ -1167,7 +1167,7 @@ RLAPI bool IsFileDropped(void); // Check if RLAPI FilePathList LoadDroppedFiles(void); // Load dropped filepaths RLAPI void UnloadDroppedFiles(FilePathList files); // Unload dropped filepaths RLAPI unsigned int GetDirectoryFileCount(const char *dirPath); // Get the file count in a directory -RLAPI unsigned int GetDirectoryFileCountEx(const char *basePath, const char *filter, bool scanSubdirs);// Get the file count in a directory with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result +RLAPI unsigned int GetDirectoryFileCountEx(const char *basePath, const char *filter, bool scanSubdirs); // Get the file count in a directory with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result // Compression/Encoding functionality RLAPI unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize); // Compress data (DEFLATE algorithm), memory must be MemFree() diff --git a/src/raymath.h b/src/raymath.h index b0b8dd199..1ea0df225 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -1523,7 +1523,7 @@ RMAPI float MatrixDeterminant(Matrix mat) a20*a01*a12*a33 - a00*a21*a12*a33 - a10*a01*a22*a33 + a00*a11*a22*a33; */ // Using Laplace expansion (https://en.wikipedia.org/wiki/Laplace_expansion), - // previous operation can be simplified to 40 multiplications, decreasing matrix + // previous operation can be simplified to 40 multiplications, decreasing matrix // size from 4x4 to 2x2 using minors // Cache the matrix values (speed optimization) @@ -1686,20 +1686,20 @@ RMAPI Matrix MatrixSubtract(Matrix left, Matrix right) RMAPI Matrix MatrixMultiply(Matrix left, Matrix right) { Matrix result = { 0 }; - + #if defined(RAYMATH_SSE_ENABLED) // Load left side and right side __m128 c0 = _mm_set_ps(right.m12, right.m8, right.m4, right.m0); __m128 c1 = _mm_set_ps(right.m13, right.m9, right.m5, right.m1); __m128 c2 = _mm_set_ps(right.m14, right.m10, right.m6, right.m2); __m128 c3 = _mm_set_ps(right.m15, right.m11, right.m7, right.m3); - + // Transpose so c0..c3 become *rows* of the right matrix in semantic order _MM_TRANSPOSE4_PS(c0, c1, c2, c3); float tmp[4] = { 0 }; __m128 row; - + // Row 0 of result: [m0, m1, m2, m3] row = _mm_mul_ps(_mm_set1_ps(left.m0), c0); row = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m1), c1)); @@ -1768,9 +1768,9 @@ RMAPI Matrix MatrixMultiply(Matrix left, Matrix right) RMAPI Matrix MatrixMultiplyValue(Matrix left, float value) { Matrix result = { - left.m0 * value, left.m4 * value, left.m8 * value, left.m12 * value, - left.m1 * value, left.m5 * value, left.m9 * value, left.m13 * value, - left.m2 * value, left.m6 * value, left.m10 * value, left.m14 * value, + left.m0 * value, left.m4 * value, left.m8 * value, left.m12 * value, + left.m1 * value, left.m5 * value, left.m9 * value, left.m13 * value, + left.m2 * value, left.m6 * value, left.m10 * value, left.m14 * value, left.m3 * value, left.m7 * value, left.m11 * value, left.m15 * value }; @@ -2679,7 +2679,7 @@ RMAPI Matrix MatrixCompose(Vector3 translation, Quaternion rotation, Vector3 sca right = Vector3RotateByQuaternion(right, rotation); up = Vector3RotateByQuaternion(up, rotation); forward = Vector3RotateByQuaternion(forward, rotation); - + // Set result matrix output Matrix result = { right.x, up.x, forward.x, translation.x, @@ -3091,9 +3091,9 @@ inline const Quaternion& operator *= (Quaternion& lhs, const Matrix& rhs) } // Matrix operators -static constexpr Matrix MatrixUnit = { 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, +static constexpr Matrix MatrixUnit = { 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 1 }; inline Matrix operator + (const Matrix& lhs, const Matrix& rhs) diff --git a/src/rcamera.h b/src/rcamera.h index 3c880bab7..21ca51a22 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -54,9 +54,9 @@ #if defined(__TINYC__) #define __declspec(x) __attribute__((x)) #endif - #define RLAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll) + #define RLAPI __declspec(dllexport) // Building the library as a Win32 shared library (.dll) #elif defined(USE_LIBTYPE_SHARED) - #define RLAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll) + #define RLAPI __declspec(dllimport) // Using the library as a Win32 shared library (.dll) #endif #endif @@ -191,7 +191,7 @@ RLAPI Matrix GetCameraProjectionMatrix(Camera *camera, float aspect); // IsKeyDown() // IsKeyPressed() // GetFrameTime() - + #include // Required for: fabsf() //---------------------------------------------------------------------------------- @@ -364,7 +364,7 @@ void CameraPitch(Camera *camera, float angle, bool lockView, bool rotateAroundTa if (lockView) { - // In these camera modes we clamp the Pitch angle + // In these camera modes, clamp the Pitch angle // to allow only viewing straight up or down. // Clamp view up diff --git a/src/rcore.c b/src/rcore.c index 85a5c0b3b..c286a389f 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -517,7 +517,7 @@ static void RecordAutomationEvent(void); // Record frame events (to internal eve #endif #if defined(_WIN32) && !defined(PLATFORM_DESKTOP_RGFW) -// NOTE: We declare Sleep() function symbol to avoid including windows.h (kernel32.lib linkage required) +// NOTE: Declaring Sleep() function symbol to avoid including windows.h (kernel32.lib linkage required) __declspec(dllimport) void __stdcall Sleep(unsigned long msTimeout); // Required for: WaitTime() #endif @@ -701,12 +701,12 @@ void InitWindow(int width, int height, const char *title) Rectangle rec = GetFontDefault().recs[95]; if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) { - // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering + // NOTE: Try to maxime rec padding to avoid pixel bleeding on MSAA filtering SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); } else { - // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding + // NOTE: Set up a 1px padding on char rectangle to avoid pixel bleeding SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); } #endif @@ -1042,7 +1042,7 @@ void EndTextureMode(void) // Set viewport to default framebuffer size SetupViewport(CORE.Window.render.width, CORE.Window.render.height); - // Go back to the modelview state from BeginDrawing since we are back to the default FBO + // Go back to the modelview state from BeginDrawing, back to the main framebuffer rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix rlLoadIdentity(); // Reset current matrix (modelview) rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required @@ -1079,7 +1079,7 @@ void EndBlendMode(void) } // Begin scissor mode (define screen area for following drawing) -// NOTE: Scissor rec refers to bottom-left corner, we change it to upper-left +// NOTE: Scissor rec refers to bottom-left corner, changing it to upper-left void BeginScissorMode(int x, int y, int width, int height) { rlDrawRenderBatchActive(); // Update and draw internal render batch @@ -1182,9 +1182,9 @@ VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device) config.projection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f)); // Compute camera transformation matrices - // NOTE: Camera movement might seem more natural if we model the head - // Our axis of rotation is the base of our head, so we might want to add - // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions + // NOTE: Camera movement might seem more natural if modelling the head + // Axis of rotation is the base of the head, so adding some y (base of head to eye level + // and -z (center of head to eye protrusion) to the camera positions config.viewOffset[0] = MatrixTranslate(device.interpupillaryDistance*0.5f, 0.075f, 0.045f); config.viewOffset[1] = MatrixTranslate(-device.interpupillaryDistance*0.5f, 0.075f, 0.045f); @@ -1247,15 +1247,15 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode) if (shader.id == 0) { - // Shader could not be loaded but we still load the location points to avoid potential crashes - // NOTE: All locations set to -1 (no location) + // Shader could not be loaded but still loading the location points to avoid potential crashes + // NOTE: All locations set to -1 (no location found) shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int)); for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1; } else if (shader.id == rlGetShaderIdDefault()) shader.locs = rlGetShaderLocsDefault(); else if (shader.id > 0) { - // After custom shader loading, we TRY to set default location names + // After custom shader loading, trying to set default location names // Default shader attribute locations have been binded before linking: // - vertex position location = 0 // - vertex texcoord location = 1 @@ -1448,7 +1448,7 @@ Ray GetScreenToWorldRayEx(Vector2 position, Camera camera, int width, int height Vector3 farPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView); // Unproject the mouse cursor in the near plane - // We need this as the source position because orthographic projects, + // It is needed as the source position because orthographic projects, // compared to perspective doesn't have a convergence point, // meaning that the "eye" of the camera is more like a plane than a point Vector3 cameraPlanePointerPos = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, -1.0f }, matProj, matView); @@ -1484,7 +1484,7 @@ Matrix GetCameraMatrix2D(Camera2D camera) // not for the camera getting bigger, hence the invert. Same deal with rotation // 3. Move it by (-offset); // Offset defines target transform relative to screen, but since effectively "moving" screen (camera) - // we need to do it into opposite direction (inverse transform) + // it needs to be moved into opposite direction (inverse transform) // Having camera transform in world-space, inverse of it gives the modelview transform // Since (A*B*C)' = C'*B'*A', the modelview is @@ -1586,7 +1586,7 @@ void SetTargetFPS(int fps) } // Get current FPS -// NOTE: We calculate an average framerate +// NOTE: Calculating an average framerate int GetFPS(void) { int fps = 0; @@ -1601,7 +1601,7 @@ int GetFPS(void) static float average = 0, last = 0; float fpsFrame = GetFrameTime(); - // if we reset the window, reset the FPS info + // If reseting the window, reset the FPS info if (CORE.Time.frameCounter == 0) { average = 0; @@ -1644,7 +1644,7 @@ float GetFrameTime(void) // Wait for some time (stop program execution) // NOTE: Sleep() granularity could be around 10 ms, it means, Sleep() could -// take longer than expected... for that reason we use the busy wait loop +// take longer than expected... for that reason a busy wait loop is used // REF: http://stackoverflow.com/questions/43057578/c-programming-win32-games-sleep-taking-longer-than-expected // REF: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timing on Win32! void WaitTime(double seconds) @@ -1659,7 +1659,7 @@ void WaitTime(double seconds) while (GetTime() < destinationTime) { } #else #if SUPPORT_PARTIALBUSY_WAIT_LOOP - double sleepSeconds = seconds - seconds*0.05; // NOTE: We reserve a percentage of the time for busy waiting + double sleepSeconds = seconds - seconds*0.05; // NOTE: Reserve a percentage of the time for busy waiting #else double sleepSeconds = seconds; #endif @@ -1818,7 +1818,7 @@ void TakeScreenshot(const char *fileName) // Security check to (partially) avoid malicious code if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } - // Apply a scale if we are doing HIGHDPI auto-scaling + // Apply content scaling if required Vector2 scale = { 1.0f, 1.0f }; if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) scale = GetWindowScaleDPI(); @@ -1973,11 +1973,11 @@ unsigned char *LoadFileData(const char *fileName, int *dataSize) if (data != NULL) { - // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements] + // NOTE: fread() returns number of read elements instead of bytes, so reading [1 byte, size elements] size_t count = fread(data, sizeof(unsigned char), size, file); // WARNING: fread() returns a size_t value, usually 'unsigned int' (32bit compilation) and 'unsigned long long' (64bit compilation) - // dataSize is unified along raylib as a 'int' type, so, for file-sizes > INT_MAX (2147483647 bytes) we have a limitation + // dataSize is unified along raylib as a 'int' type, so, for file-sizes > INT_MAX (2147483647 bytes) there is a limitation if (count > 2147483647) { TRACELOG(LOG_WARNING, "FILEIO: [%s] File is bigger than 2147483647 bytes, avoid using LoadFileData()", fileName); @@ -2456,7 +2456,7 @@ long GetFileModTime(const char *fileName) } // Get pointer to extension for a filename string (includes the dot: .png) -// WARNING: We just get the ptr but not the extension as a separate string +// WARNING: Getting the pointer to the input string extension position (not a string copy) const char *GetFileExtension(const char *fileName) { const char *dot = strrchr(fileName, '.'); @@ -2505,7 +2505,7 @@ const char *GetFileNameWithoutExt(const char *filePath) { if (fileName[i] == '.') { - // NOTE: We break on first '.' found + // NOTE: Break on first '.' found fileName[i] = '\0'; break; } @@ -2531,11 +2531,11 @@ const char *GetDirectoryPath(const char *filePath) static char dirPath[MAX_FILEPATH_LENGTH] = { 0 }; memset(dirPath, 0, MAX_FILEPATH_LENGTH); - // In case provided path does not contain a root drive letter (C:\, D:\) nor leading path separator (\, /), - // we add the current directory path to dirPath + // In case provided path does not contain a root drive letter (C:\, D:\) + // nor leading path separator (\, /), add the current directory path to dirPath if ((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/')) { - // For security, we set starting path to current directory, + // For security, set starting path to current directory, // obtained path will be concatenated to this dirPath[0] = '.'; dirPath[1] = '/'; @@ -2934,7 +2934,7 @@ unsigned int GetDirectoryFileCountEx(const char *basePath, const char *filter, b { while ((entity = readdir(dir)) != NULL) { - // NOTE: We skip '.' (current dir) and '..' (parent dir) filepaths + // NOTE: Skipping '.' (current dir) and '..' (parent dir) filepaths if ((strcmp(entity->d_name, ".") != 0) && (strcmp(entity->d_name, "..") != 0)) { // Construct new path from our base path @@ -3004,7 +3004,7 @@ unsigned char *DecompressData(const unsigned char *compData, int compDataSize, i // WARNING: RL_REALLOC can make (and leave) data copies in memory, // that can be a security concern in case of compression of sensitive data - // So, we use a second buffer to copy data manually, wiping original buffer memory + // So, using a second buffer to copy data manually, wiping original buffer memory data = (unsigned char *)RL_CALLOC(size, 1); memcpy(data, data0, size); memset(data0, 0, MAX_DECOMPRESSION_SIZE*1024*1024); // Wipe memory, is memset() safe? @@ -3534,7 +3534,7 @@ AutomationEventList LoadAutomationEventList(const char *fileName) // Allocate and empty automation event list, ready to record new events list.events = (AutomationEvent *)RL_CALLOC(MAX_AUTOMATION_EVENTS, sizeof(AutomationEvent)); list.capacity = MAX_AUTOMATION_EVENTS; - + if (fileName == NULL) TRACELOG(LOG_INFO, "AUTOMATION: New empty events list loaded successfully"); else { @@ -4578,7 +4578,7 @@ const char *TextFormat(const char *text, ...) #define MAX_TEXT_BUFFER_LENGTH 1024 // Maximum size of static text buffer #endif - // We create an array of buffers so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations + // Define an array of buffers, so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations static char buffers[MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH] = { 0 }; static int index = 0; diff --git a/src/rlgl.h b/src/rlgl.h index 84f37616a..cb92d322f 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2982,7 +2982,7 @@ void rlDrawRenderBatch(rlRenderBatch *batch) { // Activate elements VAO if (RLGL.ExtSupported.vao) glBindVertexArray(batch->vertexBuffer[batch->currentBuffer].vaoId); - + // TODO: If no data changed on the CPU arrays there is no need to re-upload data to GPU, // a flag can be used to detect changes but it would imply keeping a copy buffer and memcmp() both, does it worth it? @@ -3257,6 +3257,7 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, #if defined(GRAPHICS_API_OPENGL_11) if (format >= RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) { + // TODO: Support texture data decompression TRACELOG(RL_LOG_WARNING, "GL: OpenGL 1.1 does not support GPU compressed texture formats"); return id; } @@ -3828,8 +3829,8 @@ unsigned int rlLoadFramebuffer(void) if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return fboId; } #if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) - glGenFramebuffers(1, &fboId); // Create the framebuffer object - glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind any framebuffer + glGenFramebuffers(1, &fboId); // Create the framebuffer object + glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind any framebuffer #endif return fboId; @@ -4744,7 +4745,7 @@ Matrix rlGetMatrixTransform(void) // TODO: Consider possible transform matrices in the RLGL.State.stack //Matrix matStackTransform = rlMatrixIdentity(); //for (int i = RLGL.State.stackCounter; i > 0; i--) matStackTransform = rlMatrixMultiply(RLGL.State.stack[i], matStackTransform); - + mat = RLGL.State.transform; #endif return mat; diff --git a/src/rmodels.c b/src/rmodels.c index 9d83b7b4d..598c84577 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2400,7 +2400,7 @@ void UpdateModelAnimationEx(Model model, ModelAnimation animA, float frameA, Mod Vector3 frameBScale = Vector3Lerp( animB.keyframePoses[currentFrameB][boneIndex].scale, animB.keyframePoses[nextFrameB][boneIndex].scale, blendB); - + // Compute interpolated pose between both animations frames // NOTE: Storing animation frame data in model.currentPose model.currentPose[boneIndex].translation = Vector3Lerp(frameATranslation, frameBTranslation, blend); @@ -2435,20 +2435,20 @@ void UpdateModelAnimationEx(Model model, ModelAnimation animA, float frameA, Mod // Invert bind pose transformation Vector3 invBindTranslation = Vector3RotateByQuaternion( - Vector3Negate(model.skeleton.bindPose[boneIndex].translation), + Vector3Negate(model.skeleton.bindPose[boneIndex].translation), QuaternionInvert(model.skeleton.bindPose[boneIndex].rotation)); Quaternion invBindRotation = QuaternionInvert(model.skeleton.bindPose[boneIndex].rotation); Vector3 invBindScale = Vector3Divide((Vector3){ 1.0f, 1.0f, 1.0f }, model.skeleton.bindPose[boneIndex].scale); Vector3 boneTranslation = Vector3Add(Vector3RotateByQuaternion( - Vector3Multiply(model.currentPose[boneIndex].scale, invBindTranslation), - model.currentPose[boneIndex].rotation), + Vector3Multiply(model.currentPose[boneIndex].scale, invBindTranslation), + model.currentPose[boneIndex].rotation), model.currentPose[boneIndex].translation); Quaternion boneRotation = QuaternionMultiply(model.currentPose[boneIndex].rotation, invBindRotation); Vector3 boneScale = Vector3Multiply(model.currentPose[boneIndex].scale, invBindScale); model.boneMatrices[boneIndex] = MatrixMultiply( - MatrixMultiply(QuaternionToMatrix(boneRotation), + MatrixMultiply(QuaternionToMatrix(boneRotation), MatrixTranslate(boneTranslation.x, boneTranslation.y, boneTranslation.z)), MatrixScale(boneScale.x, boneScale.y, boneScale.z)); */ @@ -2470,14 +2470,14 @@ static void UpdateModelAnimationVertexBuffers(Model model) Vector3 animVertex = { 0 }; Vector3 animNormal = { 0 }; const int vertexValuesCount = mesh.vertexCount*3; - + int boneIndex = 0; int boneCounter = 0; float boneWeight = 0.0f; bool bufferUpdateRequired = false; // Flag to check when anim vertex information is updated // Skip if missing bone data or missing anim buffers initialization - if ((mesh.boneWeights == NULL) || (mesh.boneIndices == NULL) || + if ((mesh.boneWeights == NULL) || (mesh.boneIndices == NULL) || (mesh.animVertices == NULL) || (mesh.animNormals == NULL)) continue; for (int vCounter = 0; vCounter < vertexValuesCount; vCounter += 3) @@ -2534,7 +2534,7 @@ void UnloadModelAnimations(ModelAnimation *animations, int animCount) { for (int a = 0; a < animCount; a++) { - for (int i = 0; i < animations[a].keyframeCount; i++) + for (int i = 0; i < animations[a].keyframeCount; i++) RL_FREE(animations[a].keyframePoses[i]); RL_FREE(animations[a].keyframePoses); @@ -4560,7 +4560,7 @@ static Model LoadOBJ(const char *fileName) model.meshes[i].texcoords = (float *)MemAlloc(sizeof(float)*vertexCount*2); model.meshes[i].colors = (unsigned char *)MemAlloc(sizeof(unsigned char)*vertexCount*4); #else - if (objAttributes.texcoords != NULL && objAttributes.num_texcoords > 0) + if (objAttributes.texcoords != NULL && objAttributes.num_texcoords > 0) model.meshes[i].texcoords = (float *)MemAlloc(sizeof(float)*vertexCount*2); else model.meshes[i].texcoords = NULL; model.meshes[i].colors = NULL; @@ -5159,7 +5159,7 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou //else memcpy(bones[j].name, "ANIMJOINTNAME", 13); // Default bone name otherwise } - for (unsigned int j = 0; j < anim[a].num_frames; j++) + for (unsigned int j = 0; j < anim[a].num_frames; j++) animations[a].keyframePoses[j] = (Transform *)RL_MALLOC(iqmHeader->num_poses*sizeof(Transform)); int dcounter = anim[a].first_frame*iqmHeader->num_framechannels; @@ -6166,9 +6166,9 @@ static Model LoadGLTF(const char *fileName) worldTransform[3], worldTransform[7], worldTransform[11], worldTransform[15] }; - MatrixDecompose(worldMatrix, - &(model.skeleton.bindPose[i].translation), - &(model.skeleton.bindPose[i].rotation), + MatrixDecompose(worldMatrix, + &(model.skeleton.bindPose[i].translation), + &(model.skeleton.bindPose[i].rotation), &(model.skeleton.bindPose[i].scale)); } diff --git a/src/rshapes.c b/src/rshapes.c index a8684e353..91a8ae513 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -2387,11 +2387,11 @@ bool CheckCollisionLines(Vector2 startPos1, Vector2 endPos1, Vector2 startPos2, { collisionPoint->x = startPos1.x + t*rx; collisionPoint->y = startPos1.y + t*ry; - + collision = true; } } - + return collision; } diff --git a/src/rtext.c b/src/rtext.c index 118b79e96..f585e6a2e 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -128,7 +128,7 @@ static Font defaultFont = { 0 }; // Text vertical line spacing in pixels (between lines) -static int textLineSpacing = 2; +static int textLineSpacing = 2; //---------------------------------------------------------------------------------- // Other Modules Functions Declaration (required by text) @@ -729,7 +729,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz { stbtt_GetCodepointHMetrics(&fontInfo, cp, &glyphs[k].advanceX, NULL); glyphs[k].advanceX = (int)((float)glyphs[k].advanceX*scaleFactor); - + Image imSpace = { .data = NULL, .width = glyphs[k].advanceX, @@ -863,7 +863,7 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp int updatedAtlasHeight = atlas.height*2; int updatedAtlasDataSize = atlas.width*updatedAtlasHeight; unsigned char *updatedAtlasData = (unsigned char *)RL_CALLOC(updatedAtlasDataSize, 1); - + memcpy(updatedAtlasData, atlas.data, atlasDataSize); RL_FREE(atlas.data); atlas.data = updatedAtlasData; @@ -1023,7 +1023,7 @@ bool ExportFontAsCode(Font font, const char *fileName) // Get file name from path char fileNamePascal[256] = { 0 }; strncpy(fileNamePascal, TextToPascal(GetFileNameWithoutExt(fileName)), 256 - 1); - + // Get font atlas image and size, required to estimate code file size // NOTE: This mechanism is highly coupled to raylib Image image = LoadImageFromTexture(font.texture); @@ -1032,13 +1032,13 @@ bool ExportFontAsCode(Font font, const char *fileName) // Image data is usually GRAYSCALE + ALPHA and can be reduced to GRAYSCALE //ImageFormat(&image, PIXELFORMAT_UNCOMPRESSED_GRAYSCALE); - + // Estimate text code size // - Image data is stored as "0x%02x", so it requires at least 4 char per byte, let's use 6 // - font.recs[] data is stored as "{ %1.0f, %1.0f, %1.0f , %1.0f }", let's reserve 64 per rec // - font.glyphs[] data is stored as "{ %i, %i, %i, %i, { 0 }},\n", let's reserve 64 per glyph // - Comments and additional code, let's reserve 32KB - int txtDataSize = imageDataSize*6 + font.glyphCount*64 + font.glyphCount*64 + 32768; + int txtDataSize = imageDataSize*6 + font.glyphCount*64 + font.glyphCount*64 + 32768; char *txtData = (char *)RL_CALLOC(txtDataSize, sizeof(char)); int byteCount = 0; @@ -1493,7 +1493,7 @@ unsigned int TextLength(const char *text) unsigned int length = 0; if (text != NULL) - { + { while (text[length] != '\0') length++; } @@ -1707,7 +1707,7 @@ char *TextReplace(const char *text, const char *search, const char *replacement) char *result = NULL; if ((text != NULL) && (search != NULL) && (search[0] != '\0')) - { + { if (replacement == NULL) replacement = ""; char *insertPoint = NULL; // Next insert point @@ -1740,18 +1740,18 @@ char *TextReplace(const char *text, const char *search, const char *replacement) { insertPoint = (char *)strstr(text, search); lastReplacePos = (int)(insertPoint - text); - + memcpy(temp, text, lastReplacePos); temp += lastReplacePos; - + if (replaceLen > 0) { memcpy(temp, replacement, replaceLen); - temp += replaceLen; + temp += replaceLen; } text += (lastReplacePos + searchLen); // Move to next "end of replace" - + count--; } From 1cf278b8b4cd7ce7883453312c069a3381cdeff0 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 3 Mar 2026 22:41:08 +0100 Subject: [PATCH 116/319] Update ROADMAP.md --- ROADMAP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ROADMAP.md b/ROADMAP.md index b9392fa66..9d333089a 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -10,7 +10,7 @@ Here is a wishlist with features and ideas to improve the library. Note that fea - [raylib 5.0 wishlist](https://github.com/raysan5/raylib/discussions/2952) - [raylib wishlist 2022](https://github.com/raysan5/raylib/discussions/2272) - [raylib wishlist 2021](https://github.com/raysan5/raylib/discussions/1502) - + _Current version of raylib is complete and functional but there is always room for improvements._ **raylib 5.x** From 23f86689dcdd3343efd0a04b46f88178e1cfd57d Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 3 Mar 2026 22:41:21 +0100 Subject: [PATCH 117/319] Update CHANGELOG --- CHANGELOG | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 698cc2c80..d78e7ed4f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,7 +7,16 @@ Current Release: raylib 5.5 (18 November 2024) Release: raylib 6.0 (?? March 2026) ------------------------------------------------------------------------- KEY CHANGES: - - TODO... + - New Software Renderer backend [rlsw] + - New rcore platform backend: Memory (memory buffer) + - New rcore platform backend: Win32 (experimental) + - New rcore platform backend: Emscripten (experimental) + - Redesigned Skeletal Animation System + - Redesigned fullscreen modes and high-dpi content scaling + - Redesigned Build Config System [config.h] + - New File System API + - New Text Management API + - New tool: [rexm] raylib examples manager Detailed changes: From d0f899721b9488b8ca2719b196999c816dff2ece Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 4 Mar 2026 00:01:34 +0100 Subject: [PATCH 118/319] Update raudio.c --- src/raudio.c | 53 ++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 888973ce7..691cedf9f 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -947,9 +947,9 @@ Sound LoadSoundFromWave(Wave wave) if (wave.data != NULL) { - // When using miniaudio we need to do our own mixing - // To simplify this we need convert the format of each sound to be consistent with - // the format used to open the playback AUDIO.System.device. We can do this two ways: + // When using miniaudio mixing needs to b done manually + // To simplify this, the format of each sound needs to be converted to be consistent with + // the format used to open the playback AUDIO.System.device. It can be done in two ways: // // 1) Convert the whole sound in one go at load time (here) // 2) Convert the audio data in chunks at mixing time @@ -1394,9 +1394,9 @@ Music LoadMusicStream(const char *fileName) // OGG bit rate defaults to 16 bit, it's enough for compressed format music.stream = LoadAudioStream(info.sample_rate, 16, info.channels); - // WARNING: It seems this function returns length in frames, not samples, so we multiply by channels + // WARNING: It seems this function returns length in frames, not samples, so multiply by channels music.frameCount = (unsigned int)stb_vorbis_stream_length_in_samples((stb_vorbis *)music.ctxData); - music.looping = true; // Looping enabled by default + music.looping = true; // Looping enabled by default musicLoaded = true; } else @@ -1435,8 +1435,8 @@ Music LoadMusicStream(const char *fileName) { music.ctxType = MUSIC_AUDIO_QOA; music.ctxData = ctxQoa; - // NOTE: We are loading samples are 32bit float normalized data, so, - // we configure the output audio stream to also use float 32bit + // NOTE: Loading samples as 32bit float normalized data, so, + // configure the output audio stream to also use float 32bit music.stream = LoadAudioStream(ctxQoa->info.samplerate, 32, ctxQoa->info.channels); music.frameCount = ctxQoa->info.samples; music.looping = true; // Looping enabled by default @@ -1487,7 +1487,7 @@ Music LoadMusicStream(const char *fileName) music.stream = LoadAudioStream(AUDIO.System.device.sampleRate, bits, AUDIO_DEVICE_CHANNELS); music.frameCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm); // NOTE: Always 2 channels (stereo) music.looping = true; // Looping enabled by default - jar_xm_reset(ctxXm); // Make sure we start at the beginning of the song + jar_xm_reset(ctxXm); // Make sure to start at the beginning of the song musicLoaded = true; } else @@ -1588,7 +1588,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, // OGG bit rate defaults to 16 bit, it's enough for compressed format music.stream = LoadAudioStream(info.sample_rate, 16, info.channels); - // WARNING: It seems this function returns length in frames, not samples, so we multiply by channels + // WARNING: It seems this function returns length in frames, not samples, so multiply by channels music.frameCount = (unsigned int)stb_vorbis_stream_length_in_samples((stb_vorbis *)music.ctxData); music.looping = true; // Looping enabled by default musicLoaded = true; @@ -1634,8 +1634,9 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, { music.ctxType = MUSIC_AUDIO_QOA; music.ctxData = ctxQoa; - // NOTE: We are loading samples are 32bit float normalized data, so, - // we configure the output audio stream to also use float 32bit + + // NOTE: Loading samples are 32bit float normalized data, so, + // configure the output audio stream to also use float 32bit music.stream = LoadAudioStream(ctxQoa->info.samplerate, 32, ctxQoa->info.channels); music.frameCount = ctxQoa->info.samples; music.looping = true; // Looping enabled by default @@ -1685,7 +1686,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, music.stream = LoadAudioStream(AUDIO.System.device.sampleRate, bits, 2); music.frameCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm); // NOTE: Always 2 channels (stereo) music.looping = true; // Looping enabled by default - jar_xm_reset(ctxXm); // Make sure we start at the beginning of the song + jar_xm_reset(ctxXm); // Make sure to start at the beginning of the song musicLoaded = true; } @@ -1870,7 +1871,7 @@ void SeekMusicStream(Music music, float position) int qoaFrame = positionInFrames/QOA_FRAME_LEN; qoaplay_seek_frame((qoaplay_desc *)music.ctxData, qoaFrame); // Seeks to QOA frame, not PCM frame - // We need to compute QOA frame number and update positionInFrames + // Compute QOA frame number and update positionInFrames positionInFrames = ((qoaplay_desc *)music.ctxData)->sample_position; } break; #endif @@ -1897,7 +1898,7 @@ void UpdateMusicStream(Music music) unsigned int subBufferSizeInFrames = music.stream.buffer->sizeInFrames/2; - // On first call of this function we lazily pre-allocated a temp buffer to read audio files/memory data in + // On first call of this function, lazily pre-allocated a temp buffer to read audio files/memory data in int frameSize = music.stream.channels*music.stream.sampleSize/8; unsigned int pcmSize = subBufferSizeInFrames*frameSize; @@ -2024,7 +2025,7 @@ void UpdateMusicStream(Music music) #if SUPPORT_FILEFORMAT_XM case MUSIC_MODULE_XM: { - // NOTE: Internally we consider 2 channels generation, so sampleCount/2 + // NOTE: Internally considering 2 channels generation, so sampleCount/2 if (AUDIO_DEVICE_FORMAT == ma_format_f32) jar_xm_generate_samples((jar_xm_context_t *)music.ctxData, (float *)AUDIO.System.pcmBuffer, framesToStream); else if (AUDIO_DEVICE_FORMAT == ma_format_s16) jar_xm_generate_samples_16bit((jar_xm_context_t *)music.ctxData, (short *)AUDIO.System.pcmBuffer, framesToStream); else if (AUDIO_DEVICE_FORMAT == ma_format_u8) jar_xm_generate_samples_8bit((jar_xm_context_t *)music.ctxData, (char *)AUDIO.System.pcmBuffer, framesToStream); @@ -2262,7 +2263,7 @@ void SetAudioStreamCallback(AudioStream stream, AudioCallback callback) // Add processor to audio stream. Contrary to buffers, the order of processors is important // The new processor must be added at the end. As there aren't supposed to be a lot of processors attached to -// a given stream, we iterate through the list to find the end. That way we don't need a pointer to the last element +// a given stream, iterate through the list to find the end. That way there is no need to keep a pointer to the last element void AttachAudioStreamProcessor(AudioStream stream, AudioCallback process) { ma_mutex_lock(&AUDIO.System.lock); @@ -2396,20 +2397,20 @@ static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, if (currentSubBufferIndex > 1) return 0; // Another thread can update the processed state of buffers, so - // we just take a copy here to try and avoid potential synchronization problems + // just take a copy here to try and avoid potential synchronization problems bool isSubBufferProcessed[2] = { 0 }; isSubBufferProcessed[0] = audioBuffer->isSubBufferProcessed[0]; isSubBufferProcessed[1] = audioBuffer->isSubBufferProcessed[1]; ma_uint32 frameSizeInBytes = ma_get_bytes_per_frame(audioBuffer->converter.formatIn, audioBuffer->converter.channelsIn); - // Fill out every frame until we find a buffer that's marked as processed. Then fill the remainder with 0 + // Fill out every frame until a buffer that's marked as processed is found, then fill the remainder with 0 ma_uint32 framesRead = 0; while (1) { - // We break from this loop differently depending on the buffer's usage - // - For static buffers, we simply fill as much data as we can - // - For streaming buffers we only fill half of the buffer that are processed + // Break from this loop differently depending on the buffer's usage + // - For static buffers, simply fill as much data as possible + // - For streaming buffers, only fill half of the buffer that are processed // Unprocessed halves must keep their audio data in-tact if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC) { @@ -2464,8 +2465,8 @@ static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, { memset((unsigned char *)framesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes); - // For static buffers we can fill the remaining frames with silence for safety, but we don't want - // to report those frames as "read". The reason for this is that the caller uses the return value + // For static buffers, fill the remaining frames with silence for safety, but don't report those + // frames as "read"; The reason for this is that the caller uses the return value // to know whether a non-looping sound has finished playback if (audioBuffer->usage != AUDIO_BUFFER_USAGE_STATIC) framesRead += totalFramesRemaining; } @@ -2558,7 +2559,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const { (void)pDevice; - // Mixing is basically just an accumulation, we need to initialize the output buffer to 0 + // Mixing is basically just an accumulation, need to initialize the output buffer to 0 memset(pFramesOut, 0, frameCount*pDevice->playback.channels*ma_get_bytes_per_sample(pDevice->playback.format)); // Using a mutex here for thread-safety which makes things not real-time @@ -2576,7 +2577,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const { if (framesRead >= frameCount) break; - // Just read as much data as we can from the stream + // Just read as much data as possible from the stream ma_uint32 framesToRead = (frameCount - framesRead); while (framesToRead > 0) @@ -2615,7 +2616,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const break; } - // If we weren't able to read all the frames we requested, break + // If all the frames requested can't be read, break if (framesJustRead < framesToReadRightNow) { if (!audioBuffer->looping) From 5ada84cc6d69c720210ff9ead4d2091ca06123c4 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 4 Mar 2026 00:13:47 +0100 Subject: [PATCH 119/319] Update rtext.c --- src/rtext.c | 63 +++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index f585e6a2e..0a59e3538 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -156,17 +156,18 @@ extern void LoadFontDefault(void) { #define BIT_CHECK(a,b) ((a) & (1u << (b))) - // Check to see if we have already allocated the font for an image, and if we don't need to upload, then just return + // Check to see if the font for an image has alreeady been allocated, + // and if no need to upload, then just return if (defaultFont.glyphs != NULL) return; // NOTE: Using UTF-8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement // REF: http://www.utf8-chartable.de/unicode-utf8-table.pl - defaultFont.glyphCount = 224; // Number of glyphs included in our default font - defaultFont.glyphPadding = 0; // Characters padding + defaultFont.glyphCount = 224; // Number of glyphs included in our default font + defaultFont.glyphPadding = 0; // Characters padding // Default font is directly defined here (data generated from a sprite font image) - // This way, we reconstruct Font without creating large global variables + // This way, reconstructing Font without creating large global variables // This data is automatically allocated to Stack and automatically deallocated at the end of this function unsigned int defaultFontData[512] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00200020, 0x0001b000, 0x00000000, 0x00000000, 0x8ef92520, 0x00020a00, 0x7dbe8000, 0x1f7df45f, @@ -214,7 +215,7 @@ extern void LoadFontDefault(void) 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; int charsHeight = 10; - int charsDivisor = 1; // Every char is separated from the consecutive by a 1 pixel divisor, horizontally and vertically + int charsDivisor = 1; // Every char is separated from the consecutive by a 1 pixel divisor, horizontally and vertically int charsWidth[224] = { 3, 1, 4, 6, 5, 7, 6, 2, 3, 3, 5, 5, 2, 4, 1, 7, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 3, 4, 3, 6, 7, 6, 6, 6, 6, 6, 6, 6, 6, 3, 5, 6, 5, 7, 6, 6, 6, 6, 6, 6, 7, 6, 7, 7, 6, 6, 6, 2, 7, 2, 3, 5, @@ -241,8 +242,8 @@ extern void LoadFontDefault(void) { if (BIT_CHECK(defaultFontData[counter], j)) { - // NOTE: We are unreferencing data as short, so, - // we must consider data as little-endian order (alpha + gray) + // NOTE: Unreferencing data as short, so, + // considering data as little-endian (alpha + gray) ((unsigned short *)imFont.data)[i + j] = 0xffff; } else @@ -257,8 +258,8 @@ extern void LoadFontDefault(void) 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 + // Check again if font glyph data has been already loaded + // to avoid reallocating the glyphs and rects if (defaultFont.glyphs != NULL) { UnloadImage(imFont); @@ -267,7 +268,6 @@ extern void LoadFontDefault(void) // Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, glyphCount //------------------------------------------------------------------------------ - // Allocate space for our characters info data // NOTE: This memory must be freed at end! --> Done by CloseWindow() defaultFont.glyphs = (GlyphInfo *)RL_CALLOC(defaultFont.glyphCount, sizeof(GlyphInfo)); @@ -374,7 +374,7 @@ Font LoadFont(const char *fileName) if (font.texture.id == 0) TRACELOG(LOG_WARNING, "FONT: [%s] Failed to load font texture -> Using default font", fileName); else { - SetTextureFilter(font.texture, TEXTURE_FILTER_POINT); // By default, we set point filter (the best performance) + SetTextureFilter(font.texture, TEXTURE_FILTER_POINT); // By default, set point filter (the best performance) TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", font.baseSize, font.glyphCount); } @@ -420,8 +420,8 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) int x = 0; int y = 0; - // We allocate a temporal arrays for glyphs data measures, - // once we get the actual number of glyphs, we copy data to a sized arrays + // Allocate a temporal arrays for glyphs data measures, + // once the actual number of glyphs is obtained, copy data to a sized array int tempCharValues[MAX_GLYPHS_FROM_IMAGE] = { 0 }; Rectangle tempCharRecs[MAX_GLYPHS_FROM_IMAGE] = { 0 }; @@ -482,7 +482,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) xPosToRead = charSpacing; } - // NOTE: We need to remove key color borders from image to avoid weird + // NOTE: Key color borders need to be removed from image to avoid weird // artifacts on texture scaling when using TEXTURE_FILTER_BILINEAR or TEXTURE_FILTER_TRILINEAR for (int i = 0; i < image.height*image.width; i++) if (COLOR_EQUAL(pixels[i], key)) pixels[i] = BLANK; @@ -500,8 +500,8 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) font.glyphCount = index; font.glyphPadding = 0; - // We got tempCharValues and tempCharsRecs populated with glyphs data - // Now we move temp data to sized charValues and charRecs arrays + // Populate tempCharValues and tempCharsRecs with glyphs data + // Move temp data to sized charValues and charRecs arrays font.glyphs = (GlyphInfo *)RL_MALLOC(font.glyphCount*sizeof(GlyphInfo)); font.recs = (Rectangle *)RL_MALLOC(font.glyphCount*sizeof(Rectangle)); @@ -627,9 +627,10 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz { bool genFontChars = false; stbtt_fontinfo fontInfo = { 0 }; - int *requiredCodepoints = (int *)codepoints; // TODO: Should we create a shallow copy to avoid "dealing" with a const user array? + // TODO: Should a shallow copy be created to avoid "dealing" with a const user array? + int *requiredCodepoints = (int *)codepoints; - if (stbtt_InitFont(&fontInfo, (unsigned char *)fileData, 0)) // Initialize font for data reading + if (stbtt_InitFont(&fontInfo, (unsigned char *)fileData, 0)) // Initialize font for data reading { // Calculate font scale factor float scaleFactor = stbtt_ScaleForPixelHeight(&fontInfo, (float)fontSize); @@ -645,7 +646,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz codepointCount = (codepointCount > 0)? codepointCount : 95; // Fill fontChars in case not provided externally - // NOTE: By default we fill glyphCount consecutively, starting at 32 (Space) + // NOTE: By default filling glyphCount consecutively, starting at 32 (Space) if (requiredCodepoints == NULL) { requiredCodepoints = (int *)RL_MALLOC(codepointCount*sizeof(int)); @@ -723,7 +724,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz } //else TRACELOG(LOG_WARNING, "FONT: Glyph [0x%08x] has no image data available", cp); // Only reported for 0x20 and 0x3000 - // We create an empty image for Space character (0x20), useful for sprite font generation + // Create an empty image for Space character (0x20), useful for sprite font generation // NOTE: Another space to consider: 0x3000 (CJK - Ideographic Space) if ((cp == 0x20) || (cp == 0x3000)) { @@ -793,7 +794,7 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp *glyphRecs = NULL; - // In case no chars count provided we suppose default of 95 + // In case no chars count provided, suppose default of 95 glyphCount = (glyphCount > 0)? glyphCount : 95; // NOTE: Rectangles memory is loaded here! @@ -834,7 +835,7 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; atlas.mipmaps = 1; - // DEBUG: We can see padding in the generated image setting a gray background... + // DEBUG: View padding in the generated image setting a gray background... //for (int i = 0; i < atlas.width*atlas.height; i++) ((unsigned char *)atlas.data)[i] = 100; if (packMethod == 0) // Use basic packing algorithm @@ -1261,14 +1262,14 @@ void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSiz float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor // Character destination rectangle on screen - // NOTE: We consider glyphPadding on drawing + // NOTE: Considering glyph padding on drawing Rectangle dstRec = { position.x + font.glyphs[index].offsetX*scaleFactor - (float)font.glyphPadding*scaleFactor, position.y + font.glyphs[index].offsetY*scaleFactor - (float)font.glyphPadding*scaleFactor, (font.recs[index].width + 2.0f*font.glyphPadding)*scaleFactor, (font.recs[index].height + 2.0f*font.glyphPadding)*scaleFactor }; // Character source rectangle from font texture atlas - // NOTE: We consider glyphs padding when drawing, it could be required for outline/glow shader effects + // NOTE: Considering glyphs padding when drawing, it could be required for outline/glow shader effects Rectangle srcRec = { font.recs[index].x - (float)font.glyphPadding, font.recs[index].y - (float)font.glyphPadding, font.recs[index].width + 2.0f*font.glyphPadding, font.recs[index].height + 2.0f*font.glyphPadding }; @@ -1508,7 +1509,7 @@ const char *TextFormat(const char *text, ...) #define MAX_TEXTFORMAT_BUFFERS 4 // Maximum number of static buffers for text formatting #endif - // We create an array of buffers so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations + // Create an array of buffers so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations static char buffers[MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH] = { 0 }; static int index = 0; @@ -1878,7 +1879,7 @@ char **TextSplit(const char *text, char delimiter, int *count) { counter = 1; - // Count how many substrings we have on text and point to every one + // Count how many substrings ar found on text and set pointers to every one for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++) { buffer[i] = text[i]; @@ -2058,7 +2059,7 @@ char *LoadUTF8(const int *codepoints, int length) if ((codepoints != NULL) && (length > 0)) { - // We allocate enough memory to fit all possible codepoints + // Allocate enough memory to fit all possible codepoints // NOTE: 5 bytes for every codepoint should be enough text = (char *)RL_CALLOC(length*5, 1); const char *utf8 = NULL; @@ -2186,7 +2187,7 @@ const char *CodepointToUTF8(int codepoint, int *utf8Size) } // Get next codepoint in a UTF-8 encoded text, scanning until '\0' is found -// When an invalid UTF-8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned +// When an invalid UTF-8 byte is encountered, exit as soon as possible and a '?'(0x3f) codepoint is returned // Total number of bytes processed are returned as a parameter // NOTE: The standard says U+FFFD should be returned in case of errors // but that character is not supported by the default font in raylib @@ -2209,7 +2210,7 @@ int GetCodepoint(const char *text, int *codepointSize) *codepointSize = 1; if (text == NULL) return codepoint; - // NOTE: on decode errors we return as soon as possible + // NOTE: On decoding errors, return as soon as possible int octet = (unsigned char)(text[0]); // The first UTF8 octet if (octet <= 0x7f) @@ -2403,7 +2404,7 @@ static Font LoadBMFont(const char *fileName) char *fileTextPtr = fileText; - // NOTE: We skip first line, it contains no useful information + // NOTE: Skip first line, it contains no useful information readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); fileTextPtr += (readBytes + 1); @@ -2606,7 +2607,7 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, c if (codepoints == NULL) { // Fill internal codepoints array in case not provided externally - // NOTE: By default we fill glyphCount consecutively, starting at 32 (Space) + // NOTE: By default, filling glyph count consecutively, starting at 32 (Space) for (int i = 0; i < codepointCount; i++) requiredCodepoints[i] = i + 32; internalCodepoints = true; } From 23c06bc6f19bee2e1cb77d0f959157f8befdfc23 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 4 Mar 2026 00:22:50 +0100 Subject: [PATCH 120/319] Updating raygui for examples --- examples/core/raygui.h | 17 +++++++++++++---- examples/shaders/raygui.h | 17 +++++++++++++---- examples/shapes/raygui.h | 17 +++++++++++++---- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/examples/core/raygui.h b/examples/core/raygui.h index 42c5ae7bc..03e487912 100644 --- a/examples/core/raygui.h +++ b/examples/core/raygui.h @@ -646,6 +646,7 @@ typedef enum { // ProgressBar typedef enum { PROGRESS_PADDING = 16, // ProgressBar internal padding + PROGRESS_SIDE, // ProgressBar increment side: 0-left->right, 1-right-left } GuiProgressBarProperty; // ScrollBar @@ -3522,7 +3523,15 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight } // Draw slider internal progress bar (depends on state) - GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED))); + if (GuiGetStyle(PROGRESSBAR, PROGRESS_SIDE) == 0) // Left-->Right + { + GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED))); + } + else // Right-->Left + { + progress.x = bounds.x + bounds.width - progress.width - GuiGetStyle(PROGRESSBAR, BORDER_WIDTH); + GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED))); + } } // Draw left/right text if provided @@ -5119,11 +5128,11 @@ static const char *GetTextIcon(const char *text, int *iconId) // Get text divided into lines (by line-breaks '\n') // WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator! -static char **GetTextLines(const char *text, int *count) +static const char **GetTextLines(const char *text, int *count) { #define RAYGUI_MAX_TEXT_LINES 128 - static char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; + static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings int textLength = (int)strlen(text); @@ -5193,7 +5202,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C // WARNING: GuiTextSplit() function can't be used now because it can have already been used // before the GuiDrawText() call and its buffer is static, it would be overriden :( int lineCount = 0; - char **lines = GetTextLines(text, &lineCount); + const char **lines = GetTextLines(text, &lineCount); // Text style variables //int alignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT); diff --git a/examples/shaders/raygui.h b/examples/shaders/raygui.h index 42c5ae7bc..03e487912 100644 --- a/examples/shaders/raygui.h +++ b/examples/shaders/raygui.h @@ -646,6 +646,7 @@ typedef enum { // ProgressBar typedef enum { PROGRESS_PADDING = 16, // ProgressBar internal padding + PROGRESS_SIDE, // ProgressBar increment side: 0-left->right, 1-right-left } GuiProgressBarProperty; // ScrollBar @@ -3522,7 +3523,15 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight } // Draw slider internal progress bar (depends on state) - GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED))); + if (GuiGetStyle(PROGRESSBAR, PROGRESS_SIDE) == 0) // Left-->Right + { + GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED))); + } + else // Right-->Left + { + progress.x = bounds.x + bounds.width - progress.width - GuiGetStyle(PROGRESSBAR, BORDER_WIDTH); + GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED))); + } } // Draw left/right text if provided @@ -5119,11 +5128,11 @@ static const char *GetTextIcon(const char *text, int *iconId) // Get text divided into lines (by line-breaks '\n') // WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator! -static char **GetTextLines(const char *text, int *count) +static const char **GetTextLines(const char *text, int *count) { #define RAYGUI_MAX_TEXT_LINES 128 - static char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; + static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings int textLength = (int)strlen(text); @@ -5193,7 +5202,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C // WARNING: GuiTextSplit() function can't be used now because it can have already been used // before the GuiDrawText() call and its buffer is static, it would be overriden :( int lineCount = 0; - char **lines = GetTextLines(text, &lineCount); + const char **lines = GetTextLines(text, &lineCount); // Text style variables //int alignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT); diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h index 42c5ae7bc..03e487912 100644 --- a/examples/shapes/raygui.h +++ b/examples/shapes/raygui.h @@ -646,6 +646,7 @@ typedef enum { // ProgressBar typedef enum { PROGRESS_PADDING = 16, // ProgressBar internal padding + PROGRESS_SIDE, // ProgressBar increment side: 0-left->right, 1-right-left } GuiProgressBarProperty; // ScrollBar @@ -3522,7 +3523,15 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight } // Draw slider internal progress bar (depends on state) - GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED))); + if (GuiGetStyle(PROGRESSBAR, PROGRESS_SIDE) == 0) // Left-->Right + { + GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED))); + } + else // Right-->Left + { + progress.x = bounds.x + bounds.width - progress.width - GuiGetStyle(PROGRESSBAR, BORDER_WIDTH); + GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED))); + } } // Draw left/right text if provided @@ -5119,11 +5128,11 @@ static const char *GetTextIcon(const char *text, int *iconId) // Get text divided into lines (by line-breaks '\n') // WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator! -static char **GetTextLines(const char *text, int *count) +static const char **GetTextLines(const char *text, int *count) { #define RAYGUI_MAX_TEXT_LINES 128 - static char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; + static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings int textLength = (int)strlen(text); @@ -5193,7 +5202,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C // WARNING: GuiTextSplit() function can't be used now because it can have already been used // before the GuiDrawText() call and its buffer is static, it would be overriden :( int lineCount = 0; - char **lines = GetTextLines(text, &lineCount); + const char **lines = GetTextLines(text, &lineCount); // Text style variables //int alignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT); From faf42366ec554c929118b39b1b612f9925f50a02 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 4 Mar 2026 01:14:26 +0100 Subject: [PATCH 121/319] Code gardening --- src/config.h | 6 +- src/external/rlsw.h | 2 +- src/external/win32_clipboard.h | 2 +- src/platforms/rcore_android.c | 8 +- src/platforms/rcore_desktop_glfw.c | 6 +- src/platforms/rcore_desktop_rgfw.c | 2 +- src/platforms/rcore_desktop_sdl.c | 6 +- src/platforms/rcore_desktop_win32.c | 2 +- src/platforms/rcore_drm.c | 69 ++++++++--------- src/platforms/rcore_memory.c | 2 +- src/platforms/rcore_template.c | 2 +- src/platforms/rcore_web.c | 38 +++++---- src/platforms/rcore_web_emscripten.c | 38 +++++---- src/raudio.c | 64 +++++++-------- src/raylib.h | 6 +- src/raymath.h | 36 +++------ src/rcamera.h | 3 +- src/rcore.c | 2 +- src/rlgl.h | 12 +-- src/rmodels.c | 112 +++++++++++++-------------- src/rshapes.c | 2 +- src/rtext.c | 4 +- src/rtextures.c | 12 +-- 23 files changed, 213 insertions(+), 223 deletions(-) diff --git a/src/config.h b/src/config.h index 4dab32c36..a3502d772 100644 --- a/src/config.h +++ b/src/config.h @@ -126,7 +126,7 @@ // rcore: Configuration values // NOTE: Below values are alread defined inside [rcore.c] so there is no need to be -// redefined here, in case it must be done, just uncomment the required line and update +// redefined here, in case it must be done, uncomment the required line and update // the value; it can also be done on compilation with -DVALUE_TO_REDEFINE=128 //------------------------------------------------------------------------------------ //#define MAX_TRACELOG_MSG_LENGTH 256 // Max length of one trace-log message @@ -160,7 +160,7 @@ // rlgl: Configuration values // NOTE: Below values are alread defined inside [rlgl.h] so there is no need to be -// redefined here, in case it must be done, just uncomment the required line and update +// redefined here, in case it must be done, uncomment the required line and update // the value; it can also be done on compilation with -DVALUE_TO_REDEFINE=128 //------------------------------------------------------------------------------------ //#define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 4096 // Default internal render batch elements limits @@ -350,7 +350,7 @@ // raudio: Configuration values // NOTE: Below values are alread defined inside [rlgl.h] so there is no need to be -// redefined here, in case it must be done, just uncomment the required line and update +// redefined here, in case it must be done, uncomment the required line and update // the value; it can also be done on compilation with -DVALUE_TO_REDEFINE=128 //------------------------------------------------------------------------------------ //#define AUDIO_DEVICE_FORMAT ma_format_f32 // Device output format (miniaudio: float-32bit) diff --git a/src/external/rlsw.h b/src/external/rlsw.h index 37d8e98b1..746dd84a8 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -48,7 +48,7 @@ * recommended under specific situations and only if the developers know * what are they doing; this flag is not defined by default * -* rlsw capabilities could be customized just defining some internal +* rlsw capabilities could be customized defining some internal * values before library inclusion (default values listed): * * #define SW_GL_FRAMEBUFFER_COPY_BGRA true diff --git a/src/external/win32_clipboard.h b/src/external/win32_clipboard.h index 3beb4c8b1..5c30fbd2e 100644 --- a/src/external/win32_clipboard.h +++ b/src/external/win32_clipboard.h @@ -14,7 +14,7 @@ unsigned char *Win32GetClipboardImageData(int *width, int *height, unsigned long #include // NOTE: These search for architecture is taken from "windows.h", and it's necessary to avoid including windows.h -// and still make it compile on msvc, because import indirectly importing "winnt.h" (e.g. ) can cause problems is these are not defined. +// and still make it compile on msvc, because import indirectly importing "winnt.h" (e.g. ) can cause problems is these are not defined #if !defined(_X86_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_IX86) #define _X86_ #if !defined(_CHPE_X86_ARM64_) && defined(_M_HYBRID) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 9cf3b543b..0833214e7 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -715,7 +715,7 @@ void PollInputEvents(void) { #if SUPPORT_GESTURES_SYSTEM // NOTE: Gestures update must be called every frame to reset gestures correctly - // because ProcessGestureEvent() is just called on an event, not every frame + // because ProcessGestureEvent() is called on an event, not every frame UpdateGestures(); #endif @@ -1055,7 +1055,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) InitGraphicsDevice(); // Initialize OpenGL context (states and resources) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, stored as globals in rlgl rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); // Setup default viewport @@ -1299,7 +1299,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) } else if ((keycode == AKEYCODE_BACK) || (keycode == AKEYCODE_MENU)) { - // Eat BACK_BUTTON and AKEYCODE_MENU, just do nothing... and don't let to be handled by OS! + // Eat BACK_BUTTON and AKEYCODE_MENU, do nothing... and don't let to be handled by OS! return 1; } else if ((keycode == AKEYCODE_VOLUME_UP) || (keycode == AKEYCODE_VOLUME_DOWN)) @@ -1547,7 +1547,7 @@ FILE *__wrap_fopen(const char *fileName, const char *mode) } else { - // Just do a regular open if file is not found in the assets + // Do a regular open if file is not found in the assets file = __real_fopen(TextFormat("%s/%s", platform.app->activity->internalDataPath, fileName), mode); if (file == NULL) file = __real_fopen(fileName, mode); } diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 08e26ee03..e47cad440 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -837,7 +837,7 @@ int GetCurrentMonitor(void) { // In case the window is between two monitors, below logic is used // to try to detect the "current monitor" for that window, note that - // this is probably an overengineered solution for a very side case + // this is probably an overengineered solution for a side case // trying to match SDL behaviour int closestDist = 0x7FFFFFFF; @@ -1258,7 +1258,7 @@ void PollInputEvents(void) { #if SUPPORT_GESTURES_SYSTEM // NOTE: Gestures update must be called every frame to reset gestures correctly - // because ProcessGestureEvent() is just called on an event, not every frame + // because ProcessGestureEvent() is called on an event, not every frame UpdateGestures(); #endif @@ -1588,7 +1588,7 @@ int InitPlatform(void) } // 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 + // Forcing this initialization here avoids doing it on PollInputEvents() called by EndDrawing() after first frame has been 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); diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 316a8abbb..935b3fbb2 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -1198,7 +1198,7 @@ void PollInputEvents(void) { #if SUPPORT_GESTURES_SYSTEM // NOTE: Gestures update must be called every frame to reset gestures correctly - // because ProcessGestureEvent() is just called on an event, not every frame + // because ProcessGestureEvent() is called on an event, not every frame UpdateGestures(); #endif diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 06c33026f..9dbd0b873 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -49,7 +49,7 @@ #define USING_SDL3_PROJECT #endif #ifndef SDL_ENABLE_OLD_NAMES - #define SDL_ENABLE_OLD_NAMES // Just in case on SDL3, some in-between compatibily is needed + #define SDL_ENABLE_OLD_NAMES // In case on SDL3, some in-between compatibily is needed #endif // SDL base library (window/rendered, input, timing... functionality) #ifdef USING_SDL3_PROJECT @@ -1365,7 +1365,7 @@ void PollInputEvents(void) { #if SUPPORT_GESTURES_SYSTEM // NOTE: Gestures update must be called every frame to reset gestures correctly - // because ProcessGestureEvent() is just called on an event, not every frame + // because ProcessGestureEvent() is called on an event, not every frame UpdateGestures(); #endif @@ -1482,7 +1482,7 @@ void PollInputEvents(void) #ifndef USING_VERSION_SDL3 // 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 + // In general, handling this change means checking for the individual events instead of first checking for SDL_WINDOWEVENT // and then checking for window events; Events >= SDL_EVENT_WINDOW_FIRST and <= SDL_EVENT_WINDOW_LAST can be compared // to see whether it's a window event case SDL_WINDOWEVENT: diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index 89ae22ec0..a56a7f090 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -2198,7 +2198,7 @@ static unsigned SanitizeFlags(int mode, unsigned flags) // retry loop that continues until either the desired state is reached or the state stops changing static void UpdateFlags(HWND hwnd, unsigned desiredFlags, int width, int height) { - // Flags that just apply immediately without needing any operations + // Flags that apply immediately without needing any operations CORE.Window.flags |= (desiredFlags & FLAG_MASK_NO_UPDATE); int vsync = (desiredFlags & FLAG_VSYNC_HINT)? 1 : 0; diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 43ffac8d1..a32934aa9 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -183,14 +183,14 @@ static const int evkeyToUnicodeLUT[] = { 0, 27, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 8, 0, 113, 119, 101, 114, 116, 121, 117, 105, 111, 112, 0, 0, 13, 0, 97, 115, 100, 102, 103, 104, 106, 107, 108, 59, 39, 96, 0, 92, 122, 120, 99, 118, 98, 110, 109, 44, 46, 47, 0, 0, 0, 32 - // LUT currently incomplete, just mapped the most essential keys + // LUT currently incomplete, only mapped the most essential keys }; // This is the map used to map any keycode returned from linux to a raylib code from 'raylib.h' // NOTE: Use short here to save a little memory static const short linuxToRaylibMap[KEYMAP_SIZE] = { - // We don't map those with designated initialization, because we would getting - // into loads of naming conflicts + // Don't map with designated initialization, + // it will geenrate many naming conflicts 0, 256, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 259, 258, 81, 87, 69, 82, 84, 89, 85, 73, @@ -764,9 +764,9 @@ 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 + // This is fully asynchronous and when the page-flip happens, the DRM-fd will become readable and drmHandleEvent() can be called + // This will read all vblank/page-flip events and call our modeset_page_flip_event() callback with the data-pointer passed to drmModePageFlip() + // 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 @@ -1067,7 +1067,7 @@ void PollInputEvents(void) { #if SUPPORT_GESTURES_SYSTEM // NOTE: Gestures update must be called every frame to reset gestures correctly - // because ProcessGestureEvent() is just called on an event, not every frame + // because ProcessGestureEvent() is called on an event, not every frame UpdateGestures(); #endif @@ -1089,7 +1089,7 @@ void PollInputEvents(void) PollKeyboardEvents(); #if 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 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 @@ -1225,7 +1225,7 @@ int InitPlatform(void) if (((con->connection == DRM_MODE_CONNECTED) || (con->connection == DRM_MODE_UNKNOWNCONNECTION)) && (con->count_modes > 0)) { #if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) - // For hardware rendering, we need an encoder_id + // For hardware rendering, an encoder_id is needed if (con->encoder_id) { TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i connected with encoder", i); @@ -1234,7 +1234,7 @@ int InitPlatform(void) } else TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i connected but no encoder", i); #else - // For software rendering, we can accept even without encoder_id + // For software rendering, accept even without encoder_id TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i suitable for software rendering", i); platform.connector = con; break; @@ -1534,7 +1534,7 @@ int InitPlatform(void) return -1; } - // At this point we need to manage render size vs screen size + // At this point, manage render size vs screen size // NOTE: This function use and modify global module variables: // -> CORE.Window.screen.width/CORE.Window.screen.height // -> CORE.Window.render.width/CORE.Window.render.height @@ -1572,7 +1572,7 @@ int InitPlatform(void) // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(eglGetProcAddress); #else - // At this point we need to manage render size vs screen size + // At this point, manage render size vs screen size // NOTE: This function use and modify global module variables: // -> CORE.Window.screen.width/CORE.Window.screen.height // -> CORE.Window.render.width/CORE.Window.render.height @@ -1596,7 +1596,7 @@ int InitPlatform(void) if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) MinimizeWindow(); - // If graphic device is no properly initialized, we end program + // If graphic device is no properly initialized, end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); @@ -1745,7 +1745,7 @@ void ClosePlatform(void) // Initialize Keyboard system (using standard input) static void InitKeyboard(void) { - // NOTE: We read directly from Standard Input (stdin) - STDIN_FILENO file descriptor, + // NOTE: Read directly from Standard Input (stdin) - STDIN_FILENO file descriptor, // Reading directly from stdin will give chars already key-mapped by kernel to ASCII or UNICODE // Save terminal keyboard settings @@ -1978,8 +1978,8 @@ 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 - // It could be many things, even as simple as a power button... + // At this point, a connection to the device has been stablished, but still left to know what the device is, + // it could be many things, even as simple as a power button... //------------------------------------------------------------------------------------------------------- // Identify the device @@ -1990,8 +1990,8 @@ static void ConfigureEvdevDevice(char *device) } absinfo[ABS_CNT] = { 0 }; // These flags aren't really a one of - // Some devices could have properties we assosciate with keyboards as well as properties - // we assosciate with mice + // Some devices could have properties associated with keyboards + // as well as properties associated with mice bool isKeyboard = false; bool isMouse = false; bool isTouch = false; @@ -2027,11 +2027,10 @@ static void ConfigureEvdevDevice(char *device) TEST_BIT(keyBits, BTN_TOOL_FINGER) || TEST_BIT(keyBits, BTN_TOUCH))) isTouch = true; - // Absolute mice should really only exist with VMWare, but it shouldn't - // matter if we support them + // Absolute mice should really only exist with VMWare else if (hasAbsXY && TEST_BIT(keyBits, BTN_MOUSE)) isMouse = true; - // If any of the common joystick axes are present, we assume it's a gamepad + // If any of the common joystick axes are present, assume it's a gamepad else { for (int axis = (hasAbsXY? ABS_Z : ABS_X); axis < ABS_PRESSURE; axis++) @@ -2056,7 +2055,7 @@ static void ConfigureEvdevDevice(char *device) { ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits); - // If it has any of the gamepad or touch features we tested so far, it's not a mouse + // If it has any of the gamepad or touch features tested so far, it's not a mouse if (!isTouch && !isGamepad && TEST_BIT(relBits, REL_X) && @@ -2067,12 +2066,12 @@ static void ConfigureEvdevDevice(char *device) if (TEST_BIT(evBits, EV_KEY)) { // The first 32 keys as defined in input-event-codes.h are pretty much - // exclusive to keyboards, so we can test them using a mask + // exclusive to keyboards, so they can be tested using a mask // Leave out the first bit to not test KEY_RESERVED const unsigned long mask = 0xFFFFFFFE; if ((keyBits[0] & mask) == mask) isKeyboard = true; - // If we find any of the common gamepad buttons we assume it's a gamepad + // If any of the common gamepad buttons is found, assume it's a gamepad else { for (int button = BTN_JOYSTICK; button < BTN_DIGI; ++button) @@ -2149,7 +2148,7 @@ static void ConfigureEvdevDevice(char *device) if (absAxisCount > 0) { // TODO: Review GamepadAxis enum matching - // So gamepad axes (as in the actual linux joydev.c) are just simply enumerated + // So gamepad axes (as in the actual linux joydev.c) are 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 // If these are now enumerated, it results to LJOY_X, LJOY_Y, LEFT_SHOULDERB, RJOY_X, ... @@ -2197,7 +2196,7 @@ static void PollKeyboardEvents(void) if (event.type != EV_KEY) continue; #if SUPPORT_SSH_KEYBOARD_RPI - // If the event was a key, we know a working keyboard is connected, so disable the SSH keyboard + // If the event was a key, assume a working keyboard is connected, so disable the SSH keyboard platform.eventKeyboardMode = true; #endif // Keyboard keys appear for codes 1 to 255, ignore everthing else @@ -2206,7 +2205,7 @@ static void PollKeyboardEvents(void) // Lookup the scancode in the keymap to get a keycode keycode = linuxToRaylibMap[event.code]; - // Make sure we got a valid keycode + // Make sure a valid keycode is obtained if ((keycode > 0) && (keycode < MAX_KEYBOARD_KEYS)) { // WARNING: https://www.kernel.org/doc/Documentation/input/input.txt @@ -2380,8 +2379,8 @@ static void PollMouseEvents(void) { platform.touchPosition[platform.touchSlot].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; - // If this slot is active, it's a move. If not, we are just updating the buffer for when it becomes active. - // Only set to MOVE if we haven't already detected a DOWN or UP event this frame + // If this slot is active, it's a move; If not, update the buffer for when it becomes active + // Only set to MOVE if a DOWN or UP event has not been detected this frame if (platform.touchActive[platform.touchSlot] && touchAction == -1) touchAction = 2; // TOUCH_ACTION_MOVE } } @@ -2392,8 +2391,8 @@ static void PollMouseEvents(void) { platform.touchPosition[platform.touchSlot].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; - // If this slot is active, it's a move. If not, we are just updating the buffer for when it becomes active. - // Only set to MOVE if we haven't already detected a DOWN or UP event this frame + // If this slot is active, it's a move; If not, update the buffer for when it becomes active + // Only set to MOVE if a DOWN or UP event have not been detected this frame if (platform.touchActive[platform.touchSlot] && touchAction == -1) touchAction = 2; // TOUCH_ACTION_MOVE } } @@ -2418,7 +2417,7 @@ static void PollMouseEvents(void) platform.touchPosition[platform.touchSlot].y = -1; platform.touchId[platform.touchSlot] = -1; - // Force UP action if we haven't already set a DOWN action + // Force UP action if DOWN action has not already been set // (DOWN takes priority over UP if both happen in one frame, though rare) if (touchAction != 1) touchAction = 0; // TOUCH_ACTION_UP } @@ -2665,7 +2664,7 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt // NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified static void SetupFramebuffer(int width, int height) { - // Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var) + // Calculate CORE.Window.render.width and CORE.Window.render.height, using the display size (input params) and the desired screen size (global var) if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height)) { TRACELOG(LOG_WARNING, "DISPLAY: Downscaling required: Screen size (%ix%i) is bigger than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height); @@ -2693,8 +2692,8 @@ static void SetupFramebuffer(int width, int height) float scaleRatio = (float)CORE.Window.render.width/(float)CORE.Window.screen.width; CORE.Window.screenScale = MatrixScale(scaleRatio, scaleRatio, 1.0f); - // NOTE: We render to full display resolution! - // We just need to calculate above parameters for downscale matrix and offsets + // NOTE: Rendering to full display resolution, + // calculate above parameters for downscale matrix and offsets CORE.Window.render.width = CORE.Window.display.width; CORE.Window.render.height = CORE.Window.display.height; diff --git a/src/platforms/rcore_memory.c b/src/platforms/rcore_memory.c index 05579513f..322841439 100644 --- a/src/platforms/rcore_memory.c +++ b/src/platforms/rcore_memory.c @@ -437,7 +437,7 @@ void PollInputEvents(void) { #if SUPPORT_GESTURES_SYSTEM // NOTE: Gestures update must be called every frame to reset gestures correctly - // because ProcessGestureEvent() is just called on an event, not every frame + // because ProcessGestureEvent() is called on an event, not every frame UpdateGestures(); #endif diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 758c20179..40a1922e7 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -407,7 +407,7 @@ void PollInputEvents(void) { #if SUPPORT_GESTURES_SYSTEM // NOTE: Gestures update must be called every frame to reset gestures correctly - // because ProcessGestureEvent() is just called on an event, not every frame + // because ProcessGestureEvent() is called on an event, not every frame UpdateGestures(); #endif diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index fc9f79966..da7b2713d 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -803,17 +803,20 @@ void SetClipboardText(const char *text) // Async EM_JS to be able to await clickboard read asynchronous function EM_ASYNC_JS(void, RequestClipboardData, (void), { - if (navigator.clipboard && window.isSecureContext) { + if (navigator.clipboard && window.isSecureContext) + { let items = await navigator.clipboard.read(); - for (const item of items) { - + for (const item of items) + { // Check if this item contains plain text or image - if (item.types.includes("text/plain")) { + if (item.types.includes("text/plain")) + { const blob = await item.getType("text/plain"); const text = await blob.text(); window._lastClipboardString = text; } - else if (item.types.find(t => t.startsWith("image/"))) { + else if (item.types.find(t => t.startsWith("image/"))) + { const blob = await item.getType(item.types.find(t => t.startsWith("image/"))); const bitmap = await createImageBitmap(blob); @@ -831,16 +834,16 @@ EM_ASYNC_JS(void, RequestClipboardData, (void), { window._lastImgData = imgData; } } - } else { - console.warn("Clipboard read() requires HTTPS/Localhost"); - } + } + else console.warn("Clipboard read() requires HTTPS/Localhost"); }); // Returns the string created by RequestClipboardData from JS memory to Emscripten C memory -EM_JS(char*, GetLastPastedText, (void), { +EM_JS(char *, GetLastPastedText, (void), { var str = window._lastClipboardString || ""; var length = lengthBytesUTF8(str) + 1; - if (length > 1) { + if (length > 1) + { var ptr = _malloc(length); stringToUTF8(str, ptr, length); return ptr; @@ -849,10 +852,12 @@ EM_JS(char*, GetLastPastedText, (void), { }); // Returns the image created by RequestClipboardData from JS memory to Emscripten C memory -EM_JS(unsigned char*, GetLastPastedImage, (int* width, int* height), { - if (window._lastImgData) { +EM_JS(unsigned char *, GetLastPastedImage, (int *width, int *height), { + if (window._lastImgData) + { const data = window._lastImgData; - if (data.length > 0) { + if (data.length > 0) + { const ptr = _malloc(data.length); HEAPU8.set(data, ptr); @@ -861,12 +866,13 @@ EM_JS(unsigned char*, GetLastPastedImage, (int* width, int* height), { if (width) setValue(width, window._lastImgWidth, 'i32'); if (height) setValue(height, window._lastImgHeight, 'i32'); - // Clear the JS buffer so we don't fetch the same image twice + // Clear the JS buffer so there is no need to fetch the same image twice window._lastImgData = null; - return ptr; + return ptr; } } + return 0; }); @@ -1072,7 +1078,7 @@ void PollInputEvents(void) { #if SUPPORT_GESTURES_SYSTEM // NOTE: Gestures update must be called every frame to reset gestures correctly - // because ProcessGestureEvent() is just called on an event, not every frame + // because ProcessGestureEvent() is called on an event, not every frame UpdateGestures(); #endif diff --git a/src/platforms/rcore_web_emscripten.c b/src/platforms/rcore_web_emscripten.c index dcee45865..05da0da2e 100644 --- a/src/platforms/rcore_web_emscripten.c +++ b/src/platforms/rcore_web_emscripten.c @@ -781,17 +781,20 @@ void SetClipboardText(const char *text) // Async EM_JS to be able to await clickboard read asynchronous function EM_ASYNC_JS(void, RequestClipboardData, (void), { - if (navigator.clipboard && window.isSecureContext) { + if (navigator.clipboard && window.isSecureContext) + { let items = await navigator.clipboard.read(); - for (const item of items) { - + for (const item of items) + { // Check if this item contains plain text or image - if (item.types.includes("text/plain")) { + if (item.types.includes("text/plain")) + { const blob = await item.getType("text/plain"); const text = await blob.text(); window._lastClipboardString = text; } - else if (item.types.find(t => t.startsWith("image/"))) { + else if (item.types.find(t => t.startsWith("image/"))) + { const blob = await item.getType(item.types.find(t => t.startsWith("image/"))); const bitmap = await createImageBitmap(blob); @@ -809,16 +812,16 @@ EM_ASYNC_JS(void, RequestClipboardData, (void), { window._lastImgData = imgData; } } - } else { - console.warn("Clipboard read() requires HTTPS/Localhost"); - } + } + else console.warn("Clipboard read() requires HTTPS/Localhost"); }); // Returns the string created by RequestClipboardData from JS memory to Emscripten C memory -EM_JS(char*, GetLastPastedText, (void), { +EM_JS(char *, GetLastPastedText, (void), { var str = window._lastClipboardString || ""; var length = lengthBytesUTF8(str) + 1; - if (length > 1) { + if (length > 1) + { var ptr = _malloc(length); stringToUTF8(str, ptr, length); return ptr; @@ -827,10 +830,12 @@ EM_JS(char*, GetLastPastedText, (void), { }); // Returns the image created by RequestClipboardData from JS memory to Emscripten C memory -EM_JS(unsigned char*, GetLastPastedImage, (int* width, int* height), { - if (window._lastImgData) { +EM_JS(unsigned char *, GetLastPastedImage, (int *width, int *height), { + if (window._lastImgData) + { const data = window._lastImgData; - if (data.length > 0) { + if (data.length > 0) + { const ptr = _malloc(data.length); HEAPU8.set(data, ptr); @@ -839,12 +844,13 @@ EM_JS(unsigned char*, GetLastPastedImage, (int* width, int* height), { if (width) setValue(width, window._lastImgWidth, 'i32'); if (height) setValue(height, window._lastImgHeight, 'i32'); - // Clear the JS buffer so we don't fetch the same image twice + // Clear the JS buffer so there is no need to fetch the same image twice window._lastImgData = null; - return ptr; + return ptr; } } + return 0; }); @@ -1046,7 +1052,7 @@ void PollInputEvents(void) { #if SUPPORT_GESTURES_SYSTEM // NOTE: Gestures update must be called every frame to reset gestures correctly - // because ProcessGestureEvent() is just called on an event, not every frame + // because ProcessGestureEvent() is called on an event, not every frame UpdateGestures(); #endif diff --git a/src/raudio.c b/src/raudio.c index 691cedf9f..7020f6d53 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -26,7 +26,7 @@ * #define SUPPORT_FILEFORMAT_XM * #define SUPPORT_FILEFORMAT_MOD * Selected desired fileformats to be supported for loading. Some of those formats are -* supported by default, to remove support, just comment unrequired #define in this module +* supported by default, to remove support, comment unrequired #define in this module * * DEPENDENCIES: * miniaudio.h - Audio device management lib (https://github.com/mackron/miniaudio) @@ -299,7 +299,7 @@ typedef struct tagBITMAPINFOHEADER { #endif #ifndef AUDIO_BUFFER_RESIDUAL_CAPACITY - #define AUDIO_BUFFER_RESIDUAL_CAPACITY 8 // In PCM frames. For resampling and pitch shifting. + #define AUDIO_BUFFER_RESIDUAL_CAPACITY 8 // In PCM frames, for resampling and pitch shifting #endif //---------------------------------------------------------------------------------- @@ -406,7 +406,7 @@ static AudioData AUDIO = { // Global AUDIO context // NOTE: Music buffer size is defined by number of samples, independent of sample size and channels number // After some math, considering a sampleRate of 48000, a buffer refill rate of 1/60 seconds and a // standard double-buffering system, a 4096 samples buffer has been chosen, it should be enough - // In case of music-stalls, just increase this number + // In case of music-stalls, increase this number .Buffer.defaultSize = 0, .mixedProcessor = NULL }; @@ -601,7 +601,7 @@ AudioBuffer *LoadAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sam // be consumed. Any residual input frames need to be kept track of to // ensure there are no discontinuities. Since raylib supports pitch // shifting, which is done through resampling, a cache will always be - // required. This will be kept relatively small to avoid too much wastage. + // required. This will be kept relatively small to avoid too much wastage audioBuffer->converterResidualCount = 0; audioBuffer->converterResidual = (unsigned char*)RL_CALLOC(AUDIO_BUFFER_RESIDUAL_CAPACITY*ma_get_bytes_per_frame(format, channels), 1); @@ -721,7 +721,7 @@ void SetAudioBufferPitch(AudioBuffer *buffer, float pitch) if ((buffer != NULL) && (pitch > 0.0f)) { ma_mutex_lock(&AUDIO.System.lock); - // Pitching is just an adjustment of the sample rate + // Pitching is an adjustment of the sample rate // Note that this changes the duration of the sound: // - higher pitches will make the sound faster // - lower pitches make it slower @@ -1046,7 +1046,7 @@ void UnloadSound(Sound sound) void UnloadSoundAlias(Sound alias) { - // Untrack and unload just the sound buffer, not the sample data, it is shared with the source for the alias + // Untrack and unload the sound buffer, not the sample data, it is shared with the source for the alias if (alias.stream.buffer != NULL) { UntrackAudioBuffer(alias.stream.buffer); @@ -2316,7 +2316,7 @@ void DetachAudioStreamProcessor(AudioStream stream, AudioCallback process) // Add processor to audio pipeline. Order of processors is important // Works the same way as {Attach,Detach}AudioStreamProcessor() functions, except -// these two work on the already mixed output just before sending it to the sound hardware +// these two work on the already mixed output before sending it to the sound hardware void AttachAudioMixedProcessor(AudioCallback process) { ma_mutex_lock(&AUDIO.System.lock); @@ -2397,7 +2397,7 @@ static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, if (currentSubBufferIndex > 1) return 0; // Another thread can update the processed state of buffers, so - // just take a copy here to try and avoid potential synchronization problems + // take a copy here to try and avoid potential synchronization problems bool isSubBufferProcessed[2] = { 0 }; isSubBufferProcessed[0] = audioBuffer->isSubBufferProcessed[0]; isSubBufferProcessed[1] = audioBuffer->isSubBufferProcessed[1]; @@ -2478,8 +2478,8 @@ static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, float *framesOut, ma_uint32 frameCount) { // NOTE: Continuously converting data from the AudioBuffer's internal format to the mixing format, - // which should be defined by the output format of the data converter. - // This is done until frameCount frames have been output. + // which should be defined by the output format of the data converter + // This is done until frameCount frames have been output ma_uint32 bpf = ma_get_bytes_per_frame(audioBuffer->converter.formatIn, audioBuffer->converter.channelsIn); ma_uint8 inputBuffer[4096] = { 0 }; ma_uint32 inputBufferFrameCap = sizeof(inputBuffer)/bpf; @@ -2491,34 +2491,34 @@ static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, f ma_uint64 outputFramesToProcessThisIteration = frameCount - totalOutputFramesProcessed; //ma_uint64 inputFramesToProcessThisIteration = 0; - // Process any residual input frames from the previous read first. + // Process any residual input frames from the previous read first if (audioBuffer->converterResidualCount > 0) { ma_uint64 inputFramesProcessedThisIteration = audioBuffer->converterResidualCount; ma_uint64 outputFramesProcessedThisIteration = outputFramesToProcessThisIteration; ma_data_converter_process_pcm_frames(&audioBuffer->converter, audioBuffer->converterResidual, &inputFramesProcessedThisIteration, runningFramesOut, &outputFramesProcessedThisIteration); - // Make sure the data in the cache is consumed. This can be optimized to use a cursor instead of a memmove(). - memmove(audioBuffer->converterResidual, audioBuffer->converterResidual + inputFramesProcessedThisIteration*bpf, (size_t)(AUDIO_BUFFER_RESIDUAL_CAPACITY - inputFramesProcessedThisIteration) * bpf); + // Make sure the data in the cache is consumed, this can be optimized to use a cursor instead of a memmove() + memmove(audioBuffer->converterResidual, audioBuffer->converterResidual + inputFramesProcessedThisIteration*bpf, (size_t)(AUDIO_BUFFER_RESIDUAL_CAPACITY - inputFramesProcessedThisIteration)*bpf); audioBuffer->converterResidualCount -= (ma_uint32)inputFramesProcessedThisIteration; // Safe cast totalOutputFramesProcessed += (ma_uint32)outputFramesProcessedThisIteration; // Safe cast } else { - // Getting here means there are no residual frames from the previous read. Fresh data can now be - // pulled from the AudioBuffer and processed. + // Getting here means there are no residual frames from the previous read + // Fresh data can now be pulled from the AudioBuffer and processed // - // A best guess needs to be used made to determine how many input frames to pull from the - // buffer. There are three possible outcomes: 1) exact; 2) underestimated; 3) overestimated. + // A best guess needs to be used made to determine how many input frames to pull from the buffer + // There are three possible outcomes: 1) exact; 2) underestimated; 3) overestimated // - // When the guess is exactly correct or underestimated there is nothing special to handle - it'll be - // handled naturally by the loop. + // When the guess is exactly correct or underestimated there is nothing special to handle, + // it'll be handled naturally by the loop // - // When the guess is overestimated, that's when it gets more complicated. In this case, any overflow - // needs to be stored in a buffer for later processing by the next read. - ma_uint32 estimatedInputFrameCount = (ma_uint32)(((float)audioBuffer->converter.resampler.sampleRateIn / audioBuffer->converter.resampler.sampleRateOut) * outputFramesToProcessThisIteration); - if (estimatedInputFrameCount == 0) estimatedInputFrameCount = 1; // Make sure at least one input frame is read. + // When the guess is overestimated, that's when it gets more complicated + // In this case, any overflow needs to be stored in a buffer for later processing by the next read + ma_uint32 estimatedInputFrameCount = (ma_uint32)(((float)audioBuffer->converter.resampler.sampleRateIn / audioBuffer->converter.resampler.sampleRateOut)*outputFramesToProcessThisIteration); + if (estimatedInputFrameCount == 0) estimatedInputFrameCount = 1; // Make sure at least one input frame is read if (estimatedInputFrameCount > inputBufferFrameCap) estimatedInputFrameCount = inputBufferFrameCap; ma_uint32 inputFramesInInternalFormatCount = ReadAudioBufferFramesInInternalFormat(audioBuffer, inputBuffer, estimatedInputFrameCount); @@ -2531,17 +2531,17 @@ static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, f if (inputFramesInInternalFormatCount > inputFramesProcessedThisIteration) { - // Getting here means the estimated input frame count was overestimated. The residual needs - // be stored for later use. + // Getting here means the estimated input frame count was overestimated + // The residual needs be stored for later use ma_uint64 residualFrameCount = inputFramesInInternalFormatCount - inputFramesProcessedThisIteration; - // A safety check to make sure the capacity of the residual cache is not exceeded. + // A safety check to make sure the capacity of the residual cache is not exceeded if (residualFrameCount > AUDIO_BUFFER_RESIDUAL_CAPACITY) { residualFrameCount = AUDIO_BUFFER_RESIDUAL_CAPACITY; } - memcpy(audioBuffer->converterResidual, inputBuffer + inputFramesProcessedThisIteration*bpf, (size_t)(residualFrameCount * bpf)); + memcpy(audioBuffer->converterResidual, inputBuffer + inputFramesProcessedThisIteration*bpf, (size_t)(residualFrameCount*bpf)); audioBuffer->converterResidualCount = (unsigned int)residualFrameCount; } @@ -2559,7 +2559,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const { (void)pDevice; - // Mixing is basically just an accumulation, need to initialize the output buffer to 0 + // Mixing is basically an accumulation, need to initialize the output buffer to 0 memset(pFramesOut, 0, frameCount*pDevice->playback.channels*ma_get_bytes_per_sample(pDevice->playback.format)); // Using a mutex here for thread-safety which makes things not real-time @@ -2577,7 +2577,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const { if (framesRead >= frameCount) break; - // Just read as much data as possible from the stream + // Read as much data as possible from the stream ma_uint32 framesToRead = (frameCount - framesRead); while (framesToRead > 0) @@ -2626,7 +2626,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const } else { - // Should never get here, but just for safety, + // Should never get here, but for safety, // move the cursor position back to the start and continue the loop audioBuffer->frameCursorPos = 0; continue; @@ -2651,7 +2651,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const ma_mutex_unlock(&AUDIO.System.lock); } -// Main mixing function, pretty simple in this project, just an accumulation +// Main mixing function, pretty simple in this project, only an accumulation // NOTE: framesOut is both an input and an output, it is initially filled with zeros outside of this function static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 frameCount, AudioBuffer *buffer) { @@ -2739,7 +2739,7 @@ static void UpdateAudioStreamInLockedState(AudioStream stream, const void *data, } else { - // Just update whichever sub-buffer is processed + // Update whichever sub-buffer is processed subBufferToUpdate = (stream.buffer->isSubBufferProcessed[0])? 0 : 1; } diff --git a/src/raylib.h b/src/raylib.h index d996e298b..c8c5c85a5 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -159,7 +159,7 @@ // NOTE: Set some defines with some data types declared by raylib // Other modules (raymath, rlgl) also require some of those types, so, // to be able to use those other modules as standalone (not depending on raylib) -// this defines are very useful for internal check and avoid type (re)definitions +// this defines are useful for internal check and avoid type (re)definitions #define RL_COLOR_TYPE #define RL_RECTANGLE_TYPE #define RL_VECTOR2_TYPE @@ -733,7 +733,7 @@ typedef enum { // Gamepad buttons typedef enum { - GAMEPAD_BUTTON_UNKNOWN = 0, // Unknown button, just for error checking + GAMEPAD_BUTTON_UNKNOWN = 0, // Unknown button, for error checking GAMEPAD_BUTTON_LEFT_FACE_UP, // Gamepad left DPAD up button GAMEPAD_BUTTON_LEFT_FACE_RIGHT, // Gamepad left DPAD right button GAMEPAD_BUTTON_LEFT_FACE_DOWN, // Gamepad left DPAD down button @@ -878,7 +878,7 @@ typedef enum { // NOTE 1: Filtering considers mipmaps if available in the texture // NOTE 2: Filter is accordingly set for minification and magnification typedef enum { - TEXTURE_FILTER_POINT = 0, // No filter, just pixel approximation + TEXTURE_FILTER_POINT = 0, // No filter, pixel approximation TEXTURE_FILTER_BILINEAR, // Linear filtering TEXTURE_FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps) TEXTURE_FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x diff --git a/src/raymath.h b/src/raymath.h index 1ea0df225..8a0cce1a5 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -567,15 +567,9 @@ RMAPI Vector2 Vector2ClampValue(Vector2 v, float min, float max) { length = sqrtf(length); - float scale = 1; // By default, 1 as the neutral element. - if (length < min) - { - scale = min/length; - } - else if (length > max) - { - scale = max/length; - } + float scale = 1; // By default, 1 as the neutral element + if (length < min) scale = min/length; + else if (length > max) scale = max/length; result.x = v.x*scale; result.y = v.y*scale; @@ -1215,15 +1209,9 @@ RMAPI Vector3 Vector3ClampValue(Vector3 v, float min, float max) { length = sqrtf(length); - float scale = 1; // By default, 1 as the neutral element. - if (length < min) - { - scale = min/length; - } - else if (length > max) - { - scale = max/length; - } + float scale = 1; // By default, 1 as the neutral element + if (length < min) scale = min/length; + else if (length > max) scale = max/length; result.x = v.x*scale; result.y = v.y*scale; @@ -2574,8 +2562,8 @@ RMAPI void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle } else { - // This occurs when the angle is zero. - // Not a problem: just set an arbitrary normalized axis. + // This occurs when the angle is zero + // Not a problem, set an arbitrary normalized axis resAxis.x = 1.0f; } @@ -2702,10 +2690,10 @@ RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotatio translation->y = mat.m13; translation->z = mat.m14; - // Matrix Columns - Rotation will be extracted into here. - Vector3 matColumns[3] = { { mat.m0, mat.m4, mat.m8 }, + // Matrix Columns - Rotation will be extracted into here + Vector3 matColumns[3] = {{ mat.m0, mat.m4, mat.m8 }, { mat.m1, mat.m5, mat.m9 }, - { mat.m2, mat.m6, mat.m10 } }; + { mat.m2, mat.m6, mat.m10 }}; // Shear Parameters XY, XZ, and YZ (extract and ignored) float shear[3] = { 0 }; @@ -2756,7 +2744,7 @@ RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotatio shear[2] /= scl.z; // Correct YZ shear } - // matColumns are now orthonormal in O(3). Now ensure its in SO(3) by enforcing det = 1. + // matColumns are now orthonormal in O(3). Now ensure its in SO(3) by enforcing det = 1 if (Vector3DotProduct(matColumns[0], Vector3CrossProduct(matColumns[1], matColumns[2])) < 0) { scl = Vector3Negate(scl); diff --git a/src/rcamera.h b/src/rcamera.h index 21ca51a22..14616a771 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -365,7 +365,7 @@ void CameraPitch(Camera *camera, float angle, bool lockView, bool rotateAroundTa if (lockView) { // In these camera modes, clamp the Pitch angle - // to allow only viewing straight up or down. + // to allow only viewing straight up or down // Clamp view up float maxAngleUp = Vector3Angle(up, targetPosition); @@ -460,7 +460,6 @@ void UpdateCamera(Camera *camera, int mode) if (mode == CAMERA_CUSTOM) {} else if (mode == CAMERA_ORBITAL) { - // Orbital can just orbit Matrix rotation = MatrixRotate(GetCameraUp(camera), cameraOrbitalSpeed); Vector3 view = Vector3Subtract(camera->position, camera->target); view = Vector3Transform(view, rotation); diff --git a/src/rcore.c b/src/rcore.c index c286a389f..2e0958f35 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1841,7 +1841,7 @@ void TakeScreenshot(const char *fileName) // Setup window configuration flags (view FLAGS) // NOTE: This function is expected to be called before window creation, // because it sets up some flags for the window creation process -// To configure window states after creation, just use SetWindowState() +// To configure window states after creation, use SetWindowState() void SetConfigFlags(unsigned int flags) { if (CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetConfigFlags called after window initialization, Use \"SetWindowState\" to set flags instead"); diff --git a/src/rlgl.h b/src/rlgl.h index cb92d322f..d2f65700a 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -43,7 +43,7 @@ * #define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT * Enable debug context (only available on OpenGL 4.3) * -* rlgl capabilities could be customized just defining some internal +* rlgl capabilities could be customized defining some internal * values before library inclusion (default values listed): * * #define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 8192 // Default internal render batch elements limits @@ -396,7 +396,7 @@ typedef struct rlVertexBuffer { // Draw call type // NOTE: Only texture changes register a new draw, other state-change-related elements are not -// used at this moment (vaoId, shaderId, matrices), raylib just forces a batch draw call if any +// used at this moment (vaoId, shaderId, matrices), raylib forces a batch draw call if any // of those state-change happens (this is done in core module) typedef struct rlDrawCall { int mode; // Drawing mode: LINES, TRIANGLES, QUADS @@ -478,7 +478,7 @@ typedef enum { // NOTE 1: Filtering considers mipmaps if available in the texture // NOTE 2: Filter is accordingly set for minification and magnification typedef enum { - RL_TEXTURE_FILTER_POINT = 0, // No filter, just pixel approximation + RL_TEXTURE_FILTER_POINT = 0, // No filter, pixel approximation RL_TEXTURE_FILTER_BILINEAR, // Linear filtering RL_TEXTURE_FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps) RL_TEXTURE_FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x @@ -3356,7 +3356,7 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, } // Texture parameters configuration - // NOTE: glTexParameteri does NOT affect texture uploading, just the way it's used + // NOTE: glTexParameteri does NOT affect texture uploading #if defined(GRAPHICS_API_OPENGL_ES2) // NOTE: OpenGL ES 2.0 with no GL_OES_texture_npot support (i.e. WebGL) has limited NPOT support, so CLAMP_TO_EDGE must be used if (RLGL.ExtSupported.texNPOT) @@ -3741,7 +3741,7 @@ void *rlReadTexturePixels(unsigned int id, int width, int height, int format) // Two possible Options: // 1 - Bind texture to color fbo attachment and glReadPixels() // 2 - Create an fbo, activate it, render quad with texture, glReadPixels() - // Using Option 1, just need to care for texture format on retrieval + // Using Option 1, care for texture format on retrieval // NOTE: This behaviour could be conditioned by graphic driver... unsigned int fboId = rlLoadFramebuffer(); @@ -4199,7 +4199,7 @@ unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode) if (fsCode != NULL) fragmentShaderId = rlCompileShader(fsCode, GL_FRAGMENT_SHADER); else fragmentShaderId = RLGL.State.defaultFShaderId; - // In case vertex and fragment shader are the default ones, no need to recompile, just assign the default shader program id + // In case vertex and fragment shader are the default ones, no need to recompile, assign the default shader program id if ((vertexShaderId == RLGL.State.defaultVShaderId) && (fragmentShaderId == RLGL.State.defaultFShaderId)) id = RLGL.State.defaultShaderId; else if ((vertexShaderId > 0) && (fragmentShaderId > 0)) { diff --git a/src/rmodels.c b/src/rmodels.c index 598c84577..1463d50ba 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -792,13 +792,9 @@ void DrawCapsule(Vector3 startPos, Vector3 endPos, float radius, int slices, int { for (int j = 0; j < slices; j++) { + // Building up the rings from capCenter in the direction of the 'direction' vector computed earlier - // we build up the rings from capCenter in the direction of the 'direction' vector we computed earlier - - // as we iterate through the rings they must be placed higher above the center, the height we need is sin(angle(i)) - // as we iterate through the rings they must get smaller by the cos(angle(i)) - - // compute the four vertices + // Compute the four vertices float ringSin1 = sinf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 0 )); float ringCos1 = cosf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 0 )); Vector3 w1 = (Vector3){ @@ -935,13 +931,9 @@ void DrawCapsuleWires(Vector3 startPos, Vector3 endPos, float radius, int slices { for (int j = 0; j < slices; j++) { + // Building up the rings from capCenter in the direction of the 'direction' vector computed earlier - // we build up the rings from capCenter in the direction of the 'direction' vector we computed earlier - - // as we iterate through the rings they must be placed higher above the center, the height we need is sin(angle(i)) - // as we iterate through the rings they must get smaller by the cos(angle(i)) - - // compute the four vertices + // Compute the four vertices float ringSin1 = sinf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 0 )); float ringCos1 = cosf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 0 )); Vector3 w1 = (Vector3){ @@ -1147,7 +1139,7 @@ Model LoadModel(const char *fileName) // Load model from generated mesh // WARNING: A shallow copy of mesh is generated, passed by value, -// as long as struct contains pointers to data and some values, we get a copy +// as long as struct contains pointers to data and some values, get a copy // of mesh pointing to same data as original version... be careful! Model LoadModelFromMesh(Mesh mesh) { @@ -1194,7 +1186,7 @@ bool IsModelValid(Model model) if ((model.meshes[i].boneIndices != NULL) && (model.meshes[i].vboId[7] == 0)) { result = false; break; } // Vertex boneIndices buffer not uploaded to GPU if ((model.meshes[i].boneWeights != NULL) && (model.meshes[i].vboId[8] == 0)) { result = false; break; } // Vertex boneWeights buffer not uploaded to GPU - // NOTE: Some OpenGL versions do not support VAO, so we don't check it + // NOTE: Some OpenGL versions do not support VAO, so avoid below check //if (model.meshes[i].vaoId == 0) { result = false; break } } @@ -1211,7 +1203,7 @@ void UnloadModel(Model model) // Unload materials maps // NOTE: As the user could be sharing shaders and textures between models, - // we don't unload the material but just free its maps, + // don't unload the material but free its maps, // the user is responsible for freeing models shaders and textures for (int i = 0; i < model.materialCount; i++) RL_FREE(model.materials[i].maps); @@ -1510,8 +1502,8 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) } // Get a copy of current matrices to work with, - // just in case stereo render is required, and we need to modify them - // NOTE: At this point the modelview matrix just contains the view matrix (camera) + // in case stereo render is required, and they need to be modified + // NOTE: At this point the modelview matrix contains the view matrix (camera) // That's because BeginMode3D() sets it and there is no model-drawing function // that modifies it, all use rlPushMatrix() and rlPopMatrix() Matrix matModel = MatrixIdentity(); @@ -1729,8 +1721,8 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i } // Get a copy of current matrices to work with, - // just in case stereo render is required, and we need to modify them - // NOTE: At this point the modelview matrix just contains the view matrix (camera) + // in case stereo render is required, and they need to be modified + // NOTE: At this point the modelview matrix contains the view matrix (camera) // That's because BeginMode3D() sets it and there is no model-drawing function // that modifies it, all use rlPushMatrix() and rlPopMatrix() Matrix matModel = MatrixIdentity(); @@ -1753,7 +1745,7 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i // This could alternatively use a static VBO and either glMapBuffer() or glBufferSubData() // It isn't clear which would be reliably faster in all cases and on all platforms, - // anecdotally glMapBuffer() seems very slow (syncs) while glBufferSubData() seems + // anecdotally glMapBuffer() seems quite slow (syncs) while glBufferSubData() seems // no faster, since all the transform matrices are transferred anyway instancesVboId = rlLoadVertexBuffer(instanceTransform, instances*sizeof(float16), false); @@ -2508,7 +2500,7 @@ static void UpdateModelAnimationVertexBuffers(Model model) bufferUpdateRequired = true; // Normals processing - // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals) + // NOTE: Using meshes.baseNormals (default normal) to calculate meshes.normals (animated normals) if ((mesh.normals != NULL) && (mesh.animNormals != NULL )) { animNormal = (Vector3){ mesh.normals[vCounter], mesh.normals[vCounter + 1], mesh.normals[vCounter + 2] }; @@ -3375,7 +3367,7 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) Vector2 *mapTexcoords = (Vector2 *)RL_MALLOC(maxTriangles*3*sizeof(Vector2)); Vector3 *mapNormals = (Vector3 *)RL_MALLOC(maxTriangles*3*sizeof(Vector3)); - // Define the 6 normals of the cube, we will combine them accordingly later... + // Define the 6 normals of the cube, combined accordingly later Vector3 n1 = { 1.0f, 0.0f, 0.0f }; Vector3 n2 = { -1.0f, 0.0f, 0.0f }; Vector3 n3 = { 0.0f, 1.0f, 0.0f }; @@ -3383,7 +3375,8 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) Vector3 n5 = { 0.0f, 0.0f, -1.0f }; Vector3 n6 = { 0.0f, 0.0f, 1.0f }; - // NOTE: We use texture rectangles to define different textures for top-bottom-front-back-right-left (6) + // NOTE: Using texture rectangles to define different + // textures for top-bottom-front-back-right-left (6) typedef struct RectangleF { float x; float y; @@ -3402,7 +3395,7 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) { for (int x = 0; x < cubicmap.width; x++) { - // Define the 8 vertex of the cube, we will combine them accordingly later... + // Define the 8 vertex of the cube, to be combined accordingly later Vector3 v1 = { w*(x - 0.5f), h2, h*(z - 0.5f) }; Vector3 v2 = { w*(x - 0.5f), h2, h*(z + 0.5f) }; Vector3 v3 = { w*(x + 0.5f), h2, h*(z + 0.5f) }; @@ -3412,7 +3405,7 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) Vector3 v7 = { w*(x - 0.5f), 0, h*(z + 0.5f) }; Vector3 v8 = { w*(x + 0.5f), 0, h*(z + 0.5f) }; - // We check pixel color to be WHITE -> draw full cube + // Check pixel color to be WHITE -> draw full cube if (COLOR_EQUAL(pixels[z*cubicmap.width + x], WHITE)) { // Define triangles and checking collateral cubes @@ -3589,7 +3582,7 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) tcCounter += 6; } } - // We check pixel color to be BLACK, we will only draw floor and roof + // Check pixel color to be BLACK, in that case only drawing floor and roof else if (COLOR_EQUAL(pixels[z*cubicmap.width + x], BLACK)) { // Define top triangles (2 tris, 6 vertex --> v1-v2-v3, v1-v3-v4) @@ -4106,8 +4099,8 @@ bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, floa { bool collision = false; - // Simple way to check for collision, just checking distance between two points - // Unfortunately, sqrtf() is a costly operation, so we avoid it with following solution + // Simple way to check for collision, checking distance between two points + // Unfortunately, sqrtf() is a costly operation, so avoid it with following solution /* float dx = center1.x - center2.x; // X distance between centers float dy = center1.y - center2.y; // Y distance between centers @@ -4235,7 +4228,7 @@ RayCollision GetRayCollisionBox(Ray ray, BoundingBox box) // Get vector center point->hit point collision.normal = Vector3Subtract(collision.point, collision.normal); // Scale vector to unit cube - // NOTE: We use an additional .01 to fix numerical errors + // NOTE: Use an additional .01 to fix numerical errors collision.normal = Vector3Scale(collision.normal, 2.01f); collision.normal = Vector3Divide(collision.normal, Vector3Subtract(box.max, box.min)); // The relevant elements of the vector are now slightly larger than 1.0f (or smaller than -1.0f) @@ -4476,7 +4469,7 @@ static Model LoadOBJ(const char *fileName) } else if ((lastMaterial != -1) && (objAttributes.material_ids[faceId] != lastMaterial)) { - meshIndex++; // If this is a new material, we need to allocate a new mesh + meshIndex++; // If this is a new material, a new mesh is allocated } lastMaterial = objAttributes.material_ids[faceId]; @@ -4492,7 +4485,7 @@ static Model LoadOBJ(const char *fileName) model.materialCount = objMaterialCount; model.materials = (Material *)MemAlloc(sizeof(Material)*objMaterialCount); } - else // We must allocate at least one material + else // Allocate at least one material { model.materialCount = 1; model.materials = (Material *)MemAlloc(sizeof(Material)*1); @@ -4515,7 +4508,7 @@ static Model LoadOBJ(const char *fileName) // Walk all the faces for (unsigned int faceId = 0; faceId < objAttributes.num_faces; faceId++) { - bool newMesh = false; // Do we need a new mesh? + bool newMesh = false; // Is a new mesh required? if (faceId >= nextShapeEnd) { // Try to find the last vert in the next shape @@ -4585,7 +4578,7 @@ static Model LoadOBJ(const char *fileName) // Walk all the faces for (unsigned int faceId = 0; faceId < objAttributes.num_faces; faceId++) { - bool newMesh = false; // Do we need a new mesh? + bool newMesh = false; // Is a new mesh required? if (faceId >= nextShapeEnd) { // Try to find the last vert in the next shape @@ -4595,7 +4588,7 @@ static Model LoadOBJ(const char *fileName) newMesh = true; } - // If this is a new material, we need to allocate a new mesh + // If this is a new material, a new mesh is allocated if (lastMaterial != -1 && objAttributes.material_ids[faceId] != lastMaterial) newMesh = true; lastMaterial = objAttributes.material_ids[faceId]; @@ -4841,7 +4834,7 @@ static Model LoadIQM(const char *fileName) model.meshes[i].indices = (unsigned short *)RL_CALLOC(model.meshes[i].triangleCount*3, sizeof(unsigned short)); #if !SUPPORT_GPU_SKINNING - // Animated vertex data, what we actually process for rendering + // Animated vertex data, processed for rendering // NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning) 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)); @@ -4861,7 +4854,7 @@ static Model LoadIQM(const char *fileName) for (unsigned int i = imesh[m].first_triangle; i < (imesh[m].first_triangle + imesh[m].num_triangles); i++) { // IQM triangles indexes are stored in counter-clockwise, but raylib processes the index in linear order, - // expecting they point to the counter-clockwise vertex triangle, so we need to reverse triangle indexes + // expecting they point to the counter-clockwise vertex triangle, so triangle indexes need to be reversed // NOTE: raylib renders vertex data in counter-clockwise order (standard convention) by default model.meshes[m].indices[tcounter + 2] = tri[i].vertex[0] - imesh[m].first_vertex; model.meshes[m].indices[tcounter + 1] = tri[i].vertex[1] - imesh[m].first_vertex; @@ -5496,7 +5489,7 @@ static Model LoadGLTF(const char *fileName) int primitivesCount = 0; bool dracoCompression = false; - // NOTE: We will load every primitive in the glTF as a separate raylib Mesh + // NOTE: 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++) { @@ -5528,7 +5521,7 @@ static Model LoadGLTF(const char *fileName) model.meshCount = primitivesCount; model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); - // NOTE: We keep an extra slot for default material, in case some mesh requires it + // NOTE: Keep an extra slot for default material, in case some mesh requires it model.materialCount = (int)data->materials_count + 1; model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); model.materials[0] = LoadMaterialDefault(); // Load default material (index: 0) @@ -5684,7 +5677,7 @@ static Model LoadGLTF(const char *fileName) for (unsigned int p = 0; p < mesh->primitives_count; p++) { - // NOTE: We only support primitives defined by triangles + // NOTE: Only support primitives defined by triangles // Other alternatives: points, lines, line_strip, triangle_strip if (mesh->primitives[p].type != cgltf_primitive_type_triangles) continue; @@ -6067,7 +6060,7 @@ static Model LoadGLTF(const char *fileName) 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 + // Convert data to raylib color data type (4 bytes), color data must be normalized for (unsigned int c = 0; c < attribute->count*4; c++) model.meshes[meshIndex].colors[c] = (unsigned char)(temp[c]*255.0f); RL_FREE(temp); @@ -6125,7 +6118,7 @@ static Model LoadGLTF(const char *fileName) { // The primitive actually keeps the pointer to the corresponding material, // raylib instead assigns to the mesh the by its index, as loaded in model.materials array - // To get the index, we check if material pointers match, and we assign the corresponding index, + // To get the index, check if material pointers match, and assign the corresponding index, // skipping index 0, the default material if (&data->materials[m] == mesh->primitives[p].material) { @@ -6187,7 +6180,7 @@ static Model LoadGLTF(const char *fileName) { bool hasJoints = false; - // NOTE: We only support primitives defined by triangles + // NOTE: Only support primitives defined by triangles if (mesh->primitives[p].type != cgltf_primitive_type_triangles) continue; for (unsigned int j = 0; j < mesh->primitives[p].attributes_count; j++) @@ -6234,7 +6227,7 @@ static Model LoadGLTF(const char *fileName) boneIdOverflowWarning = true; } - // Despite the possible overflow, we convert data to unsigned char + // Despite the possible overflow, convert data to unsigned char model.meshes[meshIndex].boneIndices[b] = (unsigned char)temp[b]; } @@ -6284,8 +6277,7 @@ static Model LoadGLTF(const char *fileName) 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: - + // for cgltf_attribute_type_weights: // - data.meshes[0] (256 vertices) // - 256 values, provided as cgltf_type_vec4 of float (4 byte per joint, stride 16) LOAD_ATTRIBUTE(attribute, 4, float, model.meshes[meshIndex].boneWeights) @@ -6296,9 +6288,9 @@ static Model LoadGLTF(const char *fileName) } } - // Check if we are animated, and the mesh was not given any bone assignments, but is the child of a bone node - // in this case we need to fully attach all the verts to the parent bone so it will animate with the bone - if (data->skins_count > 0 && !hasJoints && node->parent != NULL && node->parent->mesh == NULL) + // Check if animated, and the mesh was not given any bone assignments, but is the child of a bone node + // in this case, all the verts need to be attached to the parent bone so it will animate with the bone + if ((data->skins_count > 0) && !hasJoints && (node->parent != NULL) && (node->parent->mesh == NULL)) { int parentBoneId = -1; for (int joint = 0; joint < model.skeleton.boneCount; joint++) @@ -6435,7 +6427,7 @@ static bool GetPoseAtTimeGLTF(cgltf_interpolation_type interpolationType, cgltf_ } else if (output->type == cgltf_type_vec4) { - // Only v4 is for rotations, so we know it's a quaternion + // Only v4 is for rotations, so it's a quaternion switch (interpolationType) { case cgltf_interpolation_type_step: @@ -6822,7 +6814,7 @@ static Model LoadM3D(const char *fileName) } else TRACELOG(LOG_INFO, "MODEL: [%s] M3D data loaded successfully: %i faces/%i materials", fileName, m3d->numface, m3d->nummaterial); - // no face? this is probably just a material library + // Check if face is found, if not, probably just a material library if (!m3d->numface) { m3d_free(m3d); @@ -6841,12 +6833,12 @@ static Model LoadM3D(const char *fileName) TRACELOG(LOG_INFO, "MODEL: No materials, putting all meshes in a default material"); } - // We always need a default material, so we add +1 + // A default material is always required, so adding +1 model.materialCount++; // Faces must be in non-decreasing materialid order. Verify that quickly, sorting them otherwise // WARNING: Sorting is not needed, valid M3D model files should already be sorted - // Just keeping the sorting function for reference (Check PR #3363 #3385) + // Keeping the sorting function for reference (Check PR #3363 #3385) /* for (a = 1; a < m3d->numface; a++) { @@ -6902,7 +6894,7 @@ static Model LoadM3D(const char *fileName) mi = m3d->face[i].materialid; // Only allocate colors VertexBuffer if there's a color vertex in the model for this material batch - // if all colors are fully transparent black for all verteces of this materal, then we assume no vertex colors + // if all colors are fully transparent black for all verteces of this materal, then assuming no vertex colors for (j = i, l = vcolor = 0; (j < (int)m3d->numface) && (mi == m3d->face[j].materialid); j++, l++) { if (!m3d->vertex[m3d->face[j].vertex[0]].color || @@ -6916,11 +6908,11 @@ static Model LoadM3D(const char *fileName) model.meshes[k].texcoords = (float *)RL_CALLOC(model.meshes[k].vertexCount*2, sizeof(float)); model.meshes[k].normals = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float)); - // If no map is provided, or we have colors defined, we allocate storage for vertex colors + // If no map is provided, or colors are defined, 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 = (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 no map is provided and vertex colors are allocated, set them to white if ((mi == M3D_UNDEF) && (model.meshes[k].colors != NULL)) { for (int c = 0; c < model.meshes[k].vertexCount*4; c++) model.meshes[k].colors[c] = 255; @@ -6951,7 +6943,7 @@ static Model LoadM3D(const char *fileName) model.meshes[k].vertices[l*9 + 7] = m3d->vertex[m3d->face[i].vertex[2]].y*m3d->scale; model.meshes[k].vertices[l*9 + 8] = m3d->vertex[m3d->face[i].vertex[2]].z*m3d->scale; - // Without vertex color (full transparency), we use the default color + // Without vertex color (full transparency), using the default color if (model.meshes[k].colors != NULL) { if (m3d->vertex[m3d->face[i].vertex[0]].color & 0xff000000) @@ -6992,7 +6984,7 @@ static Model LoadM3D(const char *fileName) { int skinid = m3d->vertex[m3d->face[i].vertex[n]].skinid; - // Check if there is a skin for this mesh, should be, just failsafe + // Check if there is a skin for this mesh if ((skinid != M3D_UNDEF) && (skinid < (int)m3d->numskin)) { for (j = 0; j < 4; j++) @@ -7003,8 +6995,8 @@ static Model LoadM3D(const char *fileName) } else { - // raylib does not handle boneless meshes with skeletal animations, so - // we put all vertices without a bone into a special "no bone" bone + // Boneless meshes with skeletal animations are not supported, so + // putting all vertices without a bone into a special "no bone" bone model.meshes[k].boneIndices[l*12 + n*4] = m3d->numbone; model.meshes[k].boneWeights[l*12 + n*4] = 1.0f; } @@ -7215,7 +7207,7 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou bones[i].parent = -1; memcpy(bones[i].name, "NO BONE", 7); - // M3D stores frames at arbitrary intervals with sparse skeletons. We need full skeletons at + // M3D stores frames at arbitrary intervals with sparse skeletons; Full skeletons is required at // regular intervals, so let the M3D SDK do the heavy lifting and calculate interpolated bones for (i = 0; i < animations[a].keyframeCount; i++) { diff --git a/src/rshapes.c b/src/rshapes.c index 91a8ae513..e24689040 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -1946,7 +1946,7 @@ void DrawSplineBezierCubic(const Vector2 *points, int pointCount, float thick, C // Draw spline segment: Linear, 2 points void DrawSplineSegmentLinear(Vector2 p1, Vector2 p2, float thick, Color color) { - // NOTE: For the linear spline no subdivisions are used, just a single quad + // NOTE: For the linear spline no subdivisions are used, only a single quad Vector2 delta = { p2.x - p1.x, p2.y - p1.y }; float length = sqrtf(delta.x*delta.x + delta.y*delta.y); diff --git a/src/rtext.c b/src/rtext.c index 0a59e3538..4263284b4 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -10,7 +10,7 @@ * #define SUPPORT_FILEFORMAT_TTF * #define SUPPORT_FILEFORMAT_BDF * Selected desired fileformats to be supported for loading. Some of those formats are -* supported by default, to remove support, just comment unrequired #define in this module +* supported by default, to remove support, comment unrequired #define in this module * * #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH * TextSplit() function static buffer max size @@ -157,7 +157,7 @@ extern void LoadFontDefault(void) #define BIT_CHECK(a,b) ((a) & (1u << (b))) // Check to see if the font for an image has alreeady been allocated, - // and if no need to upload, then just return + // and if no need to upload, then return if (defaultFont.glyphs != NULL) return; // NOTE: Using UTF-8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement diff --git a/src/rtextures.c b/src/rtextures.c index 2aef6f5a0..cf19edd1e 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -22,7 +22,7 @@ * #define SUPPORT_FILEFORMAT_PVR * #define SUPPORT_FILEFORMAT_ASTC * Select desired fileformats to be supported for image data loading. Some of those formats are -* supported by default, to remove support, just comment unrequired #define in this module +* supported by default, to remove support, comment unrequired #define in this module * * #define SUPPORT_IMAGE_EXPORT * Support image export in multiple file formats @@ -1870,7 +1870,7 @@ void ImageToPOT(Image *image, Color fill) if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; // Calculate next power-of-two values - // NOTE: Just add the required amount of pixels at the right and bottom sides of image... + // NOTE: Add the required amount of pixels at the right and bottom sides of image... int potWidth = (int)powf(2, ceilf(logf((float)image->width)/logf(2))); int potHeight = (int)powf(2, ceilf(logf((float)image->height)/logf(2))); @@ -2014,7 +2014,7 @@ void ImageAlphaMask(Image *image, Image alphaMask) Image mask = ImageCopy(alphaMask); if (mask.format != PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) ImageFormat(&mask, PIXELFORMAT_UNCOMPRESSED_GRAYSCALE); - // In case image is only grayscale, just add alpha channel + // In case image is only grayscale, add alpha channel if (image->format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) { unsigned char *data = (unsigned char *)RL_MALLOC(image->width*image->height*2); @@ -2589,7 +2589,7 @@ void ImageFlipHorizontal(Image *image) // OPTION 1: Move pixels with memcpy() //memcpy(flippedData + (y*image->width + x)*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + (image->width - 1 - x))*bytesPerPixel, bytesPerPixel); - // OPTION 2: Just copy data pixel by pixel + // OPTION 2: Copy data pixel by pixel for (int i = 0; i < bytesPerPixel; i++) flippedData[(y*image->width + x)*bytesPerPixel + i] = ((unsigned char *)image->data)[(y*image->width + (image->width - 1 - x))*bytesPerPixel + i]; } } @@ -4587,10 +4587,10 @@ void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 rlSetTexture(0); // NOTE: Vertex position can be transformed using matrices - // but the process is way more costly than just calculating + // but the process is way more costly than calculating // the vertex positions manually, like done above // Old implementation is left here for educational purposes, - // just in case someone wants to do some performance test + // in case someone wants to do some performance test /* rlSetTexture(texture.id); rlPushMatrix(); From e1959a4e5c51d3bd9a6893a8fecd8f7ea973397d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 4 Mar 2026 00:15:21 +0000 Subject: [PATCH 122/319] rlparser: update raylib_api.* by CI --- tools/rlparser/output/raylib_api.json | 4 ++-- tools/rlparser/output/raylib_api.lua | 4 ++-- tools/rlparser/output/raylib_api.xml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/rlparser/output/raylib_api.json b/tools/rlparser/output/raylib_api.json index f1d4c1af3..c603aad12 100644 --- a/tools/rlparser/output/raylib_api.json +++ b/tools/rlparser/output/raylib_api.json @@ -2220,7 +2220,7 @@ { "name": "GAMEPAD_BUTTON_UNKNOWN", "value": 0, - "description": "Unknown button, just for error checking" + "description": "Unknown button, for error checking" }, { "name": "GAMEPAD_BUTTON_LEFT_FACE_UP", @@ -2792,7 +2792,7 @@ { "name": "TEXTURE_FILTER_POINT", "value": 0, - "description": "No filter, just pixel approximation" + "description": "No filter, pixel approximation" }, { "name": "TEXTURE_FILTER_BILINEAR", diff --git a/tools/rlparser/output/raylib_api.lua b/tools/rlparser/output/raylib_api.lua index 9a65ccd2e..6bf7cab92 100644 --- a/tools/rlparser/output/raylib_api.lua +++ b/tools/rlparser/output/raylib_api.lua @@ -2220,7 +2220,7 @@ return { { name = "GAMEPAD_BUTTON_UNKNOWN", value = 0, - description = "Unknown button, just for error checking" + description = "Unknown button, for error checking" }, { name = "GAMEPAD_BUTTON_LEFT_FACE_UP", @@ -2792,7 +2792,7 @@ return { { name = "TEXTURE_FILTER_POINT", value = 0, - description = "No filter, just pixel approximation" + description = "No filter, pixel approximation" }, { name = "TEXTURE_FILTER_BILINEAR", diff --git a/tools/rlparser/output/raylib_api.xml b/tools/rlparser/output/raylib_api.xml index be09bbd02..8b3e384a3 100644 --- a/tools/rlparser/output/raylib_api.xml +++ b/tools/rlparser/output/raylib_api.xml @@ -470,7 +470,7 @@ - + @@ -590,7 +590,7 @@ - + From 71677765c5e2a2f5d97debfd81791f5b38ef9989 Mon Sep 17 00:00:00 2001 From: takenoko-pm Date: Wed, 4 Mar 2026 15:49:55 +0900 Subject: [PATCH 123/319] Fix Makefile template to support libraylib.web.a (#5620) --- projects/VSCode/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/VSCode/Makefile b/projects/VSCode/Makefile index 389a12cf9..219673670 100644 --- a/projects/VSCode/Makefile +++ b/projects/VSCode/Makefile @@ -343,7 +343,7 @@ ifeq ($(PLATFORM),PLATFORM_RPI) endif ifeq ($(PLATFORM),PLATFORM_WEB) # Libraries for web (HTML5) compiling - LDLIBS = $(RAYLIB_RELEASE_PATH)/libraylib.a + LDLIBS = $(RAYLIB_RELEASE_PATH)/libraylib.web.a endif # Define a recursive wildcard function From 28288fafb1fef14431eb21b467b2c502ae645a4a Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Tue, 3 Mar 2026 22:52:50 -0800 Subject: [PATCH 124/319] Fix MSVC warnings. (#5619) --- examples/audio/audio_spectrum_visualizer.c | 6 +++--- examples/core/core_keyboard_testbed.c | 16 ++++++++-------- examples/models/models_animation_blend_custom.c | 8 ++++---- examples/models/raygui.h | 2 ++ examples/shapes/raygui.h | 4 +++- examples/shapes/shapes_hilbert_curve.c | 4 ++-- .../textures/textures_framebuffer_rendering.c | 4 ++-- .../VS2022/examples/core_compute_hash.vcxproj | 16 ++++++++-------- .../VS2022/examples/core_directory_files.vcxproj | 16 ++++++++-------- .../examples/models_animation_blending.vcxproj | 16 ++++++++-------- .../examples/models_animation_timing.vcxproj | 16 ++++++++-------- .../VS2022/examples/shapes_pie_chart.vcxproj | 16 ++++++++-------- .../VS2022/examples/shapes_rlgl_triangle.vcxproj | 16 ++++++++-------- 13 files changed, 72 insertions(+), 68 deletions(-) diff --git a/examples/audio/audio_spectrum_visualizer.c b/examples/audio/audio_spectrum_visualizer.c index 5cde7e9bd..d2dbbbb57 100644 --- a/examples/audio/audio_spectrum_visualizer.c +++ b/examples/audio/audio_spectrum_visualizer.c @@ -115,7 +115,7 @@ int main(void) .tapbackPos = 0.01f }; - int wavCursor = 0; + unsigned int wavCursor = 0; const short *wavPCM16 = wav.data; short chunkSamples[AUDIO_STREAM_RING_BUFFER_SIZE] = { 0 }; @@ -274,8 +274,8 @@ static void CaptureFrame(FFTData *fftData, const float *audioSamples) static void RenderFrame(const FFTData *fftData, Image *fftImage) { - double framesSinceTapback = floor(fftData->tapbackPos/WINDOW_TIME); - framesSinceTapback = Clamp(framesSinceTapback, 0.0, fftData->fftHistoryLen - 1); + float framesSinceTapback = floorf((float)(fftData->tapbackPos/WINDOW_TIME)); + framesSinceTapback = Clamp(framesSinceTapback, 0.0f, (float)(fftData->fftHistoryLen - 1)); int historyPosition = (fftData->historyPos - 1 - (int)framesSinceTapback)%fftData->fftHistoryLen; if (historyPosition < 0) historyPosition += fftData->fftHistoryLen; diff --git a/examples/core/core_keyboard_testbed.c b/examples/core/core_keyboard_testbed.c index 904e9c90a..2ea5c361a 100644 --- a/examples/core/core_keyboard_testbed.c +++ b/examples/core/core_keyboard_testbed.c @@ -133,7 +133,7 @@ int main(void) // ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, IMP, CLOSE for (int i = 0, recOffsetX = 0; i < 15; i++) { - GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y, line01KeyWidths[i], 30 }, line01Keys[i]); + GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y, (float)line01KeyWidths[i], 30.0f }, line01Keys[i]); recOffsetX += line01KeyWidths[i] + KEY_REC_SPACING; } @@ -141,7 +141,7 @@ int main(void) // `, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, -, =, BACKSPACE, DEL for (int i = 0, recOffsetX = 0; i < 15; i++) { - GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + KEY_REC_SPACING, line02KeyWidths[i], 38 }, line02Keys[i]); + GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + KEY_REC_SPACING, (float)line02KeyWidths[i], 38.0f }, line02Keys[i]); recOffsetX += line02KeyWidths[i] + KEY_REC_SPACING; } @@ -149,7 +149,7 @@ int main(void) // TAB, Q, W, E, R, T, Y, U, I, O, P, [, ], \, INS for (int i = 0, recOffsetX = 0; i < 15; i++) { - GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38 + KEY_REC_SPACING*2, line03KeyWidths[i], 38 }, line03Keys[i]); + GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38 + KEY_REC_SPACING*2, (float)line03KeyWidths[i], 38.0f }, line03Keys[i]); recOffsetX += line03KeyWidths[i] + KEY_REC_SPACING; } @@ -157,7 +157,7 @@ int main(void) // MAYUS, A, S, D, F, G, H, J, K, L, ;, ', ENTER, REPAG for (int i = 0, recOffsetX = 0; i < 14; i++) { - GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38*2 + KEY_REC_SPACING*3, line04KeyWidths[i], 38 }, line04Keys[i]); + GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38*2 + KEY_REC_SPACING*3, (float)line04KeyWidths[i], 38.0f }, line04Keys[i]); recOffsetX += line04KeyWidths[i] + KEY_REC_SPACING; } @@ -165,7 +165,7 @@ int main(void) // LSHIFT, Z, X, C, V, B, N, M, ,, ., /, RSHIFT, UP, AVPAG for (int i = 0, recOffsetX = 0; i < 14; i++) { - GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38*3 + KEY_REC_SPACING*4, line05KeyWidths[i], 38 }, line05Keys[i]); + GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38*3 + KEY_REC_SPACING*4, (float)line05KeyWidths[i], 38.0f }, line05Keys[i]); recOffsetX += line05KeyWidths[i] + KEY_REC_SPACING; } @@ -173,7 +173,7 @@ int main(void) // LCTRL, WIN, LALT, SPACE, ALTGR, \, FN, RCTRL, LEFT, DOWN, RIGHT for (int i = 0, recOffsetX = 0; i < 11; i++) { - GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38*4 + KEY_REC_SPACING*5, line06KeyWidths[i], 38 }, line06Keys[i]); + GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38*4 + KEY_REC_SPACING*5, (float)line06KeyWidths[i], 38.0f }, line06Keys[i]); recOffsetX += line06KeyWidths[i] + KEY_REC_SPACING; } @@ -316,12 +316,12 @@ static void GuiKeyboardKey(Rectangle bounds, int key) if (IsKeyDown(key)) { DrawRectangleLinesEx(bounds, 2.0f, MAROON); - DrawText(GetKeyText(key), bounds.x + 4, bounds.y + 4, 10, MAROON); + DrawText(GetKeyText(key), (int)(bounds.x + 4), (int)(bounds.y + 4), 10, MAROON); } else { DrawRectangleLinesEx(bounds, 2.0f, DARKGRAY); - DrawText(GetKeyText(key), bounds.x + 4, bounds.y + 4, 10, DARKGRAY); + DrawText(GetKeyText(key), (int)(bounds.x + 4), (int)(bounds.y + 4), 10, DARKGRAY); } } diff --git a/examples/models/models_animation_blend_custom.c b/examples/models/models_animation_blend_custom.c index 0b83c75d2..005982ec6 100644 --- a/examples/models/models_animation_blend_custom.c +++ b/examples/models/models_animation_blend_custom.c @@ -82,10 +82,10 @@ int main(void) ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/greenman.glb", &animCount); // Use specific animation indices: 2-walk/move, 3-attack - unsigned int animIndex0 = 2; // Walk/Move animation (index 2) - unsigned int animIndex1 = 3; // Attack animation (index 3) - unsigned int animCurrentFrame0 = 0; - unsigned int animCurrentFrame1 = 0; + int animIndex0 = 2; // Walk/Move animation (index 2) + int animIndex1 = 3; // Attack animation (index 3) + int animCurrentFrame0 = 0; + int animCurrentFrame1 = 0; // Validate indices if (animIndex0 >= animCount) animIndex0 = 0; diff --git a/examples/models/raygui.h b/examples/models/raygui.h index 03e487912..58bd38bef 100644 --- a/examples/models/raygui.h +++ b/examples/models/raygui.h @@ -357,8 +357,10 @@ #elif defined(USE_LIBTYPE_SHARED) #define RAYGUIAPI __declspec(dllimport) // Using the library as a Win32 shared library (.dll) #endif +#if !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS // Disable unsafe warnings on scanf() functions in MSVC #endif +#endif // Function specifiers definition #ifndef RAYGUIAPI diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h index 03e487912..07ef3c05b 100644 --- a/examples/shapes/raygui.h +++ b/examples/shapes/raygui.h @@ -357,7 +357,9 @@ #elif defined(USE_LIBTYPE_SHARED) #define RAYGUIAPI __declspec(dllimport) // Using the library as a Win32 shared library (.dll) #endif - #define _CRT_SECURE_NO_WARNINGS // Disable unsafe warnings on scanf() functions in MSVC +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS // Disable unsafe warnings on scanf() functions in MSVC +#endif #endif // Function specifiers definition diff --git a/examples/shapes/shapes_hilbert_curve.c b/examples/shapes/shapes_hilbert_curve.c index 3f368ca03..759fe93fd 100644 --- a/examples/shapes/shapes_hilbert_curve.c +++ b/examples/shapes/shapes_hilbert_curve.c @@ -43,7 +43,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [shapes] example - hilbert curve"); int order = 2; - float size = GetScreenHeight(); + float size = (float)GetScreenHeight(); int strokeCount = 0; Vector2 *hilbertPath = LoadHilbertPath(order, size, &strokeCount); @@ -73,7 +73,7 @@ int main(void) else counter = strokeCount; prevOrder = order; - prevSize = size; + prevSize = (int)size; } //---------------------------------------------------------------------------------- diff --git a/examples/textures/textures_framebuffer_rendering.c b/examples/textures/textures_framebuffer_rendering.c index 097ae1f2f..84ecce0ac 100644 --- a/examples/textures/textures_framebuffer_rendering.c +++ b/examples/textures/textures_framebuffer_rendering.c @@ -65,7 +65,7 @@ int main(void) // Rectangles for cropping render texture const float captureSize = 128.0f; Rectangle cropSource = { (subjectTarget.texture.width - captureSize)/2.0f, (subjectTarget.texture.height - captureSize)/2.0f, captureSize, -captureSize }; - Rectangle cropDest = { splitWidth + 20, 20, captureSize, captureSize}; + Rectangle cropDest = { splitWidth + 20.0f, 20.0f, captureSize, captureSize}; SetTargetFPS(60); DisableCursor(); @@ -115,7 +115,7 @@ int main(void) EndMode3D(); - DrawRectangleLines((subjectTarget.texture.width - captureSize)/2, (subjectTarget.texture.height - captureSize)/2, captureSize, captureSize, GREEN); + DrawRectangleLines((int)((subjectTarget.texture.width - captureSize)/2.0f), (int)((subjectTarget.texture.height - captureSize)/2.0f), captureSize, captureSize, GREEN); DrawText("Subject View", 10, subjectTarget.texture.height - 30, 20, BLACK); EndTextureMode(); diff --git a/projects/VS2022/examples/core_compute_hash.vcxproj b/projects/VS2022/examples/core_compute_hash.vcxproj index bbca36d86..28255f522 100644 --- a/projects/VS2022/examples/core_compute_hash.vcxproj +++ b/projects/VS2022/examples/core_compute_hash.vcxproj @@ -292,7 +292,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -309,7 +309,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) @@ -345,7 +345,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -366,7 +366,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -410,7 +410,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -432,7 +432,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -476,7 +476,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -504,7 +504,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true diff --git a/projects/VS2022/examples/core_directory_files.vcxproj b/projects/VS2022/examples/core_directory_files.vcxproj index 426fbfca8..f20797cff 100644 --- a/projects/VS2022/examples/core_directory_files.vcxproj +++ b/projects/VS2022/examples/core_directory_files.vcxproj @@ -292,7 +292,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -309,7 +309,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) @@ -345,7 +345,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -366,7 +366,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -410,7 +410,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -432,7 +432,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -476,7 +476,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -504,7 +504,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true diff --git a/projects/VS2022/examples/models_animation_blending.vcxproj b/projects/VS2022/examples/models_animation_blending.vcxproj index 44a55845d..9ba155d6b 100644 --- a/projects/VS2022/examples/models_animation_blending.vcxproj +++ b/projects/VS2022/examples/models_animation_blending.vcxproj @@ -202,7 +202,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -219,7 +219,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) @@ -237,7 +237,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -258,7 +258,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -281,7 +281,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -303,7 +303,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -325,7 +325,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -353,7 +353,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true diff --git a/projects/VS2022/examples/models_animation_timing.vcxproj b/projects/VS2022/examples/models_animation_timing.vcxproj index 9a1034925..339f5b936 100644 --- a/projects/VS2022/examples/models_animation_timing.vcxproj +++ b/projects/VS2022/examples/models_animation_timing.vcxproj @@ -292,7 +292,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -309,7 +309,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) @@ -345,7 +345,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -366,7 +366,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -410,7 +410,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -432,7 +432,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -476,7 +476,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -504,7 +504,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true diff --git a/projects/VS2022/examples/shapes_pie_chart.vcxproj b/projects/VS2022/examples/shapes_pie_chart.vcxproj index ad1cf93d1..ba88b28f7 100644 --- a/projects/VS2022/examples/shapes_pie_chart.vcxproj +++ b/projects/VS2022/examples/shapes_pie_chart.vcxproj @@ -292,7 +292,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -309,7 +309,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) @@ -345,7 +345,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -366,7 +366,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -410,7 +410,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -432,7 +432,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -476,7 +476,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -504,7 +504,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true diff --git a/projects/VS2022/examples/shapes_rlgl_triangle.vcxproj b/projects/VS2022/examples/shapes_rlgl_triangle.vcxproj index 780f514a0..3caacc139 100644 --- a/projects/VS2022/examples/shapes_rlgl_triangle.vcxproj +++ b/projects/VS2022/examples/shapes_rlgl_triangle.vcxproj @@ -292,7 +292,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -309,7 +309,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) @@ -345,7 +345,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -366,7 +366,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -410,7 +410,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -432,7 +432,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -476,7 +476,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -504,7 +504,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true From de720a8d4cfa620d079a58282c673307bccf6d21 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Wed, 4 Mar 2026 01:40:15 -0600 Subject: [PATCH 125/319] [backend/GLFW] Added bounds check (#5621) * added bounds check * update from PR feedback --- src/platforms/rcore_desktop_glfw.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index e47cad440..1975a7bfe 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -2178,16 +2178,19 @@ static void CursorEnterCallback(GLFWwindow *window, int enter) // GLFW3: Joystick connected/disconnected callback static void JoystickCallback(int jid, int event) { - if (event == GLFW_CONNECTED) + if (jid < MAX_GAMEPADS) { - // WARNING: If glfwGetJoystickName() is longer than MAX_GAMEPAD_NAME_LENGTH, - // only copy up to (MAX_GAMEPAD_NAME_LENGTH -1) to destination string - 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) - { - memset(CORE.Input.Gamepad.name[jid], 0, MAX_GAMEPAD_NAME_LENGTH); + if (event == GLFW_CONNECTED) + { + // WARNING: If glfwGetJoystickName() is longer than MAX_GAMEPAD_NAME_LENGTH, + // only copy up to (MAX_GAMEPAD_NAME_LENGTH -1) to destination string + 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) + { + memset(CORE.Input.Gamepad.name[jid], 0, MAX_GAMEPAD_NAME_LENGTH); + } } } From 2eaac95df080b9bcb53f6fe762ffbb3e0ef0619e Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Wed, 4 Mar 2026 18:16:22 +0000 Subject: [PATCH 126/319] Check if locs is not null before try to access (#5622) --- src/external/rlsw.h | 2 +- src/rmodels.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/external/rlsw.h b/src/external/rlsw.h index 746dd84a8..46cba4c74 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -3446,7 +3446,7 @@ static inline bool sw_is_texture_valid(uint32_t id) else if (id >= SW_MAX_TEXTURES) valid = false; else if (RLSW.loadedTextures[id].pixels == NULL) valid = false; - return true; + return valid; } static inline bool sw_is_texture_filter_valid(int filter) diff --git a/src/rmodels.c b/src/rmodels.c index 1463d50ba..7bc9ff066 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3928,7 +3928,7 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota // Upload runtime bone transforms matrices, to compute skinning on the shader (GPU-skinning) // NOTE: Required location must be found and Mesh bones indices and weights must be also uploaded to shader - if ((mat.shader.locs[SHADER_LOC_MATRIX_BONETRANSFORMS] != -1) && (model.boneMatrices != NULL)) + if (mat.shader.locs != NULL && (mat.shader.locs[SHADER_LOC_MATRIX_BONETRANSFORMS] != -1) && (model.boneMatrices != NULL)) { rlEnableShader(mat.shader.id); // Enable shader to set bone transform matrices rlSetUniformMatrices(mat.shader.locs[SHADER_LOC_MATRIX_BONETRANSFORMS], model.boneMatrices, model.skeleton.boneCount); From eb1e85e400ecf7dbaf21beb5d6e99848bc10e9cb Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 4 Mar 2026 19:17:41 +0100 Subject: [PATCH 127/319] Update rmodels.c --- src/rmodels.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index 7bc9ff066..0dd4f0634 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3928,7 +3928,7 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota // Upload runtime bone transforms matrices, to compute skinning on the shader (GPU-skinning) // NOTE: Required location must be found and Mesh bones indices and weights must be also uploaded to shader - if (mat.shader.locs != NULL && (mat.shader.locs[SHADER_LOC_MATRIX_BONETRANSFORMS] != -1) && (model.boneMatrices != NULL)) + if ((mat.shader.locs != NULL) && (mat.shader.locs[SHADER_LOC_MATRIX_BONETRANSFORMS] != -1) && (model.boneMatrices != NULL)) { rlEnableShader(mat.shader.id); // Enable shader to set bone transform matrices rlSetUniformMatrices(mat.shader.locs[SHADER_LOC_MATRIX_BONETRANSFORMS], model.boneMatrices, model.skeleton.boneCount); From d6926eb46a5ccd227b5bc055a3136037ccac8ca7 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 4 Mar 2026 19:17:46 +0100 Subject: [PATCH 128/319] Update CMakeLists.txt --- src/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dad7dd582..8b14bda90 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,7 @@ # Setup the project and settings project(raylib C) -set(PROJECT_VERSION 5.5.0) -set(API_VERSION 550) +set(PROJECT_VERSION 6.0.0) +set(API_VERSION 600) include(GNUInstallDirs) include(JoinPaths) From 84dc56ba8931221eff64edd5c89c1fbbe64ca3f7 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 4 Mar 2026 20:32:16 +0100 Subject: [PATCH 129/319] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 37e37c7c4..0bc503cd6 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ features - **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.h) - Multiple **Fonts** formats supported (TTF, OTF, FNT, BDF, sprite fonts) - Multiple texture formats supported, including **compressed formats** (DXT, ETC, ASTC) - - **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more! + - **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more! - Flexible Materials system, supporting classic maps and **PBR maps** - **Animated 3D models** supported (skeletal bones animation) (IQM, M3D, glTF) - Shaders support, including model shaders and **postprocessing** shaders @@ -111,7 +111,7 @@ raylib has been developed on Windows platform using [Notepad++](https://notepad- learning and docs ------------------ -raylib is designed to be learned using [the examples](https://github.com/raysan5/raylib/tree/master/examples) as the main reference. There is no standard API documentation but there is a [**cheatsheet**](https://www.raylib.com/cheatsheet/cheatsheet.html) containing all the functions available on the library a short description of each one of them, input parameters and result value names should be intuitive enough to understand how each function works. +raylib is designed to be learned using [the examples](https://github.com/raysan5/raylib/tree/master/examples) as the main reference. There is no standard API documentation but there is a [**cheatsheet**](https://www.raylib.com/cheatsheet/cheatsheet.html) containing all the functions available on the library a short description of each one of them, input parameters and result value names should be intuitive enough to understand how each function works. Some additional documentation about raylib design can be found in [raylib GitHub Wiki](https://github.com/raysan5/raylib/wiki). Here are the relevant links: From 02b592cd7bd9cd62cebc8f4430d1dd1d475a23af Mon Sep 17 00:00:00 2001 From: moe li Date: Thu, 5 Mar 2026 18:49:57 +0800 Subject: [PATCH 130/319] [rtext] Add `MeasureTextCodepoints()` for direct measurement of codepoints (#5623) * Measuring length of an array of codepoints * Applied style changes and removed default measurement function --- src/raylib.h | 1 + src/rtext.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/src/raylib.h b/src/raylib.h index c8c5c85a5..6a23400c6 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1509,6 +1509,7 @@ RLAPI void DrawTextCodepoints(Font font, const int *codepoints, int codepointCou RLAPI void SetTextLineSpacing(int spacing); // Set vertical line spacing when drawing with line-breaks RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font +RLAPI Vector2 MeasureTextCodepoints(Font font, const int *codepoints, int length, float fontSize, float spacing); // Measure string size for an existing array of codepoints for Font RLAPI int GetGlyphIndex(Font font, int codepoint); // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found RLAPI GlyphInfo GetGlyphInfo(Font font, int codepoint); // Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found RLAPI Rectangle GetGlyphAtlasRec(Font font, int codepoint); // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found diff --git a/src/rtext.c b/src/rtext.c index 4263284b4..ff59d667e 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1388,6 +1388,64 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing return textSize; } +// Measure string size for an existing array of codepoints for Font +Vector2 MeasureTextCodepoints(Font font, const int *codepoints, int length, float fontSize, float spacing) +{ + Vector2 textSize = { 0 }; + + // Security check + if ((font.texture.id == 0) || (codepoints == NULL) || (length == 0)) return textSize; + + float textWidth = 0.0f; + // Used to count longer text line width + float tempTextWidth = 0.0f; + + // Used to count longer text line num chars + int tempGlyphCounter = 0; + int glyphCounter = 0; + + float textHeight = fontSize; + float scaleFactor = fontSize/(float)font.baseSize; + + // Current character + int letter = 0; + // Index position in sprite font + int index = 0; + + for (int i = 0; i < length; i++) + { + letter = codepoints[i]; + index = GetGlyphIndex(font, letter); + + if (letter != '\n') + { + glyphCounter++; + + if (font.glyphs[index].advanceX > 0) textWidth += font.glyphs[index].advanceX; + else textWidth += (font.recs[index].width + font.glyphs[index].offsetX); + } + else + { + if (tempTextWidth < textWidth) tempTextWidth = textWidth; + + textWidth = 0; + glyphCounter = 0; + + // NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup + textHeight += (fontSize + textLineSpacing); + } + + if (tempGlyphCounter < glyphCounter) tempGlyphCounter = glyphCounter; + } + + if (tempTextWidth < textWidth) tempTextWidth = textWidth; + + textSize.x = tempTextWidth*scaleFactor + (float)((tempGlyphCounter - 1)*spacing); + textSize.y = textHeight; + + return textSize; +} + // Get index position for a unicode character on font // NOTE: If codepoint is not found in the font it fallbacks to '?' int GetGlyphIndex(Font font, int codepoint) From ea00b97c59cfae8ce18713394c32f0c276bd444a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 5 Mar 2026 10:50:09 +0000 Subject: [PATCH 131/319] rlparser: update raylib_api.* by CI --- tools/rlparser/output/raylib_api.json | 27 ++ tools/rlparser/output/raylib_api.lua | 12 + tools/rlparser/output/raylib_api.txt | 363 +++++++++++++------------- tools/rlparser/output/raylib_api.xml | 9 +- 4 files changed, 233 insertions(+), 178 deletions(-) diff --git a/tools/rlparser/output/raylib_api.json b/tools/rlparser/output/raylib_api.json index c603aad12..a669cae56 100644 --- a/tools/rlparser/output/raylib_api.json +++ b/tools/rlparser/output/raylib_api.json @@ -9614,6 +9614,33 @@ } ] }, + { + "name": "MeasureTextCodepoints", + "description": "Measure string size for an existing array of codepoints for Font", + "returnType": "Vector2", + "params": [ + { + "type": "Font", + "name": "font" + }, + { + "type": "const int *", + "name": "codepoints" + }, + { + "type": "int", + "name": "length" + }, + { + "type": "float", + "name": "fontSize" + }, + { + "type": "float", + "name": "spacing" + } + ] + }, { "name": "GetGlyphIndex", "description": "Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found", diff --git a/tools/rlparser/output/raylib_api.lua b/tools/rlparser/output/raylib_api.lua index 6bf7cab92..accfb77c1 100644 --- a/tools/rlparser/output/raylib_api.lua +++ b/tools/rlparser/output/raylib_api.lua @@ -6845,6 +6845,18 @@ return { {type = "float", name = "spacing"} } }, + { + name = "MeasureTextCodepoints", + description = "Measure string size for an existing array of codepoints for Font", + returnType = "Vector2", + params = { + {type = "Font", name = "font"}, + {type = "const int *", name = "codepoints"}, + {type = "int", name = "length"}, + {type = "float", name = "fontSize"}, + {type = "float", name = "spacing"} + } + }, { name = "GetGlyphIndex", description = "Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found", diff --git a/tools/rlparser/output/raylib_api.txt b/tools/rlparser/output/raylib_api.txt index 38aa20c3f..d8f82544d 100644 --- a/tools/rlparser/output/raylib_api.txt +++ b/tools/rlparser/output/raylib_api.txt @@ -1000,7 +1000,7 @@ Callback 006: AudioCallback() (2 input parameters) Param[1]: bufferData (type: void *) Param[2]: frames (type: unsigned int) -Functions found: 598 +Functions found: 599 Function 001: InitWindow() (3 input parameters) Name: InitWindow @@ -3676,137 +3676,146 @@ Function 422: MeasureTextEx() (4 input parameters) Param[2]: text (type: const char *) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) -Function 423: GetGlyphIndex() (2 input parameters) +Function 423: MeasureTextCodepoints() (5 input parameters) + Name: MeasureTextCodepoints + Return type: Vector2 + Description: Measure string size for an existing array of codepoints for Font + Param[1]: font (type: Font) + Param[2]: codepoints (type: const int *) + Param[3]: length (type: int) + Param[4]: fontSize (type: float) + Param[5]: spacing (type: float) +Function 424: 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 424: GetGlyphInfo() (2 input parameters) +Function 425: 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 425: GetGlyphAtlasRec() (2 input parameters) +Function 426: 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 426: LoadUTF8() (2 input parameters) +Function 427: 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 427: UnloadUTF8() (1 input parameters) +Function 428: UnloadUTF8() (1 input parameters) Name: UnloadUTF8 Return type: void Description: Unload UTF-8 text encoded from codepoints array Param[1]: text (type: char *) -Function 428: LoadCodepoints() (2 input parameters) +Function 429: 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 429: UnloadCodepoints() (1 input parameters) +Function 430: UnloadCodepoints() (1 input parameters) Name: UnloadCodepoints Return type: void Description: Unload codepoints data from memory Param[1]: codepoints (type: int *) -Function 430: GetCodepointCount() (1 input parameters) +Function 431: 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 431: GetCodepoint() (2 input parameters) +Function 432: 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 432: GetCodepointNext() (2 input parameters) +Function 433: 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 433: GetCodepointPrevious() (2 input parameters) +Function 434: 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 434: CodepointToUTF8() (2 input parameters) +Function 435: 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 435: LoadTextLines() (2 input parameters) +Function 436: LoadTextLines() (2 input parameters) Name: LoadTextLines Return type: char ** Description: Load text as separate lines ('\n') Param[1]: text (type: const char *) Param[2]: count (type: int *) -Function 436: UnloadTextLines() (2 input parameters) +Function 437: UnloadTextLines() (2 input parameters) Name: UnloadTextLines Return type: void Description: Unload text lines Param[1]: text (type: char **) Param[2]: lineCount (type: int) -Function 437: TextCopy() (2 input parameters) +Function 438: 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 438: TextIsEqual() (2 input parameters) +Function 439: 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 439: TextLength() (1 input parameters) +Function 440: 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 440: TextFormat() (2 input parameters) +Function 441: 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 441: TextSubtext() (3 input parameters) +Function 442: 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 442: TextRemoveSpaces() (1 input parameters) +Function 443: TextRemoveSpaces() (1 input parameters) Name: TextRemoveSpaces Return type: const char * Description: Remove text spaces, concat words Param[1]: text (type: const char *) -Function 443: GetTextBetween() (3 input parameters) +Function 444: GetTextBetween() (3 input parameters) Name: GetTextBetween Return type: char * Description: Get text between two strings Param[1]: text (type: const char *) Param[2]: begin (type: const char *) Param[3]: end (type: const char *) -Function 444: TextReplace() (3 input parameters) +Function 445: 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]: search (type: const char *) Param[3]: replacement (type: const char *) -Function 445: TextReplaceBetween() (4 input parameters) +Function 446: TextReplaceBetween() (4 input parameters) Name: TextReplaceBetween Return type: char * Description: Replace text between two specific strings (WARNING: memory must be freed!) @@ -3814,89 +3823,89 @@ Function 445: TextReplaceBetween() (4 input parameters) Param[2]: begin (type: const char *) Param[3]: end (type: const char *) Param[4]: replacement (type: const char *) -Function 446: TextInsert() (3 input parameters) +Function 447: 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 447: TextJoin() (3 input parameters) +Function 448: 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 448: TextSplit() (3 input parameters) +Function 449: TextSplit() (3 input parameters) Name: TextSplit Return type: char ** 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 *) -Function 449: TextAppend() (3 input parameters) +Function 450: 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 450: TextFindIndex() (2 input parameters) +Function 451: TextFindIndex() (2 input parameters) Name: TextFindIndex Return type: int Description: Find first text occurrence within a string, -1 if not found Param[1]: text (type: const char *) Param[2]: search (type: const char *) -Function 451: TextToUpper() (1 input parameters) +Function 452: TextToUpper() (1 input parameters) Name: TextToUpper Return type: char * Description: Get upper case version of provided string Param[1]: text (type: const char *) -Function 452: TextToLower() (1 input parameters) +Function 453: TextToLower() (1 input parameters) Name: TextToLower Return type: char * Description: Get lower case version of provided string Param[1]: text (type: const char *) -Function 453: TextToPascal() (1 input parameters) +Function 454: 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 454: TextToSnake() (1 input parameters) +Function 455: 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 455: TextToCamel() (1 input parameters) +Function 456: 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 456: TextToInteger() (1 input parameters) +Function 457: TextToInteger() (1 input parameters) Name: TextToInteger Return type: int Description: Get integer value from text Param[1]: text (type: const char *) -Function 457: TextToFloat() (1 input parameters) +Function 458: TextToFloat() (1 input parameters) Name: TextToFloat Return type: float Description: Get float value from text Param[1]: text (type: const char *) -Function 458: DrawLine3D() (3 input parameters) +Function 459: 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 459: DrawPoint3D() (2 input parameters) +Function 460: 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 460: DrawCircle3D() (5 input parameters) +Function 461: DrawCircle3D() (5 input parameters) Name: DrawCircle3D Return type: void Description: Draw a circle in 3D world space @@ -3905,7 +3914,7 @@ Function 460: DrawCircle3D() (5 input parameters) Param[3]: rotationAxis (type: Vector3) Param[4]: rotationAngle (type: float) Param[5]: color (type: Color) -Function 461: DrawTriangle3D() (4 input parameters) +Function 462: DrawTriangle3D() (4 input parameters) Name: DrawTriangle3D Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -3913,14 +3922,14 @@ Function 461: DrawTriangle3D() (4 input parameters) Param[2]: v2 (type: Vector3) Param[3]: v3 (type: Vector3) Param[4]: color (type: Color) -Function 462: DrawTriangleStrip3D() (3 input parameters) +Function 463: 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 463: DrawCube() (5 input parameters) +Function 464: DrawCube() (5 input parameters) Name: DrawCube Return type: void Description: Draw cube @@ -3929,14 +3938,14 @@ Function 463: DrawCube() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 464: DrawCubeV() (3 input parameters) +Function 465: 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 465: DrawCubeWires() (5 input parameters) +Function 466: DrawCubeWires() (5 input parameters) Name: DrawCubeWires Return type: void Description: Draw cube wires @@ -3945,21 +3954,21 @@ Function 465: DrawCubeWires() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 466: DrawCubeWiresV() (3 input parameters) +Function 467: 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 467: DrawSphere() (3 input parameters) +Function 468: 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 468: DrawSphereEx() (5 input parameters) +Function 469: DrawSphereEx() (5 input parameters) Name: DrawSphereEx Return type: void Description: Draw sphere with extended parameters @@ -3968,7 +3977,7 @@ Function 468: DrawSphereEx() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 469: DrawSphereWires() (5 input parameters) +Function 470: DrawSphereWires() (5 input parameters) Name: DrawSphereWires Return type: void Description: Draw sphere wires @@ -3977,7 +3986,7 @@ Function 469: DrawSphereWires() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 470: DrawCylinder() (6 input parameters) +Function 471: DrawCylinder() (6 input parameters) Name: DrawCylinder Return type: void Description: Draw a cylinder/cone @@ -3987,7 +3996,7 @@ Function 470: DrawCylinder() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 471: DrawCylinderEx() (6 input parameters) +Function 472: DrawCylinderEx() (6 input parameters) Name: DrawCylinderEx Return type: void Description: Draw a cylinder with base at startPos and top at endPos @@ -3997,7 +4006,7 @@ Function 471: DrawCylinderEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 472: DrawCylinderWires() (6 input parameters) +Function 473: DrawCylinderWires() (6 input parameters) Name: DrawCylinderWires Return type: void Description: Draw a cylinder/cone wires @@ -4007,7 +4016,7 @@ Function 472: DrawCylinderWires() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 473: DrawCylinderWiresEx() (6 input parameters) +Function 474: DrawCylinderWiresEx() (6 input parameters) Name: DrawCylinderWiresEx Return type: void Description: Draw a cylinder wires with base at startPos and top at endPos @@ -4017,7 +4026,7 @@ Function 473: DrawCylinderWiresEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 474: DrawCapsule() (6 input parameters) +Function 475: DrawCapsule() (6 input parameters) Name: DrawCapsule Return type: void Description: Draw a capsule with the center of its sphere caps at startPos and endPos @@ -4027,7 +4036,7 @@ Function 474: DrawCapsule() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 475: DrawCapsuleWires() (6 input parameters) +Function 476: DrawCapsuleWires() (6 input parameters) Name: DrawCapsuleWires Return type: void Description: Draw capsule wireframe with the center of its sphere caps at startPos and endPos @@ -4037,51 +4046,51 @@ Function 475: DrawCapsuleWires() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 476: DrawPlane() (3 input parameters) +Function 477: 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 477: DrawRay() (2 input parameters) +Function 478: 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 478: DrawGrid() (2 input parameters) +Function 479: 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 479: LoadModel() (1 input parameters) +Function 480: LoadModel() (1 input parameters) Name: LoadModel Return type: Model Description: Load model from files (meshes and materials) Param[1]: fileName (type: const char *) -Function 480: LoadModelFromMesh() (1 input parameters) +Function 481: LoadModelFromMesh() (1 input parameters) Name: LoadModelFromMesh Return type: Model Description: Load model from generated mesh (default material) Param[1]: mesh (type: Mesh) -Function 481: IsModelValid() (1 input parameters) +Function 482: 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 482: UnloadModel() (1 input parameters) +Function 483: 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 483: GetModelBoundingBox() (1 input parameters) +Function 484: GetModelBoundingBox() (1 input parameters) Name: GetModelBoundingBox Return type: BoundingBox Description: Compute model bounding box limits (considers all meshes) Param[1]: model (type: Model) -Function 484: DrawModel() (4 input parameters) +Function 485: DrawModel() (4 input parameters) Name: DrawModel Return type: void Description: Draw a model (with texture if set) @@ -4089,7 +4098,7 @@ Function 484: DrawModel() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 485: DrawModelEx() (6 input parameters) +Function 486: DrawModelEx() (6 input parameters) Name: DrawModelEx Return type: void Description: Draw a model with extended parameters @@ -4099,7 +4108,7 @@ Function 485: DrawModelEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 486: DrawModelWires() (4 input parameters) +Function 487: DrawModelWires() (4 input parameters) Name: DrawModelWires Return type: void Description: Draw a model wires (with texture if set) @@ -4107,7 +4116,7 @@ Function 486: DrawModelWires() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 487: DrawModelWiresEx() (6 input parameters) +Function 488: DrawModelWiresEx() (6 input parameters) Name: DrawModelWiresEx Return type: void Description: Draw a model wires (with texture if set) with extended parameters @@ -4117,7 +4126,7 @@ Function 487: DrawModelWiresEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 488: DrawModelPoints() (4 input parameters) +Function 489: DrawModelPoints() (4 input parameters) Name: DrawModelPoints Return type: void Description: Draw a model as points @@ -4125,7 +4134,7 @@ Function 488: DrawModelPoints() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 489: DrawModelPointsEx() (6 input parameters) +Function 490: DrawModelPointsEx() (6 input parameters) Name: DrawModelPointsEx Return type: void Description: Draw a model as points with extended parameters @@ -4135,13 +4144,13 @@ Function 489: DrawModelPointsEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 490: DrawBoundingBox() (2 input parameters) +Function 491: 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 491: DrawBillboard() (5 input parameters) +Function 492: DrawBillboard() (5 input parameters) Name: DrawBillboard Return type: void Description: Draw a billboard texture @@ -4150,7 +4159,7 @@ Function 491: DrawBillboard() (5 input parameters) Param[3]: position (type: Vector3) Param[4]: scale (type: float) Param[5]: tint (type: Color) -Function 492: DrawBillboardRec() (6 input parameters) +Function 493: DrawBillboardRec() (6 input parameters) Name: DrawBillboardRec Return type: void Description: Draw a billboard texture defined by source @@ -4160,7 +4169,7 @@ Function 492: DrawBillboardRec() (6 input parameters) Param[4]: position (type: Vector3) Param[5]: size (type: Vector2) Param[6]: tint (type: Color) -Function 493: DrawBillboardPro() (9 input parameters) +Function 494: DrawBillboardPro() (9 input parameters) Name: DrawBillboardPro Return type: void Description: Draw a billboard texture defined by source and rotation @@ -4173,13 +4182,13 @@ Function 493: DrawBillboardPro() (9 input parameters) Param[7]: origin (type: Vector2) Param[8]: rotation (type: float) Param[9]: tint (type: Color) -Function 494: UploadMesh() (2 input parameters) +Function 495: 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 495: UpdateMeshBuffer() (5 input parameters) +Function 496: UpdateMeshBuffer() (5 input parameters) Name: UpdateMeshBuffer Return type: void Description: Update mesh vertex data in GPU for a specific buffer index @@ -4188,19 +4197,19 @@ Function 495: UpdateMeshBuffer() (5 input parameters) Param[3]: data (type: const void *) Param[4]: dataSize (type: int) Param[5]: offset (type: int) -Function 496: UnloadMesh() (1 input parameters) +Function 497: UnloadMesh() (1 input parameters) Name: UnloadMesh Return type: void Description: Unload mesh data from CPU and GPU Param[1]: mesh (type: Mesh) -Function 497: DrawMesh() (3 input parameters) +Function 498: 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 498: DrawMeshInstanced() (4 input parameters) +Function 499: DrawMeshInstanced() (4 input parameters) Name: DrawMeshInstanced Return type: void Description: Draw multiple mesh instances with material and different transforms @@ -4208,35 +4217,35 @@ Function 498: DrawMeshInstanced() (4 input parameters) Param[2]: material (type: Material) Param[3]: transforms (type: const Matrix *) Param[4]: instances (type: int) -Function 499: GetMeshBoundingBox() (1 input parameters) +Function 500: GetMeshBoundingBox() (1 input parameters) Name: GetMeshBoundingBox Return type: BoundingBox Description: Compute mesh bounding box limits Param[1]: mesh (type: Mesh) -Function 500: GenMeshTangents() (1 input parameters) +Function 501: GenMeshTangents() (1 input parameters) Name: GenMeshTangents Return type: void Description: Compute mesh tangents Param[1]: mesh (type: Mesh *) -Function 501: ExportMesh() (2 input parameters) +Function 502: 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 502: ExportMeshAsCode() (2 input parameters) +Function 503: 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 503: GenMeshPoly() (2 input parameters) +Function 504: GenMeshPoly() (2 input parameters) Name: GenMeshPoly Return type: Mesh Description: Generate polygonal mesh Param[1]: sides (type: int) Param[2]: radius (type: float) -Function 504: GenMeshPlane() (4 input parameters) +Function 505: GenMeshPlane() (4 input parameters) Name: GenMeshPlane Return type: Mesh Description: Generate plane mesh (with subdivisions) @@ -4244,42 +4253,42 @@ Function 504: GenMeshPlane() (4 input parameters) Param[2]: length (type: float) Param[3]: resX (type: int) Param[4]: resZ (type: int) -Function 505: GenMeshCube() (3 input parameters) +Function 506: 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 506: GenMeshSphere() (3 input parameters) +Function 507: 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 507: GenMeshHemiSphere() (3 input parameters) +Function 508: 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 508: GenMeshCylinder() (3 input parameters) +Function 509: 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 509: GenMeshCone() (3 input parameters) +Function 510: 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 510: GenMeshTorus() (4 input parameters) +Function 511: GenMeshTorus() (4 input parameters) Name: GenMeshTorus Return type: Mesh Description: Generate torus mesh @@ -4287,7 +4296,7 @@ Function 510: GenMeshTorus() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 511: GenMeshKnot() (4 input parameters) +Function 512: GenMeshKnot() (4 input parameters) Name: GenMeshKnot Return type: Mesh Description: Generate trefoil knot mesh @@ -4295,67 +4304,67 @@ Function 511: GenMeshKnot() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 512: GenMeshHeightmap() (2 input parameters) +Function 513: 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 513: GenMeshCubicmap() (2 input parameters) +Function 514: 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 514: LoadMaterials() (2 input parameters) +Function 515: 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 515: LoadMaterialDefault() (0 input parameters) +Function 516: LoadMaterialDefault() (0 input parameters) Name: LoadMaterialDefault Return type: Material Description: Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) No input parameters -Function 516: IsMaterialValid() (1 input parameters) +Function 517: 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 517: UnloadMaterial() (1 input parameters) +Function 518: UnloadMaterial() (1 input parameters) Name: UnloadMaterial Return type: void Description: Unload material from GPU memory (VRAM) Param[1]: material (type: Material) -Function 518: SetMaterialTexture() (3 input parameters) +Function 519: 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 519: SetModelMeshMaterial() (3 input parameters) +Function 520: 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 520: LoadModelAnimations() (2 input parameters) +Function 521: 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 521: UpdateModelAnimation() (3 input parameters) +Function 522: UpdateModelAnimation() (3 input parameters) Name: UpdateModelAnimation Return type: void Description: Update model animation pose (vertex buffers and bone matrices) Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: float) -Function 522: UpdateModelAnimationEx() (6 input parameters) +Function 523: UpdateModelAnimationEx() (6 input parameters) Name: UpdateModelAnimationEx Return type: void Description: Update model animation pose, blending two animations @@ -4365,19 +4374,19 @@ Function 522: UpdateModelAnimationEx() (6 input parameters) Param[4]: animB (type: ModelAnimation) Param[5]: frameB (type: float) Param[6]: blend (type: float) -Function 523: UnloadModelAnimations() (2 input parameters) +Function 524: 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 524: IsModelAnimationValid() (2 input parameters) +Function 525: 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 525: CheckCollisionSpheres() (4 input parameters) +Function 526: CheckCollisionSpheres() (4 input parameters) Name: CheckCollisionSpheres Return type: bool Description: Check collision between two spheres @@ -4385,40 +4394,40 @@ Function 525: CheckCollisionSpheres() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector3) Param[4]: radius2 (type: float) -Function 526: CheckCollisionBoxes() (2 input parameters) +Function 527: 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 527: CheckCollisionBoxSphere() (3 input parameters) +Function 528: 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 528: GetRayCollisionSphere() (3 input parameters) +Function 529: 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 529: GetRayCollisionBox() (2 input parameters) +Function 530: 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 530: GetRayCollisionMesh() (3 input parameters) +Function 531: 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 531: GetRayCollisionTriangle() (4 input parameters) +Function 532: GetRayCollisionTriangle() (4 input parameters) Name: GetRayCollisionTriangle Return type: RayCollision Description: Get collision info between ray and triangle @@ -4426,7 +4435,7 @@ Function 531: GetRayCollisionTriangle() (4 input parameters) Param[2]: p1 (type: Vector3) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) -Function 532: GetRayCollisionQuad() (5 input parameters) +Function 533: GetRayCollisionQuad() (5 input parameters) Name: GetRayCollisionQuad Return type: RayCollision Description: Get collision info between ray and quad @@ -4435,158 +4444,158 @@ Function 532: GetRayCollisionQuad() (5 input parameters) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) Param[5]: p4 (type: Vector3) -Function 533: InitAudioDevice() (0 input parameters) +Function 534: InitAudioDevice() (0 input parameters) Name: InitAudioDevice Return type: void Description: Initialize audio device and context No input parameters -Function 534: CloseAudioDevice() (0 input parameters) +Function 535: CloseAudioDevice() (0 input parameters) Name: CloseAudioDevice Return type: void Description: Close the audio device and context No input parameters -Function 535: IsAudioDeviceReady() (0 input parameters) +Function 536: IsAudioDeviceReady() (0 input parameters) Name: IsAudioDeviceReady Return type: bool Description: Check if audio device has been initialized successfully No input parameters -Function 536: SetMasterVolume() (1 input parameters) +Function 537: SetMasterVolume() (1 input parameters) Name: SetMasterVolume Return type: void Description: Set master volume (listener) Param[1]: volume (type: float) -Function 537: GetMasterVolume() (0 input parameters) +Function 538: GetMasterVolume() (0 input parameters) Name: GetMasterVolume Return type: float Description: Get master volume (listener) No input parameters -Function 538: LoadWave() (1 input parameters) +Function 539: LoadWave() (1 input parameters) Name: LoadWave Return type: Wave Description: Load wave data from file Param[1]: fileName (type: const char *) -Function 539: LoadWaveFromMemory() (3 input parameters) +Function 540: 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 540: IsWaveValid() (1 input parameters) +Function 541: 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 541: LoadSound() (1 input parameters) +Function 542: LoadSound() (1 input parameters) Name: LoadSound Return type: Sound Description: Load sound from file Param[1]: fileName (type: const char *) -Function 542: LoadSoundFromWave() (1 input parameters) +Function 543: LoadSoundFromWave() (1 input parameters) Name: LoadSoundFromWave Return type: Sound Description: Load sound from wave data Param[1]: wave (type: Wave) -Function 543: LoadSoundAlias() (1 input parameters) +Function 544: 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 544: IsSoundValid() (1 input parameters) +Function 545: 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 545: UpdateSound() (3 input parameters) +Function 546: UpdateSound() (3 input parameters) Name: UpdateSound Return type: void Description: Update sound buffer with new data (default data format: 32 bit float, stereo) Param[1]: sound (type: Sound) Param[2]: data (type: const void *) Param[3]: sampleCount (type: int) -Function 546: UnloadWave() (1 input parameters) +Function 547: UnloadWave() (1 input parameters) Name: UnloadWave Return type: void Description: Unload wave data Param[1]: wave (type: Wave) -Function 547: UnloadSound() (1 input parameters) +Function 548: UnloadSound() (1 input parameters) Name: UnloadSound Return type: void Description: Unload sound Param[1]: sound (type: Sound) -Function 548: UnloadSoundAlias() (1 input parameters) +Function 549: 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 549: ExportWave() (2 input parameters) +Function 550: 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 550: ExportWaveAsCode() (2 input parameters) +Function 551: 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 551: PlaySound() (1 input parameters) +Function 552: PlaySound() (1 input parameters) Name: PlaySound Return type: void Description: Play a sound Param[1]: sound (type: Sound) -Function 552: StopSound() (1 input parameters) +Function 553: StopSound() (1 input parameters) Name: StopSound Return type: void Description: Stop playing a sound Param[1]: sound (type: Sound) -Function 553: PauseSound() (1 input parameters) +Function 554: PauseSound() (1 input parameters) Name: PauseSound Return type: void Description: Pause a sound Param[1]: sound (type: Sound) -Function 554: ResumeSound() (1 input parameters) +Function 555: ResumeSound() (1 input parameters) Name: ResumeSound Return type: void Description: Resume a paused sound Param[1]: sound (type: Sound) -Function 555: IsSoundPlaying() (1 input parameters) +Function 556: IsSoundPlaying() (1 input parameters) Name: IsSoundPlaying Return type: bool Description: Check if a sound is currently playing Param[1]: sound (type: Sound) -Function 556: SetSoundVolume() (2 input parameters) +Function 557: 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 557: SetSoundPitch() (2 input parameters) +Function 558: 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 558: SetSoundPan() (2 input parameters) +Function 559: SetSoundPan() (2 input parameters) Name: SetSoundPan Return type: void Description: Set pan for a sound (-1.0 left, 0.0 center, 1.0 right) Param[1]: sound (type: Sound) Param[2]: pan (type: float) -Function 559: WaveCopy() (1 input parameters) +Function 560: WaveCopy() (1 input parameters) Name: WaveCopy Return type: Wave Description: Copy a wave to a new wave Param[1]: wave (type: Wave) -Function 560: WaveCrop() (3 input parameters) +Function 561: 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 561: WaveFormat() (4 input parameters) +Function 562: WaveFormat() (4 input parameters) Name: WaveFormat Return type: void Description: Convert wave data to desired format @@ -4594,203 +4603,203 @@ Function 561: WaveFormat() (4 input parameters) Param[2]: sampleRate (type: int) Param[3]: sampleSize (type: int) Param[4]: channels (type: int) -Function 562: LoadWaveSamples() (1 input parameters) +Function 563: 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 563: UnloadWaveSamples() (1 input parameters) +Function 564: UnloadWaveSamples() (1 input parameters) Name: UnloadWaveSamples Return type: void Description: Unload samples data loaded with LoadWaveSamples() Param[1]: samples (type: float *) -Function 564: LoadMusicStream() (1 input parameters) +Function 565: LoadMusicStream() (1 input parameters) Name: LoadMusicStream Return type: Music Description: Load music stream from file Param[1]: fileName (type: const char *) -Function 565: LoadMusicStreamFromMemory() (3 input parameters) +Function 566: 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 566: IsMusicValid() (1 input parameters) +Function 567: 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 567: UnloadMusicStream() (1 input parameters) +Function 568: UnloadMusicStream() (1 input parameters) Name: UnloadMusicStream Return type: void Description: Unload music stream Param[1]: music (type: Music) -Function 568: PlayMusicStream() (1 input parameters) +Function 569: PlayMusicStream() (1 input parameters) Name: PlayMusicStream Return type: void Description: Start music playing Param[1]: music (type: Music) -Function 569: IsMusicStreamPlaying() (1 input parameters) +Function 570: IsMusicStreamPlaying() (1 input parameters) Name: IsMusicStreamPlaying Return type: bool Description: Check if music is playing Param[1]: music (type: Music) -Function 570: UpdateMusicStream() (1 input parameters) +Function 571: UpdateMusicStream() (1 input parameters) Name: UpdateMusicStream Return type: void Description: Updates buffers for music streaming Param[1]: music (type: Music) -Function 571: StopMusicStream() (1 input parameters) +Function 572: StopMusicStream() (1 input parameters) Name: StopMusicStream Return type: void Description: Stop music playing Param[1]: music (type: Music) -Function 572: PauseMusicStream() (1 input parameters) +Function 573: PauseMusicStream() (1 input parameters) Name: PauseMusicStream Return type: void Description: Pause music playing Param[1]: music (type: Music) -Function 573: ResumeMusicStream() (1 input parameters) +Function 574: ResumeMusicStream() (1 input parameters) Name: ResumeMusicStream Return type: void Description: Resume playing paused music Param[1]: music (type: Music) -Function 574: SeekMusicStream() (2 input parameters) +Function 575: 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 575: SetMusicVolume() (2 input parameters) +Function 576: 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 576: SetMusicPitch() (2 input parameters) +Function 577: 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 577: SetMusicPan() (2 input parameters) +Function 578: SetMusicPan() (2 input parameters) Name: SetMusicPan Return type: void Description: Set pan for a music (-1.0 left, 0.0 center, 1.0 right) Param[1]: music (type: Music) Param[2]: pan (type: float) -Function 578: GetMusicTimeLength() (1 input parameters) +Function 579: GetMusicTimeLength() (1 input parameters) Name: GetMusicTimeLength Return type: float Description: Get music time length (in seconds) Param[1]: music (type: Music) -Function 579: GetMusicTimePlayed() (1 input parameters) +Function 580: GetMusicTimePlayed() (1 input parameters) Name: GetMusicTimePlayed Return type: float Description: Get current music time played (in seconds) Param[1]: music (type: Music) -Function 580: LoadAudioStream() (3 input parameters) +Function 581: 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 581: IsAudioStreamValid() (1 input parameters) +Function 582: 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 582: UnloadAudioStream() (1 input parameters) +Function 583: UnloadAudioStream() (1 input parameters) Name: UnloadAudioStream Return type: void Description: Unload audio stream and free memory Param[1]: stream (type: AudioStream) -Function 583: UpdateAudioStream() (3 input parameters) +Function 584: 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 584: IsAudioStreamProcessed() (1 input parameters) +Function 585: IsAudioStreamProcessed() (1 input parameters) Name: IsAudioStreamProcessed Return type: bool Description: Check if any audio stream buffers requires refill Param[1]: stream (type: AudioStream) -Function 585: PlayAudioStream() (1 input parameters) +Function 586: PlayAudioStream() (1 input parameters) Name: PlayAudioStream Return type: void Description: Play audio stream Param[1]: stream (type: AudioStream) -Function 586: PauseAudioStream() (1 input parameters) +Function 587: PauseAudioStream() (1 input parameters) Name: PauseAudioStream Return type: void Description: Pause audio stream Param[1]: stream (type: AudioStream) -Function 587: ResumeAudioStream() (1 input parameters) +Function 588: ResumeAudioStream() (1 input parameters) Name: ResumeAudioStream Return type: void Description: Resume audio stream Param[1]: stream (type: AudioStream) -Function 588: IsAudioStreamPlaying() (1 input parameters) +Function 589: IsAudioStreamPlaying() (1 input parameters) Name: IsAudioStreamPlaying Return type: bool Description: Check if audio stream is playing Param[1]: stream (type: AudioStream) -Function 589: StopAudioStream() (1 input parameters) +Function 590: StopAudioStream() (1 input parameters) Name: StopAudioStream Return type: void Description: Stop audio stream Param[1]: stream (type: AudioStream) -Function 590: SetAudioStreamVolume() (2 input parameters) +Function 591: 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 591: SetAudioStreamPitch() (2 input parameters) +Function 592: 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 592: SetAudioStreamPan() (2 input parameters) +Function 593: 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 593: SetAudioStreamBufferSizeDefault() (1 input parameters) +Function 594: SetAudioStreamBufferSizeDefault() (1 input parameters) Name: SetAudioStreamBufferSizeDefault Return type: void Description: Default size for new audio streams Param[1]: size (type: int) -Function 594: SetAudioStreamCallback() (2 input parameters) +Function 595: 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 595: AttachAudioStreamProcessor() (2 input parameters) +Function 596: 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 596: DetachAudioStreamProcessor() (2 input parameters) +Function 597: 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 597: AttachAudioMixedProcessor() (1 input parameters) +Function 598: 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 598: DetachAudioMixedProcessor() (1 input parameters) +Function 599: DetachAudioMixedProcessor() (1 input parameters) Name: DetachAudioMixedProcessor Return type: void Description: Detach audio stream processor from the entire audio pipeline diff --git a/tools/rlparser/output/raylib_api.xml b/tools/rlparser/output/raylib_api.xml index 8b3e384a3..82aef8b47 100644 --- a/tools/rlparser/output/raylib_api.xml +++ b/tools/rlparser/output/raylib_api.xml @@ -682,7 +682,7 @@ - + @@ -2437,6 +2437,13 @@ + + + + + + + From f9ee714c76a283f2fbab7376c77396764c174c0f Mon Sep 17 00:00:00 2001 From: ghera Date: Thu, 5 Mar 2026 11:50:47 +0100 Subject: [PATCH 132/319] [rcore][android] Add WARNING comment for --wrap=fopen build system requirements (#5624) Co-authored-by: Federico Gherardi --- src/platforms/rcore_android.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 0833214e7..42b58ff82 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -275,6 +275,22 @@ static int android_write(void *cookie, const char *buf, int size); static fpos_t android_seek(void *cookie, fpos_t offset, int whence); static int android_close(void *cookie); +// WARNING: fopen() calls are intercepted via linker flag -Wl,--wrap=fopen: the linker renames +// the original fopen -> __real_fopen and redirects all call sites to __wrap_fopen +// The flag MUST be applied at every final link step that needs wrapping, +// it has no effect when only building a static archive (.a) +// +// CMake: no action required, raylib's CMakeLists.txt already sets +// target_link_options(raylib INTERFACE -Wl,--wrap=fopen) which propagates to +// the final app link, wrapping app code and all static (.a) dependencies too +// Make (SHARED): no action required for raylib itself, src/Makefile already sets +// LDFLAGS += -Wl,--wrap=fopen wrapping fopen inside libraylib.so only; +// app code and static (.a) dependencies are NOT wrapped unless -Wl,--wrap=fopen +// is also added to the final app link step +// Make (STATIC): pass -Wl,--wrap=fopen to the linker command producing the final artifact +// build.zig: no dedicated wrap helper; pass -Wl,--wrap=fopen to the linker command producing +// the final artifact +// custom: pass -Wl,--wrap=fopen to the linker command producing the final artifact FILE *__real_fopen(const char *fileName, const char *mode); // Real fopen, provided by the linker (--wrap=fopen) FILE *__wrap_fopen(const char *fileName, const char *mode); // Replacement for fopen() From 3e926d65a0dab63b098a9161075a2cf634d3ef23 Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Thu, 5 Mar 2026 13:51:16 +0000 Subject: [PATCH 133/319] add x11 libraries for raylib as glfw make private (#5625) --- src/CMakeLists.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8b14bda90..0975457ee 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -117,6 +117,21 @@ target_include_directories(raylib # Copy the header files to the build directory for convenience file(COPY ${raylib_public_headers} DESTINATION "include") +if (PLATFORM STREQUAL "Desktop") + if (UNIX AND NOT APPLE) + if (GLFW_BUILD_X11) + find_package(X11 REQUIRED) + + target_compile_definitions(raylib PRIVATE _GLFW_X11) + + target_link_libraries(raylib PRIVATE + ${X11_LIBRARIES} + ) + + message(STATUS "X11 support enabled for raylib") + endif() + endif() +endif() # Includes information on how the library will be installed on the system # when cmake --install is run From 54f630774d62699dfdd79d2a9f4eba9269f38aa4 Mon Sep 17 00:00:00 2001 From: Gleb A Date: Fri, 6 Mar 2026 10:52:28 -0500 Subject: [PATCH 134/319] [example] cel-shading and outline using inverted hull (#5615) * added cel-shading and outline using inverted hull example * new screenshot * added glsl100+120 compat * updated view * unnecessary spacing --- .../shaders/resources/shaders/glsl100/cel.fs | 58 +++++ .../shaders/resources/shaders/glsl100/cel.vs | 47 ++++ .../resources/shaders/glsl100/outline_hull.fs | 8 + .../resources/shaders/glsl100/outline_hull.vs | 15 ++ .../shaders/resources/shaders/glsl120/cel.fs | 56 +++++ .../shaders/resources/shaders/glsl120/cel.vs | 48 +++++ .../resources/shaders/glsl120/outline_hull.fs | 6 + .../resources/shaders/glsl120/outline_hull.vs | 15 ++ .../shaders/resources/shaders/glsl330/cel.fs | 60 ++++++ .../shaders/resources/shaders/glsl330/cel.vs | 25 +++ .../resources/shaders/glsl330/outline_hull.fs | 7 + .../resources/shaders/glsl330/outline_hull.vs | 15 ++ examples/shaders/shaders_cel_shading.c | 202 ++++++++++++++++++ examples/shaders/shaders_cel_shading.png | Bin 0 -> 45710 bytes 14 files changed, 562 insertions(+) create mode 100644 examples/shaders/resources/shaders/glsl100/cel.fs create mode 100644 examples/shaders/resources/shaders/glsl100/cel.vs create mode 100644 examples/shaders/resources/shaders/glsl100/outline_hull.fs create mode 100644 examples/shaders/resources/shaders/glsl100/outline_hull.vs create mode 100644 examples/shaders/resources/shaders/glsl120/cel.fs create mode 100644 examples/shaders/resources/shaders/glsl120/cel.vs create mode 100644 examples/shaders/resources/shaders/glsl120/outline_hull.fs create mode 100644 examples/shaders/resources/shaders/glsl120/outline_hull.vs create mode 100644 examples/shaders/resources/shaders/glsl330/cel.fs create mode 100644 examples/shaders/resources/shaders/glsl330/cel.vs create mode 100644 examples/shaders/resources/shaders/glsl330/outline_hull.fs create mode 100644 examples/shaders/resources/shaders/glsl330/outline_hull.vs create mode 100644 examples/shaders/shaders_cel_shading.c create mode 100644 examples/shaders/shaders_cel_shading.png diff --git a/examples/shaders/resources/shaders/glsl100/cel.fs b/examples/shaders/resources/shaders/glsl100/cel.fs new file mode 100644 index 000000000..dfc22d0c0 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl100/cel.fs @@ -0,0 +1,58 @@ +#version 100 + +precision mediump float; + +varying vec3 fragPosition; +varying vec2 fragTexCoord; +varying vec4 fragColor; +varying vec3 fragNormal; + +uniform sampler2D texture0; +uniform vec4 colDiffuse; +uniform vec3 viewPos; +uniform float numBands; + +struct Light { + int enabled; + int type; + vec3 position; + vec3 target; + vec4 color; +}; +uniform Light lights[4]; + +void main() +{ + vec4 texColor = texture2D(texture0, fragTexCoord); + vec3 baseColor = texColor.rgb * fragColor.rgb * colDiffuse.rgb; + vec3 norm = normalize(fragNormal); + + float lightAccum = 0.08; // ambient floor + + for (int i = 0; i < 4; i++) + { + if (lights[i].enabled == 1) // no continue in GLSL ES 1.0 + { + vec3 lightDir; + if (lights[i].type == 0) + { + // Directional: direction is from position toward target. + lightDir = normalize(lights[i].position - lights[i].target); + } + else + { + // Point: direction from surface to light. + lightDir = normalize(lights[i].position - fragPosition); + } + + float NdotL = max(dot(norm, lightDir), 0.0); + + // Quantize NdotL into numBands discrete steps. + float quantized = min(floor(NdotL * numBands), numBands - 1.0) / (numBands - 1.0); + lightAccum += quantized * lights[i].color.r; + } + } + + lightAccum = clamp(lightAccum, 0.0, 1.0); + gl_FragColor = vec4(baseColor * lightAccum, texColor.a * colDiffuse.a); +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl100/cel.vs b/examples/shaders/resources/shaders/glsl100/cel.vs new file mode 100644 index 000000000..153842a9b --- /dev/null +++ b/examples/shaders/resources/shaders/glsl100/cel.vs @@ -0,0 +1,47 @@ +#version 100 + +attribute vec3 vertexPosition; +attribute vec2 vertexTexCoord; +attribute vec3 vertexNormal; +attribute vec4 vertexColor; + +uniform mat4 mvp; +uniform mat4 matModel; + +varying vec3 fragPosition; +varying vec2 fragTexCoord; +varying vec4 fragColor; +varying vec3 fragNormal; + +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; +} + +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() +{ + fragPosition = vec3(matModel * vec4(vertexPosition, 1.0)); + fragTexCoord = vertexTexCoord; + fragColor = vertexColor; + + mat3 normalMatrix = transpose(inverse(mat3(matModel))); + fragNormal = normalize(normalMatrix * vertexNormal); + + gl_Position = mvp * vec4(vertexPosition, 1.0); +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl100/outline_hull.fs b/examples/shaders/resources/shaders/glsl100/outline_hull.fs new file mode 100644 index 000000000..4f82cfc87 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl100/outline_hull.fs @@ -0,0 +1,8 @@ +#version 100 + +precision mediump float; + +void main() +{ + gl_FragColor = vec4(0.05, 0.05, 0.05, 1.0); +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl100/outline_hull.vs b/examples/shaders/resources/shaders/glsl100/outline_hull.vs new file mode 100644 index 000000000..af7be8831 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl100/outline_hull.vs @@ -0,0 +1,15 @@ +#version 100 + +attribute vec3 vertexPosition; +attribute vec3 vertexNormal; +attribute vec2 vertexTexCoord; +attribute vec4 vertexColor; + +uniform mat4 mvp; +uniform float outlineThickness; + +void main() +{ + vec3 extruded = vertexPosition + vertexNormal * outlineThickness; + gl_Position = mvp * vec4(extruded, 1.0); +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl120/cel.fs b/examples/shaders/resources/shaders/glsl120/cel.fs new file mode 100644 index 000000000..88321e60f --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/cel.fs @@ -0,0 +1,56 @@ +#version 120 + +varying vec3 fragPosition; +varying vec2 fragTexCoord; +varying vec4 fragColor; +varying vec3 fragNormal; + +uniform sampler2D texture0; +uniform vec4 colDiffuse; +uniform vec3 viewPos; +uniform float numBands; + +struct Light { + int enabled; + int type; + vec3 position; + vec3 target; + vec4 color; +}; +uniform Light lights[4]; + +void main() +{ + vec4 texColor = texture2D(texture0, fragTexCoord); + vec3 baseColor = texColor.rgb * fragColor.rgb * colDiffuse.rgb; + vec3 norm = normalize(fragNormal); + + float lightAccum = 0.08; // ambient floor + + for (int i = 0; i < 4; i++) + { + if (lights[i].enabled == 1) + { + vec3 lightDir; + if (lights[i].type == 0) + { + // Directional: direction is from position toward target. + lightDir = normalize(lights[i].position - lights[i].target); + } + else + { + // Point: direction from surface to light. + lightDir = normalize(lights[i].position - fragPosition); + } + + float NdotL = max(dot(norm, lightDir), 0.0); + + // Quantize NdotL into numBands discrete steps. + float quantized = min(floor(NdotL * numBands), numBands - 1.0) / (numBands - 1.0); + lightAccum += quantized * lights[i].color.r; + } + } + + lightAccum = clamp(lightAccum, 0.0, 1.0); + gl_FragColor = vec4(baseColor * lightAccum, texColor.a * colDiffuse.a); +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl120/cel.vs b/examples/shaders/resources/shaders/glsl120/cel.vs new file mode 100644 index 000000000..3c736aec7 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/cel.vs @@ -0,0 +1,48 @@ +#version 120 + +attribute vec3 vertexPosition; +attribute vec2 vertexTexCoord; +attribute vec3 vertexNormal; +attribute vec4 vertexColor; + +uniform mat4 mvp; +uniform mat4 matModel; + +varying vec3 fragPosition; +varying vec2 fragTexCoord; +varying vec4 fragColor; +varying vec3 fragNormal; + +// inverse() and transpose() are not built-in until GLSL 1.40 +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; +} + +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() +{ + fragPosition = vec3(matModel * vec4(vertexPosition, 1.0)); + fragTexCoord = vertexTexCoord; + fragColor = vertexColor; + + mat3 normalMatrix = transpose(inverse(mat3(matModel))); + fragNormal = normalize(normalMatrix * vertexNormal); + + gl_Position = mvp * vec4(vertexPosition, 1.0); +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl120/outline_hull.fs b/examples/shaders/resources/shaders/glsl120/outline_hull.fs new file mode 100644 index 000000000..b6e4fc828 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/outline_hull.fs @@ -0,0 +1,6 @@ +#version 120 + +void main() +{ + gl_FragColor = vec4(0.05, 0.05, 0.05, 1.0); +} diff --git a/examples/shaders/resources/shaders/glsl120/outline_hull.vs b/examples/shaders/resources/shaders/glsl120/outline_hull.vs new file mode 100644 index 000000000..7ad125be1 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/outline_hull.vs @@ -0,0 +1,15 @@ +#version 120 + +attribute vec3 vertexPosition; +attribute vec3 vertexNormal; +attribute vec2 vertexTexCoord; +attribute vec4 vertexColor; + +uniform mat4 mvp; +uniform float outlineThickness; + +void main() +{ + vec3 extruded = vertexPosition + vertexNormal * outlineThickness; + gl_Position = mvp * vec4(extruded, 1.0); +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl330/cel.fs b/examples/shaders/resources/shaders/glsl330/cel.fs new file mode 100644 index 000000000..78a2097c2 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/cel.fs @@ -0,0 +1,60 @@ +#version 330 + +in vec3 fragPosition; +in vec2 fragTexCoord; +in vec4 fragColor; +in vec3 fragNormal; + +// Raylib standard uniforms +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +// View position for future specular / fresnel use. +uniform vec3 viewPos; + +// Number of discrete toon bands (2 = hard binary, 10 = default, 20 = near-smooth). +uniform float numBands; + +// rlights.h compatible light block. +struct Light { + int enabled; + int type; // 0 = directional, 1 = point + vec3 position; + vec3 target; + vec4 color; + float attenuation; +}; +uniform Light lights[4]; + +out vec4 finalColor; + +void main() { + vec4 texColor = texture(texture0, fragTexCoord); + vec3 baseColor = texColor.rgb * fragColor.rgb * colDiffuse.rgb; + vec3 norm = normalize(fragNormal); + + float lightAccum = 0.08; // ambient floor + + for (int i = 0; i < 4; i++) { + if (lights[i].enabled == 0) continue; + + vec3 lightDir; + if (lights[i].type == 0) { + // Directional: direction is from position toward target. + lightDir = normalize(lights[i].position - lights[i].target); + } else { + // Point: direction from surface to light. + lightDir = normalize(lights[i].position - fragPosition); + } + + float NdotL = max(dot(norm, lightDir), 0.0); + + // Quantize NdotL into numBands discrete steps. + // min() guards against NdotL == 1.0 producing an out-of-range index. + float quantized = min(floor(NdotL * numBands), numBands - 1.0) / (numBands - 1.0); + lightAccum += quantized * lights[i].color.r; + } + + lightAccum = clamp(lightAccum, 0.0, 1.0); + finalColor = vec4(baseColor * lightAccum, texColor.a * colDiffuse.a); +} diff --git a/examples/shaders/resources/shaders/glsl330/cel.vs b/examples/shaders/resources/shaders/glsl330/cel.vs new file mode 100644 index 000000000..37a8d200d --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/cel.vs @@ -0,0 +1,25 @@ +#version 330 + +// Raylib standard attributes +in vec3 vertexPosition; +in vec2 vertexTexCoord; +in vec3 vertexNormal; +in vec4 vertexColor; + +// Raylib standard uniforms +uniform mat4 mvp; +uniform mat4 matModel; +uniform mat4 matNormal; + +out vec3 fragPosition; +out vec2 fragTexCoord; +out vec4 fragColor; +out vec3 fragNormal; + +void main() { + fragPosition = vec3(matModel * vec4(vertexPosition, 1.0)); + fragTexCoord = vertexTexCoord; + fragColor = vertexColor; + fragNormal = normalize(vec3(matNormal * vec4(vertexNormal, 0.0))); + gl_Position = mvp * vec4(vertexPosition, 1.0); +} diff --git a/examples/shaders/resources/shaders/glsl330/outline_hull.fs b/examples/shaders/resources/shaders/glsl330/outline_hull.fs new file mode 100644 index 000000000..c8a33de08 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/outline_hull.fs @@ -0,0 +1,7 @@ +#version 330 + +out vec4 finalColor; + +void main() { + finalColor = vec4(0.05, 0.05, 0.05, 1.0); +} diff --git a/examples/shaders/resources/shaders/glsl330/outline_hull.vs b/examples/shaders/resources/shaders/glsl330/outline_hull.vs new file mode 100644 index 000000000..2e350201e --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/outline_hull.vs @@ -0,0 +1,15 @@ +#version 330 + +in vec3 vertexPosition; +in vec3 vertexNormal; +in vec2 vertexTexCoord; +in vec4 vertexColor; + +uniform mat4 mvp; +uniform float outlineThickness; + +void main() { + // Extrude vertex along its normal to create the hull. + vec3 extruded = vertexPosition + vertexNormal * outlineThickness; + gl_Position = mvp * vec4(extruded, 1.0); +} diff --git a/examples/shaders/shaders_cel_shading.c b/examples/shaders/shaders_cel_shading.c new file mode 100644 index 000000000..cab86a26f --- /dev/null +++ b/examples/shaders/shaders_cel_shading.c @@ -0,0 +1,202 @@ +/******************************************************************************************* +* +* raylib [shaders] example - cel shading +* +* 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 +* +* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3) +* +* Example contributed by Gleb A (@ggrizzly) +* +* 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) 2015-2026 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" +#include "raymath.h" +#include "rlgl.h" +#include +#include + +#define RLIGHTS_IMPLEMENTATION +#include "rlights.h" + +#if defined(PLATFORM_DESKTOP) + #define GLSL_VERSION 330 +#else // PLATFORM_ANDROID, PLATFORM_WEB + #define GLSL_VERSION 100 +#endif + +//------------------------------------------------------------------------------------ +// Model table: path, optional diffuse texture path (NULL = embedded), draw scale +//------------------------------------------------------------------------------------ +typedef struct { + const char *modelPath; + const char *texturePath; // NULL for GLB files with embedded textures + float scale; + float outlineThickness; +} ModelInfo; + +static const ModelInfo MODEL = { "resources/models/old_car_new.glb", NULL, 0.75f, 0.005f }; + + +//------------------------------------------------------------------------------------ +// Load model and its diffuse texture (if any). Does NOT assign a shader. +//------------------------------------------------------------------------------------ +static Model celLoadModel() +{ + Model model = LoadModel(MODEL.modelPath); + + if (MODEL.texturePath != NULL) + { + Texture2D tex = LoadTexture(MODEL.texturePath); + model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = tex; + } + + return model; +} + +static void ApplyShaderToModel(Model model, Shader shader) +{ + model.materials[0].shader = shader; +} + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + SetConfigFlags(FLAG_MSAA_4X_HINT); + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - cel shading"); + + Camera camera = { 0 }; + camera.position = (Vector3){ 9.0f, 6.0f, 9.0f }; + camera.target = (Vector3){ 0.0f, 1.0f, 0.0f }; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + camera.fovy = 45.0f; + camera.projection = CAMERA_PERSPECTIVE; + + // Load cel shader + Shader celShader = LoadShader(TextFormat("resources/shaders/glsl%i/cel.vs", GLSL_VERSION), + TextFormat("resources/shaders/glsl%i/cel.fs", GLSL_VERSION)); + celShader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(celShader, "viewPos"); + + // numBands: controls toon quantization steps (2 = hard binary, 20 = near-smooth) + float numBands = 10.0f; + int numBandsLoc = GetShaderLocation(celShader, "numBands"); + SetShaderValue(celShader, numBandsLoc, &numBands, SHADER_UNIFORM_FLOAT); + + // Inverted-hull outline shader: draws back faces extruded along normals + Shader outlineShader = LoadShader( + TextFormat("resources/shaders/glsl%i/outline_hull.vs", GLSL_VERSION), + TextFormat("resources/shaders/glsl%i/outline_hull.fs", GLSL_VERSION)); + int outlineThicknessLoc = GetShaderLocation(outlineShader, "outlineThickness"); + + // Single directional white light, angled so toon bands are visible on the model sides. + // Spins opposite to CAMERA_ORBITAL (0.5 rad/s) so lighting changes as you watch. + Light lights[MAX_LIGHTS] = { 0 }; + lights[0] = CreateLight(LIGHT_DIRECTIONAL, (Vector3){ 50.0f, 50.0f, 50.0f }, Vector3Zero(), WHITE, celShader); + + + bool celEnabled = true; + bool outlineEnabled = true; + + Model model = celLoadModel(); + Shader defaultShader = model.materials[0].shader; + ApplyShaderToModel(model, celShader); + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&camera, CAMERA_ORBITAL); + + float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z }; + SetShaderValue(celShader, celShader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3); + + // [Z] Toggle cel shading on/off + if (IsKeyPressed(KEY_Z)) + { + celEnabled = !celEnabled; + ApplyShaderToModel(model, celEnabled ? celShader : defaultShader); + } + + // [C] Toggle outline on/off + if (IsKeyPressed(KEY_C)) outlineEnabled = !outlineEnabled; + + // [Q/E] Decrease/increase toon band count (press or hold to repeat) + if (IsKeyPressed(KEY_E) || IsKeyPressedRepeat(KEY_E)) numBands = Clamp(numBands + 1.0f, 2.0f, 20.0f); + if (IsKeyPressed(KEY_Q) || IsKeyPressedRepeat(KEY_Q)) numBands = Clamp(numBands - 1.0f, 2.0f, 20.0f); + SetShaderValue(celShader, numBandsLoc, &numBands, SHADER_UNIFORM_FLOAT); + + // Spin light opposite to CAMERA_ORBITAL (0.5 rad/s), angled 45 degrees off vertical + float t = (float)GetTime(); + lights[0].position = (Vector3){ + sinf(-t * 0.3f) * 5.0f, + 5.0f, + cosf(-t * 0.3f) * 5.0f + }; + + for (int i = 0; i < MAX_LIGHTS; i++) UpdateLightValues(celShader, lights[i]); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + if (outlineEnabled) + { + // Outline pass: cull front faces, draw extruded back faces as silhouette + float thickness = MODEL.outlineThickness; + SetShaderValue(outlineShader, outlineThicknessLoc, &thickness, SHADER_UNIFORM_FLOAT); + rlSetCullFace(RL_CULL_FACE_FRONT); + ApplyShaderToModel(model, outlineShader); + DrawModel(model, Vector3Zero(), MODEL.scale, WHITE); + ApplyShaderToModel(model, celEnabled ? celShader : defaultShader); + rlSetCullFace(RL_CULL_FACE_BACK); + } + + DrawModel(model, Vector3Zero(), MODEL.scale, WHITE); + DrawSphereEx(lights[0].position, 0.2f, 50, 50, YELLOW); // Light position indicator + DrawGrid(10, 10.0f); + + EndMode3D(); + + DrawFPS(10, 10); + DrawText(TextFormat("Cel: %s [Z]", celEnabled ? "ON" : "OFF"), 10, 65, 20, celEnabled ? DARKGREEN : DARKGRAY); + DrawText(TextFormat("Outline: %s [C]", outlineEnabled ? "ON" : "OFF"), 10, 90, 20, outlineEnabled ? DARKGREEN : DARKGRAY); + DrawText(TextFormat("Bands: %.0f [Q/E]", numBands), 10, 115, 20, DARKGRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadModel(model); + UnloadShader(celShader); + UnloadShader(outlineShader); + CloseWindow(); + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/shaders/shaders_cel_shading.png b/examples/shaders/shaders_cel_shading.png new file mode 100644 index 0000000000000000000000000000000000000000..51b98225e093acc13d235192e2213a3c760da4a1 GIT binary patch literal 45710 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� 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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 literal 0 HcmV?d00001 From c8d1f3e750d5fe96a1745b718382429e49efd176 Mon Sep 17 00:00:00 2001 From: JoeStrout Date: Fri, 6 Mar 2026 08:52:58 -0700 Subject: [PATCH 135/319] Make LoadFontFromImage limit its scanning to image dimensions. (#5626) --- src/rtext.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index ff59d667e..c563b1185 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -446,7 +446,8 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) int charHeight = 0; int j = 0; - while (!COLOR_EQUAL(pixels[(lineSpacing + j)*image.width + charSpacing], key)) j++; + while ((lineSpacing + j) < image.height && + !COLOR_EQUAL(pixels[(lineSpacing + j)*image.width + charSpacing], key)) j++; charHeight = j; @@ -469,7 +470,8 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) int charWidth = 0; - while (!COLOR_EQUAL(pixels[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*image.width + xPosToRead + charWidth], key)) charWidth++; + while ((xPosToRead + charWidth) < image.width && + !COLOR_EQUAL(pixels[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*image.width + xPosToRead + charWidth], key)) charWidth++; tempCharRecs[index].width = (float)charWidth; From 12039ba7d0ac51949e5bee269cd612a1f9e8e12f Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 6 Mar 2026 16:53:40 +0100 Subject: [PATCH 136/319] Update raylib.h --- src/raylib.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 6a23400c6..ad4e4133b 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1429,8 +1429,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, 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 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) From a0ffefcb9d5f4d121f0ad3c23b058503912d5976 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 6 Mar 2026 16:53:56 +0100 Subject: [PATCH 137/319] Update rlgl.h --- src/rlgl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index d2f65700a..bb6411ee2 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -37,10 +37,10 @@ * If not defined, the library is in header only mode and can be included in other headers * or source files without problems. But only ONE file should hold the implementation * -* #define RLGL_SHOW_GL_DETAILS_INFO +* #if RLGL_SHOW_GL_DETAILS_INFO * Show OpenGL extensions and capabilities detailed logs on init * -* #define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT +* #if RLGL_ENABLE_OPENGL_DEBUG_CONTEXT * Enable debug context (only available on OpenGL 4.3) * * rlgl capabilities could be customized defining some internal From eed30b6f90158b0f10055bcbc0b6e23625d25e4a Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 6 Mar 2026 16:54:15 +0100 Subject: [PATCH 138/319] Update HISTORY.md --- HISTORY.md | 47 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 02e226437..53fbc44c0 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -449,13 +449,13 @@ Some numbers for this release: Highlights for `raylib 5.0`: - **`rcore` module platform-split**: Probably the biggest raylib redesign in the last 10 years. raylib started as a library targeting 3 desktop platforms: `Windows`, `Linux` and `macOS` (thanks to `GLFW` underlying library) but with the years support for several new platforms has been added (`Android`, `Web`, `Rapsberry Pi`, `RPI native`...); lot of the platform code was shared so the logic was all together on `rcore.c` module, separated by compilation flags. This approach was very handy but also made it very difficult to support new platforms and specially painful for contributors not familiar with the module, navigating +8000 lines of code in a single file. A big redesign was really needed but the amount of work required was humungous and quite scary for a solo-developer like me, moreover considering that everything was working and the chances to break things were really high. Fortunately, some contributors were ready for the task (@ubkp, @michaelfiber, @Bigfoot71) and thanks to their initiative and super-hard work, the `rcore` [platform split](https://github.com/raysan5/raylib/blob/master/src/platforms) has been possible! This new raylib architecture greatly improves the platforms maintenance but also greatly simplifies the addition of new platforms. A [`platforms/rcore_template.c`](https://github.com/raysan5/raylib/blob/master/src/platforms/rcore_template.c) file is provided with the required structure and functions to be filled for the addition of new platforms, actually it has been simplified to mostly filling some pre-defined functions: `InitPlatform()`, `ClosePlatform`, `PollInputEvents`... Undoubtedly, **this redesign opens the doors to a new era for raylib**, letting the users to plug new platforms as desired. - + - **`NEW` Platform backend supported: SDL**: Thanks to the new `rcore` platform-split, the addition of new platforms/backends to raylib has been greatly simplified. As a proof of concept, [`SDL2`](https://libsdl.org/) platform backend has been added to raylib as an alternative for `GLFW` library for desktop builds: [`platforms/rcore_desktop_sdl`](https://github.com/raysan5/raylib/blob/master/src/platforms/rcore_desktop_sdl.c). Lot of work has been put to provide exactly the same features as the other platforms and carefully test the new implementation. Now `SDL2` fans can use this new backend, just providing the required include libraries on compilation and linkage (not included in raylib, like `GLFW`). `SDL` backend support also **eases the process of supporting a wider range of platforms** that already support `SDL`. - **`NEW` Platform backend supported: Nintendo Switch (closed source)**: The addition of the `SDL` backend was quite a challenge but to really verify the robustness and ease of the new platform plugin system, adding support for a console was a more demanding adventure. Surprisingly, only two days of work were required to add support for `Nintendo Switch` to raylib! Implementation result showed an outstanding level of simplicity, with a **self-contained module** (`rcore_swith.cpp`) supporting graphics and inputs. Unfortunately this module can not be open-sourced due to licensing restrictions. - **`NEW` Splines drawing and evaluation API**: A complete set of functions has been added to [draw](https://github.com/raysan5/raylib/blob/master/src/raylib.h#L1258) and [evaluate](https://github.com/raysan5/raylib/blob/master/src/raylib.h#L1270) different types of splines: `Linear`, `Basis`, `Catmull-Rom`, `Quadratic Bezier` and `Cubic Bezier`. Splines are extremely useful for game development (describe paths, control NPC movement...) but they can also be very useful on tools development (node-conections, elements-movement, 3d modelling, animations...). This was the missing feature on the raylib [`rshapes`](https://github.com/raysan5/raylib/blob/master/src/rshapes.h) module to make it complete! Note that `rshapes` module can also be used independently of raylib just providing the **only 6 functions required for vertex definition and drawing**. - + - **`NEW` Pseudo-random numbers generator: rprand**: After several years of users asking for this missing piece, a brand new pseudo-random generator module has been added to raylib. [`rprand`](https://github.com/raysan5/raylib/blob/master/src/external/rprand.h) implements the `Xoshiro128**` algorithm combined with `SplitMix64`, specially suited for **fast software pseudo-random numbers generation**. The module also implies some useful functions to generate non-repetitive random numbers sequences, functionality exposed by raylib. usage of this module can be controlled by a compilation flag, in case the default libc `rand()` function was preferred. - **`NEW` Automation Events System API**: This new system was first added in `raylib 4.0` as an experimental feature but it was a bit clumsy and there was no API exposed to users. For the new `raylib 5.0` the system has been redesigned and [proper API](https://github.com/raysan5/raylib/blob/master/src/raylib.h#L1135) added for the users. With this new events automation system, users can **record input events for later replay**, very useful feature for testing automation, tutorials generation, assisted game playing, in-game cinematics, speedruns saving or even AI assited game playing! @@ -464,8 +464,8 @@ Highlights for `raylib 5.0`: - **`NEW` raylib web examples functionality**: Beside the addition of several new examples, the web examples functionality has been improved. Examples have been organized by [complexity level](https://www.raylib.com/examples.html), marked with one star for simple examples and up to 4 stars for more complex ones. A new option has been added to web to allow to **filter examples by function-name** usage, to ease the learning process when looking for an usage example of some function. Finally, **open-graph metadata** information has been added to all examples individual webpages, improving a the visuals and information when sharing those webpages on social networks, sharing the example screenshot and details. -As always, those are only some highlights of the new `raylib 5.0` but there is many more improvements! Support for 16-bit HDR images/textures, SVG loading and scaling support, new OpenGL ES 3.0 graphic backend, new image gradient generators, sound alias loading, improved 3d models loading, multiple optimizations, new bindings, CodeQL integration and much more! - +As always, those are only some highlights of the new `raylib 5.0` but there is many more improvements! Support for 16-bit HDR images/textures, SVG loading and scaling support, new OpenGL ES 3.0 graphic backend, new image gradient generators, sound alias loading, improved 3d models loading, multiple optimizations, new bindings, CodeQL integration and much more! + Make sure to check raylib [CHANGELOG]([CHANGELOG](https://github.com/raysan5/raylib/blob/master/CHANGELOG)) for a detailed list of changes! Undoubtedly, this is the **biggest raylib update in 10 years**. Many new features and improvements with a special focus on maintainability and long-term sustainability. **Undoubtedly, this is the raylib of the future**. @@ -492,16 +492,16 @@ Highlights for `raylib 5.5`: - **`NEW` raylib project creator tool**: A brand new tool developed to help raylib users to **setup new projects in a professional way**. `raylib project creator` generates a complete project structure with **multiple build systems ready-to-use** and **GitHub CI/CD actions pre-configured**. It only requires providing some C files and basic project parameters! The tools is [free and open-source](https://raysan5.itch.io/raylib-project-creator), and [it can be used online](https://raysan5.itch.io/raylib-project-creator)!. - **`NEW` Platform backend supported: RGFW**: Thanks to the `rcore` platform-split implemented in `raylib 5.0`, **adding new platforms backends has been greatly simplified**, new backends can be added using provided template, self-contained in a single C module, completely portable. A new platform backend has been added: [`RGFW`](https://github.com/raysan5/raylib/blob/master/src/platforms/rcore_desktop_rgfw.c). `RGFW` is a **new single-file header-only portable library** ([`RGFW.h`](https://github.com/ColleagueRiley/RGFW)) intended for platform-functionality management (windowing and inputs); in this case for **desktop platforms** (Windows, Linux, macOS) but also for **Web platform**. It adds a new alternative to the already existing `GLFW` and `SDL` platform backends. - + - **`NEW` Platform backend version supported: SDL3**: Previous `raylib 5.0` added support for `SDL2` library, and `raylib 5.5` not only improves SDL2 functionality, with several issues reviewed, but also adds support for the recently released big SDL update in years: [`SDL3`](https://wiki.libsdl.org/SDL3/FrontPage). Now users can **select at compile time the desired SDL version to use**, increasing the number of potential platforms supported in the future! - + - **`NEW` Retro-console platforms supported: Dreamcast, N64, PSP, PSVita, PS4**: Thanks to the platform-split on `raylib 5.0`, **supporting new platform backends is easier than ever!** Along the raylib `rlgl` module support for the `OpenGL 1.1` graphics API, it opened the door to [**multiple homebrew retro-consoles backend implementations!**](https://github.com/raylib4Consoles) It's amazing to see raylib running on +20 year old consoles like [Dreamcast](https://github.com/raylib4Consoles/raylib4Dreamcast), [PSP](https://github.com/raylib4Consoles/raylib4Psp) or [PSVita](https://github.com/psp2dev/raylib4Vita), considering the hardware constraints of those platforms and proves **raylib outstanding versability!** Those additional platforms can be found in separate repositories and have been created by the amazing programmer Antonio Jose Ramos Marquez (@psxdev). - + - **`NEW` GPU Skinning support**: After lots of requests for this feature, it has been finally added to raylib thanks to the contributor Daniel Holden (@orangeduck), probably the developer that has further pushed models animations with raylib, developing two amazing tools to visualize and test animations: [GenoView](https://github.com/orangeduck/GenoView) and [BVHView](https://github.com/orangeduck/BVHView). Adding GPU skinning was a tricky feature, considering it had to be **available for all raylib supported platforms**, including limited ones like Raspberry Pi with OpenGL ES 2.0, where some advance OpenGL features are not available (UBO, SSBO, Transform Feedback) but a multi-platform solution was found to make it possible. A new example, [`models_gpu_skinning`](https://github.com/raysan5/raylib/blob/master/examples/models/models_gpu_skinning.c) has been added to illustrate this new functionality. As an extra, previous existing CPU animation system has been greatly improved, multiplying performance by a factor (simplifiying required maths). - + - **`NEW` [`raymath`](https://github.com/raysan5/raylib/blob/master/src/raymath.h) C++ operators**: After several requested for this feature, C++ math operators for `Vector2`, `Vector3`, `Vector4`, `Quaternion` and `Matrix` has been added to `raymath` as an extension to current implementation. Despite being only available for C++ because C does not support it, these operators **simplify C++ code when doing math operations**. -Beside those new big features, `raylib 5.5` comes with MANY other improvements: +Beside those new big features, `raylib 5.5` comes with MANY other improvements: - Normals support on batching system - Clipboard images reading support @@ -512,7 +512,7 @@ Beside those new big features, `raylib 5.5` comes with MANY other improvements: - Improved GLTF animations loading ...and [much much more](https://github.com/raysan5/raylib/blob/master/CHANGELOG), including **many functions reviews and new functions added!** - + Make sure to check raylib [CHANGELOG](https://github.com/raysan5/raylib/blob/master/CHANGELOG) for a detailed list of changes! To end with, I want to **thank all the contributors (+640!**) that along the years have **greatly improved raylib** and pushed it further and better day after day. Thanks to all of them, raylib is the amazing library it is today. @@ -522,3 +522,30 @@ Last but not least, I want to thank **raylib sponsors and all the raylib communi **After 11 years of development, `raylib 5.5` is the best raylib ever.** **Enjoy programming with raylib!** :) + +notes on raylib 6.0 +------------------- + +A new raylib release is finally ready, after almost 1.5 years since last release. This is the biggest release ever with many improvements to the library, thanks to the support of many amazing contributors and also thanks to the financial support of NLnet/NGI0 funds. + +Some astonishing numbers for this release: + + - **+310** closed issues (for a TOTAL of **+2120**!) + - **+1800** commits since previous RELEASE (for a TOTAL of **+9550**!) + - **18** functions ADDED to raylib API (for a TOTAL of **598**!) + - **??** functions REVIEWED with fixes and improvements + - **+190** new contributors (for a TOTAL of **+830**!) + +Highlights for `raylib 6.0`: + + - New Software Renderer backend [rlsw] + - New rcore platform backend: Memory (memory buffer) + - New rcore platform backend: Win32 (experimental) + - New rcore platform backend: Emscripten (experimental) + - Redesigned Skeletal Animation System + - Redesigned fullscreen modes and high-dpi content scaling + - Redesigned Build Config System [config.h] + - New File System API + - New Text Management API + - New tool: [rexm] raylib examples manager + From 1839b3edb04b99300c2ade2b9a80fd49c75cdc90 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 6 Mar 2026 17:11:44 +0100 Subject: [PATCH 139/319] Update shaders_cel_shading.c --- examples/shaders/shaders_cel_shading.c | 101 +++++++++---------------- 1 file changed, 37 insertions(+), 64 deletions(-) diff --git a/examples/shaders/shaders_cel_shading.c b/examples/shaders/shaders_cel_shading.c index cab86a26f..133b8db9e 100644 --- a/examples/shaders/shaders_cel_shading.c +++ b/examples/shaders/shaders_cel_shading.c @@ -9,20 +9,20 @@ * * NOTE: Shaders used in this example are #version 330 (OpenGL 3.3) * -* Example contributed by Gleb A (@ggrizzly) +* Example contributed by Gleb A (@ggrizzly) 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) 2015-2026 Ramon Santamaria (@raysan5) +* Copyright (c) 2026 Gleb A (@ggrizzly) * ********************************************************************************************/ #include "raylib.h" #include "raymath.h" #include "rlgl.h" -#include -#include + +#include // Required for: sinf(), cosf() #define RLIGHTS_IMPLEMENTATION #include "rlights.h" @@ -33,40 +33,6 @@ #define GLSL_VERSION 100 #endif -//------------------------------------------------------------------------------------ -// Model table: path, optional diffuse texture path (NULL = embedded), draw scale -//------------------------------------------------------------------------------------ -typedef struct { - const char *modelPath; - const char *texturePath; // NULL for GLB files with embedded textures - float scale; - float outlineThickness; -} ModelInfo; - -static const ModelInfo MODEL = { "resources/models/old_car_new.glb", NULL, 0.75f, 0.005f }; - - -//------------------------------------------------------------------------------------ -// Load model and its diffuse texture (if any). Does NOT assign a shader. -//------------------------------------------------------------------------------------ -static Model celLoadModel() -{ - Model model = LoadModel(MODEL.modelPath); - - if (MODEL.texturePath != NULL) - { - Texture2D tex = LoadTexture(MODEL.texturePath); - model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = tex; - } - - return model; -} - -static void ApplyShaderToModel(Model model, Shader shader) -{ - model.materials[0].shader = shader; -} - //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -81,16 +47,24 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [shaders] example - cel shading"); Camera camera = { 0 }; - camera.position = (Vector3){ 9.0f, 6.0f, 9.0f }; - camera.target = (Vector3){ 0.0f, 1.0f, 0.0f }; - camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; - camera.fovy = 45.0f; + camera.position = (Vector3){ 9.0f, 6.0f, 9.0f }; + camera.target = (Vector3){ 0.0f, 1.0f, 0.0f }; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + camera.fovy = 45.0f; camera.projection = CAMERA_PERSPECTIVE; + + // Load model + Model model = LoadModel("resources/models/old_car_new.glb"); // Load cel shader - Shader celShader = LoadShader(TextFormat("resources/shaders/glsl%i/cel.vs", GLSL_VERSION), - TextFormat("resources/shaders/glsl%i/cel.fs", GLSL_VERSION)); + Shader celShader = LoadShader( + TextFormat("resources/shaders/glsl%i/cel.vs", GLSL_VERSION), + TextFormat("resources/shaders/glsl%i/cel.fs", GLSL_VERSION)); celShader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(celShader, "viewPos"); + + // Apply cel shader to model, keep copy of default shader + Shader defaultShader = model.materials[0].shader; + model.materials[0].shader = celShader; // numBands: controls toon quantization steps (2 = hard binary, 20 = near-smooth) float numBands = 10.0f; @@ -108,14 +82,9 @@ int main(void) Light lights[MAX_LIGHTS] = { 0 }; lights[0] = CreateLight(LIGHT_DIRECTIONAL, (Vector3){ 50.0f, 50.0f, 50.0f }, Vector3Zero(), WHITE, celShader); - - bool celEnabled = true; + bool celEnabled = true; bool outlineEnabled = true; - Model model = celLoadModel(); - Shader defaultShader = model.materials[0].shader; - ApplyShaderToModel(model, celShader); - SetTargetFPS(60); //-------------------------------------------------------------------------------------- @@ -133,7 +102,8 @@ int main(void) if (IsKeyPressed(KEY_Z)) { celEnabled = !celEnabled; - ApplyShaderToModel(model, celEnabled ? celShader : defaultShader); + if (celEnabled) model.materials[0].shader = celShader; // Apply cel shader to model + else model.materials[0].shader = defaultShader; // Apply default shader to model } // [C] Toggle outline on/off @@ -146,11 +116,7 @@ int main(void) // Spin light opposite to CAMERA_ORBITAL (0.5 rad/s), angled 45 degrees off vertical float t = (float)GetTime(); - lights[0].position = (Vector3){ - sinf(-t * 0.3f) * 5.0f, - 5.0f, - cosf(-t * 0.3f) * 5.0f - }; + lights[0].position = (Vector3){ sinf(-t*0.3f)*5.0f, 5.0f, cosf(-t*0.3f)*5.0f }; for (int i = 0; i < MAX_LIGHTS; i++) UpdateLightValues(celShader, lights[i]); //---------------------------------------------------------------------------------- @@ -166,25 +132,31 @@ int main(void) if (outlineEnabled) { // Outline pass: cull front faces, draw extruded back faces as silhouette - float thickness = MODEL.outlineThickness; + float thickness = 0.005f; SetShaderValue(outlineShader, outlineThicknessLoc, &thickness, SHADER_UNIFORM_FLOAT); + rlSetCullFace(RL_CULL_FACE_FRONT); - ApplyShaderToModel(model, outlineShader); - DrawModel(model, Vector3Zero(), MODEL.scale, WHITE); - ApplyShaderToModel(model, celEnabled ? celShader : defaultShader); + + model.materials[0].shader = outlineShader; + + DrawModel(model, Vector3Zero(), 0.75f, WHITE); + + if (celEnabled) model.materials[0].shader = celShader; // Apply cel shader to model + else model.materials[0].shader = defaultShader; // Apply default shader to model + rlSetCullFace(RL_CULL_FACE_BACK); } - DrawModel(model, Vector3Zero(), MODEL.scale, WHITE); + DrawModel(model, Vector3Zero(), 0.75f, WHITE); DrawSphereEx(lights[0].position, 0.2f, 50, 50, YELLOW); // Light position indicator DrawGrid(10, 10.0f); EndMode3D(); DrawFPS(10, 10); - DrawText(TextFormat("Cel: %s [Z]", celEnabled ? "ON" : "OFF"), 10, 65, 20, celEnabled ? DARKGREEN : DARKGRAY); - DrawText(TextFormat("Outline: %s [C]", outlineEnabled ? "ON" : "OFF"), 10, 90, 20, outlineEnabled ? DARKGREEN : DARKGRAY); - DrawText(TextFormat("Bands: %.0f [Q/E]", numBands), 10, 115, 20, DARKGRAY); + DrawText(TextFormat("Cel: %s [Z]", celEnabled? "ON" : "OFF"), 10, 65, 20, celEnabled? DARKGREEN : DARKGRAY); + DrawText(TextFormat("Outline: %s [C]", outlineEnabled? "ON" : "OFF"), 10, 90, 20, outlineEnabled? DARKGREEN : DARKGRAY); + DrawText(TextFormat("Bands: %.0f [Q/E]", numBands), 10, 115, 20, DARKGRAY); EndDrawing(); //---------------------------------------------------------------------------------- @@ -195,6 +167,7 @@ int main(void) UnloadModel(model); UnloadShader(celShader); UnloadShader(outlineShader); + CloseWindow(); //-------------------------------------------------------------------------------------- From 99cab6d3a72a044b68d25b12b1c406854d2390dd Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 6 Mar 2026 17:11:48 +0100 Subject: [PATCH 140/319] Update rtext.c --- src/rtext.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index c563b1185..1cffd3bb4 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -446,7 +446,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) int charHeight = 0; int j = 0; - while ((lineSpacing + j) < image.height && + while (((lineSpacing + j) < image.height) && !COLOR_EQUAL(pixels[(lineSpacing + j)*image.width + charSpacing], key)) j++; charHeight = j; @@ -470,8 +470,8 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) int charWidth = 0; - while ((xPosToRead + charWidth) < image.width && - !COLOR_EQUAL(pixels[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*image.width + xPosToRead + charWidth], key)) charWidth++; + while (((xPosToRead + charWidth) < image.width) && + !COLOR_EQUAL(pixels[(lineSpacing + (charHeight + lineSpacing)*lineToRead)*image.width + xPosToRead + charWidth], key)) charWidth++; tempCharRecs[index].width = (float)charWidth; From 32005b9edf7cf8d9d7dc2c0aea9c3221b71f30c5 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 8 Mar 2026 23:00:05 +0100 Subject: [PATCH 141/319] Update textures_bunnymark.c --- examples/textures/textures_bunnymark.c | 33 ++++++++++++++++---------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/examples/textures/textures_bunnymark.c b/examples/textures/textures_bunnymark.c index 3279abb8f..a04741227 100644 --- a/examples/textures/textures_bunnymark.c +++ b/examples/textures/textures_bunnymark.c @@ -17,7 +17,7 @@ #include // Required for: malloc(), free() -#define MAX_BUNNIES 50000 // 50K bunnies limit +#define MAX_BUNNIES 80000 // 80K bunnies limit // This is the maximum amount of elements (quads) per batch // NOTE: This value is defined in [rlgl] module and can be changed there @@ -51,7 +51,9 @@ int main(void) int bunniesCount = 0; // Bunnies counter - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + bool paused = false; + + //SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- // Main game loop @@ -67,8 +69,8 @@ int main(void) if (bunniesCount < MAX_BUNNIES) { bunnies[bunniesCount].position = GetMousePosition(); - bunnies[bunniesCount].speed.x = (float)GetRandomValue(-250, 250)/60.0f; - bunnies[bunniesCount].speed.y = (float)GetRandomValue(-250, 250)/60.0f; + bunnies[bunniesCount].speed.x = (float)GetRandomValue(-250, 250); + bunnies[bunniesCount].speed.y = (float)GetRandomValue(-250, 250); bunnies[bunniesCount].color = (Color){ GetRandomValue(50, 240), GetRandomValue(80, 240), GetRandomValue(100, 240), 255 }; @@ -77,16 +79,21 @@ int main(void) } } - // Update bunnies - for (int i = 0; i < bunniesCount; i++) - { - bunnies[i].position.x += bunnies[i].speed.x; - bunnies[i].position.y += bunnies[i].speed.y; + if (IsKeyPressed(KEY_P)) paused = !paused; - if (((bunnies[i].position.x + (float)texBunny.width/2) > GetScreenWidth()) || - ((bunnies[i].position.x + (float)texBunny.width/2) < 0)) bunnies[i].speed.x *= -1; - if (((bunnies[i].position.y + (float)texBunny.height/2) > GetScreenHeight()) || - ((bunnies[i].position.y + (float)texBunny.height/2 - 40) < 0)) bunnies[i].speed.y *= -1; + if (!paused) + { + // Update bunnies + for (int i = 0; i < bunniesCount; i++) + { + bunnies[i].position.x += bunnies[i].speed.x*GetFrameTime(); + bunnies[i].position.y += bunnies[i].speed.y*GetFrameTime(); + + if (((bunnies[i].position.x + (float)texBunny.width/2) > GetScreenWidth()) || + ((bunnies[i].position.x + (float)texBunny.width/2) < 0)) bunnies[i].speed.x *= -1; + if (((bunnies[i].position.y + (float)texBunny.height/2) > GetScreenHeight()) || + ((bunnies[i].position.y + (float)texBunny.height/2 - 40) < 0)) bunnies[i].speed.y *= -1; + } } //---------------------------------------------------------------------------------- From 78023ffca5d0e217654737b1317e228920c88c57 Mon Sep 17 00:00:00 2001 From: victorberdugo1 Date: Mon, 9 Mar 2026 11:50:19 +0000 Subject: [PATCH 142/319] Fix memory leak in LoadGLTF when model has no bones (#5629) Co-authored-by: Victor --- src/rmodels.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index 0dd4f0634..3da1c0be7 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5511,8 +5511,10 @@ static Model LoadGLTF(const char *fileName) if (dracoCompression) { - return model; TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load glTF data", fileName); + cgltf_free(data); + UnloadFileData(fileData); + return model; } TRACELOG(LOG_DEBUG, " > Primitives (triangles only) count based on hierarchy : %i", primitivesCount); @@ -6334,6 +6336,14 @@ static Model LoadGLTF(const char *fileName) for (int j = 0; j < model.skeleton.boneCount; j++) model.boneMatrices[j] = MatrixIdentity(); //---------------------------------------------------------------------------------------------------- + if (model.skeleton.boneCount == 0) + { + RL_FREE(model.currentPose); + RL_FREE(model.boneMatrices); + model.currentPose = NULL; + model.boneMatrices = NULL; + } + // Free all cgltf loaded data cgltf_free(data); } From d604cd7f658ef99be096a51338be9678bed12b05 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Mar 2026 12:52:27 +0100 Subject: [PATCH 143/319] Update rmodels.c --- src/rmodels.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 3da1c0be7..2807f5d9b 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -6336,12 +6336,13 @@ static Model LoadGLTF(const char *fileName) for (int j = 0; j < model.skeleton.boneCount; j++) model.boneMatrices[j] = MatrixIdentity(); //---------------------------------------------------------------------------------------------------- + // Free unused allocated memory in case of no bones defined if (model.skeleton.boneCount == 0) { - RL_FREE(model.currentPose); - RL_FREE(model.boneMatrices); - model.currentPose = NULL; - model.boneMatrices = NULL; + RL_FREE(model.currentPose); + RL_FREE(model.boneMatrices); + model.currentPose = NULL; + model.boneMatrices = NULL; } // Free all cgltf loaded data From 2b207be11ec9c025ab9fe248f02a0c5df78992a4 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Mar 2026 13:22:29 +0100 Subject: [PATCH 144/319] ADDED: `rlUnloadShader()` #5631 --- src/rlgl.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index bb6411ee2..0e73e415c 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -772,6 +772,7 @@ RLAPI void rlResizeFramebuffer(int width, int height); // Res // Shaders management RLAPI unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode); // Load shader from code strings RLAPI unsigned int rlCompileShader(const char *shaderCode, int type); // Compile custom shader and return shader id (type: RL_VERTEX_SHADER, RL_FRAGMENT_SHADER, RL_COMPUTE_SHADER) +RLAPI void rlUnloadShader(unsigned int id); // Unload shader, loaded with rlCompileShader() RLAPI unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId); // Load custom shader program RLAPI void rlUnloadShaderProgram(unsigned int id); // Unload shader program RLAPI int rlGetLocationUniform(unsigned int shaderId, const char *uniformName); // Get shader location uniform, requires shader program id @@ -4182,7 +4183,7 @@ void rlUnloadVertexBuffer(unsigned int vboId) // NOTE: If shader string is NULL, using default vertex/fragment shaders unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode) { - unsigned int id = 0; + unsigned int id = 0; // Shader program id if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return id; } #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -4397,6 +4398,16 @@ void rlUnloadShaderProgram(unsigned int id) #endif } +// Delete shader +void rlUnloadShader(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glDeleteShader(id); + + TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Unloaded shader data from VRAM (GPU)", id); +#endif +} + // Get shader location uniform // NOTE: First parameter refers to shader program id int rlGetLocationUniform(unsigned int shaderId, const char *uniformName) @@ -4612,7 +4623,6 @@ void rlUnloadShaderBuffer(unsigned int ssboId) #else TRACELOG(RL_LOG_WARNING, "SSBO: SSBO not enabled. Define GRAPHICS_API_OPENGL_43"); #endif - } // Update SSBO buffer data From e40ddfabbb3552453d1feb09811d9f1c4c1c3741 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Mar 2026 14:23:54 +0100 Subject: [PATCH 145/319] **WARNING: BREAKING:** REDESIGNED: `rlgl` shader loading API function names for more consistency #5631 ADDED: `rlUnloadShader()` to unload shaders (that function was missing and compute shaders leak memory) RENAMED: `rlCompileShader()` to p `rlLoadShader()` to be consistent with `rlUnloadShader()` RENAMED: `rlLoadShaderCode()` to `rlLoadShaderProgram()`, more descriptive of return RENAMED: `rlLoadShaderProgram()` to `rlLoadShaderProgramEx()` RENAMED: `rlLoadComputeShaderProgram()` to `rlLoadShaderProgramCompute()` RENAMED: Some functions parameters for consistency --- examples/shaders/shaders_rlgl_compute.c | 12 +- src/rcore.c | 2 +- src/rlgl.h | 322 ++++++++++++------------ 3 files changed, 170 insertions(+), 166 deletions(-) diff --git a/examples/shaders/shaders_rlgl_compute.c b/examples/shaders/shaders_rlgl_compute.c index af3bb6fe4..a92fbac6a 100644 --- a/examples/shaders/shaders_rlgl_compute.c +++ b/examples/shaders/shaders_rlgl_compute.c @@ -65,8 +65,8 @@ int main(void) // Game of Life logic compute shader char *golLogicCode = LoadFileText("resources/shaders/glsl430/gol.glsl"); - unsigned int golLogicShader = rlCompileShader(golLogicCode, RL_COMPUTE_SHADER); - unsigned int golLogicProgram = rlLoadComputeShaderProgram(golLogicShader); + unsigned int golLogicShader = rlLoadShader(golLogicCode, RL_COMPUTE_SHADER); + unsigned int golLogicProgram = rlLoadShaderProgramCompute(golLogicShader); UnloadFileText(golLogicCode); // Game of Life logic render shader @@ -75,8 +75,8 @@ int main(void) // Game of Life transfert shader (CPU<->GPU download and upload) char *golTransfertCode = LoadFileText("resources/shaders/glsl430/gol_transfert.glsl"); - unsigned int golTransfertShader = rlCompileShader(golTransfertCode, RL_COMPUTE_SHADER); - unsigned int golTransfertProgram = rlLoadComputeShaderProgram(golTransfertShader); + unsigned int golTransfertShader = rlLoadShader(golTransfertCode, RL_COMPUTE_SHADER); + unsigned int golTransfertProgram = rlLoadShaderProgramCompute(golTransfertShader); UnloadFileText(golTransfertCode); // Load shader storage buffer object (SSBO), id returned @@ -169,7 +169,9 @@ int main(void) rlUnloadShaderBuffer(ssboB); rlUnloadShaderBuffer(ssboTransfert); - // Unload compute shader programs + // Unload compute shader + rlUnloadShader(golLogicShader); + rlUnloadShader(golTransfertShader); rlUnloadShaderProgram(golTransfertProgram); rlUnloadShaderProgram(golLogicProgram); diff --git a/src/rcore.c b/src/rcore.c index 2e0958f35..de1bb371c 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1243,7 +1243,7 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode) { Shader shader = { 0 }; - shader.id = rlLoadShaderCode(vsCode, fsCode); + shader.id = rlLoadShaderProgram(vsCode, fsCode); if (shader.id == 0) { diff --git a/src/rlgl.h b/src/rlgl.h index 0e73e415c..e5b7d87f4 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -762,7 +762,7 @@ RLAPI unsigned char *rlReadScreenPixels(int width, int height); // Rea // Framebuffer management (fbo) RLAPI unsigned int rlLoadFramebuffer(void); // Load an empty framebuffer -RLAPI void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType, int mipLevel); // Attach texture/renderbuffer to a framebuffer +RLAPI void rlFramebufferAttach(unsigned int id, unsigned int texId, int attachType, int texType, int mipLevel); // Attach texture/renderbuffer to a framebuffer RLAPI bool rlFramebufferComplete(unsigned int id); // Verify framebuffer is complete RLAPI void rlUnloadFramebuffer(unsigned int id); // Delete framebuffer from GPU // WARNING: Copy and resize framebuffer functionality only defined for software backend @@ -770,13 +770,14 @@ RLAPI void rlCopyFramebuffer(int x, int y, int width, int height, int format, vo RLAPI void rlResizeFramebuffer(int width, int height); // Resize internal framebuffer // Shaders management -RLAPI unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode); // Load shader from code strings -RLAPI unsigned int rlCompileShader(const char *shaderCode, int type); // Compile custom shader and return shader id (type: RL_VERTEX_SHADER, RL_FRAGMENT_SHADER, RL_COMPUTE_SHADER) -RLAPI void rlUnloadShader(unsigned int id); // Unload shader, loaded with rlCompileShader() -RLAPI unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId); // Load custom shader program +RLAPI unsigned int rlLoadShader(const char *code, int type); // Load (compile) shader and return shader id (type: RL_VERTEX_SHADER, RL_FRAGMENT_SHADER, RL_COMPUTE_SHADER) +RLAPI unsigned int rlLoadShaderProgram(const char *vsCode, const char *fsCode); // Load shader from code strings +RLAPI unsigned int rlLoadShaderProgramEx(unsigned int vsId, unsigned int fsId); // Load shader program, using already loaded shader ids +RLAPI unsigned int rlLoadShaderProgramCompute(unsigned int csId); // Load compute shader program +RLAPI void rlUnloadShader(unsigned int id); // Unload shader, loaded with rlLoadShader() RLAPI void rlUnloadShaderProgram(unsigned int id); // Unload shader program -RLAPI int rlGetLocationUniform(unsigned int shaderId, const char *uniformName); // Get shader location uniform, requires shader program id -RLAPI int rlGetLocationAttrib(unsigned int shaderId, const char *attribName); // Get shader location attribute, requires shader program id +RLAPI int rlGetLocationUniform(unsigned int id, const char *uniformName); // Get shader location uniform, requires shader program id +RLAPI int rlGetLocationAttrib(unsigned int id, const char *attribName); // Get shader location attribute, requires shader program id RLAPI void rlSetUniform(int locIndex, const void *value, int uniformType, int count); // Set shader value uniform RLAPI void rlSetUniformMatrix(int locIndex, Matrix mat); // Set shader value matrix RLAPI void rlSetUniformMatrices(int locIndex, const Matrix *mat, int count); // Set shader value matrices @@ -784,7 +785,6 @@ RLAPI void rlSetUniformSampler(int locIndex, unsigned int textureId); RLAPI void rlSetShader(unsigned int id, int *locs); // Set shader currently active (id and locations) // Compute shader management -RLAPI unsigned int rlLoadComputeShaderProgram(unsigned int shaderId); // Load compute shader program RLAPI void rlComputeShaderDispatch(unsigned int groupX, unsigned int groupY, unsigned int groupZ); // Dispatch compute shader (equivalent to *draw* for graphics pipeline) // Shader buffer storage object management (ssbo) @@ -3837,12 +3837,12 @@ unsigned int rlLoadFramebuffer(void) return fboId; } -// Attach color buffer texture to an fbo (unloads previous attachment) +// Attach color buffer texture to a framebuffer object (unloads previous attachment) // NOTE: Attach type: 0-Color, 1-Depth renderbuffer, 2-Depth texture -void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType, int mipLevel) +void rlFramebufferAttach(unsigned int id, unsigned int texId, int attachType, int texType, int mipLevel) { #if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) - glBindFramebuffer(GL_FRAMEBUFFER, fboId); + glBindFramebuffer(GL_FRAMEBUFFER, id); switch (attachType) { @@ -4179,93 +4179,14 @@ void rlUnloadVertexBuffer(unsigned int vboId) // Shaders management //----------------------------------------------------------------------------------------------- -// Load shader from code strings -// NOTE: If shader string is NULL, using default vertex/fragment shaders -unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode) -{ - unsigned int id = 0; // Shader program id - if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return id; } - -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - unsigned int vertexShaderId = 0; - unsigned int fragmentShaderId = 0; - - // Compile vertex shader (if provided) - // NOTE: If not vertex shader is provided, use default one - if (vsCode != NULL) vertexShaderId = rlCompileShader(vsCode, GL_VERTEX_SHADER); - else vertexShaderId = RLGL.State.defaultVShaderId; - - // Compile fragment shader (if provided) - // NOTE: If not vertex shader is provided, use default one - if (fsCode != NULL) fragmentShaderId = rlCompileShader(fsCode, GL_FRAGMENT_SHADER); - else fragmentShaderId = RLGL.State.defaultFShaderId; - - // In case vertex and fragment shader are the default ones, no need to recompile, assign the default shader program id - if ((vertexShaderId == RLGL.State.defaultVShaderId) && (fragmentShaderId == RLGL.State.defaultFShaderId)) id = RLGL.State.defaultShaderId; - else if ((vertexShaderId > 0) && (fragmentShaderId > 0)) - { - // One of or both shader are new, a new shader program needs to be compiled - id = rlLoadShaderProgram(vertexShaderId, fragmentShaderId); - - // Detaching and deleting vertex/fragment shaders (if not default ones) - // WARNING: Detach shader before deletion to make sure memory is freed - if (vertexShaderId != RLGL.State.defaultVShaderId) - { - // WARNING: Shader program linkage could fail and returned id is 0 - if (id > 0) glDetachShader(id, vertexShaderId); - glDeleteShader(vertexShaderId); - } - if (fragmentShaderId != RLGL.State.defaultFShaderId) - { - // WARNING: Shader program linkage could fail and returned id is 0 - if (id > 0) glDetachShader(id, fragmentShaderId); - glDeleteShader(fragmentShaderId); - } - - // In case shader program loading failed, assign default shader - if (id == 0) - { - // In case shader loading fails, reassigning default shader - TRACELOG(RL_LOG_WARNING, "SHADER: Failed to load custom shader code, using default shader"); - id = RLGL.State.defaultShaderId; - } - /* - else - { - // Get available shader uniforms - // NOTE: This information is useful for debug... - int uniformCount = -1; - glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &uniformCount); - - for (int i = 0; i < uniformCount; i++) - { - int namelen = -1; - int num = -1; - char name[256] = { 0 }; // Assume no variable names longer than 256 - GLenum type = GL_ZERO; - - // Get the name of the uniforms - glGetActiveUniform(id, i, sizeof(name) - 1, &namelen, &num, &type, name); - - name[namelen] = 0; - TRACELOG(RL_LOG_DEBUG, "SHADER: [ID %i] Active uniform (%s) set at location: %i", id, name, glGetUniformLocation(id, name)); - } - } - */ - } -#endif - - return id; -} - -// Compile custom shader and return shader id -unsigned int rlCompileShader(const char *shaderCode, int type) +// Load (compile) shader and return shader id +unsigned int rlLoadShader(const char *code, int type) { unsigned int shaderId = 0; #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) shaderId = glCreateShader(type); - glShaderSource(shaderId, 1, &shaderCode, NULL); + glShaderSource(shaderId, 1, &code, NULL); GLint success = 0; glCompileShader(shaderId); @@ -4323,8 +4244,87 @@ unsigned int rlCompileShader(const char *shaderCode, int type) return shaderId; } -// Load custom shader strings and return program id -unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId) +// Load shader program from code strings +// NOTE: If shader string is NULL, using default vertex/fragment shaders +unsigned int rlLoadShaderProgram(const char *vsCode, const char *fsCode) +{ + unsigned int id = 0; // Shader program id + if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return id; } + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + unsigned int vertexShaderId = 0; + unsigned int fragmentShaderId = 0; + + // Compile vertex shader (if provided) + // NOTE: If not vertex shader is provided, use default one + if (vsCode != NULL) vertexShaderId = rlLoadShader(vsCode, GL_VERTEX_SHADER); + else vertexShaderId = RLGL.State.defaultVShaderId; + + // Compile fragment shader (if provided) + // NOTE: If not vertex shader is provided, use default one + if (fsCode != NULL) fragmentShaderId = rlLoadShader(fsCode, GL_FRAGMENT_SHADER); + else fragmentShaderId = RLGL.State.defaultFShaderId; + + // In case vertex and fragment shader are the default ones, no need to recompile, assign the default shader program id + if ((vertexShaderId == RLGL.State.defaultVShaderId) && (fragmentShaderId == RLGL.State.defaultFShaderId)) id = RLGL.State.defaultShaderId; + else if ((vertexShaderId > 0) && (fragmentShaderId > 0)) + { + // One of or both shader are new, a new shader program needs to be compiled + id = rlLoadShaderProgramEx(vertexShaderId, fragmentShaderId); + + // Detaching and deleting vertex/fragment shaders (if not default ones) + // WARNING: Detach shader before deletion to make sure memory is freed + if (vertexShaderId != RLGL.State.defaultVShaderId) + { + // WARNING: Shader program linkage could fail and returned id is 0 + if (id > 0) glDetachShader(id, vertexShaderId); + glDeleteShader(vertexShaderId); + } + if (fragmentShaderId != RLGL.State.defaultFShaderId) + { + // WARNING: Shader program linkage could fail and returned id is 0 + if (id > 0) glDetachShader(id, fragmentShaderId); + glDeleteShader(fragmentShaderId); + } + + // In case shader program loading failed, assign default shader + if (id == 0) + { + // In case shader loading fails, reassigning default shader + TRACELOG(RL_LOG_WARNING, "SHADER: Failed to load custom shader code, using default shader"); + id = RLGL.State.defaultShaderId; + } + /* + else + { + // Get available shader uniforms + // NOTE: This information is useful for debug... + int uniformCount = -1; + glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &uniformCount); + + for (int i = 0; i < uniformCount; i++) + { + int namelen = -1; + int num = -1; + char name[256] = { 0 }; // Assume no variable names longer than 256 + GLenum type = GL_ZERO; + + // Get the name of the uniforms + glGetActiveUniform(id, i, sizeof(name) - 1, &namelen, &num, &type, name); + + name[namelen] = 0; + TRACELOG(RL_LOG_DEBUG, "SHADER: [ID %i] Active uniform (%s) set at location: %i", id, name, glGetUniformLocation(id, name)); + } + } + */ + } +#endif + + return id; +} + +// Load shader program from already loaded shader ids +unsigned int rlLoadShaderProgramEx(unsigned int vsId, unsigned int fsId) { unsigned int programId = 0; if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return programId; } @@ -4333,8 +4333,8 @@ unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId) GLint success = 0; programId = glCreateProgram(); - glAttachShader(programId, vShaderId); - glAttachShader(programId, fShaderId); + glAttachShader(programId, vsId); + glAttachShader(programId, fsId); // Default attribute shader locations must be bound before linking // NOTE: There is no problem with binding a generic attribute index to an attribute variable name @@ -4388,14 +4388,57 @@ unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId) return programId; } -// Unload shader program -void rlUnloadShaderProgram(unsigned int id) +// Load compute shader program +unsigned int rlLoadShaderProgramCompute(unsigned int csId) { -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glDeleteProgram(id); + unsigned int programId = 0; - TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Unloaded shader program data from VRAM (GPU)", id); +#if defined(GRAPHICS_API_OPENGL_43) + GLint success = 0; + programId = glCreateProgram(); + + glAttachShader(programId, csId); + + glLinkProgram(programId); + + // NOTE: All uniform variables are intitialised to 0 when a program links + + glGetProgramiv(programId, GL_LINK_STATUS, &success); + + if (success == GL_FALSE) + { + TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to link compute shader program", programId); + + int maxLength = 0; + glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &maxLength); + + if (maxLength > 0) + { + int length = 0; + char *log = (char *)RL_CALLOC(maxLength, sizeof(char)); + glGetProgramInfoLog(programId, maxLength, &length, log); + TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Link error: %s", programId, log); + RL_FREE(log); + } + + glDeleteProgram(programId); + + programId = 0; + } + else + { + // Get the size of compiled shader program (not available on OpenGL ES 2.0) + // NOTE: If GL_LINK_STATUS is GL_FALSE, program binary length is zero + //GLint binarySize = 0; + //glGetProgramiv(id, GL_PROGRAM_BINARY_LENGTH, &binarySize); + + TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader program loaded successfully", programId); + } +#else + TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not supported, enable GRAPHICS_API_OPENGL_43"); #endif + + return programId; } // Delete shader @@ -4408,13 +4451,23 @@ void rlUnloadShader(unsigned int id) #endif } +// Unload shader program +void rlUnloadShaderProgram(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glDeleteProgram(id); + + TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Unloaded shader program data from VRAM (GPU)", id); +#endif +} + // Get shader location uniform // NOTE: First parameter refers to shader program id -int rlGetLocationUniform(unsigned int shaderId, const char *uniformName) +int rlGetLocationUniform(unsigned int id, const char *uniformName) { int location = -1; #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - location = glGetUniformLocation(shaderId, uniformName); + location = glGetUniformLocation(id, uniformName); //if (location == -1) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to find shader uniform: %s", shaderId, uniformName); //else TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Shader uniform (%s) set at location: %i", shaderId, uniformName, location); @@ -4424,11 +4477,11 @@ int rlGetLocationUniform(unsigned int shaderId, const char *uniformName) // Get shader location attribute // NOTE: First parameter refers to shader program id -int rlGetLocationAttrib(unsigned int shaderId, const char *attribName) +int rlGetLocationAttrib(unsigned int id, const char *attribName) { int location = -1; #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - location = glGetAttribLocation(shaderId, attribName); + location = glGetAttribLocation(id, attribName); //if (location == -1) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to find shader attribute: %s", shaderId, attribName); //else TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Shader attribute (%s) set at location: %i", shaderId, attribName, location); @@ -4538,57 +4591,6 @@ void rlSetShader(unsigned int id, int *locs) #endif } -// Load compute shader program -unsigned int rlLoadComputeShaderProgram(unsigned int shaderId) -{ - unsigned int programId = 0; - -#if defined(GRAPHICS_API_OPENGL_43) - GLint success = 0; - programId = glCreateProgram(); - glAttachShader(programId, shaderId); - glLinkProgram(programId); - - // NOTE: All uniform variables are intitialised to 0 when a program links - - glGetProgramiv(programId, GL_LINK_STATUS, &success); - - if (success == GL_FALSE) - { - TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to link compute shader program", programId); - - int maxLength = 0; - glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &maxLength); - - if (maxLength > 0) - { - int length = 0; - char *log = (char *)RL_CALLOC(maxLength, sizeof(char)); - glGetProgramInfoLog(programId, maxLength, &length, log); - TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Link error: %s", programId, log); - RL_FREE(log); - } - - glDeleteProgram(programId); - - programId = 0; - } - else - { - // Get the size of compiled shader program (not available on OpenGL ES 2.0) - // NOTE: If GL_LINK_STATUS is GL_FALSE, program binary length is zero - //GLint binarySize = 0; - //glGetProgramiv(id, GL_PROGRAM_BINARY_LENGTH, &binarySize); - - TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader program loaded successfully", programId); - } -#else - TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43"); -#endif - - return programId; -} - // Dispatch compute shader (equivalent to *draw* for graphics pilepine) void rlComputeShaderDispatch(unsigned int groupX, unsigned int groupY, unsigned int groupZ) { @@ -5081,10 +5083,10 @@ static void rlLoadShaderDefault(void) // NOTE: Compiled vertex/fragment shaders are not deleted, // they are kept for re-use as default shaders in case some shader loading fails - RLGL.State.defaultVShaderId = rlCompileShader(defaultVShaderCode, GL_VERTEX_SHADER); // Compile default vertex shader - RLGL.State.defaultFShaderId = rlCompileShader(defaultFShaderCode, GL_FRAGMENT_SHADER); // Compile default fragment shader + RLGL.State.defaultVShaderId = rlLoadShader(defaultVShaderCode, GL_VERTEX_SHADER); // Compile default vertex shader + RLGL.State.defaultFShaderId = rlLoadShader(defaultFShaderCode, GL_FRAGMENT_SHADER); // Compile default fragment shader - RLGL.State.defaultShaderId = rlLoadShaderProgram(RLGL.State.defaultVShaderId, RLGL.State.defaultFShaderId); + RLGL.State.defaultShaderId = rlLoadShaderProgramEx(RLGL.State.defaultVShaderId, RLGL.State.defaultFShaderId); if (RLGL.State.defaultShaderId > 0) { From 7b1096dc537b926f8509a5e84fb248bd55ee1b06 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Tue, 10 Mar 2026 03:51:12 -0500 Subject: [PATCH 146/319] [raylib.h] fix audio pan comment (#5633) * fix audio pan comment -- found by a.b.c.d.a.b.c.d * rlparser: update raylib_api.* by CI --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/raylib.h | 2 +- tools/rlparser/output/raylib_api.json | 2 +- tools/rlparser/output/raylib_api.lua | 2 +- tools/rlparser/output/raylib_api.txt | 2 +- tools/rlparser/output/raylib_api.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index ad4e4133b..a1391de19 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1724,7 +1724,7 @@ RLAPI bool IsAudioStreamPlaying(AudioStream stream); // Check i RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level) RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level) -RLAPI void SetAudioStreamPan(AudioStream stream, float pan); // Set pan for audio stream (0.5 is centered) +RLAPI void SetAudioStreamPan(AudioStream stream, float pan); // Set pan for audio stream (-1.0 to 1.0 range, 0.0 is centered) RLAPI void SetAudioStreamBufferSizeDefault(int size); // Default size for new audio streams RLAPI void SetAudioStreamCallback(AudioStream stream, AudioCallback callback); // Audio thread callback to request new data diff --git a/tools/rlparser/output/raylib_api.json b/tools/rlparser/output/raylib_api.json index a669cae56..082e349b7 100644 --- a/tools/rlparser/output/raylib_api.json +++ b/tools/rlparser/output/raylib_api.json @@ -12412,7 +12412,7 @@ }, { "name": "SetAudioStreamPan", - "description": "Set pan for audio stream (0.5 is centered)", + "description": "Set pan for audio stream (-1.0 to 1.0 range, 0.0 is centered)", "returnType": "void", "params": [ { diff --git a/tools/rlparser/output/raylib_api.lua b/tools/rlparser/output/raylib_api.lua index accfb77c1..b68ab7563 100644 --- a/tools/rlparser/output/raylib_api.lua +++ b/tools/rlparser/output/raylib_api.lua @@ -8431,7 +8431,7 @@ return { }, { name = "SetAudioStreamPan", - description = "Set pan for audio stream (0.5 is centered)", + description = "Set pan for audio stream (-1.0 to 1.0 range, 0.0 is centered)", returnType = "void", params = { {type = "AudioStream", name = "stream"}, diff --git a/tools/rlparser/output/raylib_api.txt b/tools/rlparser/output/raylib_api.txt index d8f82544d..2213d3def 100644 --- a/tools/rlparser/output/raylib_api.txt +++ b/tools/rlparser/output/raylib_api.txt @@ -4768,7 +4768,7 @@ Function 592: SetAudioStreamPitch() (2 input parameters) Function 593: SetAudioStreamPan() (2 input parameters) Name: SetAudioStreamPan Return type: void - Description: Set pan for audio stream (0.5 is centered) + Description: Set pan for audio stream (-1.0 to 1.0 range, 0.0 is centered) Param[1]: stream (type: AudioStream) Param[2]: pan (type: float) Function 594: SetAudioStreamBufferSizeDefault() (1 input parameters) diff --git a/tools/rlparser/output/raylib_api.xml b/tools/rlparser/output/raylib_api.xml index 82aef8b47..2ec474efd 100644 --- a/tools/rlparser/output/raylib_api.xml +++ b/tools/rlparser/output/raylib_api.xml @@ -3181,7 +3181,7 @@ - + From 6cebf63cba7eb54fe01fa5f20c9f38227906ac40 Mon Sep 17 00:00:00 2001 From: Thomas Prowse <152029833+SardineMilk@users.noreply.github.com> Date: Tue, 10 Mar 2026 23:21:53 +0000 Subject: [PATCH 147/319] Change `GetRenderWidth()` to `GetScreenWidth()` for consistency (#5635) --- examples/models/models_basic_voxel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/models/models_basic_voxel.c b/examples/models/models_basic_voxel.c index 61313ecef..b0f4bc65e 100644 --- a/examples/models/models_basic_voxel.c +++ b/examples/models/models_basic_voxel.c @@ -137,7 +137,7 @@ int main(void) EndMode3D(); // Draw reference point for raycasting to delete blocks - DrawCircle(GetRenderWidth()/2, GetScreenHeight()/2, 4, RED); + DrawCircle(GetScreenWidth()/2, GetScreenHeight()/2, 4, RED); DrawText("Left-click a voxel to remove it!", 10, 10, 20, DARKGRAY); DrawText("WASD to move, mouse to look around", 10, 35, 10, GRAY); From ee4f4a29c2462d8d0b2542f55900f202a61217c7 Mon Sep 17 00:00:00 2001 From: dan-hoang <56205882+dan-hoang@users.noreply.github.com> Date: Thu, 12 Mar 2026 03:33:47 -0700 Subject: [PATCH 148/319] [examples] Update audio_raw_stream.c (#5637) * Update audio_raw_stream.c * Update audio_raw_stream.c to more closely follow raylib coding conventions * Update audio_raw_stream.c to more closely follow raylib coding conventions I accidentally put the file in the wrong folder. * Delete examples/audio_raw_stream.c * Remove spaces before asterisks in comments in audio_raw_stream.c * Put SetTargetFPS(30) before while (!WindowShouldClose()) in audio_raw_stream.c --- examples/audio/audio_raw_stream.c | 196 +++++++++------------------- examples/audio/audio_raw_stream.png | Bin 16736 -> 16468 bytes 2 files changed, 64 insertions(+), 132 deletions(-) diff --git a/examples/audio/audio_raw_stream.c b/examples/audio/audio_raw_stream.c index b327e92af..536f8c311 100644 --- a/examples/audio/audio_raw_stream.c +++ b/examples/audio/audio_raw_stream.c @@ -16,41 +16,10 @@ ********************************************************************************************/ #include "raylib.h" +#include -#include // Required for: malloc(), free() -#include // Required for: sinf() -#include // Required for: memcpy() - -#define MAX_SAMPLES 512 -#define MAX_SAMPLES_PER_UPDATE 4096 - -// Cycles per second (hz) -float frequency = 440.0f; - -// Audio frequency, for smoothing -float audioFrequency = 440.0f; - -// Previous value, used to test if sine needs to be rewritten, and to smoothly modulate frequency -float oldFrequency = 1.0f; - -// Index for audio rendering -float sineIdx = 0.0f; - -// Audio input processing callback -void AudioInputCallback(void *buffer, unsigned int frames) -{ - audioFrequency = frequency + (audioFrequency - frequency)*0.95f; - - float incr = audioFrequency/44100.0f; - short *d = (short *)buffer; - - for (unsigned int i = 0; i < frames; i++) - { - d[i] = (short)(32000.0f*sinf(2*PI*sineIdx)); - sineIdx += incr; - if (sineIdx > 1.0f) sineIdx -= 1.0f; - } -} +#define BUFFER_SIZE 4096 +#define SAMPLE_RATE 44100 //------------------------------------------------------------------------------------ // Program main entry point @@ -64,43 +33,24 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [audio] example - raw stream"); - InitAudioDevice(); // Initialize audio device + InitAudioDevice(); - SetAudioStreamBufferSizeDefault(MAX_SAMPLES_PER_UPDATE); + // Set the number of samples the stream will keep in memory at a time to BUFFER_SIZE + SetAudioStreamBufferSizeDefault(BUFFER_SIZE); + float buffer[BUFFER_SIZE] = {}; - // Init raw audio stream (sample rate: 44100, sample size: 16bit-short, channels: 1-mono) - AudioStream stream = LoadAudioStream(44100, 16, 1); + // Init raw audio stream (sample rate: 44100, sample size: 32bit-float, channels: 1-mono) + AudioStream stream = LoadAudioStream(SAMPLE_RATE, 32, 1); + float pan = 0.0f; + SetAudioStreamPan(stream, pan); + PlayAudioStream(stream); - SetAudioStreamCallback(stream, AudioInputCallback); + int sineFrequency = 440; + int newSineFrequency = 440; + int sineIndex = 0; + double sineStartTime = 0.0; - // Buffer for the single cycle waveform we are synthesizing - short *data = (short *)malloc(sizeof(short)*MAX_SAMPLES); - - // Frame buffer, describing the waveform when repeated over the course of a frame - short *writeBuf = (short *)malloc(sizeof(short)*MAX_SAMPLES_PER_UPDATE); - - PlayAudioStream(stream); // Start processing stream buffer (no data loaded currently) - - // Position read in to determine next frequency - Vector2 mousePosition = { -100.0f, -100.0f }; - - /* - // Cycles per second (hz) - float frequency = 440.0f; - - // Previous value, used to test if sine needs to be rewritten, and to smoothly modulate frequency - float oldFrequency = 1.0f; - - // Cursor to read and copy the samples of the sine wave buffer - int readCursor = 0; - */ - - // Computed size in samples of the sine wave - int waveLength = 1; - - Vector2 position = { 0, 0 }; - - SetTargetFPS(30); // Set our game to run at 30 frames-per-second + SetTargetFPS(30); //-------------------------------------------------------------------------------------- // Main game loop @@ -108,92 +58,77 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - mousePosition = GetMousePosition(); - if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) + if (IsKeyDown(KEY_UP)) { - float fp = (float)(mousePosition.y); - frequency = 40.0f + (float)(fp); + newSineFrequency += 10; + if (newSineFrequency > 12500) newSineFrequency = 12500; + } - float pan = (float)(mousePosition.x)/(float)screenWidth; + if (IsKeyDown(KEY_DOWN)) + { + newSineFrequency -= 10; + if (newSineFrequency < 20) newSineFrequency = 20; + } + + if (IsKeyDown(KEY_LEFT)) + { + pan -= 0.01f; + if (pan < -1.0f) pan = -1.0f; SetAudioStreamPan(stream, pan); } - // Rewrite the sine wave - // Compute two cycles to allow the buffer padding, simplifying any modulation, resampling, etc. - if (frequency != oldFrequency) + if (IsKeyDown(KEY_RIGHT)) { - // Compute wavelength. Limit size in both directions - //int oldWavelength = waveLength; - waveLength = (int)(22050/frequency); - if (waveLength > MAX_SAMPLES/2) waveLength = MAX_SAMPLES/2; - if (waveLength < 1) waveLength = 1; - - // Write sine wave - for (int i = 0; i < waveLength*2; i++) - { - data[i] = (short)(sinf(((2*PI*(float)i/waveLength)))*32000); - } - // Make sure the rest of the line is flat - for (int j = waveLength*2; j < MAX_SAMPLES; j++) - { - data[j] = (short)0; - } - - // Scale read cursor's position to minimize transition artifacts - //readCursor = (int)(readCursor*((float)waveLength/(float)oldWavelength)); - oldFrequency = frequency; + pan += 0.01f; + if (pan > 1.0f) pan = 1.0f; + SetAudioStreamPan(stream, pan); } - /* - // Refill audio stream if required if (IsAudioStreamProcessed(stream)) { - // Synthesize a buffer that is exactly the requested size - int writeCursor = 0; - - while (writeCursor < MAX_SAMPLES_PER_UPDATE) + for (int i = 0; i < BUFFER_SIZE; i++) { - // Start by trying to write the whole chunk at once - int writeLength = MAX_SAMPLES_PER_UPDATE-writeCursor; + int wavelength = SAMPLE_RATE/sineFrequency; + buffer[i] = sin(2*PI*sineIndex/wavelength); + sineIndex++; - // Limit to the maximum readable size - int readLength = waveLength-readCursor; - - if (writeLength > readLength) writeLength = readLength; - - // Write the slice - memcpy(writeBuf + writeCursor, data + readCursor, writeLength*sizeof(short)); - - // Update cursors and loop audio - readCursor = (readCursor + writeLength)%waveLength; - - writeCursor += writeLength; + if (sineIndex >= wavelength) + { + sineFrequency = newSineFrequency; + sineIndex = 0; + sineStartTime = GetTime(); + } } - // Copy finished frame to audio stream - UpdateAudioStream(stream, writeBuf, MAX_SAMPLES_PER_UPDATE); + UpdateAudioStream(stream, buffer, BUFFER_SIZE); } - */ + //---------------------------------------------------------------------------------- // Draw //---------------------------------------------------------------------------------- BeginDrawing(); + ClearBackground(RAYWHITE); - ClearBackground(RAYWHITE); + DrawText(TextFormat("sine frequency: %i", sineFrequency), screenWidth - 220, 10, 20, RED); + DrawText(TextFormat("pan: %.2f", pan), screenWidth - 220, 30, 20, RED); + DrawText("Up/down to change frequency", 10, 10, 20, DARKGRAY); + DrawText("Left/right to pan", 10, 30, 20, DARKGRAY); - DrawText(TextFormat("sine frequency: %i",(int)frequency), GetScreenWidth() - 220, 10, 20, RED); - DrawText("click mouse button to change frequency or pan", 10, 10, 20, DARKGRAY); + int windowStart = (GetTime() - sineStartTime)*SAMPLE_RATE; + int windowSize = 0.1f*SAMPLE_RATE; + int wavelength = SAMPLE_RATE/sineFrequency; - // Draw the current buffer state proportionate to the screen - for (int i = 0; i < screenWidth; i++) - { - position.x = (float)i; - position.y = 250 + 50*data[i*MAX_SAMPLES/screenWidth]/32000.0f; - - DrawPixelV(position, RED); - } + // Draw a sine wave with the same frequency as the one being sent to the audio stream + for (int i = 0; i < screenWidth; i++) + { + int t0 = windowStart + i*windowSize/screenWidth; + int t1 = windowStart + (i + 1)*windowSize/screenWidth; + Vector2 startPos = { i, 250 + 50*sin(2*PI*t0/wavelength) }; + Vector2 endPos = { i + 1, 250 + 50*sin(2*PI*t1/wavelength) }; + DrawLineV(startPos, endPos, RED); + } EndDrawing(); //---------------------------------------------------------------------------------- @@ -201,9 +136,6 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - free(data); // Unload sine wave data - free(writeBuf); // Unload write buffer - UnloadAudioStream(stream); // Close raw audio stream and delete buffers from RAM CloseAudioDevice(); // Close audio device (music streaming is automatically stopped) diff --git a/examples/audio/audio_raw_stream.png b/examples/audio/audio_raw_stream.png index 344f4a71077431c9f50c72e2815552acef26f7ed..b061abd2df6350a81782d55c1f960c08fee0e1de 100644 GIT binary patch literal 16468 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 literal 16736 zcmeHPdsI_*whj*=ViKc>CL!UifE0`*AVfeQm;j>k5-mlIiW1aVKv1DnK_H+e;aRaL zC_M^li=xI3a`AkZM92wFjHd-n-;)~p$rHEYcu_uh5$&siinkNx}h z{`UU%{+-34z*T&r0nGr1!x8;_eS&c~9WxwGs}8RR-^|-m(SXBwS^N2Th3?z1<-@x- zg9pSxOqw=gKE)v(uT_Qynh;Y_GG)?(R#HZdP(PAgAr;pM@sddnOvxTko?-3eKf(o- zGg47%4(j7Rl^LFYAybA@Bn!Tu(uV2@8!k<017sL{{nxLa<$7T@YmUc%QlC+@VsevNttQ)gYQby^t!Zlqpj_XYs!H0 zKcvd`ZXZ7$-gir2pH1PZXGOT1=1dOQku{kK>hHh~gk^>t60vo~Uici&F}#i@Gvl>T%e_ps3y%n^mQ-bbX6AQHXax&ZSPR!MC1w`{r#xkI;aM z4a)=q_3ftpJGWDq>1M501YEpfk>14$w3O*weRw=f>9y;6_Ju@i`}Ta}13HYC1#?Bc zWrpEr7HxlN-e+% z+#h7FJD{kWS2%v3G}HfS@%|5hilpqqznC-Po<6#`@sDir z+bPGHLVT;GE-wn^jXzCw4(`bW#V~hFzE~`@89C@goJ~5K?F3+TrVK2_BDD!2Z!{)& z@-!%5;iy4DgM#+|le_>sMgl5v$1`ya-_V+%Gv4ytnAmSVSG%KUR&NGcMNjfeWUnTj z?1^GKWbz|qx1I`1=%P8q3uH=`bBjviH$&@>q>E7)?Z#7Dc7b~ zk^JfsFPBeGtquM`)|`?<{joq+B$HK> zWZz`aUNQfCnjBr)*X`{iv31?=auj%SX_}cf5*OLb7r;fk^0wOP5z?z!-?H?zT2;LKUH0>){+KnFK%00 zWSz-JjCzltmRG2}Q7eVbDJX=G-4xcupex1R#48wA8_Q==wdE8BgAvO(cA8Clp|G5h z4QqPPTDnI+hJ7;IIgH0m`9P*f`Ub&i>0_tYXt{(}%I4aEZ5zP+J7@vd20&Z;t}GWF zHW~5-M9{dekC7?s4=#`7lx{8MREMVgZe&%l^MgzEoFf-<{l2}Cb>PDLjs5SL45g62t{1L9B%S-+>pO#TlVW30Roh<=TZ2+&Ve(3%7Zry~2`6$~3Et8o{nni9@Z0#liunGj5y&_wdW0v}4VgfWvBks{LEu zZg*Swv_W`s12`FV^UKDhsJTVKfYUQaPBr(j)#{c{%TTI)=dC^nRudyDcjJ7BzkLN4 zkt_G)E$@-O!C~8t1AT^ZcORitvi2qJOY|s;7NfbDD7nT|8;jM9j~zNr1o|VHvGUW` z2C(%?);3@H^3y};r{qqf^RFGkc>IWH!yfG3p*cFa%dka^b2_ir!#x1!asOTToNz3B zx~C7M7D*|^)2I~|JTC5w3BVVBRD%#@=tu&4E6T!@$Z-0vo=rSS^TEI+GYGwBe^F)G zB|HW4kg^T1<4JL|FoJZliVgwVxYnPWQZOQj*VE+-XTJWVUiC?X%C}yKKLy=&34P_P zkD|x(I2LvMkhwRw>T|Ou&c!EOt`i(f1+z+5MPU9m$P1r z2>h<4or$AWSlKQL)8q1Y$yt7hZWwDqZj!`R^rn0Tf*Oq66uP^@g7BLcL|&o5{N@`$@hwL21{3hwb}-;KRK9wUOj)@FiN-DfyMe&G!}MY;ufxC8 zc8;1bW|`TuNd4#ln!CHLCyqTdGyAZRS^genI|D5DNe#l|QS#(73HWZi_xvc&Q>Du~ zzUV?MwF!tI9roqHWh4LKvWK3TH7Ww0t*7_irbA`Ji2Zb{%{?YW2YFs07%D$_dMy@A z%8(yq{Y4N(R%cR6yZWicTAL{DGxy7D)UP9$VG?)2^vuUXinQM$34D-b46AkBB}~U| z&MXn%eO{-sc_O^z80;-Ld-@IfwO^fW90ugIJy^`O?ZtqcugAs1+X3Q04qf!TJNvuR z`(6NKPj(|J_vjR!*$W6=Q|4Rq8y_iy_G3mp>8HM8K`WaR;c^=jGCiwfiuYEyoP{xtFC=U7DlbO_+14xJ#4lPPyCD-fg} zp~k3t4`ybKkm^+A!RHEMGHPO?M zi9gyK^W2SC=-iSultOqT{;RN-!&$)U0-(zW&}J)(<(LhYdocpadIgXRoLh!>j7omi zarAi_FTbR7s7e`V0|@3+(O0%3(ucLPwUOvpb>2<;?+F*9Tt{Q$4)<0hAxnT-QwmLe5$g* z+G{^GmSv_8g+|2}Y?lid&j0{1zkT>`trc!D7_yOYGTyOgo2U4(@2D@s)l_B~ZkbT9 zEB@CgxpMJ3uHZD3VYe;JB4d3BsqYhkyIetD{Z|w8heC zHHm)!Djj;^K_DqrdCZ!BpU@Cf5x`p-hD;j_B&I5h_V70=60lhAYXyce&Kf^Ahayci ziH?sqe8u^%`nwQM_=-$v z?z3wbrm~V9dxTtpJdj}@s;}lfxR|D$W{2qkL{w5bM_b`~L*`IRt6)&X!AQbsNl~`L zYFg7Vc;*-Ibum4W! zOsz3@GsV>)W*aXr^VWn$V$4f{N(tdd<6hN^0_#P$PnQ{opcs6Vs z%J0cLjc#3sAn|Fgn2Lj>W|LVH6Y!&&)}%9J%EKk4+4uFjgyw8ckrNg#WlhR;bEO!Q zPXKm6kx~=&jj3riFd6Wrn@mGvc2Fd}@vdDTJUHL7I)zwdcC`)rHs^x@KsW&*H8;;8 zh-0QWXGR;C))+JLhwlY5pzbiW7>XML@ovtnQcD4lZ(Hz^0w9sQ8RAq2{uv?ToFe#9 zBwqdid@3PR93V08q&BUYxW=l_PXecgu0e@*Q0)WA8FvC_9)Zw+b;303zFL(e2dlM7KlSiA>JI%#n|T2Mx3H8$ zKc0jb-2<-+!0RJMvu1Fy6kw~^k`jgG{6sMnzFpo?l%H{-F48bIR4N=yVu-Q@KPqY3 zTqhC6J++W+ukp;pHP2s%+<2zUb;;cR%NF);o63GHg?!BDRt6n~9DZ*f{e1KnSV0*{ zkW-}--p{v)fts?}N*0#WVfb?(p%68Q+sEFc7KC(>8N8>4#UtcIzj7>CW))(pVo>Y_ zgSpZo zI%)HD@sOW6SD0?I2LlH-WtT0f8xWmMM^(Oq!!}Dnr7QQ*7e;^ix1}z_7@D>$gZw;& zkl{+OwDQ`u5)B`BVmW*!T9itX&DGI33} zL27LY)L98iX90*?ZwU;v7Hp=oj(w4fSXL;?eW}I2nR!4F+CFyrJ@98=eV?EuAK9uG zt>c#Fv}@a z%NDh@KN1kHpt%o~L1Ez6wx_X`AdcKzlR;2#V@Ip=>vdbPiqs&G)Pt4+dd+0m_t zlo1Z>8RtMDd+mT-LW%d(3$c>TJzp^YZ0(%=h;-2iCX{8)5z!zDK;$>%n{AXFy@>SL zDQpB7U4HOkR!{$uy@e#irx;{_VV5C)E6@NpbrRLXm}tz~eG5yE#55-&Gxu3#Z7al2 zF|sO-Mo_wRj-PS^SYyUQFJbr3YOKuzH|X7MZ^uHYE)WF#sFelV??nnhTw^Ttd={CR zO1h28tlk{Hno4SfcG@IyHgs~}62dUnbOA>bJYm3_6c=*_fBC3@*eidbDi{&wI;wkv z6A;MIeEfAHk-*Z*8esOiAJ5F)((5V9k;|zk*5K)+4Eco1%YV#VO3$Z$= zohf7bsg)z;Z*86cbA}FI!mM9Y4-QB^|4!}1%;2Rvtk{+VvBv80!ce0gIA)#>;?$1=Ew_J$U?IRQa(3vzn3OHJ!KT-vH`I zw3V~`c6+lw^*ApVDDa7}nug_5nk%7&a z{!=%BpP69wf7B}WRjp5x5ZSgmF1|ilz?yTl?f~18v~!|Ka|+q*BZ`mT)!jDjDm*%Z zv@;^O@mF{v)~u&%E!h6Pkd~noA_?6g=2sWqLGQ*w&VUf2JLx#lY!M=`_72m4q8D-- z%Y{w(mWx+7qIK;Bp)m+GcG!yRztkK~sx>O?t=UN7ejdaZCJ0aut2f+~ zHhA7o{^2KDPwRN^!ibx8(U&T1REFG38<}^mxb9J7H~T|i#BTsK&o^LeFH(nO)D@EQ2nv~!AHPIMfdxAfO@%v6k7 zk&C3VO41Dr{iC)pU{bA}hs3(No&SLDqwkku@&phW$|1?Nh^~Tv`8yYoCY)3;EIH67w|8!6|s586af3Twgnj49p+Q_jC z=o=e^=cjzx{nTezr^qvW{5R$QkGtL2i}1uhUxWCkvj{&cY=eG!voW?k zHtGBJ;D?DGaMBZ$A2$M%CU*k#Ody>woF}&ACba{+#Gb+jaQ+8x7`8;R*QjS6ldZJX z9-B+3H)W^T-NH7P^``g@c5*jPY{EU80sx}c|NP7TYA4Yq^^e+d`zLl=&qGT+xg-7; zZ0~8vpdkbB_dkC>M?(fsubRrBsSFbZ3(B{qGH7IyMkZnMNJ9oqWzh5tA6qpI88l?j hkm3Ih8E`mf*3GYro~NyX|D1sHTfWNYg16w{e*;kq+RFd{ From 4e360c97f4c35cbbfa1b6732e797de51aca3e387 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Thu, 12 Mar 2026 05:34:38 -0500 Subject: [PATCH 149/319] remove duplicate line in makefile (#5640) --- examples/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Makefile b/examples/Makefile index 1aa23b3b7..91ecf7475 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -304,7 +304,7 @@ endif # Define library paths containing required libs: LDFLAGS #------------------------------------------------------------------------------------------------ -LDFLAGS = -L. -L$(RAYLIB_RELEASE_PATH) -L$(RAYLIB_PATH)/src +LDFLAGS = -L. -L$(RAYLIB_RELEASE_PATH) ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS),WINDOWS) From 64e8bfcfb68e2de61cc4ee0ea46dbd8d82da90cd Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Thu, 12 Mar 2026 05:35:48 -0500 Subject: [PATCH 150/319] remove path override/change for linux (#5641) --- examples/Makefile | 9 --------- 1 file changed, 9 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 91ecf7475..899d040e3 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -155,15 +155,6 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) endif endif -# RAYLIB_PATH adjustment for LINUX platform -# TODO: Do we really need this? -ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) - ifeq ($(PLATFORM_OS),LINUX) - RAYLIB_PREFIX ?= .. - RAYLIB_PATH = $(realpath $(RAYLIB_PREFIX)) - endif -endif - # Default path for raylib on Raspberry Pi ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) RAYLIB_PATH ?= /home/pi/raylib From 8a93587eaa61d124298ab5d9c1cdf5fcd3a64f30 Mon Sep 17 00:00:00 2001 From: Thomas Prowse <152029833+SardineMilk@users.noreply.github.com> Date: Thu, 12 Mar 2026 10:36:53 +0000 Subject: [PATCH 151/319] Fix raycasting logic in models_basic_voxel.c (#5643) The original logic iterated through the world and broke at the first found voxel that intersected the ray. This broke in some cases, removing a voxel out of view. I changed the algorithm to track the closest found voxel, and remove it at the end of the loop if one was found. --- examples/models/models_basic_voxel.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/examples/models/models_basic_voxel.c b/examples/models/models_basic_voxel.c index b0f4bc65e..2e2756f2b 100644 --- a/examples/models/models_basic_voxel.c +++ b/examples/models/models_basic_voxel.c @@ -72,6 +72,7 @@ int main(void) UpdateCamera(&camera, CAMERA_FIRST_PERSON); // Handle voxel removal with mouse click + // This method is quite inefficient. Ray marching through the voxel grid using DDA would be faster, but more complex. if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { // Cast a ray from the screen center (where crosshair would be) @@ -79,12 +80,14 @@ int main(void) Ray ray = GetMouseRay(screenCenter, camera); // Check ray collision with all voxels - bool voxelRemoved = false; - for (int x = 0; (x < WORLD_SIZE) && !voxelRemoved; x++) + float closestDistance = 99999.0f; + Vector3 closestVoxelPosition = { -1, -1, -1 }; + bool voxelFound = false; + for (int x = 0; x < WORLD_SIZE; x++) { - for (int y = 0; (y < WORLD_SIZE) && !voxelRemoved; y++) + for (int y = 0; y < WORLD_SIZE; y++) { - for (int z = 0; (z < WORLD_SIZE) && !voxelRemoved; z++) + for (int z = 0; z < WORLD_SIZE; z++) { if (!voxels[x][y][z]) continue; // Skip empty voxels @@ -97,14 +100,23 @@ int main(void) // Check ray-box collision RayCollision collision = GetRayCollisionBox(ray, box); - if (collision.hit) + if (collision.hit && (collision.distance < closestDistance)) { - voxels[x][y][z] = false; // Remove this voxel - voxelRemoved = true; // Exit all loops + closestDistance = collision.distance; + closestVoxelPosition = (Vector3){ x, y, z }; + voxelFound = true; } } } } + + // Remove the closest voxel if one was hit + if (voxelFound) + { + voxels[(int)closestVoxelPosition.x] + [(int)closestVoxelPosition.y] + [(int)closestVoxelPosition.z] = false; + } } //---------------------------------------------------------------------------------- From f9ea60738547fed4e9a2e16af6c5b83b08475281 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Thu, 12 Mar 2026 05:38:06 -0500 Subject: [PATCH 152/319] fix error in textinsert, found by github user chrg127 (#5644) --- src/rtext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index 1cffd3bb4..aa01e9015 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1875,7 +1875,7 @@ char *TextInsert(const char *text, const char *insert, int position) result = (char *)RL_MALLOC(textLen + insertLen + 1); for (int i = 0; i < position; i++) result[i] = text[i]; - for (int i = position; i < insertLen + position; i++) result[i] = insert[i]; + for (int i = position; i < insertLen + position; i++) result[i] = insert[i - position]; for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i]; result[textLen + insertLen] = '\0'; // Add EOL From c5712db1e060ecdeff321ac08f1a99a2c28d250a Mon Sep 17 00:00:00 2001 From: Joel Torres Date: Fri, 13 Mar 2026 01:30:05 -0500 Subject: [PATCH 153/319] Update raylib-odin binding license (#5647) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 80deadff9..b9e26e690 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -59,7 +59,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers | [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 | +| [raylib-odin](https://github.com/odin-lang/Odin/tree/master/vendor/raylib) | **5.5** | [Odin](https://odin-lang.org) | Zlib | | [raylib_odin_bindings](https://github.com/Deathbat2190/raylib_odin_bindings) | 4.0-dev | [Odin](https://odin-lang.org) | MIT | | [raylib-ocaml](https://github.com/tjammer/raylib-ocaml) | **5.0** | [OCaml](https://ocaml.org) | MIT | | [TurboRaylib](https://github.com/turborium/TurboRaylib) | 4.5 | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT | From f83590aa3785287c3414eb559375447709656447 Mon Sep 17 00:00:00 2001 From: Jack Date: Fri, 13 Mar 2026 06:30:48 +0000 Subject: [PATCH 154/319] Update BINDINGS.md (#5648) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index b9e26e690..4a7680964 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -32,7 +32,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers | [DenoRaylib550](https://github.com/JJLDonley/DenoRaylib550) | **5.5** | [Deno](https://deno.land) | MIT | | [rayex](https://github.com/shiryel/rayex) | 3.7 | [elixir](https://elixir-lang.org) | Apache-2.0 | | [raylib-elle](https://github.com/acquitelol/elle/blob/rewrite/std/raylib.le) | **5.5** | [Elle](https://github.com/acquitelol/elle) | GPL-3.0 | -| [raylib-factor](https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor) | 4.5 | [Factor](https://factorcode.org) | BSD | +| [raylib-factor](https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor) | 5.5 | [Factor](https://factorcode.org) | BSD | | [raylib-freebasic](https://github.com/WIITD/raylib-freebasic) | **5.0** | [FreeBASIC](https://www.freebasic.net) | MIT | | [raylib.f](https://github.com/cthulhuology/raylib.f) | **5.5** | [Forth](https://forth.com) | Zlib | | [fortran-raylib](https://github.com/interkosmos/fortran-raylib) | **5.5** | [Fortran](https://fortran-lang.org) | ISC | From 7b9a2a4145182e07d3494f6bf0cdb015093eab37 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 13 Mar 2026 18:49:57 +0100 Subject: [PATCH 155/319] Update HISTORY.md --- HISTORY.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 53fbc44c0..7f5539816 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -530,11 +530,11 @@ A new raylib release is finally ready, after almost 1.5 years since last release Some astonishing numbers for this release: - - **+310** closed issues (for a TOTAL of **+2120**!) - - **+1800** commits since previous RELEASE (for a TOTAL of **+9550**!) - - **18** functions ADDED to raylib API (for a TOTAL of **598**!) + - **+320** closed issues (for a TOTAL of **+2130**!) + - **+1800** commits since previous RELEASE (for a TOTAL of **+9600**!) + - **18** functions ADDED to raylib API (for a TOTAL of **599**!) - **??** functions REVIEWED with fixes and improvements - - **+190** new contributors (for a TOTAL of **+830**!) + - **+200** new contributors (for a TOTAL of **+840**!) Highlights for `raylib 6.0`: From 6ba6df3af348b657a9f09a3757ebaa5e473406dc Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 13 Mar 2026 18:50:26 +0100 Subject: [PATCH 156/319] Remove trailing spaces --- src/CMakeLists.txt | 4 ++-- src/platforms/rcore_android.c | 6 +++--- src/platforms/rcore_web.c | 22 +++++++++++----------- src/platforms/rcore_web_emscripten.c | 20 ++++++++++---------- src/raudio.c | 2 +- src/rlgl.h | 2 +- src/rmodels.c | 2 +- src/rtext.c | 8 ++++---- tools/rexm/rexm.c | 20 ++++++++++---------- 9 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0975457ee..cad82aa8f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -124,10 +124,10 @@ if (PLATFORM STREQUAL "Desktop") target_compile_definitions(raylib PRIVATE _GLFW_X11) - target_link_libraries(raylib PRIVATE + target_link_libraries(raylib PRIVATE ${X11_LIBRARIES} ) - + message(STATUS "X11 support enabled for raylib") endif() endif() diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 42b58ff82..7f8ca8a7b 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -280,15 +280,15 @@ static int android_close(void *cookie); // The flag MUST be applied at every final link step that needs wrapping, // it has no effect when only building a static archive (.a) // -// CMake: no action required, raylib's CMakeLists.txt already sets +// CMake: no action required, raylib's CMakeLists.txt already sets // target_link_options(raylib INTERFACE -Wl,--wrap=fopen) which propagates to // the final app link, wrapping app code and all static (.a) dependencies too -// Make (SHARED): no action required for raylib itself, src/Makefile already sets +// Make (SHARED): no action required for raylib itself, src/Makefile already sets // LDFLAGS += -Wl,--wrap=fopen wrapping fopen inside libraylib.so only; // app code and static (.a) dependencies are NOT wrapped unless -Wl,--wrap=fopen // is also added to the final app link step // Make (STATIC): pass -Wl,--wrap=fopen to the linker command producing the final artifact -// build.zig: no dedicated wrap helper; pass -Wl,--wrap=fopen to the linker command producing +// build.zig: no dedicated wrap helper; pass -Wl,--wrap=fopen to the linker command producing // the final artifact // custom: pass -Wl,--wrap=fopen to the linker command producing the final artifact FILE *__real_fopen(const char *fileName, const char *mode); // Real fopen, provided by the linker (--wrap=fopen) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index da7b2713d..ae847d8c9 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -819,22 +819,22 @@ EM_ASYNC_JS(void, RequestClipboardData, (void), { { const blob = await item.getType(item.types.find(t => t.startsWith("image/"))); const bitmap = await createImageBitmap(blob); - + const canvas = document.createElement('canvas'); canvas.width = bitmap.width; canvas.height = bitmap.height; const ctx = canvas.getContext('2d'); ctx.drawImage(bitmap, 0, 0); - + const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height).data; - + // Store image and data for the Fetch function window._lastImgWidth = canvas.width; window._lastImgHeight = canvas.height; - window._lastImgData = imgData; + window._lastImgData = imgData; } } - } + } else console.warn("Clipboard read() requires HTTPS/Localhost"); }); @@ -856,19 +856,19 @@ EM_JS(unsigned char *, GetLastPastedImage, (int *width, int *height), { if (window._lastImgData) { const data = window._lastImgData; - if (data.length > 0) + if (data.length > 0) { const ptr = _malloc(data.length); HEAPU8.set(data, ptr); - + // Set the width and height via the pointers passed from C // HEAP32 handles the 4-byte integers if (width) setValue(width, window._lastImgWidth, 'i32'); if (height) setValue(height, window._lastImgHeight, 'i32'); - + // Clear the JS buffer so there is no need to fetch the same image twice - window._lastImgData = null; - + window._lastImgData = null; + return ptr; } } @@ -898,7 +898,7 @@ Image GetClipboardImage(void) image.mipmaps = 1; image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; } - + return image; } diff --git a/src/platforms/rcore_web_emscripten.c b/src/platforms/rcore_web_emscripten.c index 05da0da2e..df3234217 100644 --- a/src/platforms/rcore_web_emscripten.c +++ b/src/platforms/rcore_web_emscripten.c @@ -797,22 +797,22 @@ EM_ASYNC_JS(void, RequestClipboardData, (void), { { const blob = await item.getType(item.types.find(t => t.startsWith("image/"))); const bitmap = await createImageBitmap(blob); - + const canvas = document.createElement('canvas'); canvas.width = bitmap.width; canvas.height = bitmap.height; const ctx = canvas.getContext('2d'); ctx.drawImage(bitmap, 0, 0); - + const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height).data; - + // Store image and data for the Fetch function window._lastImgWidth = canvas.width; window._lastImgHeight = canvas.height; - window._lastImgData = imgData; + window._lastImgData = imgData; } } - } + } else console.warn("Clipboard read() requires HTTPS/Localhost"); }); @@ -838,15 +838,15 @@ EM_JS(unsigned char *, GetLastPastedImage, (int *width, int *height), { { const ptr = _malloc(data.length); HEAPU8.set(data, ptr); - + // Set the width and height via the pointers passed from C // HEAP32 handles the 4-byte integers if (width) setValue(width, window._lastImgWidth, 'i32'); if (height) setValue(height, window._lastImgHeight, 'i32'); - + // Clear the JS buffer so there is no need to fetch the same image twice - window._lastImgData = null; - + window._lastImgData = null; + return ptr; } } @@ -876,7 +876,7 @@ Image GetClipboardImage(void) image.mipmaps = 1; image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; } - + return image; } diff --git a/src/raudio.c b/src/raudio.c index 7020f6d53..937c33ac3 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1634,7 +1634,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, { music.ctxType = MUSIC_AUDIO_QOA; music.ctxData = ctxQoa; - + // NOTE: Loading samples are 32bit float normalized data, so, // configure the output audio stream to also use float 32bit music.stream = LoadAudioStream(ctxQoa->info.samplerate, 32, ctxQoa->info.channels); diff --git a/src/rlgl.h b/src/rlgl.h index e5b7d87f4..b40b50b43 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -4446,7 +4446,7 @@ void rlUnloadShader(unsigned int id) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glDeleteShader(id); - + TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Unloaded shader data from VRAM (GPU)", id); #endif } diff --git a/src/rmodels.c b/src/rmodels.c index 2807f5d9b..1c7a5452c 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3375,7 +3375,7 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) Vector3 n5 = { 0.0f, 0.0f, -1.0f }; Vector3 n6 = { 0.0f, 0.0f, 1.0f }; - // NOTE: Using texture rectangles to define different + // NOTE: Using texture rectangles to define different // textures for top-bottom-front-back-right-left (6) typedef struct RectangleF { float x; diff --git a/src/rtext.c b/src/rtext.c index aa01e9015..936ab9c16 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -156,7 +156,7 @@ extern void LoadFontDefault(void) { #define BIT_CHECK(a,b) ((a) & (1u << (b))) - // Check to see if the font for an image has alreeady been allocated, + // Check to see if the font for an image has alreeady been allocated, // and if no need to upload, then return if (defaultFont.glyphs != NULL) return; @@ -242,7 +242,7 @@ extern void LoadFontDefault(void) { if (BIT_CHECK(defaultFontData[counter], j)) { - // NOTE: Unreferencing data as short, so, + // NOTE: Unreferencing data as short, so, // considering data as little-endian (alpha + gray) ((unsigned short *)imFont.data)[i + j] = 0xffff; } @@ -446,7 +446,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) int charHeight = 0; int j = 0; - while (((lineSpacing + j) < image.height) && + while (((lineSpacing + j) < image.height) && !COLOR_EQUAL(pixels[(lineSpacing + j)*image.width + charSpacing], key)) j++; charHeight = j; @@ -470,7 +470,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) int charWidth = 0; - while (((xPosToRead + charWidth) < image.width) && + while (((xPosToRead + charWidth) < image.width) && !COLOR_EQUAL(pixels[(lineSpacing + (charHeight + lineSpacing)*lineToRead)*image.width + xPosToRead + charWidth], key)) charWidth++; tempCharRecs[index].width = (float)charWidth; diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 011102ce1..c681530c9 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -659,7 +659,7 @@ int main(int argc, char *argv[]) // we must store provided file paths because pointers will be overwriten // TODO: It seems projects are added to solution BUT not to required solution folder, // that process still requires to be done manually - LOG("INFO: [%s] Adding project to raylib solution (.sln)\n", + LOG("INFO: [%s] Adding project to raylib solution (.sln)\n", TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName)); AddVSProjectToSolution(exVSProjectSolutionFile, TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName), exCategory); @@ -1254,7 +1254,7 @@ int main(int argc, char *argv[]) exCollection[i].status &= ~VALID_NOT_IN_README; exCollection[i].status &= ~VALID_NOT_IN_JS; } - + // Check examples "status" information for (int i = 0; i < exCollectionCount; i++) { @@ -2686,21 +2686,21 @@ static int AddVSProjectToSolution(const char *slnFile, const char *projFile, con // Add project folder line // NOTE: Folder uuid depends on category - if (strcmp(category, "core") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, + if (strcmp(category, "core") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035}\n", uuid)); - else if (strcmp(category, "shapes") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, + else if (strcmp(category, "shapes") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s} = {278D8859-20B1-428F-8448-064F46E1F021}\n", uuid)); - else if (strcmp(category, "textures") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, + else if (strcmp(category, "textures") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE}\n", uuid)); - else if (strcmp(category, "text") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, + else if (strcmp(category, "text") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A}\n", uuid)); - else if (strcmp(category, "models") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, + else if (strcmp(category, "models") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C}\n", uuid)); - else if (strcmp(category, "shaders") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, + else if (strcmp(category, "shaders") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9}\n", uuid)); - else if (strcmp(category, "audio") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, + else if (strcmp(category, "audio") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s} = {CC132A4D-D081-4C26-BFB9-AB11984054F8}\n", uuid)); - else if (strcmp(category, "other") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, + else if (strcmp(category, "other") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s} = {E9D708A5-9C1F-4B84-A795-C5F191801762}\n", uuid)); else LOG("WARNING: Provided category is not valid: %s\n", category); //---------------------------------------------------------------------------------------- From 70cb8d15e0b1a22ab62f36d88a17e8d82b816cc6 Mon Sep 17 00:00:00 2001 From: dan-hoang <56205882+dan-hoang@users.noreply.github.com> Date: Fri, 13 Mar 2026 10:51:49 -0700 Subject: [PATCH 157/319] [examples] Add `audio_stream_callback` (#5638) * Add audio_raw_stream_callback.c * Update audio_raw_stream_callback.c to more closely follow raylib coding conventions * Remove spaces before asterisks in comments in audio_raw_stream_callback.c * Put SetTargetFPS(30) before while(!WindowShouldClose()) in audio_raw_stream_callback.c * Add audio_stream_callback.c * Delete examples/audio/audio_raw_stream_callback.c * Delete examples/audio/audio_raw_stream_callback.png * Update audio_raw_stream.c to more closely follow raylib coding conventions Drawing code wasn't tabbed in * Remove screenshot code in audio_stream_callback.c * Update raylib version and copyright year in audio_raw_stream.c * Update audio_stream_callback.c Used if instead of switch to compact code; seemed more readable in this case. --- examples/audio/audio_raw_stream.c | 44 ++--- examples/audio/audio_stream_callback.c | 236 +++++++++++++++++++++++ examples/audio/audio_stream_callback.png | Bin 0 -> 15750 bytes 3 files changed, 258 insertions(+), 22 deletions(-) create mode 100644 examples/audio/audio_stream_callback.c create mode 100644 examples/audio/audio_stream_callback.png diff --git a/examples/audio/audio_raw_stream.c b/examples/audio/audio_raw_stream.c index 536f8c311..64c9b5a50 100644 --- a/examples/audio/audio_raw_stream.c +++ b/examples/audio/audio_raw_stream.c @@ -4,14 +4,14 @@ * * Example complexity rating: [★★★☆] 3/4 * -* Example originally created with raylib 1.6, last time updated with raylib 4.2 +* Example originally created with raylib 1.6, last time updated with raylib 6.0 * * Example created by Ramon Santamaria (@raysan5) and reviewed by James Hofmann (@triplefox) * * 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) 2015-2025 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox) +* Copyright (c) 2015-2026 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox) * ********************************************************************************************/ @@ -109,26 +109,26 @@ int main(void) // Draw //---------------------------------------------------------------------------------- BeginDrawing(); - ClearBackground(RAYWHITE); - - DrawText(TextFormat("sine frequency: %i", sineFrequency), screenWidth - 220, 10, 20, RED); - DrawText(TextFormat("pan: %.2f", pan), screenWidth - 220, 30, 20, RED); - DrawText("Up/down to change frequency", 10, 10, 20, DARKGRAY); - DrawText("Left/right to pan", 10, 30, 20, DARKGRAY); - - int windowStart = (GetTime() - sineStartTime)*SAMPLE_RATE; - int windowSize = 0.1f*SAMPLE_RATE; - int wavelength = SAMPLE_RATE/sineFrequency; - - // Draw a sine wave with the same frequency as the one being sent to the audio stream - for (int i = 0; i < screenWidth; i++) - { - int t0 = windowStart + i*windowSize/screenWidth; - int t1 = windowStart + (i + 1)*windowSize/screenWidth; - Vector2 startPos = { i, 250 + 50*sin(2*PI*t0/wavelength) }; - Vector2 endPos = { i + 1, 250 + 50*sin(2*PI*t1/wavelength) }; - DrawLineV(startPos, endPos, RED); - } + ClearBackground(RAYWHITE); + + DrawText(TextFormat("sine frequency: %i", sineFrequency), screenWidth - 220, 10, 20, RED); + DrawText(TextFormat("pan: %.2f", pan), screenWidth - 220, 30, 20, RED); + DrawText("Up/down to change frequency", 10, 10, 20, DARKGRAY); + DrawText("Left/right to pan", 10, 30, 20, DARKGRAY); + + int windowStart = (GetTime() - sineStartTime)*SAMPLE_RATE; + int windowSize = 0.1f*SAMPLE_RATE; + int wavelength = SAMPLE_RATE/sineFrequency; + + // Draw a sine wave with the same frequency as the one being sent to the audio stream + for (int i = 0; i < screenWidth; i++) + { + int t0 = windowStart + i*windowSize/screenWidth; + int t1 = windowStart + (i + 1)*windowSize/screenWidth; + Vector2 startPos = { i, 250 + 50*sin(2*PI*t0/wavelength) }; + Vector2 endPos = { i + 1, 250 + 50*sin(2*PI*t1/wavelength) }; + DrawLineV(startPos, endPos, RED); + } EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/examples/audio/audio_stream_callback.c b/examples/audio/audio_stream_callback.c new file mode 100644 index 000000000..e31e8e80b --- /dev/null +++ b/examples/audio/audio_stream_callback.c @@ -0,0 +1,236 @@ +/******************************************************************************************* +* +* raylib [audio] example - stream callback +* +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 6.0, last time updated with raylib 6.0 +* +* Example created by Dan Hoang (@dan-hoang) and reviewed by +* +* 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) 2015-2026 +* +********************************************************************************************/ + +#include "raylib.h" +#include +#include + +#define BUFFER_SIZE 4096 +#define SAMPLE_RATE 44100 + +// This example sends a wave to the audio device +// The user gets the choice of four waves: sine, square, triangle, and sawtooth +// A stream is set up to play to the audio device +// The stream is hooked to a callback that generates a wave +// The callback used is determined by user choice +typedef enum +{ + SINE, + SQUARE, + TRIANGLE, + SAWTOOTH +} WaveType; + +int waveFrequency = 440; +int newWaveFrequency = 440; +int waveIndex = 0; + +// Buffer for keeping the last second of uploaded audio, part of which will be drawn on the screen +float buffer[SAMPLE_RATE] = {}; + +void SineCallback(void *framesOut, unsigned int frameCount) +{ + int wavelength = SAMPLE_RATE/waveFrequency; + + // Synthesize the sine wave + for (int i = 0; i < frameCount; i++) + { + ((float *)framesOut)[i] = sin(2*PI*waveIndex/wavelength); + + waveIndex++; + + if (waveIndex >= wavelength) + { + waveFrequency = newWaveFrequency; + waveIndex = 0; + } + } + + // Save the synthesized samples for later drawing + for (int i = 0; i < SAMPLE_RATE - frameCount; i++) buffer[i] = buffer[i + frameCount]; + for (int i = 0; i < frameCount; i++) buffer[SAMPLE_RATE - frameCount + i] = ((float *)framesOut)[i]; +} + +void SquareCallback(void *framesOut, unsigned int frameCount) +{ + int wavelength = SAMPLE_RATE/waveFrequency; + + // Synthesize the square wave + for (int i = 0; i < frameCount; i++) + { + ((float *)framesOut)[i] = (waveIndex < wavelength/2)? 1 : -1; + waveIndex++; + + if (waveIndex >= wavelength) + { + waveFrequency = newWaveFrequency; + waveIndex = 0; + } + } + + // Save the synthesized samples for later drawing + for (int i = 0; i < SAMPLE_RATE - frameCount; i++) buffer[i] = buffer[i + frameCount]; + for (int i = 0; i < frameCount; i++) buffer[SAMPLE_RATE - frameCount + i] = ((float *)framesOut)[i]; +} + +void TriangleCallback(void *framesOut, unsigned int frameCount) +{ + int wavelength = SAMPLE_RATE/waveFrequency; + + // Synthesize the triangle wave + for (int i = 0; i < frameCount; i++) + { + ((float *)framesOut)[i] = (waveIndex < wavelength/2)? (-1 + 2.0f*waveIndex/(wavelength/2)) : (1 - 2.0f*(waveIndex - wavelength/2)/(wavelength/2)); + waveIndex++; + + if (waveIndex >= wavelength) + { + waveFrequency = newWaveFrequency; + waveIndex = 0; + } + } + + // Save the synthesized samples for later drawing + for (int i = 0; i < SAMPLE_RATE - frameCount; i++) buffer[i] = buffer[i + frameCount]; + for (int i = 0; i < frameCount; i++) buffer[SAMPLE_RATE - frameCount + i] = ((float *)framesOut)[i]; +} + +void SawtoothCallback(void *framesOut, unsigned int frameCount) +{ + int wavelength = SAMPLE_RATE/waveFrequency; + + // Synthesize the sawtooth wave + for (int i = 0; i < frameCount; i++) + { + ((float *)framesOut)[i] = -1 + 2.0f*waveIndex/wavelength; + waveIndex++; + + if (waveIndex >= wavelength) + { + waveFrequency = newWaveFrequency; + waveIndex = 0; + } + } + + // Save the synthesized samples for later drawing + for (int i = 0; i < SAMPLE_RATE - frameCount; i++) buffer[i] = buffer[i + frameCount]; + for (int i = 0; i < frameCount; i++) buffer[SAMPLE_RATE - frameCount + i] = ((float *)framesOut)[i]; +} + +AudioCallback waveCallbacks[] = { SineCallback, SquareCallback, TriangleCallback, SawtoothCallback }; +char *waveTypesAsString[] = { "sine", "square", "triangle", "sawtooth" }; + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [audio] example - stream callback"); + + InitAudioDevice(); + + // Set the number of samples the stream will keep in memory at a time to BUFFER_SIZE + SetAudioStreamBufferSizeDefault(BUFFER_SIZE); + + // Init raw audio stream (sample rate: 44100, sample size: 32bit-float, channels: 1-mono) + AudioStream stream = LoadAudioStream(SAMPLE_RATE, 32, 1); + PlayAudioStream(stream); + + // Configure it so that waveCallbacks[waveType] is called whenever stream is out of samples + WaveType waveType = SINE; + SetAudioStreamCallback(stream, waveCallbacks[waveType]); + + SetTargetFPS(30); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + + if (IsKeyDown(KEY_UP)) + { + newWaveFrequency += 10; + if (newWaveFrequency > 12500) newWaveFrequency = 12500; + } + + if (IsKeyDown(KEY_DOWN)) + { + newWaveFrequency -= 10; + if (newWaveFrequency < 20) newWaveFrequency = 20; + } + + if (IsKeyPressed(KEY_LEFT)) + { + if (waveType == SINE) waveType = SAWTOOTH; + else if (waveType == SQUARE) waveType = SINE; + else if (waveType == TRIANGLE) waveType = SQUARE; + else waveType = TRIANGLE; + + SetAudioStreamCallback(stream, waveCallbacks[waveType]); + } + + if (IsKeyPressed(KEY_RIGHT)) + { + if (waveType == SINE) waveType = SQUARE; + else if (waveType == SQUARE) waveType = TRIANGLE; + else if (waveType == TRIANGLE) waveType = SAWTOOTH; + else waveType = SINE; + + SetAudioStreamCallback(stream, waveCallbacks[waveType]); + } + + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + DrawText(TextFormat("frequency: %i", newWaveFrequency), screenWidth - 220, 10, 20, RED); + DrawText(TextFormat("wave type: %s", waveTypesAsString[waveType]), screenWidth - 220, 30, 20, RED); + DrawText("Up/down to change frequency", 10, 10, 20, DARKGRAY); + DrawText("Left/right to change wave type", 10, 30, 20, DARKGRAY); + + // Draw the last 10 ms of uploaded audio + for (int i = 0; i < screenWidth; i++) + { + Vector2 startPos = { i, 250 - 50*buffer[SAMPLE_RATE - SAMPLE_RATE/100 + i*SAMPLE_RATE/100/screenWidth] }; + Vector2 endPos = { i + 1, 250 - 50*buffer[SAMPLE_RATE - SAMPLE_RATE/100 + (i + 1)*SAMPLE_RATE/100/screenWidth] }; + DrawLineV(startPos, endPos, RED); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadAudioStream(stream); // Close raw audio stream and delete buffers from RAM + CloseAudioDevice(); // Close audio device (music streaming is automatically stopped) + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/audio/audio_stream_callback.png b/examples/audio/audio_stream_callback.png new file mode 100644 index 0000000000000000000000000000000000000000..12305744e5f06d9f1f465524f75db1dedca46753 GIT binary patch literal 15750 zcmeHOYg7|w8cw)I1QHc7L;_@4gl;P$1m)6T3YZvd06~$aYNH?yh#Xe|DfeiAMJ_>f zRd9u1Xe1~WccVgtl?W;bXaW%wjaCHYQt<*oKq;Lh;IT;JkKMDLJChrtL`isv`$W3~jn9hVIx zrLMEXXmD^U4v`2rSL|zMz!0O*vmW5FDK&s@Fup7+wFW?B&T_EJj3$y62xtEzctQDG zhS(%W>`71sgLnyrewLiMO-mfE#bCHo{^J0tAtL zepR%;Z6KGi|9rir;RFXCXY+X4cNW&h#aMBmtUGR3t$X1!Y;Ool%IVncA zZ^&Vo#hdp#(gd>&h~hWVQmw$$us~LJIjbkP$Vj7n_+Vo*GsKu26o=O$dC=vD!UU^P+WVM%X1DbbdrFjUJS(FLX_;U;Aprf@63|KZSU ztPy!%(;d*t&0$fnJTR<#q~x=gZDgxrM^Uapc;At$57wTY=(?pZaw*_p6YF}yw>>!~ z*}Y^VfoIi1yw(Uvmrq50SP$+;{)Hm7Bn}X?`O(O-5Sx0(V|Vjx0>^P1CV*R(OO@ei zQWGA!sBpidfD{+prQ`Q{Q1s>cl4rJ&Lu*d<)pS1>8YdobbR!QOoyv9#BaL3mM-ohf zdAMc2<#uMFgg_`RHE&+ml~U8IUk}_gnU$9yVcFj!^gBRi<*^Rs&D9CJt$j|!ZPV9o ziVr(M-nqAdn!h_%elD-51G|f(1?aCVCzx+nhYHZ)k4ZZx zv)jIzx-Ib;%0kACq7`;{4xD%t+WkoAvezLt%sA9JBvL1spNUoTf_8}$2rGH)eka#PgE8%&HviD>=8@`C(;LAGR5?7-;&C;g)_R!2J14!V*XZTp!uKs<|E zJarFt1R{BamN|F{P2F(7N|BJQ)7U?d1dy+E(LeUD2BK2}R(g~D`wDV}dn`wQhbl5l zXsW?+u;BFVeJ2!u;2NkK5jf(%OiBU?2#<`7B=1l z7qECyokq5C^^S!(mm9L2{iKw$v3sOzOWnfK9KJGbpjsceHo?EyAP?<@k7;XhcKm$W z5H+IS)4z{@M$C7{yC=7pXw$~_upg{m7N@(j!XHyud*=sE_dR*W!p3mKG8030TlO)+ zu=ia4$RgZOsN({rSvIFnEAA!+B>B<=vQqSCj3syqil^p9U9DqCUUg4gWKU4=(XwEc>U=zXWPA7N03MJ zkhGt%)<%XBJ)h&wk`ZKCi*DJ246}k_a404+BFu0@l&EFAvGlgz4y3H&bvMy7Kxh%u z(8lu<+;@&rHb1%T(^YLzjyg56MQ2k_5sQ7ee_7a>w8}Kep@coJH|g_}zG$8&@wVpQ zDpHJ$$|6EO>%AOk|9Mi^BZkZ>_b)v~_UXp7-HXR3>XL^~-KYhsGTY8gOWt<~_VWgd zDAsk~xs#6vuP_d>Om{ccoUn z)MGjL6OQPm6qRFYM5Ts(PtzXrR3#m1S1D(*v8mh7Dja-G`~17==3NdpM_i}m!aG@< zo^!xQcYyPYrfLYwln~@vJWFW_W;Uo@5CkX+3YCL$L9X>s6rdQtrBdJ${SLAd17PN6f^Z&j2DIhSTw%3;Z(~R0$cf+QnYP-Ch3g|LZD`7`0$X~7) zH*(-?r9L9P1O(iK7#6orjBmEn7b-71KeU`?J>UEc+nyeYMAX{M-dvS8aW*0p&C3os zCucJsKdx3JR|`()hqmstX*a6uBbBOb@`47eqJ?{36$C{C*kE>eAY}b3$~Mv+l{Q}_ z*nc&w2evI%I_?Xqx};$xfO$iQJJdfM4X)qXa|?7 z>m+U{`r4>?loE@a@irrn{6bz|5RO&#bj=aSA z49Ea$1d3bSwA}KYvUG;PI{h*VO>O6-$5w#bDf7W)P3>|mQmYJHuw>$DMr8x-lbm%9 zGwR~^Plv%p5&_&76#hYRJ5}{Ug~xwmtLbNvnu@KB(2^8dl73|CH$(=-K3}M4fr^&T zq-cR=2C(LW%*u~`6a>u-(99tJt_|{bAa6&}vVe?EhzwIL#LvLnfy$)+L74;^S}<4! YBk1H8IU* Date: Fri, 13 Mar 2026 20:53:11 +0300 Subject: [PATCH 158/319] Adapt build.zig to redesigned raylib build features config system (#5645) --- build.zig | 107 ++++++++++++++++-------------------------------------- 1 file changed, 32 insertions(+), 75 deletions(-) diff --git a/build.zig b/build.zig index 7eb46699e..3dc95bc83 100644 --- a/build.zig +++ b/build.zig @@ -90,41 +90,7 @@ fn setDesktopPlatform(raylib: *std.Build.Step.Compile, platform: PlatformBackend } } -/// 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 - @setEvalBranchQuota(1 << 20); - - const config_h = @embedFile("src/config.h"); - var flags: [std.mem.count(u8, config_h, "\n") + 1][]const u8 = undefined; - - var i = 0; - var lines = std.mem.tokenizeScalar(u8, config_h, '\n'); - while (lines.next()) |line| { - if (!std.mem.containsAtLeast(u8, line, 1, "SUPPORT")) continue; - if (std.mem.containsAtLeast(u8, line, 1, "MODULE")) continue; - if (std.mem.startsWith(u8, line, "//")) continue; - if (std.mem.startsWith(u8, line, "#if")) continue; - - var flag = std.mem.trimStart(u8, line, " \t"); // Trim whitespace - flag = flag["#define ".len - 1 ..]; // Remove #define - flag = std.mem.trimStart(u8, flag, " \t"); // Trim whitespace - flag = flag[0 .. std.mem.indexOf(u8, flag, " ") orelse continue]; // Flag is only one word, so capture till space - flag = "-D" ++ flag; // Prepend with -D - - flags[i] = flag; - i += 1; - } - - // Uncomment this to check what flags normally get passed - //@compileLog(flags[0..i].*); - break :outer flags[0..i].*; -}; - fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, options: Options) !*std.Build.Step.Compile { - var raylib_flags_arr: std.ArrayList([]const u8) = .empty; - defer raylib_flags_arr.deinit(b.allocator); - const raylib = b.addLibrary(.{ .name = "raylib", .linkage = options.linkage, @@ -135,29 +101,27 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. }), }); - try raylib_flags_arr.appendSlice( + raylib.root_module.addCMacro("_GNU_SOURCE", ""); + raylib.root_module.addCMacro("GL_SILENCE_DEPRECATION", "199309L"); + + var raylib_flags_arr: std.ArrayList([]const u8) = .empty; + defer raylib_flags_arr.deinit(b.allocator); + + try raylib_flags_arr.append( b.allocator, - &[_][]const u8{ - "-std=gnu99", - "-D_GNU_SOURCE", - "-DGL_SILENCE_DEPRECATION=199309L", - "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/3674 - }, + "-std=gnu99", ); if (options.linkage == .dynamic) { - try raylib_flags_arr.appendSlice( + try raylib_flags_arr.append( b.allocator, - &[_][]const u8{ - "-fPIC", - "-DBUILD_LIBTYPE_SHARED", - }, + "-fPIC", ); + + raylib.root_module.addCMacro("BUILD_LIBTYPE_SHARED", ""); } if (options.config.len > 0) { - // Sets a flag indicating the use of a custom `config.h` - try raylib_flags_arr.append(b.allocator, "-DEXTERNAL_CONFIG_FLAGS"); // Splits a space-separated list of config flags into multiple flags // // Note: This means certain flags like `-x c++` won't be processed properly. @@ -165,30 +129,9 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. var config_iter = std.mem.tokenizeScalar(u8, options.config, ' '); // Apply config flags supplied by the user - while (config_iter.next()) |config_flag| + while (config_iter.next()) |config_flag| { try raylib_flags_arr.append(b.allocator, config_flag); - - // Apply all relevant configs from `src/config.h` *except* the user-specified ones - // - // Note: Currently using a suboptimal `O(m*n)` time algorithm where: - // `m` corresponds roughly to the number of lines in `src/config.h` - // `n` corresponds to the number of user-specified flags - outer: for (config_h_flags) |flag| { - // If a user already specified the flag, skip it - config_iter.reset(); - while (config_iter.next()) |config_flag| { - // For a user-specified flag to match, it must share the same prefix and have the - // same length or be followed by an equals sign - if (!std.mem.startsWith(u8, config_flag, flag)) continue; - if (config_flag.len == flag.len or config_flag[flag.len] == '=') continue :outer; - } - - // Otherwise, append default value from config.h to compile flags - try raylib_flags_arr.append(b.allocator, flag); } - } else { - // Set default config if no custom config got set - try raylib_flags_arr.appendSlice(b.allocator, &config_h_flags); } // No GLFW required on PLATFORM_DRM @@ -200,24 +143,38 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. c_source_files.appendSliceAssumeCapacity(&.{"src/rcore.c"}); if (options.rshapes) { + raylib.root_module.addCMacro("SUPPORT_MODULE_RSHAPES", "1"); try c_source_files.append(b.allocator, "src/rshapes.c"); - try raylib_flags_arr.append(b.allocator, "-DSUPPORT_MODULE_RSHAPES"); + } else { + raylib.root_module.addCMacro("SUPPORT_MODULE_RSHAPES", "0"); } + if (options.rtextures) { + raylib.root_module.addCMacro("SUPPORT_MODULE_RTEXTURES", "1"); try c_source_files.append(b.allocator, "src/rtextures.c"); - try raylib_flags_arr.append(b.allocator, "-DSUPPORT_MODULE_RTEXTURES"); + } else { + raylib.root_module.addCMacro("SUPPORT_MODULE_RTEXTURES", "0"); } + if (options.rtext) { + raylib.root_module.addCMacro("SUPPORT_MODULE_RTEXT", "1"); try c_source_files.append(b.allocator, "src/rtext.c"); - try raylib_flags_arr.append(b.allocator, "-DSUPPORT_MODULE_RTEXT"); + } else { + raylib.root_module.addCMacro("SUPPORT_MODULE_RTEXT", "0"); } + if (options.rmodels) { + raylib.root_module.addCMacro("SUPPORT_MODULE_RMODELS", "1"); try c_source_files.append(b.allocator, "src/rmodels.c"); - try raylib_flags_arr.append(b.allocator, "-DSUPPORT_MODULE_RMODELS"); + } else { + raylib.root_module.addCMacro("SUPPORT_MODULE_RMODELS", "0"); } + if (options.raudio) { + raylib.root_module.addCMacro("SUPPORT_MODULE_RAUDIO", "1"); try c_source_files.append(b.allocator, "src/raudio.c"); - try raylib_flags_arr.append(b.allocator, "-DSUPPORT_MODULE_RAUDIO"); + } else { + raylib.root_module.addCMacro("SUPPORT_MODULE_RAUDIO", "0"); } if (options.opengl_version != .auto) { From 38ed50c07bbf694fa9cad756b5273d8daedefc2e Mon Sep 17 00:00:00 2001 From: ghera Date: Fri, 13 Mar 2026 18:53:39 +0100 Subject: [PATCH 159/319] [rcore][android] Fix CMake shared build and improve --wrap WARNING docs (#5646) --- src/CMakeLists.txt | 2 +- src/platforms/rcore_android.c | 38 +++++++++++++++++++++-------------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0975457ee..dbd5a62a6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -78,7 +78,7 @@ endif() if (${PLATFORM} MATCHES "Android") # Wrap fopen at link time so all code (including third-party libs) goes # through __wrap_fopen, which handles Android APK asset loading - target_link_options(raylib INTERFACE -Wl,--wrap=fopen) + target_link_options(raylib PUBLIC -Wl,--wrap=fopen) endif() set_target_properties(raylib PROPERTIES diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 42b58ff82..4442632c4 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -276,21 +276,29 @@ static fpos_t android_seek(void *cookie, fpos_t offset, int whence); static int android_close(void *cookie); // WARNING: fopen() calls are intercepted via linker flag -Wl,--wrap=fopen: the linker renames -// the original fopen -> __real_fopen and redirects all call sites to __wrap_fopen -// The flag MUST be applied at every final link step that needs wrapping, -// it has no effect when only building a static archive (.a) +// the original fopen -> __real_fopen and redirects all call sites to __wrap_fopen. +// The flag MUST be applied at every final link step that needs wrapping; +// it has no effect when only building a static archive (.a). // -// CMake: no action required, raylib's CMakeLists.txt already sets -// target_link_options(raylib INTERFACE -Wl,--wrap=fopen) which propagates to -// the final app link, wrapping app code and all static (.a) dependencies too -// Make (SHARED): no action required for raylib itself, src/Makefile already sets -// LDFLAGS += -Wl,--wrap=fopen wrapping fopen inside libraylib.so only; -// app code and static (.a) dependencies are NOT wrapped unless -Wl,--wrap=fopen -// is also added to the final app link step -// Make (STATIC): pass -Wl,--wrap=fopen to the linker command producing the final artifact -// build.zig: no dedicated wrap helper; pass -Wl,--wrap=fopen to the linker command producing -// the final artifact -// custom: pass -Wl,--wrap=fopen to the linker command producing the final artifact +// STATIC library (.a) — wrapping deferred to consumer's final link step: +// both raylib and consumer fopen calls are wrapped together in one link +// CMake: handled automatically — the PUBLIC flag propagates as INTERFACE_LINK_OPTIONS +// to the consumer's final link via target_link_libraries +// Make: pass -Wl,--wrap=fopen to the linker command producing the final artifact +// build.zig: pass -Wl,--wrap=fopen to the linker command producing the final artifact +// custom: pass -Wl,--wrap=fopen to the linker command producing the final artifact +// +// SHARED library (.so) — wrapping is self-contained: +// only fopen calls linked into the .so are wrapped; the consumer's own fopen calls +// are NOT wrapped unless the consumer also links with -Wl,--wrap=fopen independently +// CMake: handled automatically — CMakeLists.txt sets target_link_options(raylib PUBLIC +// -Wl,--wrap=fopen) which applies the flag to the .so link; +// only raylib internals are wrapped, app code requires a separate flag +// Make: handled automatically — src/Makefile sets LDFLAGS += -Wl,--wrap=fopen; +// only raylib internals are wrapped, app code requires a separate flag +// build.zig: NOT supported — std.Build has no dedicated linker wrap helper, the flag +// is not correctly applied at the .so link step +// custom: apply -Wl,--wrap=fopen to the linker command producing the .so FILE *__real_fopen(const char *fileName, const char *mode); // Real fopen, provided by the linker (--wrap=fopen) FILE *__wrap_fopen(const char *fileName, const char *mode); // Replacement for fopen() @@ -1542,7 +1550,7 @@ static void SetupFramebuffer(int width, int height) // Replacement for fopen(), used as linker wrap entry point (-Wl,--wrap=fopen) // REF: https://developer.android.com/ndk/reference/group/asset -FILE *__wrap_fopen(const char *fileName, const char *mode) +__attribute__((visibility("default"))) FILE *__wrap_fopen(const char *fileName, const char *mode) { FILE *file = NULL; From a6d5a7ffbcda2d33fbb8d2975bedff226a06ffde Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 13 Mar 2026 19:44:06 +0100 Subject: [PATCH 160/319] Update GitHub usernames for contributors --- CHANGELOG | 314 +++++++++++++++++++++++++++--------------------------- 1 file changed, 157 insertions(+), 157 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index d78e7ed4f..9bfdbe484 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -34,34 +34,34 @@ KEY CHANGES: Detailed changes: -[rcore] ADDED: Working directory info at initialization by @Ray +[rcore] ADDED: Working directory info at initialization by @raysan5 [rcore] ADDED: `GetClipboardImage()`, supported by multiple backends (#4459) by @evertonse -[rcore] ADDED: `MakeDirectory()`, supporting recursive directory creation by @Ray +[rcore] ADDED: `MakeDirectory()`, supporting recursive directory creation by @raysan5 [rcore] ADDED: `ComputeSHA1()` (#4390) by @Anthony Carbajal -[rcore] ADDED: `ComputeCRC32()` and `ComputeMD5()` by @Ray +[rcore] ADDED: `ComputeCRC32()` and `ComputeMD5()` by @raysan5 [rcore] ADDED: `GetKeyName()` (#4161) by @MrScautHD -[rcore] ADDED: `IsFileNameValid()` by @Ray -[rcore] ADDED: `GetViewRay()`, viewport independent raycast (#3709) by @Luís Almeida -[rcore] RENAMED: `GetMouseRay()` to `GetScreenToWorldRay()` (#3830) by @Ray -[rcore] RENAMED: `GetViewRay()` to `GetScreenToWorldRayEx()` (#3830) by @Ray -[rcore] REVIEWED: `GetApplicationDirectory()` for FreeBSD (#4318) by @base +[rcore] ADDED: `IsFileNameValid()` by @raysan5 +[rcore] ADDED: `GetViewRay()`, viewport independent raycast (#3709) by @luis605 +[rcore] RENAMED: `GetMouseRay()` to `GetScreenToWorldRay()` (#3830) by @raysan5 +[rcore] RENAMED: `GetViewRay()` to `GetScreenToWorldRayEx()` (#3830) by @raysan5 +[rcore] REVIEWED: `GetApplicationDirectory()` for FreeBSD (#4318) by @git [rcore] REVIEWED: `LoadDirectoryFilesEx()`/`ScanDirectoryFiles()`, support directory on filter (#4302) by @foxblock -[rcore] REVIEWED: Update comments on fullscreen and boderless window to describe what they do (#4280) by @Jeffery Myers -[rcore] REVIEWED: Correct processing of mouse wheel on Automation events #4263 by @Ray +[rcore] REVIEWED: Update comments on fullscreen and boderless window to describe what they do (#4280) by @Jeffm2501 +[rcore] REVIEWED: Correct processing of mouse wheel on Automation events #4263 by @raysan5 [rcore] REVIEWED: Fix gamepad axis movement and its automation event recording (#4184) by @maxmutant [rcore] REVIEWED: Do not set RL_TEXTURE_FILTER_LINEAR when high dpi flag is enabled (#4189) by @Dave Green [rcore] REVIEWED: `GetScreenWidth()`/`GetScreenHeight()` (#4074) by @Anthony Carbajal [rcore] REVIEWED: Initial window dimensions checks (#3950) by @Christian Haas -[rcore] REVIEWED: Set default init values for random #3954 by @Ray -[rcore] REVIEWED: Window positioning, avoid out-of-screen window-bar by @Ray -[rcore] REVIEWED: Fix framerate recording for .gif (#3894) by @Rob Loach +[rcore] REVIEWED: Set default init values for random #3954 by @raysan5 +[rcore] REVIEWED: Window positioning, avoid out-of-screen window-bar by @raysan5 +[rcore] REVIEWED: Fix framerate recording for .gif (#3894) by @RobLoach [rcore] REVIEWED: Screen space related functions consistency (#3830) by @aiafrasinei [rcore] REVIEWED: `GetFileNameWithoutExt()` (#3771) by @oblerion -[rcore] REVIEWED: `GetWindowScaleDPI()`, simplified (#3701) by @Karl Zylinski -[rcore] REVIEWED: `UnloadAutomationEventList()` (#3658) by @Antonis Geralis +[rcore] REVIEWED: `GetWindowScaleDPI()`, simplified (#3701) by @karl +[rcore] REVIEWED: `UnloadAutomationEventList()` (#3658) by @planetis-m [rcore] REVIEWED: Flip VR screens (#3633) by @Matthew Oros [rcore] REVIEWED: Remove unused vScreenCenter (#3632) by @Matthew Oros -[rcore] REVIEWED: `LoadRandomSequence()`, issue in sequence generation #3612 by @Ray +[rcore] REVIEWED: `LoadRandomSequence()`, issue in sequence generation #3612 by @raysan5 [rcore] REVIEWED: `IsMouseButtonUp()` (#3609) by @Kenneth M [rcore] REVIEWED: Fix typos in src/platforms/rcore_*.c (#3581) by @RadsammyT [rcore] REVIEWED: `ExportDataAsCode()`, change sanitization check (#3837) by @Laurentino Luna @@ -69,17 +69,17 @@ Detailed changes: [rcore] REVIEWED: `GetScreenWidth()`/`GetScreenHeight()` align with all platforms (#4451) by @Arche Washi [rcore] REVIEWED: `SetGamepadVibration()`, added duration parameter (#4410) by @Asdqwe -WARNING- [rcore] REVIEWED: `GetGamepadAxisMovement()`, fix #4405 (#4420) by @Asdqwe -[rcore] REVIEWED: `GetGestureHoldDuration()` comments by @Ray -[rcore][rlgl] REVIEWED: Fix scale issues when ending a view mode (#3746) by @Jeffery Myers +[rcore] REVIEWED: `GetGestureHoldDuration()` comments by @raysan5 +[rcore][rlgl] REVIEWED: Fix scale issues when ending a view mode (#3746) by @Jeffm2501 [rcore][GLFW] REVIEWED: Keep CORE.Window.position properly in sync with glfw window position (#4190) by @Dave Green [rcore][GLFW] REVIEWED: Set AUTO_ICONIFY flag to false per default (#4188) by @Dave Green [rcore][GLFW] REVIEWED: `InitPlatform()`, add workaround for NetBSD (#4139) by @NishiOwO [rcore][GLFW] REVIEWED: Fix window not initializing on primary monitor (#3923) by @Rafael Bordoni -[rcore][GLFW] REVIEWED: Set relative mouse mode when the cursor is disabled (#3874) by @Jeffery Myers -[rcore][GLFW] REVIEWED: Remove GLFW mouse passthrough hack and increase GLFW version in CMake (#3852) by @Alexandre Almeida -[rcore][GLFW] REVIEWED: Updated GLFW to 3.4 (#3827) by @Alexandre Almeida +[rcore][GLFW] REVIEWED: Set relative mouse mode when the cursor is disabled (#3874) by @Jeffm2501 +[rcore][GLFW] REVIEWED: Remove GLFW mouse passthrough hack and increase GLFW version in CMake (#3852) by @M374LX +[rcore][GLFW] REVIEWED: Updated GLFW to 3.4 (#3827) by @M374LX [rcore][GLFW] REVIEWED: Feature test macros before include (#3737) by @John -[rcore][GLFW] REVIEWED: Fix inconsistent dll linkage warning on windows (#4447) by @Jeffery Myers +[rcore][GLFW] REVIEWED: Fix inconsistent dll linkage warning on windows (#4447) by @Jeffm2501 [rcore][Web] ADDED: `SetWindowOpacity()` implementation (#4403) by @Asdqwe [rcore][Web] ADDED: `MaximizeWindow()` and `RestoreWindow()` implementations (#4397) by @Asdqwe [rcore][Web] ADDED: `ToggleFullscreen()` implementation (#3634) by @ubkp @@ -92,21 +92,21 @@ Detailed changes: [rcore][Web] REVIEWED: `ShowCursor()`, `HideCursor()` and `SetMouseCursor()` (#3647) by @ubkp [rcore][Web] REVIEWED: Fix CORE.Input.Mouse.cursorHidden with callbacks (#3644) by @ubkp [rcore][Web] REVIEWED: Fix `IsMouseButtonUp()` (#3611) by @ubkp -[rcore][Web] REVIEWED: HighDPI support #3372 by @Ray +[rcore][Web] REVIEWED: HighDPI support #3372 by @raysan5 [rcore][Web] REVIEWED: `SetWindowSize()` (#4452) by @Asdqwe [rcore][Web] REVIEWED: `EmscriptenResizeCallback()`, simplified (#4415) by @Asdqwe [rcore][SDL] ADDED: `IsCursorOnScreen()` (#3862) by @Peter0x44 -[rcore][SDL] ADDED: Gamepad rumble/vibration support (#3819) by @GideonSerf +[rcore][SDL] ADDED: Gamepad rumble/vibration support (#3819) by @gdserf.gs [rcore][SDL] REVIEWED: Gamepad support (#3776) by @A [rcore][SDL] REVIEWED: `GetWorkingDirectory()`, return correct path (#4392) by @Asdqwe [rcore][SDL] REVIEWED: `GetClipboardText()`, fix memory leak (#4354) by @Asdqwe [rcore][SDL] REVIEWED: Change SDL_Joystick to SDL_GameController (#4129) by @Frank Kartheuser -[rcore][SDL] REVIEWED: Update storage base path, use provided SDL base path by @Ray +[rcore][SDL] REVIEWED: Update storage base path, use provided SDL base path by @raysan5 [rcore][SDL] REVIEWED: Call SDL_GL_SetSwapInterval() after GL context creation (#3997) by @JupiterRider [rcore][SDL] REVIEWED: `GetKeyPressed()` (#3869) by @Arthur [rcore][SDL] REVIEWED: Fix SDL multitouch tracking (#3810) by @mooff [rcore][SDL] REVIEWED: Fix `SUPPORT_WINMM_HIGHRES_TIMER` (#3679) by @ubkp -[rcore][SDL] REVIEWED: SDL text input to Unicode codepoints #3650 by @Ray +[rcore][SDL] REVIEWED: SDL text input to Unicode codepoints #3650 by @raysan5 [rcore][SDL] REVIEWED: `IsMouseButtonUp()` and add touch events (#3610) by @ubkp [rcore][SDL] REVIEWED: Fix real touch gestures (#3614) by @ubkp [rcore][SDL] REVIEWED: `IsKeyPressedRepeat()` (#3605) by @ubkp @@ -118,7 +118,7 @@ Detailed changes: [rcore][Android] REVIEWED: Allow main() to return it its caller on configuration changes (#4288) by @Hesham Abourgheba [rcore][Android] REVIEWED: Replace deprecated Android function ALooper_pollAll with ALooper_pollOnce (#4275) by @Menno van der Graaf [rcore][Android] REVIEWED: `PollInputEvents()`, register previous gamepad events (#3910) by @Aria -[rcore][Android] REVIEWED: Fix Android keycode translation and duplicate key constants (#3733) by @Alexandre Almeida +[rcore][Android] REVIEWED: Fix Android keycode translation and duplicate key constants (#3733) by @M374LX [rcore][DRM] ADDED: uConsole keys mapping (#4297) by @carverdamien [rcore][DRM] ADDED: `GetMonitorWidth/Height()` (#3956) by @gabriel-marques [rcore][DRM] REVIEWED: `IsMouseButtonUp()` (#3611) by @ubkp @@ -128,124 +128,124 @@ Detailed changes: [rcore][DRM] REVIEWED: DRM backend to only use one api to allow for more devices (#3879) by @MrMugame [rcore][DRM] REVIEWED: Avoid separate thread when polling for gamepad events (#3641) by @Cinghy Creations [rcore][DRM] REVIEWED: Connector status reported as UNKNOWN but should be considered as CONNECTED (#4305) by @Michał Jaskólski -[rcore][RGFW] ADDED: RGFW, new rcore backend platform (#3941) by @Colleague Riley -[rcore][RGFW] REVIEWED: RGFW 1.0 (#4144) by @Colleague Riley -[rcore][RGFW] REVIEWED: Fix errors when compiling with mingw (#4282) by @Colleague Riley -[rcore][RGFW] REVIEWED: Replace long switch with a lookup table (#4108) by @Colleague Riley -[rcore][RGFW] REVIEWED: Fix MSVC build errors (#4441) by @Colleague Riley -[rlgl] ADDED: More uniform data type options #4137 by @Ray +[rcore][RGFW] ADDED: RGFW, new rcore backend platform (#3941) by @colleagueRiley +[rcore][RGFW] REVIEWED: RGFW 1.0 (#4144) by @colleagueRiley +[rcore][RGFW] REVIEWED: Fix errors when compiling with mingw (#4282) by @colleagueRiley +[rcore][RGFW] REVIEWED: Replace long switch with a lookup table (#4108) by @colleagueRiley +[rcore][RGFW] REVIEWED: Fix MSVC build errors (#4441) by @colleagueRiley +[rlgl] ADDED: More uniform data type options #4137 by @raysan5 [rlgl] ADDED: Vertex normals for RLGL immediate drawing mode (#3866) by @bohonghuang -WARNING- [rlgl] ADDED: `rlCullDistance*()` variables and getters (#3912) by @KotzaBoss [rlgl] ADDED: `rlSetClipPlanes()` function (#3912) by @KotzaBoss [rlgl] ADDED: `isGpuReady` flag, allow font loading with no GPU acceleration by @Ray -WARNING- -[rlgl] REVIEWED: Changed RLGL_VERSION from 4.5 to 5.0 (#3914) by @Mute +[rlgl] REVIEWED: Changed RLGL_VERSION from 4.5 to 5.0 (#3914) by @rsteinke1111 [rlgl] REVIEWED: Shader load failing returns 0, instead of fallback by @Ray -WARNING- [rlgl] REVIEWED: Standalone mode default flags (#4334) by @Asdqwe -[rlgl] REVIEWED: Fix hardcoded index values in vboID array (#4312) by @Jett +[rlgl] REVIEWED: Fix hardcoded index values in vboID array (#4312) by @JettMonstersGoBoom [rlgl] REVIEWED: GLint64 did not exist before OpenGL 3.2 (#4284) by @Tchan0 [rlgl] REVIEWED: Extra warnings in case OpenGL 4.3 is not enabled (#4202) by @Maxim Knyazkin [rlgl] REVIEWED: Using GLint64 for glGetBufferParameteri64v() (#4197) by @Randy Palamar [rlgl] REVIEWED: Replace `glGetInteger64v()` with `glGetBufferParameteri64v()` (#4154) by @Kai Kitagawa-Jones [rlgl] REVIEWED: `rlMultMatrixf()`, fix matrix multiplication order (#3935) by @bohonghuang -[rlgl] REVIEWED: `rlSetVertexAttribute()`, define last parameter as offset #3800 by @Ray -[rlgl] REVIEWED: `rlDisableVertexAttribute()`, remove redundat calls for SHADER_LOC_VERTEX_COLOR (#3871) by @Kacper Zybała -[rlgl] REVIEWED: `rlLoadTextureCubemap()`, load mipmaps for cubemaps (#4429) by @Nikolas -[rlgl] REVIEWED: `rlLoadFramebuffer()`, parameters not required by @Ray +[rlgl] REVIEWED: `rlSetVertexAttribute()`, define last parameter as offset #3800 by @raysan5 +[rlgl] REVIEWED: `rlDisableVertexAttribute()`, remove redundat calls for SHADER_LOC_VERTEX_COLOR (#3871) by @zyperpl +[rlgl] REVIEWED: `rlLoadTextureCubemap()`, load mipmaps for cubemaps (#4429) by @Not-Nik +[rlgl] REVIEWED: `rlLoadFramebuffer()`, parameters not required by @raysan5 [rlgl] REVIEWED: `rlSetUniformSampler()` (#3759) by @veins1 [rlgl] REVIEWED: Renamed near/far variables (#4039) by @jgabaut [rlgl] REVIEWED: Expose OpenGL symbols (#3588) by @Peter0x44 -[rlgl] REVIEWED: Fix OpenGL 1.1 build issues (#3876) by @Ray +[rlgl] REVIEWED: Fix OpenGL 1.1 build issues (#3876) by @raysan5 [rlgl] REVIEWED: Fixed compilation for OpenGL ES (#4243) by @Maxim Knyazkin -[rlgl] REVIEWED: rlgl function description and comments by @Ray +[rlgl] REVIEWED: rlgl function description and comments by @raysan5 [rlgl] REVIEWED: Expose glad functions when building raylib as a shared lib (#3572) by @Peter0x44 -[rlgl] REVIEWED: Fix version info in rlgl.h (#3558) by @Steven Schveighoffer -[rlgl] REVIEWED: Use the vertex color to the base shader in GLSL330 (#4431) by @Jeffery Myers +[rlgl] REVIEWED: Fix version info in rlgl.h (#3558) by @schveiguy +[rlgl] REVIEWED: Use the vertex color to the base shader in GLSL330 (#4431) by @Jeffm2501 [rcamera] REVIEWED: Make camera movement independant of framerate (#4247) by @hanaxars -WARNING- [rcamera] REVIEWED: Updated camera speeds with GetFrameTime() (#4362) by @Anthony Carbajal [rcamera] REVIEWED: `UpdateCamera()`, added CAMERA_CUSTOM check (#3938) by @Tomas Fabrizio Orsi [rcamera] REVIEWED: Support mouse/keyboard and gamepad coexistence for input (#3579) by @ubkp -[rcamera] REVIEWED: Cleaned away unused macros(#3762) by @Brian E +[rcamera] REVIEWED: Cleaned away unused macros(#3762) by @Brian-ED [rcamera] REVIEWED: Fix for free camera mode (#3603) by @lesleyrs [rcamera] REVIEWED: `GetCameraRight()` (#3784) by @Danil [raymath] ADDED: C++ operator overloads for common math function (#4385) by @Jeffery Myers -WARNING- [raymath] ADDED: Vector4 math functions and Vector2 variants of some Vector3 functions (#3828) by @Bowserinator -[raymath] REVIEWED: Fix MSVC warnings/errors in C++ (#4125) by @Jeffery Myers -[raymath] REVIEWED: Add extern "C" to raymath header for C++ (#3978) by @Jeffery Myers +[raymath] REVIEWED: Fix MSVC warnings/errors in C++ (#4125) by @Jeffm2501 +[raymath] REVIEWED: Add extern "C" to raymath header for C++ (#3978) by @Jeffm2501 [raymath] REVIEWED: `QuaternionFromAxisAngle()`, remove redundant axis length calculation (#3900) by @jtainer [raymath] REVIEWED: `Vector3Perpendicular()`, avoid implicit conversion from float to double (#3799) by @João Foscarini [raymath] REVIEWED: `MatrixDecompose()`, incorrect output for certain scale and rotations (#4461) by @waveydave [raymath] REVIEWED: Small code refactor (#3753) by @Idir Carlos Aliane [rshapes] ADDED: `CheckCollisionCircleLine()` (#4018) by @kai-z99 [rshapes] REVIEWED: Multisegment Bezier splines (#3744) by @Santiago Pelufo -[rshapes] REVIEWED: Expose shapes drawing texture and rectangle (#3677) by @Jeffery Myers -[rshapes] REVIEWED: `DrawLine()` #4075 by @Ray -[rshapes] REVIEWED: `DrawPixel()` drawing by @Ray -[rshapes] REVIEWED: `DrawLine()` to avoid pixel rounding issues #3931 by @Ray -[rshapes] REVIEWED: `DrawRectangleLines()`, considering view matrix for lines "alignment" by @Ray +[rshapes] REVIEWED: Expose shapes drawing texture and rectangle (#3677) by @Jeffm2501 +[rshapes] REVIEWED: `DrawLine()` #4075 by @raysan5 +[rshapes] REVIEWED: `DrawPixel()` drawing by @raysan5 +[rshapes] REVIEWED: `DrawLine()` to avoid pixel rounding issues #3931 by @raysan5 +[rshapes] REVIEWED: `DrawRectangleLines()`, considering view matrix for lines "alignment" by @raysan5 [rshapes] REVIEWED: `DrawRectangleLines()`, pixel offset (#4261) by @RadsammyT -[rshapes] REVIEWED: `DrawRectangleLines()`, pixel offset when scaling (#3884) by @Ray +[rshapes] REVIEWED: `DrawRectangleLines()`, pixel offset when scaling (#3884) by @raysan5 [rshapes] REVIEWED: `DrawRectangleLinesEx()`, make sure accounts for square tiles (#4382) by @Jojaby [rshapes] REVIEWED: `Draw*Gradient()` color parameter names (#4270) by @Paperdomo101 -[rshapes] REVIEWED: `DrawGrid()`, remove duplicate color calls (#4148) by @Jeffery Myers -[rshapes] REVIEWED: `DrawSplineLinear()` to `SUPPORT_SPLINE_MITERS` by @Ray +[rshapes] REVIEWED: `DrawGrid()`, remove duplicate color calls (#4148) by @Jeffm2501 +[rshapes] REVIEWED: `DrawSplineLinear()` to `SUPPORT_SPLINE_MITERS` by @raysan5 [rshapes] REVIEWED: `DrawSplineLinear()`, implement miters (#3585) by @Toctave -[rshapes] REVIEWED: `CheckCollisionPointRec()` by @Ray +[rshapes] REVIEWED: `CheckCollisionPointRec()` by @raysan5 [rshapes] REVIEWED: `CheckCollisionPointCircle()`, new implementation (#4135) by @kai-z99 [rshapes] REVIEWED: `CheckCollisionCircles()`, optimized (#4065) by @kai-z99 [rshapes] REVIEWED: `CheckCollisionPointPoly()` (#3750) by @Antonio Raúl [rshapes] REVIEWED: `CheckCollisionCircleRec()` (#3584) by @ubkp -[rshapes] REVIEWED: Add more detail to function comment (#4344) by @Jeffery Myers -[rshapes] REVIEWED: Functions that draw point arrays take them as const (#4051) by @Jeffery Myers -[rtextures] ADDED: `ColorIsEqual()` by @Ray +[rshapes] REVIEWED: Add more detail to function comment (#4344) by @Jeffm2501 +[rshapes] REVIEWED: Functions that draw point arrays take them as const (#4051) by @Jeffm2501 +[rtextures] ADDED: `ColorIsEqual()` by @raysan5 [rtextures] ADDED: `ColorLerp()`, to mix 2 colors together (#4310) by @SusgUY446 [rtextures] ADDED: `LoadImageAnimFromMemory()` (#3681) by @IoIxD [rtextures] ADDED: `ImageKernelConvolution()` (#3528) by @Karim -[rtextures] ADDED: `ImageFromChannel()` (#4105) by @Bruno Cabral -[rtextures] ADDED: `ImageDrawLineEx()` (#4097) by @Le Juez Victor -[rtextures] ADDED: `ImageDrawTriangle()` (#4094) by @Le Juez Victor +[rtextures] ADDED: `ImageFromChannel()` (#4105) by @brccabral +[rtextures] ADDED: `ImageDrawLineEx()` (#4097) by @Bigfoot71 +[rtextures] ADDED: `ImageDrawTriangle()` (#4094) by @Bigfoot71 [rtextures] REMOVED: SVG files loading and drawing, moving it to raylib-extras by @Ray -WARNING- [rtextures] REVIEWED: `LoadImage()`, added support for 3-channel QOI images (#4384) by @R-YaTian -[rtextures] REVIEWED: `LoadImageRaw()` #3926 by @Ray -[rtextures] REVIEWED: `LoadImageColors()`, advance k in loop (#4120) by @Bruno Cabral -[rtextures] REVIEWED: `LoadTextureCubemap()`, added `mipmaps` #3665 by @Ray +[rtextures] REVIEWED: `LoadImageRaw()` #3926 by @raysan5 +[rtextures] REVIEWED: `LoadImageColors()`, advance k in loop (#4120) by @brccabral +[rtextures] REVIEWED: `LoadTextureCubemap()`, added `mipmaps` #3665 by @raysan5 [rtextures] REVIEWED: `LoadTextureCubemap()`, assign format to cubemap (#3823) by @Gary M -[rtextures] REVIEWED: `LoadTextureCubemap()`, load mipmaps for cubemaps (#4429) by @Nikolas -[rtextures] REVIEWED: `LoadTextureCubemap()`, avoid dangling re-allocated pointers (#4439) by @Nikolas +[rtextures] REVIEWED: `LoadTextureCubemap()`, load mipmaps for cubemaps (#4429) by @Not-Nik +[rtextures] REVIEWED: `LoadTextureCubemap()`, avoid dangling re-allocated pointers (#4439) by @Not-Nik [rtextures] REVIEWED: `LoadImageFromScreen()`, fix scaling (#3881) by @proberge-dev [rtextures] REVIEWED: `LoadImageFromMemory()`, warnings on invalid image data (#4179) by @Jutastre -[rtextures] REVIEWED: `LoadImageAnimFromMemory()`, added security checks (#3924) by @Ray -[rtextures] REVIEWED: `ImageColorTint()` and `ColorTint()`, optimized (#4015) by @Le Juez Victor -[rtextures] REVIEWED: `ImageKernelConvolution()`, formating and warnings by @Ray +[rtextures] REVIEWED: `LoadImageAnimFromMemory()`, added security checks (#3924) by @raysan5 +[rtextures] REVIEWED: `ImageColorTint()` and `ColorTint()`, optimized (#4015) by @Bigfoot71 +[rtextures] REVIEWED: `ImageKernelConvolution()`, formating and warnings by @raysan5 [rtextures] REVIEWED: `ImageDrawRectangleRec`, fix bounds check (#3732) by @Blockguy24 [rtextures] REVIEWED: `ImageResizeCanvas()`, implemented fill color (#3720) by @Lieven Petersen -[rtextures] REVIEWED: `ImageDrawRectangleRec()` (#3721) by @Le Juez Victor -[rtextures] REVIEWED: `ImageDraw()`, don't try to blend images without alpha (#4395) by @Nikolas +[rtextures] REVIEWED: `ImageDrawRectangleRec()` (#3721) by @Bigfoot71 +[rtextures] REVIEWED: `ImageDraw()`, don't try to blend images without alpha (#4395) by @Not-Nik [rtextures] REVIEWED: `GenImagePerlinNoise()` being stretched (#4276) by @Bugsia [rtextures] REVIEWED: `GenImageGradientLinear()`, fix some angles (#4462) by @decromo -[rtextures] REVIEWED: `DrawTexturePro()` to avoid negative dest rec #4316 by @Ray +[rtextures] REVIEWED: `DrawTexturePro()` to avoid negative dest rec #4316 by @raysan5 [rtextures] REVIEWED: `ColorToInt()`, fix undefined behaviour (#3996) by @OetkenPurveyorOfCode -[rtextures] REVIEWED: Remove panorama cubemap layout option (#4425) by @Jeffery Myers -[rtextures] REVIEWED: Removed unneeded module check, `rtextures` should not depend on `rtext` by @Ray +[rtextures] REVIEWED: Remove panorama cubemap layout option (#4425) by @Jeffm2501 +[rtextures] REVIEWED: Removed unneeded module check, `rtextures` should not depend on `rtext` by @raysan5 [rtextures] REVIEWED: Simplified for loop for some image manipulation functions (#3712) by @Alice Nyaa [rtext] ADDED: BDF fonts support (#3735) by @Stanley Fuller -WARNING- [rtext] ADDED: `TextToCamel()` (#4033) by @IoIxD [rtext] ADDED: `TextToSnake()` (#4033) by @IoIxD [rtext] ADDED: `TextToFloat()` (#3627) by @Benjamin Schmid Ties [rtext] REDESIGNED: `SetTextLineSpacing()` by @Ray -WARNING- -[rtext] REVIEWED: `LoadFontDataBDF()` name and formating by @Ray -[rtext] REVIEWED: `LoadFontDefault()`, initialize glyphs and recs to zero #4319 by @Ray +[rtext] REVIEWED: `LoadFontDataBDF()` name and formating by @raysan5 +[rtext] REVIEWED: `LoadFontDefault()`, initialize glyphs and recs to zero #4319 by @raysan5 [rtext] REVIEWED: `LoadFontEx()`, avoid default font fallback (#4077) by @Peter0x44 -WARNING- [rtext] REVIEWED: `LoadBMFont()`, extended functionality (#3536) by @Dongkun Lee -[rtext] REVIEWED: `LoadBMFont()`, issue on not glyph data initialized by @Ray +[rtext] REVIEWED: `LoadBMFont()`, issue on not glyph data initialized by @raysan5 [rtext] REVIEWED: `LoadFontFromMemory()`, use strncpy() to fix buffer overflow (#3795) by @Mingjie Shen [rtext] REVIEWED: `LoadCodepoints()` returning a freed ptr when count is 0 (#4089) by @Alice Nyaa [rtext] REVIEWED: `LoadFontData()` avoid fallback glyphs by @Ray -WARNING- -[rtext] REVIEWED: `LoadFontData()`, load image only if glyph has been found in font by @Ray +[rtext] REVIEWED: `LoadFontData()`, load image only if glyph has been found in font by @raysan5 [rtext] REVIEWED: `ExportFontAsCode()`, fix C++ compiler errors (#4013) by @DarkAssassin23 [rtext] REVIEWED: `MeasureTextEx()` height calculation (#3770) by @Marrony Neris [rtext] REVIEWED: `MeasureTextEx()`, additional check for empty input string (#4448) by @mpv-enjoyer -[rtext] REVIEWED: `CodepointToUTF8()`, clean static buffer #4379 by @Ray -[rtext] REVIEWED: `TextToFloat()`, always multiply by sign (#4273) by @listeria +[rtext] REVIEWED: `CodepointToUTF8()`, clean static buffer #4379 by @raysan5 +[rtext] REVIEWED: `TextToFloat()`, always multiply by sign (#4273) by @ListeriaM [rtext] REVIEWED: `TextReplace()` const correctness (#3678) by @maverikou [rtext] REVIEWED: `TextToFloat()`, coding style (#3627) by @Benjamin Schmid Ties [rtext] REVIEWED: Some comments to align to style (#3756) by @Idir Carlos Aliane @@ -254,23 +254,23 @@ Detailed changes: [rmodels] ADDED: Support 16-bit unsigned short vec4 format for gltf joint loading (#3821) by @Gary M [rmodels] ADDED: Support animation names for the m3d model format (#3714) by @kolunmi [rmodels] ADDED: `DrawModelPoints()`, more performant point cloud rendering (#4203) by @Reese Gallagher -[rmodels] ADDED: `ExportMeshAsCode()` by @Ray +[rmodels] ADDED: `ExportMeshAsCode()` by @raysan5 [rmodels] REVIEWED: Multiple updates to gltf loading, improved macro (#4373) by @Harald Scheirich -[rmodels] REVIEWED: `LoadOBJ()`, correctly split obj meshes by material (#4285) by @Jeffery Myers -[rmodels] REVIEWED: `LoadOBJ()`, add warning when loading an OBJ with multiple materials (#4271) by @Jeffery Myers -[rmodels] REVIEWED: `LoadOBJ()`, fix bug that fragmented the loaded meshes (#4494) by @Eike Decker +[rmodels] REVIEWED: `LoadOBJ()`, correctly split obj meshes by material (#4285) by @Jeffm2501 +[rmodels] REVIEWED: `LoadOBJ()`, add warning when loading an OBJ with multiple materials (#4271) by @Jeffm2501 +[rmodels] REVIEWED: `LoadOBJ()`, fix bug that fragmented the loaded meshes (#4494) by @zet23t [rmodels] REVIEWED: `LoadIQM()`, set model.meshMaterial[] (#4092) by @SuperUserNameMan -[rmodels] REVIEWED: `LoadIQM()`, attempt to load texture from IQM at loadtime (#4029) by @Jett -[rmodels] REVIEWED: `LoadM3D(), fix vertex colors for m3d files (#3859) by @Jeffery Myers +[rmodels] REVIEWED: `LoadIQM()`, attempt to load texture from IQM at loadtime (#4029) by @JettMonstersGoBoom +[rmodels] REVIEWED: `LoadM3D(), fix vertex colors for m3d files (#3859) by @Jeffm2501 [rmodels] REVIEWED: `LoadGLTF()`, supporting additional vertex data formats (#3546) by @MrScautHD [rmodels] REVIEWED: `LoadGLTF()`, correctly handle the node hierarchy in a glTF file (#4037) by @Paul Melis [rmodels] REVIEWED: `LoadGLTF()`, replaced SQUAD quat interpolation with cubic hermite (gltf 2.0 specs) (#3920) by @Benji -[rmodels] REVIEWED: `LoadGLTF()`, support 2nd texture coordinates loading by @Ray -[rmodels] REVIEWED: `LoadGLTF()`, support additional vertex attributes data formats #3890 by @Ray +[rmodels] REVIEWED: `LoadGLTF()`, support 2nd texture coordinates loading by @raysan5 +[rmodels] REVIEWED: `LoadGLTF()`, support additional vertex attributes data formats #3890 by @raysan5 [rmodels] REVIEWED: `LoadGLTF()`, set cgltf callbacks to use `LoadFileData()` and `UnloadFileData()` (#3652) by @kolunmi -[rmodels] REVIEWED: `LoadGLTF()`, JOINTS loading #3836 by @Ray +[rmodels] REVIEWED: `LoadGLTF()`, JOINTS loading #3836 by @raysan5 [rmodels] REVIEWED: `LoadImageFromCgltfImage()`, fix base64 padding support (#4112) by @SuperUserNameMan -[rmodels] REVIEWED: `LoadModelAnimationsIQM()`, fix corrupted animation names (#4026) by @Jett +[rmodels] REVIEWED: `LoadModelAnimationsIQM()`, fix corrupted animation names (#4026) by @JettMonstersGoBoom [rmodels] REVIEWED: `LoadModelAnimationsGLTF()`, load animations with 1 frame (#3804) by @Nikita Blizniuk [rmodels] REVIEWED: `LoadModelAnimationsGLTF()`, added missing interpolation types (#3919) by @Benji [rmodels] REVIEWED: `LoadModelAnimationsGLTF()` (#4107) by @VitoTringolo @@ -283,19 +283,19 @@ Detailed changes: [rmodels] REVIEWED: `DrawCylinder()`, fix drawing of cap (#4478) by @JeffM2501 [rmodels] REVIEWED: `DrawMesh()`, send full matModel to shader in DrawMesh (#4005) (#4022) by @David Holland [rmodels] REVIEWED: `DrawMesh()`, fix material specular map retrieval (#3758) by @Victor Gallet -[rmodels] REVIEWED: `DrawModelEx()`, simplified multiplication of colors (#4002) by @Le Juez Victor +[rmodels] REVIEWED: `DrawModelEx()`, simplified multiplication of colors (#4002) by @Bigfoot71 [rmodels] REVIEWED: `DrawBillboardPro()`, to be consistend with `DrawTexturePro()` (#4132) by @bohonghuang [rmodels] REVIEWED: `DrawSphereEx()` optimization (#4106) by @smalltimewizard [raudio] REVIEWED: `LoadMusicStreamFromMemory()`, support 24-bit FLACs (#4279) by @konstruktor227 -[raudio] REVIEWED: `LoadWaveSamples()`, fix mapping of wave data (#4062) by @listeria +[raudio] REVIEWED: `LoadWaveSamples()`, fix mapping of wave data (#4062) by @ListeriaM [raudio] REVIEWED: `LoadMusicStream()`, remove drwav_uninit() (#3986) by @FishingHacks [raudio] REVIEWED: `LoadMusicStream()` qoa and wav loading (#3966) by @veins1 [raudio] REVIEWED: `ExportWaveAsCode()`, segfault (#3769) by @IoIxD -[raudio] REVIEWED: `WaveCrop()`, fix issues and use frames instead of samples (#3994) by @listeria +[raudio] REVIEWED: `WaveCrop()`, fix issues and use frames instead of samples (#3994) by @ListeriaM [raudio] REVIEWED: Crash from multithreading issues (#3907) by @Christian Haas [raudio] REVIEWED: Reset music.ctxType if loading wasn't succesful (#3917) by @veins1 [raudio] REVIEWED: Added missing functions in "standalone" mode (#3760) by @Alessandro Nikolaev -[raudio] REVIEWED: Disable unused miniaudio features (#3544) by @Alexandre Almeida +[raudio] REVIEWED: Disable unused miniaudio features (#3544) by @M374LX [raudio] REVIEWED: Fix crash when switching playback device at runtime (#4102) by @jkaup [raudio] REVIEWED: Support 24 bits samples for FLAC format (#4058) by @Alexey Kutepov [examples] ADDED: `core_random_sequence` (#3846) by @Dalton Overmyer @@ -304,36 +304,36 @@ Detailed changes: [examples] ADDED: `models_bone_socket` (#3833) by @iP [examples] ADDED: `shaders_vertex_displacement` (#4186) by @Alex ZH [examples] ADDED: `shaders_shadowmap` (#3653) by @TheManTheMythTheGameDev -[examples] REVIEWED: `core_2d_camera_platformer` by @Ray -[examples] REVIEWED: `core_2d_camera_mouse_zoom`, use logarithmic scaling for a 2d zoom functionality (#3977) by @Mike Will +[examples] REVIEWED: `core_2d_camera_platformer` by @raysan5 +[examples] REVIEWED: `core_2d_camera_mouse_zoom`, use logarithmic scaling for a 2d zoom functionality (#3977) by @myQwil [examples] REVIEWED: `core_input_gamepad_info`, all buttons displayed within the window (#4241) by @Asdqwe [examples] REVIEWED: `core_input_gamepad_info`, show ps3 controller (#4040) by @Konrad Gutvik Grande [examples] REVIEWED: `core_input_gamepad`, add drawing for generic gamepad (#4424) by @Asdqwe [examples] REVIEWED: `core_input_gamepad`, add deadzone handling (#4422) by @Asdqwe [examples] REVIEWED: `shapes_bouncing_ball` (#4226) by @Anthony Carbajal [examples] REVIEWED: `shapes_following_eyes` (#3710) by @Hongyu Ouyang -[examples] REVIEWED: `shapes_draw_rectangle_rounded` by @Ray +[examples] REVIEWED: `shapes_draw_rectangle_rounded` by @raysan5 [examples] REVIEWED: `shapes_draw_ring`, fix other examples (#4211) by @kai-z99 -[examples] REVIEWED: `shapes_lines_bezier` by @Ray -[examples] REVIEWED: `textures_image_kernel` #3556 by @Ray +[examples] REVIEWED: `shapes_lines_bezier` by @raysan5 +[examples] REVIEWED: `textures_image_kernel` #3556 by @raysan5 [examples] REVIEWED: `text_input_box` (#4229) by @Anthony Carbajal [examples] REVIEWED: `text_writing_anim` (#4230) by @Anthony Carbajal -[examples] REVIEWED: `models_billboard` by @Ray -[examples] REVIEWED: `models_cubicmap` by @Ray -[examples] REVIEWED: `models_point_rendering` by @Ray +[examples] REVIEWED: `models_billboard` by @raysan5 +[examples] REVIEWED: `models_cubicmap` by @raysan5 +[examples] REVIEWED: `models_point_rendering` by @raysan5 [examples] REVIEWED: `models_box_collisions` (#4224) by @Anthony Carbajal -[examples] REVIEWED: `models_skybox`, do not use HDR by default (#4115) by @Jeffery Myers +[examples] REVIEWED: `models_skybox`, do not use HDR by default (#4115) by @Jeffm2501 [examples] REVIEWED: `shaders_basic_pbr` (#4225) by @Anthony Carbajal -[examples] REVIEWED: `shaders_palette_switch` by @Ray +[examples] REVIEWED: `shaders_palette_switch` by @raysan5 [examples] REVIEWED: `shaders_hybrid_render` (#3908) by @Yousif -[examples] REVIEWED: `shaders_lighting_instancing`, fix vertex shader (#4056) by @Karl Zylinski +[examples] REVIEWED: `shaders_lighting_instancing`, fix vertex shader (#4056) by @karl [examples] REVIEWED: `shaders_raymarching`, add `raymarching.fs` for GLSL120 (#4183) by @CDM15y [examples] REVIEWED: `shaders_shadowmap`, fix shaders for GLSL 1.20 (#4167) by @CDM15y -[examples] REVIEWED: `shaders_deferred_render` (#3655) by @Jett -[examples] REVIEWED: `shaders_basic_pbr` (#3621) by @devdad +[examples] REVIEWED: `shaders_deferred_render` (#3655) by @JettMonstersGoBoom +[examples] REVIEWED: `shaders_basic_pbr` (#3621) by @afanolovcic [examples] REVIEWED: `shaders_basic_pbr`, remove dependencies (#3649) by @TheManTheMythTheGameDev -[examples] REVIEWED: `shaders_basic_pbr`, added more comments by @Ray -[examples] REVIEWED: `shaders_gpu_skinning`, to work with OpenGL ES 2.0 #4412 by @Ray +[examples] REVIEWED: `shaders_basic_pbr`, added more comments by @raysan5 +[examples] REVIEWED: `shaders_gpu_skinning`, to work with OpenGL ES 2.0 #4412 by @raysan5 [examples] REVIEWED: `shaders_model_shader`, use free camera (#4428) by @IcyLeave6109 [examples] REVIEWED: `audio_stream_effects` (#3618) by @lipx [examples] REVIEWED: `audio_raw_stream` (#3624) by @riadbettole @@ -342,21 +342,21 @@ Detailed changes: [examples] REVIEWED: Update examples missing UnloadTexture() calls (#4234) by @Anthony Carbajal [examples] REVIEWED: Added GLSL 100 and 120 shaders to lightmap example (#3543) by @Jussi Viitala [examples] REVIEWED: Set FPS to always 60 in all exampels (#4235) by @Anthony Carbajal -[build] REVIEWED: Makefile by @Ray -[build] REVIEWED: Makefile, fix wrong flag #3593 by @Ray +[build] REVIEWED: Makefile by @raysan5 +[build] REVIEWED: Makefile, fix wrong flag #3593 by @raysan5 [build] REVIEWED: Makefile, disable wayland by default (#4369) by @Anthony Carbajal [build] REVIEWED: Makefile, VSCode, fix to support multiple .c files (#4391) by @Alan Arrecis [build] REVIEWED: Makefile, fix -Wstringop-truncation warning (#4096) by @Peter0x44 -[build] REVIEWED: Makefile, fix issues for RGFW on Linux/macOS (#3969) by @Colleague Riley +[build] REVIEWED: Makefile, fix issues for RGFW on Linux/macOS (#3969) by @colleagueRiley [build] REVIEWED: Makefile, update RAYLIB_VERSION (#3901) by @Belllg [build] REVIEWED: Makefile, use mingw32-make for Windows (#4436) by @Asdqwe [build] REVIEWED: Makefile, move CUSTOM_CFLAGS for better visibility (#4054) by @Lázaro Albuquerque -[build] REVIEWED: Makefile, update emsdk paths to latest versions by @Ray +[build] REVIEWED: Makefile, update emsdk paths to latest versions by @raysan5 [build] REVIEWED: Makefile examples, align /usr/local with /src Makefile (#4286) by @Tchan0 [build] REVIEWED: Makefile examples, added `textures_image_kernel` (#3555) by @Sergey Zapunidi [build] REVIEWED: Makefile examples (#4209) by @Anthony Carbajal [build] REVIEWED: Makefile examples, to work on NetBSD (#4438) by @NishiOwO -[build] REVIEWED: Makefile examples, WebGL2 (OpenGL ES 3.0) backend flags #4330 by @Ray +[build] REVIEWED: Makefile examples, WebGL2 (OpenGL ES 3.0) backend flags #4330 by @raysan5 [build] REVIEWED: Makefile examples, web building (#4434) by @Asdqwe [build] REVIEWED: build.zig, fix various issues around `-Dconfig` (#4398) by @Sage Hane [build] REVIEWED: build.zig, fix type mismatch (#4383) by @yuval_dev @@ -365,15 +365,15 @@ Detailed changes: [build] REVIEWED: build.zig, improve logic (#4375) by @Sage Hane [build] REVIEWED: build.zig, issues (#4374) by @William Culver [build] REVIEWED: build.zig, issues (#4366) by @Visen -[build] REVIEWED: build.zig, support desktop backend change (#4358) by @Nikolas +[build] REVIEWED: build.zig, support desktop backend change (#4358) by @Not-Nik [build] REVIEWED: build.zig, use zig fmt (#4242) by @freakmangd [build] REVIEWED: build.zig, check if wayland-scanner is installed (#4217) by @lnc3l0t [build] REVIEWED: build.zig, override config.h definitions (#4193) by @lnc3l0t [build] REVIEWED: build.zig, support GLFW platform detection (#4150) by @InventorXtreme -[build] REVIEWED: build.zig, make emscripten build compatible with Zig 0.13.0 (#4121) by @Mike Will +[build] REVIEWED: build.zig, make emscripten build compatible with Zig 0.13.0 (#4121) by @myQwil [build] REVIEWED: build.zig, pass the real build.zig file (#4113) by @InKryption [build] REVIEWED: build.zig, leverage `dependencyFromBuildZig` (#4109) by @InKryption -[build] REVIEWED: build.zig, run examples from their directories (#4063) by @Mike Will +[build] REVIEWED: build.zig, run examples from their directories (#4063) by @myQwil [build] REVIEWED: build.zig, fix raygui build when using addRaygui externally (#4027) by @Viktor Pocedulić [build] REVIEWED: build.zig, fix emscripten build (#4012) by @Dylan [build] REVIEWED: build.zig, update to zig 0.12.0dev while keeping 0.11.0 compatibility (#3715) by @freakmangd @@ -389,7 +389,7 @@ Detailed changes: [build] REVIEWED: build.zig, improve cross-compilation (#4468) by @deathbeam [build] REVIEWED: CMake, update to raylib 5.0 (#3623) by @Peter0x44 [build] REVIEWED: CMake, added PLATFORM option for Desktop SDL (#3809) by @mooff -[build] REVIEWED: CMake, fix GRAPHICS_* check (#4359) by @Kacper Zybała +[build] REVIEWED: CMake, fix GRAPHICS_* check (#4359) by @zyperpl [build] REVIEWED: CMake, examples projects (#4332) by @Ridge3Dproductions [build] REVIEWED: CMake, fix warnings in projects/CMake/CMakeLists.txt (#4278) by @Peter0x44 [build] REVIEWED: CMake, delete BuildOptions.cmake (#4277) by @Peter0x44 @@ -398,54 +398,54 @@ Detailed changes: [build] REVIEWED: CMake, support OpenGL ES3 in `LibraryConfigurations.cmake` (#4079) by @manuel5975p [build] REVIEWED: CMake, `config.h` fully available to users (#4044) by @Lázaro Albuquerque [build] REVIEWED: CMake, pass -sFULL_ES3 instead of -sFULL_ES3=1 (#4090) by @manuel5975p -[build] REVIEWED: CMake, SDL build link the glfw dependency (#3860) by @Rob Loach +[build] REVIEWED: CMake, SDL build link the glfw dependency (#3860) by @RobLoach [build] REVIEWED: CMake, infer CMAKE_MODULE_PATH in super-build (#4042) by @fruzitent -[build] REVIEWED: CMake, remove USE_WAYLAND option (#3851) by @Alexandre Almeida -[build] REVIEWED: CMake, disable SDL rlgl_standalone example (#3861) by @Rob Loach -[build] REVIEWED: CMake, bump version required to avoid deprecated #3639 by @Ray +[build] REVIEWED: CMake, remove USE_WAYLAND option (#3851) by @M374LX +[build] REVIEWED: CMake, disable SDL rlgl_standalone example (#3861) by @RobLoach +[build] REVIEWED: CMake, bump version required to avoid deprecated #3639 by @raysan5 [build] REVIEWED: CMake, fix examples linking -DPLATFORM=SDL (#3825) by @Peter0x44 [build] REVIEWED: CMake, don't build for wayland by default (#4432) by @Peter0x44 -[build] REVIEWED: VS2022, misc improvements by @Ray -[build] REVIEWED: VS2022, fix build warnings (#4095) by @Jeffery Myers -[build] REVIEWED: VS2022, added new examples (#4492) by @Jeffery Myers +[build] REVIEWED: VS2022, misc improvements by @raysan5 +[build] REVIEWED: VS2022, fix build warnings (#4095) by @Jeffm2501 +[build] REVIEWED: VS2022, added new examples (#4492) by @Jeffm2501 [build] REVIEWED: Fix fix-build-paths (#3849) by @Caleb Barger [build] REVIEWED: Fix build paths (#3835) by @Steve Biedermann [build] REVIEWED: Fix VSCode sample project for macOS (#3666) by @Tim Romero [build] REVIEWED: Fix some warnings on web builds and remove some redundant flags (#4069) by @Lázaro Albuquerque [build] REVIEWED: Fix examples not building with gestures system disabled (#4020) by @Sprix -[build] REVIEWED: Fix GLFW runtime platform detection (#3863) by @Alexandre Almeida +[build] REVIEWED: Fix GLFW runtime platform detection (#3863) by @M374LX [build] REVIEWED: Fix DRM cross-compile without sysroot (#3839) by @Christian W. Zuckschwerdt [build] REVIEWED: Fix cmake-built libraylib.a to properly include GLFW's object files (#3598) by @Peter0x44 [build] REVIEWED: Hide unneeded internal symbols when building raylib as an so or dylib (#3573) by @Peter0x44 [build] REVIEWED: Corrected the path of android ndk toolchains for OSX platforms (#3574) by @Emmanuel Méra [build][CI] ADDED: Automatic update for raylib_api.* (#3692) by @seiren -[build][CI] REVIEWED: Update workflows to use latest actions/upload-artifact by @Ray -[build][CI] REVIEWED: CodeQL minor tweaks to avoid some warnings by @Ray -[build][CI] REVIEWED: Update linux_examples.yml by @Ray -[build][CI] REVIEWED: Update linux.yml by @Ray -[build][CI] REVIEWED: Update webassembly.yml by @Ray -[build][CI] REVIEWED: Update cmake.yml by @Ray -[build][CI] REVIEWED: Update codeql.yml, exclude src/external files by @Ray -[bindings] ADDED: raylib-APL (#4253) by @Brian E -[bindings] ADDED: raylib-bqn, moved rayed-bqn (#4331) by @Brian E +[build][CI] REVIEWED: Update workflows to use latest actions/upload-artifact by @raysan5 +[build][CI] REVIEWED: CodeQL minor tweaks to avoid some warnings by @raysan5 +[build][CI] REVIEWED: Update linux_examples.yml by @raysan5 +[build][CI] REVIEWED: Update linux.yml by @raysan5 +[build][CI] REVIEWED: Update webassembly.yml by @raysan5 +[build][CI] REVIEWED: Update cmake.yml by @raysan5 +[build][CI] REVIEWED: Update codeql.yml, exclude src/external files by @raysan5 +[bindings] ADDED: raylib-APL (#4253) by @Brian-ED +[bindings] ADDED: raylib-bqn, moved rayed-bqn (#4331) by @Brian-ED [bindings] ADDED: brainfuck binding (#4169) by @_Tradam [bindings] ADDED: raylib-zig-bindings (#4004) by @Lionel Briand [bindings] ADDED: Raylib-CSharp wrapper (#3963) by @MrScautHD [bindings] ADDED: COBOL binding (#3661) by @glowiak [bindings] ADDED: raylib-beef binding (#3640) by @Braedon Lewis [bindings] ADDED: Raylib-CSharp-Vinculum (#3571) by @Danil -[bindings] REVIEWED: Remove broken-link bindings #3899 by @Ray -[bindings] REVIEWED: Updated some versions on BINDINGS.md by @Ray -[bindings] REVIEWED: Removed umaintained repos (#3999) by @Antonis Geralis +[bindings] REVIEWED: Remove broken-link bindings #3899 by @raysan5 +[bindings] REVIEWED: Updated some versions on BINDINGS.md by @raysan5 +[bindings] REVIEWED: Removed umaintained repos (#3999) by @planetis-m [bindings] REDESIGNED: Add binding link to name, instead of separate column (#3995) by @Carmine Pietroluongo [bindings] UPDATED: h-raylib (#4378) by @Anand Swaroop [bindings] UPDATED: Raylib.lean, to master version (#4337) by @Daniil Kisel [bindings] UPDATED: raybit, to latest master (#4311) by @Alex [bindings] UPDATED: dray binding (#4163) by @red thing [bindings] UPDATED: Julia (#4068) by @ShalokShalom -[bindings] UPDATED: nim to latest master (#3999) by @Antonis Geralis +[bindings] UPDATED: nim to latest master (#3999) by @planetis-m [bindings] UPDATED: raylib-rs (#3991) by @IoIxD -[bindings] UPDATED: raylib-zig version (#3902) by @Nikolas +[bindings] UPDATED: raylib-zig version (#3902) by @Not-Nik [bindings] UPDATED: raylib-odin (#3868) by @joyousblunder [bindings] UPDATED: Raylib VAPI (#3829) by @Alex Macafee [bindings] UPDATED: Raylib-cs (#3774) by @Brandon Baker @@ -455,41 +455,41 @@ Detailed changes: [bindings] UPDATED: ray-cyber to 5.0 (#3654) by @fubark [bindings] UPDATED: raylib-freebasic binding (#3591) by @WIITD [bindings] UPDATED: SmallBASIC (#3562) by @Chris Warren-Smith -[bindings] UPDATED: Python raylib-py v5.0.0beta1 (#3557) by @Jorge A. Gomes -[bindings] UPDATED: raylib-d binding (#3561) by @Steven Schveighoffer +[bindings] UPDATED: Python raylib-py v5.0.0beta1 (#3557) by @jorgegomes83 +[bindings] UPDATED: raylib-d binding (#3561) by @schveiguy [bindings] UPDATED: Janet (#3553) by @Dmitry Matveyev -[bindings] UPDATED: Raylib.nelua (#3552) by @Auz -[bindings] UPDATED: raylib-cpp to 5.0 (#3551) by @Rob Loach +[bindings] UPDATED: Raylib.nelua (#3552) by @guy.smith764 +[bindings] UPDATED: raylib-cpp to 5.0 (#3551) by @RobLoach [bindings] UPDATED: Pascal binding (#3548) by @Gunko Vadim -[external] UPDATED: stb_truetype.h to latest version by @Ray -[external] UPDATED: stb_image_resize2.h to latest version by @Ray -[external] UPDATED: stb_image.h to latest version by @Ray -[external] UPDATED: qoa.h to latest version by @Ray -[external] UPDATED: dr_wav.h to latest version by @Ray -[external] UPDATED: dr_mp3.h to latest version by @Ray -[external] UPDATED: cgltf.h to latest version by @Ray -[external] REVIEWED: rl_gputex, correctly load mipmaps from DDS files (#4399) by @Nikolas +[external] UPDATED: stb_truetype.h to latest version by @raysan5 +[external] UPDATED: stb_image_resize2.h to latest version by @raysan5 +[external] UPDATED: stb_image.h to latest version by @raysan5 +[external] UPDATED: qoa.h to latest version by @raysan5 +[external] UPDATED: dr_wav.h to latest version by @raysan5 +[external] UPDATED: dr_mp3.h to latest version by @raysan5 +[external] UPDATED: cgltf.h to latest version by @raysan5 +[external] REVIEWED: rl_gputex, correctly load mipmaps from DDS files (#4399) by @Not-Nik [external] REVIEWED: stb_image_resize2, dix vld1q_f16 undeclared in arm (#4309) by @masnm [external] REVIEWED: miniaudio, fix library and Makefile for NetBSD (#4212) by @NishiOwO [external] REVIEWED: raygui, update to latest version 4.5-dev (#4238) by @Anthony Carbajal -[external] REVIEWED: jar_xml, replace unicode characters by ascii characters to avoid warning in MSVC (#4196) by @Rico P +[external] REVIEWED: jar_xml, replace unicode characters by ascii characters to avoid warning in MSVC (#4196) by @RicoP [external] REVIEWED: vox_loader, normals and new voxels shader (#3843) by @johann nadalutti [parser] REVIEWED: README.md, to mirror fixed help text (#4336) by @Daniil Kisel [parser] REVIEWED: Fix seg fault with long comment lines (#4306) by @Chris Warren-Smith [parser] REVIEWED: Don't crash for files that don't end in newlines (#3981) by @Peter0x44 [parser] REVIEWED: Issues in usage example help text (#4084) by @Peter0x44 [parser] REVIEWED: Fix parsing of empty parentheses (#3974) by @Filyus -[parser] REVIEWED: Address parsing issue when generating XML #3893 by @Ray +[parser] REVIEWED: Address parsing issue when generating XML #3893 by @raysan5 [parser] REVIEWED: `MemoryCopy()`, prevent buffer overflow by replacing hard-coded arguments (#4011) by @avx0 -[misc] ADDED: Create logo/raylib.icns by @Ray -[misc] ADDED: Create logo/raylib_1024x1024.png by @Ray +[misc] ADDED: Create logo/raylib.icns by @raysan5 +[misc] ADDED: Create logo/raylib_1024x1024.png by @raysan5 [misc] ADDED: Default vertex/fragment shader for OpenGL ES 3.0 (#4178) by @Lázaro Albuquerque [misc] REVIEWED: README.md, fix Reddit badge (#4136) by @Ninad Sachania [misc] REVIEWED: .gitignore, ignore compiled dll binaries (#3628) by @2Bear [misc] REVIEWED: Fix undesired scrollbars on web shell files (#4104) by @jspast [misc] REVIEWED: Made comments on raylib.h match comments in rcamera.h (#3942) by @Tomas Fabrizio Orsi [misc] REVIEWED: Make raylib/raygui work better on touchscreen (#3728) by @Hongyu Ouyang -[misc] REVIEWED: Update config.h by @Ray +[misc] REVIEWED: Update config.h by @raysan5 ------------------------------------------------------------------------- Release: raylib 5.0 - 10th Anniversary Edition (18 November 2023) From 116191fd8049b43a23d098ed72368389c0069314 Mon Sep 17 00:00:00 2001 From: Krzysztof Szenk Date: Sun, 15 Mar 2026 08:48:55 +0100 Subject: [PATCH 161/319] LibraryConfigurations.cmake: exchanged MATCHES -> STREQUAL in platform choosing if-statements (#5654) * RGFW also requires RGBA8 images as window icons, as raylib already reports in raylib.h * LibraryConfigurations.cmake: exchanged MATCHES -> STREQUAL in platform choosing if-statements --- cmake/LibraryConfigurations.cmake | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index 2fe05827a..9e7efe792 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -9,7 +9,7 @@ endif() set(RAYLIB_DEPENDENCIES "include(CMakeFindDependencyMacro)") -if (${PLATFORM} MATCHES "Desktop") +if (${PLATFORM} STREQUAL "Desktop") set(PLATFORM_CPP "PLATFORM_DESKTOP") if (APPLE) @@ -67,14 +67,14 @@ if (${PLATFORM} MATCHES "Desktop") endif () endif () -elseif (${PLATFORM} MATCHES "Web") +elseif (${PLATFORM} STREQUAL "Web") set(PLATFORM_CPP "PLATFORM_WEB") if(NOT GRAPHICS) set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") endif() set(CMAKE_STATIC_LIBRARY_SUFFIX ".a") -elseif (${PLATFORM} MATCHES "Android") +elseif (${PLATFORM} STREQUAL "Android") set(PLATFORM_CPP "PLATFORM_ANDROID") set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") set(CMAKE_POSITION_INDEPENDENT_CODE ON) @@ -94,7 +94,7 @@ elseif (${PLATFORM} MATCHES "Android") set(LIBS_PRIVATE log android EGL GLESv2 OpenSLES atomic c) set(LIBS_PUBLIC m) -elseif ("${PLATFORM}" MATCHES "DRM") +elseif ("${PLATFORM}" STREQUAL "DRM") set(PLATFORM_CPP "PLATFORM_DRM") set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") @@ -113,7 +113,7 @@ elseif ("${PLATFORM}" MATCHES "DRM") set(LIBS_PRIVATE ${GLESV2} ${EGL} ${DRM} ${GBM} atomic pthread dl) set(LIBS_PUBLIC m) -elseif ("${PLATFORM}" MATCHES "SDL") +elseif ("${PLATFORM}" STREQUAL "SDL") # First, check if SDL is included as a subdirectory if(TARGET SDL3::SDL3) message(STATUS "Using SDL3 from subdirectory") @@ -147,7 +147,7 @@ elseif ("${PLATFORM}" MATCHES "SDL") add_compile_definitions(USING_SDL2_PACKAGE) endif() endif() -elseif ("${PLATFORM}" MATCHES "RGFW") +elseif ("${PLATFORM}" STREQUAL "RGFW") set(PLATFORM_CPP "PLATFORM_DESKTOP_RGFW") if (APPLE) @@ -168,7 +168,7 @@ elseif ("${PLATFORM}" MATCHES "RGFW") set(LIBS_PRIVATE ${X11_LIBRARIES} ${OPENGL_LIBRARIES}) endif () -elseif ("${PLATFORM}" MATCHES "WebRGFW") +elseif ("${PLATFORM}" STREQUAL "WebRGFW") set(PLATFORM_CPP "PLATFORM_WEB_RGFW") set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") set(CMAKE_STATIC_LIBRARY_SUFFIX ".a") From 8395374e3d34804fbe7da542edb1fdce43dbb3e8 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 15 Mar 2026 19:17:03 +0100 Subject: [PATCH 162/319] Update HISTORY.md --- HISTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 7f5539816..4fc5569d5 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -533,7 +533,7 @@ Some astonishing numbers for this release: - **+320** closed issues (for a TOTAL of **+2130**!) - **+1800** commits since previous RELEASE (for a TOTAL of **+9600**!) - **18** functions ADDED to raylib API (for a TOTAL of **599**!) - - **??** functions REVIEWED with fixes and improvements + - **+50** new examples to learn from (for a TOTAL of **212**!) - **+200** new contributors (for a TOTAL of **+840**!) Highlights for `raylib 6.0`: From fbec6b05938f39f5e00c4122b5700f66ff161eae Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 15 Mar 2026 19:17:47 +0100 Subject: [PATCH 163/319] ADDED: Most relevant changes for `raylib 6.0` --- CHANGELOG | 761 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 759 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 9bfdbe484..1536d4e4c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -17,10 +17,767 @@ KEY CHANGES: - New File System API - New Text Management API - New tool: [rexm] raylib examples manager + - New examples: +50 new examples and format unification Detailed changes: -TODO... +[rcore] ADDED: Logging and file-system functionality from `utils` (#4551) by @raysan5 -WARNING- +[rcore] ADDED: Flags set/clear macros: FLAG_SET, FLAG_CLEAR, FLAG_IS_SET (#5169) by @raysan5 +[rcore] ADDED: Warnings in case of no platform backend defined by @raysan5 +[rcore] ADDED: `ComputeSHA256()` (#5264) by @iamahuman1395 +[rcore] ADDED: `GetKeyName()` (#4544) by @lecongsebastien +[rcore] REMOVED: GIF recording option, added example by @raysan5 -WARNING- +[rcore] REMOVED: `CORE.Window.fullscreen` variable, using available flag instead by @raysan5 +[rcore] REMOVED: `SetupFramebuffer()`, most platforms do not need it any more by @raysan5 +[rcore] REMOVED: `TRACELOGD()` macro, hardly ever used by @raysan5 +[rcore] RENAMED: Shader location SHADER_LOC_VERTEX_INSTANCETRANSFORMS (#5592) by @CrackedPixel +[rcore] REVIEWED: Gamepads database on latest `SDL2 2.32.8` and `SDL3 3.3.6` #5403 by @raysan5 +[rcore] REVIEWED: Alt-Tab not working in borderless fullscreen (#3865, #4655) by @veins1 +[rcore] REVIEWED: Automation system, saving to memory buffer and binary file (#5476) by @belan2470 +[rcore] REVIEWED: Check if `shader.locs` is not NULL before try to access (#5622) by @maiconpintoabreu +[rcore] REVIEWED: Content scaling on macOS (#5186) by @mchcopl +[rcore] REVIEWED: `GetRandomValue()`, fix modulo bias (#5392) by @Marcos-D +[rcore] REVIEWED: Controller not available after window init (#5358) by @sergii +[rcore] REVIEWED: Cursor lock/unlock inconsistent behaviour (#5323) by @tacf +[rcore] REVIEWED: Description of `RestoreWindow()` by @johnnymarler +[rcore] REVIEWED: Touch position register on automation event handling (#5470) by @belan2470 +[rcore] REVIEWED: FLAG_IS_SET to check if all bits in the flag are set in the value (#5441) by @vushu +[rcore] REVIEWED: Fix for BI_ALPHABITFIELDS for clipbaord image narrow support (#5586) by @crisserpl2 +[rcore] REVIEWED: Flags checks, fixes #5597 by @raysan5 +[rcore] REVIEWED: Fullscreen request #5601 by @raysan5 +[rcore] REVIEWED: HighDPI content scaling on changing monitors with different DPI #5335 #5356 by @raysan5 +[rcore] REVIEWED: HighDPI content scaling on macOS by @raysan5 +[rcore] REVIEWED: Implement `FLAG_WINDOW_ALWAYS_RUN` on Android (#5414) by @caszuu +[rcore] REVIEWED: Init main framebuffer using render size (should be same as currentFbo) by @raysan5 +[rcore] REVIEWED: Platform code formatting and organization by @raysan5 +[rcore] REVIEWED: Replace #pragma message with TRACELOG inside the clipboard image function (#5596) by @maiconpintoabreu +[rcore] REVIEWED: Requested window flags application after window initialization by @raysan5 +[rcore] REVIEWED: Setting some flags disabled fullscreen (#4618, 4619) by @veins1 +[rcore] REVIEWED: Some flags for window/context creation attributes by @raysan5 +[rcore] REVIEWED: Support window flags with initialization issues (#4837) by @Andersama +[rcore] REVIEWED: Swap `TraceLog()` to `TRACELOG` macro (#5226) by @iamahuman1395 +[rcore] REVIEWED: Swap local variable `isCursorHidden` to use new method `IsCursorHidden()` by @maiconpintoabreu +[rcore] REVIEWED: Touch `pointCount` reduction (#4661) by @maiconpintoabreu +[rcore] REVIEWED: Unscale the window size on resize if we are doing automatic HighDPI scaling by @Jeffm2501 +[rcore] REVIEWED: Use `FLAG_*` macros where possible (#5169) by @iamahuman1395 +[rcore] REVIEWED: Wayland checks, using compilation flags when possible #5564 by @raysan5 +[rcore] REVIEWED: `InitWindow()`, issues due to unchecked result of `InitPlatform()` (#4803) by @sleeptightAnsiC +[rcore] REVIEWED: `InitWindow()`, fixed issue when using an empty window title (#5526) by @CrackedPixel +[rcore] REVIEWED: `GetRenderWidth()`, and `GetRenderHeight()`, return the FBO size if one is active by @Jeffm2501 +[rcore] REVIEWED: `GetWindowPosition()`, return internal value by @raysan5 +[rcore] REVIEWED: `ToggleBorderlessFullscreen()`, not hiding taskbar (#5383) by @xenomustache +[rcore] REVIEWED: `IsMouseButton*()`, random key codes return unexpected results (#5516) by @jasoncnm +[rcore] REVIEWED: `IsGamepadButton*()`, for consistency with key and mouse equivalents by @raysan5 +[rcore] REVIEWED: `SetGamepadVibration()`, TRACELOG message (#4615) by @Asdqwe +[rcore] REVIEWED: `MAX_GAMEPAD_AXES` for consistency #4960 by @raysan5 +[rcore] REVIEWED: `MAX_GAMEPAD_NAME_LENGTH` #4695 by @raysan5 +[rcore] REVIEWED: `LoadRandomSequence()`, using `GetRandomValue()` #5393 by @raysan5 +[rcore] REVIEWED: `LoadShaderFromMemory()`, use default locations for default shader #4641 by @raysan5 +[rcore] REVIEWED: `ComputeSHA1()`, computation on messages longer than 31 bytes (#5397) by @me +[rcore] REVIEWED: `ComputeSHA256()` by @raysan5 +[rcore] REVIEWED: `DecodeDataBase64()`, decoding error when input string is invalid (#5170) by @zet23t +[rcore] REVIEWED: `DecodeDataBase64()`, follow convention: by @raysan5 +[rcore] REVIEWED: `DecompressData()`, fixed buffer copying by @raysan5 +[rcore] REVIEWED: `GetClipboardImage()`, make symbol available on all platforms by @raysan5 +[rcore] REVIEWED: `GetRandomValue()`, explained the new approach to get more uniform random values range by @raysan5 +[rcore] REVIEWED: `FileExists()`, using macro by @raysan5 +[rcore] REVIEWED: `IsFileExtension()`, to avoid other modules dependency #5071 by @raysan5 +[rcore] REVIEWED: `LoadDirectoryFilesEx()`, count files if not recursive (#5496) by @katanya04 +[rcore] REVIEWED: `LoadDirectoryFilesEx()`, minor tweak #5569 by @raysan5 +[rcore] REVIEWED: `UnloadDirectoryFiles`, added NULL check by @Bigfoot71 +[rcore] REVIEWED: `ScanDirectoryFiles*()`, #4833 by @raysan5 +[rcore] REVIEWED: `ScanDirectoryFilesRecursively()`, fix issues by @mlorenc +[rcore] REVIEWED: `ScanDirectoryFilesRecursively()`, makes `path` static by @Bigfoot71 +[rcore] REVIEWED: `SaveFileText()`, const input text by @raysan5 +[rcore] REVIEWED: `TakeScreenshot()`, avoid path filtering by @raysan5 +[rcore] REVIEWED: `TakeScreenshot()`, only scale the screenshot by the DPI scale if required by @Jeffm2501 +[rcore] REDESIGNED: Fullscreen modes, use current display resolution by @raysan5 -WARNING- +[rcore] REDESIGNED: `EncodeDataBase64()`/`DecodeDataBase64()`, use NULL terminated strings by @raysan5 -WARNING- +[rcore][GLFW] ADDED: Clipboard image support for linux (#5603) by @maiconpintoabreu +[rcore][GLFW] REVIEWED: Fix `IsWindowFocused()` inverted logic (#5333) by @iamahuman1395 +[rcore][GLFW] REVIEWED: Fullscreen modes on Linux (X11 over XWayland) by @raysan5 +[rcore][GLFW] REVIEWED: Make sure that GLFW uses RL_*ALLOC macros (#4777) by @sleeptightAnsiC +[rcore][GLFW] REVIEWED: Set correct default axes for gamepads that are not connected (#5444) by @LeapersEdge +[rcore][GLFW] REVIEWED: Window initialization on HighDPI monitor (Windows) #5549 by @raysan5 +[rcore][GLFW] REVIEWED: Window scaling on Wayland with GLFW 3.4+ (#5564) by @0xPD33 +[rcore][GLFW] REVIEWED: Window scaling with HighDPI on macOS #5059 by @raysan5 +[rcore][GLFW] REVIEWED: `InitPlatform()`, crash caused by `glfwCreateWindow()` (#4804) by @sleeptightAnsiC +[rcore][GLFW] REVIEWED: `InitPlatform()`, code simplification by @raysan5 +[rcore][GLFW] REVIEWED: `GetWindowHandle()`, return correct handle under Linux X11 by @Didas72 +[rcore][GLFW] REVIEWED: `WindowSizeCallback()` by @raysan5 +[rcore][GLFW] REVIEWED: `CORE.Window.eventWaiting` and `FLAG_WINDOW_ALWAYS_RUN` handling (#4642) by @Asdqwe +[rcore][GLFW] REVIEWED: `GetGamepadButtonPressed()`, return GAMEPAD_BUTTON_*_TRIGGER_2 if pressed (#4742) by @whaleymar +[rcore][GLFW] REVIEWED: Update `mappings.h` using `GenerateMappings.cmake` by @sleeptightAnsiC +[rcore][RGFW] ADDED: New backend option: `PLATFORM_WEB_RGFW` and update RGFW (#4480) by @colleagueRiley +[rcore][RGFW] REVIEWED: Added missing Right Control key by @M374LX +[rcore][RGFW] REVIEWED: Changed `RGFW_window_eventWait` timeout to -1 by @doggymangc +[rcore][RGFW] REVIEWED: Duplicate entries reemoved from `keyMappingRGFW` (#5242) by @iamahuman1395 +[rcore][RGFW] REVIEWED: Fix Escape always closing the window by @M374LX +[rcore][RGFW] REVIEWED: Forward declare the windows stuff, prevents failures in GCC (#5269) by @Jeffm2501 +[rcore][RGFW] REVIEWED: Requires RGBA8 images as window icons (#5431) by @crisserpl2 +[rcore][RGFW] REVIEWED: Window width calculation by adding wOffset (#5457) by @qubitxr +[rcore][RGFW] REVIEWED: `SetGamepadVibration()` implementation (#4612) by @JupiterRider +[rcore][SDL] REVIEWED: `FLAG_WINDOW_ALWAYS_RUN` implementation (#4598) by @Asdqwe +[rcore][SDL] REVIEWED: First touch position is overwritten with mouse position by @zet23t +[rcore][SDL] REVIEWED: Avoid `rtext` module dependency #4959 by @raysan5 +[rcore][SDL] REVIEWED: Fix crash when `strncpy()` tries to copy using NULL pointer (#5359) by @MikiZX1 +[rcore][SDL] REVIEWED: Fix gamepad detection (#5176) by @brccabral +[rcore][SDL] REVIEWED: Fix maximizing, minimizing and restoring windows for SDL2 (#4607) by @Asdqwe +[rcore][SDL] REVIEWED: Fix show, hide, focus and unfocus window/flags states for SDL2 (#4610) by @Asdqwe +[rcore][SDL] REVIEWED: Fix text input (characters) (#5574) by @TheKodeToad +[rcore][SDL] REVIEWED: Gamepad event handling by adding joystick instance id tracking (#4724) by @Asdqwe +[rcore][SDL] REVIEWED: RGB order on SDL3 #3568 by @raysan5 +[rcore][SDL] REVIEWED: `SetGamepadMappings()` (#5548) by @arleyuti +[rcore][SDL] REVIEWED: Handle monitor ID correctly on SDL3 (#5290) by @sleeptightAnsiC +[rcore][Web] ADDED: Clipboard image implementation for web (#5614) by @maiconpintoabreu +[rcore][Web] ADDED: `GetWindowScaleDPI()`, implementation for PLATFORM_WEB (#4526) by @Asdqwe +[rcore][Web] ADDED: `IsWindowHidden()` (#4789) by @marionauta +[rcore][Web] REVIEWED: Fix reversed window blur/focus logic for web (#5590) by @aceiii +[rcore][Web] REVIEWED: Fix undeclared canvas variable error (#5507) by @vdemcak +[rcore][Web] REVIEWED: Replaced hardcoded canvas id references with module variable (#4735) by @zet23t +[rcore][Web] REVIEWED: Store canvas name id at platform initialization by @raysan5 +[rcore][Web] REVIEWED: Support software renderer on Web, blitting framebuffer data directly to a 2d canvas by @raysan5 +[rcore][Web] REVIEWED: Using `Module` provided canvas id for event binding (#4690) by @zet23t +[rcore][Web] REVIEWED: Using `EmscriptenKeyboardCallback()` to consume key events by @moros1138 +[rcore][Web] REVIEWED: `IsWindowFocused()` (#4640) by @marionauta +[rcore][Web] REVIEWED: `GetApplicationDirectory()` for Wasm, returning root VFS (#5495) by @git +[rcore][Win32] ADDED: `rcore_desktop_win32`, new native Win32 platform backend (#4869) by @johnnymarler +[rcore][Win32] REDESIGNED: New native Win32 platform backend, simplification and formating by @raysan5 +[rcore][Win32] REVIEWED: New Win32 platform backend to accomodate `rlsw` Software Renderer by @raysan5 +[rcore][Win32] REVIEWED: Fix window width calculation by adding wOffset (#5480) by @crisserpl2 +[rcore][Win32] REVIEWED: Implemented `SetWindowMaxSize()`, `SetWindowMinSize()` and `SetWindowSize()` (#5536) by @mikeemm +[rcore][Win32] REVIEWED: Remove `-fno-stack-protector` and add `requestFullscreen` on exported methods by @emilhakimov415 +[rcore][Win32] REVIEWED: VSync flag not being applied (#5521) by @mikeemm +[rcore][Win32] REVIEWED: Window minimizing/maximizing functionality (#5524) by @mikeemm +[rcore][Android] REVIEWED: Content scaling functionality for HighDPI (#4769) by @Bigfoot71 +[rcore][Android] REVIEWED: `SetWindowState()` (#5424) by @caszuu +[rcore][Android] REVIEWED: `GetCurrentMonitor()` implementation (#5204) by @guntzjonas +[rcore][Android] REVIEWED: `android_fopen()` mechanims, after removing `utils` module dependency by @raysan5 +[rcore][Android] REVIEWED: `fopen()` wrapping mechanism, to use --wrap=fopen on build systems (#5624) by @ghera +[rcore][Android] REVIEWED: `LoadMusicStream()` due to missing fopen redirect (#5589) by @ghera +[rcore][Android] REVIEWED: Fallback on dirent filename when all route fails (#5484) by @lucas150670 +[rcore][Android] REVIEWED: Gesture system not reporting GESTURE_NONE (#5452) by @padmadevd +[rcore][Android] REVIEWED: Keyboard input detected as gamepad on some devices (#5439) by @decruz +[rcore][Android] REVIEWED: Replaced `android_fopen()` with linker `--wrap=fopen` (#5605) by @ghera +[rcore][DRM] ADDED: Support asynchronous page-flipping and framebuffer caching by @rob-bits -WARNING- +[rcore][DRM] REVIEWED: Compiler flag SUPPORT_DRM_CACHE, to enable triple buffered DRM caching by @rob-bits +[rcore][DRM] REVIEWED: Backspace key in RPI SSH keyboard by @matthijs +[rcore][DRM] REVIEWED: Check if video mode is valid and rename to match with other methods (#5235) by @maiconpintoabreu +[rcore][DRM] REVIEWED: Disable touch position simulation from mouse movement for touchscreen devices (#5279) by @MULTidll +[rcore][DRM] REVIEWED: Fix resources memory leak (#5494) by @matthewkennedy +[rcore][DRM] REVIEWED: Platform initialization messages by @raysan5 +[rcore][DRM] REVIEWED: Use `eglGetPlatformDisplayEXT()` for Mali compatibility (#5446) by @decruz +[rcore][DRM] REVIEWED: `FindNearestConnectorMode()`, prefer mode with smallest number of unused pixels (#5158) by @maiphi +[rcore][DRM] REVIEWED: `eglGetPlatformDisplay()` usage by @raysan5 +[rcore][DRM] REVIEWED: Improved touch input handling and multitouch support (#5447) by @MULTidll +[rcore][DRM] REVIEWED: DRM cache buffers support #4988 by @raysan5 +[rlgl] ADDED: Software Rendering Support (#4832) by @Bigfoot71 +[rlgl] ADDED: `rlGetProcAddress()` (#5062) by @danilwhale +[rlgl] ADDED: `rlUnloadShader()` (#5631) by @raysan5 +[rlgl] REMOVED: `RLGL_RENDER_TEXTURES_HINT`, enabled by default and no complaints of anyone having issues #5479 by @raysan5 +[rlgl] REVIEWED: Allow tint colors in GL_LINE (wires) and GL_POINT (points) draw modes on OpenGL 1.1 clean (#5207) by @lanur622 +[rlgl] REVIEWED: Attribute name and location, for consistency by @raysan5 +[rlgl] REVIEWED: Behavior for texture in OpenGL 1.1 draw states (#5328) by @lanur622 +[rlgl] REVIEWED: Better default values for normals and tangents (VBOs) (#4763) by @Bigfoot71 +[rlgl] REVIEWED: Clean up Matrix handling and rl* functions to follow convention (#5505) by @nathanmburg +[rlgl] REVIEWED: Delete shader object in case compilation fails by @raysan5 +[rlgl] REVIEWED: Fixed shader tangents to be vec4 by @Sir-Irk +[rlgl] REVIEWED: Increased depth size and clip distances to avoid z-fighting issues by @raysan5 +[rlgl] REVIEWED: Issues compiling for OpenGL 1.1 #5088 by @raysan5 +[rlgl] REVIEWED: Make sure ShaderUniformDataType matches rlShaderUniformDataType (#4577) by @RicoP +[rlgl] REVIEWED: Potential issue with animated normals on OpenGL 1.1 by @raysan5 +[rlgl] REVIEWED: Using define for buffers size by @raysan5 +[rlgl] REVIEWED: Warning log macro (#5043) by @vinnyhorgan +[rlgl] REVIEWED: Preserve texture on mode switching (#4364) by @Destructor17 +[rlgl] REVIEWED: Proposed fix for default near/far clipping range (#4906) by @Bigfoot71 +[rlgl] REVIEWED: `rlLoadTexture()`, max mipmap levels to use #5400 by @raysan5 +[rlgl] REVIEWED: `rlLoadTexture()`, uncomplete texture do to issue on mipmap loading #5416 by @raysan5 +[rlgl] REVIEWED: `rlLoadTextureDepth()`, address inconsistencies with WebGL 2.0 for sized depth formats #5500 by @raysan5 +[rlgl] REVIEWED: `rlReadScreenPixels()`, optimizations (#4667) by @pope +[rlgl] REVIEWED: `rlSetTexture()` not overriding default mode (#5200) by @maiconpintoabreu +[rlgl] REVIEWED: `rlActiveDrawBuffers`, fix for OpenGL ES 3.0 (#4605) by @Bigfoot71 +[rlgl] REVIEWED: `rlGetPixelDataSize()`, correct compressed data size calculation per blocks #5416 by @raysan5 +[rlgl] REVIEWED: `instranceTransform` shader location index #4538 (#4579) by @meadiode +[rlgl] REVIEWED: `SetShaderValueTexture()`, updatee comments (#4703) by @pejorativefox +[rlgl] REDESIGNED: Avoid program crash if GPU data is tried to be loaded before `InitWindow()` #4751 by @raysan5 -WARNING- +[rlgl] REDESIGNED: Shader loading API function names for more consistency #5631 by @raysan5 -WARNING- +[rlsw] ADDED: `swGetColorBuffer()` for convenience by @raysan5 +[rlsw] REVIEWED: Completeness of `glDraw*()` functions (#5304) by @Bigfoot71 +[rlsw] REVIEWED: Fix axis aligned quad detection (#5314) by @Bigfoot71 +[rlsw] REVIEWED: Fix clipping issue on software render (#5342) by @Bigfoot71 +[rlsw] REVIEWED: Make sure SSE is being used when compiling with MSVC by @raysan5 +[rlsw] REVIEWED: SIMD instrinsics must be explicitly enabled by developer, only SSE supported at the moment #5316 by @raysan5 +[rlsw] REVIEWED: Segmentation fault when trying to render a model (#5622) by @maiconpintoabreu +[rlsw] REVIEWED: Simplify framebuffer logic and add blit/copy fast path (#5312) by @Bigfoot71 +[rlsw] REVIEWED: Review depth formats and fix depth writing (#5317) by @Bigfoot71 +[rlsw] REVIEWED: Smarter texture management (#5296) by @Bigfoot71 +[rlsw] REVIEWED: Subpixel correction (#5300) by @Bigfoot71 +[rlsw] REVIEWED: C++ support (#5291) by @alexgb0 +[rshapes] ADDED: `DrawLineDashed()` (#5222) by @luis605 +[rshapes] ADDED: `DrawEllipseV()` and `DrawEllipseLinesV()` (#4963) by @meowstr +[rshapes] REVIEWED: `DrawLine()`, fix pixel offset issue on drawing (#4666) by @Bigfoot71 +[rshapes] REVIEWED: `DrawRectangleGradientEx()`, incorrect parameter names (#4980) by @vincent +[rshapes] REVIEWED: `DrawRectangleLines`, fix pixel offset issue (#4669) by @Bigfoot71 +[rshapes] REVIEWED: `DrawRectangleRounded()`, allow to draw rectangles with a size < 1 (#4683) by @tea +[rshapes] REVIEWED: `CheckCollisionLines()`, formating and follow raylib conventions by @raysan5 +[rshapes] REVIEWED: `DrawCircleSector*()`, optimization to prevent division by zero (#4843) by @theundergroundsorcerer +[rshapes] REVIEWED: `CheckCollisionLines()`, properly detect collision if one line is almost vertical (#5510, #5531) by @bielern +[rtextures] REVIEWED: Cubemap mipmap loading improvements (#4721) by @Not-Nik +[rtextures] REVIEWED: Setting mipmaps MAX_LEVEL based on actual mipcount input (#4622) by @JettMonstersGoBoom +[rtextures] REVIEWED: TCC compilation due to `emmintrin.h` not found (#4707) by @sleeptightAnsiC +[rtextures] REVIEWED: `Image`, initialization align to avoid issues with some platforms (#5214) by @psxdev +[rtextures] REVIEWED: `LoadImageDataNormalized()`, missing increments of k (#4745) by @henrikglass +[rtextures] REVIEWED: `LoadImageFromScreen()`, use the render size instead of screen size (#5192) by @JeffM2501 +[rtextures] REVIEWED: `GenImageFontAtlas()`, fix memory corruption in reallocation (#5602) by @creeperblin +[rtextures] REVIEWED: `GenImageFontAtlas()`, no need for the conservative approach flag by @raysan5 +[rtextures] REVIEWED: `GenImageFontAtlas()`, scale image to fit all characters, in case it fails size estimation #5542 by @raysan5 +[rtextures] REVIEWED: `GetImageColor()` (#5560) by @raysan5 +[rtextures] REVIEWED: `ExportImage()` by @raysan5 +[rtextures] REVIEWED: `HalfToFloat()` and `FloatToHalf()` dereferencing issues with an union (#4729) by @Asdqwe +[rtextures] REVIEWED: `ImageClearBackground()` and `ImageDrawRectangleRec()` optimizations (#5363) by @ChocolateChipKookie +[rtextures] REVIEWED: `ImageColorReplace()`, alpha issue replacement (#5047) by @RomainPlmg +[rtextures] REVIEWED: `ImageDrawLineEx(), to be able to draw even numbered thicknesses (#5042) by @Sir-Irk +[rtextures] REVIEWED: `ImageBlurGaussian()`, fix integer overflow in cast (#5037) by @garrisonhh +[rtext] ADDED: `LoadTextLines()`/`UnloadTextLines()` by @raysan5 +[rtext] ADDED: `MeasureTextCodepoints()` for direct measurement of codepoints (#5623) by @creeperblin +[rtext] RENAMED: Variable names for consistency, `textLength` (length in bytes) vs `textSize` (measure in pixels) by @raysan5 +[rtext] REVIEWED: Support default font loading with no GPU enabled, to be used with Image API +[rtext] REVIEWED: Default font alpha on Big Endian systems (#4624) by @Fancy2209 +[rtext] REVIEWED: Font atlas image generation, added some comments #5141 by @raysan5 +[rtext] REVIEWED: Multiply security checks to avoid crashes on wrongly provided string data (#4751) by @raysan5 +[rtext] REVIEWED: Potential security concerns while copying unbounded text data between strings by @raysan5 +[rtext] REVIEWED: Properly clean up the default font on unload, it may be reused if the window is created again by @Jeffm2501 +[rtext] REVIEWED: Remove some `const` from text buffer return values by @raysan5 +[rtext] REVIEWED: Some security vulnerabilities in font loading (#5433, #5434, #5436, #5450) by @oneafter +[rtext] REVIEWED: Use `const` for codepoints arrays passed to function #5159 by @raysan5 +[rtext] REVIEWED: Use `strstr()` instead of `TextFindIndex()` (#4764) by @brabissimoooooo +[rtext] REVIEWED: `LoadFont()`, improve load message (#5050) by @katanya04 +[rtext] REVIEWED: `LoadFont*()`, provide more detailed info on glyphs failing to pack (#5141) by @raysan5 +[rtext] REVIEWED: `LoadCodepoints()`, local variable shadowing `codepoints` (#5430) by @KiviTK +[rtext] REVIEWED: `LoadCodepoints()`, comment explaining functionality (#4787) by @loftafi +[rtext] REVIEWED: `LoadFontFromImage()`, limit pixel scanning to image dimensions (#5626) by @joe +[rtext] REVIEWED: `LoadFontDataBDF()` #5346 by @raysan5 +[rtext] REVIEWED: `LoadTextLines()` by @raysan5 +[rtext] REVIEWED: `ExportFontAsCode()` #5497 by @raysan5 +[rtext] REVIEWED: `ExportFontAsCode()` not checking `isGpuReady` #4894 by @raysan5 +[rtext] REVIEWED: `GetCodepointCount()`, misuse of cast (#4741) by @sleeptightAnsiC +[rtext] REVIEWED: `GenImageFontAtlas()`, memory corruption and invalid size calculation (#5602) by @konakona418 +[rtext] REVIEWED: `TextInsert()`, fix bug on insertion (#5644) by @CrackedPixel +[rtext] REVIEWED: `TextJoin()`, convert `const char **` to `char**` by @raysan5 +[rtext] REVIEWED: `TextReplace()` and `TextLength()`, avoid using `strcpy()` by @raysan5 +[rtext] REVIEWED: `TextReplace()` by @raysan5 +[rtext] REVIEWED: `TextReplace()`, improvements (#5511) by @belan2470 +[rtext] REVIEWED: `TextReplaceBetween()` by @raysan5 +[rtext] REVIEWED: `TextSubtext(), fixes (#4759) by @veins1 +[rtext] REVIEWED: `TextToPascal()`, fix issue by @raysan5 +[rtext] REVIEWED: `TextToFloat()`, remove removed inaccurate comment (#4596) by @hexmaster111 +[rtext] REDESIGNED: `LoadFontData()`, added input parameter by @raysan5 -WARNING- +[rmodels] ADDED: Support CPU animation in OpenGL 1.1 (#4925) by @JeffM2501 +[rmodels] RENAMED: Skinning shader variables (new default naming) by @raysan5 +[rmodels] REVIEWED: glTF animation framerate calculation (#4472, #5445) by @TheLazyIndianTechie +[rmodels] REVIEWED: Assign meshes without bone weights to the bone they are attached to so they animate by @Jeffm2501 +[rmodels] REVIEWED: Out of bound memory read in `Material.maps` (#5534) by @Sumethh +[rmodels] REVIEWED: Segfaults with animation system (#4635) by @JettMonstersGoBoom +[rmodels] REVIEWED: Transform the vertex normals by the animated matrix (#4646) by @Jeffm2501 +[rmodels] REVIEWED: Use the animated verts and normals in OpenGL 1.1 if they exist by @Jeffm2501 +[rmodels] REVIEWED: GLTF bone weight assignments for meshes parented to armature (#4923) by @JeffM2501 +[rmodels] REVIEWED: `LoadOBJ()`, avoid fatal errors on OBJ model loading by @raysan5 +[rmodels] REVIEWED: `LoadOBJ()`, don't upload meshes, `LoadModel()` will upload them (#5083) by @JeffM2501 +[rmodels] REVIEWED: `LoadOBJ()`, loading crash when there are no normals present (#5079) by @zet23t +[rmodels] REVIEWED: `LoadGLFT()`, avoid loading attributes already loaded (#4996) by @raysan5 +[rmodels] REVIEWED: `LoadGLTF()`, anim correctly inherits world transform (#5206) by @ArmanOmmid +[rmodels] REVIEWED: `LoadGLTF()`, fix memory leak when model has no bones (#5629) by @vberdugo +[rmodels] REVIEWED: `LoadGLTF()`, fixing offset for processing tangents (#5015) by @Sir-Irk +[rmodels] REVIEWED: `LoadGLTF()`, log warning about draco compression not supported #5567 by @raysan5 +[rmodels] REVIEWED: `LoadGLTF()`, separate roughness and metallic channels on load (#4739) by @Not-Nik +[rmodels] REVIEWED: `LoadGLTF()`, support 16 bit vec3 values (#5388) by @Jeffm2501 +[rmodels] REVIEWED: `LoadGLTF()`, fixed NULL pointer dereference (#4564) by @CalebHeydon +[rmodels] REVIEWED: `LoadIQM()` and `LoadModelAnimationsIQM()`, fix memory leaks (#4649) by @peter15914 +[rmodels] REVIEWED: `LoadM3D()`, don't require a M3d animation only file to have a mesh (#5475) by @Jeffm2501 +[rmodels] REVIEWED: `LoadImageFromCgltfImage()`, fix crash when NULL is passed (#4563) by @CalebHeydon +[rmodels] REVIEWED: `LoadImageFromCgltfImage()`, fix NULL pointer dereference (#4557) by @CalebHeydon +[rmodels] REVIEWED: `LoadModelAnimationsGLTF()`, properly load 1 frame animations (#5561) by @arlez80 +[rmodels] REVIEWED: `LoadModelAnimationsGLTF()`, anim correctly inherits world transform (#5206) by @ArmanOmmid +[rmodels] REVIEWED: `UploadMesh()`, improve default normal and tangent values (#4763) by @Bigfoot71 +[rmodels] REVIEWED: `GenMeshTangents()`, improvements (#4937) by @Bigfoot71 +[rmodels] REVIEWED: `UpdateModelAnimation()` optimization (#5244) by @Arrangemonk +[rmodels] REVIEWED: `UpdateModelAnimationBones()`, break on first mesh found and formating by @raysan5 +[rmodels] REVIEWED: `UpdateModelAnimationBones()`, fix normal transform (#4634) by @Bigfoot71 +[rmodels] REVIEWED: `UpdateModelAnimationBones()`, optimizations (#4602) by @Kirandeep-Singh-Khehra +[rmodels] REVIEWED: `UpdateModelAnimationBones()`, bones animation scale (#4896) by @mUnicorn +[rmodels] REVIEWED: `DrawBillboardPro()`, flipped texture source rectangle draw correctly (#5276) by @aixiansheng +[rmodels] REVIEWED: `DrawMesh()`, OpenGL11 fixes added texture consistency check (#5328) by @meisei4 +[rmodels] REVIEWED: `DrawMeshInstanced()`, breaking if instanceTransform was unused (#5469) by @al13n321 +[rmodels] REVIEWED: `DrawSphereEx()`, normals support (#4926) by @karl-zylinski +[rmodels] REVIEWED: `ExportMesh()`, improve OBJ vertex data precision and lower memory usage (#4496) by @mikeemm +[raudio] REVIEWED: Fix a glitch at the end of a sound (#5578) by @mackron +[raudio] REVIEWED: Improvements to device configuration (#5577) by @mackron +[raudio] REVIEWED: Initialize sound alias properties as if it was a new sound (#5123) by @JeffM2501 +[raudio] REVIEWED: Music stopping some frames too early by @veins1 +[raudio] REVIEWED: Remove usage of `ma_data_converter_get_required_input_frame_count()` (#5568) by @mackron +[raudio] REVIEWED: Better computation of the default audio stream buffer size (#4898) by @JeffM2501 +[raudio] REVIEWED: Documentation for audio stream processor, number of channels (#4753) by @goto40 +[raudio] REVIEWED: `LoadWAV()`, fixed buffer overflow when loading WAV files (#4539) by @CalebHeydon +[raudio] REVIEWED: `LoadMusicStream()`, issues in Android due to missing fopen redirect (#5589) by @ghera +[raudio] REVIEWED: `LoadMusicStreamFromMemory()` for QOA, reduced the number of allocs and memcpy (#5108) by @lassade +[raudio] REVIEWED: `UnloadMusicStream()`, properly close FLAC (#5133) by @rossberg +[raudio] REVIEWED: `UpdateMusicStream()`, prevent to run without music playing (#5046) by @maiconpintoabreu +[raudio] REVIEWED: `WaveFormat()`, fixed memory leak on early-return (#4779) by @deckarep +[raudio] REVIEWED: `WaveFormat()`, freeing memory issue (#5481) by @MarcosTypeAP +[raudio] REVIEWED: `GetMusicTimePlayed()`, fix music shorter than buffer size (#5048) by @veins1 +[raudio] REVIEWED: `GetMusicTimePlayed()`, returns incorrect time after restart (#4914) by @Servall4 +[raudio] REDESIGNED: `SetSoundPan()` and `SetMusicPan()` ranges from [-1.0..1.0] (#5350) by @raysan5 -WARNING- +[raymath] ADDED: `MatrixUnit()` and `MatrixMultiplyValue()` (#5613) by @lamweilun +[raymath] ADDED: `MatrixCompose()` (#5324) by @EDBCREPO +[raymath] ADDED: `Vector2CrossProduct()` (#4520) by @uwiwiow +[raymath] REVIEWED: Fix C++ operator overloads (#4535) by @ListeriaM +[raymath] REVIEWED: Vector2Angle(), detailed documentation (#4556) by @mrjonjonjon +[raymath] REVIEWED: Wrap float3 and float16 for consistency with other types (#5540) by @n-s-kiselev +[raymath] REVIEWED: `HalfToFloat()`, fix mantissa hex value by @backspaceoverload +[raymath] REVIEWED: `MatrixCompose()` by @raysan5 +[raymath] REVIEWED: `MatrixDeterminant()` #4780 by @raysan5 +[raymath] REVIEWED: `MatrixDecompose()` removes shear (#5201) by @ArmanOmmid +[raymath] REVIEWED: `MatrixMultiply()` intrinsics support: SSE (#5427) by @mcdubhghlas +[raymath] REVIEWED: `QuaternionFromVector3ToVector3()` (#5508) by @The4codeblocks +[rcamera] REVIEWED: `Camera3D.fovy` description (#5164) by @0stamina +[rcamera] REVIEWED: Camera move up and right work with Z up cameras (#5458) by @Jeffm2501 +[rcamera] REVIEWED: Camera pan speed (#5554) by @CrackedPixel +[rcamera] REVIEWED: Fix camera initial position (#4657) by @Asdqwe +[rcamera] REVIEWED: Clamp camera pitch (#5137) by @feive7 + +[build][CI] ADDED: ARM64 with Visual Studio (#4781) by @Peter0x44 +[build][CI] ADDED: `update_examples.yml` by @raysan5 +[build][CI] RENAMED: GitHub Actions files, clearer naming by @raysan5 +[build][CI] REVIEWED: Linux workflow to run on `ubuntu-latest` by @raysan5 +[build][CI] REVIEWED: Remove double zip and misleading zip type (#5512) by @maiconpintoabreu +[build][CI] REVIEWED: `update_examples.yml` workflow (#5130) by @iamahuman1395 +[build][Script] REVIEWED: `build_example_web.bat` by @raysan5 +[build][Make] ADDED: ARM64 support to Linux builds by @quaylynrimer11 +[build][Make] REVIEWED: Use `libraylib.web.a` naming on PLATFORM_WEB by @raysan5 -WARNING- +[build][Make] REVIEWED: Only include rglfw.c for glfw platform by @johnnymarler +[build][Make] REVIEWED: Fix template to support `libraylib.web.a` (#5620) by @skazu0611 +[build][Make] REVIEWED: Avoid using != assignment (#5464) by @sleeptightAnsiC +[build][Make] REVIEWED: Improve support for `PLATFORM_DESKTOP_WIN32` (#5311) by @GithubPrankster +[build][Make] REVIEWED: Latest NDK version compiling errors on Android (#5389) by @Sethbones +[build][Make] REVIEWED: Makefile to support software renderer by @raysan5 +[build][Make] REVIEWED: Properly detect if ABI is Android by @lumenkeyes +[build][Make] REVIEWED: Remove path override/change for linux (#5641) by @CrackedPixel +[build][Make] REVIEWED: Renamed `PLATFORM_DESKTOP_SDL3` to `USING_VERSION_SDL3` #5175 by @raysan5 +[build][Make] REVIEWED: Tag to locate examples list: `#EXAMPLES_LIST_*` by @raysan5 +[build][Make] REVIEWED: Undefine _GNU_SOURCE for rglfw building (#4732) by @Peter0x44 +[build][Make] REVIEWED: `-sASSERTIONS` usage by linker #4717 by @raysan5 +[build][Make] REVIEWED: `Makefile.Web` per-example target generation including used resources by @raysan5 +[build][Make] REVIEWED: android-libc.txt generation by @lumenkeyes +[build][Make] REVIEWED: examples Makefile PLATFORM define (#4582) by @Asdqwe +[build][VS2022] ADDED: Filters and platform files in MSVC for ease of editing (#4644) by @Jeffm2501 +[build][VS2022] REVIEWED: Default raylib resource file .rc for projects by @raysan5 +[build][VS2022] REVIEWED: Fix MSVC warnings (#5619) by @Jeffm2501 +[build][VS2022] REVIEWED: MSVC warnings (#5595) by @Jeffm2501 +[build][VS2022] REVIEWED: Fixed VS2022 broken build by @alexander_nichols +[build][VSCode] REVIEWED: Makefile for `PLATFORM_WEB` (#5620) by @takenoko-pm +[build][CMake] ADDED: Support for SDL3, reviewed SDL2 package cheking by @kariem2k +[build][CMake] ADDED: emscripten build options (#5180) by @brccabral +[build][CMake] ADDED: QNX EGL 2.0 library configuration (#5499) by @jscaffidi +[build][CMake] REVIEWED: Android configuration issues (#4671) by @Bigfoot71 +[build][CMake] REVIEWED: Definition to use SDL3 (#5175) by @brccabral +[build][CMake] REVIEWED: X11 libraries as glfw make private (#5625) by @maiconpintoabreu +[build][CMake] REVIEWED: Don't build examples using audio if audio is disabled (#4652) by @Peter0x44 +[build][CMake] REVIEWED: Export automatically raylib definitions and compile/link options (#5179) by @brccabral +[build][CMake] REVIEWED: Expose RGFW platform backend (#5386) by @Sethbones +[build][CMake] REVIEWED: Set PLATFORM to Web by default when configuring web builds with emcmake (#4748) by @gilzoide +[build][CMake] REVIEWED: Set correct description for verbose option by @brccabral +[build][CMake] REVIEWED: Set version to 3.12 (#4547) by @zyperpl +[build][CMake] REVIEWED: Update `LibraryConfigurations.cmake (#5617) by @m0391n +[build][CMake] REVIEWED: Use STATIC lib ON by default (#4799) by @brccabral +[build][CMake] REVIEWED: Using addCMacro instead of defineCMacro (#4620) by @Joonsey +[build][CMake] REVIEWED: Web compilation system (#5181) by @brccabral +[build][CMake] REVIEWED: Set `libm` as public so it can be linked by consumer (#5193) by @brccabral +[build][Cmake] REVIEWED: Expose PLATFORM_WEB_RGFW (#5579) by @crisserpl2 +[build][Zig] ADDED: Android target to build by @lumenkeyes +[build][Zig] REVIEWED: Link EGL for Android by @lumenkeyes +[build][Zig] REVIEWED: Approach to build for web with zig-build (#5157) by @haxsam +[build][Zig] REVIEWED: Fix build accessing env vars (#5490) by @iisakki +[build][Zig] REVIEWED: Fix emscripten building by @johnnymarler +[build][Zig] REVIWED: Move examples/build.zig into main build.zig by @johnnymarler +[build][Zig] REVIEWED: Fix raygui inclusion in windows crosscompilation (#4489) by @kimierik +[build][Zig] REVIEWED: Issue on emscripten run if the user has not installed emsdk by @emilhakimov415 +[build][Zig] REVIEWED: Make X11 the default display backend instead of choosing at runtime (#5168) by @Not-Nik +[build][Zig] REVIEWED: Using `addLibrary()` and set root modules manually by @myQwil +[build][Zig] REVIEWED: `build.zig.zon`, use the new zig v0.14 version (#4819) by @zewenn +[build][Zig] REVIEWED: `build.zig.zon`, update hashes (#4826) by @david +[build][Zig] REVIEWED: Improve build system (#4531) by @haxsam +[build][Zig] REVIEWED: Properly generate Android triple by @lumenkeyes +[build][Zig] REVIEWED: XCode-frameworks dependency for latest zig (#4675) by @mobiuscog + +[examples] ADDED: Missing examples VS2022 projects by @raysan5 +[examples] ADDED: Missing glsl120 shaders by @raysan5 +[examples] ADDED: Missing resources on some examples by @raysan5 +[examples] ADDED: `core_3d_fps_controller` by @agnis16 +[examples] ADDED: `core_clipboard_text` (#5231) by @robinsaviary +[examples] ADDED: `core_delta_time` (#5216) by @robinsaviary +[examples] ADDED: `core_directory_files` (#5230) by @hugo +[examples] ADDED: `core_highdpi_testbed` by @raysan5 +[examples] ADDED: `core_highdpi_demo` example by @johnnymarler +[examples] ADDED: `core_input_actions` (#5211) by @JettMonstersGoBoom +[examples] ADDED: `core_keyboard_testbed` by @raysan5 +[examples] ADDED: `core_monitor_change` (#5215) by @maiconpintoabreu +[examples] ADDED: `core_render_texture` by @raysan5 +[examples] ADDED: `core_text_file_loading` (#5278) by @NimComPoo-04 +[examples] ADDED: `core_undo_redo` by @raysan5 +[examples] ADDED: `core_viewport_scaling` (#5313) by @agnis16 +[examples] ADDED: `core_compute_hash` by @raysan5 +[examples] ADDED: `shapes_ball_physics` (#5372) by @davidbuzatto +[examples] ADDED: `shapes_bullet_hell` (#5218) by @zerohorsepower +[examples] ADDED: `shapes_clock_of_clocks` (#5263) by @themushroompirates +[examples] ADDED: `shapes_clock_of_clocks` by @hmz-rhl +[examples] ADDED: `shapes_dashed_line` (#5222) by @luis605 +[examples] ADDED: `shapes_double_pendulum` by @joecheong2006 +[examples] ADDED: `shapes_hilbert_curve` (#5454) by @hmz-rhl +[examples] ADDED: `shapes_kaleidoscope` (#5233) by @hugo +[examples] ADDED: `shapes_lines_drawing` (#5283) by @robinsaviary +[examples] ADDED: `shapes_math_angle_rotation` (#5236) by @krispysnacc +[examples] ADDED: `shapes_math_sine_cosine` (#5257) by @Jopestpe +[examples] ADDED: `shapes_mouse_trail` (#5246) by @balamurugan +[examples] ADDED: `shapes_particles` (#5260) by @JordSant +[examples] ADDED: `shapes_penrose_tile` (#5376) by @davidbuzatto +[examples] ADDED: `shapes_pie_chart` (#5227) by @GideonSerf +[examples] ADDED: `shapes_recursive_tree` (#5229) by @Jopestpe +[examples] ADDED: `shapes_rlgl_color_wheel` example (#5355) by @robinsaviary +[examples] ADDED: `shapes_rlgl_triangle` example (#5353) by @robinsaviary +[examples] ADDED: `shapes_starfield` (#5255) by @themushroompirates +[examples] ADDED: `shapes_triangle_strip` (#5240) by @Jopestpe +[examples] ADDED: `text_inline_styling` by @raysan5 +[examples] ADDED: `text_strings_management` (#5379) by @davidbuzatto +[examples] ADDED: `text_words_alignment` (#5254) by @themushroompirates +[examples] ADDED: `textured_clipboard_image` (#5604) by @maiconpintoabreu +[examples] ADDED: `textures_cellular_automata` (#5395) by @JordSant +[examples] ADDED: `textures_frame_buffer_rendering` (#5468) by @jackboakes +[examples] ADDED: `textures_magnifying_glass` (#5587) by @dgamebillda +[examples] ADDED: `textures_screen_buffer` (#5357) by @agnis16 +[examples] ADDED: `textures_sprite_stacking` example (#5345) by @robinsaviary +[examples] ADDED: `models_animation_timing` by @raysan5 +[examples] ADDED: `models_directional_billboard` (#5351) by @robinsaviary +[examples] ADDED: `models_animation_bone_blending` (#5543) by @dmitrii-brand +[examples] ADDED: `models_basic_voxel` (#5212) by @timlittle88 +[examples] ADDED: `models_decals` (#5298) by @themushroompirates +[examples] ADDED: `models_geometry_textures_cube` (#5221) by @Jopestpe +[examples] ADDED: `models_tesseract` by @raysan5 +[examples] ADDED: `shaders_render_depth_texture` by @luis605 +[examples] ADDED: `shaders_ascii_rendering` (#5213) by @maiconpintoabreu +[examples] ADDED: `shaders_color_correction` (#5307) by @JordSant +[examples] ADDED: `shaders_game_of_life` (#5394) by @JordSant +[examples] ADDED: `shaders_mandelbrot_set` (#5282) by @JordSant +[examples] ADDED: `shaders_normalmap` by @Sir-Irk +[examples] ADDED: `shaders_rounded_rectangle` (#4719) by @anstropleuton +[examples] ADDED: `shaders_view_depth` (luis605) +[examples] ADDED: `audio_sound_positionning` by @Bigfoot71 +[examples] REMOVED: `core_gamepad_info` by @raysan5 +[examples] REMOVED: `core_loading_thread` by @raysan5 +[examples] RENAMED: Sprite fonts resource directory by @raysan5 +[examples] RENAMED: `core_3d_fps_controller` to `core_3d_camera_fps` by @raysan5 +[examples] RENAMED: `core_high_dpi` to `core_highdpi_demo` by @raysan5 +[examples] RENAMED: `core_input_gestures_web` to `core_input_gestures_testbed` by @raysan5 +[examples] RENAMED: `shapes_draw_circle_sector` to `shapes_circle_sector_drawing` by @raysan5 +[examples] RENAMED: `shapes_draw_rectangle_rounded` to `shapes_rounded_rectangle_drawing` by @raysan5 +[examples] RENAMED: `shapes_draw_ring` to `shapes_ring_drawing` by @raysan5 +[examples] RENAMED: `shapes_easings_ball_anim` to `shapes_easings_ball` by @raysan5 +[examples] RENAMED: `shapes_easings_box_anim` to `shapes_easings_box` by @raysan5 +[examples] RENAMED: `shapes_easings_rectangle_array` to `shapes_easings_rectangles` by @raysan5 +[examples] RENAMED: `text_draw_3d` to `text_3d_drawing` by @raysan5 +[examples] RENAMED: `text_raylib_fonts` -> `text_sprite_fonts` by @raysan5 +[examples] RENAMED: `text_unicode` to `text_unicode_emojis` by @raysan5 +[examples] RENAMED: `textures_draw_tiled` to `textures_tiled_drawing` by @raysan5 +[examples] RENAMED: `textures_polygon` to `textures_polygon_drawing` by @raysan5 +[examples] RENAMED: `textures_sprite_anim` to `textures_sprite_animation` by @raysan5 +[examples] RENAMED: `models_animation` to `models_animation_playing` by @raysan5 +[examples] RENAMED: `models_billboard` to `models_billboard_rendering` by @raysan5 +[examples] RENAMED: `models_cubicmap` to `models_cubicmap_rendering` by @raysan5 +[examples] RENAMED: `models_draw_cube_texture` to `models_textured_cube` by @raysan5 +[examples] RENAMED: `models_geometry_textures_cube` to `models_rotating_cube` by @raysan5 +[examples] RENAMED: `models_gpu_skinning` to `models_animation_gpu_skinning` by @raysan5 +[examples] RENAMED: `models_heightmap` to `models_heightmap_rendering` by @raysan5 +[examples] RENAMED: `models_skybox` to `models_skybox_rendering` by @raysan5 +[examples] RENAMED: `shaders_deferred_render` to `shaders_deferred_rendering` by @raysan5 +[examples] RENAMED: `shaders_eratosthenes` to `shaders_eratosthenes_sieve` by @raysan5 +[examples] RENAMED: `shaders_fog` to `shaders_fog_rendering` by @raysan5 +[examples] RENAMED: `shaders_hybrid_render` to `shaders_hybrid_rendering` by @raysan5 +[examples] RENAMED: `shaders_lightmap` to `shaders_lightmap_rendering` by @raysan5 +[examples] RENAMED: `shaders_normal_map` to `shaders_normalmap_rendering` by @raysan5 +[examples] RENAMED: `shaders_raymarching` to `shaders_raymarching_rendering` by @raysan5 +[examples] RENAMED: `shaders_shadowmap` to `shaders_shadowmap_rendering` by @raysan5 +[examples] RENAMED: `shaders_spotlight` to `shaders_spotlight_rendering` by @raysan5 +[examples] RENAMED: `shaders_texture_drawing` to `shaders_texture_rendering` by @raysan5 +[examples] RENAMED: `shaders_view_depth` to `shaders_depth_rendering` by @raysan5 +[examples] RENAMED: `shaders_write_depth` to `shaders_depth_writing` by @raysan5 +[examples] RENAMED: `shaders_normalmap` to `shaders_normal_map` by @raysan5 +[examples] RENAMED: `audio_fft_spectrum_visualizer` to `audio_spectrum_visualizer` by @raysan5 +[examples] REVIEWED: All examples header section comments, for better organization and consistency by @raysan5 +[examples] REVIEWED: All examples MSVC warnings and non-working examples by @Jeffm2501 +[examples] REVIEWED: Moved some examples out of `others` category, removing category by @raysan5 +[examples] REVIEWED: Shaders formating to follow raylib code conventions by @raysan5 +[examples] REVIEWED: `examples_template`, adding info about new rexm tool by @raysan5 +[examples] REVIEWED: `core_2d_camera_mouse_zoom` by @raysan5 +[examples] REVIEWED: `core_3d_camera_fps` by @raysan5 +[examples] REVIEWED: `core_3d_fps_controller` by @agnis16 +[examples] REVIEWED: `core_3d_fps_controller` by @raysan5 +[examples] REVIEWED: `core_clipboard_text` by @raysan5 +[examples] REVIEWED: `core_custom_frame_control`, to work properly on web by @PanicTitan +[examples] REVIEWED: `core_custom_frame_control`, avoid divide by 0 by @johnnymarler +[examples] REVIEWED: `core_delta_time` by @raysan5 +[examples] REVIEWED: `core_directory_files` by @raysan5 +[examples] REVIEWED: `core_highdpi_testbed` by @raysan5 +[examples] REVIEWED: `core_input_actions` by @raysan5 +[examples] REVIEWED: `core_input_gamepad, fix hardcoded gamepad 0 by @lepasona +[examples] REVIEWED: `core_input_gamepad` by @raysan5 +[examples] REVIEWED: `core_input_gestures_testbed` by @raysan5 +[examples] REVIEWED: `core_monitor_detector` by @raysan5 +[examples] REVIEWED: `core_random_sequence` by @raysan5 +[examples] REVIEWED: `core_render_texture` by @raysan5 +[examples] REVIEWED: `core_text_file_loading` (#5339) by @cs24b016 +[examples] REVIEWED: `core_undo_redo` by @raysan5 +[examples] REVIEWED: `core_viewport_scaling` by @raysan5 +[examples] REVIEWED: `core_window_flags` by @raysan5 +[examples] REVIEWED: `core_window_flags`, add borderless windowed toggle by @johnnymarler +[examples] REVIEWED: `core_window_flags`, prevent immediate window restore from minimize by @johnnymarler +[examples] REVIEWED: `core_window_letterbox` by @raysan5 +[examples] REVIEWED: `core_input_gamepad`, vibration test button (#5362) by @sergii +[examples] REVIEWED: `core_directory_files` (#5343) by @hugo +[examples] REVIEWED: `core_input_virtual_controls` (#4584) by @danilwhale +[examples] REVIEWED: `shapes_bouncing_ball`, added gravity (#5217) by @Jopestpe +[examples] REVIEWED: `shapes_kaleidoscope` rewind, forward and reset buttons (#5369) by @hugo +[examples] REVIEWED: `shapes_digital_clock` by @raysan5 +[examples] REVIEWED: `shapes_bullet_hell` by @raysan5 +[examples] REVIEWED: `shapes_double_pendulum` by @raysan5 +[examples] REVIEWED: `shapes_easings_testbed` by @raysan5 +[examples] REVIEWED: `shapes_hilbert_curve` #5454 by @raysan5 +[examples] REVIEWED: `shapes_hilbert_curve` by @raysan5 +[examples] REVIEWED: `shapes_lines_drawing` by @raysan5 +[examples] REVIEWED: `shapes_penrose_tile` by @raysan5 +[examples] REVIEWED: `shapes_penrose_tile` formating by @raysan5 +[examples] REVIEWED: `shapes_pie_chart` by @raysan5 +[examples] REVIEWED: `shapes_rlgl_triangle` by @raysan5 +[examples] REVIEWED: `shapes_triangle_strip`, fix array size and simplify loop (#5280) by @Jopestpe +[examples] REVIEWED: `text_draw_3d` by @zeankey +[examples] REVIEWED: `text_font_sdf` by @raysan5 +[examples] REVIEWED: `text_inline_styling` by @raysan5 +[examples] REVIEWED: `text_raylib_fonts` by @raysan5 +[examples] REVIEWED: `text_unicode_ranges` by @raysan5 +[examples] REVIEWED: `text_words_alignment` (#5411) by @lucarandrianirina2507 +[examples] REVIEWED: `text_draw_3d`, fix text size by @zeankey +[examples] REVIEWED: `textures_bunnymark`, replaced bunny image #5344 by @raysan5 +[examples] REVIEWED: `textures_clipboard_image` by @raysan5 +[examples] REVIEWED: `textures_framebuffer_rendering` by @raysan5 +[examples] REVIEWED: `textures_image_kernel` by @raysan5 +[examples] REVIEWED: `textures_screen_buffer` by @raysan5 +[examples] REVIEWED: `textures_sprite_stacking` by @raysan5 +[examples] REVIEWED: `textures_textured_curve` (#5463) by @Jeffm2501 +[examples] REVIEWED: `textures_bunnymark` by @raysan5 +[examples] REVIEWED: `models_first_person_maze` (#5478) by @pauldahacker +[examples] REVIEWED: `models_decals`, fixed unload crash, added buttons (#5306) by @themushroompirates +[examples] REVIEWED: `models_animation_bone_blending` by @raysan5 +[examples] REVIEWED: `models_basic_voxel` by @raysan5 +[examples] REVIEWED: `models_basic_voxel`, fix raycasting logic (#5643) by @SardineMilk +[examples] REVIEWED: `models_basic_voxel`, replaced `GetRenderWidth()` by `GetScreenWidth()` (#5635) by @SardineMilk +[examples] REVIEWED: `models_decals` by @raysan5 +[examples] REVIEWED: `models_first_person_maze` by @raysan5 +[examples] REVIEWED: `models_loading_vox` by @raysan5 +[examples] REVIEWED: `models_mesh_picking` by @raysan5 +[examples] REVIEWED: `models_point_rendering` by @raysan5 +[examples] REVIEWED: `models_rlgl_solar_system` by @raysan5 +[examples] REVIEWED: `models_skybox_rendering` by @raysan5 +[examples] REVIEWED: `models_textures_tiling` shaders by @raysan5 +[examples] REVIEWED: `models_animation_blending` by @raysan5 +[examples] REVIEWED: `shaders_ascii_rendering` (#5219) by @maiconpintoabreu +[examples] REVIEWED: `shaders_ascii_rendering` by @raysan5 +[examples] REVIEWED: `shaders_basic_pbr`, to work on web (#4516) by @afanolovcic +[examples] REVIEWED: `shaders_color_correction` by @raysan5 +[examples] REVIEWED: `shaders_defered_render` for OpenGL ES 3.0 (#4617) by @Bigfoot71 +[examples] REVIEWED: `shaders_deferred_render` (#4676) by @veins1 +[examples] REVIEWED: `shaders_deferred_render` by @raysan5 +[examples] REVIEWED: `shaders_deferred_rendering` by @raysan5 +[examples] REVIEWED: `shaders_game_of_life` by @raysan5 +[examples] REVIEWED: `shaders_game_of_life` for web (#5399) by @JordSant +[examples] REVIEWED: `shaders_hybrid_rendering`, shaders issues by @raysan5 +[examples] REVIEWED: `shaders_julia_set` by @raysan5 +[examples] REVIEWED: `shaders_mandelbrot_set` (#5310) by @JordSant +[examples] REVIEWED: `shaders_mandelbrot_set` for WebGL (#5286) by @JordSant +[examples] REVIEWED: `shaders_mesh_instancing` by @raysan5 +[examples] REVIEWED: `shaders_multi_sample2d` by @raysan5 +[examples] REVIEWED: `shaders_normal_map` by @raysan5 +[examples] REVIEWED: `shaders_normalmap_rendering` by @raysan5 +[examples] REVIEWED: `shaders_normalmap` #5032 by @raysan5 +[examples] REVIEWED: `shaders_normalmap` by @raysan5 +[examples] REVIEWED: `shaders_rounded_rectangle` by @raysan5 +[examples] REVIEWED: `shaders_shadowmap_rendering` by @raysan5 +[examples] REVIEWED: `shaders_shadowmap_rendering` example by @raysan5 +[examples] REVIEWED: `shaders_spotlight` by @raysan5 +[examples] REVIEWED: `shaders_texture_drawing` by @raysan5 +[examples] REVIEWED: `shaders_cel_shading` by @raysan5 +[examples] REVIEWED: `shaders_cel_shading`, cel-shading and outline using inverted hull (#5615) by @galexeev +[examples] REVIEWED: `audio_fft_spectrum_visualizer`, not working on web by @raysan5 +[examples] REVIEWED: `audio_music_stream` by @raysan5 +[examples] REVIEWED: `audio_sound_multi` by @raysan5 +[examples] REVIEWED: `audio_sound_positioning` by @dabbott +[examples] REVIEWED: `rlgl_compute_shader` by @raysan5 +[examples] REVIEWED: `raylib_opengl_interop`, remove unnecessary defines (#5618) by @m0391n +[examples] REVIEWED: `raylib_opengl_interop` single header library not having it's implementation loaded (#5498) by @jscaffidi +[examples] REVIEWED: Example shader `depth_render.fs` by @raysan5 +[examples] REVIEWED: Example shader `depth_write.fs` by @raysan5 +[examples] REVIEWED: Example shader `normalmap.fs` by @raysan5 +[examples] REVIEWED: Example shader `palette_switch.fs` (#5205) by @benjaminsmallarz +[examples] REVIEWED: Example shader `write_depth.fs` for glsl120 by @maiconpintoabreu +[examples] REDESIGNED: `core_clipboard_text`, based on #5248 by @raysan5 +[examples] REDESIGNED: `shapes_digital_clock` by @raysan5 +[examples] REDESIGNED: `shapes_kaleidoscope`, store lines #5361 by @raysan5 +[examples] REDESIGNED: `text_unicode_ranges` by @raysan5 +[examples] REDESIGNED: `models_animation_blending` by @raysan5 +[examples] REDESIGNED: `audio_raw_stream`, remove callback usage (#5637) by @dan-hoang + +[bindings] ADDED: Common Lisp binding by @colin +[bindings] ADDED: D Object Oriented wrapper (#4550) by @RealDoigt +[bindings] ADDED: Dart binding (#5585) by @FinnDemonCat +[bindings] ADDED: Deno binding for 5.5 (#5462) by @JJLDonley +[bindings] ADDED: Jai binding (#4565) by @ahmedqarmout2 +[bindings] ADDED: Raylib-cs.BleedingEdge (#4999) by @danilwhale +[bindings] ADDED: SwiftForth language binding (#5319) by @dave +[bindings] ADDED: Wave language (#5539) by @youngjae681 +[bindings] ADDED: c3-lang for raylib 5.5 (#4555) by @afanolovcic +[bindings] ADDED: chicken scheme (#5449) by @meowstr +[bindings] ADDED: elle bindings (#5370) by @acquitefx +[bindings] ADDED: raylib-ada (#5150) by @artem.v.ageev +[bindings] ADDED: uiua bindings (#4993) by @marcosgrzesiak +[bindings] ADDED: Ring language (#5421) by @SabeDoesThings +[bindings] ADDED: ReiLua, Lua binding (#4589) by @legendaryredfox +[bindings] ADDED: Matte bindings (#4586) by @legendaryredfox +[bindings] UPDATED: Nim wrapper version (#4638) by @planetis-m +[bindings] UPDATED: Raylib.nelua version (#4698) by @AuzFox +[bindings] UPDATED: Fennel binding (#4585) by @0riginaln0 +[bindings] UPDATED: Jaylib to 5.5 (#4546) by @glowiak +[bindings] UPDATED: Ruby binding (#4639) by @BotRandomness +[bindings] UPDATED: V language binding (#4691) by @mobiuscog +[bindings] UPDATED: `racket-raylib` version by @eutro +[bindings] UPDATED: fortran-raylib to 5.5 (#4518) by @interkosmos +[bindings] UPDATED: raylib-beef (#4514) by @Jannis.v.hagen +[bindings] UPDATED: raylib-cpp to 5.5 (#4511) by @RobLoach +[bindings] UPDATED: raylib-cs binding link (#4746) by @Rgebee +[bindings] UPDATED: raylib-d to 5.5 (#4506) by @schveiguy +[bindings] UPDATED: raylib-lua (#4567) by @legendaryredfox +[bindings] UPDATED: raylib-py to 5.5 (#4519) by @jorgegomes83 +[bindings] UPDATED: raylib-python-cffi to 5.0 (#4512) by @villares +[bindings] UPDATED: raylib-python-cffi to 5.5 (#4517) by @villares +[bindings] UPDATED: raylib-zig by @Not-Nik +[bindings] UPDATED: Multiple bindings (#4504, #4559, #4633, #4920, #5538, #5546) +[bindings] REMOVED: Broken repo links from bindings (#4572) by @legendaryredfox +[bindings] UPDATED: Factor bindings to 5.5 (#5648) by @ArnautDaniel +[bindings] UPDATED: Raylib-cs to 5.5 (#4628) by @JupiterRider +[bindings] UPDATED: raylib-go to 5.5 (#4566) by @JupiterRider +[bindings] UPDATED: racket-raylib (#4884) by @eutro +[bindings] UPDATED: raylib-odin binding license (#5647) by @jtorrestx +[bindings] UPDATED: raylib-zig (#5103) by @Not-Nik +[bindings] UPDATED: keybindings versions (#4570) by @legendaryredfox +[bindings] UPDATED: Common Lisp binding (#5014) by @fosskers +[bindings] REMOVED: raylib-v7 binding (#5090) by @Auios + +[rlparser] RENAMED: `raylib_parser` to `rlparser` by @raysan5 +[rlparser] REVIEWED: Command-line info by @raysan5 +[rexm] ADDED: Automated-testing system by @raysan5 +[rexm] ADDED: Build check warnings logs by @raysan5 +[rexm] ADDED: Command `update` to validate and update required files by @raysan5 +[rexm] ADDED: Example automated-testing by @raysan5 +[rexm] ADDED: Examples validation by @raysan5 +[rexm] ADDED: Implemented examples commands for automatic testing by @raysan5 +[rexm] ADDED: Implemented file rename requirements by @raysan5 +[rexm] ADDED: More detailed log info by @raysan5 +[rexm] ADDED: Read examples years created/reviewed info by @raysan5 +[rexm] ADDED: Resources scanning from examples for automated copying by @raysan5 +[rexm] ADDED: Security checks to verify examples categories provided by @raysan5 +[rexm] ADDED: Support example removal by @raysan5 +[rexm] ADDED: TestLog option for logs processing (without rebuilding) by @raysan5 +[rexm] ADDED: Web metadata validation and update by @raysan5 +[rexm] ADDED: Web platform logs automated reports by @raysan5 +[rexm] ADDED: `LoadExamplesData()` to support filtering and sorting by @raysan5 +[rexm] ADDED: `RemoveVSProjectFromSolution()` by @raysan5 +[rexm] ADDED: `AddVSProjectToSolution()` by @raysan5 +[rexm] ADDED: Examples buildind command by @raysan5 +[rexm] ADDED: Legend info on report generation by @raysan5 +[rexm] ADDED: `UpdateSourceMetadata()` by @raysan5 +[rexm] ADDED: Allow building web examples locally on Windows platform by @raysan5 +[rexm] ADDED: Generate second report with examples with issues by @raysan5 +[rexm] ADDED: Automatic validation and update of examples by @raysan5 +[rexm] ADDED: Automatically fix not found VS2022 project / solution by @raysan5 +[rexm] ADDED: Check example exists (compilation worked) before trying to run it by @raysan5 +[rexm] ADDED: Copy examples web-build files to `raylib.com` by @raysan5 +[rexm] ADDED: Remove duplicate entries on examples list on `update` process by @raysan5 +[rexm] ADDED: Automatic validation and update by @raysan5 +[rexm] ADDED: Example building, using default examples `Makefile`/`Makefile.Web` by @raysan5 +[rexm] ADDED: Rebuild to support full categories by @raysan5 +[rexm] ADDED: Support building full example collection and categories by @raysan5 +[rexm] ADDED: Support examples data `validation` and **report generation** by @raysan5 +[rexm] ADDED: Support paths customization with environment variables by @raysan5 +[rexm] ADDED: Support testing running on `PLATFORM_DRM` by @raysan5 +[rexm] REVIEWED: Fixed some issues, improved `make` call defining base path by @raysan5 +[rexm] REVIEWED: Avoid external tool for UUIDv4 generation, implement custom function by @raysan5 +[rexm] REVIEWED: Avoid updating metadata from `others` examples by @raysan5 +[rexm] REVIEWED: Allow external path definition for VS solution file by @raysan5 +[rexm] REVIEWED: Reports moved to reports directory by @raysan5 +[rexm] REVIEWED: Automated testing for Web by @raysan5 +[rexm] REVIEWED: Examples header info inconsistencies by @raysan5 +[rexm] REVIEWED: Get example info from example header when added, to be added to collection by @raysan5 +[rexm] REVIEWED: Makefile and default paths (#5224) by @Teeto44 +[rexm] REVIEWED: Testing report generation by @raysan5 +[rexm] REVIEWED: Update `nextCatIndex` (#5616) by @CrackedPixel +[rexm] REVIEWED: Added variable assignment to rename option (#5077) by @lpow100 +[rexm] REVIEWED: Ran examples testing for macos (#5366) by @MaeBrooks +[remx] REVIEWED: Add check for category on new name (#5093) by @maiconpintoabreu +[remx] REVIEWED: Add warning for missing new name (#5092) by @maiconpintoabreu +[rexm] REVIEWED: Add compiled rexm to gitignore (#5086) by @maiconpintoabreu +[rexm] REVIEWED: Update examples status report when examples Updated by @raysan5 +[rexm] REVIEWED: `ScanExampleResources()` avoid resources to be saved by the program by @raysan5 +[rexm] REVIEWED: `UpdateSourceMetadata()` and `TextReplaceBetween()` by @raysan5 +[rexm] REVIEWED: `examples.js` example addition working by @raysan5 +[rexm] REVIEWED: `add` command logic for existing examplee addition by @raysan5 +[rexm] REVIEWED: Replace example name on project file by @raysan5 +[rexm] REVIEWED: Report issues if logs can not be loaded by @raysan5 +[rexm] REVIEWED: VS project adding to solution by @raysan5 +[rexm] REVIEWED: `UpdateSourceMetadata()` by @raysan5 +[rexm] REVIEWED: Example renaming on `examples.js` by @raysan5 +[rexm] REVIEWED: Examples source code headers metadata by @raysan5 +[rexm] REVIEWED: `README.md` generation improvements by @raysan5 +[rexm] REVIEWED: Store example resource paths by @raysan5 +[rexm] REVIEWED: `Makefile.Web` before trying to rebuild new example for web by @raysan5 +[rexm] REVIEWED: Using `Makefile.Web` for specific web versions generation by @raysan5 + +[external] REVIEWED: `jar_mod.h`, buffer overflow on memcpy by @LoneAuios +[external] REVIEWED: `sdefl` and `sinfl` issues (#5367) by @raysan5 +[external] REVIEWED: `sinfl_bsr()`, improvements by @RicoP +[external] REVIEWED: `rl_gputex.h`, make it usable standalone by @raysan5 +[external] REVIEWED: `rl_gputex.h,, fix the swizzling in `rl_load_dds_from_memory()` (#5422) by @msmith-codes +[external]`REVIEWED: `rl_gputex.h,, decouple logging and memory allocation from raylib by @sleeptightAnsiC +[external] REVIEWED: `stb_truetype`, fix composite glyph scaling logic (#4811) by @ashishbhattarai +[external] UPDATED: dr_libs (#5020) by @Emil2010 +[external] UPDATED: miniaudio to v0.11.22 (#4983) by @M374LX +[external] UPDATED: miniaudio to v0.11.23 (#5234) by @pyrokn8 +[external] UPDATED: miniaudio to v0.11.24 (#5506) by @vdemcak +[external] UPDATED: raygui to 5.0-dev for examples by @raysan5 +[external] UPDATED: RGFW to 1.5 (#4688) by @ColleagueRiley +[external] UPDATED: RGFW to 1.6 (#4795) by @colleagueRiley +[external] UPDATED: RGFW to 1.7 (#4965) by @M374LX +[external] UPDATED: RGFW to 1.7.5-dev (#4976) by @M374LX +[external] UPDATED: RGFW to 2.0.0 (#5582) by @CrackedPixel + +[misc] ADDED: SECURITY.md for security reporting policies by @raysan5 +[misc] ADDED: `examples/examples_list`, to be used by `rexm` or other tools by @raysan5 +[misc] ADDED: `fix_win32_compatibility.h`, utility header to avoid `windows.h` inclusion conflicts by @Jeffm2501 +[misc] RENAMED: Move and rename `raylib_parser` to `tools/rlparser` by @raysan5 -WARNING- +[misc] REVIEWED: Avoid `realloc()` usage, security improvement by @raysan5 +[misc] REVIEWED: Prioritize `calloc()` calls than `malloc()` on some cases by @raysan5 +[misc] REVIEWED: Code comments using imperative format by @raysan5 +[misc] REVIEWED: Webpage reference comments start with `REF:`, more consistent with `TODO:` and `NOTE:` comments by @raysan5 +[misc] REVIEWED: CONVENTIONS.md, added additional conventions by @raysan5 +[misc] REVIEWED: ROADMAP.md with potential future improvements by @raysan5 +[misc] REVIEWED: Comments formating, using imperative mode by @raysan5 +[misc] REVIEWED: Multiple examples inconsistencies on heeaders and overall structure to follow template by @raysan5 +[misc] REVIEWED: Code gardening: cleaning, reviewed comments, warnings, variables names... by @raysan5, @JeffM2501, @maiconpintoabreu, @CrackedPixel ------------------------------------------------------------------------- Release: raylib 5.5 (18 November 2024) @@ -458,7 +1215,7 @@ Detailed changes: [bindings] UPDATED: Python raylib-py v5.0.0beta1 (#3557) by @jorgegomes83 [bindings] UPDATED: raylib-d binding (#3561) by @schveiguy [bindings] UPDATED: Janet (#3553) by @Dmitry Matveyev -[bindings] UPDATED: Raylib.nelua (#3552) by @guy.smith764 +[bindings] UPDATED: Raylib.nelua (#3552) by @AuzFox [bindings] UPDATED: raylib-cpp to 5.0 (#3551) by @RobLoach [bindings] UPDATED: Pascal binding (#3548) by @Gunko Vadim [external] UPDATED: stb_truetype.h to latest version by @raysan5 From fd017c0b2d40505c0d8257d82ca4f7d77cdbaea5 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Sun, 15 Mar 2026 14:25:31 -0500 Subject: [PATCH 164/319] fixed issue with mouse modes (#5659) --- src/platforms/rcore_desktop_rgfw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 935b3fbb2..b091215ed 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -1096,7 +1096,7 @@ void HideCursor(void) // Enables cursor (unlock cursor) void EnableCursor(void) { - RGFW_window_captureMouse(platform.window, false); + RGFW_window_captureRawMouse(platform.window, false); // Set cursor position in the middle SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); @@ -1108,7 +1108,7 @@ void EnableCursor(void) // Disables cursor (lock cursor) void DisableCursor(void) { - RGFW_window_captureMouse(platform.window, true); + RGFW_window_captureRawMouse(platform.window, true); HideCursor(); CORE.Input.Mouse.cursorLocked = true; From 29280971beac11a8f6ccd9bdf947fcdf0260671a Mon Sep 17 00:00:00 2001 From: Soma Mizobuchi <34585795+somamizobuchi@users.noreply.github.com> Date: Sun, 15 Mar 2026 15:31:18 -0400 Subject: [PATCH 165/319] rcore_platform_sdl: Fix `GetTime()` resolution for sdl (#5653) `SDL_GetTicks()` only has millisecond resolution so switched to `SLD_GetPerformanceCounter()` combined with `SDL_GetPerformanceFrequency()` which should allow more granular timing Fix: remove intermediate variable Remove second cast --- src/platforms/rcore_desktop_sdl.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 9dbd0b873..11c83c72c 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1276,9 +1276,7 @@ void SwapScreenBuffer(void) // Get elapsed time measure in seconds double GetTime(void) { - unsigned int ms = SDL_GetTicks(); // Elapsed time in milliseconds since SDL_Init() - double time = (double)ms/1000; - return time; + return (double)SDL_GetPerformanceCounter() / SDL_GetPerformanceFrequency(); } // Open URL with default system browser (if available) From 1d9e24eb58c8016addc2b8d0a2fb857e0b592913 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 15 Mar 2026 20:39:57 +0100 Subject: [PATCH 166/319] REVIEWED: `GetTime()`, make it consistent between platforms, consider window initialization base time --- src/platforms/rcore_desktop_glfw.c | 1 + src/platforms/rcore_desktop_rgfw.c | 7 ++++--- src/platforms/rcore_desktop_sdl.c | 12 ++++++++---- src/platforms/rcore_desktop_win32.c | 5 ++++- src/platforms/rcore_template.c | 1 - src/platforms/rcore_web.c | 1 + src/platforms/rcore_web_emscripten.c | 9 +-------- 7 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 1975a7bfe..9b6881d40 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1179,6 +1179,7 @@ void SwapScreenBuffer(void) double GetTime(void) { double time = glfwGetTime(); // Elapsed time since glfwInit() + return time; } diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index b091215ed..b92775592 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -174,7 +174,6 @@ extern "C" { // Types and Structures Definition //---------------------------------------------------------------------------------- typedef struct { - double startTime; RGFW_window *window; // Native display device (physical screen connection) RGFW_monitor *monitor; mg_gamepads minigamepad; @@ -1127,7 +1126,9 @@ void SwapScreenBuffer(void) // Get elapsed time measure in seconds since InitTimer() double GetTime(void) { - return get_time_seconds() - platform.startTime; + double time = get_time_seconds() - CORE.Time.base; + + return time; } // Open URL with default system browser (if available) @@ -1628,7 +1629,7 @@ int InitPlatform(void) RGFW_setGlobalHints_OpenGL(hints); platform.window = RGFW_createWindow((CORE.Window.title != 0)? CORE.Window.title : " ", 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, flags | RGFW_windowOpenGL); - platform.startTime = get_time_seconds(); + CORE.Time.base = get_time_seconds(); #ifndef PLATFORM_WEB_RGFW i32 screenSizeWidth; diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 11c83c72c..5cd93ebd7 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1276,7 +1276,9 @@ void SwapScreenBuffer(void) // Get elapsed time measure in seconds double GetTime(void) { - return (double)SDL_GetPerformanceCounter() / SDL_GetPerformanceFrequency(); + double time = (double)(SDL_GetPerformanceCounter()/SDL_GetPerformanceFrequency()) - CORE.Time.base; + + return time; } // Open URL with default system browser (if available) @@ -2122,12 +2124,14 @@ int InitPlatform(void) // Initialize timing system //---------------------------------------------------------------------------- - // NOTE: No need to call InitTimer(), let SDL manage it internally - CORE.Time.previous = GetTime(); // Get time as double - + // Get base time from window initialization + CORE.Time.base = (double)(SDL_GetPerformanceCounter()/SDL_GetPerformanceFrequency()); + #if defined(_WIN32) && SUPPORT_WINMM_HIGHRES_TIMER && !SUPPORT_BUSY_WAIT_LOOP SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "1"); // SDL equivalent of timeBeginPeriod() and timeEndPeriod() #endif + + // NOTE: No need to call InitTimer(), let SDL manage it internally //---------------------------------------------------------------------------- // Initialize storage system diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index a56a7f090..1cf8826aa 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -1238,9 +1238,12 @@ void SwapScreenBuffer(void) // Get elapsed time measure in seconds double GetTime(void) { + double time = 0.0; LARGE_INTEGER now = { 0 }; QueryPerformanceCounter(&now); - return (double)(now.QuadPart - CORE.Time.base)/(double)platform.timerFrequency.QuadPart; + time = (double)(now.QuadPart - CORE.Time.base)/(double)platform.timerFrequency.QuadPart; + + return time; } // Open URL with default system browser (if available) diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 40a1922e7..368dd3e77 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -341,7 +341,6 @@ void SwapScreenBuffer(void) double GetTime(void) { double time = 0.0; - struct timespec ts = { 0 }; clock_gettime(CLOCK_MONOTONIC, &ts); unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index ae847d8c9..62b2afda9 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -986,6 +986,7 @@ void SwapScreenBuffer(void) double GetTime(void) { double time = glfwGetTime(); // Elapsed time since glfwInit() + return time; } diff --git a/src/platforms/rcore_web_emscripten.c b/src/platforms/rcore_web_emscripten.c index df3234217..60b647f60 100644 --- a/src/platforms/rcore_web_emscripten.c +++ b/src/platforms/rcore_web_emscripten.c @@ -962,14 +962,7 @@ void SwapScreenBuffer(void) // Get elapsed time measure in seconds since InitTimer() double GetTime(void) { - double time = 0.0; - /* - struct timespec ts = { 0 }; - clock_gettime(CLOCK_MONOTONIC, &ts); - unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; - time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer() - */ - time = emscripten_get_now()*1000.0; + double time = emscripten_get_now()*1000.0; return time; } From 26f329a5e7a33689f91619ce396f0cb2e86d25f7 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Sun, 15 Mar 2026 14:40:26 -0500 Subject: [PATCH 167/319] fixed copy paste error in makefiles (#5660) --- examples/Makefile | 2 +- src/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 899d040e3..5ae40af6f 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -95,7 +95,7 @@ USE_EXTERNAL_GLFW ?= FALSE GLFW_LINUX_ENABLE_WAYLAND ?= FALSE GLFW_LINUX_ENABLE_X11 ?= TRUE -# Enable support for X11 by default on Linux when using GLFW +# Enable support for X11 by default on Linux when using RGFW # NOTE: Wayland is disabled by default, only enable if you are sure RGFW_LINUX_ENABLE_WAYLAND ?= FALSE RGFW_LINUX_ENABLE_X11 ?= TRUE diff --git a/src/Makefile b/src/Makefile index e53514701..9826ddf8e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -122,7 +122,7 @@ USE_EXTERNAL_GLFW ?= FALSE GLFW_LINUX_ENABLE_WAYLAND ?= FALSE GLFW_LINUX_ENABLE_X11 ?= TRUE -# Enable support for X11 by default on Linux when using GLFW +# Enable support for X11 by default on Linux when using RGFW # NOTE: Wayland is disabled by default, only enable if you are sure RGFW_LINUX_ENABLE_WAYLAND ?= FALSE RGFW_LINUX_ENABLE_X11 ?= TRUE From dfc3f58a06d080b7db50efc92eea152c7470a546 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Sun, 15 Mar 2026 17:26:29 -0500 Subject: [PATCH 168/319] fixed web platform resize events (#5662) --- 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 b92775592..87ad7d7c4 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -1332,7 +1332,7 @@ void PollInputEvents(void) CORE.Window.currentFbo.width = CORE.Window.render.width; CORE.Window.currentFbo.height = CORE.Window.render.height; #elif defined(PLATFORM_WEB_RGFW) - return; + // do nothing but prevent other behavior #else SetupViewport(platform.window->w, platform.window->h); From ceeaf57a8bc041c5e90e512797bb076fceacb2e2 Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Mon, 16 Mar 2026 08:24:33 +0000 Subject: [PATCH 169/319] [CHANGELOG] fix github name (#5663) Just for consistency, changing @Ray to @raysan5 --- CHANGELOG | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 1536d4e4c..192fe9207 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -894,9 +894,9 @@ Detailed changes: [rlgl] ADDED: Vertex normals for RLGL immediate drawing mode (#3866) by @bohonghuang -WARNING- [rlgl] ADDED: `rlCullDistance*()` variables and getters (#3912) by @KotzaBoss [rlgl] ADDED: `rlSetClipPlanes()` function (#3912) by @KotzaBoss -[rlgl] ADDED: `isGpuReady` flag, allow font loading with no GPU acceleration by @Ray -WARNING- +[rlgl] ADDED: `isGpuReady` flag, allow font loading with no GPU acceleration by @raysan5 -WARNING- [rlgl] REVIEWED: Changed RLGL_VERSION from 4.5 to 5.0 (#3914) by @rsteinke1111 -[rlgl] REVIEWED: Shader load failing returns 0, instead of fallback by @Ray -WARNING- +[rlgl] REVIEWED: Shader load failing returns 0, instead of fallback by @raysan5 -WARNING- [rlgl] REVIEWED: Standalone mode default flags (#4334) by @Asdqwe [rlgl] REVIEWED: Fix hardcoded index values in vboID array (#4312) by @JettMonstersGoBoom [rlgl] REVIEWED: GLint64 did not exist before OpenGL 3.2 (#4284) by @Tchan0 @@ -960,7 +960,7 @@ Detailed changes: [rtextures] ADDED: `ImageFromChannel()` (#4105) by @brccabral [rtextures] ADDED: `ImageDrawLineEx()` (#4097) by @Bigfoot71 [rtextures] ADDED: `ImageDrawTriangle()` (#4094) by @Bigfoot71 -[rtextures] REMOVED: SVG files loading and drawing, moving it to raylib-extras by @Ray -WARNING- +[rtextures] REMOVED: SVG files loading and drawing, moving it to raylib-extras by @raysan5 -WARNING- [rtextures] REVIEWED: `LoadImage()`, added support for 3-channel QOI images (#4384) by @R-YaTian [rtextures] REVIEWED: `LoadImageRaw()` #3926 by @raysan5 [rtextures] REVIEWED: `LoadImageColors()`, advance k in loop (#4120) by @brccabral @@ -988,7 +988,7 @@ Detailed changes: [rtext] ADDED: `TextToCamel()` (#4033) by @IoIxD [rtext] ADDED: `TextToSnake()` (#4033) by @IoIxD [rtext] ADDED: `TextToFloat()` (#3627) by @Benjamin Schmid Ties -[rtext] REDESIGNED: `SetTextLineSpacing()` by @Ray -WARNING- +[rtext] REDESIGNED: `SetTextLineSpacing()` by @raysan5 -WARNING- [rtext] REVIEWED: `LoadFontDataBDF()` name and formating by @raysan5 [rtext] REVIEWED: `LoadFontDefault()`, initialize glyphs and recs to zero #4319 by @raysan5 [rtext] REVIEWED: `LoadFontEx()`, avoid default font fallback (#4077) by @Peter0x44 -WARNING- @@ -996,7 +996,7 @@ Detailed changes: [rtext] REVIEWED: `LoadBMFont()`, issue on not glyph data initialized by @raysan5 [rtext] REVIEWED: `LoadFontFromMemory()`, use strncpy() to fix buffer overflow (#3795) by @Mingjie Shen [rtext] REVIEWED: `LoadCodepoints()` returning a freed ptr when count is 0 (#4089) by @Alice Nyaa -[rtext] REVIEWED: `LoadFontData()` avoid fallback glyphs by @Ray -WARNING- +[rtext] REVIEWED: `LoadFontData()` avoid fallback glyphs by @raysan5 -WARNING- [rtext] REVIEWED: `LoadFontData()`, load image only if glyph has been found in font by @raysan5 [rtext] REVIEWED: `ExportFontAsCode()`, fix C++ compiler errors (#4013) by @DarkAssassin23 [rtext] REVIEWED: `MeasureTextEx()` height calculation (#3770) by @Marrony Neris From 4a16dc9b0971f15aae2b6b55c96dd63db427b407 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 16 Mar 2026 17:01:26 +0100 Subject: [PATCH 170/319] Update build_webassembly.yml --- .github/workflows/build_webassembly.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_webassembly.yml b/.github/workflows/build_webassembly.yml index d79b12b1c..2a126ff46 100644 --- a/.github/workflows/build_webassembly.yml +++ b/.github/workflows/build_webassembly.yml @@ -29,7 +29,7 @@ jobs: - name: Setup emsdk uses: mymindstorm/setup-emsdk@v14 with: - version: 3.1.71 + version: 5.0.3 actions-cache-folder: 'emsdk-cache' - name: Setup Release Version From e2aed43410705ae8d00c53e7e83113fa977ff6b5 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 16 Mar 2026 17:03:02 +0100 Subject: [PATCH 171/319] Update examples version to latest `raylib 6.0` --- examples/Makefile | 7 +- examples/Makefile.Web | 23 +- examples/README.md | 75 +-- examples/audio/audio_spectrum_visualizer.c | 2 +- examples/core/core_clipboard_text.c | 2 +- examples/core/core_compute_hash.c | 2 +- examples/core/core_delta_time.c | 2 +- examples/core/core_highdpi_testbed.c | 2 +- examples/core/core_input_gestures_testbed.c | 2 +- examples/core/core_render_texture.c | 2 +- examples/core/core_screen_recording.c | 2 +- examples/examples_list.txt | 69 ++- .../models/models_animation_blend_custom.c | 2 +- examples/models/models_animation_blending.c | 2 +- examples/models/models_animation_timing.c | 2 +- examples/models/models_decals.c | 2 +- .../models/models_directional_billboard.c | 2 +- examples/models/models_rotating_cube.c | 2 +- examples/models/models_tesseract_view.c | 2 +- examples/shaders/shaders_depth_rendering.c | 2 +- examples/shapes/shapes_ball_physics.c | 2 +- examples/shapes/shapes_clock_of_clocks.c | 2 +- examples/shapes/shapes_lines_drawing.c | 2 +- examples/shapes/shapes_math_angle_rotation.c | 2 +- examples/shapes/shapes_math_sine_cosine.c | 2 +- examples/shapes/shapes_penrose_tile.c | 2 +- examples/shapes/shapes_recursive_tree.c | 2 +- examples/shapes/shapes_rlgl_color_wheel.c | 2 +- examples/shapes/shapes_rlgl_triangle.c | 2 +- examples/shapes/shapes_starfield_effect.c | 2 +- examples/shapes/shapes_triangle_strip.c | 2 +- examples/text/text_inline_styling.c | 2 +- examples/text/text_strings_management.c | 2 +- examples/text/text_words_alignment.c | 2 +- examples/textures/textures_clipboard_image.c | 2 +- examples/textures/textures_sprite_stacking.c | 2 +- .../examples/shaders_cel_shading.vcxproj | 569 ++++++++++++++++++ .../examples/textures_clipboard_image.vcxproj | 569 ++++++++++++++++++ .../textures_magnifying_glass.vcxproj | 569 ++++++++++++++++++ projects/VS2022/raylib.sln | 81 +++ tools/rexm/reports/examples_validation.md | 5 +- 41 files changed, 1925 insertions(+), 106 deletions(-) create mode 100644 projects/VS2022/examples/shaders_cel_shading.vcxproj create mode 100644 projects/VS2022/examples/textures_clipboard_image.vcxproj create mode 100644 projects/VS2022/examples/textures_magnifying_glass.vcxproj diff --git a/examples/Makefile b/examples/Makefile index 5ae40af6f..95719672c 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -169,8 +169,8 @@ ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_R 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 + PYTHON_PATH = $(EMSDK_PATH)/python/3.13.3_64bit + NODE_PATH = $(EMSDK_PATH)/node/22.16.0_64bit/bin export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH):$$(PATH) endif endif @@ -622,6 +622,7 @@ TEXTURES = \ textures/textures_blend_modes \ textures/textures_bunnymark \ textures/textures_cellular_automata \ + textures/textures_clipboard_image \ textures/textures_fog_of_war \ textures/textures_framebuffer_rendering \ textures/textures_gif_player \ @@ -634,6 +635,7 @@ TEXTURES = \ textures/textures_image_rotate \ textures/textures_image_text \ textures/textures_logo_raylib \ + textures/textures_magnifying_glass \ textures/textures_mouse_painting \ textures/textures_npatch_drawing \ textures/textures_particles_blending \ @@ -703,6 +705,7 @@ SHADERS = \ shaders/shaders_ascii_rendering \ shaders/shaders_basic_lighting \ shaders/shaders_basic_pbr \ + shaders/shaders_cel_shading \ shaders/shaders_color_correction \ shaders/shaders_custom_uniform \ shaders/shaders_deferred_rendering \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 648e5eef4..4bf635a22 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -168,8 +168,8 @@ ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_R 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 + PYTHON_PATH = $(EMSDK_PATH)/python/3.13.3_64bit + NODE_PATH = $(EMSDK_PATH)/node/22.16.0_64bit/bin export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH):$$(PATH) endif endif @@ -596,6 +596,7 @@ TEXTURES = \ textures/textures_blend_modes \ textures/textures_bunnymark \ textures/textures_cellular_automata \ + textures/textures_clipboard_image \ textures/textures_fog_of_war \ textures/textures_framebuffer_rendering \ textures/textures_gif_player \ @@ -608,6 +609,7 @@ TEXTURES = \ textures/textures_image_rotate \ textures/textures_image_text \ textures/textures_logo_raylib \ + textures/textures_magnifying_glass \ textures/textures_mouse_painting \ textures/textures_npatch_drawing \ textures/textures_particles_blending \ @@ -677,6 +679,7 @@ SHADERS = \ shaders/shaders_ascii_rendering \ shaders/shaders_basic_lighting \ shaders/shaders_basic_pbr \ + shaders/shaders_cel_shading \ shaders/shaders_color_correction \ shaders/shaders_custom_uniform \ shaders/shaders_deferred_rendering \ @@ -1023,6 +1026,9 @@ textures/textures_bunnymark: textures/textures_bunnymark.c textures/textures_cellular_automata: textures/textures_cellular_automata.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +textures/textures_clipboard_image: textures/textures_clipboard_image.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + textures/textures_fog_of_war: textures/textures_fog_of_war.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -1071,6 +1077,11 @@ textures/textures_logo_raylib: textures/textures_logo_raylib.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file textures/resources/raylib_logo.png@resources/raylib_logo.png +textures/textures_magnifying_glass: textures/textures_magnifying_glass.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file textures/resources/raybunny.png@resources/raybunny.png \ + --preload-file textures/resources/parrots.png@resources/parrots.png + textures/textures_mouse_painting: textures/textures_mouse_painting.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -1365,6 +1376,14 @@ shaders/shaders_basic_pbr: shaders/shaders_basic_pbr.c --preload-file shaders/resources/road_mra.png@resources/road_mra.png \ --preload-file shaders/resources/road_n.png@resources/road_n.png +shaders/shaders_cel_shading: shaders/shaders_cel_shading.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file shaders/resources/models/old_car_new.glb@resources/models/old_car_new.glb \ + --preload-file shaders/resources/shaders/glsl100/cel.vs@resources/shaders/glsl100/cel.vs \ + --preload-file shaders/resources/shaders/glsl100/cel.fs@resources/shaders/glsl100/cel.fs \ + --preload-file shaders/resources/shaders/glsl100/outline_hull.vs@resources/shaders/glsl100/outline_hull.vs \ + --preload-file shaders/resources/shaders/glsl100/outline_hull.fs@resources/shaders/glsl100/outline_hull.fs + shaders/shaders_color_correction: shaders/shaders_color_correction.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/parrots.png@resources/parrots.png \ diff --git a/examples/README.md b/examples/README.md index a4e65b9f5..17719be00 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,7 +17,7 @@ You may find it easier to use than other toolchains, especially when it comes to - `zig build [module]` to compile all examples for a module (e.g. `zig build core`) - `zig build [example]` to compile _and run_ a particular example (e.g. `zig build core_basic_window`) -## EXAMPLES COLLECTION [TOTAL: 208] +## EXAMPLES COLLECTION [TOTAL: 211] ### category: core [49] @@ -26,14 +26,14 @@ Examples using raylib [core](../src/rcore.c) module platform functionality: wind | 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_delta_time](core/core_delta_time.c) | core_delta_time | ⭐☆☆☆ | 5.5 | 5.6-dev | [Robin](https://github.com/RobinsAviary) | +| [core_delta_time](core/core_delta_time.c) | core_delta_time | ⭐☆☆☆ | 5.5 | 6.0 | [Robin](https://github.com/RobinsAviary) | | [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_gestures_testbed](core/core_input_gestures_testbed.c) | core_input_gestures_testbed | ⭐⭐⭐☆ | 5.0 | 5.6-dev | [ubkp](https://github.com/ubkp) | +| [core_input_gestures_testbed](core/core_input_gestures_testbed.c) | core_input_gestures_testbed | ⭐⭐⭐☆ | 5.0 | 6.0 | [ubkp](https://github.com/ubkp) | | [core_input_virtual_controls](core/core_input_virtual_controls.c) | core_input_virtual_controls | ⭐⭐☆☆ | 5.0 | 5.0 | [GreenSnakeLinux](https://github.com/GreenSnakeLinux) | | [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) | @@ -62,16 +62,16 @@ Examples using raylib [core](../src/rcore.c) module platform functionality: wind | [core_random_sequence](core/core_random_sequence.c) | core_random_sequence | ⭐☆☆☆ | 5.0 | 5.0 | [Dalton Overmyer](https://github.com/REDl3east) | | [core_automation_events](core/core_automation_events.c) | core_automation_events | ⭐⭐⭐☆ | 5.0 | 5.0 | [Ramon Santamaria](https://github.com/raysan5) | | [core_highdpi_demo](core/core_highdpi_demo.c) | core_highdpi_demo | ⭐⭐☆☆ | 5.0 | 5.5 | [Jonathan Marler](https://github.com/marler8997) | -| [core_render_texture](core/core_render_texture.c) | core_render_texture | ⭐☆☆☆ | 5.6-dev | 5.6-dev | [Ramon Santamaria](https://github.com/raysan5) | +| [core_render_texture](core/core_render_texture.c) | core_render_texture | ⭐☆☆☆ | 6.0 | 6.0 | [Ramon Santamaria](https://github.com/raysan5) | | [core_undo_redo](core/core_undo_redo.c) | core_undo_redo | ⭐⭐⭐☆ | 5.5 | 5.6 | [Ramon Santamaria](https://github.com/raysan5) | | [core_viewport_scaling](core/core_viewport_scaling.c) | core_viewport_scaling | ⭐⭐☆☆ | 5.5 | 5.5 | [Agnis Aldiņš](https://github.com/nezvers) | | [core_input_actions](core/core_input_actions.c) | core_input_actions | ⭐⭐☆☆ | 5.5 | 5.6 | [Jett](https://github.com/JettMonstersGoBoom) | | [core_directory_files](core/core_directory_files.c) | core_directory_files | ⭐☆☆☆ | 5.5 | 5.6 | [Hugo ARNAL](https://github.com/hugoarnal) | -| [core_highdpi_testbed](core/core_highdpi_testbed.c) | core_highdpi_testbed | ⭐☆☆☆ | 5.6-dev | 5.6-dev | [Ramon Santamaria](https://github.com/raysan5) | -| [core_screen_recording](core/core_screen_recording.c) | core_screen_recording | ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Ramon Santamaria](https://github.com/raysan5) | -| [core_clipboard_text](core/core_clipboard_text.c) | core_clipboard_text | ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Ananth S](https://github.com/Ananth1839) | +| [core_highdpi_testbed](core/core_highdpi_testbed.c) | core_highdpi_testbed | ⭐☆☆☆ | 6.0 | 6.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_screen_recording](core/core_screen_recording.c) | core_screen_recording | ⭐⭐☆☆ | 6.0 | 6.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_clipboard_text](core/core_clipboard_text.c) | core_clipboard_text | ⭐⭐☆☆ | 6.0 | 6.0 | [Ananth S](https://github.com/Ananth1839) | | [core_text_file_loading](core/core_text_file_loading.c) | core_text_file_loading | ⭐☆☆☆ | 5.5 | 5.6 | [Aanjishnu Bhattacharyya](https://github.com/NimComPoo-04) | -| [core_compute_hash](core/core_compute_hash.c) | core_compute_hash | ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Ramon Santamaria](https://github.com/raysan5) | +| [core_compute_hash](core/core_compute_hash.c) | core_compute_hash | ⭐⭐☆☆ | 6.0 | 6.0 | [Ramon Santamaria](https://github.com/raysan5) | | [core_keyboard_testbed](core/core_keyboard_testbed.c) | core_keyboard_testbed | ⭐⭐☆☆ | 5.6 | 5.6 | [Ramon Santamaria](https://github.com/raysan5) | | [core_window_web](core/core_window_web.c) | core_window_web | ⭐☆☆☆ | 1.3 | 5.5 | [Ramon Santamaria](https://github.com/raysan5) | @@ -94,7 +94,7 @@ Examples using raylib shapes drawing functionality, provided by raylib [shapes]( | [shapes_easings_ball](shapes/shapes_easings_ball.c) | shapes_easings_ball | ⭐⭐☆☆ | 2.5 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) | | [shapes_easings_box](shapes/shapes_easings_box.c) | shapes_easings_box | ⭐⭐☆☆ | 2.5 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) | | [shapes_easings_rectangles](shapes/shapes_easings_rectangles.c) | shapes_easings_rectangles | ⭐⭐⭐☆ | 2.0 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) | -| [shapes_recursive_tree](shapes/shapes_recursive_tree.c) | shapes_recursive_tree | ⭐⭐⭐☆ | 5.6-dev | 5.6-dev | [Jopestpe](https://github.com/jopestpe) | +| [shapes_recursive_tree](shapes/shapes_recursive_tree.c) | shapes_recursive_tree | ⭐⭐⭐☆ | 6.0 | 6.0 | [Jopestpe](https://github.com/jopestpe) | | [shapes_ring_drawing](shapes/shapes_ring_drawing.c) | shapes_ring_drawing | ⭐⭐⭐☆ | 2.5 | 2.5 | [Vlad Adrian](https://github.com/demizdor) | | [shapes_circle_sector_drawing](shapes/shapes_circle_sector_drawing.c) | shapes_circle_sector_drawing | ⭐⭐⭐☆ | 2.5 | 2.5 | [Vlad Adrian](https://github.com/demizdor) | | [shapes_rounded_rectangle_drawing](shapes/shapes_rounded_rectangle_drawing.c) | shapes_rounded_rectangle_drawing | ⭐⭐⭐☆ | 2.5 | 2.5 | [Vlad Adrian](https://github.com/demizdor) | @@ -104,30 +104,32 @@ Examples using raylib shapes drawing functionality, provided by raylib [shapes]( | [shapes_digital_clock](shapes/shapes_digital_clock.c) | shapes_digital_clock | ⭐⭐⭐⭐️ | 5.5 | 5.6 | [Hamza RAHAL](https://github.com/hmz-rhl) | | [shapes_double_pendulum](shapes/shapes_double_pendulum.c) | shapes_double_pendulum | ⭐⭐☆☆ | 5.5 | 5.5 | [JoeCheong](https://github.com/Joecheong2006) | | [shapes_dashed_line](shapes/shapes_dashed_line.c) | shapes_dashed_line | ⭐☆☆☆ | 5.5 | 5.5 | [Luís Almeida](https://github.com/luis605) | -| [shapes_triangle_strip](shapes/shapes_triangle_strip.c) | shapes_triangle_strip | ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Jopestpe](https://github.com/jopestpe) | +| [shapes_triangle_strip](shapes/shapes_triangle_strip.c) | shapes_triangle_strip | ⭐⭐☆☆ | 6.0 | 6.0 | [Jopestpe](https://github.com/jopestpe) | | [shapes_vector_angle](shapes/shapes_vector_angle.c) | shapes_vector_angle | ⭐⭐☆☆ | 1.0 | 5.0 | [Ramon Santamaria](https://github.com/raysan5) | | [shapes_pie_chart](shapes/shapes_pie_chart.c) | shapes_pie_chart | ⭐⭐⭐☆ | 5.5 | 5.6 | [Gideon Serfontein](https://github.com/GideonSerf) | | [shapes_kaleidoscope](shapes/shapes_kaleidoscope.c) | shapes_kaleidoscope | ⭐⭐☆☆ | 5.5 | 5.6 | [Hugo ARNAL](https://github.com/hugoarnal) | -| [shapes_clock_of_clocks](shapes/shapes_clock_of_clocks.c) | shapes_clock_of_clocks | ⭐⭐☆☆ | 5.5 | 5.6-dev | [JP Mortiboys](https://github.com/themushroompirates) | -| [shapes_math_sine_cosine](shapes/shapes_math_sine_cosine.c) | shapes_math_sine_cosine | ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Jopestpe](https://github.com/jopestpe) | -| [shapes_mouse_trail](shapes/shapes_mouse_trail.c) | shapes_mouse_trail | ⭐☆☆☆ | 5.6 | 5.6-dev | [Balamurugan R](https://github.com/Bala050814) | +| [shapes_clock_of_clocks](shapes/shapes_clock_of_clocks.c) | shapes_clock_of_clocks | ⭐⭐☆☆ | 5.5 | 6.0 | [JP Mortiboys](https://github.com/themushroompirates) | +| [shapes_math_sine_cosine](shapes/shapes_math_sine_cosine.c) | shapes_math_sine_cosine | ⭐⭐☆☆ | 6.0 | 6.0 | [Jopestpe](https://github.com/jopestpe) | +| [shapes_mouse_trail](shapes/shapes_mouse_trail.c) | shapes_mouse_trail | ⭐☆☆☆ | 5.6 | 6.0 | [Balamurugan R](https://github.com/Bala050814) | | [shapes_simple_particles](shapes/shapes_simple_particles.c) | shapes_simple_particles | ⭐⭐☆☆ | 5.6 | 5.6 | [Jordi Santonja](https://github.com/JordSant) | -| [shapes_starfield_effect](shapes/shapes_starfield_effect.c) | shapes_starfield_effect | ⭐⭐☆☆ | 5.5 | 5.6-dev | [JP Mortiboys](https://github.com/themushroompirates) | -| [shapes_lines_drawing](shapes/shapes_lines_drawing.c) | shapes_lines_drawing | ⭐☆☆☆ | 5.6-dev | 5.6 | [Robin](https://github.com/RobinsAviary) | -| [shapes_math_angle_rotation](shapes/shapes_math_angle_rotation.c) | shapes_math_angle_rotation | ⭐☆☆☆ | 5.6-dev | 5.6 | [Kris](https://github.com/krispy-snacc) | -| [shapes_rlgl_color_wheel](shapes/shapes_rlgl_color_wheel.c) | shapes_rlgl_color_wheel | ⭐⭐⭐☆ | 5.6-dev | 5.6-dev | [Robin](https://github.com/RobinsAviary) | -| [shapes_rlgl_triangle](shapes/shapes_rlgl_triangle.c) | shapes_rlgl_triangle | ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Robin](https://github.com/RobinsAviary) | -| [shapes_ball_physics](shapes/shapes_ball_physics.c) | shapes_ball_physics | ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [David Buzatto](https://github.com/davidbuzatto) | -| [shapes_penrose_tile](shapes/shapes_penrose_tile.c) | shapes_penrose_tile | ⭐⭐⭐⭐️ | 5.5 | 5.6-dev | [David Buzatto](https://github.com/davidbuzatto) | +| [shapes_starfield_effect](shapes/shapes_starfield_effect.c) | shapes_starfield_effect | ⭐⭐☆☆ | 5.5 | 6.0 | [JP Mortiboys](https://github.com/themushroompirates) | +| [shapes_lines_drawing](shapes/shapes_lines_drawing.c) | shapes_lines_drawing | ⭐☆☆☆ | 6.0 | 5.6 | [Robin](https://github.com/RobinsAviary) | +| [shapes_math_angle_rotation](shapes/shapes_math_angle_rotation.c) | shapes_math_angle_rotation | ⭐☆☆☆ | 6.0 | 5.6 | [Kris](https://github.com/krispy-snacc) | +| [shapes_rlgl_color_wheel](shapes/shapes_rlgl_color_wheel.c) | shapes_rlgl_color_wheel | ⭐⭐⭐☆ | 6.0 | 6.0 | [Robin](https://github.com/RobinsAviary) | +| [shapes_rlgl_triangle](shapes/shapes_rlgl_triangle.c) | shapes_rlgl_triangle | ⭐⭐☆☆ | 6.0 | 6.0 | [Robin](https://github.com/RobinsAviary) | +| [shapes_ball_physics](shapes/shapes_ball_physics.c) | shapes_ball_physics | ⭐⭐☆☆ | 6.0 | 6.0 | [David Buzatto](https://github.com/davidbuzatto) | +| [shapes_penrose_tile](shapes/shapes_penrose_tile.c) | shapes_penrose_tile | ⭐⭐⭐⭐️ | 5.5 | 6.0 | [David Buzatto](https://github.com/davidbuzatto) | | [shapes_hilbert_curve](shapes/shapes_hilbert_curve.c) | shapes_hilbert_curve | ⭐⭐⭐☆ | 5.6 | 5.6 | [Hamza RAHAL](https://github.com/hmz-rhl) | | [shapes_easings_testbed](shapes/shapes_easings_testbed.c) | shapes_easings_testbed | ⭐⭐⭐☆ | 2.5 | 2.5 | [Juan Miguel López](https://github.com/flashback-fx) | -### category: textures [30] +### category: textures [32] 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 | |-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| +| [textures_clipboard_image](textures/textures_clipboard_image.c) | textures_clipboard_image | ⭐☆☆☆ | 6.0 | 6.0 | [Maicon Santana](https://github.com/maiconpintoabreu) | +| [textures_magnifying_glass](textures/textures_magnifying_glass.c) | textures_magnifying_glass | ⭐⭐⭐☆ | 5.6 | 5.6 | [Luke Vaughan](https://github.com/badram) | | [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) | @@ -155,7 +157,7 @@ Examples using raylib textures functionality, including image/textures loading/g | [textures_image_rotate](textures/textures_image_rotate.c) | textures_image_rotate | ⭐⭐☆☆ | 1.0 | 1.0 | [Ramon Santamaria](https://github.com/raysan5) | | [textures_screen_buffer](textures/textures_screen_buffer.c) | textures_screen_buffer | ⭐⭐☆☆ | 5.5 | 5.5 | [Agnis Aldiņš](https://github.com/nezvers) | | [textures_textured_curve](textures/textures_textured_curve.c) | textures_textured_curve | ⭐⭐⭐☆ | 4.5 | 4.5 | [Jeffery Myers](https://github.com/JeffM2501) | -| [textures_sprite_stacking](textures/textures_sprite_stacking.c) | textures_sprite_stacking | ⭐⭐☆☆ | 5.6-dev | 6.0 | [Robin](https://github.com/RobinsAviary) | +| [textures_sprite_stacking](textures/textures_sprite_stacking.c) | textures_sprite_stacking | ⭐⭐☆☆ | 6.0 | 6.0 | [Robin](https://github.com/RobinsAviary) | | [textures_cellular_automata](textures/textures_cellular_automata.c) | textures_cellular_automata | ⭐⭐☆☆ | 5.6 | 5.6 | [Jordi Santonja](https://github.com/JordSant) | | [textures_framebuffer_rendering](textures/textures_framebuffer_rendering.c) | textures_framebuffer_rendering | ⭐⭐☆☆ | 5.6 | 5.6 | [Jack Boakes](https://github.com/jackboakes) | @@ -178,9 +180,9 @@ Examples using raylib text functionality, including sprite fonts loading/generat | [text_unicode_ranges](text/text_unicode_ranges.c) | text_unicode_ranges | ⭐⭐⭐⭐️ | 5.5 | 5.6 | [Vadim Gunko](https://github.com/GuvaCode) | | [text_3d_drawing](text/text_3d_drawing.c) | text_3d_drawing | ⭐⭐⭐⭐️ | 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) | -| [text_inline_styling](text/text_inline_styling.c) | text_inline_styling | ⭐⭐⭐☆ | 5.6-dev | 5.6-dev | [Wagner Barongello](https://github.com/SultansOfCode) | -| [text_words_alignment](text/text_words_alignment.c) | text_words_alignment | ⭐☆☆☆ | 5.6-dev | 5.6-dev | [JP Mortiboys](https://github.com/themushroompirates) | -| [text_strings_management](text/text_strings_management.c) | text_strings_management | ⭐⭐⭐☆ | 5.6-dev | 5.6-dev | [David Buzatto](https://github.com/davidbuzatto) | +| [text_inline_styling](text/text_inline_styling.c) | text_inline_styling | ⭐⭐⭐☆ | 6.0 | 6.0 | [Wagner Barongello](https://github.com/SultansOfCode) | +| [text_words_alignment](text/text_words_alignment.c) | text_words_alignment | ⭐☆☆☆ | 6.0 | 6.0 | [JP Mortiboys](https://github.com/themushroompirates) | +| [text_strings_management](text/text_strings_management.c) | text_strings_management | ⭐⭐⭐☆ | 6.0 | 6.0 | [David Buzatto](https://github.com/davidbuzatto) | ### category: models [30] @@ -210,16 +212,16 @@ Examples using raylib models functionality, including models loading/generation | [models_textured_cube](models/models_textured_cube.c) | models_textured_cube | ⭐⭐☆☆ | 4.5 | 4.5 | [Ramon Santamaria](https://github.com/raysan5) | | [models_animation_gpu_skinning](models/models_animation_gpu_skinning.c) | models_animation_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-dev | 5.6-dev | [Timothy van der Valk](https://github.com/arceryz) | +| [models_tesseract_view](models/models_tesseract_view.c) | models_tesseract_view | ⭐⭐☆☆ | 6.0 | 6.0 | [Timothy van der Valk](https://github.com/arceryz) | | [models_basic_voxel](models/models_basic_voxel.c) | models_basic_voxel | ⭐⭐☆☆ | 5.5 | 5.5 | [Tim Little](https://github.com/timlittle) | -| [models_rotating_cube](models/models_rotating_cube.c) | models_rotating_cube | ⭐☆☆☆ | 5.6-dev | 5.6-dev | [Jopestpe](https://github.com/jopestpe) | -| [models_decals](models/models_decals.c) | models_decals | ⭐⭐⭐⭐️ | 5.6-dev | 5.6-dev | [JP Mortiboys](https://github.com/themushroompirates) | -| [models_directional_billboard](models/models_directional_billboard.c) | models_directional_billboard | ⭐⭐☆☆ | 5.6-dev | 5.6 | [Robin](https://github.com/RobinsAviary) | -| [models_animation_blend_custom](models/models_animation_blend_custom.c) | models_animation_blend_custom | ⭐⭐⭐⭐️ | 5.5 | 5.5 | [dmitrii-brand](https://github.com/dmitrii-brand) | -| [models_animation_blending](models/models_animation_blending.c) | models_animation_blending | ☆☆☆☆ | 5.5 | 5.6-dev | [Kirandeep](https://github.com/Kirandeep-Singh-Khehra) | -| [models_animation_timming](models/models_animation_timming.c) | models_animation_timming | ⭐⭐☆☆ | 5.6 | 5.6 | [Ramon Santamaria](https://github.com/raysan5) | +| [models_rotating_cube](models/models_rotating_cube.c) | models_rotating_cube | ⭐☆☆☆ | 6.0 | 6.0 | [Jopestpe](https://github.com/jopestpe) | +| [models_decals](models/models_decals.c) | models_decals | ⭐⭐⭐⭐️ | 6.0 | 6.0 | [JP Mortiboys](https://github.com/themushroompirates) | +| [models_directional_billboard](models/models_directional_billboard.c) | models_directional_billboard | ⭐⭐☆☆ | 6.0 | 5.6 | [Robin](https://github.com/RobinsAviary) | +| [models_animation_blend_custom](models/models_animation_blend_custom.c) | models_animation_blend_custom | ⭐⭐⭐⭐️ | 5.5 | 6.0 | [dmitrii-brand](https://github.com/dmitrii-brand) | +| [models_animation_blending](models/models_animation_blending.c) | models_animation_blending | ⭐⭐⭐⭐️ | 5.5 | 6.0 | [Kirandeep](https://github.com/Kirandeep-Singh-Khehra) | +| [models_animation_timing](models/models_animation_timing.c) | models_animation_timing | ⭐⭐⭐☆ | 5.6 | 6.0 | [Ramon Santamaria](https://github.com/raysan5) | -### category: shaders [34] +### category: shaders [35] 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. @@ -256,9 +258,10 @@ Examples using raylib shaders functionality, including shaders loading, paramete | [shaders_basic_pbr](shaders/shaders_basic_pbr.c) | shaders_basic_pbr | ⭐⭐⭐⭐️ | 5.0 | 5.5 | [Afan OLOVCIC](https://github.com/_DevDad) | | [shaders_lightmap_rendering](shaders/shaders_lightmap_rendering.c) | shaders_lightmap_rendering | ⭐⭐⭐☆ | 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_depth_rendering](shaders/shaders_depth_rendering.c) | shaders_depth_rendering | ⭐⭐⭐☆ | 5.6-dev | 5.6-dev | [Luís Almeida](https://github.com/luis605) | +| [shaders_depth_rendering](shaders/shaders_depth_rendering.c) | shaders_depth_rendering | ⭐⭐⭐☆ | 6.0 | 6.0 | [Luís Almeida](https://github.com/luis605) | | [shaders_game_of_life](shaders/shaders_game_of_life.c) | shaders_game_of_life | ⭐⭐⭐☆ | 5.6 | 5.6 | [Jordi Santonja](https://github.com/JordSant) | | [shaders_rlgl_compute](shaders/shaders_rlgl_compute.c) | shaders_rlgl_compute | ⭐⭐⭐⭐️ | 4.0 | 4.0 | [Teddy Astie](https://github.com/tsnake41) | +| [shaders_cel_shading](shaders/shaders_cel_shading.c) | shaders_cel_shading | ⭐⭐⭐☆ | 6.0 | 6.0 | [Gleb A](https://github.com/ggrizzly) | ### category: audio [9] @@ -268,13 +271,13 @@ Examples using raylib audio functionality, including sound/music loading and pla |-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| | [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_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐⭐⭐☆ | 1.6 | 6.0 | [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 | ⭐⭐☆☆ | 5.0 | 5.0 | [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) | -| [audio_spectrum_visualizer](audio/audio_spectrum_visualizer.c) | audio_spectrum_visualizer | ⭐⭐⭐☆ | 6.0 | 5.6-dev | [IANN](https://github.com/meisei4) | +| [audio_spectrum_visualizer](audio/audio_spectrum_visualizer.c) | audio_spectrum_visualizer | ⭐⭐⭐☆ | 6.0 | 6.0 | [IANN](https://github.com/meisei4) | 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! diff --git a/examples/audio/audio_spectrum_visualizer.c b/examples/audio/audio_spectrum_visualizer.c index d2dbbbb57..f5ffe2f2d 100644 --- a/examples/audio/audio_spectrum_visualizer.c +++ b/examples/audio/audio_spectrum_visualizer.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★★☆] 3/4 * -* Example originally created with raylib 6.0, last time updated with raylib 5.6-dev +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Inspired by Inigo Quilez's https://www.shadertoy.com/ * Resources/specification: https://gist.github.com/soulthreads/2efe50da4be1fb5f7ab60ff14ca434b8 diff --git a/examples/core/core_clipboard_text.c b/examples/core/core_clipboard_text.c index 59de6509c..6ced65654 100644 --- a/examples/core/core_clipboard_text.c +++ b/examples/core/core_clipboard_text.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★☆☆] 2/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by Ananth S (@Ananth1839) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/core/core_compute_hash.c b/examples/core/core_compute_hash.c index f7d3ba9ac..890c14620 100644 --- a/examples/core/core_compute_hash.c +++ b/examples/core/core_compute_hash.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★☆☆] 2/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software diff --git a/examples/core/core_delta_time.c b/examples/core/core_delta_time.c index b77957ed6..26b0d13b1 100644 --- a/examples/core/core_delta_time.c +++ b/examples/core/core_delta_time.c @@ -4,7 +4,7 @@ * * 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 6.0 * * Example contributed by Robin (@RobinsAviary) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/core/core_highdpi_testbed.c b/examples/core/core_highdpi_testbed.c index cfff1f3c5..601d12e8c 100644 --- a/examples/core/core_highdpi_testbed.c +++ b/examples/core/core_highdpi_testbed.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★☆☆☆] 1/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by Ramon Santamaria (@raysan5) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/core/core_input_gestures_testbed.c b/examples/core/core_input_gestures_testbed.c index e0ffeb13f..6cb09d0d2 100644 --- a/examples/core/core_input_gestures_testbed.c +++ b/examples/core/core_input_gestures_testbed.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★★☆] 3/4 * -* Example originally created with raylib 5.0, last time updated with raylib 5.6-dev +* Example originally created with raylib 5.0, last time updated with raylib 6.0 * * Example contributed by ubkp (@ubkp) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/core/core_render_texture.c b/examples/core/core_render_texture.c index e30220a73..b11237ebc 100644 --- a/examples/core/core_render_texture.c +++ b/examples/core/core_render_texture.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★☆☆☆] 1/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software diff --git a/examples/core/core_screen_recording.c b/examples/core/core_screen_recording.c index 633ddcf81..33de31a5a 100644 --- a/examples/core/core_screen_recording.c +++ b/examples/core/core_screen_recording.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★☆☆] 2/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software diff --git a/examples/examples_list.txt b/examples/examples_list.txt index dc3312893..0fe453785 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -10,14 +10,14 @@ # This list is used as the main reference by [rexm] tool for examples collection validation and management # core;core_basic_window;★☆☆☆;1.0;1.0;2013;2025;"Ramon Santamaria";@raysan5 -core;core_delta_time;★☆☆☆;5.5;5.6-dev;2025;2025;"Robin";@RobinsAviary +core;core_delta_time;★☆☆☆;5.5;6.0;2025;2025;"Robin";@RobinsAviary core;core_input_keys;★☆☆☆;1.0;1.0;2014;2025;"Ramon Santamaria";@raysan5 core;core_input_mouse;★☆☆☆;1.0;5.5;2014;2025;"Ramon Santamaria";@raysan5 core;core_input_mouse_wheel;★☆☆☆;1.1;1.3;2014;2025;"Ramon Santamaria";@raysan5 core;core_input_gamepad;★☆☆☆;1.1;4.2;2013;2025;"Ramon Santamaria";@raysan5 core;core_input_multitouch;★☆☆☆;2.1;2.5;2019;2025;"Berni";@Berni8k core;core_input_gestures;★★☆☆;1.4;4.2;2016;2025;"Ramon Santamaria";@raysan5 -core;core_input_gestures_testbed;★★★☆;5.0;5.6-dev;2023;2025;"ubkp";@ubkp +core;core_input_gestures_testbed;★★★☆;5.0;6.0;2023;2025;"ubkp";@ubkp core;core_input_virtual_controls;★★☆☆;5.0;5.0;2024;2025;"GreenSnakeLinux";@GreenSnakeLinux core;core_2d_camera;★★☆☆;1.5;3.0;2016;2025;"Ramon Santamaria";@raysan5 core;core_2d_camera_mouse_zoom;★★☆☆;4.2;4.2;2022;2025;"Jeffery Myers";@JeffM2501 @@ -46,16 +46,16 @@ core;core_smooth_pixelperfect;★★★☆;3.7;4.0;2021;2025;"Giancamillo Alessa core;core_random_sequence;★☆☆☆;5.0;5.0;2023;2025;"Dalton Overmyer";@REDl3east core;core_automation_events;★★★☆;5.0;5.0;2023;2025;"Ramon Santamaria";@raysan5 core;core_highdpi_demo;★★☆☆;5.0;5.5;2025;2025;"Jonathan Marler";@marler8997 -core;core_render_texture;★☆☆☆;5.6-dev;5.6-dev;2025;2025;"Ramon Santamaria";@raysan5 +core;core_render_texture;★☆☆☆;6.0;6.0;2025;2025;"Ramon Santamaria";@raysan5 core;core_undo_redo;★★★☆;5.5;5.6;2025;2025;"Ramon Santamaria";@raysan5 core;core_viewport_scaling;★★☆☆;5.5;5.5;2025;2025;"Agnis Aldiņš";@nezvers core;core_input_actions;★★☆☆;5.5;5.6;2025;2025;"Jett";@JettMonstersGoBoom core;core_directory_files;★☆☆☆;5.5;5.6;2025;2025;"Hugo ARNAL";@hugoarnal -core;core_highdpi_testbed;★☆☆☆;5.6-dev;5.6-dev;2025;2025;"Ramon Santamaria";@raysan5 -core;core_screen_recording;★★☆☆;5.6-dev;5.6-dev;2025;2025;"Ramon Santamaria";@raysan5 -core;core_clipboard_text;★★☆☆;5.6-dev;5.6-dev;2025;2025;"Ananth S";@Ananth1839 +core;core_highdpi_testbed;★☆☆☆;6.0;6.0;2025;2025;"Ramon Santamaria";@raysan5 +core;core_screen_recording;★★☆☆;6.0;6.0;2025;2025;"Ramon Santamaria";@raysan5 +core;core_clipboard_text;★★☆☆;6.0;6.0;2025;2025;"Ananth S";@Ananth1839 core;core_text_file_loading;★☆☆☆;5.5;5.6;0;0;"Aanjishnu Bhattacharyya";@NimComPoo-04 -core;core_compute_hash;★★☆☆;5.6-dev;5.6-dev;2025;2025;"Ramon Santamaria";@raysan5 +core;core_compute_hash;★★☆☆;6.0;6.0;2025;2025;"Ramon Santamaria";@raysan5 core;core_keyboard_testbed;★★☆☆;5.6;5.6;2026;2026;"Ramon Santamaria";@raysan5 core;core_window_web;★☆☆☆;1.3;5.5;2015;2025;"Ramon Santamaria";@raysan5 shapes;shapes_basic_shapes;★☆☆☆;1.0;4.2;2014;2025;"Ramon Santamaria";@raysan5 @@ -71,7 +71,7 @@ shapes;shapes_following_eyes;★★☆☆;2.5;2.5;2013;2025;"Ramon Santamaria";@ shapes;shapes_easings_ball;★★☆☆;2.5;2.5;2014;2025;"Ramon Santamaria";@raysan5 shapes;shapes_easings_box;★★☆☆;2.5;2.5;2014;2025;"Ramon Santamaria";@raysan5 shapes;shapes_easings_rectangles;★★★☆;2.0;2.5;2014;2025;"Ramon Santamaria";@raysan5 -shapes;shapes_recursive_tree;★★★☆;5.6-dev;5.6-dev;2025;2025;"Jopestpe";@jopestpe +shapes;shapes_recursive_tree;★★★☆;6.0;6.0;2025;2025;"Jopestpe";@jopestpe shapes;shapes_ring_drawing;★★★☆;2.5;2.5;2018;2025;"Vlad Adrian";@demizdor shapes;shapes_circle_sector_drawing;★★★☆;2.5;2.5;2018;2025;"Vlad Adrian";@demizdor shapes;shapes_rounded_rectangle_drawing;★★★☆;2.5;2.5;2018;2025;"Vlad Adrian";@demizdor @@ -81,23 +81,25 @@ shapes;shapes_splines_drawing;★★★☆;5.0;5.0;2023;2025;"Ramon Santamaria"; shapes;shapes_digital_clock;★★★★;5.5;5.6;2025;2025;"Hamza RAHAL";@hmz-rhl shapes;shapes_double_pendulum;★★☆☆;5.5;5.5;2025;2025;"JoeCheong";@Joecheong2006 shapes;shapes_dashed_line;★☆☆☆;5.5;5.5;2025;2025;"Luís Almeida";@luis605 -shapes;shapes_triangle_strip;★★☆☆;5.6-dev;5.6-dev;2025;2025;"Jopestpe";@jopestpe +shapes;shapes_triangle_strip;★★☆☆;6.0;6.0;2025;2025;"Jopestpe";@jopestpe shapes;shapes_vector_angle;★★☆☆;1.0;5.0;2023;2025;"Ramon Santamaria";@raysan5 shapes;shapes_pie_chart;★★★☆;5.5;5.6;2025;2025;"Gideon Serfontein";@GideonSerf shapes;shapes_kaleidoscope;★★☆☆;5.5;5.6;2025;2025;"Hugo ARNAL";@hugoarnal -shapes;shapes_clock_of_clocks;★★☆☆;5.5;5.6-dev;2025;2025;"JP Mortiboys";@themushroompirates -shapes;shapes_math_sine_cosine;★★☆☆;5.6-dev;5.6-dev;2025;2025;"Jopestpe";@jopestpe -shapes;shapes_mouse_trail;★☆☆☆;5.6;5.6-dev;2025;2025;"Balamurugan R";@Bala050814 +shapes;shapes_clock_of_clocks;★★☆☆;5.5;6.0;2025;2025;"JP Mortiboys";@themushroompirates +shapes;shapes_math_sine_cosine;★★☆☆;6.0;6.0;2025;2025;"Jopestpe";@jopestpe +shapes;shapes_mouse_trail;★☆☆☆;5.6;6.0;2025;2025;"Balamurugan R";@Bala050814 shapes;shapes_simple_particles;★★☆☆;5.6;5.6;2025;2025;"Jordi Santonja";@JordSant -shapes;shapes_starfield_effect;★★☆☆;5.5;5.6-dev;2025;2025;"JP Mortiboys";@themushroompirates -shapes;shapes_lines_drawing;★☆☆☆;5.6-dev;5.6;2025;2025;"Robin";@RobinsAviary -shapes;shapes_math_angle_rotation;★☆☆☆;5.6-dev;5.6;2025;2025;"Kris";@krispy-snacc -shapes;shapes_rlgl_color_wheel;★★★☆;5.6-dev;5.6-dev;2025;2025;"Robin";@RobinsAviary -shapes;shapes_rlgl_triangle;★★☆☆;5.6-dev;5.6-dev;2025;2025;"Robin";@RobinsAviary -shapes;shapes_ball_physics;★★☆☆;5.6-dev;5.6-dev;2025;2025;"David Buzatto";@davidbuzatto -shapes;shapes_penrose_tile;★★★★;5.5;5.6-dev;2025;2025;"David Buzatto";@davidbuzatto +shapes;shapes_starfield_effect;★★☆☆;5.5;6.0;2025;2025;"JP Mortiboys";@themushroompirates +shapes;shapes_lines_drawing;★☆☆☆;6.0;5.6;2025;2025;"Robin";@RobinsAviary +shapes;shapes_math_angle_rotation;★☆☆☆;6.0;5.6;2025;2025;"Kris";@krispy-snacc +shapes;shapes_rlgl_color_wheel;★★★☆;6.0;6.0;2025;2025;"Robin";@RobinsAviary +shapes;shapes_rlgl_triangle;★★☆☆;6.0;6.0;2025;2025;"Robin";@RobinsAviary +shapes;shapes_ball_physics;★★☆☆;6.0;6.0;2025;2025;"David Buzatto";@davidbuzatto +shapes;shapes_penrose_tile;★★★★;5.5;6.0;2025;2025;"David Buzatto";@davidbuzatto shapes;shapes_hilbert_curve;★★★☆;5.6;5.6;2025;2025;"Hamza RAHAL";@hmz-rhl shapes;shapes_easings_testbed;★★★☆;2.5;2.5;2019;2025;"Juan Miguel López";@flashback-fx +textures;textures_clipboard_image;★☆☆☆;6.0;6.0;2026;2026;"Maicon Santana";@maiconpintoabreu +textures;textures_magnifying_glass;★★★☆;5.6;5.6;2026;2026;"Luke Vaughan";@badram textures;textures_logo_raylib;★☆☆☆;1.0;1.0;2014;2025;"Ramon Santamaria";@raysan5 textures;textures_srcrec_dstrec;★★★☆;1.3;1.3;2015;2025;"Ramon Santamaria";@raysan5 textures;textures_image_drawing;★★☆☆;1.4;1.4;2016;2025;"Ramon Santamaria";@raysan5 @@ -125,7 +127,7 @@ textures;textures_image_channel;★★☆☆;5.5;5.5;2024;2025;"Bruno Cabral";@b textures;textures_image_rotate;★★☆☆;1.0;1.0;2014;2025;"Ramon Santamaria";@raysan5 textures;textures_screen_buffer;★★☆☆;5.5;5.5;2025;2025;"Agnis Aldiņš";@nezvers textures;textures_textured_curve;★★★☆;4.5;4.5;2022;2025;"Jeffery Myers";@JeffM2501 -textures;textures_sprite_stacking;★★☆☆;5.6-dev;6.0;2025;2025;"Robin";@RobinsAviary +textures;textures_sprite_stacking;★★☆☆;6.0;6.0;2025;2025;"Robin";@RobinsAviary textures;textures_cellular_automata;★★☆☆;5.6;5.6;2025;2025;"Jordi Santonja";@JordSant textures;textures_framebuffer_rendering;★★☆☆;5.6;5.6;2026;2026;"Jack Boakes";@jackboakes text;text_sprite_fonts;★☆☆☆;1.7;3.7;2017;2025;"Ramon Santamaria";@raysan5 @@ -141,9 +143,9 @@ text;text_unicode_emojis;★★★★;2.5;4.0;2019;2025;"Vlad Adrian";@demizdor text;text_unicode_ranges;★★★★;5.5;5.6;2025;2025;"Vadim Gunko";@GuvaCode text;text_3d_drawing;★★★★;3.5;4.0;2021;2025;"Vlad Adrian";@demizdor text;text_codepoints_loading;★★★☆;4.2;4.2;2022;2025;"Ramon Santamaria";@raysan5 -text;text_inline_styling;★★★☆;5.6-dev;5.6-dev;2025;2025;"Wagner Barongello";@SultansOfCode -text;text_words_alignment;★☆☆☆;5.6-dev;5.6-dev;2025;2025;"JP Mortiboys";@themushroompirates -text;text_strings_management;★★★☆;5.6-dev;5.6-dev;2025;2025;"David Buzatto";@davidbuzatto +text;text_inline_styling;★★★☆;6.0;6.0;2025;2025;"Wagner Barongello";@SultansOfCode +text;text_words_alignment;★☆☆☆;6.0;6.0;2025;2025;"JP Mortiboys";@themushroompirates +text;text_strings_management;★★★☆;6.0;6.0;2025;2025;"David Buzatto";@davidbuzatto models;models_loading_iqm;★★☆☆;2.5;3.5;2019;2025;"Culacant";@culacant models;models_billboard_rendering;★★★☆;1.3;3.5;2015;2025;"Ramon Santamaria";@raysan5 models;models_box_collisions;★☆☆☆;1.3;3.5;2015;2025;"Ramon Santamaria";@raysan5 @@ -166,14 +168,14 @@ models;models_skybox_rendering;★★☆☆;1.8;4.0;2017;2025;"Ramon Santamaria" models;models_textured_cube;★★☆☆;4.5;4.5;2022;2025;"Ramon Santamaria";@raysan5 models;models_animation_gpu_skinning;★★★☆;4.5;4.5;2024;2025;"Daniel Holden";@orangeduck models;models_bone_socket;★★★★;4.5;4.5;2024;2025;"iP";@ipzaur -models;models_tesseract_view;★★☆☆;5.6-dev;5.6-dev;2024;2025;"Timothy van der Valk";@arceryz +models;models_tesseract_view;★★☆☆;6.0;6.0;2024;2025;"Timothy van der Valk";@arceryz models;models_basic_voxel;★★☆☆;5.5;5.5;2025;2025;"Tim Little";@timlittle -models;models_rotating_cube;★☆☆☆;5.6-dev;5.6-dev;2025;2025;"Jopestpe";@jopestpe -models;models_decals;★★★★;5.6-dev;5.6-dev;2025;2025;"JP Mortiboys";@themushroompirates -models;models_directional_billboard;★★☆☆;5.6-dev;5.6;2025;2025;"Robin";@RobinsAviary -models;models_animation_blend_custom;★★★★;5.5;5.5;2026;2026;"dmitrii-brand";@dmitrii-brand -models;models_animation_blending;☆☆☆☆;5.5;5.6-dev;2024;2024;"Kirandeep";@Kirandeep-Singh-Khehra -models;models_animation_timing;★★☆☆;5.6;5.6;2026;2026;"Ramon Santamaria";@raysan5 +models;models_rotating_cube;★☆☆☆;6.0;6.0;2025;2025;"Jopestpe";@jopestpe +models;models_decals;★★★★;6.0;6.0;2025;2025;"JP Mortiboys";@themushroompirates +models;models_directional_billboard;★★☆☆;6.0;5.6;2025;2025;"Robin";@RobinsAviary +models;models_animation_blend_custom;★★★★;5.5;6.0;2026;2026;"dmitrii-brand";@dmitrii-brand +models;models_animation_blending;★★★★;5.5;6.0;2024;2026;"Kirandeep";@Kirandeep-Singh-Khehra +models;models_animation_timing;★★★☆;5.6;6.0;2026;2026;"Ramon Santamaria";@raysan5 shaders;shaders_ascii_rendering;★★☆☆;5.5;5.6;2025;2025;"Maicon Santana";@maiconpintoabreu shaders;shaders_basic_lighting;★★★★;3.0;4.2;2019;2025;"Chris Camacho";@chriscamacho shaders;shaders_model_shader;★★☆☆;1.3;3.7;2014;2025;"Ramon Santamaria";@raysan5 @@ -205,15 +207,16 @@ shaders;shaders_depth_writing;★★☆☆;4.2;4.2;2022;2025;"Buğra Alptekin Sa shaders;shaders_basic_pbr;★★★★;5.0;5.5;2023;2025;"Afan OLOVCIC";@_DevDad shaders;shaders_lightmap_rendering;★★★☆;4.5;4.5;2019;2025;"Jussi Viitala";@nullstare shaders;shaders_rounded_rectangle;★★★☆;5.5;5.5;2025;2025;"Anstro Pleuton";@anstropleuton -shaders;shaders_depth_rendering;★★★☆;5.6-dev;5.6-dev;2025;2025;"Luís Almeida";@luis605 +shaders;shaders_depth_rendering;★★★☆;6.0;6.0;2025;2025;"Luís Almeida";@luis605 shaders;shaders_game_of_life;★★★☆;5.6;5.6;2025;2025;"Jordi Santonja";@JordSant shaders;shaders_rlgl_compute;★★★★;4.0;4.0;2021;2025;"Teddy Astie";@tsnake41 +shaders;shaders_cel_shading;★★★☆;6.0;6.0;2026;2026;"Gleb A";@ggrizzly audio;audio_module_playing;★☆☆☆;1.5;3.5;2016;2025;"Ramon Santamaria";@raysan5 audio;audio_music_stream;★☆☆☆;1.3;4.2;2015;2025;"Ramon Santamaria";@raysan5 -audio;audio_raw_stream;★★★☆;1.6;4.2;2015;2025;"Ramon Santamaria";@raysan5 +audio;audio_raw_stream;★★★☆;1.6;6.0;2015;2026;"Ramon Santamaria";@raysan5 audio;audio_sound_loading;★☆☆☆;1.1;3.5;2014;2025;"Ramon Santamaria";@raysan5 audio;audio_mixed_processor;★★★★;4.2;4.2;2023;2025;"hkc";@hatkidchan audio;audio_stream_effects;★★★★;4.2;5.0;2022;2025;"Ramon Santamaria";@raysan5 audio;audio_sound_multi;★★☆☆;5.0;5.0;2023;2025;"Jeffery Myers";@JeffM2501 audio;audio_sound_positioning;★★☆☆;5.5;5.5;2025;2025;"Le Juez Victor";@Bigfoot71 -audio;audio_spectrum_visualizer;★★★☆;6.0;5.6-dev;2025;2025;"IANN";@meisei4 +audio;audio_spectrum_visualizer;★★★☆;6.0;6.0;2025;2025;"IANN";@meisei4 diff --git a/examples/models/models_animation_blend_custom.c b/examples/models/models_animation_blend_custom.c index 005982ec6..8262f6ac2 100644 --- a/examples/models/models_animation_blend_custom.c +++ b/examples/models/models_animation_blend_custom.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★★★] 4/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 6.0 * * Example contributed by dmitrii-brand (@dmitrii-brand) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/models/models_animation_blending.c b/examples/models/models_animation_blending.c index e44583299..e79067c3d 100644 --- a/examples/models/models_animation_blending.c +++ b/examples/models/models_animation_blending.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★★★] 4/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 6.0 * * Example contributed by Kirandeep (@Kirandeep-Singh-Khehra) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/models/models_animation_timing.c b/examples/models/models_animation_timing.c index 030ad81e1..52d262957 100644 --- a/examples/models/models_animation_timing.c +++ b/examples/models/models_animation_timing.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★★☆] 3/4 * -* Example originally created with raylib 5.6, last time updated with raylib 5.6 +* Example originally created with raylib 5.6, last time updated with raylib 6.0 * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software diff --git a/examples/models/models_decals.c b/examples/models/models_decals.c index 9eba33de6..6e80283f2 100644 --- a/examples/models/models_decals.c +++ b/examples/models/models_decals.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★★★] 4/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by JP Mortiboys (@themushroompirates) and reviewed by Ramon Santamaria (@raysan5) * Based on previous work by @mrdoob diff --git a/examples/models/models_directional_billboard.c b/examples/models/models_directional_billboard.c index caab94afb..390616fa2 100644 --- a/examples/models/models_directional_billboard.c +++ b/examples/models/models_directional_billboard.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★☆☆] 2/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6 +* Example originally created with raylib 6.0, last time updated with raylib 5.6 * * Example contributed by Robin (@RobinsAviary) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/models/models_rotating_cube.c b/examples/models/models_rotating_cube.c index c5b633fea..7cfca1620 100644 --- a/examples/models/models_rotating_cube.c +++ b/examples/models/models_rotating_cube.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★☆☆☆] 1/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by Jopestpe (@jopestpe) * diff --git a/examples/models/models_tesseract_view.c b/examples/models/models_tesseract_view.c index 1544873fb..9829d63e3 100644 --- a/examples/models/models_tesseract_view.c +++ b/examples/models/models_tesseract_view.c @@ -6,7 +6,7 @@ * * Example complexity rating: [★★☆☆] 2/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by Timothy van der Valk (@arceryz) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/shaders/shaders_depth_rendering.c b/examples/shaders/shaders_depth_rendering.c index 866135020..0bec4eb35 100644 --- a/examples/shaders/shaders_depth_rendering.c +++ b/examples/shaders/shaders_depth_rendering.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★★☆] 3/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by Luís Almeida (@luis605) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/shapes/shapes_ball_physics.c b/examples/shapes/shapes_ball_physics.c index b790c7e90..f01a7c8e1 100644 --- a/examples/shapes/shapes_ball_physics.c +++ b/examples/shapes/shapes_ball_physics.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★☆☆] 2/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by David Buzatto (@davidbuzatto) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/shapes/shapes_clock_of_clocks.c b/examples/shapes/shapes_clock_of_clocks.c index d87ec6411..ad87411f9 100644 --- a/examples/shapes/shapes_clock_of_clocks.c +++ b/examples/shapes/shapes_clock_of_clocks.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★☆☆] 2/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 6.0 * * Example contributed by JP Mortiboys (@themushroompirates) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/shapes/shapes_lines_drawing.c b/examples/shapes/shapes_lines_drawing.c index 76e81abb5..c3ea0fc7b 100644 --- a/examples/shapes/shapes_lines_drawing.c +++ b/examples/shapes/shapes_lines_drawing.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★☆☆☆] 1/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6 +* Example originally created with raylib 6.0, last time updated with raylib 5.6 * * Example contributed by Robin (@RobinsAviary) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/shapes/shapes_math_angle_rotation.c b/examples/shapes/shapes_math_angle_rotation.c index 63c9aaa91..bf7e84b86 100644 --- a/examples/shapes/shapes_math_angle_rotation.c +++ b/examples/shapes/shapes_math_angle_rotation.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★☆☆☆] 1/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6 +* Example originally created with raylib 6.0, last time updated with raylib 5.6 * * Example contributed by Kris (@krispy-snacc) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/shapes/shapes_math_sine_cosine.c b/examples/shapes/shapes_math_sine_cosine.c index c9964a9ff..7ffa1c217 100644 --- a/examples/shapes/shapes_math_sine_cosine.c +++ b/examples/shapes/shapes_math_sine_cosine.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★☆☆] 2/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by Jopestpe (@jopestpe) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/shapes/shapes_penrose_tile.c b/examples/shapes/shapes_penrose_tile.c index f94eae763..0221a10b3 100644 --- a/examples/shapes/shapes_penrose_tile.c +++ b/examples/shapes/shapes_penrose_tile.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★★★] 4/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 6.0 * Based on: https://processing.org/examples/penrosetile.html * * Example contributed by David Buzatto (@davidbuzatto) and reviewed by Ramon Santamaria (@raysan5) diff --git a/examples/shapes/shapes_recursive_tree.c b/examples/shapes/shapes_recursive_tree.c index f15758773..3977eecc6 100644 --- a/examples/shapes/shapes_recursive_tree.c +++ b/examples/shapes/shapes_recursive_tree.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★★☆] 3/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by Jopestpe (@jopestpe) * diff --git a/examples/shapes/shapes_rlgl_color_wheel.c b/examples/shapes/shapes_rlgl_color_wheel.c index f02226a83..a03dcf2de 100644 --- a/examples/shapes/shapes_rlgl_color_wheel.c +++ b/examples/shapes/shapes_rlgl_color_wheel.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★★☆] 3/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by Robin (@RobinsAviary) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/shapes/shapes_rlgl_triangle.c b/examples/shapes/shapes_rlgl_triangle.c index 56cdc43bc..30920131c 100644 --- a/examples/shapes/shapes_rlgl_triangle.c +++ b/examples/shapes/shapes_rlgl_triangle.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★☆☆] 2/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by Robin (@RobinsAviary) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/shapes/shapes_starfield_effect.c b/examples/shapes/shapes_starfield_effect.c index 484f4fb78..f0d5d57e1 100644 --- a/examples/shapes/shapes_starfield_effect.c +++ b/examples/shapes/shapes_starfield_effect.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★☆☆] 2/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 6.0 * * Example contributed by JP Mortiboys (@themushroompirates) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/shapes/shapes_triangle_strip.c b/examples/shapes/shapes_triangle_strip.c index de712270c..aef0c0291 100644 --- a/examples/shapes/shapes_triangle_strip.c +++ b/examples/shapes/shapes_triangle_strip.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★☆☆] 2/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by Jopestpe (@jopestpe) * diff --git a/examples/text/text_inline_styling.c b/examples/text/text_inline_styling.c index 8634ccc19..2d2b5e135 100644 --- a/examples/text/text_inline_styling.c +++ b/examples/text/text_inline_styling.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★★☆] 3/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by Wagner Barongello (@SultansOfCode) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/text/text_strings_management.c b/examples/text/text_strings_management.c index db4540081..60f0941f3 100644 --- a/examples/text/text_strings_management.c +++ b/examples/text/text_strings_management.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★★☆] 3/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by David Buzatto (@davidbuzatto) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/text/text_words_alignment.c b/examples/text/text_words_alignment.c index 8017f33eb..f0ebc78a5 100644 --- a/examples/text/text_words_alignment.c +++ b/examples/text/text_words_alignment.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★☆☆☆] 1/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by JP Mortiboys (@themushroompirates) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/textures/textures_clipboard_image.c b/examples/textures/textures_clipboard_image.c index aa955da2e..1e9ec0846 100644 --- a/examples/textures/textures_clipboard_image.c +++ b/examples/textures/textures_clipboard_image.c @@ -34,7 +34,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [textures] example - clipboard_image"); + InitWindow(screenWidth, screenHeight, "raylib [textures] example - clipboard image"); TextureCollection collection[MAX_TEXTURE_COLLECTION] = { 0 }; int currentCollectionIndex = 0; diff --git a/examples/textures/textures_sprite_stacking.c b/examples/textures/textures_sprite_stacking.c index b20474711..671e82461 100644 --- a/examples/textures/textures_sprite_stacking.c +++ b/examples/textures/textures_sprite_stacking.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★☆☆] 2/4 * -* Example originally created with raylib 5.6-dev, last time updated with raylib 6.0 +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by Robin (@RobinsAviary) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/projects/VS2022/examples/shaders_cel_shading.vcxproj b/projects/VS2022/examples/shaders_cel_shading.vcxproj new file mode 100644 index 000000000..c37636be1 --- /dev/null +++ b/projects/VS2022/examples/shaders_cel_shading.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 + + + + {D5556782-5113-403C-8E7A-2417B8CD0CC6} + Win32Proj + shaders_cel_shading + 10.0 + shaders_cel_shading + + + + 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/textures_clipboard_image.vcxproj b/projects/VS2022/examples/textures_clipboard_image.vcxproj new file mode 100644 index 000000000..ed2c667a2 --- /dev/null +++ b/projects/VS2022/examples/textures_clipboard_image.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 + + + + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654} + Win32Proj + textures_clipboard_image + 10.0 + textures_clipboard_image + + + + 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_magnifying_glass.vcxproj b/projects/VS2022/examples/textures_magnifying_glass.vcxproj new file mode 100644 index 000000000..3abbd2d94 --- /dev/null +++ b/projects/VS2022/examples/textures_magnifying_glass.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 + + + + {099441CA-5A99-4E50-AA2E-EB397EF0A50A} + Win32Proj + textures_magnifying_glass + 10.0 + textures_magnifying_glass + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 51782892c..7d10de904 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -437,6 +437,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_window_web", "examples EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_timing", "examples\models_animation_timing.vcxproj", "{89D5A0E9-683C-465C-BF85-A880865175C8}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_magnifying_glass", "examples\textures_magnifying_glass.vcxproj", "{099441CA-5A99-4E50-AA2E-EB397EF0A50A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_clipboard_image", "examples\textures_clipboard_image.vcxproj", "{6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_cel_shading", "examples\shaders_cel_shading.vcxproj", "{D5556782-5113-403C-8E7A-2417B8CD0CC6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|ARM64 = Debug.DLL|ARM64 @@ -5469,6 +5475,78 @@ Global {89D5A0E9-683C-465C-BF85-A880865175C8}.Release|x64.Build.0 = Release|x64 {89D5A0E9-683C-465C-BF85-A880865175C8}.Release|x86.ActiveCfg = Release|Win32 {89D5A0E9-683C-465C-BF85-A880865175C8}.Release|x86.Build.0 = Release|Win32 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Debug|ARM64.Build.0 = Debug|ARM64 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Debug|x64.ActiveCfg = Debug|x64 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Debug|x64.Build.0 = Debug|x64 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Debug|x86.ActiveCfg = Debug|Win32 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Debug|x86.Build.0 = Debug|Win32 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Release|ARM64.ActiveCfg = Release|ARM64 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Release|ARM64.Build.0 = Release|ARM64 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Release|x64.ActiveCfg = Release|x64 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Release|x64.Build.0 = Release|x64 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Release|x86.ActiveCfg = Release|Win32 + {099441CA-5A99-4E50-AA2E-EB397EF0A50A}.Release|x86.Build.0 = Release|Win32 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Debug|ARM64.Build.0 = Debug|ARM64 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Debug|x64.ActiveCfg = Debug|x64 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Debug|x64.Build.0 = Debug|x64 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Debug|x86.ActiveCfg = Debug|Win32 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Debug|x86.Build.0 = Debug|Win32 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Release|ARM64.ActiveCfg = Release|ARM64 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Release|ARM64.Build.0 = Release|ARM64 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Release|x64.ActiveCfg = Release|x64 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Release|x64.Build.0 = Release|x64 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Release|x86.ActiveCfg = Release|Win32 + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654}.Release|x86.Build.0 = Release|Win32 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Debug|ARM64.Build.0 = Debug|ARM64 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Debug|x64.ActiveCfg = Debug|x64 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Debug|x64.Build.0 = Debug|x64 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Debug|x86.ActiveCfg = Debug|Win32 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Debug|x86.Build.0 = Debug|Win32 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Release|ARM64.ActiveCfg = Release|ARM64 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Release|ARM64.Build.0 = Release|ARM64 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Release|x64.ActiveCfg = Release|x64 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Release|x64.Build.0 = Release|x64 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Release|x86.ActiveCfg = Release|Win32 + {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -5689,6 +5767,9 @@ Global {BB9C957D-34F1-46AE-B64A-9E0499C1746D} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {89D5A0E9-683C-465C-BF85-A880865175C8} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {099441CA-5A99-4E50-AA2E-EB397EF0A50A} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {D5556782-5113-403C-8E7A-2417B8CD0CC6} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} diff --git a/tools/rexm/reports/examples_validation.md b/tools/rexm/reports/examples_validation.md index e46b93e46..25e32779a 100644 --- a/tools/rexm/reports/examples_validation.md +++ b/tools/rexm/reports/examples_validation.md @@ -109,6 +109,8 @@ Example elements validated: | shapes_penrose_tile | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_hilbert_curve | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_easings_testbed | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_clipboard_image | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_magnifying_glass | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | textures_logo_raylib | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | textures_srcrec_dstrec | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | textures_image_drawing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -184,7 +186,7 @@ Example elements validated: | models_directional_billboard | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_animation_blend_custom | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_animation_blending | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| models_animation_timming | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_animation_timing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_ascii_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_basic_lighting | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_model_shader | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -219,6 +221,7 @@ Example elements validated: | shaders_depth_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_game_of_life | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_rlgl_compute | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_cel_shading | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_module_playing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_music_stream | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_raw_stream | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | From 19ec2588be8d1e9254a0c529f12e54033d098e18 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 16 Mar 2026 17:04:30 +0100 Subject: [PATCH 172/319] Update Makefiles for emsdk version 5.0.x, using required `node 22.16.0` and `Python 3.13.3` --- projects/4coder/Makefile | 6 +++--- projects/VSCode/Makefile | 4 ++-- src/Makefile | 4 ++-- tools/rexm/Makefile | 4 ++-- tools/rexm/rexm.c | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/projects/4coder/Makefile b/projects/4coder/Makefile index 1b598eed5..30dcd7770 100644 --- a/projects/4coder/Makefile +++ b/projects/4coder/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. In no event # will the authors be held liable for any damages arising from the use of this software. @@ -117,8 +117,8 @@ ifeq ($(PLATFORM),PLATFORM_WEB) 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 + PYTHON_PATH = $(EMSDK_PATH)/python/3.13.3_64bit + NODE_PATH = $(EMSDK_PATH)/node/22.16.0_64bit/bin export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH);C:\raylib\MinGW\bin:$$(PATH) endif diff --git a/projects/VSCode/Makefile b/projects/VSCode/Makefile index 219673670..4f6d03b31 100644 --- a/projects/VSCode/Makefile +++ b/projects/VSCode/Makefile @@ -120,8 +120,8 @@ ifeq ($(PLATFORM),PLATFORM_WEB) 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 + PYTHON_PATH = $(EMSDK_PATH)/python/3.13.3_64bit + NODE_PATH = $(EMSDK_PATH)/node/22.16.0_64bit/bin export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH):$$(PATH) endif diff --git a/src/Makefile b/src/Makefile index 9826ddf8e..b67ba3e6a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -198,8 +198,8 @@ ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_R 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 + PYTHON_PATH := $(EMSDK_PATH)/python/3.13.3_64bit + NODE_PATH := $(EMSDK_PATH)/node/22.16.0_64bit/bin export PATH := $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH);C:/raylib/MinGW/bin;$(PATH) endif endif diff --git a/tools/rexm/Makefile b/tools/rexm/Makefile index d7d71f64f..c8f2490c0 100644 --- a/tools/rexm/Makefile +++ b/tools/rexm/Makefile @@ -99,8 +99,8 @@ ifeq ($(PLATFORM_OS),WINDOWS) 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 + PYTHON_PATH = $(EMSDK_PATH)/python/3.13.3_64bit + NODE_PATH = $(EMSDK_PATH)/node/22.16.0_64bit/bin export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH):$$(PATH) endif endif diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index c681530c9..49d4dce9a 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -1504,11 +1504,11 @@ int main(int argc, char *argv[]) //ChangeDirectory(exBasePath); //_putenv("MAKE_PATH=C:\\raylib\\w64devkit\\bin"); //_putenv("EMSDK_PATH = C:\\raylib\\emsdk"); - //_putenv("PYTHON_PATH=$(EMSDK_PATH)\\python\\3.9.2-nuget_64bit"); - //_putenv("NODE_PATH=$(EMSDK_PATH)\\node\\20.18.0_64bit\\bin"); + //_putenv("PYTHON_PATH=$(EMSDK_PATH)\\python\\3.13.3_64bit"); + //_putenv("NODE_PATH=$(EMSDK_PATH)\\node\\22.16.0_64bit\\bin"); //_putenv("PATH=%PATH%;$(MAKE_PATH);$(EMSDK_PATH);$(NODE_PATH);$(PYTHON_PATH)"); - _putenv("PATH=%PATH%;C:\\raylib\\w64devkit\\bin;C:\\raylib\\emsdk\\python\\3.9.2-nuget_64bit;C:\\raylib\\emsdk\\node\\20.18.0_64bit\\bin"); + _putenv("PATH=%PATH%;C:\\raylib\\w64devkit\\bin;C:\\raylib\\emsdk\\python\\3.13.3_64bit;C:\\raylib\\emsdk\\node\\22.16.0_64bit\\bin"); #endif for (int i = 0; i < exBuildListCount; i++) From d2be4ac2c9b80dbcd3d9fe4b09d428fcca215426 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 16 Mar 2026 17:04:36 +0100 Subject: [PATCH 173/319] Update CHANGELOG --- CHANGELOG | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 192fe9207..f6a3e8ad1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -8,16 +8,16 @@ Release: raylib 6.0 (?? March 2026) ------------------------------------------------------------------------- KEY CHANGES: - New Software Renderer backend [rlsw] - - New rcore platform backend: Memory (memory buffer) - - New rcore platform backend: Win32 (experimental) - - New rcore platform backend: Emscripten (experimental) - - Redesigned Skeletal Animation System + - New platform backend: Memory (memory buffer) + - New platform backend: Win32 (experimental) + - New platform backend: Emscripten (experimental) - Redesigned fullscreen modes and high-dpi content scaling + - Redesigned Skeletal Animation System - Redesigned Build Config System [config.h] - New File System API - New Text Management API - New tool: [rexm] raylib examples manager - - New examples: +50 new examples and format unification + - Added +50 new examples to learn from Detailed changes: @@ -36,7 +36,7 @@ Detailed changes: [rcore] REVIEWED: Automation system, saving to memory buffer and binary file (#5476) by @belan2470 [rcore] REVIEWED: Check if `shader.locs` is not NULL before try to access (#5622) by @maiconpintoabreu [rcore] REVIEWED: Content scaling on macOS (#5186) by @mchcopl -[rcore] REVIEWED: `GetRandomValue()`, fix modulo bias (#5392) by @Marcos-D +[rcore] REVIEWED: `GetRandomValue()`, fix modulo bias (#5392) by @Marcos-D -WARNING- [rcore] REVIEWED: Controller not available after window init (#5358) by @sergii [rcore] REVIEWED: Cursor lock/unlock inconsistent behaviour (#5323) by @tacf [rcore] REVIEWED: Description of `RestoreWindow()` by @johnnymarler @@ -783,8 +783,9 @@ Detailed changes: Release: raylib 5.5 (18 November 2024) ------------------------------------------------------------------------- KEY CHANGES: - - New tool: raylib project creator - - New rcore backends: RGFW and SDL3 + - New tool: raylib project creator [rpc] + - New platform backends: RGFW + - New platform backends: SDL3 (along SDL2) - New platforms supported: Dreamcast, N64, PSP, PSVita, PS4 - Added GPU Skinning support (all platforms and GL versions) - Added raymath C++ operators From 7401214e92bc7aa41e3f06a7bd94a3182f38426b Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 16 Mar 2026 17:14:53 +0100 Subject: [PATCH 174/319] Update audio_stream_callback.c --- examples/audio/audio_stream_callback.c | 226 +++++++++++++------------ 1 file changed, 118 insertions(+), 108 deletions(-) diff --git a/examples/audio/audio_stream_callback.c b/examples/audio/audio_stream_callback.c index e31e8e80b..088c74dcb 100644 --- a/examples/audio/audio_stream_callback.c +++ b/examples/audio/audio_stream_callback.c @@ -6,133 +6,53 @@ * * Example originally created with raylib 6.0, last time updated with raylib 6.0 * -* Example created by Dan Hoang (@dan-hoang) and reviewed by +* Example created by Dan Hoang (@dan-hoang) and reviewed by Ramon Santamaria (@raysan5) +* +* NOTE: Example sends a wave to the audio device, +* user gets the choice of four waves: sine, square, triangle, and sawtooth +* A stream is set up to play to the audio device; stream is hooked to a callback that +* generates a wave, that is determined by user choice * * 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) 2015-2026 +* Copyright (c) 2026 Dan Hoang (@dan-hoang) * ********************************************************************************************/ #include "raylib.h" + #include #include #define BUFFER_SIZE 4096 #define SAMPLE_RATE 44100 -// This example sends a wave to the audio device -// The user gets the choice of four waves: sine, square, triangle, and sawtooth -// A stream is set up to play to the audio device -// The stream is hooked to a callback that generates a wave -// The callback used is determined by user choice -typedef enum -{ +// Wave type +typedef enum { SINE, SQUARE, TRIANGLE, SAWTOOTH } WaveType; -int waveFrequency = 440; -int newWaveFrequency = 440; -int waveIndex = 0; +//------------------------------------------------------------------------------------ +// Module Functions Declaration +//------------------------------------------------------------------------------------ +static void SineCallback(void *framesOut, unsigned int frameCount); +static void SquareCallback(void *framesOut, unsigned int frameCount); +static void TriangleCallback(void *framesOut, unsigned int frameCount); +static void SawtoothCallback(void *framesOut, unsigned int frameCount); -// Buffer for keeping the last second of uploaded audio, part of which will be drawn on the screen -float buffer[SAMPLE_RATE] = {}; +static int waveFrequency = 440; +static int newWaveFrequency = 440; +static int waveIndex = 0; -void SineCallback(void *framesOut, unsigned int frameCount) -{ - int wavelength = SAMPLE_RATE/waveFrequency; - - // Synthesize the sine wave - for (int i = 0; i < frameCount; i++) - { - ((float *)framesOut)[i] = sin(2*PI*waveIndex/wavelength); - - waveIndex++; - - if (waveIndex >= wavelength) - { - waveFrequency = newWaveFrequency; - waveIndex = 0; - } - } - - // Save the synthesized samples for later drawing - for (int i = 0; i < SAMPLE_RATE - frameCount; i++) buffer[i] = buffer[i + frameCount]; - for (int i = 0; i < frameCount; i++) buffer[SAMPLE_RATE - frameCount + i] = ((float *)framesOut)[i]; -} - -void SquareCallback(void *framesOut, unsigned int frameCount) -{ - int wavelength = SAMPLE_RATE/waveFrequency; - - // Synthesize the square wave - for (int i = 0; i < frameCount; i++) - { - ((float *)framesOut)[i] = (waveIndex < wavelength/2)? 1 : -1; - waveIndex++; - - if (waveIndex >= wavelength) - { - waveFrequency = newWaveFrequency; - waveIndex = 0; - } - } - - // Save the synthesized samples for later drawing - for (int i = 0; i < SAMPLE_RATE - frameCount; i++) buffer[i] = buffer[i + frameCount]; - for (int i = 0; i < frameCount; i++) buffer[SAMPLE_RATE - frameCount + i] = ((float *)framesOut)[i]; -} - -void TriangleCallback(void *framesOut, unsigned int frameCount) -{ - int wavelength = SAMPLE_RATE/waveFrequency; - - // Synthesize the triangle wave - for (int i = 0; i < frameCount; i++) - { - ((float *)framesOut)[i] = (waveIndex < wavelength/2)? (-1 + 2.0f*waveIndex/(wavelength/2)) : (1 - 2.0f*(waveIndex - wavelength/2)/(wavelength/2)); - waveIndex++; - - if (waveIndex >= wavelength) - { - waveFrequency = newWaveFrequency; - waveIndex = 0; - } - } - - // Save the synthesized samples for later drawing - for (int i = 0; i < SAMPLE_RATE - frameCount; i++) buffer[i] = buffer[i + frameCount]; - for (int i = 0; i < frameCount; i++) buffer[SAMPLE_RATE - frameCount + i] = ((float *)framesOut)[i]; -} - -void SawtoothCallback(void *framesOut, unsigned int frameCount) -{ - int wavelength = SAMPLE_RATE/waveFrequency; - - // Synthesize the sawtooth wave - for (int i = 0; i < frameCount; i++) - { - ((float *)framesOut)[i] = -1 + 2.0f*waveIndex/wavelength; - waveIndex++; - - if (waveIndex >= wavelength) - { - waveFrequency = newWaveFrequency; - waveIndex = 0; - } - } - - // Save the synthesized samples for later drawing - for (int i = 0; i < SAMPLE_RATE - frameCount; i++) buffer[i] = buffer[i + frameCount]; - for (int i = 0; i < frameCount; i++) buffer[SAMPLE_RATE - frameCount + i] = ((float *)framesOut)[i]; -} - -AudioCallback waveCallbacks[] = { SineCallback, SquareCallback, TriangleCallback, SawtoothCallback }; -char *waveTypesAsString[] = { "sine", "square", "triangle", "sawtooth" }; +// Buffer to keep the last second of uploaded audio, +// part of which will be drawn on the screen +static float buffer[SAMPLE_RATE] = { 0 }; +static AudioCallback waveCallbacks[] = { SineCallback, SquareCallback, TriangleCallback, SawtoothCallback }; +static char *waveTypesAsString[] = { "sine", "square", "triangle", "sawtooth" }; //------------------------------------------------------------------------------------ // Program main entry point @@ -167,7 +87,6 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - if (IsKeyDown(KEY_UP)) { newWaveFrequency += 10; @@ -199,7 +118,6 @@ int main(void) SetAudioStreamCallback(stream, waveCallbacks[waveType]); } - //---------------------------------------------------------------------------------- // Draw @@ -226,7 +144,7 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - UnloadAudioStream(stream); // Close raw audio stream and delete buffers from RAM + UnloadAudioStream(stream); // Close raw audio stream and delete buffers from RAM CloseAudioDevice(); // Close audio device (music streaming is automatically stopped) CloseWindow(); // Close window and OpenGL context @@ -234,3 +152,95 @@ int main(void) return 0; } + +//------------------------------------------------------------------------------------ +// Module Functions Definition +//------------------------------------------------------------------------------------ +static void SineCallback(void *framesOut, unsigned int frameCount) +{ + int wavelength = SAMPLE_RATE/waveFrequency; + + // Synthesize the sine wave + for (int i = 0; i < frameCount; i++) + { + ((float *)framesOut)[i] = sin(2*PI*waveIndex/wavelength); + + waveIndex++; + + if (waveIndex >= wavelength) + { + waveFrequency = newWaveFrequency; + waveIndex = 0; + } + } + + // Save the synthesized samples for later drawing + for (int i = 0; i < SAMPLE_RATE - frameCount; i++) buffer[i] = buffer[i + frameCount]; + for (int i = 0; i < frameCount; i++) buffer[SAMPLE_RATE - frameCount + i] = ((float *)framesOut)[i]; +} + +static void SquareCallback(void *framesOut, unsigned int frameCount) +{ + int wavelength = SAMPLE_RATE/waveFrequency; + + // Synthesize the square wave + for (int i = 0; i < frameCount; i++) + { + ((float *)framesOut)[i] = (waveIndex < wavelength/2)? 1 : -1; + waveIndex++; + + if (waveIndex >= wavelength) + { + waveFrequency = newWaveFrequency; + waveIndex = 0; + } + } + + // Save the synthesized samples for later drawing + for (int i = 0; i < SAMPLE_RATE - frameCount; i++) buffer[i] = buffer[i + frameCount]; + for (int i = 0; i < frameCount; i++) buffer[SAMPLE_RATE - frameCount + i] = ((float *)framesOut)[i]; +} + +static void TriangleCallback(void *framesOut, unsigned int frameCount) +{ + int wavelength = SAMPLE_RATE/waveFrequency; + + // Synthesize the triangle wave + for (int i = 0; i < frameCount; i++) + { + ((float *)framesOut)[i] = (waveIndex < wavelength/2)? (-1 + 2.0f*waveIndex/(wavelength/2)) : (1 - 2.0f*(waveIndex - wavelength/2)/(wavelength/2)); + waveIndex++; + + if (waveIndex >= wavelength) + { + waveFrequency = newWaveFrequency; + waveIndex = 0; + } + } + + // Save the synthesized samples for later drawing + for (int i = 0; i < SAMPLE_RATE - frameCount; i++) buffer[i] = buffer[i + frameCount]; + for (int i = 0; i < frameCount; i++) buffer[SAMPLE_RATE - frameCount + i] = ((float *)framesOut)[i]; +} + +static void SawtoothCallback(void *framesOut, unsigned int frameCount) +{ + int wavelength = SAMPLE_RATE/waveFrequency; + + // Synthesize the sawtooth wave + for (int i = 0; i < frameCount; i++) + { + ((float *)framesOut)[i] = -1 + 2.0f*waveIndex/wavelength; + waveIndex++; + + if (waveIndex >= wavelength) + { + waveFrequency = newWaveFrequency; + waveIndex = 0; + } + } + + // Save the synthesized samples for later drawing + for (int i = 0; i < SAMPLE_RATE - frameCount; i++) buffer[i] = buffer[i + frameCount]; + for (int i = 0; i < frameCount; i++) buffer[SAMPLE_RATE - frameCount + i] = ((float *)framesOut)[i]; +} From 483d68790037d0e7f87e4d4025788d802c84566e Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 16 Mar 2026 17:19:27 +0100 Subject: [PATCH 175/319] Updated examples versions, validated with REXM --- examples/Makefile | 1 + examples/Makefile.Web | 4 + examples/README.md | 19 +- examples/examples_list.txt | 15 +- examples/models/models_animation_timing.c | 2 +- .../models/models_directional_billboard.c | 2 +- examples/shaders/shaders_ascii_rendering.c | 2 +- examples/shaders/shaders_color_correction.c | 2 +- examples/shaders/shaders_game_of_life.c | 2 +- examples/shaders/shaders_mandelbrot_set.c | 2 +- .../shaders/shaders_normalmap_rendering.c | 2 +- .../examples/audio_stream_callback.vcxproj | 569 ++++++++++++++++++ projects/VS2022/raylib.sln | 29 +- tools/rexm/reports/examples_validation.md | 1 + 14 files changed, 628 insertions(+), 24 deletions(-) create mode 100644 projects/VS2022/examples/audio_stream_callback.vcxproj diff --git a/examples/Makefile b/examples/Makefile index 95719672c..6b15d22ff 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -747,6 +747,7 @@ AUDIO = \ audio/audio_sound_multi \ audio/audio_sound_positioning \ audio/audio_spectrum_visualizer \ + audio/audio_stream_callback \ audio/audio_stream_effects #EXAMPLES_LIST_END diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 4bf635a22..730fec5f9 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -721,6 +721,7 @@ AUDIO = \ audio/audio_sound_multi \ audio/audio_sound_positioning \ audio/audio_spectrum_visualizer \ + audio/audio_stream_callback \ audio/audio_stream_effects # Default target entry @@ -1596,6 +1597,9 @@ audio/audio_spectrum_visualizer: audio/audio_spectrum_visualizer.c --preload-file audio/resources/shaders/glsl100/fft.fs@resources/shaders/glsl100/fft.fs \ --preload-file audio/resources/country.mp3@resources/country.mp3 +audio/audio_stream_callback: audio/audio_stream_callback.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + audio/audio_stream_effects: audio/audio_stream_effects.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file audio/resources/country.mp3@resources/country.mp3 diff --git a/examples/README.md b/examples/README.md index 17719be00..43e2dcc63 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,7 +17,7 @@ You may find it easier to use than other toolchains, especially when it comes to - `zig build [module]` to compile all examples for a module (e.g. `zig build core`) - `zig build [example]` to compile _and run_ a particular example (e.g. `zig build core_basic_window`) -## EXAMPLES COLLECTION [TOTAL: 211] +## EXAMPLES COLLECTION [TOTAL: 212] ### category: core [49] @@ -216,10 +216,10 @@ Examples using raylib models functionality, including models loading/generation | [models_basic_voxel](models/models_basic_voxel.c) | models_basic_voxel | ⭐⭐☆☆ | 5.5 | 5.5 | [Tim Little](https://github.com/timlittle) | | [models_rotating_cube](models/models_rotating_cube.c) | models_rotating_cube | ⭐☆☆☆ | 6.0 | 6.0 | [Jopestpe](https://github.com/jopestpe) | | [models_decals](models/models_decals.c) | models_decals | ⭐⭐⭐⭐️ | 6.0 | 6.0 | [JP Mortiboys](https://github.com/themushroompirates) | -| [models_directional_billboard](models/models_directional_billboard.c) | models_directional_billboard | ⭐⭐☆☆ | 6.0 | 5.6 | [Robin](https://github.com/RobinsAviary) | +| [models_directional_billboard](models/models_directional_billboard.c) | models_directional_billboard | ⭐⭐☆☆ | 6.0 | 6.0 | [Robin](https://github.com/RobinsAviary) | | [models_animation_blend_custom](models/models_animation_blend_custom.c) | models_animation_blend_custom | ⭐⭐⭐⭐️ | 5.5 | 6.0 | [dmitrii-brand](https://github.com/dmitrii-brand) | | [models_animation_blending](models/models_animation_blending.c) | models_animation_blending | ⭐⭐⭐⭐️ | 5.5 | 6.0 | [Kirandeep](https://github.com/Kirandeep-Singh-Khehra) | -| [models_animation_timing](models/models_animation_timing.c) | models_animation_timing | ⭐⭐⭐☆ | 5.6 | 6.0 | [Ramon Santamaria](https://github.com/raysan5) | +| [models_animation_timing](models/models_animation_timing.c) | models_animation_timing | ⭐⭐⭐☆ | 6.0 | 6.0 | [Ramon Santamaria](https://github.com/raysan5) | ### category: shaders [35] @@ -227,7 +227,7 @@ Examples using raylib shaders functionality, including shaders loading, paramete | example | image | difficulty
level | version
created | last version
updated | original
developer | |-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| [shaders_ascii_rendering](shaders/shaders_ascii_rendering.c) | shaders_ascii_rendering | ⭐⭐☆☆ | 5.5 | 5.6 | [Maicon Santana](https://github.com/maiconpintoabreu) | +| [shaders_ascii_rendering](shaders/shaders_ascii_rendering.c) | shaders_ascii_rendering | ⭐⭐☆☆ | 5.5 | 6.0 | [Maicon Santana](https://github.com/maiconpintoabreu) | | [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) | @@ -239,15 +239,15 @@ Examples using raylib shaders functionality, including shaders loading, paramete | [shaders_texture_outline](shaders/shaders_texture_outline.c) | shaders_texture_outline | ⭐⭐⭐☆ | 4.0 | 4.0 | [Serenity 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_mandelbrot_set](shaders/shaders_mandelbrot_set.c) | shaders_mandelbrot_set | ⭐⭐⭐☆ | 5.6 | 5.6 | [Jordi Santonja](https://github.com/JordSant) | -| [shaders_color_correction](shaders/shaders_color_correction.c) | shaders_color_correction | ⭐⭐☆☆ | 5.6 | 5.6 | [Jordi Santonja](https://github.com/JordSant) | +| [shaders_mandelbrot_set](shaders/shaders_mandelbrot_set.c) | shaders_mandelbrot_set | ⭐⭐⭐☆ | 6.0 | 6.0 | [Jordi Santonja](https://github.com/JordSant) | +| [shaders_color_correction](shaders/shaders_color_correction.c) | shaders_color_correction | ⭐⭐☆☆ | 6.0 | 6.0 | [Jordi Santonja](https://github.com/JordSant) | | [shaders_eratosthenes_sieve](shaders/shaders_eratosthenes_sieve.c) | shaders_eratosthenes_sieve | ⭐⭐⭐☆ | 2.5 | 4.0 | [ProfJski](https://github.com/ProfJski) | | [shaders_fog_rendering](shaders/shaders_fog_rendering.c) | shaders_fog_rendering | ⭐⭐⭐☆ | 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_normalmap_rendering](shaders/shaders_normalmap_rendering.c) | shaders_normalmap_rendering | ⭐⭐⭐⭐️ | 5.6 | 5.6 | [Jeremy Montgomery](https://github.com/Sir_Irk) | +| [shaders_normalmap_rendering](shaders/shaders_normalmap_rendering.c) | shaders_normalmap_rendering | ⭐⭐⭐⭐️ | 6.0 | 6.0 | [Jeremy Montgomery](https://github.com/Sir_Irk) | | [shaders_spotlight_rendering](shaders/shaders_spotlight_rendering.c) | shaders_spotlight_rendering | ⭐⭐☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/chriscamacho) | | [shaders_deferred_rendering](shaders/shaders_deferred_rendering.c) | shaders_deferred_rendering | ⭐⭐⭐⭐️ | 4.5 | 4.5 | [Justin Andreas Lacoste](https://github.com/27justin) | | [shaders_hybrid_rendering](shaders/shaders_hybrid_rendering.c) | shaders_hybrid_rendering | ⭐⭐⭐⭐️ | 4.2 | 4.2 | [Buğra Alptekin Sarı](https://github.com/BugraAlptekinSari) | @@ -259,11 +259,11 @@ Examples using raylib shaders functionality, including shaders loading, paramete | [shaders_lightmap_rendering](shaders/shaders_lightmap_rendering.c) | shaders_lightmap_rendering | ⭐⭐⭐☆ | 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_depth_rendering](shaders/shaders_depth_rendering.c) | shaders_depth_rendering | ⭐⭐⭐☆ | 6.0 | 6.0 | [Luís Almeida](https://github.com/luis605) | -| [shaders_game_of_life](shaders/shaders_game_of_life.c) | shaders_game_of_life | ⭐⭐⭐☆ | 5.6 | 5.6 | [Jordi Santonja](https://github.com/JordSant) | +| [shaders_game_of_life](shaders/shaders_game_of_life.c) | shaders_game_of_life | ⭐⭐⭐☆ | 6.0 | 6.0 | [Jordi Santonja](https://github.com/JordSant) | | [shaders_rlgl_compute](shaders/shaders_rlgl_compute.c) | shaders_rlgl_compute | ⭐⭐⭐⭐️ | 4.0 | 4.0 | [Teddy Astie](https://github.com/tsnake41) | | [shaders_cel_shading](shaders/shaders_cel_shading.c) | shaders_cel_shading | ⭐⭐⭐☆ | 6.0 | 6.0 | [Gleb A](https://github.com/ggrizzly) | -### category: audio [9] +### category: audio [10] 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. @@ -278,6 +278,7 @@ Examples using raylib audio functionality, including sound/music loading and pla | [audio_sound_multi](audio/audio_sound_multi.c) | audio_sound_multi | ⭐⭐☆☆ | 5.0 | 5.0 | [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) | | [audio_spectrum_visualizer](audio/audio_spectrum_visualizer.c) | audio_spectrum_visualizer | ⭐⭐⭐☆ | 6.0 | 6.0 | [IANN](https://github.com/meisei4) | +| [audio_stream_callback](audio/audio_stream_callback.c) | audio_stream_callback | ⭐⭐⭐☆ | 6.0 | 6.0 | [Dan Hoang](https://github.com/dan-hoang) | 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! diff --git a/examples/examples_list.txt b/examples/examples_list.txt index 0fe453785..b5b27da1d 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -172,11 +172,11 @@ models;models_tesseract_view;★★☆☆;6.0;6.0;2024;2025;"Timothy van der Val models;models_basic_voxel;★★☆☆;5.5;5.5;2025;2025;"Tim Little";@timlittle models;models_rotating_cube;★☆☆☆;6.0;6.0;2025;2025;"Jopestpe";@jopestpe models;models_decals;★★★★;6.0;6.0;2025;2025;"JP Mortiboys";@themushroompirates -models;models_directional_billboard;★★☆☆;6.0;5.6;2025;2025;"Robin";@RobinsAviary +models;models_directional_billboard;★★☆☆;6.0;6.0;2025;2025;"Robin";@RobinsAviary models;models_animation_blend_custom;★★★★;5.5;6.0;2026;2026;"dmitrii-brand";@dmitrii-brand models;models_animation_blending;★★★★;5.5;6.0;2024;2026;"Kirandeep";@Kirandeep-Singh-Khehra -models;models_animation_timing;★★★☆;5.6;6.0;2026;2026;"Ramon Santamaria";@raysan5 -shaders;shaders_ascii_rendering;★★☆☆;5.5;5.6;2025;2025;"Maicon Santana";@maiconpintoabreu +models;models_animation_timing;★★★☆;6.0;6.0;2026;2026;"Ramon Santamaria";@raysan5 +shaders;shaders_ascii_rendering;★★☆☆;5.5;6.0;2025;2025;"Maicon Santana";@maiconpintoabreu shaders;shaders_basic_lighting;★★★★;3.0;4.2;2019;2025;"Chris Camacho";@chriscamacho shaders;shaders_model_shader;★★☆☆;1.3;3.7;2014;2025;"Ramon Santamaria";@raysan5 shaders;shaders_shapes_textures;★★☆☆;1.7;3.7;2015;2025;"Ramon Santamaria";@raysan5 @@ -188,15 +188,15 @@ shaders;shaders_texture_rendering;★★☆☆;2.0;3.7;2019;2025;"Michał Ciesie shaders;shaders_texture_outline;★★★☆;4.0;4.0;2021;2025;"Serenity Skiff";@GoldenThumbs shaders;shaders_texture_waves;★★☆☆;2.5;3.7;2019;2025;"Anata";@anatagawa shaders;shaders_julia_set;★★★☆;2.5;4.0;2019;2025;"Josh Colclough";@joshcol9232 -shaders;shaders_mandelbrot_set;★★★☆;5.6;5.6;2025;2025;"Jordi Santonja";@JordSant -shaders;shaders_color_correction;★★☆☆;5.6;5.6;2025;2025;"Jordi Santonja";@JordSant +shaders;shaders_mandelbrot_set;★★★☆;6.0;6.0;2025;2025;"Jordi Santonja";@JordSant +shaders;shaders_color_correction;★★☆☆;6.0;6.0;2025;2025;"Jordi Santonja";@JordSant shaders;shaders_eratosthenes_sieve;★★★☆;2.5;4.0;2019;2025;"ProfJski";@ProfJski shaders;shaders_fog_rendering;★★★☆;2.5;3.7;2019;2025;"Chris Camacho";@chriscamacho shaders;shaders_simple_mask;★★☆☆;2.5;3.7;2019;2025;"Chris Camacho";@chriscamacho shaders;shaders_hot_reloading;★★★☆;3.0;3.5;2020;2025;"Ramon Santamaria";@raysan5 shaders;shaders_mesh_instancing;★★★★;3.7;4.2;2020;2025;"seanpringle";@seanpringle shaders;shaders_multi_sample2d;★★☆☆;3.5;3.5;2020;2025;"Ramon Santamaria";@raysan5 -shaders;shaders_normalmap_rendering;★★★★;5.6;5.6;2025;2025;"Jeremy Montgomery";@Sir_Irk +shaders;shaders_normalmap_rendering;★★★★;6.0;6.0;2025;2025;"Jeremy Montgomery";@Sir_Irk shaders;shaders_spotlight_rendering;★★☆☆;2.5;3.7;2019;2025;"Chris Camacho";@chriscamacho shaders;shaders_deferred_rendering;★★★★;4.5;4.5;2023;2025;"Justin Andreas Lacoste";@27justin shaders;shaders_hybrid_rendering;★★★★;4.2;4.2;2022;2025;"Buğra Alptekin Sarı";@BugraAlptekinSari @@ -208,7 +208,7 @@ shaders;shaders_basic_pbr;★★★★;5.0;5.5;2023;2025;"Afan OLOVCIC";@_DevDad shaders;shaders_lightmap_rendering;★★★☆;4.5;4.5;2019;2025;"Jussi Viitala";@nullstare shaders;shaders_rounded_rectangle;★★★☆;5.5;5.5;2025;2025;"Anstro Pleuton";@anstropleuton shaders;shaders_depth_rendering;★★★☆;6.0;6.0;2025;2025;"Luís Almeida";@luis605 -shaders;shaders_game_of_life;★★★☆;5.6;5.6;2025;2025;"Jordi Santonja";@JordSant +shaders;shaders_game_of_life;★★★☆;6.0;6.0;2025;2025;"Jordi Santonja";@JordSant shaders;shaders_rlgl_compute;★★★★;4.0;4.0;2021;2025;"Teddy Astie";@tsnake41 shaders;shaders_cel_shading;★★★☆;6.0;6.0;2026;2026;"Gleb A";@ggrizzly audio;audio_module_playing;★☆☆☆;1.5;3.5;2016;2025;"Ramon Santamaria";@raysan5 @@ -220,3 +220,4 @@ audio;audio_stream_effects;★★★★;4.2;5.0;2022;2025;"Ramon Santamaria";@ra audio;audio_sound_multi;★★☆☆;5.0;5.0;2023;2025;"Jeffery Myers";@JeffM2501 audio;audio_sound_positioning;★★☆☆;5.5;5.5;2025;2025;"Le Juez Victor";@Bigfoot71 audio;audio_spectrum_visualizer;★★★☆;6.0;6.0;2025;2025;"IANN";@meisei4 +audio;audio_stream_callback;★★★☆;6.0;6.0;2026;2026;"Dan Hoang";@dan-hoang diff --git a/examples/models/models_animation_timing.c b/examples/models/models_animation_timing.c index 52d262957..a302322fe 100644 --- a/examples/models/models_animation_timing.c +++ b/examples/models/models_animation_timing.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★★☆] 3/4 * -* Example originally created with raylib 5.6, last time updated with raylib 6.0 +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software diff --git a/examples/models/models_directional_billboard.c b/examples/models/models_directional_billboard.c index 390616fa2..56164943b 100644 --- a/examples/models/models_directional_billboard.c +++ b/examples/models/models_directional_billboard.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★☆☆] 2/4 * -* Example originally created with raylib 6.0, last time updated with raylib 5.6 +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by Robin (@RobinsAviary) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/shaders/shaders_ascii_rendering.c b/examples/shaders/shaders_ascii_rendering.c index d3302bf53..a4225abe3 100644 --- a/examples/shaders/shaders_ascii_rendering.c +++ b/examples/shaders/shaders_ascii_rendering.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★☆☆] 2/4 * -* Example originally created with raylib 5.5, last time updated with raylib 5.6 +* Example originally created with raylib 5.5, last time updated with raylib 6.0 * * Example contributed by Maicon Santana (@maiconpintoabreu) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/shaders/shaders_color_correction.c b/examples/shaders/shaders_color_correction.c index 826baecc7..8a78066f1 100644 --- a/examples/shaders/shaders_color_correction.c +++ b/examples/shaders/shaders_color_correction.c @@ -7,7 +7,7 @@ * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, * OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * -* Example originally created with raylib 5.6, last time updated with raylib 5.6 +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by Jordi Santonja (@JordSant) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/shaders/shaders_game_of_life.c b/examples/shaders/shaders_game_of_life.c index 0ae91b756..251095e2b 100644 --- a/examples/shaders/shaders_game_of_life.c +++ b/examples/shaders/shaders_game_of_life.c @@ -7,7 +7,7 @@ * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, * OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * -* Example originally created with raylib 5.6, last time updated with raylib 5.6 +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by Jordi Santonja (@JordSant) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/examples/shaders/shaders_mandelbrot_set.c b/examples/shaders/shaders_mandelbrot_set.c index dc592b6e4..b5c8a8e94 100644 --- a/examples/shaders/shaders_mandelbrot_set.c +++ b/examples/shaders/shaders_mandelbrot_set.c @@ -9,7 +9,7 @@ * * NOTE: Shaders used in this example are #version 330 (OpenGL 3.3) * -* Example originally created with raylib 5.6, last time updated with raylib 5.6 +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by Jordi Santonja (@JordSant) * Based on previous work by Josh Colclough (@joshcol9232) diff --git a/examples/shaders/shaders_normalmap_rendering.c b/examples/shaders/shaders_normalmap_rendering.c index 07399a78e..6d39cc28e 100644 --- a/examples/shaders/shaders_normalmap_rendering.c +++ b/examples/shaders/shaders_normalmap_rendering.c @@ -7,7 +7,7 @@ * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, * OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * -* Example originally created with raylib 5.6, last time updated with raylib 5.6 +* Example originally created with raylib 6.0, last time updated with raylib 6.0 * * Example contributed by Jeremy Montgomery (@Sir_Irk) and reviewed by Ramon Santamaria (@raysan5) * diff --git a/projects/VS2022/examples/audio_stream_callback.vcxproj b/projects/VS2022/examples/audio_stream_callback.vcxproj new file mode 100644 index 000000000..70d3daae5 --- /dev/null +++ b/projects/VS2022/examples/audio_stream_callback.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 + + + + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED} + Win32Proj + audio_stream_callback + 10.0 + audio_stream_callback + + + + 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/raylib.sln b/projects/VS2022/raylib.sln index 7d10de904..c852d7855 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -443,6 +443,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_clipboard_image", EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_cel_shading", "examples\shaders_cel_shading.vcxproj", "{D5556782-5113-403C-8E7A-2417B8CD0CC6}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_stream_callback", "examples\audio_stream_callback.vcxproj", "{F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|ARM64 = Debug.DLL|ARM64 @@ -5547,6 +5549,30 @@ Global {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Release|x64.Build.0 = Release|x64 {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Release|x86.ActiveCfg = Release|Win32 {D5556782-5113-403C-8E7A-2417B8CD0CC6}.Release|x86.Build.0 = Release|Win32 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Debug|ARM64.Build.0 = Debug|ARM64 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Debug|x64.ActiveCfg = Debug|x64 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Debug|x64.Build.0 = Debug|x64 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Debug|x86.ActiveCfg = Debug|Win32 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Debug|x86.Build.0 = Debug|Win32 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Release|ARM64.ActiveCfg = Release|ARM64 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Release|ARM64.Build.0 = Release|ARM64 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Release|x64.ActiveCfg = Release|x64 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Release|x64.Build.0 = Release|x64 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Release|x86.ActiveCfg = Release|Win32 + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -5709,7 +5735,7 @@ Global {C54703BF-D68A-480D-BE27-49B62E45D582} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {9CD8BCAD-F212-4BCC-BA98-899743CE3279} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} {0981CA28-E4A5-4DF1-987F-A41D09131EFC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {6777EC3C-077C-42FC-B4AD-B799CE55CCE4} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} {A61DAD9C-271C-4E95-81AA-DB4CD58564D4} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} @@ -5770,6 +5796,7 @@ Global {099441CA-5A99-4E50-AA2E-EB397EF0A50A} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} {6E6F4A8A-675A-42F2-8AD1-4BF2757C2654} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} {D5556782-5113-403C-8E7A-2417B8CD0CC6} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {F94B1675-03AE-4EB0-BC0E-071F3C33B9ED} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} diff --git a/tools/rexm/reports/examples_validation.md b/tools/rexm/reports/examples_validation.md index 25e32779a..3863ea38d 100644 --- a/tools/rexm/reports/examples_validation.md +++ b/tools/rexm/reports/examples_validation.md @@ -231,3 +231,4 @@ Example elements validated: | audio_sound_multi | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_sound_positioning | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_spectrum_visualizer | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| audio_stream_callback | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | From e0d3037e1519c2f64f9ee1c9adbaf1facc2d97b4 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 16 Mar 2026 17:51:44 +0100 Subject: [PATCH 176/319] Update raylib.h --- src/raylib.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/raylib.h b/src/raylib.h index a1391de19..951f1ffb3 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -7,6 +7,7 @@ * - Multiplatform: Windows, Linux, macOS, FreeBSD, Web, Android, Raspberry Pi, DRM native... * - 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) +* - Software renderer optional, for systems with no GPU: [rlsw] * - Custom OpenGL abstraction layer (usable as standalone module): [rlgl] * - Multiple Fonts formats supported (TTF, OTF, FNT, BDF, Sprite fonts) * - Many texture formats supportted, including compressed formats (DXT, ETC, ASTC) From 1eea511070ece8e4f96e961588811340ebd0b11e Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 16 Mar 2026 17:51:53 +0100 Subject: [PATCH 177/319] Remove trailing spaces --- .github/workflows/build_webassembly.yml | 18 +++++++++--------- examples/audio/audio_stream_callback.c | 8 ++++---- projects/4coder/Makefile | 8 ++++---- src/platforms/rcore_desktop_sdl.c | 4 ++-- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build_webassembly.yml b/.github/workflows/build_webassembly.yml index 2a126ff46..1e4066c26 100644 --- a/.github/workflows/build_webassembly.yml +++ b/.github/workflows/build_webassembly.yml @@ -18,20 +18,20 @@ on: jobs: build: runs-on: windows-latest - + env: RELEASE_NAME: raylib-dev_webassembly - + steps: - name: Checkout uses: actions/checkout@master - + - name: Setup emsdk uses: mymindstorm/setup-emsdk@v14 with: version: 5.0.3 actions-cache-folder: 'emsdk-cache' - + - name: Setup Release Version run: | echo "RELEASE_NAME=raylib-${{ github.event.release.tag_name }}_webassembly" >> $GITHUB_ENV @@ -39,7 +39,7 @@ jobs: if: github.event_name == 'release' && github.event.action == 'published' - name: Setup Environment - run: | + run: | mkdir build cd build mkdir ${{ env.RELEASE_NAME }} @@ -47,14 +47,14 @@ jobs: mkdir include mkdir lib cd ../.. - + - name: Build Library run: | cd src emcc -v make PLATFORM=PLATFORM_WEB EMSDK_PATH="D:/a/raylib/raylib/emsdk-cache/emsdk-main" RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" -B cd .. - + - name: Generate Artifacts run: | copy /Y .\src\raylib.h .\build\${{ env.RELEASE_NAME }}\include\raylib.h @@ -67,7 +67,7 @@ jobs: 7z a ./${{ env.RELEASE_NAME }}.zip ./${{ env.RELEASE_NAME }} dir shell: cmd - + - name: Upload Artifacts uses: actions/upload-artifact@v4 with: @@ -75,7 +75,7 @@ jobs: path: | ./build/${{ env.RELEASE_NAME }} !./build/${{ env.RELEASE_NAME }}.zip - + - name: Upload Artifact to Release uses: softprops/action-gh-release@v1 with: diff --git a/examples/audio/audio_stream_callback.c b/examples/audio/audio_stream_callback.c index 088c74dcb..a033571d1 100644 --- a/examples/audio/audio_stream_callback.c +++ b/examples/audio/audio_stream_callback.c @@ -8,9 +8,9 @@ * * Example created by Dan Hoang (@dan-hoang) and reviewed by Ramon Santamaria (@raysan5) * -* NOTE: Example sends a wave to the audio device, +* NOTE: Example sends a wave to the audio device, * user gets the choice of four waves: sine, square, triangle, and sawtooth -* A stream is set up to play to the audio device; stream is hooked to a callback that +* A stream is set up to play to the audio device; stream is hooked to a callback that * generates a wave, that is determined by user choice * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, @@ -28,7 +28,7 @@ #define BUFFER_SIZE 4096 #define SAMPLE_RATE 44100 -// Wave type +// Wave type typedef enum { SINE, SQUARE, @@ -48,7 +48,7 @@ static int waveFrequency = 440; static int newWaveFrequency = 440; static int waveIndex = 0; -// Buffer to keep the last second of uploaded audio, +// Buffer to keep the last second of uploaded audio, // part of which will be drawn on the screen static float buffer[SAMPLE_RATE] = { 0 }; static AudioCallback waveCallbacks[] = { SineCallback, SquareCallback, TriangleCallback, SawtoothCallback }; diff --git a/projects/4coder/Makefile b/projects/4coder/Makefile index 30dcd7770..ec0977aaf 100644 --- a/projects/4coder/Makefile +++ b/projects/4coder/Makefile @@ -163,7 +163,7 @@ ifeq ($(PLATFORM),PLATFORM_RPI) endif ifeq ($(PLATFORM),PLATFORM_WEB) # HTML5 emscripten compiler - # WARNING: To compile to HTML5, code must be redesigned + # WARNING: To compile to HTML5, code must be redesigned # to use emscripten.h and emscripten_set_main_loop() CC = emcc endif @@ -297,12 +297,12 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) # Libraries for Debian GNU/Linux desktop compiling # NOTE: Required packages: libegl1-mesa-dev LDLIBS = -lraylib -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 @@ -354,7 +354,7 @@ OBJS = main.c # For Android platform we call a custom Makefile.Android ifeq ($(PLATFORM),PLATFORM_ANDROID) - MAKEFILE_PARAMS = -f Makefile.Android + MAKEFILE_PARAMS = -f Makefile.Android export PROJECT_NAME export SRC_DIR else diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 5cd93ebd7..9b6bea987 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -2126,11 +2126,11 @@ int InitPlatform(void) //---------------------------------------------------------------------------- // Get base time from window initialization CORE.Time.base = (double)(SDL_GetPerformanceCounter()/SDL_GetPerformanceFrequency()); - + #if defined(_WIN32) && SUPPORT_WINMM_HIGHRES_TIMER && !SUPPORT_BUSY_WAIT_LOOP SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "1"); // SDL equivalent of timeBeginPeriod() and timeEndPeriod() #endif - + // NOTE: No need to call InitTimer(), let SDL manage it internally //---------------------------------------------------------------------------- From 22e1e86c52eb9650193557b22155eab3056a960e Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Mar 2026 08:12:09 +0100 Subject: [PATCH 178/319] Update raylib.vcxproj.filters --- projects/VS2022/raylib/raylib.vcxproj.filters | 3 --- 1 file changed, 3 deletions(-) diff --git a/projects/VS2022/raylib/raylib.vcxproj.filters b/projects/VS2022/raylib/raylib.vcxproj.filters index 75cc28a7e..17ac7f9b8 100644 --- a/projects/VS2022/raylib/raylib.vcxproj.filters +++ b/projects/VS2022/raylib/raylib.vcxproj.filters @@ -102,9 +102,6 @@ Header Files - - Header Files - From 5cd7202777f9e9c35756527e3dc8a465a50e4367 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Mar 2026 08:12:15 +0100 Subject: [PATCH 179/319] Update models_rlgl_solar_system.c --- examples/models/models_rlgl_solar_system.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/models/models_rlgl_solar_system.c b/examples/models/models_rlgl_solar_system.c index 0988ede8d..1becc6348 100644 --- a/examples/models/models_rlgl_solar_system.c +++ b/examples/models/models_rlgl_solar_system.c @@ -66,8 +66,6 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera, CAMERA_ORBITAL); - earthRotation += (5.0f*rotationSpeed); earthOrbitRotation += (365/360.0f*(5.0f*rotationSpeed)*rotationSpeed); moonRotation += (2.0f*rotationSpeed); From 93be463322ad2ae0c3940415b722ca44f9c8612b Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Mar 2026 08:13:00 +0100 Subject: [PATCH 180/319] Added missing resource to `audio_module_playing` example #5664 --- examples/Makefile.Web | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 730fec5f9..68c34f31b 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -1570,7 +1570,8 @@ 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) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --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) \ From e7d999e3c75c5b72a02afaf0f1455d88a599293d Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Tue, 17 Mar 2026 17:50:32 +0100 Subject: [PATCH 181/319] [rlsw] RenderTexture support (#5655) * review texture formats Added support for `R3G3B2`, `R5G6B5`, `R4G4B4A4` and `R5G5B5A1` Added depth formats * use of textures for the framebuffer - Framebuffers can now use all texture types that are already available. - The 24-bit depth format has been removed as it is no longer needed. - Framebuffer formats are still defined at compile time. - The allocated texture size is now preserved, which avoids frequent reallocations when resizing framebuffers and will allow the use of `glTexSubImage2D`. * review framebuffer blit/copy This greatly simplifies the framebuffer blit/copy logic while now supporting all pixel formats. It is slightly slower in debug builds, but this path is mainly kept for compatibility anyway. The `copy_fast` version is still used for the "normal" cases when presenting to the screen. * review pixel get/set less ops for certain formats + fixes * fix depth write * texture read/write cleanup + tweaks I made the pointers parameters `restrict` for reading/writing textures, which resulted in a slight improvement. And I reviewed the `static inline` statements, which could potentially bias the compiler; no difference, but it's cleaner. * style tweaks * review uint8_t <-> float conversion * added a reusable object pool system will allow management of both textures and framebuffers added support for `glTexSubImage2D` added handling of 'GL_OUT_OF_MEMORY' errors removed the default internal texture (unused) * added FBO API + refactored rasterizer dispatch logic * fix ndc projection + review presentation and rename rlsw's resize/copy/blit * add `glRenderbufferStorage` binding + tweaks and fixes * fix quad sorting + simplify quad rasterization part * fix line shaking issue * support of `GL_DRAW_FRAMEBUFFER_BINDING` * update rlgl - support of rlsw's framebuffers * fix pixel origin in line rasterization my bad, an oversight in my previous fix. This offset should have been moved here rather than per pixel during truncation. * style tweaks * fix vla issue with msvc - fill depth / fill color --- src/external/rlsw.h | 6547 +++++++++++++++++++++++++------------------ src/rlgl.h | 28 +- 2 files changed, 3881 insertions(+), 2694 deletions(-) diff --git a/src/external/rlsw.h b/src/external/rlsw.h index 46cba4c74..81d4a54f1 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -51,10 +51,9 @@ * rlsw capabilities could be customized defining some internal * values before library inclusion (default values listed): * -* #define SW_GL_FRAMEBUFFER_COPY_BGRA true -* #define SW_GL_BINDING_COPY_TEXTURE true -* #define SW_COLOR_BUFFER_BITS 24 -* #define SW_DEPTH_BUFFER_BITS 16 +* #define SW_FRAMEBUFFER_OUTPUT_BGRA true +* #define SW_FRAMEBUFFER_COLOR_TYPE R8G8B8A8 +* #define SW_FRAMEBUFFER_DEPTH_TYPE D32 * #define SW_MAX_PROJECTION_STACK_SIZE 2 * #define SW_MAX_MODELVIEW_STACK_SIZE 8 * #define SW_MAX_TEXTURE_STACK_SIZE 2 @@ -102,6 +101,9 @@ #ifndef SW_MALLOC #define SW_MALLOC(sz) malloc(sz) #endif +#ifndef SW_CALLOC + #define SW_CALLOC(n,sz) calloc(n,sz) +#endif #ifndef SW_REALLOC #define SW_REALLOC(ptr, newSz) realloc(ptr, newSz) #endif @@ -117,16 +119,16 @@ #endif #endif -#ifndef SW_GL_FRAMEBUFFER_COPY_BGRA - #define SW_GL_FRAMEBUFFER_COPY_BGRA true +#ifndef SW_FRAMEBUFFER_OUTPUT_BGRA + #define SW_FRAMEBUFFER_OUTPUT_BGRA true #endif -#ifndef SW_COLOR_BUFFER_BITS - #define SW_COLOR_BUFFER_BITS 32 //< 32 (rgba), 16 (rgb packed) or 8 (rgb packed) +#ifndef SW_FRAMEBUFFER_COLOR_TYPE + #define SW_FRAMEBUFFER_COLOR_TYPE R8G8B8A8 // Or R5G6B5, R3G3B2, etc; see `sw_pixelformat_t` #endif -#ifndef SW_DEPTH_BUFFER_BITS - #define SW_DEPTH_BUFFER_BITS 16 //< 32, 24 or 16 +#ifndef SW_FRAMEBUFFER_DEPTH_TYPE + #define SW_FRAMEBUFFER_DEPTH_TYPE D32 // Or D16, D8 #endif #ifndef SW_MAX_PROJECTION_STACK_SIZE @@ -141,6 +143,10 @@ #define SW_MAX_TEXTURE_STACK_SIZE 2 #endif +#ifndef SW_MAX_FRAMEBUFFERS + #define SW_MAX_FRAMEBUFFERS 8 +#endif + #ifndef SW_MAX_TEXTURES #define SW_MAX_TEXTURES 128 #endif @@ -294,6 +300,7 @@ typedef double GLclampd; #define GL_LUMINANCE_ALPHA 0x190A #define GL_RGB 0x1907 #define GL_RGBA 0x1908 +#define GL_DEPTH_COMPONENT 0x1902 #define GL_BYTE 0x1400 #define GL_UNSIGNED_BYTE 0x1401 @@ -302,6 +309,70 @@ typedef double GLclampd; #define GL_INT 0x1404 #define GL_UNSIGNED_INT 0x1405 #define GL_FLOAT 0x1406 +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#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 + +// OpenGL internal formats (not all are supported; see SWinternalformat) +#define GL_ALPHA4 0x803B +#define GL_ALPHA8 0x803C +#define GL_ALPHA12 0x803D +#define GL_ALPHA16 0x803E +#define GL_LUMINANCE4 0x803F +#define GL_LUMINANCE8 0x8040 +#define GL_LUMINANCE12 0x8041 +#define GL_LUMINANCE16 0x8042 +#define GL_LUMINANCE4_ALPHA4 0x8043 +#define GL_LUMINANCE6_ALPHA2 0x8044 +#define GL_LUMINANCE8_ALPHA8 0x8045 +#define GL_LUMINANCE12_ALPHA4 0x8046 +#define GL_LUMINANCE12_ALPHA12 0x8047 +#define GL_LUMINANCE16_ALPHA16 0x8048 +#define GL_INTENSITY 0x8049 +#define GL_INTENSITY4 0x804A +#define GL_INTENSITY8 0x804B +#define GL_INTENSITY12 0x804C +#define GL_INTENSITY16 0x804D +#define GL_R3_G3_B2 0x2A10 +#define GL_RGB4 0x804F +#define GL_RGB5 0x8050 +#define GL_RGB8 0x8051 +#define GL_RGB10 0x8052 +#define GL_RGB12 0x8053 +#define GL_RGB16 0x8054 +#define GL_RGBA2 0x8055 +#define GL_RGBA4 0x8056 +#define GL_RGB5_A1 0x8057 +#define GL_RGBA8 0x8058 +#define GL_RGB10_A2 0x8059 +#define GL_RGBA12 0x805A +#define GL_RGBA16 0x805B + +// OpenGL internal formats extension +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#define GL_DEPTH_COMPONENT32F 0x8CAC +#define GL_R16F 0x822D +#define GL_RGB16F 0x881B +#define GL_RGBA16F 0x881A +#define GL_R32F 0x822E +#define GL_RGB32F 0x8815 +#define GL_RGBA32F 0x8814 + +// OpenGL GL_EXT_framebuffer_object +#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9 +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_STENCIL_ATTACHMENT 0x8D20 // WARNING: Not implemented (defined for RLGL) // OpenGL Definitions NOT USED #define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50 @@ -320,13 +391,21 @@ typedef double GLclampd; #define GL_NOTEQUAL 0x0205 #define GL_GEQUAL 0x0206 #define GL_ALWAYS 0x0207 +#define GL_RENDERBUFFER 0x8D41 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 //---------------------------------------------------------------------------------- // OpenGL Bindings to rlsw //---------------------------------------------------------------------------------- -#define glReadPixels(x, y, w, h, f, t, p) swCopyFramebuffer((x), (y), (w), (h), (f), (t), (p)) +#define glReadPixels(x, y, w, h, f, t, p) swReadPixels((x), (y), (w), (h), (f), (t), (p)) #define glEnable(state) swEnable((state)) #define glDisable(state) swDisable((state)) +#define glGetIntegerv(pname, params) swGetIntegerv((pname), (params)) #define glGetFloatv(pname, params) swGetFloatv((pname), (params)) #define glGetString(pname) swGetString((pname)) #define glGetError() swGetError() @@ -381,23 +460,36 @@ typedef double GLclampd; #define glDrawElements(m,c,t,i) swDrawElements((m),(c),(t),(i)) #define glGenTextures(c, v) swGenTextures((c), (v)) #define glDeleteTextures(c, v) swDeleteTextures((c), (v)) -#define glTexImage2D(tr, l, if, w, h, b, f, t, p) swTexImage2D((w), (h), (f), (t), (p)) -#define glTexParameteri(tr, pname, param) swTexParameteri((pname), (param)) #define glBindTexture(tr, id) swBindTexture((id)) +#define glTexImage2D(tr, l, if, w, h, b, f, t, p) swTexImage2D((w), (h), (f), (t), (p)) +#define glTexSubImage2D(tr, l, x, y, w, h, f, t, p) swTexSubImage2D((x), (y), (w), (h), (f), (t), (p)); +#define glTexParameteri(tr, pname, param) swTexParameteri((pname), (param)) + +// OpenGL GL_EXT_framebuffer_object +#define glGenFramebuffers(c, v) swGenFramebuffers((c), (v)) +#define glDeleteFramebuffers(c, v) swDeleteFramebuffers((c), (v)) +#define glBindFramebuffer(tr, id) swBindFramebuffer((id)) +#define glFramebufferTexture2D(tr, a, t, tex, ml) swFramebufferTexture2D((a), (tex)) +#define glFramebufferRenderbuffer(tr, a, rbtr, rb) swFramebufferTexture2D((a), (rb)) +#define glCheckFramebufferStatus(tr) swCheckFramebufferStatus() +#define glGetFramebufferAttachmentParameteriv(tr, a, p, v) swGetFramebufferAttachmentParameteriv((a), (p), (v)) +#define glGenRenderbuffers(c, v) swGenTextures((c), (v)) +#define glDeleteRenderbuffers(c, v) swDeleteTextures((c), (v)) +#define glBindRenderbuffer(tr, rb) swBindTexture((rb)) +#define glRenderbufferStorage(tr, f, w, h) swTexStorage2D((w), (h), (f)) // OpenGL functions NOT IMPLEMENTED by rlsw -#define glDepthMask(X) ((void)(X)) -#define glColorMask(X,Y,Z,W) ((void)(X),(void)(Y),(void)(Z),(void)(W)) -#define glPixelStorei(X,Y) ((void)(X),(void)(Y)) -#define glHint(X,Y) ((void)(X),(void)(Y)) -#define glShadeModel(X) ((void)(X)) -#define glFrontFace(X) ((void)(X)) -#define glDepthFunc(X) ((void)(X)) -#define glTexSubImage2D(X,Y,Z,W,A,B,C,D,E) ((void)(X),(void)(Y),(void)(Z),(void)(W),(void)(A),(void)(B),(void)(C),(void)(D),(void)(E)) -#define glGetTexImage(X,Y,Z,W,A) ((void)(X),(void)(Y),(void)(Z),(void)(W),(void)(A)) -#define glNormal3f(X,Y,Z) ((void)(X),(void)(Y),(void)(Z)) -#define glNormal3fv(X) ((void)(X)) -#define glNormalPointer(X,Y,Z) ((void)(X),(void)(Y),(void)(Z)) +#define glDepthMask(X) ((void)(X)) +#define glColorMask(X,Y,Z,W) ((void)(X),(void)(Y),(void)(Z),(void)(W)) +#define glPixelStorei(X,Y) ((void)(X),(void)(Y)) +#define glHint(X,Y) ((void)(X),(void)(Y)) +#define glShadeModel(X) ((void)(X)) +#define glFrontFace(X) ((void)(X)) +#define glDepthFunc(X) ((void)(X)) +#define glGetTexImage(X,Y,Z,W,A) ((void)(X),(void)(Y),(void)(Z),(void)(W),(void)(A)) +#define glNormal3f(X,Y,Z) ((void)(X),(void)(Y),(void)(Z)) +#define glNormal3fv(X) ((void)(X)) +#define glNormalPointer(X,Y,Z) ((void)(X),(void)(Y),(void)(Z)) //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -427,7 +519,8 @@ typedef enum { SW_PROJECTION_STACK_DEPTH = GL_PROJECTION_STACK_DEPTH, SW_TEXTURE_MATRIX = GL_TEXTURE_MATRIX, SW_TEXTURE_STACK_DEPTH = GL_TEXTURE_STACK_DEPTH, - SW_VIEWPORT = GL_VIEWPORT + SW_VIEWPORT = GL_VIEWPORT, + SW_DRAW_FRAMEBUFFER_BINDING = GL_DRAW_FRAMEBUFFER_BINDING, } SWget; typedef enum { @@ -448,6 +541,7 @@ typedef enum { } SWarray; typedef enum { + SW_DRAW_INVALID = -1, SW_POINTS = GL_POINTS, SW_LINES = GL_LINES, SW_TRIANGLES = GL_TRIANGLES, @@ -484,21 +578,47 @@ typedef enum { SW_LUMINANCE_ALPHA = GL_LUMINANCE_ALPHA, SW_RGB = GL_RGB, SW_RGBA = GL_RGBA, + SW_DEPTH_COMPONENT = GL_DEPTH_COMPONENT, } SWformat; typedef enum { SW_UNSIGNED_BYTE = GL_UNSIGNED_BYTE, + SW_UNSIGNED_BYTE_3_3_2 = GL_UNSIGNED_BYTE_3_3_2, SW_BYTE = GL_BYTE, SW_UNSIGNED_SHORT = GL_UNSIGNED_SHORT, + SW_UNSIGNED_SHORT_5_6_5 = GL_UNSIGNED_SHORT_5_6_5, + SW_UNSIGNED_SHORT_4_4_4_4 = GL_UNSIGNED_SHORT_4_4_4_4, + SW_UNSIGNED_SHORT_5_5_5_1 = GL_UNSIGNED_SHORT_5_5_5_1, SW_SHORT = GL_SHORT, SW_UNSIGNED_INT = GL_UNSIGNED_INT, SW_INT = GL_INT, SW_FLOAT = GL_FLOAT } SWtype; +typedef enum { + SW_LUMINANCE8 = GL_LUMINANCE8, + SW_LUMINANCE8_ALPHA8 = GL_LUMINANCE8_ALPHA8, + SW_R3_G3_B2 = GL_R3_G3_B2, + SW_RGB8 = GL_RGB8, + SW_RGBA4 = GL_RGBA4, + SW_RGB5_A1 = GL_RGB5_A1, + SW_RGBA8 = GL_RGBA8, + SW_R16F = GL_R16F, + SW_RGB16F = GL_RGB16F, + SW_RGBA16F = GL_RGBA16F, + SW_R32F = GL_R32F, + SW_RGB32F = GL_RGB32F, + SW_RGBA32F = GL_RGBA32F, + SW_DEPTH_COMPONENT16 = GL_DEPTH_COMPONENT16, + SW_DEPTH_COMPONENT24 = GL_DEPTH_COMPONENT24, + SW_DEPTH_COMPONENT32 = GL_DEPTH_COMPONENT32, + SW_DEPTH_COMPONENT32F = GL_DEPTH_COMPONENT32F, + //SW_R5_G6_B5, // Not defined by OpenGL +} SWinternalformat; + typedef enum { SW_NEAREST = GL_NEAREST, - SW_LINEAR = GL_LINEAR + SW_LINEAR = GL_LINEAR } SWfilter; typedef enum { @@ -513,6 +633,23 @@ typedef enum { SW_TEXTURE_WRAP_T = GL_TEXTURE_WRAP_T } SWtexparam; +typedef enum { + SW_COLOR_ATTACHMENT = GL_COLOR_ATTACHMENT0, + SW_DEPTH_ATTACHMENT = GL_DEPTH_ATTACHMENT, +} SWattachment; + +typedef enum { + SW_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME = GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, + SW_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, +} SWattachget; + +typedef enum { + SW_FRAMEBUFFER_COMPLETE = GL_FRAMEBUFFER_COMPLETE, + SW_FRAMEBUFFER_INCOMPLETE_ATTACHMENT = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, + SW_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT, + SW_FRAMEBUFFER_INCOMPLETE_DIMENSIONS = GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS, +} SWfbstatus; + typedef enum { SW_NO_ERROR = GL_NO_ERROR, SW_INVALID_ENUM = GL_INVALID_ENUM, @@ -520,6 +657,7 @@ typedef enum { SW_STACK_OVERFLOW = GL_STACK_OVERFLOW, SW_STACK_UNDERFLOW = GL_STACK_UNDERFLOW, SW_INVALID_OPERATION = GL_INVALID_OPERATION, + SW_OUT_OF_MEMORY = GL_OUT_OF_MEMORY, } SWerrcode; //------------------------------------------------------------------------------------ @@ -528,9 +666,9 @@ typedef enum { SWAPI bool swInit(int w, int h); SWAPI void swClose(void); -SWAPI bool swResizeFramebuffer(int w, int h); -SWAPI void swCopyFramebuffer(int x, int y, int w, int h, SWformat format, SWtype type, void *pixels); -SWAPI void swBlitFramebuffer(int xDst, int yDst, int wDst, int hDst, int xSrc, int ySrc, int wSrc, int hSrc, SWformat format, SWtype type, void *pixels); +SWAPI bool swResize(int w, int h); +SWAPI void swReadPixels(int x, int y, int w, int h, SWformat format, SWtype type, void *pixels); +SWAPI void swBlitPixels(int xDst, int yDst, int wDst, int hDst, int xSrc, int ySrc, int wSrc, int hSrc, SWformat format, SWtype type, void *pixels); SWAPI void swEnable(SWstate state); SWAPI void swDisable(SWstate state); @@ -595,10 +733,17 @@ SWAPI void swDrawElements(SWdraw mode, int count, int type, const void *indices) SWAPI void swGenTextures(int count, uint32_t *textures); SWAPI void swDeleteTextures(int count, uint32_t *textures); - -SWAPI void swTexImage2D(int width, int height, SWformat format, SWtype type, const void *data); -SWAPI void swTexParameteri(int param, int value); SWAPI void swBindTexture(uint32_t id); +SWAPI void swTexImage2D(int width, int height, SWformat format, SWtype type, const void *data); +SWAPI void swTexSubImage2D(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +SWAPI void swTexParameteri(int param, int value); + +SWAPI void swGenFramebuffers(int count, uint32_t* framebuffers); +SWAPI void swDeleteFramebuffers(int count, uint32_t* framebuffers); +SWAPI void swBindFramebuffer(uint32_t id); +SWAPI void swFramebufferTexture2D(SWattachment attach, uint32_t texture); +SWAPI SWfbstatus swCheckFramebufferStatus(void); +SWAPI void swGetFramebufferAttachmentParameteriv(SWattachment attachment, SWattachget property, int *v); #endif // RLSW_H @@ -607,9 +752,10 @@ SWAPI void swBindTexture(uint32_t id); * RLSW IMPLEMENTATION * ************************************************************************************/ -#define RLSW_IMPLEMENTATION #if defined(RLSW_IMPLEMENTATION) +#undef RLSW_IMPLEMENTATION // Undef to allow template expanding without implementation redefinition + #include // Required for: malloc(), free() #include // Required for: NULL, size_t, uint8_t, uint16_t, uint32_t... #include // Required for: sinf(), cosf(), floorf(), fabsf(), sqrtf(), roundf() @@ -711,79 +857,27 @@ SWAPI void swBindTexture(uint32_t id); #define SW_DEG2RAD (SW_PI/180.0f) #define SW_RAD2DEG (180.0f/SW_PI) -#define SW_COLOR_PIXEL_SIZE (SW_COLOR_BUFFER_BITS >> 3) -#define SW_DEPTH_PIXEL_SIZE (SW_DEPTH_BUFFER_BITS >> 3) -#define SW_PIXEL_SIZE (SW_COLOR_PIXEL_SIZE + SW_DEPTH_PIXEL_SIZE) +#define SW_HANDLE_NULL 0u +#define SW_POOL_SLOT_LIVE 0x80u // bit7 of the generation byte +#define SW_POOL_SLOT_VER_MASK 0x7Fu // bits6:0 = anti-ABA counter -#if (SW_PIXEL_SIZE <= 4) - #define SW_PIXEL_ALIGNMENT 4 -#else // if (SW_PIXEL_SIZE <= 8) - #define SW_PIXEL_ALIGNMENT 8 -#endif +#define SW_CONCAT(a, b) a##b +#define SW_CONCATX(a, b) SW_CONCAT(a, b) -#if (SW_COLOR_BUFFER_BITS == 8) - #define SW_COLOR_TYPE uint8_t - #define SW_COLOR_IS_PACKED 1 - #define SW_COLOR_PACK_COMP 1 - #define SW_PACK_COLOR(r,g,b) ((((uint8_t)((r)*7+0.5f))&0x07)<<5 | (((uint8_t)((g)*7+0.5f))&0x07)<<2 | ((uint8_t)((b)*3+0.5f))&0x03) - #define SW_UNPACK_R(p) (((p)>>5)&0x07) - #define SW_UNPACK_G(p) (((p)>>2)&0x07) - #define SW_UNPACK_B(p) ((p)&0x03) - #define SW_SCALE_R(v) ((v)*255+3)/7 - #define SW_SCALE_G(v) ((v)*255+3)/7 - #define SW_SCALE_B(v) ((v)*255+1)/3 - #define SW_TO_FLOAT_R(v) ((v)*(1.0f/7.0f)) - #define SW_TO_FLOAT_G(v) ((v)*(1.0f/7.0f)) - #define SW_TO_FLOAT_B(v) ((v)*(1.0f/3.0f)) -#elif (SW_COLOR_BUFFER_BITS == 16) - #define SW_COLOR_TYPE uint16_t - #define SW_COLOR_IS_PACKED 1 - #define SW_COLOR_PACK_COMP 1 - #define SW_PACK_COLOR(r,g,b) ((((uint16_t)((r)*31+0.5f))&0x1F)<<11 | (((uint16_t)((g)*63+0.5f))&0x3F)<<5 | ((uint16_t)((b)*31+0.5f))&0x1F) - #define SW_UNPACK_R(p) (((p)>>11)&0x1F) - #define SW_UNPACK_G(p) (((p)>>5)&0x3F) - #define SW_UNPACK_B(p) ((p)&0x1F) - #define SW_SCALE_R(v) ((v)*255+15)/31 - #define SW_SCALE_G(v) ((v)*255+31)/63 - #define SW_SCALE_B(v) ((v)*255+15)/31 - #define SW_TO_FLOAT_R(v) ((v)*(1.0f/31.0f)) - #define SW_TO_FLOAT_G(v) ((v)*(1.0f/63.0f)) - #define SW_TO_FLOAT_B(v) ((v)*(1.0f/31.0f)) -#else // 32 bits - #define SW_COLOR_TYPE uint8_t - #define SW_COLOR_IS_PACKED 0 - #define SW_COLOR_PACK_COMP 4 -#endif +#define SW_FRAMEBUFFER_COLOR8_GET(c,p,o) SW_CONCATX(sw_pixel_get_color8_, SW_FRAMEBUFFER_COLOR_TYPE)((c),(p),(o)) +#define SW_FRAMEBUFFER_COLOR_GET(c,p,o) SW_CONCATX(sw_pixel_get_color_, SW_FRAMEBUFFER_COLOR_TYPE)((c),(p),(o)) +#define SW_FRAMEBUFFER_COLOR_SET(p,c,o) SW_CONCATX(sw_pixel_set_color_, SW_FRAMEBUFFER_COLOR_TYPE)((p),(c),(o)) -#if (SW_DEPTH_BUFFER_BITS == 16) - #define SW_DEPTH_TYPE uint16_t - #define SW_DEPTH_IS_PACKED 1 - #define SW_DEPTH_PACK_COMP 1 - #define SW_DEPTH_MAX UINT16_MAX - #define SW_DEPTH_SCALE (1.0f/UINT16_MAX) - #define SW_PACK_DEPTH(d) ((SW_DEPTH_TYPE)((d)*SW_DEPTH_MAX)) - #define SW_UNPACK_DEPTH(p) (p) -#elif (SW_DEPTH_BUFFER_BITS == 24) - #define SW_DEPTH_TYPE uint8_t - #define SW_DEPTH_IS_PACKED 0 - #define SW_DEPTH_PACK_COMP 3 - #define SW_DEPTH_MAX 0xFFFFFFU - #define SW_DEPTH_SCALE (1.0f/0xFFFFFFU) - #define SW_PACK_DEPTH_0(d) ((uint8_t)(((uint32_t)((d)*SW_DEPTH_MAX)>>16)&0xFFU)) - #define SW_PACK_DEPTH_1(d) ((uint8_t)(((uint32_t)((d)*SW_DEPTH_MAX)>>8)&0xFFU)) - #define SW_PACK_DEPTH_2(d) ((uint8_t)((uint32_t)((d)*SW_DEPTH_MAX)&0xFFU)) - #define SW_UNPACK_DEPTH(p) ((((uint32_t)(p)[0]<<16)|((uint32_t)(p)[1]<<8)|(uint32_t)(p)[2])) -#else // 32 bits - #define SW_DEPTH_TYPE float - #define SW_DEPTH_IS_PACKED 1 - #define SW_DEPTH_PACK_COMP 1 - #define SW_DEPTH_MAX 1.0f - #define SW_DEPTH_SCALE 1.0f - #define SW_PACK_DEPTH(d) ((SW_DEPTH_TYPE)(d)) - #define SW_UNPACK_DEPTH(p) (p) -#endif +#define SW_FRAMEBUFFER_DEPTH_GET(p,o) SW_CONCATX(sw_pixel_get_depth_, SW_FRAMEBUFFER_DEPTH_TYPE)((p),(o)) +#define SW_FRAMEBUFFER_DEPTH_SET(p,d,o) SW_CONCATX(sw_pixel_set_depth_, SW_FRAMEBUFFER_DEPTH_TYPE)((p),(d),(o)) -#define SW_STATE_CHECK(flags) (SW_STATE_CHECK_EX(RLSW.stateFlags, (flags))) +#define SW_FRAMEBUFFER_COLOR_FORMAT SW_CONCATX(SW_PIXELFORMAT_COLOR_, SW_FRAMEBUFFER_COLOR_TYPE) +#define SW_FRAMEBUFFER_DEPTH_FORMAT SW_CONCATX(SW_PIXELFORMAT_DEPTH_, SW_FRAMEBUFFER_DEPTH_TYPE) + +#define SW_FRAMEBUFFER_COLOR_SIZE SW_PIXELFORMAT_SIZE[SW_FRAMEBUFFER_COLOR_FORMAT] +#define SW_FRAMEBUFFER_DEPTH_SIZE SW_PIXELFORMAT_SIZE[SW_FRAMEBUFFER_DEPTH_FORMAT] + +#define SW_STATE_CHECK(flags) (SW_STATE_CHECK_EX(RLSW.stateFlags, (flags))) #define SW_STATE_CHECK_EX(state, flags) (((state) & (flags)) == (flags)) #define SW_STATE_SCISSOR_TEST (1 << 0) @@ -795,33 +889,34 @@ SWAPI void swBindTexture(uint32_t id); //---------------------------------------------------------------------------------- // Module Types and Structures Definition //---------------------------------------------------------------------------------- +// Aliases types +typedef uint32_t sw_handle_t; +typedef uint16_t sw_half_t; + // Pixel data format type -// NOTE: Enum aligned with raylib PixelFormat typedef enum { SW_PIXELFORMAT_UNKNOWN = 0, - SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE, // 8 bit per pixel (no alpha) - SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA, // 8*2 bpp (2 channels) - SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5, // 16 bpp - SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8, // 24 bpp - SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) - SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha) - SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, // 32 bpp - SW_PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) - SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) - SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) - SW_PIXELFORMAT_UNCOMPRESSED_R16, // 16 bpp (1 channel - half float) - SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16, // 16*3 bpp (3 channels - half float) - SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16, // 16*4 bpp (4 channels - half float) + SW_PIXELFORMAT_COLOR_GRAYSCALE, // 8 bit per pixel (no alpha) + SW_PIXELFORMAT_COLOR_GRAYALPHA, // 8*2 bpp (2 channels) + SW_PIXELFORMAT_COLOR_R3G3B2, // 8 bpp + SW_PIXELFORMAT_COLOR_R5G6B5, // 16 bpp + SW_PIXELFORMAT_COLOR_R8G8B8, // 24 bpp + SW_PIXELFORMAT_COLOR_R5G5B5A1, // 16 bpp (1 bit alpha) + SW_PIXELFORMAT_COLOR_R4G4B4A4, // 16 bpp (4 bit alpha) + SW_PIXELFORMAT_COLOR_R8G8B8A8, // 32 bpp + SW_PIXELFORMAT_COLOR_R32, // 32 bpp (1 channel - float) + SW_PIXELFORMAT_COLOR_R32G32B32, // 32*3 bpp (3 channels - float) + SW_PIXELFORMAT_COLOR_R32G32B32A32, // 32*4 bpp (4 channels - float) + SW_PIXELFORMAT_COLOR_R16, // 16 bpp (1 channel - half float) + SW_PIXELFORMAT_COLOR_R16G16B16, // 16*3 bpp (3 channels - half float) + SW_PIXELFORMAT_COLOR_R16G16B16A16, // 16*4 bpp (4 channels - half float) + SW_PIXELFORMAT_DEPTH_D8, // 1 bpp + SW_PIXELFORMAT_DEPTH_D16, // 2 bpp + SW_PIXELFORMAT_DEPTH_D32, // 4 bpp + SW_PIXELFORMAT_COUNT } sw_pixelformat_t; -typedef void (*sw_factor_f)( - float *SW_RESTRICT factor, - const float *SW_RESTRICT src, - const float *SW_RESTRICT dst -); - typedef float sw_matrix_t[4*4]; -typedef uint16_t sw_half_t; typedef struct { float position[4]; // Position coordinates @@ -833,53 +928,58 @@ typedef struct { } sw_vertex_t; typedef struct { - uint8_t *pixels; // Texture pixels (RGBA32) - + void *pixels; // Texture pixels + sw_pixelformat_t format; // Texture format int width, height; // Dimensions of the texture int wMinus1, hMinus1; // Dimensions minus one - + int allocSz; // Allocated size SWfilter minFilter; // Minification filter SWfilter magFilter; // Magnification filter - SWwrap sWrap; // texcoord.x wrap mode SWwrap tWrap; // texcoord.y wrap mode - float tx; // Texel width float ty; // Texel height } sw_texture_t; -// Pixel data type -typedef SW_ALIGN(SW_PIXEL_ALIGNMENT) struct { - SW_COLOR_TYPE color[SW_COLOR_PACK_COMP]; - SW_DEPTH_TYPE depth[SW_DEPTH_PACK_COMP]; -#if (SW_PIXEL_SIZE % SW_PIXEL_ALIGNMENT != 0) - uint8_t padding[SW_PIXEL_ALIGNMENT - SW_PIXEL_SIZE % SW_PIXEL_ALIGNMENT]; -#endif -} sw_pixel_t; +typedef struct { + sw_texture_t color; + sw_texture_t depth; +} sw_default_framebuffer_t; typedef struct { - sw_pixel_t *pixels; - int width; - int height; - int allocSz; + sw_handle_t colorAttachment; + sw_handle_t depthAttachment; } sw_framebuffer_t; typedef struct { - sw_framebuffer_t framebuffer; // Main framebuffer - sw_pixel_t clearValue; // Clear value of the framebuffer + void *data; // flat storage [capacity*stride] bytes + uint8_t *gen; // generation per slot [capacity] + uint32_t *freeList; // free indices stack [capacity] + int freeCount; + int watermark; // next blank index (starts at 1, skips 0) + int capacity; + size_t stride; +} sw_pool_t; - float vpCenter[2]; // Viewport center - float vpHalf[2]; // Viewport half dimensions - int vpSize[2]; // Viewport dimensions (minus one) - int vpMin[2]; // Viewport minimum renderable point (top-left) - int vpMax[2]; // Viewport maximum renderable point (bottom-right) +typedef void (*sw_blend_factor_t)(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst); +typedef void (*sw_raster_triangle_f)(const sw_vertex_t *v0, const sw_vertex_t *v1, const sw_vertex_t *v2); +typedef void (*sw_raster_quad_f)(const sw_vertex_t *v0, const sw_vertex_t *v1, const sw_vertex_t *v2, const sw_vertex_t *v3); +typedef void (*sw_raster_line_f)(const sw_vertex_t *v0, const sw_vertex_t *v1); +typedef void (*sw_raster_point_f)(const sw_vertex_t *v); - int scMin[2]; // Scissor rectangle minimum renderable point (top-left) - int scMax[2]; // Scissor rectangle maximum renderable point (bottom-right) - float scClipMin[2]; // Scissor rectangle minimum renderable point in clip space - float scClipMax[2]; // Scissor rectangle maximum renderable point in clip space +typedef struct { + sw_default_framebuffer_t framebuffer; // Default framebuffer + float clearColor[4]; // Clear color of the framebuffer + float clearDepth; // Clear depth of the framebuffer - uint32_t currentTexture; // Current active texture id + float vpCenter[2]; // Viewport center + float vpHalf[2]; // Viewport half dimensions + int vpSize[2]; // Viewport dimensions + + int scMin[2]; // Scissor rectangle minimum renderable point (top-left) + int scMax[2]; // Scissor rectangle maximum renderable point (bottom-right) + float scClipMin[2]; // Scissor rectangle minimum renderable point in clip space + float scClipMax[2]; // Scissor rectangle maximum renderable point in clip space struct { float *positions; @@ -897,7 +997,6 @@ typedef struct { SWdraw drawMode; // Current primitive mode (e.g., lines, triangles) SWpoly polyMode; // Current polygon filling mode (e.g., lines, triangles) - int reqVertices; // Number of vertices required for the primitive being drawn float pointRadius; // Rasterized point radius float lineWidth; // Rasterized line width @@ -912,21 +1011,23 @@ typedef struct { sw_matrix_t matMVP; // Model view projection matrix, calculated and used internally bool isDirtyMVP; // Indicates if the MVP matrix should be rebuilt + sw_handle_t boundFramebufferId; // Framebuffer currently bound + sw_texture_t* colorBuffer; // Color buffer currently bound + sw_texture_t* depthBuffer; // Depth buffer currently bound + sw_pool_t framebufferPool; // Framebuffer object pool + + sw_texture_t* boundTexture; // Texture currently bound + sw_pool_t texturePool; // Texture object pool + SWfactor srcFactor; SWfactor dstFactor; - sw_factor_f srcFactorFunc; - sw_factor_f dstFactorFunc; + sw_blend_factor_t srcFactorFunc; + sw_blend_factor_t dstFactorFunc; SWface cullFace; // Faces to cull SWerrcode errCode; // Last error code - sw_texture_t *loadedTextures; - int loadedTextureCount; - - uint32_t *freeTextureIds; - int freeTextureIdCount; - uint32_t stateFlags; } sw_context_t; @@ -936,7 +1037,44 @@ typedef struct { static sw_context_t RLSW = { 0 }; //---------------------------------------------------------------------------------- -// Module Functions Declaration +// Internal Constants Definition +//---------------------------------------------------------------------------------- +// Pixel formats sizes in bytes +static const int SW_PIXELFORMAT_SIZE[SW_PIXELFORMAT_COUNT] = +{ + [SW_PIXELFORMAT_COLOR_GRAYSCALE] = 1, + [SW_PIXELFORMAT_COLOR_GRAYALPHA] = 2, + [SW_PIXELFORMAT_COLOR_R3G3B2] = 1, + [SW_PIXELFORMAT_COLOR_R5G6B5] = 2, + [SW_PIXELFORMAT_COLOR_R8G8B8] = 3, + [SW_PIXELFORMAT_COLOR_R5G5B5A1] = 2, + [SW_PIXELFORMAT_COLOR_R4G4B4A4] = 2, + [SW_PIXELFORMAT_COLOR_R8G8B8A8] = 4, + [SW_PIXELFORMAT_COLOR_R32] = 4, + [SW_PIXELFORMAT_COLOR_R32G32B32] = 12, + [SW_PIXELFORMAT_COLOR_R32G32B32A32] = 16, + [SW_PIXELFORMAT_COLOR_R16] = 2, + [SW_PIXELFORMAT_COLOR_R16G16B16] = 6, + [SW_PIXELFORMAT_COLOR_R16G16B16A16] = 8, + [SW_PIXELFORMAT_DEPTH_D8] = 1, + [SW_PIXELFORMAT_DEPTH_D16] = 2, + [SW_PIXELFORMAT_DEPTH_D32] = 4, +}; + +static const int SW_PRIMITIVE_VERTEX_COUNT[] = +{ + // Remember that this is acceptable; these are small indices + [SW_POINTS] = 1, + [SW_LINES] = 2, + [SW_TRIANGLES] = 3, + [SW_QUADS] = 4, +}; + +//---------------------------------------------------------------------------------- +// Internal Functions Definitions +//---------------------------------------------------------------------------------- + +// Math helper functions //---------------------------------------------------------------------------------- static inline void sw_matrix_id(sw_matrix_t dst) { @@ -977,12 +1115,24 @@ static inline void sw_matrix_mul_rst(float *SW_RESTRICT dst, const float *SW_RES static inline void sw_matrix_mul(sw_matrix_t dst, const sw_matrix_t left, const sw_matrix_t right) { float result[16]; - sw_matrix_mul_rst(result, left, right); - for (int i = 0; i < 16; i++) dst[i] = result[i]; } +static inline int sw_clamp_int(int v, int min, int max) +{ + if (v < min) return min; + if (v > max) return max; + return v; +} + +static inline int sw_clamp(float v, float min, float max) +{ + if (v < min) return min; + if (v > max) return max; + return v; +} + static inline float sw_saturate(float x) { union { float f; uint32_t u; } fb; @@ -1005,11 +1155,14 @@ static inline float sw_fract(float x) return (x - floorf(x)); } -static inline int sw_clampi(int v, int min, int max) +static inline uint8_t sw_luminance8(const uint8_t *color) { - if (v < min) return min; - if (v > max) return max; - return v; + return (uint8_t)((color[0]*77 + color[1]*150 + color[2]*29) >> 8); +} + +static inline float sw_luminance(const float *color) +{ + return color[0]*0.299f + color[1]*0.587f + color[2]*0.114f; } static inline void sw_lerp_vertex_PTCH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT a, const sw_vertex_t *SW_RESTRICT b, float t) @@ -1117,105 +1270,30 @@ static inline void sw_add_vertex_grad_scaled_PTCH( out->homogeneous[3] += gradients->homogeneous[3]*scale; } -static inline void sw_float_to_unorm8_simd(uint8_t dst[4], const float src[4]) -{ -#if defined(SW_HAS_NEON) - float32x4_t values = vld1q_f32(src); - float32x4_t scaled = vmulq_n_f32(values, 255.0f); - int32x4_t clamped_s32 = vcvtq_s32_f32(scaled); // f32 -> s32 (truncated) - int16x4_t narrow16_s = vqmovn_s32(clamped_s32); - int16x8_t combined16_s = vcombine_s16(narrow16_s, narrow16_s); - uint8x8_t narrow8_u = vqmovun_s16(combined16_s); - vst1_lane_u32((uint32_t*)dst, vreinterpret_u32_u8(narrow8_u), 0); -#elif defined(SW_HAS_SSE41) - __m128 values = _mm_loadu_ps(src); - __m128 scaled = _mm_mul_ps(values, _mm_set1_ps(255.0f)); - __m128i clamped = _mm_cvtps_epi32(scaled); // f32 -> s32 (truncated) - clamped = _mm_packus_epi32(clamped, clamped); // s32 -> u16 (saturated < 0 to 0) - clamped = _mm_packus_epi16(clamped, clamped); // u16 -> u8 (saturated > 255 to 255) - *(uint32_t*)dst = _mm_cvtsi128_si32(clamped); -#elif defined(SW_HAS_SSE2) - __m128 values = _mm_loadu_ps(src); - __m128 scaled = _mm_mul_ps(values, _mm_set1_ps(255.0f)); - __m128i clamped = _mm_cvtps_epi32(scaled); // f32 -> s32 (truncated) - clamped = _mm_packs_epi32(clamped, clamped); // s32 -> s16 (saturated) - clamped = _mm_packus_epi16(clamped, clamped); // s16 -> u8 (saturated < 0 to 0) - *(uint32_t*)dst = _mm_cvtsi128_si32(clamped); -#elif defined(SW_HAS_RVV) - // TODO: Sample code generated by AI, needs testing and review - // NOTE: RVV 1.0 specs define the use of __riscv_ prefix for instrinsic functions - size_t vl = __riscv_vsetvl_e32m1(4); // Load up to 4 floats into a vector register - vfloat32m1_t vsrc = __riscv_vle32_v_f32m1(src, vl); // Load float32 values - - // Clamp to [0.0f, 1.0f] - vfloat32m1_t vzero = __riscv_vfmv_v_f_f32m1(0.0f, vl); - vfloat32m1_t vone = __riscv_vfmv_v_f_f32m1(1.0f, vl); - vsrc = __riscv_vfmin_vv_f32m1(vsrc, vone, vl); - vsrc = __riscv_vfmax_vv_f32m1(vsrc, vzero, vl); - - // Multiply by 255.0f and add 0.5f for rounding - vfloat32m1_t vscaled = __riscv_vfmul_vf_f32m1(vsrc, 255.0f, vl); - vscaled = __riscv_vfadd_vf_f32m1(vscaled, 0.5f, vl); - - // Convert to unsigned integer (truncate toward zero) - vuint32m1_t vu32 = __riscv_vfcvt_xu_f_v_u32m1(vscaled, vl); - - // Narrow from u32 -> u8 - vuint8m1_t vu8 = __riscv_vnclipu_wx_u8m1(vu32, 0, vl); // Round toward zero - __riscv_vse8_v_u8m1(dst, vu8, vl); // Store result -#else - for (int i = 0; i < 4; i++) - { - float val = src[i]*255.0f; - val = (val > 255.0f)? 255.0f : val; - val = (val < 0.0f)? 0.0f : val; - dst[i] = (uint8_t)val; - } -#endif -} - -static inline void sw_float_from_unorm8_simd(float dst[4], const uint8_t src[4]) -{ -#if defined(SW_HAS_NEON) - uint8x8_t bytes8 = vld1_u8(src); // Reading 8 bytes, faster, but let's hope not hitting the end of the page (unlikely)... - uint16x8_t bytes16 = vmovl_u8(bytes8); - uint32x4_t ints = vmovl_u16(vget_low_u16(bytes16)); - float32x4_t floats = vcvtq_f32_u32(ints); - floats = vmulq_n_f32(floats, SW_INV_255); - vst1q_f32(dst, floats); -#elif defined(SW_HAS_SSE41) - __m128i bytes = _mm_cvtsi32_si128(*(const uint32_t *)src); - __m128i ints = _mm_cvtepu8_epi32(bytes); - __m128 floats = _mm_cvtepi32_ps(ints); - floats = _mm_mul_ps(floats, _mm_set1_ps(SW_INV_255)); - _mm_storeu_ps(dst, floats); -#elif defined(SW_HAS_SSE2) - __m128i bytes = _mm_cvtsi32_si128(*(const uint32_t *)src); - bytes = _mm_unpacklo_epi8(bytes, _mm_setzero_si128()); - __m128i ints = _mm_unpacklo_epi16(bytes, _mm_setzero_si128()); - __m128 floats = _mm_cvtepi32_ps(ints); - floats = _mm_mul_ps(floats, _mm_set1_ps(SW_INV_255)); - _mm_storeu_ps(dst, floats); -#elif defined(SW_HAS_RVV) - // TODO: Sample code generated by AI, needs testing and review - size_t vl = __riscv_vsetvl_e8m1(4); // Set vector length for 8-bit input elements - vuint8m1_t vsrc_u8 = __riscv_vle8_v_u8m1(src, vl); // Load 4 unsigned 8-bit integers - vuint32m1_t vsrc_u32 = __riscv_vwcvt_xu_u_v_u32m1(vsrc_u8, vl); // Widen to 32-bit unsigned integers - vfloat32m1_t vsrc_f32 = __riscv_vfcvt_f_xu_v_f32m1(vsrc_u32, vl); // Convert to float32 - vfloat32m1_t vnorm = __riscv_vfmul_vf_f32m1(vsrc_f32, SW_INV_255, vl); // Multiply by 1/255.0 to normalize - __riscv_vse32_v_f32m1(dst, vnorm, vl); // Store result -#else - dst[0] = (float)src[0]*SW_INV_255; - dst[1] = (float)src[1]*SW_INV_255; - dst[2] = (float)src[2]*SW_INV_255; - dst[3] = (float)src[3]*SW_INV_255; -#endif -} - // Half conversion functions +static inline uint16_t sw_float_to_half_ui(uint32_t ui) +{ + int32_t s = (ui >> 16) & 0x8000; + int32_t em = ui & 0x7fffffff; + + // bias exponent and round to nearest; 112 is relative exponent bias (127-15) + int32_t h = (em - (112 << 23) + (1 << 12)) >> 13; + + // underflow: flush to zero; 113 encodes exponent -14 + h = (em < (113 << 23))? 0 : h; + + // overflow: infinity; 143 encodes exponent 16 + h = (em >= (143 << 23))? 0x7c00 : h; + + // NaN; note that we convert all types of NaN to qNaN + h = (em > (255 << 23))? 0x7e00 : h; + + return (uint16_t)(s | h); +} + static inline uint32_t sw_half_to_float_ui(uint16_t h) { - uint32_t s = (uint32_t)(h & 0x8000) << 16; + uint32_t s = (unsigned)(h & 0x8000) << 16; int32_t em = h & 0x7fff; // bias exponent and pad mantissa with 0; 112 is relative exponent bias (127-15) @@ -1224,2229 +1302,147 @@ static inline uint32_t sw_half_to_float_ui(uint16_t h) // denormal: flush to zero r = (em < (1 << 10))? 0 : r; - // NOTE: infinity/NaN; NaN payload is preserved as a byproduct of unifying inf/nan cases - // 112 is an exponent bias fixup; since it is already applied once, applying it twice converts 31 to 255 + // infinity/NaN; note that we preserve NaN payload as a byproduct of unifying inf/nan cases + // 112 is an exponent bias fixup; since we already applied it once, applying it twice converts 31 to 255 r += (em >= (31 << 10))? (112 << 23) : 0; return s | r; } -static inline float sw_half_to_float(sw_half_t y) -{ - union { float f; uint32_t i; } v = { .i = sw_half_to_float_ui(y) }; - - return v.f; -} - -static inline uint16_t sw_half_from_float_ui(uint32_t ui) -{ - int32_t s = (ui >> 16) & 0x8000; - int32_t em = ui & 0x7fffffff; - - // Bias exponent and round to nearest; 112 is relative exponent bias (127-15) - int32_t h = (em - (112 << 23) + (1 << 12)) >> 13; - - // Underflow: flush to zero; 113 encodes exponent -14 - h = (em < (113 << 23))? 0 : h; - - // Overflow: infinity; 143 encodes exponent 16 - h = (em >= (143 << 23))? 0x7c00 : h; - - // NOTE: NaN; all types of NaN aree converted to qNaN - h = (em > (255 << 23))? 0x7e00 : h; - - return (uint16_t)(s | h); -} - -static inline sw_half_t sw_half_from_float(float i) +static inline sw_half_t sw_float_to_half(float i) { union { float f; uint32_t i; } v; v.f = i; - return sw_half_from_float_ui(v.i); + return sw_float_to_half_ui(v.i); } -// Framebuffer management functions -//------------------------------------------------------------------------------------------- -static inline bool sw_framebuffer_load(int w, int h) +static inline float sw_half_to_float(sw_half_t y) { - int size = w*h; + union { float f; uint32_t i; } v; + v.i = sw_half_to_float_ui(y); + return v.f; +} - RLSW.framebuffer.pixels = SW_MALLOC(sizeof(sw_pixel_t)*size); - if (RLSW.framebuffer.pixels == NULL) return false; +static inline uint8_t sw_expand_1to8(uint32_t v) { return v ? 255 : 0; } +static inline uint8_t sw_expand_2to8(uint32_t v) { return (uint8_t)(v*85); } +static inline uint8_t sw_expand_3to8(uint32_t v) { return (uint8_t)((v << 5) | (v << 2) | (v >> 1)); } +static inline uint8_t sw_expand_4to8(uint32_t v) { return (uint8_t)((v << 4) | v); } +static inline uint8_t sw_expand_5to8(uint32_t v) { return (uint8_t)((v << 3) | (v >> 2)); } +static inline uint8_t sw_expand_6to8(uint32_t v) { return (uint8_t)((v << 2) | (v >> 4)); } - RLSW.framebuffer.width = w; - RLSW.framebuffer.height = h; - RLSW.framebuffer.allocSz = size; +static inline float sw_expand_1tof(uint32_t v) { return v ? 1.0f : 0.0f; } +static inline float sw_expand_2tof(uint32_t v) { return (float)v*(1.0f/3.0f); } +static inline float sw_expand_3tof(uint32_t v) { return (float)v*(1.0f/7.0f); } +static inline float sw_expand_4tof(uint32_t v) { return (float)v*(1.0f/15.0f); } +static inline float sw_expand_5tof(uint32_t v) { return (float)v*(1.0f/31.0f); } +static inline float sw_expand_6tof(uint32_t v) { return (float)v*(1.0f/63.0f); } + +static inline uint32_t sw_compress_8to1(uint8_t v) { return v >> 7; } +static inline uint32_t sw_compress_8to2(uint8_t v) { return v >> 6; } +static inline uint32_t sw_compress_8to3(uint8_t v) { return v >> 5; } +static inline uint32_t sw_compress_8to4(uint8_t v) { return v >> 4; } +static inline uint32_t sw_compress_8to5(uint8_t v) { return v >> 3; } +static inline uint32_t sw_compress_8to6(uint8_t v) { return v >> 2; } + +static inline uint32_t sw_compress_fto1(float v) { return v >= 0.5f ? 1 : 0; } +static inline uint32_t sw_compress_fto2(float v) { return (uint32_t)(v* 3.0f + 0.5f) & 0x03; } +static inline uint32_t sw_compress_fto3(float v) { return (uint32_t)(v* 7.0f + 0.5f) & 0x07; } +static inline uint32_t sw_compress_fto4(float v) { return (uint32_t)(v*15.0f + 0.5f) & 0x0F; } +static inline uint32_t sw_compress_fto5(float v) { return (uint32_t)(v*31.0f + 0.5f) & 0x1F; } +static inline uint32_t sw_compress_fto6(float v) { return (uint32_t)(v*63.0f + 0.5f) & 0x3F; } +//------------------------------------------------------------------------------------------- + +// Object pool functions +//------------------------------------------------------------------------------------------- +static bool sw_pool_init(sw_pool_t *pool, int capacity, size_t stride) +{ + *pool = (sw_pool_t) { 0 }; + + pool->data = SW_CALLOC(capacity, stride); + pool->gen = SW_CALLOC(capacity, sizeof(uint8_t)); + pool->freeList = SW_MALLOC(capacity*sizeof(uint32_t)); + + if (!pool->data || !pool->gen || !pool->freeList) + { + SW_FREE(pool->data); + SW_FREE(pool->gen); + SW_FREE(pool->freeList); + return false; + } + + pool->watermark = 1; + pool->capacity = capacity; + pool->stride = stride; return true; } -static inline bool sw_framebuffer_resize(int w, int h) +static void sw_pool_destroy(sw_pool_t *pool) { - int newSize = w*h; + SW_FREE(pool->data); + SW_FREE(pool->gen); + SW_FREE(pool->freeList); + *pool = (sw_pool_t) { 0 }; +} - if (newSize <= RLSW.framebuffer.allocSz) +static sw_handle_t sw_pool_alloc(sw_pool_t *pool) +{ + uint32_t index; + + if (pool->freeCount > 0) { - RLSW.framebuffer.width = w; - RLSW.framebuffer.height = h; - return true; + index = pool->freeList[--pool->freeCount]; + } + else + { + if (pool->watermark >= pool->capacity) return SW_HANDLE_NULL; + index = (uint32_t)pool->watermark++; } - void *newPixels = SW_REALLOC(RLSW.framebuffer.pixels, sizeof(sw_pixel_t)*newSize); - if (newPixels == NULL) return false; + uint8_t ver = ((pool->gen[index] & SW_POOL_SLOT_VER_MASK) + 1) & SW_POOL_SLOT_VER_MASK; + if (ver == 0) ver = 1; + pool->gen[index] = SW_POOL_SLOT_LIVE | ver; - RLSW.framebuffer.pixels = newPixels; + uint8_t *slot = (uint8_t *)pool->data + index*pool->stride; + for (size_t i = 0; i < pool->stride; i++) slot[i] = 0; - RLSW.framebuffer.width = w; - RLSW.framebuffer.height = h; - RLSW.framebuffer.allocSz = newSize; + return (sw_handle_t)index; +} +static void *sw_pool_get(const sw_pool_t *pool, sw_handle_t handle) +{ + if (handle == SW_HANDLE_NULL) return NULL; + if ((int)handle >= pool->capacity) return NULL; + if (!(pool->gen[handle] & SW_POOL_SLOT_LIVE)) return NULL; + + return (char *)pool->data + handle*pool->stride; +} + +static bool sw_pool_valid(const sw_pool_t *pool, sw_handle_t handle) +{ + return sw_pool_get(pool, handle) != NULL; +} + +static bool sw_pool_free(sw_pool_t *pool, sw_handle_t handle) +{ + if (!sw_pool_valid(pool, handle)) return false; + + pool->gen[handle] &= SW_POOL_SLOT_VER_MASK; // delete live flag + pool->freeList[pool->freeCount++] = handle; return true; } - -static inline void sw_framebuffer_read_color(float dst[4], const sw_pixel_t *src) -{ -#if SW_COLOR_IS_PACKED - SW_COLOR_TYPE pixel = src->color[0]; - dst[0] = SW_TO_FLOAT_R(SW_UNPACK_R(pixel)); - dst[1] = SW_TO_FLOAT_G(SW_UNPACK_G(pixel)); - dst[2] = SW_TO_FLOAT_B(SW_UNPACK_B(pixel)); - dst[3] = 1.0f; -#else - sw_float_from_unorm8_simd(dst, src->color); -#endif -} - -static inline void sw_framebuffer_read_color8(uint8_t dst[4], const sw_pixel_t *src) -{ -#if SW_COLOR_IS_PACKED - SW_COLOR_TYPE pixel = src->color[0]; - dst[0] = SW_SCALE_R(SW_UNPACK_R(pixel)); - dst[1] = SW_SCALE_G(SW_UNPACK_G(pixel)); - dst[2] = SW_SCALE_B(SW_UNPACK_B(pixel)); - dst[3] = 255; -#else - const SW_COLOR_TYPE *p = src->color; - dst[0] = p[0]; - dst[1] = p[1]; - dst[2] = p[2]; - dst[3] = p[3]; -#endif -} - -static inline float sw_framebuffer_read_depth(const sw_pixel_t *src) -{ -#if SW_DEPTH_IS_PACKED - return src->depth[0]*SW_DEPTH_SCALE; -#else - return SW_UNPACK_DEPTH(src->depth)*SW_DEPTH_SCALE; -#endif -} - -static inline void sw_framebuffer_write_color(sw_pixel_t *dst, const float src[4]) -{ -#if SW_COLOR_IS_PACKED - dst->color[0] = SW_PACK_COLOR(src[0], src[1], src[2]); -#else - sw_float_to_unorm8_simd(dst->color, src); -#endif -} - -static inline void sw_framebuffer_write_depth(sw_pixel_t *dst, float depth) -{ - depth = sw_saturate(depth); // REVIEW: An overflow can occur in certain circumstances with clipping, and needs to be reviewed... - -#if SW_DEPTH_IS_PACKED - dst->depth[0] = SW_PACK_DEPTH(depth); -#else - dst->depth[0] = SW_PACK_DEPTH_0(depth); - dst->depth[1] = SW_PACK_DEPTH_1(depth); - dst->depth[2] = SW_PACK_DEPTH_2(depth); -#endif -} - -static inline void sw_framebuffer_fill_color(sw_pixel_t *ptr, int size, const SW_COLOR_TYPE color[SW_COLOR_PACK_COMP]) -{ - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) - { - int w = RLSW.scMax[0] - RLSW.scMin[0] + 1; - for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) - { - sw_pixel_t *row = ptr + y*RLSW.framebuffer.width + RLSW.scMin[0]; - for (int x = 0; x < w; x++, row++) - { - for (int i = 0; i < SW_COLOR_PACK_COMP; i++) row->color[i] = color[i]; - } - } - } - else - { - for (int i = 0; i < size; i++, ptr++) - { - for (int j = 0; j < SW_COLOR_PACK_COMP; j++) ptr->color[j] = color[j]; - } - } -} - -static inline void sw_framebuffer_fill_depth(sw_pixel_t *ptr, int size, const SW_DEPTH_TYPE depth[SW_DEPTH_PACK_COMP]) -{ - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) - { - int w = RLSW.scMax[0] - RLSW.scMin[0] + 1; - for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) - { - sw_pixel_t *row = ptr + y*RLSW.framebuffer.width + RLSW.scMin[0]; - for (int x = 0; x < w; x++, row++) - { - for (int i = 0; i < SW_DEPTH_PACK_COMP; i++) row->depth[i] = depth[i]; - } - } - } - else - { - for (int i = 0; i < size; i++, ptr++) - { - for (int j = 0; j < SW_DEPTH_PACK_COMP; j++) ptr->depth[j] = depth[j]; - } - } -} - -static inline void sw_framebuffer_fill(sw_pixel_t *ptr, int size, sw_pixel_t value) -{ - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) - { - int w = RLSW.scMax[0] - RLSW.scMin[0] + 1; - for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) - { - sw_pixel_t *row = ptr + y*RLSW.framebuffer.width + RLSW.scMin[0]; - for (int x = 0; x < w; x++, row++) *row = value; - } - } - else - { - for (int i = 0; i < size; i++, ptr++) *ptr = value; - } -} - -static inline void sw_framebuffer_copy_fast(void* dst) -{ - int size = RLSW.framebuffer.width*RLSW.framebuffer.height; - const sw_pixel_t *pixels = RLSW.framebuffer.pixels; - -#if SW_COLOR_BUFFER_BITS == 8 - uint8_t *dst8 = (uint8_t*)dst; - for (int i = 0; i < size; i++) dst8[i] = pixels[i].color[0]; -#elif SW_COLOR_BUFFER_BITS == 16 - uint16_t *dst16 = (uint16_t*)dst; - for (int i = 0; i < size; i++) dst16[i] = *(uint16_t*)pixels[i].color; -#else // 32 bits - uint32_t *dst32 = (uint32_t*)dst; - #if SW_GL_FRAMEBUFFER_COPY_BGRA - for (int i = 0; i < size; i++) - { - const uint8_t *c = pixels[i].color; - dst32[i] = (uint32_t)c[2] | ((uint32_t)c[1] << 8) | ((uint32_t)c[0] << 16) | ((uint32_t)c[3] << 24); - } - #else // RGBA - for (int i = 0; i < size; i++) dst32[i] = *(uint32_t*)pixels[i].color; - #endif -#endif -} - -#define DEFINE_FRAMEBUFFER_COPY_BEGIN(name, DST_PTR_T) \ -static inline void sw_framebuffer_copy_to_##name(int x, int y, int w, int h, DST_PTR_T *dst) \ -{ \ - const int stride = RLSW.framebuffer.width; \ - const sw_pixel_t *src = RLSW.framebuffer.pixels + (y*stride + x); \ - \ - for (int iy = 0; iy < h; iy++) { \ - const sw_pixel_t *line = src; \ - for (int ix = 0; ix < w; ix++) { \ - uint8_t color[4]; \ - sw_framebuffer_read_color8(color, line); \ - -#define DEFINE_FRAMEBUFFER_COPY_END() \ - ++line; \ - } \ - src += stride; \ - } \ -} - -DEFINE_FRAMEBUFFER_COPY_BEGIN(GRAYSCALE, uint8_t) -{ - // NTSC grayscale conversion: Y = 0.299R + 0.587G + 0.114B - uint8_t gray = (uint8_t)((color[0]*299 + color[1]*587 + color[2]*114 + 500)/1000); - *dst++ = gray; -} -DEFINE_FRAMEBUFFER_COPY_END() - -DEFINE_FRAMEBUFFER_COPY_BEGIN(GRAYALPHA, uint8_t) -{ - // Convert RGB to grayscale using NTSC formula - uint8_t gray = (uint8_t)((color[0]*299 + color[1]*587 + color[2]*114 + 500)/1000); - - dst[0] = gray; - dst[1] = color[3]; // alpha - - dst += 2; -} -DEFINE_FRAMEBUFFER_COPY_END() - -DEFINE_FRAMEBUFFER_COPY_BEGIN(R5G6B5, uint16_t) -{ - // Convert 8-bit RGB to 5:6:5 format - uint8_t r5 = (color[0]*31 + 127)/255; - uint8_t g6 = (color[1]*63 + 127)/255; - uint8_t b5 = (color[2]*31 + 127)/255; - -#if SW_GL_FRAMEBUFFER_COPY_BGRA - uint16_t rgb565 = (b5 << 11) | (g6 << 5) | r5; -#else // RGBA - uint16_t rgb565 = (r5 << 11) | (g6 << 5) | b5; -#endif - - *dst++ = rgb565; -} -DEFINE_FRAMEBUFFER_COPY_END() - -DEFINE_FRAMEBUFFER_COPY_BEGIN(R8G8B8, uint8_t) -{ -#if SW_GL_FRAMEBUFFER_COPY_BGRA - dst[0] = color[2]; - dst[1] = color[1]; - dst[2] = color[0]; -#else // RGBA - dst[0] = color[0]; - dst[1] = color[1]; - dst[2] = color[2]; -#endif - - dst += 3; -} -DEFINE_FRAMEBUFFER_COPY_END() - -DEFINE_FRAMEBUFFER_COPY_BEGIN(R5G5B5A1, uint16_t) -{ - uint8_t r5 = (color[0]*31 + 127)/255; - uint8_t g5 = (color[1]*31 + 127)/255; - uint8_t b5 = (color[2]*31 + 127)/255; - uint8_t a1 = (color[3] >= 128)? 1 : 0; - -#if SW_GL_FRAMEBUFFER_COPY_BGRA - uint16_t pixel = (b5 << 11) | (g5 << 6) | (r5 << 1) | a1; -#else // RGBA - uint16_t pixel = (r5 << 11) | (g5 << 6) | (b5 << 1) | a1; -#endif - - *dst++ = pixel; -} -DEFINE_FRAMEBUFFER_COPY_END() - -DEFINE_FRAMEBUFFER_COPY_BEGIN(R4G4B4A4, uint16_t) -{ - uint8_t r4 = (color[0]*15 + 127)/255; - uint8_t g4 = (color[1]*15 + 127)/255; - uint8_t b4 = (color[2]*15 + 127)/255; - uint8_t a4 = (color[3]*15 + 127)/255; - -#if SW_GL_FRAMEBUFFER_COPY_BGRA - uint16_t pixel = (b4 << 12) | (g4 << 8) | (r4 << 4) | a4; -#else // RGBA - uint16_t pixel = (r4 << 12) | (g4 << 8) | (b4 << 4) | a4; -#endif - - *dst++ = pixel; -} -DEFINE_FRAMEBUFFER_COPY_END() - -DEFINE_FRAMEBUFFER_COPY_BEGIN(R8G8B8A8, uint8_t) -{ -#if SW_GL_FRAMEBUFFER_COPY_BGRA - dst[0] = color[2]; - dst[1] = color[1]; - dst[2] = color[0]; -#else // RGBA - dst[0] = color[0]; - dst[1] = color[1]; - dst[2] = color[2]; -#endif - dst[3] = color[3]; - - dst += 4; -} -DEFINE_FRAMEBUFFER_COPY_END() - -#define DEFINE_FRAMEBUFFER_BLIT_BEGIN(name, DST_PTR_T) \ -static inline void sw_framebuffer_blit_to_##name( \ - int xDst, int yDst, int wDst, int hDst, \ - int xSrc, int ySrc, int wSrc, int hSrc, \ - DST_PTR_T *dst) \ -{ \ - const sw_pixel_t *srcBase = RLSW.framebuffer.pixels; \ - const int fbWidth = RLSW.framebuffer.width; \ - \ - const uint32_t xScale = ((uint32_t)wSrc << 16)/(uint32_t)wDst; \ - const uint32_t yScale = ((uint32_t)hSrc << 16)/(uint32_t)hDst; \ - \ - for (int dy = 0; dy < hDst; dy++) { \ - uint32_t yFix = ((uint32_t)ySrc << 16) + dy*yScale; \ - int sy = yFix >> 16; \ - const sw_pixel_t *srcLine = srcBase + sy*fbWidth + xSrc; \ - \ - const sw_pixel_t *srcPtr = srcLine; \ - for (int dx = 0; dx < wDst; dx++) { \ - uint32_t xFix = dx*xScale; \ - int sx = xFix >> 16; \ - const sw_pixel_t *pixel = srcPtr + sx; \ - uint8_t color[4]; \ - sw_framebuffer_read_color8(color, pixel); - -#define DEFINE_FRAMEBUFFER_BLIT_END() \ - } \ - } \ -} - -DEFINE_FRAMEBUFFER_BLIT_BEGIN(GRAYSCALE, uint8_t) -{ - uint8_t gray = (uint8_t)((color[0]*299 + color[1]*587 + color[2]*114 + 500)/1000); - *dst++ = gray; -} -DEFINE_FRAMEBUFFER_BLIT_END() - -DEFINE_FRAMEBUFFER_BLIT_BEGIN(GRAYALPHA, uint8_t) -{ - uint8_t gray = (uint8_t)((color[0]*299 + color[1]*587 + color[2]*114 + 500)/1000); - - dst[0] = gray; - dst[1] = color[3]; // alpha - - dst += 2; -} -DEFINE_FRAMEBUFFER_BLIT_END() - -DEFINE_FRAMEBUFFER_BLIT_BEGIN(R5G6B5, uint16_t) -{ - uint8_t r5 = (color[0]*31 + 127)/255; - uint8_t g6 = (color[1]*63 + 127)/255; - uint8_t b5 = (color[2]*31 + 127)/255; - -#if SW_GL_FRAMEBUFFER_COPY_BGRA - uint16_t rgb565 = (b5 << 11) | (g6 << 5) | r5; -#else // RGBA - uint16_t rgb565 = (r5 << 11) | (g6 << 5) | b5; -#endif - - *dst++ = rgb565; -} -DEFINE_FRAMEBUFFER_BLIT_END() - -DEFINE_FRAMEBUFFER_BLIT_BEGIN(R8G8B8, uint8_t) -{ -#if SW_GL_FRAMEBUFFER_COPY_BGRA - dst[0] = color[2]; - dst[1] = color[1]; - dst[2] = color[0]; -#else // RGBA - dst[0] = color[0]; - dst[1] = color[1]; - dst[2] = color[2]; -#endif - - dst += 3; -} -DEFINE_FRAMEBUFFER_BLIT_END() - -DEFINE_FRAMEBUFFER_BLIT_BEGIN(R5G5B5A1, uint16_t) -{ - uint8_t r5 = (color[0]*31 + 127)/255; - uint8_t g5 = (color[1]*31 + 127)/255; - uint8_t b5 = (color[2]*31 + 127)/255; - uint8_t a1 = (color[3] >= 128)? 1 : 0; - -#if SW_GL_FRAMEBUFFER_COPY_BGRA - uint16_t pixel = (b5 << 11) | (g5 << 6) | (r5 << 1) | a1; -#else // RGBA - uint16_t pixel = (r5 << 11) | (g5 << 6) | (b5 << 1) | a1; -#endif - - *dst++ = pixel; -} -DEFINE_FRAMEBUFFER_BLIT_END() - -DEFINE_FRAMEBUFFER_BLIT_BEGIN(R4G4B4A4, uint16_t) -{ - uint8_t r4 = (color[0]*15 + 127)/255; - uint8_t g4 = (color[1]*15 + 127)/255; - uint8_t b4 = (color[2]*15 + 127)/255; - uint8_t a4 = (color[3]*15 + 127)/255; - -#if SW_GL_FRAMEBUFFER_COPY_BGRA - uint16_t pixel = (b4 << 12) | (g4 << 8) | (r4 << 4) | a4; -#else // RGBA - uint16_t pixel = (r4 << 12) | (g4 << 8) | (b4 << 4) | a4; -#endif - - *dst++ = pixel; -} -DEFINE_FRAMEBUFFER_BLIT_END() - -DEFINE_FRAMEBUFFER_BLIT_BEGIN(R8G8B8A8, uint8_t) -{ -#if SW_GL_FRAMEBUFFER_COPY_BGRA - dst[0] = color[2]; - dst[1] = color[1]; - dst[2] = color[0]; -#else // RGBA - dst[0] = color[0]; - dst[1] = color[1]; - dst[2] = color[2]; -#endif - dst[3] = color[3]; - - dst += 4; -} -DEFINE_FRAMEBUFFER_BLIT_END() -//------------------------------------------------------------------------------------------- - -// Pixel format management functions -//------------------------------------------------------------------------------------------- -static inline int sw_get_pixel_format(SWformat format, SWtype type) -{ - int channels = 0; - int bitsPerChannel = 8; // Default: 8 bits per channel - - // Determine the number of channels (format) - switch (format) - { - case SW_LUMINANCE: channels = 1; break; - case SW_LUMINANCE_ALPHA: channels = 2; break; - case SW_RGB: channels = 3; break; - case SW_RGBA: channels = 4; break; - default: return SW_PIXELFORMAT_UNKNOWN; - } - - // Determine the depth of each channel (type) - switch (type) - { - case SW_UNSIGNED_BYTE: bitsPerChannel = 8; break; - case SW_BYTE: bitsPerChannel = 8; break; - case SW_UNSIGNED_SHORT: bitsPerChannel = 16; break; - case SW_SHORT: bitsPerChannel = 16; break; - case SW_UNSIGNED_INT: bitsPerChannel = 32; break; - case SW_INT: bitsPerChannel = 32; break; - case SW_FLOAT: bitsPerChannel = 32; break; - default: return SW_PIXELFORMAT_UNKNOWN; - } - - // Map the format and type to the correct internal format - if (bitsPerChannel == 8) - { - if (channels == 1) return SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; - if (channels == 2) return SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA; - if (channels == 3) return SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8; - if (channels == 4) return SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; - } - else if (bitsPerChannel == 16) - { - if (channels == 1) return SW_PIXELFORMAT_UNCOMPRESSED_R16; - if (channels == 3) return SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16; - if (channels == 4) return SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16; - } - else if (bitsPerChannel == 32) - { - if (channels == 1) return SW_PIXELFORMAT_UNCOMPRESSED_R32; - if (channels == 3) return SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32; - if (channels == 4) return SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32; - } - - return SW_PIXELFORMAT_UNKNOWN; -} - -static inline void sw_get_pixel(uint8_t *color, const void *pixels, uint32_t offset, sw_pixelformat_t format) -{ - switch (format) - { - case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: - { - uint8_t gray = ((const uint8_t*)pixels)[offset]; - color[0] = gray; - color[1] = gray; - color[2] = gray; - color[3] = 255; - break; - } - case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: - { - const uint8_t *src = &((const uint8_t*)pixels)[offset*2]; - color[0] = src[0]; - color[1] = src[0]; - color[2] = src[0]; - color[3] = src[1]; - break; - } - case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: - { - uint16_t pixel = ((const uint16_t*)pixels)[offset]; - color[0] = ((pixel >> 11) & 0x1F)*255/31; // R (5 bits) - color[1] = ((pixel >> 5) & 0x3F)*255/63; // G (6 bits) - color[2] = (pixel & 0x1F)*255/31; // B (5 bits) - color[3] = 255; - break; - } - case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: - { - const uint8_t *src = &((const uint8_t*)pixels)[offset*3]; - color[0] = src[0]; - color[1] = src[1]; - color[2] = src[2]; - color[3] = 255; - break; - } - case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: - { - uint16_t pixel = ((const uint16_t*)pixels)[offset]; - color[0] = ((pixel >> 11) & 0x1F)*255/31; // R (5 bits) - color[1] = ((pixel >> 6) & 0x1F)*255/31; // G (5 bits) - color[2] = ((pixel >> 1) & 0x1F)*255/31; // B (5 bits) - color[3] = (pixel & 0x01)*255; // A (1 bit) - break; - } - case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: - { - uint16_t pixel = ((const uint16_t*)pixels)[offset]; - color[0] = ((pixel >> 12) & 0x0F)*255/15; // R (4 bits) - color[1] = ((pixel >> 8) & 0x0F)*255/15; // G (4 bits) - color[2] = ((pixel >> 4) & 0x0F)*255/15; // B (4 bits) - color[3] = (pixel & 0x0F)*255/15; // A (4 bits) - break; - } - case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: - { - const uint8_t *src = &((const uint8_t*)pixels)[offset*4]; - color[0] = src[0]; - color[1] = src[1]; - color[2] = src[2]; - color[3] = src[3]; - break; - } - case SW_PIXELFORMAT_UNCOMPRESSED_R32: - { - float val = ((const float*)pixels)[offset]; - uint8_t gray = (uint8_t)(val*255.0f); - color[0] = gray; - color[1] = gray; - color[2] = gray; - color[3] = 255; - break; - } - case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: - { - const float *src = &((const float*)pixels)[offset*3]; - color[0] = (uint8_t)(src[0]*255.0f); - color[1] = (uint8_t)(src[1]*255.0f); - color[2] = (uint8_t)(src[2]*255.0f); - color[3] = 255; - break; - } - case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: - { - const float *src = &((const float*)pixels)[offset*4]; - color[0] = (uint8_t)(src[0]*255.0f); - color[1] = (uint8_t)(src[1]*255.0f); - color[2] = (uint8_t)(src[2]*255.0f); - color[3] = (uint8_t)(src[3]*255.0f); - break; - } - case SW_PIXELFORMAT_UNCOMPRESSED_R16: - { - uint16_t val = ((const uint16_t*)pixels)[offset]; - uint8_t gray = sw_half_to_float(val)*SW_INV_255; - color[0] = gray; - color[1] = gray; - color[2] = gray; - color[3] = 255; - break; - } - case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: - { - const uint16_t *src = &((const uint16_t*)pixels)[offset*3]; - color[0] = sw_half_to_float(src[0])*SW_INV_255; - color[1] = sw_half_to_float(src[1])*SW_INV_255; - color[2] = sw_half_to_float(src[2])*SW_INV_255; - color[3] = 255; - break; - } - case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: - { - const uint16_t *src = &((const uint16_t*)pixels)[offset*4]; - color[0] = sw_half_to_float(src[0])*SW_INV_255; - color[1] = sw_half_to_float(src[1])*SW_INV_255; - color[2] = sw_half_to_float(src[2])*SW_INV_255; - color[3] = sw_half_to_float(src[3])*SW_INV_255; - break; - } - case SW_PIXELFORMAT_UNKNOWN: - default: - { - color[0] = 0; - color[1] = 0; - color[2] = 0; - color[3] = 0; - break; - } - } -} -//------------------------------------------------------------------------------------------- - -// Texture sampling functionality -//------------------------------------------------------------------------------------------- -static inline void sw_texture_fetch(float* color, const sw_texture_t* tex, int x, int y) -{ - sw_float_from_unorm8_simd(color, &tex->pixels[4*(y*tex->width + x)]); -} - -static inline void sw_texture_sample_nearest(float *color, const sw_texture_t *tex, float u, float v) -{ - u = (tex->sWrap == SW_REPEAT)? sw_fract(u) : sw_saturate(u); - v = (tex->tWrap == SW_REPEAT)? sw_fract(v) : sw_saturate(v); - - int x = u*tex->width; - int y = v*tex->height; - - sw_texture_fetch(color, tex, x, y); -} - -static inline void sw_texture_sample_linear(float *color, const sw_texture_t *tex, float u, float v) -{ - // TODO: With a bit more cleverness thee number of operations can - // be clearly reduced, but for now it works fine - - float xf = (u*tex->width) - 0.5f; - float yf = (v*tex->height) - 0.5f; - - float fx = sw_fract(xf); - float fy = sw_fract(yf); - - int x0 = (int)xf; - int y0 = (int)yf; - - int x1 = x0 + 1; - int y1 = y0 + 1; - - // NOTE: If the textures are POT, avoid the division for SW_REPEAT - - if (tex->sWrap == SW_CLAMP) - { - x0 = (x0 > tex->wMinus1)? tex->wMinus1 : x0; - x1 = (x1 > tex->wMinus1)? tex->wMinus1 : x1; - } - else - { - x0 = (x0%tex->width + tex->width)%tex->width; - x1 = (x1%tex->width + tex->width)%tex->width; - } - - if (tex->tWrap == SW_CLAMP) - { - y0 = (y0 > tex->hMinus1)? tex->hMinus1 : y0; - y1 = (y1 > tex->hMinus1)? tex->hMinus1 : y1; - } - else - { - y0 = (y0%tex->height + tex->height)%tex->height; - y1 = (y1%tex->height + tex->height)%tex->height; - } - - float c00[4], c10[4], c01[4], c11[4]; - sw_texture_fetch(c00, tex, x0, y0); - sw_texture_fetch(c10, tex, x1, y0); - sw_texture_fetch(c01, tex, x0, y1); - sw_texture_fetch(c11, tex, x1, y1); - - for (int i = 0; i < 4; i++) - { - float t = c00[i] + fx*(c10[i] - c00[i]); - float b = c01[i] + fx*(c11[i] - c01[i]); - color[i] = t + fy*(b - t); - } -} - -static inline void sw_texture_sample(float *color, const sw_texture_t *tex, float u, float v, float dUdx, float dUdy, float dVdx, float dVdy) -{ - // Previous method: There is no need to compute the square root - // because using the squared value, the comparison remains (L2 > 1.0f*1.0f) - //float du = sqrtf(dUdx*dUdx + dUdy*dUdy); - //float dv = sqrtf(dVdx*dVdx + dVdy*dVdy); - //float L = (du > dv)? du : dv; - - // Calculate the derivatives for each axis - float dU2 = dUdx*dUdx + dUdy*dUdy; - float dV2 = dVdx*dVdx + dVdy*dVdy; - float L2 = (dU2 > dV2)? dU2 : dV2; - - SWfilter filter = (L2 > 1.0f)? tex->minFilter : tex->magFilter; - - switch (filter) - { - case SW_NEAREST: sw_texture_sample_nearest(color, tex, u, v); break; - case SW_LINEAR: sw_texture_sample_linear(color, tex, u, v); break; - default: break; - } -} -//------------------------------------------------------------------------------------------- - -// Color blending functionality -//------------------------------------------------------------------------------------------- -static inline void sw_factor_zero(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) -{ - factor[0] = factor[1] = factor[2] = factor[3] = 0.0f; -} - -static inline void sw_factor_one(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) -{ - factor[0] = factor[1] = factor[2] = factor[3] = 1.0f; -} - -static inline void sw_factor_src_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) -{ - factor[0] = src[0]; factor[1] = src[1]; factor[2] = src[2]; factor[3] = src[3]; -} - -static inline void sw_factor_one_minus_src_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) -{ - factor[0] = 1.0f - src[0]; factor[1] = 1.0f - src[1]; - factor[2] = 1.0f - src[2]; factor[3] = 1.0f - src[3]; -} - -static inline void sw_factor_src_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) -{ - factor[0] = factor[1] = factor[2] = factor[3] = src[3]; -} - -static inline void sw_factor_one_minus_src_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) -{ - float invAlpha = 1.0f - src[3]; - factor[0] = factor[1] = factor[2] = factor[3] = invAlpha; -} - -static inline void sw_factor_dst_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) -{ - factor[0] = factor[1] = factor[2] = factor[3] = dst[3]; -} - -static inline void sw_factor_one_minus_dst_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) -{ - float invAlpha = 1.0f - dst[3]; - factor[0] = factor[1] = factor[2] = factor[3] = invAlpha; -} - -static inline void sw_factor_dst_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) -{ - factor[0] = dst[0]; factor[1] = dst[1]; factor[2] = dst[2]; factor[3] = dst[3]; -} - -static inline void sw_factor_one_minus_dst_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) -{ - factor[0] = 1.0f - dst[0]; factor[1] = 1.0f - dst[1]; - factor[2] = 1.0f - dst[2]; factor[3] = 1.0f - dst[3]; -} - -static inline void sw_factor_src_alpha_saturate(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) -{ - factor[0] = factor[1] = factor[2] = 1.0f; - factor[3] = (src[3] < 1.0f)? src[3] : 1.0f; -} - -static inline void sw_blend_colors(float *SW_RESTRICT dst/*[4]*/, const float *SW_RESTRICT src/*[4]*/) -{ - float srcFactor[4], dstFactor[4]; - - RLSW.srcFactorFunc(srcFactor, src, dst); - RLSW.dstFactorFunc(dstFactor, src, dst); - - dst[0] = srcFactor[0]*src[0] + dstFactor[0]*dst[0]; - dst[1] = srcFactor[1]*src[1] + dstFactor[1]*dst[1]; - dst[2] = srcFactor[2]*src[2] + dstFactor[2]*dst[2]; - dst[3] = srcFactor[3]*src[3] + dstFactor[3]*dst[3]; -} -//------------------------------------------------------------------------------------------- - -// Projection helper functions -//------------------------------------------------------------------------------------------- -static inline void sw_project_ndc_to_screen(float screen[2], const float ndc[4]) -{ - screen[0] = RLSW.vpCenter[0] + ndc[0]*RLSW.vpHalf[0] + 0.5f; - screen[1] = RLSW.vpCenter[1] - ndc[1]*RLSW.vpHalf[1] + 0.5f; -} -//------------------------------------------------------------------------------------------- - -// Polygon clipping management -//------------------------------------------------------------------------------------------- -#define DEFINE_CLIP_FUNC(name, FUNC_IS_INSIDE, FUNC_COMPUTE_T) \ -static inline int sw_clip_##name( \ - sw_vertex_t output[SW_MAX_CLIPPED_POLYGON_VERTICES], \ - const sw_vertex_t input[SW_MAX_CLIPPED_POLYGON_VERTICES], \ - int n) \ -{ \ - const sw_vertex_t *prev = &input[n - 1]; \ - int prevInside = FUNC_IS_INSIDE(prev->homogeneous); \ - int outputCount = 0; \ - \ - for (int i = 0; i < n; i++) { \ - const sw_vertex_t *curr = &input[i]; \ - int currInside = FUNC_IS_INSIDE(curr->homogeneous); \ - \ - /* If transition between interior/exterior, calculate intersection point */ \ - if (prevInside != currInside) { \ - float t = FUNC_COMPUTE_T(prev->homogeneous, curr->homogeneous); \ - sw_lerp_vertex_PTCH(&output[outputCount++], prev, curr, t); \ - } \ - \ - /* If current vertex inside, add it */ \ - if (currInside) { \ - output[outputCount++] = *curr; \ - } \ - \ - prev = curr; \ - prevInside = currInside; \ - } \ - \ - return outputCount; \ -} -//------------------------------------------------------------------------------------------- - -// Frustum cliping functions -//------------------------------------------------------------------------------------------- -#define IS_INSIDE_PLANE_W(h) ((h)[3] >= SW_CLIP_EPSILON) -#define IS_INSIDE_PLANE_X_POS(h) ( (h)[0] < (h)[3]) // Exclusive for +X -#define IS_INSIDE_PLANE_X_NEG(h) (-(h)[0] < (h)[3]) // Exclusive for -X -#define IS_INSIDE_PLANE_Y_POS(h) ( (h)[1] < (h)[3]) // Exclusive for +Y -#define IS_INSIDE_PLANE_Y_NEG(h) (-(h)[1] < (h)[3]) // Exclusive for -Y -#define IS_INSIDE_PLANE_Z_POS(h) ( (h)[2] <= (h)[3]) // Inclusive for +Z -#define IS_INSIDE_PLANE_Z_NEG(h) (-(h)[2] <= (h)[3]) // Inclusive for -Z - -#define COMPUTE_T_PLANE_W(hPrev, hCurr) ((SW_CLIP_EPSILON - (hPrev)[3])/((hCurr)[3] - (hPrev)[3])) -#define COMPUTE_T_PLANE_X_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[0])/(((hPrev)[3] - (hPrev)[0]) - ((hCurr)[3] - (hCurr)[0]))) -#define COMPUTE_T_PLANE_X_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[0])/(((hPrev)[3] + (hPrev)[0]) - ((hCurr)[3] + (hCurr)[0]))) -#define COMPUTE_T_PLANE_Y_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[1])/(((hPrev)[3] - (hPrev)[1]) - ((hCurr)[3] - (hCurr)[1]))) -#define COMPUTE_T_PLANE_Y_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[1])/(((hPrev)[3] + (hPrev)[1]) - ((hCurr)[3] + (hCurr)[1]))) -#define COMPUTE_T_PLANE_Z_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[2])/(((hPrev)[3] - (hPrev)[2]) - ((hCurr)[3] - (hCurr)[2]))) -#define COMPUTE_T_PLANE_Z_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[2])/(((hPrev)[3] + (hPrev)[2]) - ((hCurr)[3] + (hCurr)[2]))) - -DEFINE_CLIP_FUNC(w, IS_INSIDE_PLANE_W, COMPUTE_T_PLANE_W) -DEFINE_CLIP_FUNC(x_pos, IS_INSIDE_PLANE_X_POS, COMPUTE_T_PLANE_X_POS) -DEFINE_CLIP_FUNC(x_neg, IS_INSIDE_PLANE_X_NEG, COMPUTE_T_PLANE_X_NEG) -DEFINE_CLIP_FUNC(y_pos, IS_INSIDE_PLANE_Y_POS, COMPUTE_T_PLANE_Y_POS) -DEFINE_CLIP_FUNC(y_neg, IS_INSIDE_PLANE_Y_NEG, COMPUTE_T_PLANE_Y_NEG) -DEFINE_CLIP_FUNC(z_pos, IS_INSIDE_PLANE_Z_POS, COMPUTE_T_PLANE_Z_POS) -DEFINE_CLIP_FUNC(z_neg, IS_INSIDE_PLANE_Z_NEG, COMPUTE_T_PLANE_Z_NEG) -//------------------------------------------------------------------------------------------- - -// Scissor clip functions -//------------------------------------------------------------------------------------------- -#define COMPUTE_T_SCISSOR_X_MIN(hPrev, hCurr) (((RLSW.scClipMin[0])*(hPrev)[3] - (hPrev)[0])/(((hCurr)[0] - (RLSW.scClipMin[0])*(hCurr)[3]) - ((hPrev)[0] - (RLSW.scClipMin[0])*(hPrev)[3]))) -#define COMPUTE_T_SCISSOR_X_MAX(hPrev, hCurr) (((RLSW.scClipMax[0])*(hPrev)[3] - (hPrev)[0])/(((hCurr)[0] - (RLSW.scClipMax[0])*(hCurr)[3]) - ((hPrev)[0] - (RLSW.scClipMax[0])*(hPrev)[3]))) -#define COMPUTE_T_SCISSOR_Y_MIN(hPrev, hCurr) (((RLSW.scClipMin[1])*(hPrev)[3] - (hPrev)[1])/(((hCurr)[1] - (RLSW.scClipMin[1])*(hCurr)[3]) - ((hPrev)[1] - (RLSW.scClipMin[1])*(hPrev)[3]))) -#define COMPUTE_T_SCISSOR_Y_MAX(hPrev, hCurr) (((RLSW.scClipMax[1])*(hPrev)[3] - (hPrev)[1])/(((hCurr)[1] - (RLSW.scClipMax[1])*(hCurr)[3]) - ((hPrev)[1] - (RLSW.scClipMax[1])*(hPrev)[3]))) - -#define IS_INSIDE_SCISSOR_X_MIN(h) ((h)[0] >= (RLSW.scClipMin[0])*(h)[3]) -#define IS_INSIDE_SCISSOR_X_MAX(h) ((h)[0] <= (RLSW.scClipMax[0])*(h)[3]) -#define IS_INSIDE_SCISSOR_Y_MIN(h) ((h)[1] >= (RLSW.scClipMin[1])*(h)[3]) -#define IS_INSIDE_SCISSOR_Y_MAX(h) ((h)[1] <= (RLSW.scClipMax[1])*(h)[3]) - -DEFINE_CLIP_FUNC(scissor_x_min, IS_INSIDE_SCISSOR_X_MIN, COMPUTE_T_SCISSOR_X_MIN) -DEFINE_CLIP_FUNC(scissor_x_max, IS_INSIDE_SCISSOR_X_MAX, COMPUTE_T_SCISSOR_X_MAX) -DEFINE_CLIP_FUNC(scissor_y_min, IS_INSIDE_SCISSOR_Y_MIN, COMPUTE_T_SCISSOR_Y_MIN) -DEFINE_CLIP_FUNC(scissor_y_max, IS_INSIDE_SCISSOR_Y_MAX, COMPUTE_T_SCISSOR_Y_MAX) -//------------------------------------------------------------------------------------------- - -// Main polygon clip function -static inline bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int *vertexCounter) -{ - static sw_vertex_t tmp[SW_MAX_CLIPPED_POLYGON_VERTICES]; - - int n = *vertexCounter; - - #define CLIP_AGAINST_PLANE(FUNC_CLIP) \ - { \ - n = FUNC_CLIP(tmp, polygon, n); \ - if (n < 3) \ - { \ - *vertexCounter = 0; \ - return false; \ - } \ - for (int i = 0; i < n; i++) polygon[i] = tmp[i]; \ - } - - CLIP_AGAINST_PLANE(sw_clip_w); - CLIP_AGAINST_PLANE(sw_clip_x_pos); - CLIP_AGAINST_PLANE(sw_clip_x_neg); - CLIP_AGAINST_PLANE(sw_clip_y_pos); - CLIP_AGAINST_PLANE(sw_clip_y_neg); - CLIP_AGAINST_PLANE(sw_clip_z_pos); - CLIP_AGAINST_PLANE(sw_clip_z_neg); - - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) - { - CLIP_AGAINST_PLANE(sw_clip_scissor_x_min); - CLIP_AGAINST_PLANE(sw_clip_scissor_x_max); - CLIP_AGAINST_PLANE(sw_clip_scissor_y_min); - CLIP_AGAINST_PLANE(sw_clip_scissor_y_max); - } - - *vertexCounter = n; - - return (n >= 3); -} - -// Triangle rendering logic -//------------------------------------------------------------------------------------------- -static inline bool sw_triangle_face_culling(void) -{ - // NOTE: Face culling is done before clipping to avoid unnecessary computations - // To handle triangles crossing the w=0 plane correctly, - // the winding order test is performeed in homogeneous coordinates directly, - // before the perspective division (division by w) - // This test determines the orientation of the triangle in the (x,y,w) plane, - // which corresponds to the projected 2D winding order sign, - // even with negative w values - - // Preload homogeneous coordinates into local variables - const float *h0 = RLSW.vertexBuffer[0].homogeneous; - const float *h1 = RLSW.vertexBuffer[1].homogeneous; - const float *h2 = RLSW.vertexBuffer[2].homogeneous; - - // Compute a value proportional to the signed area in the projected 2D plane, - // calculated directly using homogeneous coordinates BEFORE division by w - // This is the determinant of the matrix formed by the (x, y, w) components - // of the vertices, which correctly captures the winding order in homogeneous - // space and its relationship to the projected 2D winding order, even with - // negative w values - // The determinant formula used here is: - // h0.x*(h1.y*h2.w - h2.y*h1.w) + - // h1.x*(h2.y*h0.w - h0.y*h2.w) + - // h2.x*(h0.y*h1.w - h1.y*h0.w) - - const float hSgnArea = - h0[0]*(h1[1]*h2[3] - h2[1]*h1[3]) + - h1[0]*(h2[1]*h0[3] - h0[1]*h2[3]) + - h2[0]*(h0[1]*h1[3] - h1[1]*h0[3]); - - // Discard the triangle if its winding order (determined by the sign - // of the homogeneous area/determinant) matches the culled direction - // A positive hSgnArea typically corresponds to a counter-clockwise - // winding in the projected space when all w > 0 - // This test is robust for points with w > 0 or w < 0, correctly - // capturing the change in orientation when crossing the w=0 plane - - // The culling logic remains the same based on the signed area/determinant - // A value of 0 for hSgnArea means the points are collinear in (x, y, w) - // space, which corresponds to a degenerate triangle projection - // Such triangles are typically not culled by this test (0 < 0 is false, 0 > 0 is false) - // and should be handled by the clipper if necessary - return (RLSW.cullFace == SW_FRONT)? (hSgnArea < 0) : (hSgnArea > 0); // Cull if winding is "clockwise" : "counter-clockwise" -} - -static inline void sw_triangle_clip_and_project(void) -{ - sw_vertex_t *polygon = RLSW.vertexBuffer; - int *vertexCounter = &RLSW.vertexCounter; - - if (sw_polygon_clip(polygon, vertexCounter)) - { - // Transformation to screen space and normalization - for (int i = 0; i < *vertexCounter; i++) - { - sw_vertex_t *v = &polygon[i]; - - // Calculation of the reciprocal of W for normalization - // as well as perspective-correct attributes - const float wRcp = 1.0f/v->homogeneous[3]; - v->homogeneous[3] = wRcp; - - // Division of XYZ coordinates by weight - v->homogeneous[0] *= wRcp; - v->homogeneous[1] *= wRcp; - v->homogeneous[2] *= wRcp; - - // Division of texture coordinates (perspective-correct) - v->texcoord[0] *= wRcp; - v->texcoord[1] *= wRcp; - - // Division of colors (perspective-correct) - v->color[0] *= wRcp; - v->color[1] *= wRcp; - v->color[2] *= wRcp; - v->color[3] *= wRcp; - - // Transformation to screen space - sw_project_ndc_to_screen(v->screen, v->homogeneous); - } - } -} - -#define DEFINE_TRIANGLE_RASTER_SCANLINE(FUNC_NAME, ENABLE_TEXTURE, ENABLE_DEPTH_TEST, ENABLE_COLOR_BLEND) \ -static inline void FUNC_NAME(const sw_texture_t *tex, const sw_vertex_t *start, \ - const sw_vertex_t *end, float dUdy, float dVdy) \ -{ \ - /* Gets the start and end coordinates */ \ - int xStart = (int)start->screen[0]; \ - int xEnd = (int)end->screen[0]; \ - \ - /* Avoid empty lines */ \ - if (xStart == xEnd) return; \ - \ - /* Compute the subpixel distance to traverse before the first pixel */ \ - float xSubstep = 1.0f - sw_fract(start->screen[0]); \ - \ - /* Compute the inverse horizontal distance along the X axis */ \ - float dxRcp = 1.0f/(end->screen[0] - start->screen[0]); \ - \ - /* Compute the interpolation steps along the X axis */ \ - float dZdx = (end->homogeneous[2] - start->homogeneous[2])*dxRcp; \ - float dWdx = (end->homogeneous[3] - start->homogeneous[3])*dxRcp; \ - \ - float dCdx[4] = { \ - (end->color[0] - start->color[0])*dxRcp, \ - (end->color[1] - start->color[1])*dxRcp, \ - (end->color[2] - start->color[2])*dxRcp, \ - (end->color[3] - start->color[3])*dxRcp \ - }; \ - \ - float dUdx = 0.0f; \ - float dVdx = 0.0f; \ - if (ENABLE_TEXTURE) { \ - dUdx = (end->texcoord[0] - start->texcoord[0])*dxRcp; \ - dVdx = (end->texcoord[1] - start->texcoord[1])*dxRcp; \ - } \ - \ - /* Initializing the interpolation starting values */ \ - float z = start->homogeneous[2] + dZdx*xSubstep; \ - float w = start->homogeneous[3] + dWdx*xSubstep; \ - \ - float color[4] = { \ - start->color[0] + dCdx[0]*xSubstep, \ - start->color[1] + dCdx[1]*xSubstep, \ - start->color[2] + dCdx[2]*xSubstep, \ - start->color[3] + dCdx[3]*xSubstep \ - }; \ - \ - float u = 0.0f; \ - float v = 0.0f; \ - if (ENABLE_TEXTURE) { \ - u = start->texcoord[0] + dUdx*xSubstep; \ - v = start->texcoord[1] + dVdx*xSubstep; \ - } \ - \ - /* Pre-calculate the starting pointers for the framebuffer row */ \ - int y = (int)start->screen[1]; \ - sw_pixel_t *ptr = RLSW.framebuffer.pixels + y*RLSW.framebuffer.width + xStart; \ - \ - /* Scanline rasterization */ \ - for (int x = xStart; x < xEnd; x++) \ - { \ - float wRcp = 1.0f/w; \ - float srcColor[4] = { \ - color[0]*wRcp, \ - color[1]*wRcp, \ - color[2]*wRcp, \ - color[3]*wRcp \ - }; \ - \ - if (ENABLE_DEPTH_TEST) \ - { \ - /* TODO: Implement different depth funcs? */ \ - float depth = sw_framebuffer_read_depth(ptr); \ - if (z > depth) goto discard; \ - } \ - \ - /* TODO: Implement depth mask */ \ - sw_framebuffer_write_depth(ptr, z); \ - \ - if (ENABLE_TEXTURE) \ - { \ - float texColor[4]; \ - float s = u*wRcp; \ - float t = v*wRcp; \ - sw_texture_sample(texColor, tex, s, t, dUdx, dUdy, dVdx, dVdy); \ - srcColor[0] *= texColor[0]; \ - srcColor[1] *= texColor[1]; \ - srcColor[2] *= texColor[2]; \ - srcColor[3] *= texColor[3]; \ - } \ - \ - if (ENABLE_COLOR_BLEND) \ - { \ - float dstColor[4]; \ - sw_framebuffer_read_color(dstColor, ptr); \ - sw_blend_colors(dstColor, srcColor); \ - sw_framebuffer_write_color(ptr, dstColor); \ - } \ - else \ - { \ - sw_framebuffer_write_color(ptr, srcColor); \ - } \ - \ - /* Increment the interpolation parameter, UVs, and pointers */ \ - discard: \ - z += dZdx; \ - w += dWdx; \ - color[0] += dCdx[0]; \ - color[1] += dCdx[1]; \ - color[2] += dCdx[2]; \ - color[3] += dCdx[3]; \ - if (ENABLE_TEXTURE) \ - { \ - u += dUdx; \ - v += dVdx; \ - } \ - ++ptr; \ - } \ -} - -#define DEFINE_TRIANGLE_RASTER(FUNC_NAME, FUNC_SCANLINE, ENABLE_TEXTURE) \ -static inline void FUNC_NAME(const sw_vertex_t *v0, const sw_vertex_t *v1, \ - const sw_vertex_t *v2, const sw_texture_t *tex) \ -{ \ - /* Swap vertices by increasing y */ \ - if (v0->screen[1] > v1->screen[1]) { const sw_vertex_t *tmp = v0; v0 = v1; v1 = tmp; } \ - if (v1->screen[1] > v2->screen[1]) { const sw_vertex_t *tmp = v1; v1 = v2; v2 = tmp; } \ - if (v0->screen[1] > v1->screen[1]) { const sw_vertex_t *tmp = v0; v0 = v1; v1 = tmp; } \ - \ - /* Extracting coordinates from the sorted vertices */ \ - float x0 = v0->screen[0], y0 = v0->screen[1]; \ - float x1 = v1->screen[0], y1 = v1->screen[1]; \ - float x2 = v2->screen[0], y2 = v2->screen[1]; \ - \ - /* Compute height differences */ \ - float h02 = y2 - y0; \ - float h01 = y1 - y0; \ - float h12 = y2 - y1; \ - \ - if (h02 < 1e-6f) return; \ - \ - /* Precompute the inverse values without additional checks */ \ - float h02Rcp = 1.0f/h02; \ - float h01Rcp = (h01 > 1e-6f)? 1.0f/h01 : 0.0f; \ - float h12Rcp = (h12 > 1e-6f)? 1.0f/h12 : 0.0f; \ - \ - /* Pre-calculation of slopes */ \ - float dXdy02 = (x2 - x0)*h02Rcp; \ - float dXdy01 = (x1 - x0)*h01Rcp; \ - float dXdy12 = (x2 - x1)*h12Rcp; \ - \ - /* Y subpixel correction */ \ - float y0Substep = 1.0f - sw_fract(y0); \ - float y1Substep = 1.0f - sw_fract(y1); \ - \ - /* Y bounds (vertical clipping) */ \ - int yTop = (int)y0; \ - int yMid = (int)y1; \ - int yBot = (int)y2; \ - \ - /* Compute gradients for each side of the triangle */ \ - sw_vertex_t dVXdy02, dVXdy01, dVXdy12; \ - sw_get_vertex_grad_PTCH(&dVXdy02, v0, v2, h02Rcp); \ - sw_get_vertex_grad_PTCH(&dVXdy01, v0, v1, h01Rcp); \ - sw_get_vertex_grad_PTCH(&dVXdy12, v1, v2, h12Rcp); \ - \ - /* Get a copy of vertices for interpolation and apply substep correction */ \ - sw_vertex_t vLeft = *v0, vRight = *v0; \ - sw_add_vertex_grad_scaled_PTCH(&vLeft, &dVXdy02, y0Substep); \ - sw_add_vertex_grad_scaled_PTCH(&vRight, &dVXdy01, y0Substep); \ - \ - vLeft.screen[0] += dXdy02*y0Substep; \ - vRight.screen[0] += dXdy01*y0Substep; \ - \ - /* Scanline for the upper part of the triangle */ \ - for (int y = yTop; y < yMid; y++) \ - { \ - vLeft.screen[1] = vRight.screen[1] = y; \ - \ - if (vLeft.screen[0] < vRight.screen[0]) FUNC_SCANLINE(tex, &vLeft, &vRight, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); \ - else FUNC_SCANLINE(tex, &vRight, &vLeft, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); \ - \ - sw_add_vertex_grad_PTCH(&vLeft, &dVXdy02); \ - vLeft.screen[0] += dXdy02; \ - \ - sw_add_vertex_grad_PTCH(&vRight, &dVXdy01); \ - vRight.screen[0] += dXdy01; \ - } \ - \ - /* Get a copy of next right for interpolation and apply substep correction */ \ - vRight = *v1; \ - sw_add_vertex_grad_scaled_PTCH(&vRight, &dVXdy12, y1Substep); \ - vRight.screen[0] += dXdy12*y1Substep; \ - \ - /* Scanline for the lower part of the triangle */ \ - for (int y = yMid; y < yBot; y++) \ - { \ - vLeft.screen[1] = vRight.screen[1] = y; \ - \ - if (vLeft.screen[0] < vRight.screen[0]) FUNC_SCANLINE(tex, &vLeft, &vRight, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); \ - else FUNC_SCANLINE(tex, &vRight, &vLeft, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); \ - \ - sw_add_vertex_grad_PTCH(&vLeft, &dVXdy02); \ - vLeft.screen[0] += dXdy02; \ - \ - sw_add_vertex_grad_PTCH(&vRight, &dVXdy12); \ - vRight.screen[0] += dXdy12; \ - } \ -} - -DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline, 0, 0, 0) -DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_TEX, 1, 0, 0) -DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_DEPTH, 0, 1, 0) -DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_BLEND, 0, 0, 1) -DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_TEX_DEPTH, 1, 1, 0) -DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_TEX_BLEND, 1, 0, 1) -DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_DEPTH_BLEND, 0, 1, 1) -DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_TEX_DEPTH_BLEND, 1, 1, 1) - -DEFINE_TRIANGLE_RASTER(sw_triangle_raster, sw_triangle_raster_scanline, false) -DEFINE_TRIANGLE_RASTER(sw_triangle_raster_TEX, sw_triangle_raster_scanline_TEX, true) -DEFINE_TRIANGLE_RASTER(sw_triangle_raster_DEPTH, sw_triangle_raster_scanline_DEPTH, false) -DEFINE_TRIANGLE_RASTER(sw_triangle_raster_BLEND, sw_triangle_raster_scanline_BLEND, false) -DEFINE_TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH, sw_triangle_raster_scanline_TEX_DEPTH, true) -DEFINE_TRIANGLE_RASTER(sw_triangle_raster_TEX_BLEND, sw_triangle_raster_scanline_TEX_BLEND, true) -DEFINE_TRIANGLE_RASTER(sw_triangle_raster_DEPTH_BLEND, sw_triangle_raster_scanline_DEPTH_BLEND, false) -DEFINE_TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH_BLEND, sw_triangle_raster_scanline_TEX_DEPTH_BLEND, true) - -static inline void sw_triangle_render(void) -{ - if (RLSW.stateFlags & SW_STATE_CULL_FACE) - { - if (!sw_triangle_face_culling()) return; - } - - sw_triangle_clip_and_project(); - - if (RLSW.vertexCounter < 3) return; - - #define TRIANGLE_RASTER(RASTER_FUNC) \ - { \ - for (int i = 0; i < RLSW.vertexCounter - 2; i++) \ - { \ - RASTER_FUNC( \ - &RLSW.vertexBuffer[0], \ - &RLSW.vertexBuffer[i + 1], \ - &RLSW.vertexBuffer[i + 2], \ - &RLSW.loadedTextures[RLSW.currentTexture] \ - ); \ - } \ - } - - uint32_t state = RLSW.stateFlags; - if (RLSW.currentTexture == 0) state &= ~SW_STATE_TEXTURE_2D; - if ((RLSW.srcFactor == SW_ONE) && (RLSW.dstFactor == SW_ZERO)) state &= ~SW_STATE_BLEND; - - if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH_BLEND) - else if (SW_STATE_CHECK_EX(state, SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_DEPTH_BLEND) - else if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_TEX_BLEND) - else if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST)) TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH) - else if (SW_STATE_CHECK_EX(state, SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_BLEND) - else if (SW_STATE_CHECK_EX(state, SW_STATE_DEPTH_TEST)) TRIANGLE_RASTER(sw_triangle_raster_DEPTH) - else if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D)) TRIANGLE_RASTER(sw_triangle_raster_TEX) - else TRIANGLE_RASTER(sw_triangle_raster) - - #undef TRIANGLE_RASTER -} -//------------------------------------------------------------------------------------------- - -// Quad rendering logic -//------------------------------------------------------------------------------------------- -static inline bool sw_quad_face_culling(void) -{ - // NOTE: Face culling is done before clipping to avoid unnecessary computations - // To handle quads crossing the w=0 plane correctly, - // the winding order test is performed in homogeneous coordinates directly, - // before the perspective division (division by w) - // For a convex quad with vertices P0, P1, P2, P3 in sequential order, - // the winding order of the quad is the same as the winding order - // of the triangle P0 P1 P2. The homogeneous triangle is used on - // winding test on this first triangle - - // Preload homogeneous coordinates into local variables - const float *h0 = RLSW.vertexBuffer[0].homogeneous; - const float *h1 = RLSW.vertexBuffer[1].homogeneous; - const float *h2 = RLSW.vertexBuffer[2].homogeneous; - - // NOTE: h3 is not needed for this test - // const float *h3 = RLSW.vertexBuffer[3].homogeneous; - - // Compute a value proportional to the signed area of the triangle P0 P1 P2 - // in the projected 2D plane, calculated directly using homogeneous coordinates - // BEFORE division by w - // This is the determinant of the matrix formed by the (x, y, w) components - // of the vertices P0, P1, and P2. Its sign correctly indicates the winding order - // in homogeneous space and its relationship to the projected 2D winding order, - // even with negative w values - // The determinant formula used here is: - // h0.x*(h1.y*h2.w - h2.y*h1.w) + - // h1.x*(h2.y*h0.w - h0.y*h2.w) + - // h2.x*(h0.y*h1.w - h1.y*h0.w) - - const float hSgnArea = - h0[0]*(h1[1]*h2[3] - h2[1]*h1[3]) + - h1[0]*(h2[1]*h0[3] - h0[1]*h2[3]) + - h2[0]*(h0[1]*h1[3] - h1[1]*h0[3]); - - // Perform face culling based on the winding order determined by the sign - // of the homogeneous area/determinant of triangle P0 P1 P2 - // This test is robust for points with w > 0 or w < 0 within the triangle, - // correctly capturing the change in orientation when crossing the w=0 plane - - // A positive hSgnArea typically corresponds to a counter-clockwise - // winding in the projected space when all w > 0 - // A value of 0 for hSgnArea means P0, P1, P2 are collinear in (x, y, w) - // space, which corresponds to a degenerate triangle projection - // Such quads might also be degenerate or non-planar. They are typically - // not culled by this test (0 < 0 is false, 0 > 0 is false) - // and should be handled by the clipper if necessary - - return (RLSW.cullFace == SW_FRONT)? (hSgnArea < 0.0f) : (hSgnArea > 0.0f); // Cull if winding is "clockwise" : "counter-clockwise" -} - -static inline void sw_quad_clip_and_project(void) -{ - sw_vertex_t *polygon = RLSW.vertexBuffer; - int *vertexCounter = &RLSW.vertexCounter; - - if (sw_polygon_clip(polygon, vertexCounter)) - { - // Transformation to screen space and normalization - for (int i = 0; i < *vertexCounter; i++) - { - sw_vertex_t *v = &polygon[i]; - - // Calculation of the reciprocal of W for normalization - // as well as perspective-correct attributes - const float wRcp = 1.0f/v->homogeneous[3]; - v->homogeneous[3] = wRcp; - - // Division of XYZ coordinates by weight - v->homogeneous[0] *= wRcp; - v->homogeneous[1] *= wRcp; - v->homogeneous[2] *= wRcp; - - // Division of texture coordinates (perspective-correct) - v->texcoord[0] *= wRcp; - v->texcoord[1] *= wRcp; - - // Division of colors (perspective-correct) - v->color[0] *= wRcp; - v->color[1] *= wRcp; - v->color[2] *= wRcp; - v->color[3] *= wRcp; - - // Transformation to screen space - sw_project_ndc_to_screen(v->screen, v->homogeneous); - } - } -} - -static inline bool sw_quad_is_axis_aligned(void) -{ - // Reject quads with perspective projection - // The fast path assumes affine (non-perspective) quads, - // so it's required for all vertices to have homogeneous w = 1.0 - for (int i = 0; i < 4; i++) - { - if (RLSW.vertexBuffer[i].homogeneous[3] != 1.0f) return false; - } - - // Epsilon tolerance in screen space (pixels) - const float epsilon = 0.5f; - - // Fetch screen-space positions for the four quad vertices - const float *p0 = RLSW.vertexBuffer[0].screen; - const float *p1 = RLSW.vertexBuffer[1].screen; - const float *p2 = RLSW.vertexBuffer[2].screen; - const float *p3 = RLSW.vertexBuffer[3].screen; - - // Compute edge vectors between consecutive vertices - // These define the four sides of the quad in screen space - float dx01 = p1[0] - p0[0], dy01 = p1[1] - p0[1]; - float dx12 = p2[0] - p1[0], dy12 = p2[1] - p1[1]; - float dx23 = p3[0] - p2[0], dy23 = p3[1] - p2[1]; - float dx30 = p0[0] - p3[0], dy30 = p0[1] - p3[1]; - - // Each edge must be either horizontal or vertical within epsilon tolerance - // If any edge deviates significantly from either axis, the quad is not axis-aligned - if (!((fabsf(dy01) < epsilon) || (fabsf(dx01) < epsilon))) return false; - if (!((fabsf(dy12) < epsilon) || (fabsf(dx12) < epsilon))) return false; - if (!((fabsf(dy23) < epsilon) || (fabsf(dx23) < epsilon))) return false; - if (!((fabsf(dy30) < epsilon) || (fabsf(dx30) < epsilon))) return false; - - return true; -} - -static inline void sw_quad_sort_cw(const sw_vertex_t* *output) -{ - const sw_vertex_t *input = RLSW.vertexBuffer; - - // Calculate the centroid of the quad - float cx = (input[0].screen[0] + input[1].screen[0] + - input[2].screen[0] + input[3].screen[0])*0.25f; - float cy = (input[0].screen[1] + input[1].screen[1] + - input[2].screen[1] + input[3].screen[1])*0.25f; - - // Calculate the angle of each vertex relative to the center - // and assign them directly to their correct position - const sw_vertex_t *corners[4] = { 0 }; - - for (int i = 0; i < 4; i++) - { - float dx = input[i].screen[0] - cx; - float dy = input[i].screen[1] - cy; - - // Determine the quadrant (clockwise from top-left) - // top-left: dx < 0, dy < 0 - // top-right: dx >= 0, dy < 0 - // bottom-right: dx >= 0, dy >= 0 - // bottom-left: dx < 0, dy >= 0 - - int idx; - if (dy < 0) idx = (dx < 0)? 0 : 1; // Top row - else idx = (dx < 0)? 3 : 2; // Bottom row - - corners[idx] = &input[i]; - } - - output[0] = corners[0]; // top-left - output[1] = corners[1]; // top-right - output[2] = corners[2]; // bottom-right - output[3] = corners[3]; // bottom-left -} - -// TODO: REVIEW: Could a perfectly aligned quad, where one of the four points has a different depth, -// still appear perfectly aligned from a certain point of view? -// Because in that case, it's still needed to perform perspective division for textures and colors... -#define DEFINE_QUAD_RASTER_AXIS_ALIGNED(FUNC_NAME, ENABLE_TEXTURE, ENABLE_DEPTH_TEST, ENABLE_COLOR_BLEND) \ -static inline void FUNC_NAME(void) \ -{ \ - const sw_vertex_t *sortedVerts[4]; \ - sw_quad_sort_cw(sortedVerts); \ - \ - const sw_vertex_t *v0 = sortedVerts[0]; \ - const sw_vertex_t *v1 = sortedVerts[1]; \ - const sw_vertex_t *v2 = sortedVerts[2]; \ - const sw_vertex_t *v3 = sortedVerts[3]; \ - \ - /* Screen bounds (axis-aligned) */ \ - int xMin = (int)v0->screen[0]; \ - int yMin = (int)v0->screen[1]; \ - int xMax = (int)v2->screen[0]; \ - int yMax = (int)v2->screen[1]; \ - \ - float w = v2->screen[0] - v0->screen[0]; \ - float h = v2->screen[1] - v0->screen[1]; \ - \ - if ((w == 0) || (h == 0)) return; \ - \ - float wRcp = (w > 0.0f)? 1.0f/w : 0.0f; \ - float hRcp = (h > 0.0f)? 1.0f/h : 0.0f; \ - \ - /* Subpixel corrections */ \ - float xSubstep = 1.0f - sw_fract(v0->screen[0]); \ - float ySubstep = 1.0f - sw_fract(v0->screen[1]); \ - \ - /* Calculation of vertex gradients in X and Y */ \ - float dUdx = 0.0f, dVdx = 0.0f; \ - float dUdy = 0.0f, dVdy = 0.0f; \ - if (ENABLE_TEXTURE) { \ - dUdx = (v1->texcoord[0] - v0->texcoord[0])*wRcp; \ - dVdx = (v1->texcoord[1] - v0->texcoord[1])*wRcp; \ - dUdy = (v3->texcoord[0] - v0->texcoord[0])*hRcp; \ - dVdy = (v3->texcoord[1] - v0->texcoord[1])*hRcp; \ - } \ - \ - float dCdx[4], dCdy[4]; \ - dCdx[0] = (v1->color[0] - v0->color[0])*wRcp; \ - dCdx[1] = (v1->color[1] - v0->color[1])*wRcp; \ - dCdx[2] = (v1->color[2] - v0->color[2])*wRcp; \ - dCdx[3] = (v1->color[3] - v0->color[3])*wRcp; \ - dCdy[0] = (v3->color[0] - v0->color[0])*hRcp; \ - dCdy[1] = (v3->color[1] - v0->color[1])*hRcp; \ - dCdy[2] = (v3->color[2] - v0->color[2])*hRcp; \ - dCdy[3] = (v3->color[3] - v0->color[3])*hRcp; \ - \ - float dZdx, dZdy; \ - dZdx = (v1->homogeneous[2] - v0->homogeneous[2])*wRcp; \ - dZdy = (v3->homogeneous[2] - v0->homogeneous[2])*hRcp; \ - \ - /* Start of quad rasterization */ \ - const sw_texture_t *tex; \ - if (ENABLE_TEXTURE) tex = &RLSW.loadedTextures[RLSW.currentTexture]; \ - \ - sw_pixel_t *pixels = RLSW.framebuffer.pixels; \ - int wDst = RLSW.framebuffer.width; \ - \ - float zScanline = v0->homogeneous[2] + dZdx*xSubstep + dZdy*ySubstep; \ - float uScanline = v0->texcoord[0] + dUdx*xSubstep + dUdy*ySubstep; \ - float vScanline = v0->texcoord[1] + dVdx*xSubstep + dVdy*ySubstep; \ - \ - float colorScanline[4] = { \ - v0->color[0] + dCdx[0]*xSubstep + dCdy[0]*ySubstep, \ - v0->color[1] + dCdx[1]*xSubstep + dCdy[1]*ySubstep, \ - v0->color[2] + dCdx[2]*xSubstep + dCdy[2]*ySubstep, \ - v0->color[3] + dCdx[3]*xSubstep + dCdy[3]*ySubstep \ - }; \ - \ - for (int y = yMin; y < yMax; y++) \ - { \ - sw_pixel_t *ptr = pixels + y*wDst + xMin; \ - \ - float z = zScanline; \ - float u = uScanline; \ - float v = vScanline; \ - \ - float color[4] = { \ - colorScanline[0], \ - colorScanline[1], \ - colorScanline[2], \ - colorScanline[3] \ - }; \ - \ - /* Scanline rasterization */ \ - for (int x = xMin; x < xMax; x++) \ - { \ - /* Pixel color computation */ \ - float srcColor[4] = { \ - color[0], \ - color[1], \ - color[2], \ - color[3] \ - }; \ - \ - /* Test and write depth */ \ - if (ENABLE_DEPTH_TEST) \ - { \ - /* TODO: Implement different depth funcs? */ \ - float depth = sw_framebuffer_read_depth(ptr); \ - if (z > depth) goto discard; \ - } \ - \ - /* TODO: Implement depth mask */ \ - sw_framebuffer_write_depth(ptr, z); \ - \ - if (ENABLE_TEXTURE) \ - { \ - float texColor[4]; \ - sw_texture_sample(texColor, tex, u, v, dUdx, dUdy, dVdx, dVdy); \ - srcColor[0] *= texColor[0]; \ - srcColor[1] *= texColor[1]; \ - srcColor[2] *= texColor[2]; \ - srcColor[3] *= texColor[3]; \ - } \ - \ - if (ENABLE_COLOR_BLEND) \ - { \ - float dstColor[4]; \ - sw_framebuffer_read_color(dstColor, ptr); \ - sw_blend_colors(dstColor, srcColor); \ - sw_framebuffer_write_color(ptr, dstColor); \ - } \ - else sw_framebuffer_write_color(ptr, srcColor); \ - \ - discard: \ - z += dZdx; \ - color[0] += dCdx[0]; \ - color[1] += dCdx[1]; \ - color[2] += dCdx[2]; \ - color[3] += dCdx[3]; \ - if (ENABLE_TEXTURE) \ - { \ - u += dUdx; \ - v += dVdx; \ - } \ - ++ptr; \ - } \ - \ - zScanline += dZdy; \ - colorScanline[0] += dCdy[0]; \ - colorScanline[1] += dCdy[1]; \ - colorScanline[2] += dCdy[2]; \ - colorScanline[3] += dCdy[3]; \ - \ - if (ENABLE_TEXTURE) \ - { \ - uScanline += dUdy; \ - vScanline += dVdy; \ - } \ - } \ -} - -DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned, 0, 0, 0) -DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_TEX, 1, 0, 0) -DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_DEPTH, 0, 1, 0) -DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_BLEND, 0, 0, 1) -DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_TEX_DEPTH, 1, 1, 0) -DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_TEX_BLEND, 1, 0, 1) -DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_DEPTH_BLEND, 0, 1, 1) -DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_TEX_DEPTH_BLEND, 1, 1, 1) - -static inline void sw_quad_render(void) -{ - if (RLSW.stateFlags & SW_STATE_CULL_FACE) - { - if (!sw_quad_face_culling()) return; - } - - sw_quad_clip_and_project(); - - if (RLSW.vertexCounter < 3) return; - - uint32_t state = RLSW.stateFlags; - if (RLSW.currentTexture == 0) state &= ~SW_STATE_TEXTURE_2D; - if ((RLSW.srcFactor == SW_ONE) && (RLSW.dstFactor == SW_ZERO)) state &= ~SW_STATE_BLEND; - - if ((RLSW.vertexCounter == 4) && sw_quad_is_axis_aligned()) - { - if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_quad_raster_axis_aligned_TEX_DEPTH_BLEND(); - else if (SW_STATE_CHECK_EX(state, SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_quad_raster_axis_aligned_DEPTH_BLEND(); - else if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D | SW_STATE_BLEND)) sw_quad_raster_axis_aligned_TEX_BLEND(); - else if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST)) sw_quad_raster_axis_aligned_TEX_DEPTH(); - else if (SW_STATE_CHECK_EX(state, SW_STATE_BLEND)) sw_quad_raster_axis_aligned_BLEND(); - else if (SW_STATE_CHECK_EX(state, SW_STATE_DEPTH_TEST)) sw_quad_raster_axis_aligned_DEPTH(); - else if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D)) sw_quad_raster_axis_aligned_TEX(); - else sw_quad_raster_axis_aligned(); - return; - } - - #define TRIANGLE_RASTER(RASTER_FUNC) \ - { \ - for (int i = 0; i < RLSW.vertexCounter - 2; i++) \ - { \ - RASTER_FUNC( \ - &RLSW.vertexBuffer[0], \ - &RLSW.vertexBuffer[i + 1], \ - &RLSW.vertexBuffer[i + 2], \ - &RLSW.loadedTextures[RLSW.currentTexture] \ - ); \ - } \ - } - - if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH_BLEND) - else if (SW_STATE_CHECK_EX(state, SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_DEPTH_BLEND) - else if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_TEX_BLEND) - else if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST)) TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH) - else if (SW_STATE_CHECK_EX(state, SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_BLEND) - else if (SW_STATE_CHECK_EX(state, SW_STATE_DEPTH_TEST)) TRIANGLE_RASTER(sw_triangle_raster_DEPTH) - else if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D)) TRIANGLE_RASTER(sw_triangle_raster_TEX) - else TRIANGLE_RASTER(sw_triangle_raster) - - #undef TRIANGLE_RASTER -} -//------------------------------------------------------------------------------------------- - -// Line rendering logic -//------------------------------------------------------------------------------------------- -static inline bool sw_line_clip_coord(float q, float p, float *t0, float *t1) -{ - if (fabsf(p) < SW_CLIP_EPSILON) - { - // Check if the line is entirely outside the window - if (q < -SW_CLIP_EPSILON) return 0; // Completely outside - return 1; // Completely inside or on the edges - } - - const float r = q/p; - - if (p < 0) - { - if (r > *t1) return 0; - if (r > *t0) *t0 = r; - } - else - { - if (r < *t0) return 0; - if (r < *t1) *t1 = r; - } - - return 1; -} - -static inline bool sw_line_clip(sw_vertex_t *v0, sw_vertex_t *v1) -{ - float t0 = 0.0f, t1 = 1.0f; - float dH[4], dC[4]; - - for (int i = 0; i < 4; i++) - { - dH[i] = v1->homogeneous[i] - v0->homogeneous[i]; - dC[i] = v1->color[i] - v0->color[i]; - } - - // Clipping Liang-Barsky - if (!sw_line_clip_coord(v0->homogeneous[3] - v0->homogeneous[0], -dH[3] + dH[0], &t0, &t1)) return false; - if (!sw_line_clip_coord(v0->homogeneous[3] + v0->homogeneous[0], -dH[3] - dH[0], &t0, &t1)) return false; - if (!sw_line_clip_coord(v0->homogeneous[3] - v0->homogeneous[1], -dH[3] + dH[1], &t0, &t1)) return false; - if (!sw_line_clip_coord(v0->homogeneous[3] + v0->homogeneous[1], -dH[3] - dH[1], &t0, &t1)) return false; - if (!sw_line_clip_coord(v0->homogeneous[3] - v0->homogeneous[2], -dH[3] + dH[2], &t0, &t1)) return false; - if (!sw_line_clip_coord(v0->homogeneous[3] + v0->homogeneous[2], -dH[3] - dH[2], &t0, &t1)) return false; - - // Clipping Scissor - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) - { - if (!sw_line_clip_coord(v0->homogeneous[0] - RLSW.scClipMin[0]*v0->homogeneous[3], RLSW.scClipMin[0]*dH[3] - dH[0], &t0, &t1)) return false; - if (!sw_line_clip_coord(RLSW.scClipMax[0]*v0->homogeneous[3] - v0->homogeneous[0], dH[0] - RLSW.scClipMax[0]*dH[3], &t0, &t1)) return false; - if (!sw_line_clip_coord(v0->homogeneous[1] - RLSW.scClipMin[1]*v0->homogeneous[3], RLSW.scClipMin[1]*dH[3] - dH[1], &t0, &t1)) return false; - if (!sw_line_clip_coord(RLSW.scClipMax[1]*v0->homogeneous[3] - v0->homogeneous[1], dH[1] - RLSW.scClipMax[1]*dH[3], &t0, &t1)) return false; - } - - // Interpolation of new coordinates - if (t1 < 1.0f) - { - for (int i = 0; i < 4; i++) - { - v1->homogeneous[i] = v0->homogeneous[i] + t1*dH[i]; - v1->color[i] = v0->color[i] + t1*dC[i]; - } - } - - if (t0 > 0.0f) - { - for (int i = 0; i < 4; i++) - { - v0->homogeneous[i] += t0*dH[i]; - v0->color[i] += t0*dC[i]; - } - } - - return true; -} - -static inline bool sw_line_clip_and_project(sw_vertex_t *v0, sw_vertex_t *v1) -{ - if (!sw_line_clip(v0, v1)) return false; - - // Convert homogeneous coordinates to NDC - v0->homogeneous[3] = 1.0f/v0->homogeneous[3]; - v1->homogeneous[3] = 1.0f/v1->homogeneous[3]; - for (int i = 0; i < 3; i++) - { - v0->homogeneous[i] *= v0->homogeneous[3]; - v1->homogeneous[i] *= v1->homogeneous[3]; - } - - // Convert NDC coordinates to screen space - sw_project_ndc_to_screen(v0->screen, v0->homogeneous); - sw_project_ndc_to_screen(v1->screen, v1->homogeneous); - - return true; -} - -#define DEFINE_LINE_RASTER(FUNC_NAME, ENABLE_DEPTH_TEST, ENABLE_COLOR_BLEND) \ -static inline void FUNC_NAME(const sw_vertex_t *v0, const sw_vertex_t *v1) \ -{ \ - float x0 = v0->screen[0]; \ - float y0 = v0->screen[1]; \ - float x1 = v1->screen[0]; \ - float y1 = v1->screen[1]; \ - \ - float dx = x1 - x0; \ - float dy = y1 - y0; \ - \ - /* Compute dominant axis and subpixel offset */ \ - float steps, substep; \ - if (fabsf(dx) > fabsf(dy)) \ - { \ - steps = fabsf(dx); \ - if (steps < 1.0f) return; \ - substep = (dx >= 0.0f)? (1.0f - sw_fract(x0)) : sw_fract(x0); \ - } \ - else \ - { \ - steps = fabsf(dy); \ - if (steps < 1.0f) return; \ - substep = (dy >= 0.0f)? (1.0f - sw_fract(y0)) : sw_fract(y0); \ - } \ - \ - /* Compute per pixel increments */ \ - float xInc = dx/steps; \ - float yInc = dy/steps; \ - float stepRcp = 1.0f/steps; \ - \ - float zInc = (v1->homogeneous[2] - v0->homogeneous[2])*stepRcp; \ - float rInc = (v1->color[0] - v0->color[0])*stepRcp; \ - float gInc = (v1->color[1] - v0->color[1])*stepRcp; \ - float bInc = (v1->color[2] - v0->color[2])*stepRcp; \ - float aInc = (v1->color[3] - v0->color[3])*stepRcp; \ - \ - /* Initializing the interpolation starting values */ \ - float x = x0 + xInc*substep; \ - float y = y0 + yInc*substep; \ - float z = v0->homogeneous[2] + zInc*substep; \ - float r = v0->color[0] + rInc*substep; \ - float g = v0->color[1] + gInc*substep; \ - float b = v0->color[2] + bInc*substep; \ - float a = v0->color[3] + aInc*substep; \ - \ - const int fbWidth = RLSW.framebuffer.width; \ - sw_pixel_t *pixels = RLSW.framebuffer.pixels; \ - \ - int numPixels = (int)(steps - substep) + 1; \ - \ - for (int i = 0; i < numPixels; i++) \ - { \ - /* TODO: REVIEW: May require reviewing projection details */ \ - int px = (int)(x - 0.5f); \ - int py = (int)(y - 0.5f); \ - \ - sw_pixel_t *ptr = pixels + py*fbWidth + px; \ - \ - if (ENABLE_DEPTH_TEST) \ - { \ - float depth = sw_framebuffer_read_depth(ptr); \ - if (z > depth) goto discard; \ - } \ - \ - sw_framebuffer_write_depth(ptr, z); \ - \ - float color[4] = {r, g, b, a}; \ - \ - if (ENABLE_COLOR_BLEND) \ - { \ - float dstColor[4]; \ - sw_framebuffer_read_color(dstColor, ptr); \ - sw_blend_colors(dstColor, color); \ - sw_framebuffer_write_color(ptr, dstColor); \ - } \ - else sw_framebuffer_write_color(ptr, color); \ - \ - discard: \ - x += xInc; y += yInc; z += zInc; \ - r += rInc; g += gInc; b += bInc; a += aInc; \ - } \ -} - -#define DEFINE_LINE_THICK_RASTER(FUNC_NAME, RASTER_FUNC) \ -void FUNC_NAME(const sw_vertex_t *v1, const sw_vertex_t *v2) \ -{ \ - sw_vertex_t tv1, tv2; \ - \ - int x1 = (int)v1->screen[0]; \ - int y1 = (int)v1->screen[1]; \ - int x2 = (int)v2->screen[0]; \ - int y2 = (int)v2->screen[1]; \ - \ - int dx = x2 - x1; \ - int dy = y2 - y1; \ - \ - RASTER_FUNC(v1, v2); \ - \ - if ((dx != 0) && (abs(dy/dx) < 1)) \ - { \ - int wy = (int)((RLSW.lineWidth - 1.0f)*abs(dx)/sqrtf(dx*dx + dy*dy)); \ - wy >>= 1; \ - for (int i = 1; i <= wy; i++) \ - { \ - tv1 = *v1, tv2 = *v2; \ - tv1.screen[1] -= i; \ - tv2.screen[1] -= i; \ - RASTER_FUNC(&tv1, &tv2); \ - tv1 = *v1, tv2 = *v2; \ - tv1.screen[1] += i; \ - tv2.screen[1] += i; \ - RASTER_FUNC(&tv1, &tv2); \ - } \ - } \ - else if (dy != 0) \ - { \ - int wx = (int)((RLSW.lineWidth - 1.0f)*abs(dy)/sqrtf(dx*dx + dy*dy)); \ - wx >>= 1; \ - for (int i = 1; i <= wx; i++) \ - { \ - tv1 = *v1, tv2 = *v2; \ - tv1.screen[0] -= i; \ - tv2.screen[0] -= i; \ - RASTER_FUNC(&tv1, &tv2); \ - tv1 = *v1, tv2 = *v2; \ - tv1.screen[0] += i; \ - tv2.screen[0] += i; \ - RASTER_FUNC(&tv1, &tv2); \ - } \ - } \ -} - -DEFINE_LINE_RASTER(sw_line_raster, 0, 0) -DEFINE_LINE_RASTER(sw_line_raster_DEPTH, 1, 0) -DEFINE_LINE_RASTER(sw_line_raster_BLEND, 0, 1) -DEFINE_LINE_RASTER(sw_line_raster_DEPTH_BLEND, 1, 1) - -DEFINE_LINE_THICK_RASTER(sw_line_thick_raster, sw_line_raster) -DEFINE_LINE_THICK_RASTER(sw_line_thick_raster_DEPTH, sw_line_raster_DEPTH) -DEFINE_LINE_THICK_RASTER(sw_line_thick_raster_BLEND, sw_line_raster_BLEND) -DEFINE_LINE_THICK_RASTER(sw_line_thick_raster_DEPTH_BLEND, sw_line_raster_DEPTH_BLEND) - -static inline void sw_line_render(sw_vertex_t *vertices) -{ - if (!sw_line_clip_and_project(&vertices[0], &vertices[1])) return; - - if (RLSW.lineWidth >= 2.0f) - { - if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_line_thick_raster_DEPTH_BLEND(&vertices[0], &vertices[1]); - else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_line_thick_raster_BLEND(&vertices[0], &vertices[1]); - else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_line_thick_raster_DEPTH(&vertices[0], &vertices[1]); - else sw_line_thick_raster(&vertices[0], &vertices[1]); - } - else - { - if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_line_raster_DEPTH_BLEND(&vertices[0], &vertices[1]); - else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_line_raster_BLEND(&vertices[0], &vertices[1]); - else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_line_raster_DEPTH(&vertices[0], &vertices[1]); - else sw_line_raster(&vertices[0], &vertices[1]); - } -} -//------------------------------------------------------------------------------------------- - -// Point rendering logic -//------------------------------------------------------------------------------------------- -static inline bool sw_point_clip_and_project(sw_vertex_t *v) -{ - if (v->homogeneous[3] != 1.0f) - { - for (int_fast8_t i = 0; i < 3; i++) - { - if ((v->homogeneous[i] < -v->homogeneous[3]) || (v->homogeneous[i] > v->homogeneous[3])) return false; - } - - v->homogeneous[3] = 1.0f/v->homogeneous[3]; - v->homogeneous[0] *= v->homogeneous[3]; - v->homogeneous[1] *= v->homogeneous[3]; - v->homogeneous[2] *= v->homogeneous[3]; - } - - sw_project_ndc_to_screen(v->screen, v->homogeneous); - - const int *min = NULL, *max = NULL; - - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) - { - min = RLSW.scMin; - max = RLSW.scMax; - } - else - { - min = RLSW.vpMin; - max = RLSW.vpMax; - } - - bool insideX = (v->screen[0] - RLSW.pointRadius < max[0]) && (v->screen[0] + RLSW.pointRadius > min[0]); - bool insideY = (v->screen[1] - RLSW.pointRadius < max[1]) && (v->screen[1] + RLSW.pointRadius > min[1]); - - return (insideX && insideY); -} - -#define DEFINE_POINT_RASTER(FUNC_NAME, ENABLE_DEPTH_TEST, ENABLE_COLOR_BLEND, CHECK_BOUNDS) \ -static inline void FUNC_NAME(int x, int y, float z, const float color[4]) \ -{ \ - if (CHECK_BOUNDS == 1) \ - { \ - if ((x < RLSW.vpMin[0]) || (x >= RLSW.vpMax[0])) return; \ - if ((y < RLSW.vpMin[1]) || (y >= RLSW.vpMax[1])) return; \ - } \ - else if (CHECK_BOUNDS == SW_SCISSOR_TEST) \ - { \ - if ((x < RLSW.scMin[0]) || (x >= RLSW.scMax[0])) return; \ - if ((y < RLSW.scMin[1]) || (y >= RLSW.scMax[1])) return; \ - } \ - \ - int offset = y*RLSW.framebuffer.width + x; \ - sw_pixel_t *ptr = RLSW.framebuffer.pixels + offset; \ - \ - if (ENABLE_DEPTH_TEST) \ - { \ - float depth = sw_framebuffer_read_depth(ptr); \ - if (z > depth) return; \ - } \ - \ - sw_framebuffer_write_depth(ptr, z); \ - \ - if (ENABLE_COLOR_BLEND) \ - { \ - float dstColor[4]; \ - sw_framebuffer_read_color(dstColor, ptr); \ - sw_blend_colors(dstColor, color); \ - sw_framebuffer_write_color(ptr, dstColor); \ - } \ - else sw_framebuffer_write_color(ptr, color); \ -} - -#define DEFINE_POINT_THICK_RASTER(FUNC_NAME, RASTER_FUNC) \ -static inline void FUNC_NAME(sw_vertex_t *v) \ -{ \ - int cx = v->screen[0]; \ - int cy = v->screen[1]; \ - float cz = v->homogeneous[2]; \ - int radius = RLSW.pointRadius; \ - const float *color = v->color; \ - \ - int x = 0; \ - int y = radius; \ - int d = 3 - 2*radius; \ - \ - while (x <= y) \ - { \ - for (int i = -x; i <= x; i++) \ - { \ - RASTER_FUNC(cx + i, cy + y, cz, color); \ - RASTER_FUNC(cx + i, cy - y, cz, color); \ - } \ - for (int i = -y; i <= y; i++) \ - { \ - RASTER_FUNC(cx + i, cy + x, cz, color); \ - RASTER_FUNC(cx + i, cy - x, cz, color); \ - } \ - if (d > 0) \ - { \ - y--; \ - d = d + 4*(x - y) + 10; \ - } \ - else d = d + 4*x + 6; \ - x++; \ - } \ -} - -DEFINE_POINT_RASTER(sw_point_raster, 0, 0, 0) -DEFINE_POINT_RASTER(sw_point_raster_DEPTH, 1, 0, 0) -DEFINE_POINT_RASTER(sw_point_raster_BLEND, 0, 1, 0) -DEFINE_POINT_RASTER(sw_point_raster_DEPTH_BLEND, 1, 1, 0) - -DEFINE_POINT_RASTER(sw_point_raster_CHECK, 0, 0, 1) -DEFINE_POINT_RASTER(sw_point_raster_DEPTH_CHECK, 1, 0, 1) -DEFINE_POINT_RASTER(sw_point_raster_BLEND_CHECK, 0, 1, 1) -DEFINE_POINT_RASTER(sw_point_raster_DEPTH_BLEND_CHECK, 1, 1, 1) - -DEFINE_POINT_RASTER(sw_point_raster_CHECK_SCISSOR, 0, 0, SW_SCISSOR_TEST) -DEFINE_POINT_RASTER(sw_point_raster_DEPTH_CHECK_SCISSOR, 1, 0, SW_SCISSOR_TEST) -DEFINE_POINT_RASTER(sw_point_raster_BLEND_CHECK_SCISSOR, 0, 1, SW_SCISSOR_TEST) -DEFINE_POINT_RASTER(sw_point_raster_DEPTH_BLEND_CHECK_SCISSOR, 1, 1, SW_SCISSOR_TEST) - -DEFINE_POINT_THICK_RASTER(sw_point_thick_raster, sw_point_raster_CHECK) -DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_DEPTH, sw_point_raster_DEPTH_CHECK) -DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_BLEND, sw_point_raster_BLEND_CHECK) -DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_DEPTH_BLEND, sw_point_raster_DEPTH_BLEND_CHECK) - -DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_SCISSOR, sw_point_raster_CHECK_SCISSOR) -DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_DEPTH_SCISSOR, sw_point_raster_DEPTH_CHECK_SCISSOR) -DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_BLEND_SCISSOR, sw_point_raster_BLEND_CHECK_SCISSOR) -DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_DEPTH_BLEND_SCISSOR, sw_point_raster_DEPTH_BLEND_CHECK_SCISSOR) - -static inline void sw_point_render(sw_vertex_t *v) -{ - if (!sw_point_clip_and_project(v)) return; - - if (RLSW.pointRadius >= 1.0f) - { - if (SW_STATE_CHECK(SW_STATE_SCISSOR_TEST)) - { - if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_point_thick_raster_DEPTH_BLEND_SCISSOR(v); - else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_point_thick_raster_BLEND_SCISSOR(v); - else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_point_thick_raster_DEPTH_SCISSOR(v); - else sw_point_thick_raster_SCISSOR(v); - } - else - { - if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_point_thick_raster_DEPTH_BLEND(v); - else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_point_thick_raster_BLEND(v); - else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_point_thick_raster_DEPTH(v); - else sw_point_thick_raster(v); - } - } - else - { - if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_point_raster_DEPTH_BLEND(v->screen[0], v->screen[1], v->homogeneous[2], v->color); - else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_point_raster_BLEND(v->screen[0], v->screen[1], v->homogeneous[2], v->color); - else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_point_raster_DEPTH(v->screen[0], v->screen[1], v->homogeneous[2], v->color); - else sw_point_raster(v->screen[0], v->screen[1], v->homogeneous[2], v->color); - } -} -//------------------------------------------------------------------------------------------- - -// Polygon modes rendering logic -//------------------------------------------------------------------------------------------- -static inline void sw_poly_point_render(void) -{ - for (int i = 0; i < RLSW.vertexCounter; i++) sw_point_render(&RLSW.vertexBuffer[i]); -} - -static inline void sw_poly_line_render(void) -{ - const sw_vertex_t *vertices = RLSW.vertexBuffer; - int cm1 = RLSW.vertexCounter - 1; - - for (int i = 0; i < cm1; i++) - { - sw_vertex_t verts[2] = { vertices[i], vertices[i + 1] }; - sw_line_render(verts); - } - - sw_vertex_t verts[2] = { vertices[cm1], vertices[0] }; - sw_line_render(verts); -} - -static inline void sw_poly_fill_render(void) -{ - switch (RLSW.drawMode) - { - case SW_POINTS: sw_point_render(&RLSW.vertexBuffer[0]); break; - case SW_LINES: sw_line_render(RLSW.vertexBuffer); break; - case SW_TRIANGLES: sw_triangle_render(); break; - case SW_QUADS: sw_quad_render(); break; - } -} -//------------------------------------------------------------------------------------------- - -// Immediate rendering logic -//------------------------------------------------------------------------------------------- -void sw_immediate_push_vertex(const float position[4], const float color[4], const float texcoord[2]) -{ - // Copy the attributes in the current vertex - sw_vertex_t *vertex = &RLSW.vertexBuffer[RLSW.vertexCounter++]; - for (int i = 0; i < 4; i++) - { - vertex->position[i] = position[i]; - if (i < 2) vertex->texcoord[i] = texcoord[i]; - vertex->color[i] = color[i]; - } - - // Calculate homogeneous coordinates - const float *m = RLSW.matMVP, *v = vertex->position; - vertex->homogeneous[0] = m[0]*v[0] + m[4]*v[1] + m[8]*v[2] + m[12]*v[3]; - vertex->homogeneous[1] = m[1]*v[0] + m[5]*v[1] + m[9]*v[2] + m[13]*v[3]; - vertex->homogeneous[2] = m[2]*v[0] + m[6]*v[1] + m[10]*v[2] + m[14]*v[3]; - vertex->homogeneous[3] = m[3]*v[0] + m[7]*v[1] + m[11]*v[2] + m[15]*v[3]; - - // Immediate rendering of the primitive if the required number is reached - if (RLSW.vertexCounter == RLSW.reqVertices) - { - switch (RLSW.polyMode) - { - case SW_FILL: sw_poly_fill_render(); break; - case SW_LINE: sw_poly_line_render(); break; - case SW_POINT: sw_poly_point_render(); break; - default: break; - } - - RLSW.vertexCounter = 0; - } -} - //------------------------------------------------------------------------------------------- // Validity check helper functions //------------------------------------------------------------------------------------------- -static inline bool sw_is_texture_valid(uint32_t id) +static inline bool sw_is_texture_valid(sw_handle_t id) { - bool valid = true; + return sw_pool_valid(&RLSW.texturePool, id); +} - if (id == 0) valid = false; - else if (id >= SW_MAX_TEXTURES) valid = false; - else if (RLSW.loadedTextures[id].pixels == NULL) valid = false; - - return valid; +static inline bool sw_is_texture_complete(sw_texture_t* tex) +{ + return (tex != NULL) && (tex->pixels != NULL); } static inline bool sw_is_texture_filter_valid(int filter) @@ -3539,6 +1535,2381 @@ static inline bool sw_is_blend_dst_factor_valid(int blend) return result; } + +static inline bool sw_is_ready_to_render(void) +{ + return (swCheckFramebufferStatus() == SW_FRAMEBUFFER_COMPLETE); +} +//------------------------------------------------------------------------------------------- + +// Pixel management functions +//------------------------------------------------------------------------------------------- +static inline int sw_pixel_get_format(SWformat format, SWtype type) +{ + int channels = 0; + int bitsPerChannel = 8; // Default: 8 bits per channel + + // Handle case where format is a depth component + if (format == SW_DEPTH_COMPONENT) + { + switch (type) + { + case SW_UNSIGNED_BYTE: return SW_PIXELFORMAT_DEPTH_D8; + case SW_BYTE: return SW_PIXELFORMAT_DEPTH_D8; + case SW_UNSIGNED_SHORT: return SW_PIXELFORMAT_DEPTH_D16; + case SW_SHORT: return SW_PIXELFORMAT_DEPTH_D16; + case SW_UNSIGNED_INT: return SW_PIXELFORMAT_DEPTH_D32; + case SW_INT: return SW_PIXELFORMAT_DEPTH_D32; + case SW_FLOAT: return SW_PIXELFORMAT_DEPTH_D32; + default: return SW_PIXELFORMAT_UNKNOWN; + } + } + + // Handle case where the type is a packed color + switch (type) + { + case SW_UNSIGNED_BYTE_3_3_2: return SW_PIXELFORMAT_COLOR_R3G3B2; + case SW_UNSIGNED_SHORT_5_6_5: return SW_PIXELFORMAT_COLOR_R5G6B5; + case SW_UNSIGNED_SHORT_4_4_4_4: return SW_PIXELFORMAT_COLOR_R4G4B4A4; + case SW_UNSIGNED_SHORT_5_5_5_1: return SW_PIXELFORMAT_COLOR_R5G5B5A1; + default: break; + } + + // Determine the number of channels (format) + switch (format) + { + case SW_LUMINANCE: channels = 1; break; + case SW_LUMINANCE_ALPHA: channels = 2; break; + case SW_RGB: channels = 3; break; + case SW_RGBA: channels = 4; break; + default: return SW_PIXELFORMAT_UNKNOWN; + } + + // Determine the depth of each channel (type) + switch (type) + { + case SW_UNSIGNED_BYTE: bitsPerChannel = 8; break; + case SW_BYTE: bitsPerChannel = 8; break; + case SW_UNSIGNED_SHORT: bitsPerChannel = 16; break; + case SW_SHORT: bitsPerChannel = 16; break; + case SW_UNSIGNED_INT: bitsPerChannel = 32; break; + case SW_INT: bitsPerChannel = 32; break; + case SW_FLOAT: bitsPerChannel = 32; break; + default: return SW_PIXELFORMAT_UNKNOWN; + } + + // Map the format and type to the correct internal format + if (bitsPerChannel == 8) + { + if (channels == 1) return SW_PIXELFORMAT_COLOR_GRAYSCALE; + if (channels == 2) return SW_PIXELFORMAT_COLOR_GRAYALPHA; + if (channels == 3) return SW_PIXELFORMAT_COLOR_R8G8B8; + if (channels == 4) return SW_PIXELFORMAT_COLOR_R8G8B8A8; + } + else if (bitsPerChannel == 16) + { + if (channels == 1) return SW_PIXELFORMAT_COLOR_R16; + if (channels == 3) return SW_PIXELFORMAT_COLOR_R16G16B16; + if (channels == 4) return SW_PIXELFORMAT_COLOR_R16G16B16A16; + } + else if (bitsPerChannel == 32) + { + if (channels == 1) return SW_PIXELFORMAT_COLOR_R32; + if (channels == 3) return SW_PIXELFORMAT_COLOR_R32G32B32; + if (channels == 4) return SW_PIXELFORMAT_COLOR_R32G32B32A32; + } + + return SW_PIXELFORMAT_UNKNOWN; +} + +static inline void sw_pixel_get_color8_GRAYSCALE(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + uint8_t gray = ((const uint8_t *)pixels)[offset]; + color[0] = gray; + color[1] = gray; + color[2] = gray; + color[3] = 255; +} + +static inline void sw_pixel_get_color8_GRAYALPHA(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + const uint8_t *src = &((const uint8_t *)pixels)[offset*2]; + color[0] = src[0]; + color[1] = src[0]; + color[2] = src[0]; + color[3] = src[1]; +} + +static inline void sw_pixel_get_color8_R3G3B2(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + uint8_t pixel = ((const uint8_t *)pixels)[offset]; + color[0] = sw_expand_3to8((pixel >> 5) & 0x07); + color[1] = sw_expand_3to8((pixel >> 2) & 0x07); + color[2] = sw_expand_2to8( pixel & 0x03); + color[3] = 255; +} + +static inline void sw_pixel_get_color8_R5G6B5(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + uint16_t pixel = ((const uint16_t *)pixels)[offset]; + color[0] = sw_expand_5to8((pixel >> 11) & 0x1F); + color[1] = sw_expand_6to8((pixel >> 5) & 0x3F); + color[2] = sw_expand_5to8( pixel & 0x1F); + color[3] = 255; +} + +static inline void sw_pixel_get_color8_R8G8B8(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + const uint8_t *src = &((const uint8_t *)pixels)[offset*3]; + color[0] = src[0]; + color[1] = src[1]; + color[2] = src[2]; + color[3] = 255; +} + +static inline void sw_pixel_get_color8_R5G5B5A1(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + uint16_t pixel = ((const uint16_t *)pixels)[offset]; + color[0] = sw_expand_5to8((pixel >> 11) & 0x1F); + color[1] = sw_expand_5to8((pixel >> 6) & 0x1F); + color[2] = sw_expand_5to8((pixel >> 1) & 0x1F); + color[3] = sw_expand_1to8( pixel & 0x01); +} + +static inline void sw_pixel_get_color8_R4G4B4A4(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + uint16_t pixel = ((const uint16_t *)pixels)[offset]; + color[0] = sw_expand_4to8((pixel >> 12) & 0x0F); + color[1] = sw_expand_4to8((pixel >> 8) & 0x0F); + color[2] = sw_expand_4to8((pixel >> 4) & 0x0F); + color[3] = sw_expand_4to8( pixel & 0x0F); +} + +static inline void sw_pixel_get_color8_R8G8B8A8(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + const uint8_t *src = &((const uint8_t *)pixels)[offset*4]; + color[0] = src[0]; + color[1] = src[1]; + color[2] = src[2]; + color[3] = src[3]; +} + +static inline void sw_pixel_get_color8_R32(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + uint8_t gray = (uint8_t)(((const float *)pixels)[offset]*255.0f); + color[0] = gray; + color[1] = gray; + color[2] = gray; + color[3] = 255; +} + +static inline void sw_pixel_get_color8_R32G32B32(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + const float *src = &((const float *)pixels)[offset*3]; + color[0] = (uint8_t)(src[0]*255.0f); + color[1] = (uint8_t)(src[1]*255.0f); + color[2] = (uint8_t)(src[2]*255.0f); + color[3] = 255; +} + +static inline void sw_pixel_get_color8_R32G32B32A32(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + const float *src = &((const float *)pixels)[offset*4]; + color[0] = (uint8_t)(src[0]*255.0f); + color[1] = (uint8_t)(src[1]*255.0f); + color[2] = (uint8_t)(src[2]*255.0f); + color[3] = (uint8_t)(src[3]*255.0f); +} + +static inline void sw_pixel_get_color8_R16(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + uint8_t gray = (uint8_t)(sw_half_to_float(((const uint16_t *)pixels)[offset])*255.0f); + color[0] = gray; + color[1] = gray; + color[2] = gray; + color[3] = 255; +} + +static inline void sw_pixel_get_color8_R16G16B16(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + const uint16_t *src = &((const uint16_t *)pixels)[offset*3]; + color[0] = (uint8_t)(sw_half_to_float(src[0])*255.0f); + color[1] = (uint8_t)(sw_half_to_float(src[1])*255.0f); + color[2] = (uint8_t)(sw_half_to_float(src[2])*255.0f); + color[3] = 255; +} + +static inline void sw_pixel_get_color8_R16G16B16A16(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + const uint16_t *src = &((const uint16_t *)pixels)[offset*4]; + color[0] = (uint8_t)(sw_half_to_float(src[0])*255.0f); + color[1] = (uint8_t)(sw_half_to_float(src[1])*255.0f); + color[2] = (uint8_t)(sw_half_to_float(src[2])*255.0f); + color[3] = (uint8_t)(sw_half_to_float(src[3])*255.0f); +} + +static inline void sw_pixel_get_color8(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset, sw_pixelformat_t format) +{ + switch (format) + { + case SW_PIXELFORMAT_COLOR_GRAYSCALE: + sw_pixel_get_color8_GRAYSCALE(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_GRAYALPHA: + sw_pixel_get_color8_GRAYALPHA(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R3G3B2: + sw_pixel_get_color8_R3G3B2(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R5G6B5: + sw_pixel_get_color8_R5G6B5(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R8G8B8: + sw_pixel_get_color8_R8G8B8(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R5G5B5A1: + sw_pixel_get_color8_R5G5B5A1(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R4G4B4A4: + sw_pixel_get_color8_R4G4B4A4(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R8G8B8A8: + sw_pixel_get_color8_R8G8B8A8(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R32: + sw_pixel_get_color8_R32(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R32G32B32: + sw_pixel_get_color8_R32G32B32(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R32G32B32A32: + sw_pixel_get_color8_R32G32B32A32(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R16: + sw_pixel_get_color8_R16(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R16G16B16: + sw_pixel_get_color8_R16G16B16(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R16G16B16A16: + sw_pixel_get_color8_R16G16B16A16(color, pixels, offset); + break; + + case SW_PIXELFORMAT_UNKNOWN: + case SW_PIXELFORMAT_DEPTH_D8: + case SW_PIXELFORMAT_DEPTH_D16: + case SW_PIXELFORMAT_DEPTH_D32: + case SW_PIXELFORMAT_COUNT: + { + color[0] = 0.0f; + color[1] = 0.0f; + color[2] = 0.0f; + color[3] = 0.0f; + break; + } + } +} + +static inline void sw_pixel_set_color8_GRAYSCALE(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +{ + ((uint8_t *)pixels)[offset] = sw_luminance8(color); +} + +static inline void sw_pixel_set_color8_GRAYALPHA(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +{ + uint8_t *dst = &((uint8_t *)pixels)[offset*2]; + dst[0] = sw_luminance8(color); + dst[1] = color[3]; +} + +static inline void sw_pixel_set_color8_R3G3B2(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +{ + uint8_t pixel = (sw_compress_8to3(color[0]) << 5) + | (sw_compress_8to3(color[1]) << 2) + | sw_compress_8to2(color[2]); + ((uint8_t *)pixels)[offset] = pixel; +} + +static inline void sw_pixel_set_color8_R5G6B5(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +{ + uint16_t pixel = (sw_compress_8to5(color[0]) << 11) + | (sw_compress_8to6(color[1]) << 5) + | sw_compress_8to5(color[2]); + ((uint16_t *)pixels)[offset] = pixel; +} + +static inline void sw_pixel_set_color8_R8G8B8(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +{ + uint8_t *dst = &((uint8_t *)pixels)[offset*3]; + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; +} + +static inline void sw_pixel_set_color8_R5G5B5A1(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +{ + uint16_t pixel = (sw_compress_8to5(color[0]) << 11) + | (sw_compress_8to5(color[1]) << 6) + | (sw_compress_8to5(color[2]) << 1) + | sw_compress_8to1(color[3]); + ((uint16_t *)pixels)[offset] = pixel; +} + +static inline void sw_pixel_set_color8_R4G4B4A4(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +{ + uint16_t pixel = (sw_compress_8to4(color[0]) << 12) + | (sw_compress_8to4(color[1]) << 8) + | (sw_compress_8to4(color[2]) << 4) + | sw_compress_8to4(color[3]); + ((uint16_t *)pixels)[offset] = pixel; +} + +static inline void sw_pixel_set_color8_R8G8B8A8(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +{ + uint8_t *dst = &((uint8_t *)pixels)[offset*4]; + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; + dst[3] = color[3]; +} + +static inline void sw_pixel_set_color8_R32(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +{ + ((float *)pixels)[offset] = sw_luminance8(color)*SW_INV_255; +} + +static inline void sw_pixel_set_color8_R32G32B32(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +{ + float *dst = &((float *)pixels)[offset*3]; + dst[0] = color[0]*SW_INV_255; + dst[1] = color[1]*SW_INV_255; + dst[2] = color[2]*SW_INV_255; +} + +static inline void sw_pixel_set_color8_R32G32B32A32(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +{ + float *dst = &((float *)pixels)[offset*4]; + dst[0] = color[0]*SW_INV_255; + dst[1] = color[1]*SW_INV_255; + dst[2] = color[2]*SW_INV_255; + dst[3] = color[3]*SW_INV_255; +} + +static inline void sw_pixel_set_color8_R16(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +{ + ((uint16_t *)pixels)[offset] = sw_float_to_half(sw_luminance8(color)*SW_INV_255); +} + +static inline void sw_pixel_set_color8_R16G16B16(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +{ + uint16_t *dst = &((uint16_t *)pixels)[offset*3]; + dst[0] = sw_float_to_half(color[0]*SW_INV_255); + dst[1] = sw_float_to_half(color[1]*SW_INV_255); + dst[2] = sw_float_to_half(color[2]*SW_INV_255); +} + +static inline void sw_pixel_set_color8_R16G16B16A16(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +{ + uint16_t *dst = &((uint16_t *)pixels)[offset*4]; + dst[0] = sw_float_to_half(color[0]*SW_INV_255); + dst[1] = sw_float_to_half(color[1]*SW_INV_255); + dst[2] = sw_float_to_half(color[2]*SW_INV_255); + dst[3] = sw_float_to_half(color[3]*SW_INV_255); +} + +static inline void sw_pixel_set_color8(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset, sw_pixelformat_t format) +{ + switch (format) + { + case SW_PIXELFORMAT_COLOR_GRAYSCALE: + sw_pixel_set_color8_GRAYSCALE(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_GRAYALPHA: + sw_pixel_set_color8_GRAYALPHA(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R3G3B2: + sw_pixel_set_color8_R3G3B2(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R5G6B5: + sw_pixel_set_color8_R5G6B5(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R8G8B8: + sw_pixel_set_color8_R8G8B8(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R5G5B5A1: + sw_pixel_set_color8_R5G5B5A1(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R4G4B4A4: + sw_pixel_set_color8_R4G4B4A4(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R8G8B8A8: + sw_pixel_set_color8_R8G8B8A8(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R32: + sw_pixel_set_color8_R32(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R32G32B32: + sw_pixel_set_color8_R32G32B32(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R32G32B32A32: + sw_pixel_set_color8_R32G32B32A32(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R16: + sw_pixel_set_color8_R16(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R16G16B16: + sw_pixel_set_color8_R16G16B16(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R16G16B16A16: + sw_pixel_set_color8_R16G16B16A16(pixels, color, offset); + break; + + case SW_PIXELFORMAT_UNKNOWN: + case SW_PIXELFORMAT_DEPTH_D8: + case SW_PIXELFORMAT_DEPTH_D16: + case SW_PIXELFORMAT_DEPTH_D32: + case SW_PIXELFORMAT_COUNT: + break; + } +} + +static inline void sw_pixel_get_color_GRAYSCALE(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + float gray = ((const uint8_t *)pixels)[offset]*SW_INV_255; + color[0] = gray; + color[1] = gray; + color[2] = gray; + color[3] = 1.0f; +} + +static inline void sw_pixel_get_color_GRAYALPHA(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + const uint8_t *src = &((const uint8_t *)pixels)[offset*2]; + float gray = src[0]*SW_INV_255; + color[0] = gray; + color[1] = gray; + color[2] = gray; + color[3] = src[1]*SW_INV_255; +} + +static inline void sw_pixel_get_color_R3G3B2(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + uint8_t pixel = ((const uint8_t *)pixels)[offset]; + color[0] = sw_expand_3tof((pixel >> 5) & 0x07); + color[1] = sw_expand_3tof((pixel >> 2) & 0x07); + color[2] = sw_expand_2tof( pixel & 0x03); + color[3] = 1.0f; +} + +static inline void sw_pixel_get_color_R5G6B5(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + uint16_t pixel = ((const uint16_t *)pixels)[offset]; + color[0] = sw_expand_5tof((pixel >> 11) & 0x1F); + color[1] = sw_expand_6tof((pixel >> 5) & 0x3F); + color[2] = sw_expand_5tof( pixel & 0x1F); + color[3] = 1.0f; +} + +static inline void sw_pixel_get_color_R8G8B8(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + const uint8_t *src = &((const uint8_t *)pixels)[offset*3]; + color[0] = src[0]*SW_INV_255; + color[1] = src[1]*SW_INV_255; + color[2] = src[2]*SW_INV_255; + color[3] = 1.0f; +} + +static inline void sw_pixel_get_color_R5G5B5A1(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + uint16_t pixel = ((const uint16_t *)pixels)[offset]; + color[0] = sw_expand_5tof((pixel >> 11) & 0x1F); + color[1] = sw_expand_5tof((pixel >> 6) & 0x1F); + color[2] = sw_expand_5tof((pixel >> 1) & 0x1F); + color[3] = sw_expand_1tof( pixel & 0x01); +} + +static inline void sw_pixel_get_color_R4G4B4A4(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + uint16_t pixel = ((const uint16_t *)pixels)[offset]; + color[0] = sw_expand_4tof((pixel >> 12) & 0x0F); + color[1] = sw_expand_4tof((pixel >> 8) & 0x0F); + color[2] = sw_expand_4tof((pixel >> 4) & 0x0F); + color[3] = sw_expand_4tof( pixel & 0x0F); +} + +static inline void sw_pixel_get_color_R8G8B8A8(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + const uint8_t *src = &((const uint8_t *)pixels)[offset*4]; + +#if defined(SW_HAS_NEON) + uint32_t tmp = (uint32_t)src[0] | ((uint32_t)src[1] << 8) | ((uint32_t)src[2] << 16) | ((uint32_t)src[3] << 24); + uint8x8_t bytes = vreinterpret_u8_u32(vdup_n_u32(tmp)); + uint16x8_t words = vmovl_u8(bytes); + uint32x4_t dwords = vmovl_u16(vget_low_u16(words)); + float32x4_t fvals = vmulq_f32(vcvtq_f32_u32(dwords), vdupq_n_f32(SW_INV_255)); + vst1q_f32(color, fvals); + +#elif defined(SW_HAS_SSE41) + __m128i bytes = _mm_loadu_si32(src); + __m128 fvals = _mm_mul_ps(_mm_cvtepi32_ps(_mm_cvtepu8_epi32(bytes)), _mm_set1_ps(SW_INV_255)); + _mm_storeu_ps(color, fvals); + +#elif defined(SW_HAS_SSE2) + __m128i bytes = _mm_loadu_si32(src); + __m128i words = _mm_unpacklo_epi8(bytes, _mm_setzero_si128()); + __m128i dwords = _mm_unpacklo_epi16(words, _mm_setzero_si128()); + __m128 fvals = _mm_mul_ps(_mm_cvtepi32_ps(dwords), _mm_set1_ps(SW_INV_255)); + _mm_storeu_ps(color, fvals); + +#elif defined(SW_HAS_RVV) + // TODO: Sample code generated by AI, needs testing and review + size_t vl = __riscv_vsetvl_e8m1(4); // Set vector length for 8-bit input elements + vuint8m1_t vsrc_u8 = __riscv_vle8_v_u8m1(src, vl); // Load 4 unsigned 8-bit integers + vuint32m1_t vsrc_u32 = __riscv_vwcvt_xu_u_v_u32m1(vsrc_u8, vl); // Widen to 32-bit unsigned integers + vfloat32m1_t vsrc_f32 = __riscv_vfcvt_f_xu_v_f32m1(vsrc_u32, vl); // Convert to float32 + vfloat32m1_t vnorm = __riscv_vfmul_vf_f32m1(vsrc_f32, SW_INV_255, vl); // Multiply by 1/255.0 to normalize + __riscv_vse32_v_f32m1(color, vnorm, vl); // Store result + +#else + color[0] = (float)src[0]*SW_INV_255; + color[1] = (float)src[1]*SW_INV_255; + color[2] = (float)src[2]*SW_INV_255; + color[3] = (float)src[3]*SW_INV_255; +#endif +} + +static inline void sw_pixel_get_color_R32(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + float val = ((const float *)pixels)[offset]; + color[0] = val; + color[1] = val; + color[2] = val; + color[3] = 1.0f; +} + +static inline void sw_pixel_get_color_R32G32B32(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + const float *src = &((const float *)pixels)[offset*3]; + color[0] = src[0]; + color[1] = src[1]; + color[2] = src[2]; + color[3] = 1.0f; +} + +static inline void sw_pixel_get_color_R32G32B32A32(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + const float *src = &((const float *)pixels)[offset*4]; + color[0] = src[0]; + color[1] = src[1]; + color[2] = src[2]; + color[3] = src[3]; +} + +static inline void sw_pixel_get_color_R16(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + float val = sw_half_to_float(((const uint16_t *)pixels)[offset]); + color[0] = val; + color[1] = val; + color[2] = val; + color[3] = 1.0f; +} + +static inline void sw_pixel_get_color_R16G16B16(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + const uint16_t *src = &((const uint16_t *)pixels)[offset*3]; + color[0] = sw_half_to_float(src[0]); + color[1] = sw_half_to_float(src[1]); + color[2] = sw_half_to_float(src[2]); + color[3] = 1.0f; +} + +static inline void sw_pixel_get_color_R16G16B16A16(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +{ + const uint16_t *src = &((const uint16_t *)pixels)[offset*4]; + color[0] = sw_half_to_float(src[0]); + color[1] = sw_half_to_float(src[1]); + color[2] = sw_half_to_float(src[2]); + color[3] = sw_half_to_float(src[3]); +} + +static inline void sw_pixel_get_color(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset, sw_pixelformat_t format) +{ + switch (format) + { + case SW_PIXELFORMAT_COLOR_GRAYSCALE: + sw_pixel_get_color_GRAYSCALE(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_GRAYALPHA: + sw_pixel_get_color_GRAYALPHA(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R3G3B2: + sw_pixel_get_color_R3G3B2(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R5G6B5: + sw_pixel_get_color_R5G6B5(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R8G8B8: + sw_pixel_get_color_R8G8B8(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R5G5B5A1: + sw_pixel_get_color_R5G5B5A1(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R4G4B4A4: + sw_pixel_get_color_R4G4B4A4(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R8G8B8A8: + sw_pixel_get_color_R8G8B8A8(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R32: + sw_pixel_get_color_R32(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R32G32B32: + sw_pixel_get_color_R32G32B32(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R32G32B32A32: + sw_pixel_get_color_R32G32B32A32(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R16: + sw_pixel_get_color_R16(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R16G16B16: + sw_pixel_get_color_R16G16B16(color, pixels, offset); + break; + case SW_PIXELFORMAT_COLOR_R16G16B16A16: + sw_pixel_get_color_R16G16B16A16(color, pixels, offset); + break; + + case SW_PIXELFORMAT_UNKNOWN: + case SW_PIXELFORMAT_DEPTH_D8: + case SW_PIXELFORMAT_DEPTH_D16: + case SW_PIXELFORMAT_DEPTH_D32: + case SW_PIXELFORMAT_COUNT: + { + color[0] = 0.0f; + color[1] = 0.0f; + color[2] = 0.0f; + color[3] = 0.0f; + break; + } + } +} + +static inline void sw_pixel_set_color_GRAYSCALE(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +{ + ((uint8_t *)pixels)[offset] = (uint8_t)(sw_luminance(color)*255.0f); +} + +static inline void sw_pixel_set_color_GRAYALPHA(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +{ + uint8_t *dst = &((uint8_t *)pixels)[offset*2]; + dst[0] = (uint8_t)(sw_luminance(color)*255.0f); + dst[1] = (uint8_t)(color[3]*255.0f); +} + +static inline void sw_pixel_set_color_R3G3B2(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +{ + uint8_t pixel = (sw_compress_fto3(color[0]) << 5) + | (sw_compress_fto3(color[1]) << 2) + | sw_compress_fto2(color[2]); + ((uint8_t *)pixels)[offset] = pixel; +} + +static inline void sw_pixel_set_color_R5G6B5(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +{ + uint16_t pixel = (sw_compress_fto5(color[0]) << 11) + | (sw_compress_fto6(color[1]) << 5) + | sw_compress_fto5(color[2]); + ((uint16_t *)pixels)[offset] = pixel; +} + +static inline void sw_pixel_set_color_R8G8B8(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +{ + uint8_t *dst = &((uint8_t *)pixels)[offset*3]; + dst[0] = (uint8_t)(color[0]*255.0f); + dst[1] = (uint8_t)(color[1]*255.0f); + dst[2] = (uint8_t)(color[2]*255.0f); +} + +static inline void sw_pixel_set_color_R5G5B5A1(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +{ + uint16_t pixel = (sw_compress_fto5(color[0]) << 11) + | (sw_compress_fto5(color[1]) << 6) + | (sw_compress_fto5(color[2]) << 1) + | sw_compress_fto1(color[3]); + ((uint16_t *)pixels)[offset] = pixel; +} + +static inline void sw_pixel_set_color_R4G4B4A4(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +{ + uint16_t pixel = (sw_compress_fto4(color[0]) << 12) + | (sw_compress_fto4(color[1]) << 8) + | (sw_compress_fto4(color[2]) << 4) + | sw_compress_fto4(color[3]); + ((uint16_t *)pixels)[offset] = pixel; +} + +static inline void sw_pixel_set_color_R8G8B8A8(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +{ + uint8_t *dst = &((uint8_t *)pixels)[offset*4]; + +#if defined(SW_HAS_NEON) + float32x4_t fvals = vmulq_f32(vld1q_f32(color), vdupq_n_f32(255.0f)); + uint32x4_t i32 = vcvtq_u32_f32(fvals); + uint16x4_t i16 = vmovn_u32(i32); + uint8x8_t i8 = vmovn_u16(vcombine_u16(i16, i16)); + dst[0] = vget_lane_u8(i8, 0); + dst[1] = vget_lane_u8(i8, 1); + dst[2] = vget_lane_u8(i8, 2); + dst[3] = vget_lane_u8(i8, 3); + +#elif defined(SW_HAS_SSE41) + __m128 fvals = _mm_mul_ps(_mm_loadu_ps(color), _mm_set1_ps(255.0f)); + __m128i i32 = _mm_cvttps_epi32(fvals); + __m128i i16 = _mm_packus_epi32(i32, i32); + __m128i i8 = _mm_packus_epi16(i16, i16); + _mm_storeu_si32(dst, i8); + +#elif defined(SW_HAS_SSE2) + __m128 fvals = _mm_mul_ps(_mm_loadu_ps(color), _mm_set1_ps(255.0f)); + __m128i i32 = _mm_cvttps_epi32(fvals); + __m128i i16 = _mm_packs_epi32(i32, i32); + __m128i i8 = _mm_packus_epi16(i16, i16); + _mm_storeu_si32(dst, i8); + +#elif defined(SW_HAS_RVV) + // TODO: Sample code generated by AI, needs testing and review + // REVIEW: It shouldn't perform so many operations; take inspiration from other versions + // NOTE: RVV 1.0 specs define the use of __riscv_ prefix for instrinsic functions + size_t vl = __riscv_vsetvl_e32m1(4); // Load up to 4 floats into a vector register + vfloat32m1_t vsrc = __riscv_vle32_v_f32m1(src, vl); // Load float32 values + + // Multiply by 255.0f and add 0.5f for rounding + vfloat32m1_t vscaled = __riscv_vfmul_vf_f32m1(vsrc, 255.0f, vl); + vscaled = __riscv_vfadd_vf_f32m1(vscaled, 0.5f, vl); + + // Convert to unsigned integer (truncate toward zero) + vuint32m1_t vu32 = __riscv_vfcvt_xu_f_v_u32m1(vscaled, vl); + + // Narrow from u32 -> u8 + vuint8m1_t vu8 = __riscv_vnclipu_wx_u8m1(vu32, 0, vl); // Round toward zero + __riscv_vse8_v_u8m1(dst, vu8, vl); // Store result + +#else + dst[0] = (uint8_t)(color[0]*255.0f); + dst[1] = (uint8_t)(color[1]*255.0f); + dst[2] = (uint8_t)(color[2]*255.0f); + dst[3] = (uint8_t)(color[3]*255.0f); +#endif +} + +static inline void sw_pixel_set_color_R32(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +{ + ((float *)pixels)[offset] = sw_luminance(color); +} + +static inline void sw_pixel_set_color_R32G32B32(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +{ + float *dst = &((float *)pixels)[offset*3]; + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; +} + +static inline void sw_pixel_set_color_R32G32B32A32(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +{ + float *dst = &((float *)pixels)[offset*4]; + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; + dst[3] = color[3]; +} + +static inline void sw_pixel_set_color_R16(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +{ + ((uint16_t *)pixels)[offset] = sw_float_to_half(sw_luminance(color)); +} + +static inline void sw_pixel_set_color_R16G16B16(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +{ + uint16_t *dst = &((uint16_t *)pixels)[offset*3]; + dst[0] = sw_float_to_half(color[0]); + dst[1] = sw_float_to_half(color[1]); + dst[2] = sw_float_to_half(color[2]); +} + +static inline void sw_pixel_set_color_R16G16B16A16(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +{ + uint16_t *dst = &((uint16_t *)pixels)[offset*4]; + dst[0] = sw_float_to_half(color[0]); + dst[1] = sw_float_to_half(color[1]); + dst[2] = sw_float_to_half(color[2]); + dst[3] = sw_float_to_half(color[3]); +} + +static inline void sw_pixel_set_color(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset, sw_pixelformat_t format) +{ + switch (format) + { + case SW_PIXELFORMAT_COLOR_GRAYSCALE: + sw_pixel_set_color_GRAYSCALE(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_GRAYALPHA: + sw_pixel_set_color_GRAYALPHA(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R3G3B2: + sw_pixel_set_color_R3G3B2(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R5G6B5: + sw_pixel_set_color_R5G6B5(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R8G8B8: + sw_pixel_set_color_R8G8B8(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R5G5B5A1: + sw_pixel_set_color_R5G5B5A1(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R4G4B4A4: + sw_pixel_set_color_R4G4B4A4(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R8G8B8A8: + sw_pixel_set_color_R8G8B8A8(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R32: + sw_pixel_set_color_R32(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R32G32B32: + sw_pixel_set_color_R32G32B32(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R32G32B32A32: + sw_pixel_set_color_R32G32B32A32(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R16: + sw_pixel_set_color_R16(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R16G16B16: + sw_pixel_set_color_R16G16B16(pixels, color, offset); + break; + case SW_PIXELFORMAT_COLOR_R16G16B16A16: + sw_pixel_set_color_R16G16B16A16(pixels, color, offset); + break; + + case SW_PIXELFORMAT_UNKNOWN: + case SW_PIXELFORMAT_DEPTH_D8: + case SW_PIXELFORMAT_DEPTH_D16: + case SW_PIXELFORMAT_DEPTH_D32: + case SW_PIXELFORMAT_COUNT: + break; + } +} + +static inline float sw_pixel_get_depth_D8(const void *pixels, uint32_t offset) +{ + return (float)((uint8_t *)pixels)[offset]*SW_INV_255; +} + +static inline float sw_pixel_get_depth_D16(const void *pixels, uint32_t offset) +{ + return (float)((uint16_t *)pixels)[offset]/UINT16_MAX; +} + +static inline float sw_pixel_get_depth_D32(const void *pixels, uint32_t offset) +{ + return ((float *)pixels)[offset]; +} + +static inline float sw_pixel_get_depth(const void *pixels, uint32_t offset, sw_pixelformat_t format) +{ + switch (format) + { + case SW_PIXELFORMAT_DEPTH_D8: + return sw_pixel_get_depth_D8(pixels, offset); + case SW_PIXELFORMAT_DEPTH_D16: + return sw_pixel_get_depth_D16(pixels, offset); + case SW_PIXELFORMAT_DEPTH_D32: + return sw_pixel_get_depth_D32(pixels, offset); + + case SW_PIXELFORMAT_UNKNOWN: + case SW_PIXELFORMAT_COLOR_GRAYSCALE: + case SW_PIXELFORMAT_COLOR_GRAYALPHA: + case SW_PIXELFORMAT_COLOR_R3G3B2: + case SW_PIXELFORMAT_COLOR_R5G6B5: + case SW_PIXELFORMAT_COLOR_R8G8B8: + case SW_PIXELFORMAT_COLOR_R5G5B5A1: + case SW_PIXELFORMAT_COLOR_R4G4B4A4: + case SW_PIXELFORMAT_COLOR_R8G8B8A8: + case SW_PIXELFORMAT_COLOR_R32: + case SW_PIXELFORMAT_COLOR_R32G32B32: + case SW_PIXELFORMAT_COLOR_R32G32B32A32: + case SW_PIXELFORMAT_COLOR_R16: + case SW_PIXELFORMAT_COLOR_R16G16B16: + case SW_PIXELFORMAT_COLOR_R16G16B16A16: + case SW_PIXELFORMAT_COUNT: + break; + } + + return 0.0f; +} + +static inline void sw_pixel_set_depth_D8(void *pixels, float depth, uint32_t offset) +{ + ((uint8_t *)pixels)[offset] = (uint8_t)(depth*UINT8_MAX); +} + +static inline void sw_pixel_set_depth_D16(void *pixels, float depth, uint32_t offset) +{ + ((uint16_t *)pixels)[offset] = (uint16_t)(depth*UINT16_MAX); +} + +static inline void sw_pixel_set_depth_D32(void *pixels, float depth, uint32_t offset) +{ + ((float *)pixels)[offset] = depth; +} + +static inline void sw_pixel_set_depth(void *pixels, float depth, uint32_t offset, sw_pixelformat_t format) +{ + switch (format) + { + case SW_PIXELFORMAT_DEPTH_D8: + sw_pixel_set_depth_D8(pixels, depth, offset); + break; + case SW_PIXELFORMAT_DEPTH_D16: + sw_pixel_set_depth_D16(pixels, depth, offset); + break; + case SW_PIXELFORMAT_DEPTH_D32: + sw_pixel_set_depth_D32(pixels, depth, offset); + break; + + case SW_PIXELFORMAT_UNKNOWN: + case SW_PIXELFORMAT_COLOR_GRAYSCALE: + case SW_PIXELFORMAT_COLOR_GRAYALPHA: + case SW_PIXELFORMAT_COLOR_R3G3B2: + case SW_PIXELFORMAT_COLOR_R5G6B5: + case SW_PIXELFORMAT_COLOR_R8G8B8: + case SW_PIXELFORMAT_COLOR_R5G5B5A1: + case SW_PIXELFORMAT_COLOR_R4G4B4A4: + case SW_PIXELFORMAT_COLOR_R8G8B8A8: + case SW_PIXELFORMAT_COLOR_R32: + case SW_PIXELFORMAT_COLOR_R32G32B32: + case SW_PIXELFORMAT_COLOR_R32G32B32A32: + case SW_PIXELFORMAT_COLOR_R16: + case SW_PIXELFORMAT_COLOR_R16G16B16: + case SW_PIXELFORMAT_COLOR_R16G16B16A16: + case SW_PIXELFORMAT_COUNT: + break; + } +} +//------------------------------------------------------------------------------------------- + +// Texture functionality +//------------------------------------------------------------------------------------------- +static inline bool sw_texture_alloc(sw_texture_t *texture, const void *data, int w, int h, sw_pixelformat_t format) +{ + int bpp = SW_PIXELFORMAT_SIZE[format]; + int newSize = w*h*bpp; + + if (newSize > texture->allocSz) + { + void* ptr = SW_REALLOC(texture->pixels, newSize); + if (!ptr) { RLSW.errCode = SW_OUT_OF_MEMORY; return false; } + texture->allocSz = newSize; + texture->pixels = ptr; + } + + uint8_t* dst = texture->pixels; + const uint8_t* src = data; + + if (data) for (int i = 0; i < newSize; i++) dst[i] = src[i]; + else for (int i = 0; i < newSize; i++) dst[i] = 0; + + texture->format = format; + texture->width = w; + texture->height = h; + texture->wMinus1 = w - 1; + texture->hMinus1 = h - 1; + texture->tx = 1.0f/w; + texture->ty = 1.0f/h; + + return true; +} + +static inline void sw_texture_free(sw_texture_t *texture) +{ + SW_FREE(texture->pixels); +} + +static inline void sw_texture_fetch(float *SW_RESTRICT color, const sw_texture_t *SW_RESTRICT tex, int x, int y) +{ + sw_pixel_get_color(color, tex->pixels, y*tex->width + x, tex->format); +} + +static inline void sw_texture_sample_nearest(float *SW_RESTRICT color, const sw_texture_t *SW_RESTRICT tex, float u, float v) +{ + u = (tex->sWrap == SW_REPEAT)? sw_fract(u) : sw_saturate(u); + v = (tex->tWrap == SW_REPEAT)? sw_fract(v) : sw_saturate(v); + + int x = u*tex->width; + int y = v*tex->height; + + sw_texture_fetch(color, tex, x, y); +} + +static inline void sw_texture_sample_linear(float *SW_RESTRICT color, const sw_texture_t *SW_RESTRICT tex, float u, float v) +{ + // TODO: With a bit more cleverness thee number of operations can + // be clearly reduced, but for now it works fine + + float xf = (u*tex->width) - 0.5f; + float yf = (v*tex->height) - 0.5f; + + float fx = sw_fract(xf); + float fy = sw_fract(yf); + + int x0 = (int)xf; + int y0 = (int)yf; + + int x1 = x0 + 1; + int y1 = y0 + 1; + + // NOTE: If the textures are POT, avoid the division for SW_REPEAT + + if (tex->sWrap == SW_CLAMP) + { + x0 = (x0 > tex->wMinus1)? tex->wMinus1 : x0; + x1 = (x1 > tex->wMinus1)? tex->wMinus1 : x1; + } + else + { + x0 = (x0%tex->width + tex->width)%tex->width; + x1 = (x1%tex->width + tex->width)%tex->width; + } + + if (tex->tWrap == SW_CLAMP) + { + y0 = (y0 > tex->hMinus1)? tex->hMinus1 : y0; + y1 = (y1 > tex->hMinus1)? tex->hMinus1 : y1; + } + else + { + y0 = (y0%tex->height + tex->height)%tex->height; + y1 = (y1%tex->height + tex->height)%tex->height; + } + + float c00[4], c10[4], c01[4], c11[4]; + sw_texture_fetch(c00, tex, x0, y0); + sw_texture_fetch(c10, tex, x1, y0); + sw_texture_fetch(c01, tex, x0, y1); + sw_texture_fetch(c11, tex, x1, y1); + + for (int i = 0; i < 4; i++) + { + float t = c00[i] + fx*(c10[i] - c00[i]); + float b = c01[i] + fx*(c11[i] - c01[i]); + color[i] = t + fy*(b - t); + } +} + +static inline void sw_texture_sample(float *SW_RESTRICT color, const sw_texture_t *SW_RESTRICT tex, + float u, float v, float dUdx, float dUdy, float dVdx, float dVdy) +{ + // Previous method: There is no need to compute the square root + // because using the squared value, the comparison remains (L2 > 1.0f*1.0f) + //float du = sqrtf(dUdx*dUdx + dUdy*dUdy); + //float dv = sqrtf(dVdx*dVdx + dVdy*dVdy); + //float L = (du > dv)? du : dv; + + // Calculate the derivatives for each axis + float dU2 = dUdx*dUdx + dUdy*dUdy; + float dV2 = dVdx*dVdx + dVdy*dVdy; + float L2 = (dU2 > dV2)? dU2 : dV2; + + SWfilter filter = (L2 > 1.0f)? tex->minFilter : tex->magFilter; + + switch (filter) + { + case SW_NEAREST: sw_texture_sample_nearest(color, tex, u, v); break; + case SW_LINEAR: sw_texture_sample_linear(color, tex, u, v); break; + default: break; + } +} +//------------------------------------------------------------------------------------------- + +// Framebuffer management functions +//------------------------------------------------------------------------------------------- +static inline bool sw_default_framebuffer_alloc(sw_default_framebuffer_t* fb, int w, int h) +{ + if (!sw_texture_alloc(&fb->color, NULL, w, h, SW_FRAMEBUFFER_COLOR_FORMAT)) + { + return false; + } + + if (!sw_texture_alloc(&fb->depth, NULL, w, h, SW_FRAMEBUFFER_DEPTH_FORMAT)) + { + return false; + } + + return true; +} + +static inline void sw_default_framebuffer_free(sw_default_framebuffer_t* fb) +{ + sw_texture_free(&fb->color); + sw_texture_free(&fb->depth); +} + +static inline void sw_framebuffer_fill_color(sw_texture_t *colorBuffer, const float color[4]) +{ + // NOTE: MSVC doesn't support VLA, so we allocate the largest possible size + //uint8_t pixel[SW_FRAMEBUFFER_COLOR_SIZE]; + + uint8_t pixel[16]; + SW_FRAMEBUFFER_COLOR_SET(pixel, color, 0); + + uint8_t *dst = (uint8_t *)colorBuffer->pixels; + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { + int xMin = sw_clamp_int(RLSW.scMin[0], 0, colorBuffer->width - 1); + int xMax = sw_clamp_int(RLSW.scMax[0], 0, colorBuffer->width - 1); + int yMin = sw_clamp_int(RLSW.scMin[1], 0, colorBuffer->height - 1); + int yMax = sw_clamp_int(RLSW.scMax[1], 0, colorBuffer->height - 1); + + int w = xMax - xMin; + for (int y = yMin; y <= yMax; y++) + { + uint8_t *row = dst + (y*colorBuffer->width + xMin)*SW_FRAMEBUFFER_COLOR_SIZE; + for (int x = 0; x <= w; x++, row += SW_FRAMEBUFFER_COLOR_SIZE) + { + for (int b = 0; b < SW_FRAMEBUFFER_COLOR_SIZE; b++) row[b] = pixel[b]; + } + } + } + else + { + int size = colorBuffer->width*colorBuffer->height; + for (int i = 0; i < size; i++) + { + for (int b = 0; b < SW_FRAMEBUFFER_COLOR_SIZE; b++) + { + dst[i*SW_FRAMEBUFFER_COLOR_SIZE + b] = pixel[b]; + } + } + } +} + +static inline void sw_framebuffer_fill_depth(sw_texture_t *depthBuffer, float depth) +{ + // NOTE: MSVC doesn't support VLA, so we allocate the largest possible size + //uint8_t pixel[SW_FRAMEBUFFER_DEPTH_SIZE]; + + uint8_t pixel[4]; + SW_FRAMEBUFFER_DEPTH_SET(pixel, depth, 0); + + uint8_t *dst = (uint8_t *)depthBuffer->pixels; + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { + int xMin = sw_clamp_int(RLSW.scMin[0], 0, depthBuffer->width - 1); + int xMax = sw_clamp_int(RLSW.scMax[0], 0, depthBuffer->width - 1); + int yMin = sw_clamp_int(RLSW.scMin[1], 0, depthBuffer->height - 1); + int yMax = sw_clamp_int(RLSW.scMax[1], 0, depthBuffer->height - 1); + + int w = xMax - xMin; + for (int y = yMin; y <= yMax; y++) + { + uint8_t *row = dst + (y*depthBuffer->width + xMin)*SW_FRAMEBUFFER_DEPTH_SIZE; + for (int x = 0; x <= w; x++, row += SW_FRAMEBUFFER_DEPTH_SIZE) + { + for (int b = 0; b < SW_FRAMEBUFFER_DEPTH_SIZE; b++) row[b] = pixel[b]; + } + } + } + else + { + int size = depthBuffer->width*depthBuffer->height; + for (int i = 0; i < size; i++) + { + for (int b = 0; b < SW_FRAMEBUFFER_DEPTH_SIZE; b++) + { + dst[i*SW_FRAMEBUFFER_DEPTH_SIZE + b] = pixel[b]; + } + } + } +} + +static inline void sw_framebuffer_output_fast(void* dst, const sw_texture_t* buffer) +{ + int width = buffer->width; + int height = buffer->height; + + uint8_t *d = (uint8_t *)dst; + +#if SW_FRAMEBUFFER_OUTPUT_BGRA && (SW_FRAMEBUFFER_COLOR_FORMAT == SW_PIXELFORMAT_COLOR_R8G8B8A8) + for (int y = height - 1; y >= 0; y--) + { + const uint8_t *src = (uint8_t *)(buffer->pixels) + y*width*4; + for (int x = 0; x < width; x++, src += 4, d += 4) + { + d[0] = src[2]; + d[1] = src[1]; + d[2] = src[0]; + d[3] = src[3]; + } + } +#elif SW_FRAMEBUFFER_OUTPUT_BGRA && (SW_FRAMEBUFFER_COLOR_FORMAT == SW_PIXELFORMAT_COLOR_R8G8B8) + for (int y = height - 1; y >= 0; y--) + { + const uint8_t *src = (uint8_t *)(buffer->pixels) + y*width*3; + for (int x = 0; x < width; x++, src += 3, d += 3) + { + d[0] = src[2]; + d[1] = src[1]; + d[2] = src[0]; + } + } +#else + int rowBytes = width*SW_FRAMEBUFFER_COLOR_SIZE; + for (int y = height - 1; y >= 0; y--) + { + const uint8_t *src = (uint8_t *)(buffer->pixels) + y*rowBytes; + for (int i = 0; i < rowBytes; i++) d[i] = src[i]; + d += rowBytes; + } +#endif +} + +static inline void sw_framebuffer_output_copy(void* dst, const sw_texture_t* buffer, int x, int y, int w, int h, sw_pixelformat_t format) +{ + int dstPixelSize = SW_PIXELFORMAT_SIZE[format]; + int stride = buffer->width; + + const uint8_t *src = (uint8_t *)(buffer->pixels) + ((y + h - 1)*stride + x)*SW_FRAMEBUFFER_COLOR_SIZE; + uint8_t *d = dst; + + for (int iy = 0; iy < h; iy++) + { + const uint8_t *line = src; + uint8_t *dline = d; + + for (int ix = 0; ix < w; ix++) + { + uint8_t color[4]; + SW_FRAMEBUFFER_COLOR8_GET(color, line, 0); + + #if SW_FRAMEBUFFER_OUTPUT_BGRA + if (format == SW_PIXELFORMAT_COLOR_R8G8B8A8 || format == SW_PIXELFORMAT_COLOR_R8G8B8) + { + uint8_t tmp = color[0]; color[0] = color[2]; color[2] = tmp; + } + #endif + + sw_pixel_set_color8(dline, color, 0, format); + line += SW_FRAMEBUFFER_COLOR_SIZE; + dline += dstPixelSize; + } + + src -= stride*SW_FRAMEBUFFER_COLOR_SIZE; + d += w*dstPixelSize; + } +} + +static inline void sw_framebuffer_output_blit( + void* dst, const sw_texture_t* buffer, + int xDst, int yDst, int wDst, int hDst, + int xSrc, int ySrc, int wSrc, int hSrc, + sw_pixelformat_t format) +{ + const uint8_t *srcBase = buffer->pixels; + + int fbWidth = buffer->width; + int dstPixelSize = SW_PIXELFORMAT_SIZE[format]; + + uint32_t xScale = ((uint32_t)wSrc << 16)/(uint32_t)wDst; + uint32_t yScale = ((uint32_t)hSrc << 16)/(uint32_t)hDst; + + int ySrcLast = ySrc + hSrc - 1; + + uint8_t *d = (uint8_t *)dst; + + for (int dy = 0; dy < hDst; dy++) + { + int sy = ySrcLast - (int)(dy*yScale >> 16); + const uint8_t *srcLine = srcBase + (sy*fbWidth + xSrc)*SW_FRAMEBUFFER_COLOR_SIZE; + uint8_t *dline = d; + + for (int dx = 0; dx < wDst; dx++) + { + int sx = (int)(dx*xScale >> 16); + const uint8_t *pixel = srcLine + sx*SW_FRAMEBUFFER_COLOR_SIZE; + + uint8_t color[4]; + SW_FRAMEBUFFER_COLOR8_GET(color, pixel, 0); + + #if SW_FRAMEBUFFER_OUTPUT_BGRA + if (format == SW_PIXELFORMAT_COLOR_R8G8B8A8 || format == SW_PIXELFORMAT_COLOR_R8G8B8) + { + uint8_t tmp = color[0]; color[0] = color[2]; color[2] = tmp; + } + #endif + + sw_pixel_set_color8(dline, color, 0, format); + dline += dstPixelSize; + } + + d += wDst*dstPixelSize; + } +} +//------------------------------------------------------------------------------------------- + +// Color blending functionality +//------------------------------------------------------------------------------------------- +static inline void sw_factor_zero(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + factor[0] = factor[1] = factor[2] = factor[3] = 0.0f; +} + +static inline void sw_factor_one(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + factor[0] = factor[1] = factor[2] = factor[3] = 1.0f; +} + +static inline void sw_factor_src_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + factor[0] = src[0]; factor[1] = src[1]; factor[2] = src[2]; factor[3] = src[3]; +} + +static inline void sw_factor_one_minus_src_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + factor[0] = 1.0f - src[0]; factor[1] = 1.0f - src[1]; + factor[2] = 1.0f - src[2]; factor[3] = 1.0f - src[3]; +} + +static inline void sw_factor_src_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + factor[0] = factor[1] = factor[2] = factor[3] = src[3]; +} + +static inline void sw_factor_one_minus_src_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + float invAlpha = 1.0f - src[3]; + factor[0] = factor[1] = factor[2] = factor[3] = invAlpha; +} + +static inline void sw_factor_dst_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + factor[0] = factor[1] = factor[2] = factor[3] = dst[3]; +} + +static inline void sw_factor_one_minus_dst_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + float invAlpha = 1.0f - dst[3]; + factor[0] = factor[1] = factor[2] = factor[3] = invAlpha; +} + +static inline void sw_factor_dst_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + factor[0] = dst[0]; factor[1] = dst[1]; factor[2] = dst[2]; factor[3] = dst[3]; +} + +static inline void sw_factor_one_minus_dst_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + factor[0] = 1.0f - dst[0]; factor[1] = 1.0f - dst[1]; + factor[2] = 1.0f - dst[2]; factor[3] = 1.0f - dst[3]; +} + +static inline void sw_factor_src_alpha_saturate(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + factor[0] = factor[1] = factor[2] = 1.0f; + factor[3] = (src[3] < 1.0f)? src[3] : 1.0f; +} + +static inline void sw_blend_colors(float *SW_RESTRICT dst/*[4]*/, const float *SW_RESTRICT src/*[4]*/) +{ + float srcFactor[4], dstFactor[4]; + + RLSW.srcFactorFunc(srcFactor, src, dst); + RLSW.dstFactorFunc(dstFactor, src, dst); + + dst[0] = srcFactor[0]*src[0] + dstFactor[0]*dst[0]; + dst[1] = srcFactor[1]*src[1] + dstFactor[1]*dst[1]; + dst[2] = srcFactor[2]*src[2] + dstFactor[2]*dst[2]; + dst[3] = srcFactor[3]*src[3] + dstFactor[3]*dst[3]; +} +//------------------------------------------------------------------------------------------- + +// Projection helper functions +//------------------------------------------------------------------------------------------- +static inline void sw_project_ndc_to_screen(float screen[2], const float ndc[4]) +{ + screen[0] = RLSW.vpCenter[0] + ndc[0]*RLSW.vpHalf[0] + 0.5f; + screen[1] = RLSW.vpCenter[1] + ndc[1]*RLSW.vpHalf[1] + 0.5f; +} +//------------------------------------------------------------------------------------------- + +// Polygon clipping management +//------------------------------------------------------------------------------------------- +#define DEFINE_CLIP_FUNC(name, FUNC_IS_INSIDE, FUNC_COMPUTE_T) \ +static int sw_clip_##name( \ + sw_vertex_t output[SW_MAX_CLIPPED_POLYGON_VERTICES], \ + const sw_vertex_t input[SW_MAX_CLIPPED_POLYGON_VERTICES], \ + int n) \ +{ \ + const sw_vertex_t *prev = &input[n - 1]; \ + int prevInside = FUNC_IS_INSIDE(prev->homogeneous); \ + int outputCount = 0; \ + \ + for (int i = 0; i < n; i++) { \ + const sw_vertex_t *curr = &input[i]; \ + int currInside = FUNC_IS_INSIDE(curr->homogeneous); \ + \ + /* If transition between interior/exterior, calculate intersection point */ \ + if (prevInside != currInside) { \ + float t = FUNC_COMPUTE_T(prev->homogeneous, curr->homogeneous); \ + sw_lerp_vertex_PTCH(&output[outputCount++], prev, curr, t); \ + } \ + \ + /* If current vertex inside, add it */ \ + if (currInside) { \ + output[outputCount++] = *curr; \ + } \ + \ + prev = curr; \ + prevInside = currInside; \ + } \ + \ + return outputCount; \ +} +//------------------------------------------------------------------------------------------- + +// Frustum cliping functions +//------------------------------------------------------------------------------------------- +#define IS_INSIDE_PLANE_W(h) ((h)[3] >= SW_CLIP_EPSILON) +#define IS_INSIDE_PLANE_X_POS(h) ( (h)[0] < (h)[3]) // Exclusive for +X +#define IS_INSIDE_PLANE_X_NEG(h) (-(h)[0] < (h)[3]) // Exclusive for -X +#define IS_INSIDE_PLANE_Y_POS(h) ( (h)[1] < (h)[3]) // Exclusive for +Y +#define IS_INSIDE_PLANE_Y_NEG(h) (-(h)[1] < (h)[3]) // Exclusive for -Y +#define IS_INSIDE_PLANE_Z_POS(h) ( (h)[2] <= (h)[3]) // Inclusive for +Z +#define IS_INSIDE_PLANE_Z_NEG(h) (-(h)[2] <= (h)[3]) // Inclusive for -Z + +#define COMPUTE_T_PLANE_W(hPrev, hCurr) ((SW_CLIP_EPSILON - (hPrev)[3])/((hCurr)[3] - (hPrev)[3])) +#define COMPUTE_T_PLANE_X_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[0])/(((hPrev)[3] - (hPrev)[0]) - ((hCurr)[3] - (hCurr)[0]))) +#define COMPUTE_T_PLANE_X_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[0])/(((hPrev)[3] + (hPrev)[0]) - ((hCurr)[3] + (hCurr)[0]))) +#define COMPUTE_T_PLANE_Y_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[1])/(((hPrev)[3] - (hPrev)[1]) - ((hCurr)[3] - (hCurr)[1]))) +#define COMPUTE_T_PLANE_Y_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[1])/(((hPrev)[3] + (hPrev)[1]) - ((hCurr)[3] + (hCurr)[1]))) +#define COMPUTE_T_PLANE_Z_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[2])/(((hPrev)[3] - (hPrev)[2]) - ((hCurr)[3] - (hCurr)[2]))) +#define COMPUTE_T_PLANE_Z_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[2])/(((hPrev)[3] + (hPrev)[2]) - ((hCurr)[3] + (hCurr)[2]))) + +DEFINE_CLIP_FUNC(w, IS_INSIDE_PLANE_W, COMPUTE_T_PLANE_W) +DEFINE_CLIP_FUNC(x_pos, IS_INSIDE_PLANE_X_POS, COMPUTE_T_PLANE_X_POS) +DEFINE_CLIP_FUNC(x_neg, IS_INSIDE_PLANE_X_NEG, COMPUTE_T_PLANE_X_NEG) +DEFINE_CLIP_FUNC(y_pos, IS_INSIDE_PLANE_Y_POS, COMPUTE_T_PLANE_Y_POS) +DEFINE_CLIP_FUNC(y_neg, IS_INSIDE_PLANE_Y_NEG, COMPUTE_T_PLANE_Y_NEG) +DEFINE_CLIP_FUNC(z_pos, IS_INSIDE_PLANE_Z_POS, COMPUTE_T_PLANE_Z_POS) +DEFINE_CLIP_FUNC(z_neg, IS_INSIDE_PLANE_Z_NEG, COMPUTE_T_PLANE_Z_NEG) +//------------------------------------------------------------------------------------------- + +// Scissor clip functions +//------------------------------------------------------------------------------------------- +#define COMPUTE_T_SCISSOR_X_MIN(hPrev, hCurr) (((RLSW.scClipMin[0])*(hPrev)[3] - (hPrev)[0])/(((hCurr)[0] - (RLSW.scClipMin[0])*(hCurr)[3]) - ((hPrev)[0] - (RLSW.scClipMin[0])*(hPrev)[3]))) +#define COMPUTE_T_SCISSOR_X_MAX(hPrev, hCurr) (((RLSW.scClipMax[0])*(hPrev)[3] - (hPrev)[0])/(((hCurr)[0] - (RLSW.scClipMax[0])*(hCurr)[3]) - ((hPrev)[0] - (RLSW.scClipMax[0])*(hPrev)[3]))) +#define COMPUTE_T_SCISSOR_Y_MIN(hPrev, hCurr) (((RLSW.scClipMin[1])*(hPrev)[3] - (hPrev)[1])/(((hCurr)[1] - (RLSW.scClipMin[1])*(hCurr)[3]) - ((hPrev)[1] - (RLSW.scClipMin[1])*(hPrev)[3]))) +#define COMPUTE_T_SCISSOR_Y_MAX(hPrev, hCurr) (((RLSW.scClipMax[1])*(hPrev)[3] - (hPrev)[1])/(((hCurr)[1] - (RLSW.scClipMax[1])*(hCurr)[3]) - ((hPrev)[1] - (RLSW.scClipMax[1])*(hPrev)[3]))) + +#define IS_INSIDE_SCISSOR_X_MIN(h) ((h)[0] >= (RLSW.scClipMin[0])*(h)[3]) +#define IS_INSIDE_SCISSOR_X_MAX(h) ((h)[0] <= (RLSW.scClipMax[0])*(h)[3]) +#define IS_INSIDE_SCISSOR_Y_MIN(h) ((h)[1] >= (RLSW.scClipMin[1])*(h)[3]) +#define IS_INSIDE_SCISSOR_Y_MAX(h) ((h)[1] <= (RLSW.scClipMax[1])*(h)[3]) + +DEFINE_CLIP_FUNC(scissor_x_min, IS_INSIDE_SCISSOR_X_MIN, COMPUTE_T_SCISSOR_X_MIN) +DEFINE_CLIP_FUNC(scissor_x_max, IS_INSIDE_SCISSOR_X_MAX, COMPUTE_T_SCISSOR_X_MAX) +DEFINE_CLIP_FUNC(scissor_y_min, IS_INSIDE_SCISSOR_Y_MIN, COMPUTE_T_SCISSOR_Y_MIN) +DEFINE_CLIP_FUNC(scissor_y_max, IS_INSIDE_SCISSOR_Y_MAX, COMPUTE_T_SCISSOR_Y_MAX) +//------------------------------------------------------------------------------------------- + +// Main polygon clip function +static bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int *vertexCounter) +{ + static sw_vertex_t tmp[SW_MAX_CLIPPED_POLYGON_VERTICES]; + + int n = *vertexCounter; + + #define CLIP_AGAINST_PLANE(FUNC_CLIP) \ + { \ + n = FUNC_CLIP(tmp, polygon, n); \ + if (n < 3) \ + { \ + *vertexCounter = 0; \ + return false; \ + } \ + for (int i = 0; i < n; i++) polygon[i] = tmp[i]; \ + } + + CLIP_AGAINST_PLANE(sw_clip_w); + CLIP_AGAINST_PLANE(sw_clip_x_pos); + CLIP_AGAINST_PLANE(sw_clip_x_neg); + CLIP_AGAINST_PLANE(sw_clip_y_pos); + CLIP_AGAINST_PLANE(sw_clip_y_neg); + CLIP_AGAINST_PLANE(sw_clip_z_pos); + CLIP_AGAINST_PLANE(sw_clip_z_neg); + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { + CLIP_AGAINST_PLANE(sw_clip_scissor_x_min); + CLIP_AGAINST_PLANE(sw_clip_scissor_x_max); + CLIP_AGAINST_PLANE(sw_clip_scissor_y_min); + CLIP_AGAINST_PLANE(sw_clip_scissor_y_max); + } + + *vertexCounter = n; + + return (n >= 3); +} + +// Triangle rasterizer variant dispatch +//------------------------------------------------------------------------------------------- +#ifndef RLSW_TEMPLATE_RASTER_TRIANGLE_EXPANDING +#define RLSW_TEMPLATE_RASTER_TRIANGLE_EXPANDING + + // State mask to apply before indexing the dispatch table + #define SW_RASTER_TRIANGLE_STATE_MASK \ + (SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND) + + // Single source of truth for all rasterizer specializations + // X(NAME, STATE_FLAGS) + #define SW_RASTER_VARIANTS(X) \ + X(BASE, 0) \ + X(TEX, SW_STATE_TEXTURE_2D) \ + X(DEPTH, SW_STATE_DEPTH_TEST) \ + X(BLEND, SW_STATE_BLEND) \ + X(TEX_DEPTH, SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST) \ + X(TEX_BLEND, SW_STATE_TEXTURE_2D | SW_STATE_BLEND) \ + X(DEPTH_BLEND, SW_STATE_DEPTH_TEST | SW_STATE_BLEND) \ + X(TEX_DEPTH_BLEND, SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND) + + // Forward declarations because clangd does not follow #include __FILE__ to avoid infinite recursion + // These declarations make all variants visible to static analysis tools without affecting compilation + #define SW_FWD_DECL(NAME, _FLAGS) \ + static void sw_raster_triangle_##NAME(const sw_vertex_t*, const sw_vertex_t*, const sw_vertex_t*); + SW_RASTER_VARIANTS(SW_FWD_DECL) // NOLINT + #undef SW_FWD_DECL + + // Specialization generation via self-inclusion + + #define RLSW_TEMPLATE_RASTER_TRIANGLE BASE + #include __FILE__ // IWYU pragma: keep + #undef RLSW_TEMPLATE_RASTER_TRIANGLE + + #define RLSW_TEMPLATE_RASTER_TRIANGLE TEX + #define SW_ENABLE_TEXTURE + #include __FILE__ + #undef SW_ENABLE_TEXTURE + #undef RLSW_TEMPLATE_RASTER_TRIANGLE + + #define RLSW_TEMPLATE_RASTER_TRIANGLE DEPTH + #define SW_ENABLE_DEPTH_TEST + #include __FILE__ + #undef SW_ENABLE_DEPTH_TEST + #undef RLSW_TEMPLATE_RASTER_TRIANGLE + + #define RLSW_TEMPLATE_RASTER_TRIANGLE BLEND + #define SW_ENABLE_BLEND + #include __FILE__ + #undef SW_ENABLE_BLEND + #undef RLSW_TEMPLATE_RASTER_TRIANGLE + + #define RLSW_TEMPLATE_RASTER_TRIANGLE TEX_DEPTH + #define SW_ENABLE_TEXTURE + #define SW_ENABLE_DEPTH_TEST + #include __FILE__ + #undef SW_ENABLE_DEPTH_TEST + #undef SW_ENABLE_TEXTURE + #undef RLSW_TEMPLATE_RASTER_TRIANGLE + + #define RLSW_TEMPLATE_RASTER_TRIANGLE TEX_BLEND + #define SW_ENABLE_TEXTURE + #define SW_ENABLE_BLEND + #include __FILE__ + #undef SW_ENABLE_BLEND + #undef SW_ENABLE_TEXTURE + #undef RLSW_TEMPLATE_RASTER_TRIANGLE + + #define RLSW_TEMPLATE_RASTER_TRIANGLE DEPTH_BLEND + #define SW_ENABLE_DEPTH_TEST + #define SW_ENABLE_BLEND + #include __FILE__ + #undef SW_ENABLE_BLEND + #undef SW_ENABLE_DEPTH_TEST + #undef RLSW_TEMPLATE_RASTER_TRIANGLE + + #define RLSW_TEMPLATE_RASTER_TRIANGLE TEX_DEPTH_BLEND + #define SW_ENABLE_TEXTURE + #define SW_ENABLE_DEPTH_TEST + #define SW_ENABLE_BLEND + #include __FILE__ + #undef SW_ENABLE_BLEND + #undef SW_ENABLE_DEPTH_TEST + #undef SW_ENABLE_TEXTURE + #undef RLSW_TEMPLATE_RASTER_TRIANGLE + + // Dispatch table (auto-generated from SW_RASTER_VARIANTS) + #define SW_TABLE_ENTRY(NAME, FLAGS) [FLAGS] = sw_raster_triangle_##NAME, + static const sw_raster_triangle_f SW_RASTER_TRIANGLE_FUNCS[] = { + SW_RASTER_VARIANTS(SW_TABLE_ENTRY) + }; + #undef SW_TABLE_ENTRY + +#undef SW_RASTER_VARIANTS +#undef RLSW_TEMPLATE_RASTER_TRIANGLE_EXPANDING + +#endif // RLSW_TEMPLATE_RASTER_TRIANGLE_EXPANDING +//------------------------------------------------------------------------------------------- + +// Quad rasterizer variant dispatch +//------------------------------------------------------------------------------------------- +#ifndef RLSW_TEMPLATE_RASTER_QUAD_EXPANDING +#define RLSW_TEMPLATE_RASTER_QUAD_EXPANDING + + // State mask to apply before indexing the dispatch table + #define SW_RASTER_QUAD_STATE_MASK \ + (SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND) + + // Single source of truth for all rasterizer specializations + // X(NAME, STATE_FLAGS) + #define SW_RASTER_VARIANTS(X) \ + X(BASE, 0) \ + X(TEX, SW_STATE_TEXTURE_2D) \ + X(DEPTH, SW_STATE_DEPTH_TEST) \ + X(BLEND, SW_STATE_BLEND) \ + X(TEX_DEPTH, SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST) \ + X(TEX_BLEND, SW_STATE_TEXTURE_2D | SW_STATE_BLEND) \ + X(DEPTH_BLEND, SW_STATE_DEPTH_TEST | SW_STATE_BLEND) \ + X(TEX_DEPTH_BLEND, SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND) + + // Forward declarations because clangd does not follow #include __FILE__ to avoid infinite recursion + // These declarations make all variants visible to static analysis tools without affecting compilation + #define SW_FWD_DECL(NAME, _FLAGS) \ + static void sw_raster_quad_##NAME(const sw_vertex_t *v0, const sw_vertex_t *v1, const sw_vertex_t *v2, const sw_vertex_t *v3); + SW_RASTER_VARIANTS(SW_FWD_DECL) // NOLINT + #undef SW_FWD_DECL + + // Specialization generation via self-inclusion + + #define RLSW_TEMPLATE_RASTER_QUAD BASE + #include __FILE__ // IWYU pragma: keep + #undef RLSW_TEMPLATE_RASTER_QUAD + + #define RLSW_TEMPLATE_RASTER_QUAD TEX + #define SW_ENABLE_TEXTURE + #include __FILE__ + #undef SW_ENABLE_TEXTURE + #undef RLSW_TEMPLATE_RASTER_QUAD + + #define RLSW_TEMPLATE_RASTER_QUAD DEPTH + #define SW_ENABLE_DEPTH_TEST + #include __FILE__ + #undef SW_ENABLE_DEPTH_TEST + #undef RLSW_TEMPLATE_RASTER_QUAD + + #define RLSW_TEMPLATE_RASTER_QUAD BLEND + #define SW_ENABLE_BLEND + #include __FILE__ + #undef SW_ENABLE_BLEND + #undef RLSW_TEMPLATE_RASTER_QUAD + + #define RLSW_TEMPLATE_RASTER_QUAD TEX_DEPTH + #define SW_ENABLE_TEXTURE + #define SW_ENABLE_DEPTH_TEST + #include __FILE__ + #undef SW_ENABLE_DEPTH_TEST + #undef SW_ENABLE_TEXTURE + #undef RLSW_TEMPLATE_RASTER_QUAD + + #define RLSW_TEMPLATE_RASTER_QUAD TEX_BLEND + #define SW_ENABLE_TEXTURE + #define SW_ENABLE_BLEND + #include __FILE__ + #undef SW_ENABLE_BLEND + #undef SW_ENABLE_TEXTURE + #undef RLSW_TEMPLATE_RASTER_QUAD + + #define RLSW_TEMPLATE_RASTER_QUAD DEPTH_BLEND + #define SW_ENABLE_DEPTH_TEST + #define SW_ENABLE_BLEND + #include __FILE__ + #undef SW_ENABLE_BLEND + #undef SW_ENABLE_DEPTH_TEST + #undef RLSW_TEMPLATE_RASTER_QUAD + + #define RLSW_TEMPLATE_RASTER_QUAD TEX_DEPTH_BLEND + #define SW_ENABLE_TEXTURE + #define SW_ENABLE_DEPTH_TEST + #define SW_ENABLE_BLEND + #include __FILE__ + #undef SW_ENABLE_BLEND + #undef SW_ENABLE_DEPTH_TEST + #undef SW_ENABLE_TEXTURE + #undef RLSW_TEMPLATE_RASTER_QUAD + + // Dispatch table (auto-generated from SW_RASTER_VARIANTS) + #define SW_TABLE_ENTRY(NAME, FLAGS) [FLAGS] = sw_raster_quad_##NAME, + static const sw_raster_quad_f SW_RASTER_QUAD_FUNCS[] = { + SW_RASTER_VARIANTS(SW_TABLE_ENTRY) + }; + #undef SW_TABLE_ENTRY + +#undef SW_RASTER_VARIANTS +#undef RLSW_TEMPLATE_RASTER_QUAD_EXPANDING + +#endif // RLSW_TEMPLATE_RASTER_QUAD_EXPANDING +//------------------------------------------------------------------------------------------- + +// Line rasterizer variant dispatch +//------------------------------------------------------------------------------------------- +#ifndef RLSW_TEMPLATE_RASTER_LINE_EXPANDING +#define RLSW_TEMPLATE_RASTER_LINE_EXPANDING + + // State mask to apply before indexing the dispatch table + #define SW_RASTER_LINE_STATE_MASK \ + (SW_STATE_DEPTH_TEST | SW_STATE_BLEND) + + // Single source of truth for all rasterizer specializations + // X(NAME, STATE_FLAGS) + #define SW_RASTER_VARIANTS(X) \ + X(BASE, 0) \ + X(DEPTH, SW_STATE_DEPTH_TEST) \ + X(BLEND, SW_STATE_BLEND) \ + X(DEPTH_BLEND, SW_STATE_DEPTH_TEST | SW_STATE_BLEND) + + // Forward declarations because clangd does not follow #include __FILE__ to avoid infinite recursion + // These declarations make all variants visible to static analysis tools without affecting compilation + #define SW_FWD_DECL(NAME, _FLAGS) \ + static void sw_raster_line_##NAME(const sw_vertex_t *v0, const sw_vertex_t *v1); \ + static void sw_raster_line_thick_##NAME(const sw_vertex_t *v0, const sw_vertex_t *v1); + SW_RASTER_VARIANTS(SW_FWD_DECL) // NOLINT + #undef SW_FWD_DECL + + // Specialization generation via self-inclusion + + #define RLSW_TEMPLATE_RASTER_LINE BASE + #include __FILE__ // IWYU pragma: keep + #undef RLSW_TEMPLATE_RASTER_LINE + + #define RLSW_TEMPLATE_RASTER_LINE DEPTH + #define SW_ENABLE_DEPTH_TEST + #include __FILE__ + #undef SW_ENABLE_DEPTH_TEST + #undef RLSW_TEMPLATE_RASTER_LINE + + #define RLSW_TEMPLATE_RASTER_LINE BLEND + #define SW_ENABLE_BLEND + #include __FILE__ + #undef SW_ENABLE_BLEND + #undef RLSW_TEMPLATE_RASTER_LINE + + #define RLSW_TEMPLATE_RASTER_LINE DEPTH_BLEND + #define SW_ENABLE_DEPTH_TEST + #define SW_ENABLE_BLEND + #include __FILE__ + #undef SW_ENABLE_BLEND + #undef SW_ENABLE_DEPTH_TEST + #undef RLSW_TEMPLATE_RASTER_LINE + + // Dispatch table (auto-generated from SW_RASTER_VARIANTS) + #define SW_TABLE_ENTRY0(NAME, FLAGS) [FLAGS] = sw_raster_line_##NAME, + #define SW_TABLE_ENTRY1(NAME, FLAGS) [FLAGS] = sw_raster_line_thick_##NAME, + static const sw_raster_line_f SW_RASTER_LINE_FUNCS[] = { SW_RASTER_VARIANTS(SW_TABLE_ENTRY0) }; + static const sw_raster_line_f SW_RASTER_LINE_THICK_FUNCS[] = { SW_RASTER_VARIANTS(SW_TABLE_ENTRY1) }; + #undef SW_TABLE_ENTRY0 + #undef SW_TABLE_ENTRY1 + +#undef SW_RASTER_VARIANTS +#undef RLSW_TEMPLATE_RASTER_LINE_EXPANDING + +#endif // RLSW_TEMPLATE_RASTER_LINE_EXPANDING +//------------------------------------------------------------------------------------------- + +// Point rasterizer variant dispatch +//------------------------------------------------------------------------------------------- +#ifndef RLSW_TEMPLATE_RASTER_POINT_EXPANDING +#define RLSW_TEMPLATE_RASTER_POINT_EXPANDING + + // State mask to apply before indexing the dispatch table + #define SW_RASTER_POINT_STATE_MASK \ + (SW_STATE_DEPTH_TEST | SW_STATE_BLEND) + + // Single source of truth for all rasterizer specializations + // X(NAME, STATE_FLAGS) + #define SW_RASTER_VARIANTS(X) \ + X(BASE, 0) \ + X(DEPTH, SW_STATE_DEPTH_TEST) \ + X(BLEND, SW_STATE_BLEND) \ + X(DEPTH_BLEND, SW_STATE_DEPTH_TEST | SW_STATE_BLEND) + + // Forward declarations because clangd does not follow #include __FILE__ to avoid infinite recursion + // These declarations make all variants visible to static analysis tools without affecting compilation + #define SW_FWD_DECL(NAME, _FLAGS) \ + static void sw_raster_point_##NAME(const sw_vertex_t *v); + SW_RASTER_VARIANTS(SW_FWD_DECL) // NOLINT + #undef SW_FWD_DECL + + // Specialization generation via self-inclusion + + #define RLSW_TEMPLATE_RASTER_POINT BASE + #include __FILE__ // IWYU pragma: keep + #undef RLSW_TEMPLATE_RASTER_POINT + + #define RLSW_TEMPLATE_RASTER_POINT DEPTH + #define SW_ENABLE_DEPTH_TEST + #include __FILE__ + #undef SW_ENABLE_DEPTH_TEST + #undef RLSW_TEMPLATE_RASTER_POINT + + #define RLSW_TEMPLATE_RASTER_POINT BLEND + #define SW_ENABLE_BLEND + #include __FILE__ + #undef SW_ENABLE_BLEND + #undef RLSW_TEMPLATE_RASTER_POINT + + #define RLSW_TEMPLATE_RASTER_POINT DEPTH_BLEND + #define SW_ENABLE_DEPTH_TEST + #define SW_ENABLE_BLEND + #include __FILE__ + #undef SW_ENABLE_BLEND + #undef SW_ENABLE_DEPTH_TEST + #undef RLSW_TEMPLATE_RASTER_POINT + + // Dispatch table (auto-generated from SW_RASTER_VARIANTS) + #define SW_TABLE_ENTRY(NAME, FLAGS) [FLAGS] = sw_raster_point_##NAME, + static const sw_raster_point_f SW_RASTER_POINT_FUNCS[] = { + SW_RASTER_VARIANTS(SW_TABLE_ENTRY) + }; + #undef SW_TABLE_ENTRY + +#undef SW_RASTER_VARIANTS +#undef RLSW_TEMPLATE_RASTER_POINT_EXPANDING + +#endif // RLSW_TEMPLATE_RASTER_POINT_EXPANDING +//------------------------------------------------------------------------------------------- + +// Triangle rendering logic +//------------------------------------------------------------------------------------------- +static inline bool sw_triangle_face_culling(void) +{ + // NOTE: Face culling is done before clipping to avoid unnecessary computations + // To handle triangles crossing the w=0 plane correctly, + // the winding order test is performeed in homogeneous coordinates directly, + // before the perspective division (division by w) + // This test determines the orientation of the triangle in the (x,y,w) plane, + // which corresponds to the projected 2D winding order sign, + // even with negative w values + + // Preload homogeneous coordinates into local variables + const float *h0 = RLSW.vertexBuffer[0].homogeneous; + const float *h1 = RLSW.vertexBuffer[1].homogeneous; + const float *h2 = RLSW.vertexBuffer[2].homogeneous; + + // Compute a value proportional to the signed area in the projected 2D plane, + // calculated directly using homogeneous coordinates BEFORE division by w + // This is the determinant of the matrix formed by the (x, y, w) components + // of the vertices, which correctly captures the winding order in homogeneous + // space and its relationship to the projected 2D winding order, even with + // negative w values + // The determinant formula used here is: + // h0.x*(h1.y*h2.w - h2.y*h1.w) + + // h1.x*(h2.y*h0.w - h0.y*h2.w) + + // h2.x*(h0.y*h1.w - h1.y*h0.w) + + const float hSgnArea = + h0[0]*(h1[1]*h2[3] - h2[1]*h1[3]) + + h1[0]*(h2[1]*h0[3] - h0[1]*h2[3]) + + h2[0]*(h0[1]*h1[3] - h1[1]*h0[3]); + + // Discard the triangle if its winding order (determined by the sign + // of the homogeneous area/determinant) matches the culled direction + // A positive hSgnArea typically corresponds to a counter-clockwise + // winding in the projected space when all w > 0 + // This test is robust for points with w > 0 or w < 0, correctly + // capturing the change in orientation when crossing the w=0 plane + + // The culling logic remains the same based on the signed area/determinant + // A value of 0 for hSgnArea means the points are collinear in (x, y, w) + // space, which corresponds to a degenerate triangle projection + // Such triangles are typically not culled by this test (0 < 0 is false, 0 > 0 is false) + // and should be handled by the clipper if necessary + return (RLSW.cullFace == SW_FRONT)? (hSgnArea < 0) : (hSgnArea > 0); // Cull if winding is "clockwise" : "counter-clockwise" +} + +static void sw_triangle_clip_and_project(void) +{ + sw_vertex_t *polygon = RLSW.vertexBuffer; + int *vertexCounter = &RLSW.vertexCounter; + + if (sw_polygon_clip(polygon, vertexCounter)) + { + // Transformation to screen space and normalization + for (int i = 0; i < *vertexCounter; i++) + { + sw_vertex_t *v = &polygon[i]; + + // Calculation of the reciprocal of W for normalization + // as well as perspective-correct attributes + const float wRcp = 1.0f/v->homogeneous[3]; + v->homogeneous[3] = wRcp; + + // Division of XYZ coordinates by weight + v->homogeneous[0] *= wRcp; + v->homogeneous[1] *= wRcp; + v->homogeneous[2] *= wRcp; + + // Division of texture coordinates (perspective-correct) + v->texcoord[0] *= wRcp; + v->texcoord[1] *= wRcp; + + // Division of colors (perspective-correct) + v->color[0] *= wRcp; + v->color[1] *= wRcp; + v->color[2] *= wRcp; + v->color[3] *= wRcp; + + // Transformation to screen space + sw_project_ndc_to_screen(v->screen, v->homogeneous); + } + } +} + +static void sw_triangle_render(uint32_t state) +{ + if (RLSW.stateFlags & SW_STATE_CULL_FACE) + { + if (!sw_triangle_face_culling()) return; + } + + sw_triangle_clip_and_project(); + if (RLSW.vertexCounter < 3) return; + + state &= SW_RASTER_TRIANGLE_STATE_MASK; + + for (int i = 0; i < RLSW.vertexCounter - 2; i++) + { + SW_RASTER_TRIANGLE_FUNCS[state]( + &RLSW.vertexBuffer[0], + &RLSW.vertexBuffer[i + 1], + &RLSW.vertexBuffer[i + 2] + ); + } +} +//------------------------------------------------------------------------------------------- + +// Quad rendering logic +//------------------------------------------------------------------------------------------- +static inline bool sw_quad_face_culling(void) +{ + // NOTE: Face culling is done before clipping to avoid unnecessary computations + // To handle quads crossing the w=0 plane correctly, + // the winding order test is performed in homogeneous coordinates directly, + // before the perspective division (division by w) + // For a convex quad with vertices P0, P1, P2, P3 in sequential order, + // the winding order of the quad is the same as the winding order + // of the triangle P0 P1 P2. The homogeneous triangle is used on + // winding test on this first triangle + + // Preload homogeneous coordinates into local variables + const float *h0 = RLSW.vertexBuffer[0].homogeneous; + const float *h1 = RLSW.vertexBuffer[1].homogeneous; + const float *h2 = RLSW.vertexBuffer[2].homogeneous; + + // NOTE: h3 is not needed for this test + // const float *h3 = RLSW.vertexBuffer[3].homogeneous; + + // Compute a value proportional to the signed area of the triangle P0 P1 P2 + // in the projected 2D plane, calculated directly using homogeneous coordinates + // BEFORE division by w + // This is the determinant of the matrix formed by the (x, y, w) components + // of the vertices P0, P1, and P2. Its sign correctly indicates the winding order + // in homogeneous space and its relationship to the projected 2D winding order, + // even with negative w values + // The determinant formula used here is: + // h0.x*(h1.y*h2.w - h2.y*h1.w) + + // h1.x*(h2.y*h0.w - h0.y*h2.w) + + // h2.x*(h0.y*h1.w - h1.y*h0.w) + + const float hSgnArea = + h0[0]*(h1[1]*h2[3] - h2[1]*h1[3]) + + h1[0]*(h2[1]*h0[3] - h0[1]*h2[3]) + + h2[0]*(h0[1]*h1[3] - h1[1]*h0[3]); + + // Perform face culling based on the winding order determined by the sign + // of the homogeneous area/determinant of triangle P0 P1 P2 + // This test is robust for points with w > 0 or w < 0 within the triangle, + // correctly capturing the change in orientation when crossing the w=0 plane + + // A positive hSgnArea typically corresponds to a counter-clockwise + // winding in the projected space when all w > 0 + // A value of 0 for hSgnArea means P0, P1, P2 are collinear in (x, y, w) + // space, which corresponds to a degenerate triangle projection + // Such quads might also be degenerate or non-planar. They are typically + // not culled by this test (0 < 0 is false, 0 > 0 is false) + // and should be handled by the clipper if necessary + + return (RLSW.cullFace == SW_FRONT)? (hSgnArea < 0.0f) : (hSgnArea > 0.0f); // Cull if winding is "clockwise" : "counter-clockwise" +} + +static void sw_quad_clip_and_project(void) +{ + sw_vertex_t *polygon = RLSW.vertexBuffer; + int *vertexCounter = &RLSW.vertexCounter; + + if (sw_polygon_clip(polygon, vertexCounter)) + { + // Transformation to screen space and normalization + for (int i = 0; i < *vertexCounter; i++) + { + sw_vertex_t *v = &polygon[i]; + + // Calculation of the reciprocal of W for normalization + // as well as perspective-correct attributes + const float wRcp = 1.0f/v->homogeneous[3]; + v->homogeneous[3] = wRcp; + + // Division of XYZ coordinates by weight + v->homogeneous[0] *= wRcp; + v->homogeneous[1] *= wRcp; + v->homogeneous[2] *= wRcp; + + // Division of texture coordinates (perspective-correct) + v->texcoord[0] *= wRcp; + v->texcoord[1] *= wRcp; + + // Division of colors (perspective-correct) + v->color[0] *= wRcp; + v->color[1] *= wRcp; + v->color[2] *= wRcp; + v->color[3] *= wRcp; + + // Transformation to screen space + sw_project_ndc_to_screen(v->screen, v->homogeneous); + } + } +} + +static bool sw_quad_is_axis_aligned(void) +{ + // Reject quads with perspective projection + // The fast path assumes affine (non-perspective) quads, + // so it's required for all vertices to have homogeneous w = 1.0 + for (int i = 0; i < 4; i++) + { + if (RLSW.vertexBuffer[i].homogeneous[3] != 1.0f) return false; + } + + // Epsilon tolerance in screen space (pixels) + const float epsilon = 0.5f; + + // Fetch screen-space positions for the four quad vertices + const float *p0 = RLSW.vertexBuffer[0].screen; + const float *p1 = RLSW.vertexBuffer[1].screen; + const float *p2 = RLSW.vertexBuffer[2].screen; + const float *p3 = RLSW.vertexBuffer[3].screen; + + // Compute edge vectors between consecutive vertices + // These define the four sides of the quad in screen space + float dx01 = p1[0] - p0[0], dy01 = p1[1] - p0[1]; + float dx12 = p2[0] - p1[0], dy12 = p2[1] - p1[1]; + float dx23 = p3[0] - p2[0], dy23 = p3[1] - p2[1]; + float dx30 = p0[0] - p3[0], dy30 = p0[1] - p3[1]; + + // Each edge must be either horizontal or vertical within epsilon tolerance + // If any edge deviates significantly from either axis, the quad is not axis-aligned + if (!((fabsf(dy01) < epsilon) || (fabsf(dx01) < epsilon))) return false; + if (!((fabsf(dy12) < epsilon) || (fabsf(dx12) < epsilon))) return false; + if (!((fabsf(dy23) < epsilon) || (fabsf(dx23) < epsilon))) return false; + if (!((fabsf(dy30) < epsilon) || (fabsf(dx30) < epsilon))) return false; + + return true; +} + +static void sw_quad_render(uint32_t state) +{ + if (RLSW.stateFlags & SW_STATE_CULL_FACE) + { + if (!sw_quad_face_culling()) return; + } + + sw_quad_clip_and_project(); + if (RLSW.vertexCounter < 3) return; + + state &= SW_RASTER_QUAD_STATE_MASK; + + if ((RLSW.vertexCounter == 4) && sw_quad_is_axis_aligned()) + { + SW_RASTER_QUAD_FUNCS[state]( + &RLSW.vertexBuffer[0], + &RLSW.vertexBuffer[1], + &RLSW.vertexBuffer[2], + &RLSW.vertexBuffer[3] + ); + } + else + { + for (int i = 0; i < RLSW.vertexCounter - 2; i++) + { + SW_RASTER_TRIANGLE_FUNCS[state]( + &RLSW.vertexBuffer[0], + &RLSW.vertexBuffer[i + 1], + &RLSW.vertexBuffer[i + 2] + ); + } + } +} +//------------------------------------------------------------------------------------------- + +// Line rendering logic +//------------------------------------------------------------------------------------------- +static inline bool sw_line_clip_coord(float q, float p, float *t0, float *t1) +{ + if (fabsf(p) < SW_CLIP_EPSILON) + { + // Check if the line is entirely outside the window + if (q < -SW_CLIP_EPSILON) return 0; // Completely outside + return 1; // Completely inside or on the edges + } + + const float r = q/p; + + if (p < 0) + { + if (r > *t1) return 0; + if (r > *t0) *t0 = r; + } + else + { + if (r < *t0) return 0; + if (r < *t1) *t1 = r; + } + + return 1; +} + +static bool sw_line_clip(sw_vertex_t *v0, sw_vertex_t *v1) +{ + float t0 = 0.0f, t1 = 1.0f; + float dH[4], dC[4]; + + for (int i = 0; i < 4; i++) + { + dH[i] = v1->homogeneous[i] - v0->homogeneous[i]; + dC[i] = v1->color[i] - v0->color[i]; + } + + // Clipping Liang-Barsky + if (!sw_line_clip_coord(v0->homogeneous[3] - v0->homogeneous[0], -dH[3] + dH[0], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->homogeneous[3] + v0->homogeneous[0], -dH[3] - dH[0], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->homogeneous[3] - v0->homogeneous[1], -dH[3] + dH[1], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->homogeneous[3] + v0->homogeneous[1], -dH[3] - dH[1], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->homogeneous[3] - v0->homogeneous[2], -dH[3] + dH[2], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->homogeneous[3] + v0->homogeneous[2], -dH[3] - dH[2], &t0, &t1)) return false; + + // Clipping Scissor + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { + if (!sw_line_clip_coord(v0->homogeneous[0] - RLSW.scClipMin[0]*v0->homogeneous[3], RLSW.scClipMin[0]*dH[3] - dH[0], &t0, &t1)) return false; + if (!sw_line_clip_coord(RLSW.scClipMax[0]*v0->homogeneous[3] - v0->homogeneous[0], dH[0] - RLSW.scClipMax[0]*dH[3], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->homogeneous[1] - RLSW.scClipMin[1]*v0->homogeneous[3], RLSW.scClipMin[1]*dH[3] - dH[1], &t0, &t1)) return false; + if (!sw_line_clip_coord(RLSW.scClipMax[1]*v0->homogeneous[3] - v0->homogeneous[1], dH[1] - RLSW.scClipMax[1]*dH[3], &t0, &t1)) return false; + } + + // Interpolation of new coordinates + if (t1 < 1.0f) + { + for (int i = 0; i < 4; i++) + { + v1->homogeneous[i] = v0->homogeneous[i] + t1*dH[i]; + v1->color[i] = v0->color[i] + t1*dC[i]; + } + } + + if (t0 > 0.0f) + { + for (int i = 0; i < 4; i++) + { + v0->homogeneous[i] += t0*dH[i]; + v0->color[i] += t0*dC[i]; + } + } + + return true; +} + +static bool sw_line_clip_and_project(sw_vertex_t *v0, sw_vertex_t *v1) +{ + if (!sw_line_clip(v0, v1)) return false; + + // Convert homogeneous coordinates to NDC + v0->homogeneous[3] = 1.0f/v0->homogeneous[3]; + v1->homogeneous[3] = 1.0f/v1->homogeneous[3]; + for (int i = 0; i < 3; i++) + { + v0->homogeneous[i] *= v0->homogeneous[3]; + v1->homogeneous[i] *= v1->homogeneous[3]; + } + + // Convert NDC coordinates to screen space + sw_project_ndc_to_screen(v0->screen, v0->homogeneous); + sw_project_ndc_to_screen(v1->screen, v1->homogeneous); + + // NDC +1.0 projects to exactly (width + 0.5f), which truncates out of bounds + // The clamp is at most 0.5px on a boundary endpoint, it's visually imperceptible + v0->screen[0] = sw_clamp(v0->screen[0], 0.0f, (float)(RLSW.colorBuffer->width - 1) + 0.5f); + v0->screen[1] = sw_clamp(v0->screen[1], 0.0f, (float)(RLSW.colorBuffer->height - 1) + 0.5f); + v1->screen[0] = sw_clamp(v1->screen[0], 0.0f, (float)(RLSW.colorBuffer->width - 1) + 0.5f); + v1->screen[1] = sw_clamp(v1->screen[1], 0.0f, (float)(RLSW.colorBuffer->height - 1) + 0.5f); + + return true; +} + +static void sw_line_render(uint32_t state, sw_vertex_t *vertices) +{ + if (!sw_line_clip_and_project(&vertices[0], &vertices[1])) return; + + state &= SW_RASTER_LINE_STATE_MASK; + + if (RLSW.lineWidth >= 2.0f) + { + SW_RASTER_LINE_THICK_FUNCS[state](&vertices[0], &vertices[1]); + } + else + { + SW_RASTER_LINE_FUNCS[state](&vertices[0], &vertices[1]); + } +} +//------------------------------------------------------------------------------------------- + +// Point rendering logic +//------------------------------------------------------------------------------------------- +static bool sw_point_clip_and_project(sw_vertex_t *v) +{ + if (v->homogeneous[3] != 1.0f) + { + for (int_fast8_t i = 0; i < 3; i++) + { + if ((v->homogeneous[i] < -v->homogeneous[3]) || (v->homogeneous[i] > v->homogeneous[3])) return false; + } + + v->homogeneous[3] = 1.0f/v->homogeneous[3]; + v->homogeneous[0] *= v->homogeneous[3]; + v->homogeneous[1] *= v->homogeneous[3]; + v->homogeneous[2] *= v->homogeneous[3]; + } + + sw_project_ndc_to_screen(v->screen, v->homogeneous); + + int min[2] = { 0, 0 }; + int max[2] = { RLSW.colorBuffer->width, RLSW.colorBuffer->height }; + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { + min[0] = sw_clamp_int(RLSW.scMin[0], 0, RLSW.colorBuffer->width); + min[1] = sw_clamp_int(RLSW.scMin[1], 0, RLSW.colorBuffer->height); + max[0] = sw_clamp_int(RLSW.scMax[0], 0, RLSW.colorBuffer->width); + max[1] = sw_clamp_int(RLSW.scMax[1], 0, RLSW.colorBuffer->height); + } + + bool insideX = (v->screen[0] - RLSW.pointRadius < max[0]) && (v->screen[0] + RLSW.pointRadius > min[0]); + bool insideY = (v->screen[1] - RLSW.pointRadius < max[1]) && (v->screen[1] + RLSW.pointRadius > min[1]); + + return (insideX && insideY); +} + +static void sw_point_render(uint32_t state, sw_vertex_t *v) +{ + if (!sw_point_clip_and_project(v)) return; + state &= SW_RASTER_POINT_STATE_MASK; + SW_RASTER_POINT_FUNCS[state](v); +} +//------------------------------------------------------------------------------------------- + +// Polygon modes rendering logic +//------------------------------------------------------------------------------------------- +static inline void sw_poly_point_render(uint32_t state) +{ + for (int i = 0; i < RLSW.vertexCounter; i++) sw_point_render(state, &RLSW.vertexBuffer[i]); +} + +static inline void sw_poly_line_render(uint32_t state) +{ + const sw_vertex_t *vertices = RLSW.vertexBuffer; + int cm1 = RLSW.vertexCounter - 1; + + for (int i = 0; i < cm1; i++) + { + sw_vertex_t verts[2] = { vertices[i], vertices[i + 1] }; + sw_line_render(state, verts); + } + + sw_vertex_t verts[2] = { vertices[cm1], vertices[0] }; + sw_line_render(state, verts); +} + +static inline void sw_poly_fill_render(uint32_t state) +{ + switch (RLSW.drawMode) + { + case SW_POINTS: sw_point_render(state, &RLSW.vertexBuffer[0]); break; + case SW_LINES: sw_line_render(state, RLSW.vertexBuffer); break; + case SW_TRIANGLES: sw_triangle_render(state); break; + case SW_QUADS: sw_quad_render(state); break; + default: break; + } +} +//------------------------------------------------------------------------------------------- + +// Immediate rendering logic +//------------------------------------------------------------------------------------------- +static void sw_immediate_push_vertex(const float position[4], const float color[4], const float texcoord[2]) +{ + // Check if we are in a valid draw mode + if (!sw_is_draw_mode_valid(RLSW.drawMode)) + { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + + // Copy the attributes in the current vertex + sw_vertex_t *vertex = &RLSW.vertexBuffer[RLSW.vertexCounter++]; + for (int i = 0; i < 4; i++) + { + vertex->position[i] = position[i]; + if (i < 2) vertex->texcoord[i] = texcoord[i]; + vertex->color[i] = color[i]; + } + + // Calculate homogeneous coordinates + const float *m = RLSW.matMVP, *v = vertex->position; + vertex->homogeneous[0] = m[0]*v[0] + m[4]*v[1] + m[8]*v[2] + m[12]*v[3]; + vertex->homogeneous[1] = m[1]*v[0] + m[5]*v[1] + m[9]*v[2] + m[13]*v[3]; + vertex->homogeneous[2] = m[2]*v[0] + m[6]*v[1] + m[10]*v[2] + m[14]*v[3]; + vertex->homogeneous[3] = m[3]*v[0] + m[7]*v[1] + m[11]*v[2] + m[15]*v[3]; + + // Immediate rendering of the primitive if the required number is reached + if (RLSW.vertexCounter == SW_PRIMITIVE_VERTEX_COUNT[RLSW.drawMode]) + { + // Restricts the pipeline state to the minimum required + uint32_t state = RLSW.stateFlags; + if (!sw_is_texture_complete(RLSW.depthBuffer)) state &= ~SW_STATE_DEPTH_TEST; + if (!sw_is_texture_complete(RLSW.boundTexture)) state &= ~SW_STATE_TEXTURE_2D; + if ((RLSW.srcFactor == SW_ONE) && (RLSW.dstFactor == SW_ZERO)) state &= ~SW_STATE_BLEND; + + switch (RLSW.polyMode) + { + case SW_FILL: sw_poly_fill_render(state); break; + case SW_LINE: sw_poly_line_render(state); break; + case SW_POINT: sw_poly_point_render(state); break; + default: break; + } + + RLSW.vertexCounter = 0; + } +} + //------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------- @@ -3546,20 +3917,35 @@ static inline bool sw_is_blend_dst_factor_valid(int blend) //---------------------------------------------------------------------------------- bool swInit(int w, int h) { - if (!sw_framebuffer_load(w, h)) { swClose(); return false; } + if (!sw_default_framebuffer_alloc(&RLSW.framebuffer, w, h)) + { + swClose(); + return false; + } + + if (!sw_pool_init(&RLSW.framebufferPool, SW_MAX_FRAMEBUFFERS, sizeof(sw_framebuffer_t))) + { + swClose(); + return false; + } + + if (!sw_pool_init(&RLSW.texturePool, SW_MAX_TEXTURES, sizeof(sw_texture_t))) + { + swClose(); + return false; + } + + RLSW.colorBuffer = &RLSW.framebuffer.color; + RLSW.depthBuffer = &RLSW.framebuffer.depth; swViewport(0, 0, w, h); swScissor(0, 0, w, h); - RLSW.loadedTextures = (sw_texture_t *)SW_MALLOC(SW_MAX_TEXTURES*sizeof(sw_texture_t)); - if (RLSW.loadedTextures == NULL) { swClose(); return false; } - - RLSW.freeTextureIds = (uint32_t *)SW_MALLOC(SW_MAX_TEXTURES*sizeof(uint32_t)); - if (RLSW.loadedTextures == NULL) { swClose(); return false; } - - const float clearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; - sw_framebuffer_write_color(&RLSW.clearValue, clearColor); - sw_framebuffer_write_depth(&RLSW.clearValue, 1.0f); + RLSW.clearColor[0] = 0.0f; + RLSW.clearColor[1] = 0.0f; + RLSW.clearColor[2] = 0.0f; + RLSW.clearColor[3] = 1.0f; + RLSW.clearDepth = 1.0f; RLSW.currentMatrixMode = SW_MODELVIEW; RLSW.currentMatrix = &RLSW.stackModelview[0]; @@ -3588,30 +3974,10 @@ bool swInit(int w, int h) RLSW.srcFactorFunc = sw_factor_src_alpha; RLSW.dstFactorFunc = sw_factor_one_minus_src_alpha; + RLSW.drawMode = SW_DRAW_INVALID; RLSW.polyMode = SW_FILL; RLSW.cullFace = SW_BACK; - static uint32_t defaultTex[3*2*2] = { - 0xFFFFFFFF, - 0xFFFFFFFF, - 0xFFFFFFFF, - 0xFFFFFFFF - }; - - RLSW.loadedTextures[0].pixels = (uint8_t*)defaultTex; - RLSW.loadedTextures[0].width = 2; - RLSW.loadedTextures[0].height = 2; - RLSW.loadedTextures[0].wMinus1 = 1; - RLSW.loadedTextures[0].hMinus1 = 1; - RLSW.loadedTextures[0].minFilter = SW_NEAREST; - RLSW.loadedTextures[0].magFilter = SW_NEAREST; - RLSW.loadedTextures[0].sWrap = SW_REPEAT; - RLSW.loadedTextures[0].tWrap = SW_REPEAT; - RLSW.loadedTextures[0].tx = 0.5f; - RLSW.loadedTextures[0].ty = 0.5f; - - RLSW.loadedTextureCount = 1; - SW_LOG("INFO: RLSW: Software renderer initialized successfully\n"); #if defined(SW_HAS_FMA_AVX) && defined(SW_HAS_FMA_AVX2) SW_LOG("INFO: RLSW: Using SIMD instructions: FMA AVX\n"); @@ -3634,118 +4000,81 @@ bool swInit(int w, int h) void swClose(void) { - // NOTE: Starts at texture 1, texture 0 does not have to be freed - for (int i = 1; i < RLSW.loadedTextureCount; i++) + for (int i = 1; i < RLSW.texturePool.watermark; i++) { - if (sw_is_texture_valid(i)) + if (RLSW.texturePool.gen[i] & SW_POOL_SLOT_LIVE) { - SW_FREE(RLSW.loadedTextures[i].pixels); + sw_texture_free((sw_texture_t *)RLSW.texturePool.data + i); } } - SW_FREE(RLSW.framebuffer.pixels); - SW_FREE(RLSW.loadedTextures); - SW_FREE(RLSW.freeTextureIds); + sw_pool_destroy(&RLSW.texturePool); + sw_pool_destroy(&RLSW.framebufferPool); + sw_default_framebuffer_free(&RLSW.framebuffer); RLSW = SW_CURLY_INIT(sw_context_t) { 0 }; } -bool swResizeFramebuffer(int w, int h) +bool swResize(int w, int h) { - return sw_framebuffer_resize(w, h); + return sw_default_framebuffer_alloc(&RLSW.framebuffer, w, h); } -void swCopyFramebuffer(int x, int y, int w, int h, SWformat format, SWtype type, void *pixels) +void swReadPixels(int x, int y, int w, int h, SWformat format, SWtype type, void *pixels) { - sw_pixelformat_t pFormat = (sw_pixelformat_t)sw_get_pixel_format(format, type); + // REVIEW: Handle depth buffer copy here or consider it as an error ? + if (format == SW_DEPTH_COMPONENT) { RLSW.errCode = SW_INVALID_ENUM; return; } + + sw_pixelformat_t pFormat = (sw_pixelformat_t)sw_pixel_get_format(format, type); + if (pFormat <= SW_PIXELFORMAT_UNKNOWN) { RLSW.errCode = SW_INVALID_ENUM; return; } if (w <= 0) { RLSW.errCode = SW_INVALID_VALUE; return; } if (h <= 0) { RLSW.errCode = SW_INVALID_VALUE; return; } - if (w > RLSW.framebuffer.width) w = RLSW.framebuffer.width; - if (h > RLSW.framebuffer.height) h = RLSW.framebuffer.height; + if (w > RLSW.colorBuffer->width) w = RLSW.colorBuffer->width; + if (h > RLSW.colorBuffer->height) h = RLSW.colorBuffer->height; - x = sw_clampi(x, 0, w); - y = sw_clampi(y, 0, h); + x = sw_clamp_int(x, 0, w); + y = sw_clamp_int(y, 0, h); if ((x >= w) || (y >= h)) return; - if ((x == 0) && (y == 0) && (w == RLSW.framebuffer.width) && (h == RLSW.framebuffer.height)) + if ((pFormat == SW_FRAMEBUFFER_COLOR_FORMAT) && (x == 0) && (y == 0) && (w == RLSW.colorBuffer->width) && (h == RLSW.colorBuffer->height)) { - #if SW_COLOR_BUFFER_BITS == 32 - if (pFormat == SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) - { - sw_framebuffer_copy_fast(pixels); - return; - } - #elif SW_COLOR_BUFFER_BITS == 16 - if (pFormat == SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5) - { - sw_framebuffer_copy_fast(pixels); - return; - } - #endif + sw_framebuffer_output_fast(pixels, RLSW.colorBuffer); } - - switch (pFormat) + else { - case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: sw_framebuffer_copy_to_GRAYALPHA(x, y, w, h, (uint8_t *)pixels); break; - case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: sw_framebuffer_copy_to_GRAYALPHA(x, y, w, h, (uint8_t *)pixels); break; - case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: sw_framebuffer_copy_to_R5G6B5(x, y, w, h, (uint16_t *)pixels); break; - case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: sw_framebuffer_copy_to_R8G8B8(x, y, w, h, (uint8_t *)pixels); break; - case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: sw_framebuffer_copy_to_R5G5B5A1(x, y, w, h, (uint16_t *)pixels); break; - case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: sw_framebuffer_copy_to_R4G4B4A4(x, y, w, h, (uint16_t *)pixels); break; - //case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: sw_framebuffer_copy_to_R8G8B8A8(x, y, w, h, (uint8_t *)pixels); break; - // Below: not implemented - case SW_PIXELFORMAT_UNCOMPRESSED_R32: - case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: - case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: - case SW_PIXELFORMAT_UNCOMPRESSED_R16: - case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: - case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: - default: RLSW.errCode = SW_INVALID_ENUM; break; + sw_framebuffer_output_copy(pixels, RLSW.colorBuffer, x, y, w, h, pFormat); } } -void swBlitFramebuffer(int xDst, int yDst, int wDst, int hDst, int xSrc, int ySrc, int wSrc, int hSrc, SWformat format, SWtype type, void *pixels) +void swBlitPixels(int xDst, int yDst, int wDst, int hDst, int xSrc, int ySrc, int wSrc, int hSrc, SWformat format, SWtype type, void *pixels) { - sw_pixelformat_t pFormat = (sw_pixelformat_t)sw_get_pixel_format(format, type); + // REVIEW: Handle depth buffer copy here or consider it as an error ? + if (format == SW_DEPTH_COMPONENT) { RLSW.errCode = SW_INVALID_ENUM; return; } + + sw_pixelformat_t pFormat = (sw_pixelformat_t)sw_pixel_get_format(format, type); + if (pFormat <= SW_PIXELFORMAT_UNKNOWN) { RLSW.errCode = SW_INVALID_ENUM; return; } if (wSrc <= 0) { RLSW.errCode = SW_INVALID_VALUE; return; } if (hSrc <= 0) { RLSW.errCode = SW_INVALID_VALUE; return; } - if (wSrc > RLSW.framebuffer.width) wSrc = RLSW.framebuffer.width; - if (hSrc > RLSW.framebuffer.height) hSrc = RLSW.framebuffer.height; + if (wSrc > RLSW.colorBuffer->width) wSrc = RLSW.colorBuffer->width; + if (hSrc > RLSW.colorBuffer->height) hSrc = RLSW.colorBuffer->height; - xSrc = sw_clampi(xSrc, 0, wSrc); - ySrc = sw_clampi(ySrc, 0, hSrc); + xSrc = sw_clamp_int(xSrc, 0, wSrc); + ySrc = sw_clamp_int(ySrc, 0, hSrc); // Check if the sizes are identical after clamping the source to avoid unexpected issues // TODO: REVIEW: This repeats the operations if true, so a copy function can be made without these checks - if (xDst == xSrc && yDst == ySrc && wDst == wSrc && hDst == hSrc) + if ((xDst == xSrc) && (yDst == ySrc) && (wDst == wSrc) && (hDst == hSrc)) { - swCopyFramebuffer(xSrc, ySrc, wSrc, hSrc, format, type, pixels); + swReadPixels(xSrc, ySrc, wSrc, hSrc, format, type, pixels); } - - switch (pFormat) + else { - case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: sw_framebuffer_blit_to_GRAYALPHA(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, (uint8_t *)pixels); break; - case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: sw_framebuffer_blit_to_GRAYALPHA(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, (uint8_t *)pixels); break; - case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: sw_framebuffer_blit_to_R5G6B5(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, (uint16_t *)pixels); break; - case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: sw_framebuffer_blit_to_R8G8B8(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, (uint8_t *)pixels); break; - case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: sw_framebuffer_blit_to_R5G5B5A1(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, (uint16_t *)pixels); break; - case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: sw_framebuffer_blit_to_R4G4B4A4(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, (uint16_t *)pixels); break; - case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: sw_framebuffer_blit_to_R8G8B8A8(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, (uint8_t *)pixels); break; - // Below: not implemented - case SW_PIXELFORMAT_UNCOMPRESSED_R32: - case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: - case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: - case SW_PIXELFORMAT_UNCOMPRESSED_R16: - case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: - case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: - default: - RLSW.errCode = SW_INVALID_ENUM; - break; + sw_framebuffer_output_blit(pixels, RLSW.colorBuffer, xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pFormat); } } @@ -3782,6 +4111,7 @@ void swGetIntegerv(SWget name, int *v) case SW_MODELVIEW_STACK_DEPTH: *v = SW_MODELVIEW_STACK_DEPTH; break; case SW_PROJECTION_STACK_DEPTH: *v = SW_PROJECTION_STACK_DEPTH; break; case SW_TEXTURE_STACK_DEPTH: *v = SW_TEXTURE_STACK_DEPTH; break; + case SW_DRAW_FRAMEBUFFER_BINDING: *v = RLSW.boundFramebufferId; break; default: RLSW.errCode = SW_INVALID_ENUM; break; } } @@ -3792,11 +4122,14 @@ void swGetFloatv(SWget name, float *v) { case SW_COLOR_CLEAR_VALUE: { - sw_framebuffer_read_color(v, &RLSW.clearValue); + v[0] = RLSW.clearColor[0]; + v[1] = RLSW.clearColor[1]; + v[2] = RLSW.clearColor[2]; + v[3] = RLSW.clearColor[3]; } break; case SW_DEPTH_CLEAR_VALUE: { - v[0] = sw_framebuffer_read_depth(&RLSW.clearValue); + v[0] = RLSW.clearDepth; } break; case SW_CURRENT_COLOR: { @@ -3876,11 +4209,6 @@ void swViewport(int x, int y, int width, int height) RLSW.vpCenter[0] = (float)x + RLSW.vpHalf[0]; RLSW.vpCenter[1] = (float)y + RLSW.vpHalf[1]; - - RLSW.vpMin[0] = sw_clampi(x, 0, RLSW.framebuffer.width - 1); - RLSW.vpMin[1] = sw_clampi(y, 0, RLSW.framebuffer.height - 1); - RLSW.vpMax[0] = sw_clampi(x + width, 0, RLSW.framebuffer.width - 1); - RLSW.vpMax[1] = sw_clampi(y + height, 0, RLSW.framebuffer.height - 1); } void swScissor(int x, int y, int width, int height) @@ -3891,10 +4219,10 @@ void swScissor(int x, int y, int width, int height) return; } - RLSW.scMin[0] = sw_clampi(x, 0, RLSW.framebuffer.width - 1); - RLSW.scMin[1] = sw_clampi(y, 0, RLSW.framebuffer.height - 1); - RLSW.scMax[0] = sw_clampi(x + width, 0, RLSW.framebuffer.width - 1); - RLSW.scMax[1] = sw_clampi(y + height, 0, RLSW.framebuffer.height - 1); + RLSW.scMin[0] = x; + RLSW.scMin[1] = y; + RLSW.scMax[0] = x + width; + RLSW.scMax[1] = y + height; RLSW.scClipMin[0] = (2.0f*(float)RLSW.scMin[0]/(float)RLSW.vpSize[0]) - 1.0f; RLSW.scClipMax[0] = (2.0f*(float)RLSW.scMax[0]/(float)RLSW.vpSize[0]) - 1.0f; @@ -3904,30 +4232,31 @@ void swScissor(int x, int y, int width, int height) void swClearColor(float r, float g, float b, float a) { - float v[4] = { r, g, b, a }; - sw_framebuffer_write_color(&RLSW.clearValue, v); + RLSW.clearColor[0] = r; + RLSW.clearColor[1] = g; + RLSW.clearColor[2] = b; + RLSW.clearColor[3] = a; } void swClearDepth(float depth) { - sw_framebuffer_write_depth(&RLSW.clearValue, depth); + RLSW.clearDepth = depth; } void swClear(uint32_t bitmask) { - int size = RLSW.framebuffer.width*RLSW.framebuffer.height; + if (!sw_is_ready_to_render()) return; - if ((bitmask & (SW_COLOR_BUFFER_BIT | SW_DEPTH_BUFFER_BIT)) == (SW_COLOR_BUFFER_BIT | SW_DEPTH_BUFFER_BIT)) + if ((bitmask & (SW_COLOR_BUFFER_BIT)) && (RLSW.colorBuffer != NULL) && (RLSW.colorBuffer->pixels != NULL)) { - sw_framebuffer_fill(RLSW.framebuffer.pixels, size, RLSW.clearValue); + int size = RLSW.colorBuffer->width*RLSW.colorBuffer->height; + sw_framebuffer_fill_color(RLSW.colorBuffer, RLSW.clearColor); } - else if (bitmask & (SW_COLOR_BUFFER_BIT)) + + if ((bitmask & (SW_DEPTH_BUFFER_BIT)) && (RLSW.depthBuffer != NULL) && (RLSW.depthBuffer->pixels != NULL)) { - sw_framebuffer_fill_color(RLSW.framebuffer.pixels, size, RLSW.clearValue.color); - } - else if (bitmask & SW_DEPTH_BUFFER_BIT) - { - sw_framebuffer_fill_depth(RLSW.framebuffer.pixels, size, RLSW.clearValue.depth); + int size = RLSW.depthBuffer->width*RLSW.depthBuffer->height; + sw_framebuffer_fill_depth(RLSW.depthBuffer, RLSW.clearDepth); } } @@ -4277,6 +4606,13 @@ void swOrtho(double left, double right, double bottom, double top, double znear, void swBegin(SWdraw mode) { + // Check if we can render + if (!sw_is_ready_to_render()) + { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + // Check if the draw mode is valid if (!sw_is_draw_mode_valid(mode)) { @@ -4294,15 +4630,6 @@ void swBegin(SWdraw mode) RLSW.isDirtyMVP = false; } - // Obtain the number of vertices needed for this primitive - switch (mode) - { - case SW_POINTS: RLSW.reqVertices = 1; break; - case SW_LINES: RLSW.reqVertices = 2; break; - case SW_TRIANGLES: RLSW.reqVertices = 3; break; - case SW_QUADS: RLSW.reqVertices = 4; break; - } - // Initialize required values RLSW.vertexCounter = 0; RLSW.drawMode = mode; @@ -4310,7 +4637,7 @@ void swBegin(SWdraw mode) void swEnd(void) { - RLSW.drawMode = (SWdraw)0; + RLSW.drawMode = SW_DRAW_INVALID; } void swVertex2i(int x, int y) @@ -4477,7 +4804,7 @@ void swBindArray(SWarray type, void *buffer) void swDrawArrays(SWdraw mode, int offset, int count) { - if (RLSW.array.positions == 0) + if ((!sw_is_ready_to_render()) || (RLSW.array.positions == NULL)) { RLSW.errCode = SW_INVALID_OPERATION; return; @@ -4546,13 +4873,13 @@ void swDrawArrays(SWdraw mode, int offset, int count) void swDrawElements(SWdraw mode, int count, int type, const void *indices) { - if (RLSW.array.positions == 0) + if ((!sw_is_ready_to_render()) || (RLSW.array.positions == NULL)) { RLSW.errCode = SW_INVALID_OPERATION; return; } - if (count < 0) + if ((count < 0) || (indices == NULL)) { RLSW.errCode = SW_INVALID_VALUE; return; @@ -4640,162 +4967,1014 @@ void swDrawElements(SWdraw mode, int count, int type, const void *indices) swEnd(); } -void swGenTextures(int count, uint32_t *textures) +void swGenTextures(int count, sw_handle_t *textures) { - if ((count == 0) || (textures == NULL)) return; + if (!count || !textures) return; for (int i = 0; i < count; i++) { - if (RLSW.loadedTextureCount >= SW_MAX_TEXTURES) - { - RLSW.errCode = SW_STACK_OVERFLOW; // WARNING: Out of memory, not really stack overflow - return; - } - - uint32_t id = 0; - if (RLSW.freeTextureIdCount > 0) id = RLSW.freeTextureIds[--RLSW.freeTextureIdCount]; - else id = RLSW.loadedTextureCount++; - - RLSW.loadedTextures[id] = RLSW.loadedTextures[0]; - textures[i] = id; + sw_handle_t h = sw_pool_alloc(&RLSW.texturePool); + if (h == SW_HANDLE_NULL) { RLSW.errCode = SW_OUT_OF_MEMORY; return; } + textures[i] = h; } } -void swDeleteTextures(int count, uint32_t *textures) +void swDeleteTextures(int count, sw_handle_t *textures) { - if ((count == 0) || (textures == NULL)) return; + if (!count || !textures) return; for (int i = 0; i < count; i++) { - if (!sw_is_texture_valid(textures[i])) - { - RLSW.errCode = SW_INVALID_VALUE; - continue; - } + sw_texture_t *tex = sw_pool_get(&RLSW.texturePool, textures[i]); + if (!tex) { RLSW.errCode = SW_INVALID_VALUE; continue; } + if (tex == RLSW.boundTexture) RLSW.boundTexture = NULL; + if (tex == RLSW.colorBuffer) RLSW.colorBuffer = NULL; + if (tex == RLSW.depthBuffer) RLSW.depthBuffer = NULL; - SW_FREE(RLSW.loadedTextures[textures[i]].pixels); + sw_texture_free(tex); + *tex = (sw_texture_t) { 0 }; - RLSW.loadedTextures[textures[i]].pixels = NULL; - RLSW.freeTextureIds[RLSW.freeTextureIdCount++] = textures[i]; + sw_pool_free(&RLSW.texturePool, textures[i]); } } +void swBindTexture(sw_handle_t id) +{ + if (id == SW_HANDLE_NULL) { RLSW.boundTexture = NULL; return; } + if (!sw_is_texture_valid(id)) { RLSW.errCode = SW_INVALID_VALUE; return; } + + RLSW.boundTexture = sw_pool_get(&RLSW.texturePool, id); +} + +void swTexStorage2D(int width, int height, SWinternalformat format) +{ + if (RLSW.boundTexture == NULL) return; + + int pixelFormat = SW_PIXELFORMAT_UNKNOWN; + switch (format) + { + case SW_LUMINANCE8: pixelFormat = SW_PIXELFORMAT_COLOR_GRAYSCALE; break; + case SW_LUMINANCE8_ALPHA8: pixelFormat = SW_PIXELFORMAT_COLOR_GRAYALPHA; break; + case SW_R3_G3_B2: pixelFormat = SW_PIXELFORMAT_COLOR_R3G3B2; break; + case SW_RGB8: pixelFormat = SW_PIXELFORMAT_COLOR_R8G8B8; break; + case SW_RGBA4: pixelFormat = SW_PIXELFORMAT_COLOR_R4G4B4A4; break; + case SW_RGB5_A1: pixelFormat = SW_PIXELFORMAT_COLOR_R5G5B5A1; break; + case SW_RGBA8: pixelFormat = SW_PIXELFORMAT_COLOR_R8G8B8A8; break; + case SW_R16F: pixelFormat = SW_PIXELFORMAT_COLOR_R16; break; + case SW_RGB16F: pixelFormat = SW_PIXELFORMAT_COLOR_R16G16B16; break; + case SW_RGBA16F: pixelFormat = SW_PIXELFORMAT_COLOR_R16G16B16A16; break; + case SW_R32F: pixelFormat = SW_PIXELFORMAT_COLOR_R32; break; + case SW_RGB32F: pixelFormat = SW_PIXELFORMAT_COLOR_R32G32B32; break; + case SW_RGBA32F: pixelFormat = SW_PIXELFORMAT_COLOR_R32G32B32A32; break; + case SW_DEPTH_COMPONENT16: pixelFormat = SW_PIXELFORMAT_DEPTH_D16; break; + case SW_DEPTH_COMPONENT24: pixelFormat = SW_PIXELFORMAT_DEPTH_D32; break; + case SW_DEPTH_COMPONENT32: pixelFormat = SW_PIXELFORMAT_DEPTH_D32; break; + case SW_DEPTH_COMPONENT32F: pixelFormat = SW_PIXELFORMAT_DEPTH_D32; break; + default: + RLSW.errCode = SW_INVALID_ENUM; + return; + } + + (void)sw_texture_alloc(RLSW.boundTexture, NULL, width, height, pixelFormat); +} + void swTexImage2D(int width, int height, SWformat format, SWtype type, const void *data) { - uint32_t id = RLSW.currentTexture; + if (RLSW.boundTexture == NULL) return; - if (!sw_is_texture_valid(id)) + int pixelFormat = sw_pixel_get_format(format, type); + if (pixelFormat <= SW_PIXELFORMAT_UNKNOWN) { RLSW.errCode = SW_INVALID_ENUM; return; } + + (void)sw_texture_alloc(RLSW.boundTexture, data, width, height, pixelFormat); +} + +void swTexSubImage2D(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) +{ + if (!sw_is_texture_complete(RLSW.boundTexture) || (!pixels) || (width <= 0) || (height <= 0)) { RLSW.errCode = SW_INVALID_VALUE; return; } - int pixelFormat = sw_get_pixel_format(format, type); + if ((x < 0) || (y < 0) || (x + width > RLSW.boundTexture->width) || (y + height > RLSW.boundTexture->height)) + { + RLSW.errCode = SW_INVALID_VALUE; + return; + } - if (pixelFormat <= SW_PIXELFORMAT_UNKNOWN) + sw_pixelformat_t pFormat = sw_pixel_get_format(format, type); + if ((pFormat <= 0) || (pFormat >= SW_PIXELFORMAT_COUNT)) { RLSW.errCode = SW_INVALID_ENUM; return; } - sw_texture_t *texture = &RLSW.loadedTextures[id]; + const int srcPixelSize = SW_PIXELFORMAT_SIZE[pFormat]; + const int dstPixelSize = SW_PIXELFORMAT_SIZE[RLSW.boundTexture->format]; - int size = width*height; - texture->pixels = SW_MALLOC(4*size); + const uint8_t* srcBytes = (const uint8_t *)pixels; + uint8_t* dstBytes = (uint8_t *)RLSW.boundTexture->pixels; - if (texture->pixels == NULL) + if (pFormat == RLSW.boundTexture->format) { - RLSW.errCode = SW_STACK_OVERFLOW; // WARNING: Out of memory... + const int rowSize = width*dstPixelSize; + const int dstStride = RLSW.boundTexture->width*dstPixelSize; + const int srcStride = width*srcPixelSize; + + uint8_t *dstRow = dstBytes + (y*RLSW.boundTexture->width + x)*dstPixelSize; + const uint8_t *srcRow = srcBytes; + + for (int j = 0; j < height; ++j) + { + for (int i = 0; i < rowSize; ++i) dstRow[i] = srcRow[i]; + dstRow += dstStride; + srcRow += srcStride; + } return; } - for (int i = 0; i < size; i++) + for (int j = 0; j < height; ++j) { - uint32_t *dst = &((uint32_t*)texture->pixels)[i]; - sw_get_pixel((uint8_t*)dst, data, i, pixelFormat); - } + for (int i = 0; i < width; ++i) + { + float color[4]; + const int srcPixelOffset = ((j*width) + i)*srcPixelSize; + const int dstPixelOffset = (((y + j)*RLSW.boundTexture->width) + (x + i))*dstPixelSize; - texture->width = width; - texture->height = height; - texture->wMinus1 = width - 1; - texture->hMinus1 = height - 1; - texture->tx = 1.0f/width; - texture->ty = 1.0f/height; + sw_pixel_get_color(color, srcBytes, srcPixelOffset, pFormat); + sw_pixel_set_color(dstBytes, color, dstPixelOffset, RLSW.boundTexture->format); + } + } } void swTexParameteri(int param, int value) { - uint32_t id = RLSW.currentTexture; - - if (!sw_is_texture_valid(id)) - { - RLSW.errCode = SW_INVALID_VALUE; - return; - } - - sw_texture_t *texture = &RLSW.loadedTextures[id]; + if (RLSW.boundTexture == NULL) return; switch (param) { case SW_TEXTURE_MIN_FILTER: - { - if (!sw_is_texture_filter_valid(value)) - { - RLSW.errCode = SW_INVALID_ENUM; - return; - } + if (!sw_is_texture_filter_valid(value)) { RLSW.errCode = SW_INVALID_ENUM; return; } + RLSW.boundTexture->minFilter = (SWfilter)value; + break; - texture->minFilter = (SWfilter)value; - } break; case SW_TEXTURE_MAG_FILTER: - { - if (!sw_is_texture_filter_valid(value)) - { - RLSW.errCode = SW_INVALID_ENUM; - return; - } + if (!sw_is_texture_filter_valid(value)) { RLSW.errCode = SW_INVALID_ENUM; return; } + RLSW.boundTexture->magFilter = (SWfilter)value; + break; - texture->magFilter = (SWfilter)value; - } break; case SW_TEXTURE_WRAP_S: - { - if (!sw_is_texture_wrap_valid(value)) - { - RLSW.errCode = SW_INVALID_ENUM; - return; - } + if (!sw_is_texture_wrap_valid(value)) { RLSW.errCode = SW_INVALID_ENUM; return; } + RLSW.boundTexture->sWrap = (SWwrap)value; + break; - texture->sWrap = (SWwrap)value; - } break; case SW_TEXTURE_WRAP_T: - { - if (!sw_is_texture_wrap_valid(value)) - { - RLSW.errCode = SW_INVALID_ENUM; - return; - } + if (!sw_is_texture_wrap_valid(value)) { RLSW.errCode = SW_INVALID_ENUM; return; } + RLSW.boundTexture->tWrap = (SWwrap)value; + break; - texture->tWrap = (SWwrap)value; - } break; - default: RLSW.errCode = SW_INVALID_ENUM; break; + default: + RLSW.errCode = SW_INVALID_ENUM; + break; } } -void swBindTexture(uint32_t id) +void swGenFramebuffers(int count, uint32_t* framebuffers) { - if (id >= SW_MAX_TEXTURES) + if (!count || !framebuffers) return; + + for (int i = 0; i < count; i++) + { + sw_handle_t h = sw_pool_alloc(&RLSW.framebufferPool); + if (h == SW_HANDLE_NULL) { RLSW.errCode = SW_OUT_OF_MEMORY; return; } + framebuffers[i] = h; + } +} + +void swDeleteFramebuffers(int count, uint32_t* framebuffers) +{ + if (!count || !framebuffers) return; + + for (int i = 0; i < count; i++) + { + sw_framebuffer_t *fb = sw_pool_get(&RLSW.framebufferPool, framebuffers[i]); + if (!fb) { RLSW.errCode = SW_INVALID_VALUE; continue; } + + if (framebuffers[i] == RLSW.boundFramebufferId) + { + RLSW.boundFramebufferId = SW_HANDLE_NULL; + RLSW.colorBuffer = &RLSW.framebuffer.color; + RLSW.depthBuffer = &RLSW.framebuffer.depth; + } + + sw_pool_free(&RLSW.framebufferPool, framebuffers[i]); + } +} + +void swBindFramebuffer(uint32_t id) +{ + if (id == SW_HANDLE_NULL) + { + RLSW.boundFramebufferId = SW_HANDLE_NULL; + RLSW.colorBuffer = &RLSW.framebuffer.color; + RLSW.depthBuffer = &RLSW.framebuffer.depth; + return; + } + + sw_framebuffer_t* fb = sw_pool_get(&RLSW.framebufferPool, id); + if (fb == NULL) { RLSW.errCode = SW_INVALID_VALUE; return; } - if (RLSW.loadedTextures[id].pixels == NULL) + RLSW.boundFramebufferId = id; + RLSW.colorBuffer = sw_pool_get(&RLSW.texturePool, fb->colorAttachment); + RLSW.depthBuffer = sw_pool_get(&RLSW.texturePool, fb->depthAttachment); +} + +void swFramebufferTexture2D(SWattachment attach, uint32_t texture) +{ + if (RLSW.boundFramebufferId == SW_HANDLE_NULL) + { + RLSW.errCode = SW_INVALID_OPERATION; // not really standard but hey + return; + } + + sw_framebuffer_t* fb = sw_pool_get(&RLSW.framebufferPool, RLSW.boundFramebufferId); + if (fb == NULL) return; // Should never happen + + switch (attach) + { + case SW_COLOR_ATTACHMENT: + fb->colorAttachment = texture; + RLSW.colorBuffer = sw_pool_get(&RLSW.texturePool, texture); + break; + case SW_DEPTH_ATTACHMENT: + fb->depthAttachment = texture; + RLSW.depthBuffer = sw_pool_get(&RLSW.texturePool, texture); + break; + default: + RLSW.errCode = SW_INVALID_ENUM; + break; + } +} + +SWfbstatus swCheckFramebufferStatus(void) +{ + if (RLSW.boundFramebufferId == SW_HANDLE_NULL) + { + return SW_FRAMEBUFFER_COMPLETE; + } + + if (RLSW.colorBuffer == NULL) + { + return SW_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; + } + + if (!sw_is_texture_complete(RLSW.colorBuffer)) + { + return SW_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + } + + if (RLSW.colorBuffer->format != SW_FRAMEBUFFER_COLOR_FORMAT) + { + return SW_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + } + + if (RLSW.depthBuffer) + { + if (!sw_is_texture_complete(RLSW.depthBuffer)) + { + return SW_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + } + + if (RLSW.depthBuffer->format != SW_FRAMEBUFFER_DEPTH_FORMAT) + { + return SW_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + } + + if ((RLSW.colorBuffer->width != RLSW.depthBuffer->width) || + (RLSW.colorBuffer->height != RLSW.depthBuffer->height)) + { + return SW_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; + } + } + + return SW_FRAMEBUFFER_COMPLETE; +} + +void swGetFramebufferAttachmentParameteriv(SWattachment attachment, SWattachget property, int *v) +{ + if (RLSW.boundFramebufferId == SW_HANDLE_NULL) { RLSW.errCode = SW_INVALID_OPERATION; return; } - RLSW.currentTexture = id; + if (v == NULL) + { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + sw_framebuffer_t* fb = sw_pool_get(&RLSW.framebufferPool, RLSW.boundFramebufferId); + if (fb == NULL) return; // Should never happen + + switch (property) + { + case SW_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: + { + switch (attachment) + { + case SW_COLOR_ATTACHMENT: + *v = fb->colorAttachment; + break; + case SW_DEPTH_ATTACHMENT: + *v = fb->depthAttachment; + break; + } + } + break; + + case SW_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: + { + *v = GL_TEXTURE; + break; + } + } } #endif // RLSW_IMPLEMENTATION + +//---------------------------------------------------------------------------------- +// Template Rasterization Functions +//---------------------------------------------------------------------------------- + +// Triangle rasterization functions +//------------------------------------------------------------------------------------------- +#ifdef RLSW_TEMPLATE_RASTER_TRIANGLE + +// Options: +// - RLSW_TEMPLATE_RASTER_TRIANGLE -> Contains the suffix name +// - SW_ENABLE_DEPTH_TEST +// - SW_ENABLE_TEXTURE +// - SW_ENABLE_BLEND + +#define SW_RASTER_TRIANGLE_SPAN SW_CONCATX(sw_raster_triangle_span_, RLSW_TEMPLATE_RASTER_TRIANGLE) +#define SW_RASTER_TRIANGLE SW_CONCATX(sw_raster_triangle_, RLSW_TEMPLATE_RASTER_TRIANGLE) + +static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t *end, float dUdy, float dVdy) +{ + // Gets the start and end coordinates + int xStart = (int)start->screen[0]; + int xEnd = (int)end->screen[0]; + + // Avoid empty lines + if (xStart == xEnd) return; + + // Compute the subpixel distance to traverse before the first pixel + float xSubstep = 1.0f - sw_fract(start->screen[0]); + + // Compute the inverse horizontal distance along the X axis + float dxRcp = 1.0f/(end->screen[0] - start->screen[0]); + + // Compute the interpolation steps along the X axis +#ifdef SW_ENABLE_DEPTH_TEST + float dZdx = (end->homogeneous[2] - start->homogeneous[2])*dxRcp; +#endif + float dWdx = (end->homogeneous[3] - start->homogeneous[3])*dxRcp; + float dCdx[4] = { + (end->color[0] - start->color[0])*dxRcp, + (end->color[1] - start->color[1])*dxRcp, + (end->color[2] - start->color[2])*dxRcp, + (end->color[3] - start->color[3])*dxRcp + }; +#ifdef SW_ENABLE_TEXTURE + float dUdx = (end->texcoord[0] - start->texcoord[0])*dxRcp; + float dVdx = (end->texcoord[1] - start->texcoord[1])*dxRcp; +#endif + + // Initializing the interpolation starting values +#ifdef SW_ENABLE_DEPTH_TEST + float z = start->homogeneous[2] + dZdx*xSubstep; +#endif + float w = start->homogeneous[3] + dWdx*xSubstep; + float color[4] = { + start->color[0] + dCdx[0]*xSubstep, + start->color[1] + dCdx[1]*xSubstep, + start->color[2] + dCdx[2]*xSubstep, + start->color[3] + dCdx[3]*xSubstep + }; +#ifdef SW_ENABLE_TEXTURE + float u = start->texcoord[0] + dUdx*xSubstep; + float v = start->texcoord[1] + dVdx*xSubstep; +#endif + + // Pre-calculate the starting pointers for the framebuffer row + int y = (int)start->screen[1]; + int baseOffset = y*RLSW.colorBuffer->width + xStart; + uint8_t *cPtr = (uint8_t *)(RLSW.colorBuffer->pixels) + baseOffset*SW_FRAMEBUFFER_COLOR_SIZE; +#ifdef SW_ENABLE_DEPTH_TEST + uint8_t *dPtr = (uint8_t *)(RLSW.depthBuffer->pixels) + baseOffset*SW_FRAMEBUFFER_DEPTH_SIZE; +#endif + + // Scanline rasterization + for (int x = xStart; x < xEnd; x++) + { + float wRcp = 1.0f/w; + float srcColor[4] = { + color[0]*wRcp, + color[1]*wRcp, + color[2]*wRcp, + color[3]*wRcp + }; + + #ifdef SW_ENABLE_DEPTH_TEST + { + /* TODO: Implement different depth funcs? */ + float depth = SW_FRAMEBUFFER_DEPTH_GET(dPtr, 0); + if (z > depth) goto discard; + + /* TODO: Implement depth mask */ + SW_FRAMEBUFFER_DEPTH_SET(dPtr, z, 0); + } + #endif + + #ifdef SW_ENABLE_TEXTURE + { + float texColor[4]; + float s = u*wRcp; + float t = v*wRcp; + sw_texture_sample(texColor, RLSW.boundTexture, s, t, dUdx, dUdy, dVdx, dVdy); + srcColor[0] *= texColor[0]; + srcColor[1] *= texColor[1]; + srcColor[2] *= texColor[2]; + srcColor[3] *= texColor[3]; + } + #endif + + #ifdef SW_ENABLE_BLEND + { + float dstColor[4]; + SW_FRAMEBUFFER_COLOR_GET(dstColor, cPtr, 0); + sw_blend_colors(dstColor, srcColor); + SW_FRAMEBUFFER_COLOR_SET(cPtr, dstColor, 0); + } + #else + { + SW_FRAMEBUFFER_COLOR_SET(cPtr, srcColor, 0); + } + #endif + + // Increment the interpolation parameter, UVs, and pointers + discard: + #ifdef SW_ENABLE_DEPTH_TEST + { + z += dZdx; + } + #endif + + w += dWdx; + + color[0] += dCdx[0]; + color[1] += dCdx[1]; + color[2] += dCdx[2]; + color[3] += dCdx[3]; + + #ifdef SW_ENABLE_TEXTURE + { + u += dUdx; + v += dVdx; + } + #endif + + cPtr += SW_FRAMEBUFFER_COLOR_SIZE; + + #ifdef SW_ENABLE_DEPTH_TEST + { + dPtr += SW_FRAMEBUFFER_DEPTH_SIZE; + } + #endif + } +} + +static void SW_RASTER_TRIANGLE(const sw_vertex_t *v0, const sw_vertex_t *v1, const sw_vertex_t *v2) +{ + // Swap vertices by increasing Y + if (v0->screen[1] > v1->screen[1]) { const sw_vertex_t *tmp = v0; v0 = v1; v1 = tmp; } + if (v1->screen[1] > v2->screen[1]) { const sw_vertex_t *tmp = v1; v1 = v2; v2 = tmp; } + if (v0->screen[1] > v1->screen[1]) { const sw_vertex_t *tmp = v0; v0 = v1; v1 = tmp; } + + // Extracting coordinates from the sorted vertices + float x0 = v0->screen[0], y0 = v0->screen[1]; + float x1 = v1->screen[0], y1 = v1->screen[1]; + float x2 = v2->screen[0], y2 = v2->screen[1]; + + // Compute height differences + float h02 = y2 - y0; + float h01 = y1 - y0; + float h12 = y2 - y1; + + if (h02 < 1e-6f) return; + + // Precompute the inverse values without additional checks + float h02Rcp = 1.0f/h02; + float h01Rcp = (h01 > 1e-6f)? 1.0f/h01 : 0.0f; + float h12Rcp = (h12 > 1e-6f)? 1.0f/h12 : 0.0f; + + // Pre-calculation of slopes + float dXdy02 = (x2 - x0)*h02Rcp; + float dXdy01 = (x1 - x0)*h01Rcp; + float dXdy12 = (x2 - x1)*h12Rcp; + + // Y subpixel correction + float y0Substep = 1.0f - sw_fract(y0); + float y1Substep = 1.0f - sw_fract(y1); + + // Y bounds (vertical clipping) + int yTop = (int)y0; + int yMid = (int)y1; + int yBot = (int)y2; + + // Compute gradients for each side of the triangle + sw_vertex_t dVXdy02, dVXdy01, dVXdy12; + sw_get_vertex_grad_PTCH(&dVXdy02, v0, v2, h02Rcp); + sw_get_vertex_grad_PTCH(&dVXdy01, v0, v1, h01Rcp); + sw_get_vertex_grad_PTCH(&dVXdy12, v1, v2, h12Rcp); + + // Get a copy of vertices for interpolation and apply substep correction + sw_vertex_t vLeft = *v0, vRight = *v0; + sw_add_vertex_grad_scaled_PTCH(&vLeft, &dVXdy02, y0Substep); + sw_add_vertex_grad_scaled_PTCH(&vRight, &dVXdy01, y0Substep); + + vLeft.screen[0] += dXdy02*y0Substep; + vRight.screen[0] += dXdy01*y0Substep; + + // Scanline for the upper part of the triangle + for (int y = yTop; y < yMid; y++) + { + vLeft.screen[1] = vRight.screen[1] = y; + + if (vLeft.screen[0] < vRight.screen[0]) + SW_RASTER_TRIANGLE_SPAN(&vLeft, &vRight, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); + else + SW_RASTER_TRIANGLE_SPAN(&vRight, &vLeft, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); + + sw_add_vertex_grad_PTCH(&vLeft, &dVXdy02); + vLeft.screen[0] += dXdy02; + + sw_add_vertex_grad_PTCH(&vRight, &dVXdy01); + vRight.screen[0] += dXdy01; + } + + // Get a copy of next right for interpolation and apply substep correction + vRight = *v1; + sw_add_vertex_grad_scaled_PTCH(&vRight, &dVXdy12, y1Substep); + vRight.screen[0] += dXdy12*y1Substep; + + // Scanline for the lower part of the triangle + for (int y = yMid; y < yBot; y++) + { + vLeft.screen[1] = vRight.screen[1] = y; + + if (vLeft.screen[0] < vRight.screen[0]) + SW_RASTER_TRIANGLE_SPAN(&vLeft, &vRight, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); + else + SW_RASTER_TRIANGLE_SPAN(&vRight, &vLeft, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); + + sw_add_vertex_grad_PTCH(&vLeft, &dVXdy02); + vLeft.screen[0] += dXdy02; + + sw_add_vertex_grad_PTCH(&vRight, &dVXdy12); + vRight.screen[0] += dXdy12; + } +} + +#endif // RLSW_TEMPLATE_RASTER_TRIANGLE +//------------------------------------------------------------------------------------------- + +// Quad rasterization functions +//------------------------------------------------------------------------------------------- +#ifdef RLSW_TEMPLATE_RASTER_QUAD + +// Options: +// - RLSW_TEMPLATE_RASTER_QUAD -> Contains the suffix name +// - SW_ENABLE_DEPTH_TEST +// - SW_ENABLE_TEXTURE +// - SW_ENABLE_BLEND + +#define SW_RASTER_QUAD SW_CONCATX(sw_raster_quad_, RLSW_TEMPLATE_RASTER_QUAD) + +// REVIEW: Could a perfectly aligned quad, where one of the four points has a different depth, +// still appear perfectly aligned from a certain point of view? +// Because in that case, it's still needed to perform perspective division for textures and colors... + +static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b, + const sw_vertex_t *c, const sw_vertex_t *d) +{ + // Classify corners + // For axis-aligned quads x+y and x-y uniquely identify each corner + const sw_vertex_t *verts[4] = { a, b, c, d }; + const sw_vertex_t *tl = verts[0], *tr = verts[0], *br = verts[0], *bl = verts[0]; + for (int i = 1; i < 4; i++) + { + float sum = verts[i]->screen[0] + verts[i]->screen[1]; + float diff = verts[i]->screen[0] - verts[i]->screen[1]; + if (sum < tl->screen[0] + tl->screen[1]) tl = verts[i]; + if (diff > tr->screen[0] - tr->screen[1]) tr = verts[i]; + if (sum > br->screen[0] + br->screen[1]) br = verts[i]; + if (diff < bl->screen[0] - bl->screen[1]) bl = verts[i]; + } + + int xMin = (int)tl->screen[0]; + int yMin = (int)tl->screen[1]; + int xMax = (int)br->screen[0]; + int yMax = (int)br->screen[1]; + + float w = (float)(xMax - xMin); + float h = (float)(yMax - yMin); + if ((w <= 0) || (h <= 0)) return; + + float wRcp = 1.0f/w; + float hRcp = 1.0f/h; + + // Subpixel corrections + float xSubstep = 1.0f - sw_fract(tl->screen[0]); + float ySubstep = 1.0f - sw_fract(tl->screen[1]); + + // Gradients along X (tl->tr) and Y (tl->bl) + float dCdx[4] = { + (tr->color[0] - tl->color[0])*wRcp, + (tr->color[1] - tl->color[1])*wRcp, + (tr->color[2] - tl->color[2])*wRcp, + (tr->color[3] - tl->color[3])*wRcp, + }; + float dCdy[4] = { + (bl->color[0] - tl->color[0])*hRcp, + (bl->color[1] - tl->color[1])*hRcp, + (bl->color[2] - tl->color[2])*hRcp, + (bl->color[3] - tl->color[3])*hRcp, + }; + +#ifdef SW_ENABLE_DEPTH_TEST + float dZdx = (tr->homogeneous[2] - tl->homogeneous[2])*wRcp; + float dZdy = (bl->homogeneous[2] - tl->homogeneous[2])*hRcp; + float zRow = tl->homogeneous[2] + dZdx*xSubstep + dZdy*ySubstep; +#endif + +#ifdef SW_ENABLE_TEXTURE + float dUdx = (tr->texcoord[0] - tl->texcoord[0])*wRcp; + float dVdx = (tr->texcoord[1] - tl->texcoord[1])*wRcp; + float dUdy = (bl->texcoord[0] - tl->texcoord[0])*hRcp; + float dVdy = (bl->texcoord[1] - tl->texcoord[1])*hRcp; + float uRow = tl->texcoord[0] + dUdx*xSubstep + dUdy*ySubstep; + float vRow = tl->texcoord[1] + dVdx*xSubstep + dVdy*ySubstep; +#endif + + float cRow[4] = { + tl->color[0] + dCdx[0]*xSubstep + dCdy[0]*ySubstep, + tl->color[1] + dCdx[1]*xSubstep + dCdy[1]*ySubstep, + tl->color[2] + dCdx[2]*xSubstep + dCdy[2]*ySubstep, + tl->color[3] + dCdx[3]*xSubstep + dCdy[3]*ySubstep, + }; + + int stride = RLSW.colorBuffer->width; + uint8_t *cPixels = RLSW.colorBuffer->pixels; +#ifdef SW_ENABLE_DEPTH_TEST + uint8_t *dPixels = RLSW.depthBuffer->pixels; +#endif + + for (int y = yMin; y < yMax; y++) + { + int baseOffset = y*stride + xMin; + uint8_t *cPtr = cPixels + baseOffset*SW_FRAMEBUFFER_COLOR_SIZE; + #ifdef SW_ENABLE_DEPTH_TEST + uint8_t *dPtr = dPixels + baseOffset*SW_FRAMEBUFFER_DEPTH_SIZE; + float z = zRow; + #endif + #ifdef SW_ENABLE_TEXTURE + float u = uRow; + float v = vRow; + #endif + float color[4] = { cRow[0], cRow[1], cRow[2], cRow[3] }; + + for (int x = xMin; x < xMax; x++) + { + float srcColor[4] = { color[0], color[1], color[2], color[3] }; + + #ifdef SW_ENABLE_DEPTH_TEST + { + float depth = SW_FRAMEBUFFER_DEPTH_GET(dPtr, 0); + if (z > depth) goto discard; + SW_FRAMEBUFFER_DEPTH_SET(dPtr, z, 0); + } + #endif + + #ifdef SW_ENABLE_TEXTURE + { + float texColor[4]; + sw_texture_sample(texColor, RLSW.boundTexture, u, v, dUdx, dUdy, dVdx, dVdy); + srcColor[0] *= texColor[0]; + srcColor[1] *= texColor[1]; + srcColor[2] *= texColor[2]; + srcColor[3] *= texColor[3]; + } + #endif + + #ifdef SW_ENABLE_BLEND + { + float dstColor[4]; + SW_FRAMEBUFFER_COLOR_GET(dstColor, cPtr, 0); + sw_blend_colors(dstColor, srcColor); + SW_FRAMEBUFFER_COLOR_SET(cPtr, dstColor, 0); + } + #else + { + SW_FRAMEBUFFER_COLOR_SET(cPtr, srcColor, 0); + } + #endif + + discard: + color[0] += dCdx[0]; + color[1] += dCdx[1]; + color[2] += dCdx[2]; + color[3] += dCdx[3]; + + #ifdef SW_ENABLE_DEPTH_TEST + { + z += dZdx; + dPtr += SW_FRAMEBUFFER_DEPTH_SIZE; + } + #endif + + #ifdef SW_ENABLE_TEXTURE + { + u += dUdx; + v += dVdx; + } + #endif + + cPtr += SW_FRAMEBUFFER_COLOR_SIZE; + } + + cRow[0] += dCdy[0]; + cRow[1] += dCdy[1]; + cRow[2] += dCdy[2]; + cRow[3] += dCdy[3]; + + #ifdef SW_ENABLE_DEPTH_TEST + { + zRow += dZdy; + } + #endif + + #ifdef SW_ENABLE_TEXTURE + { + uRow += dUdy; + vRow += dVdy; + } + #endif + } +} + +#endif // RLSW_TEMPLATE_RASTER_QUAD +//------------------------------------------------------------------------------------------- + +// Quad rasterization functions +//------------------------------------------------------------------------------------------- +#ifdef RLSW_TEMPLATE_RASTER_LINE + +// Options: +// - RLSW_TEMPLATE_RASTER_LINE -> Contains the suffix name +// - SW_ENABLE_DEPTH_TEST +// - SW_ENABLE_BLEND + +#define SW_RASTER_LINE SW_CONCATX(sw_raster_line_, RLSW_TEMPLATE_RASTER_LINE) +#define SW_RASTER_LINE_THICK SW_CONCATX(sw_raster_line_thick_, RLSW_TEMPLATE_RASTER_LINE) + +static void SW_RASTER_LINE(const sw_vertex_t *v0, const sw_vertex_t *v1) +{ + // Convert from pixel-center convention (n+0.5) to pixel-origin convention (n) + float x0 = v0->screen[0] - 0.5f; + float y0 = v0->screen[1] - 0.5f; + float x1 = v1->screen[0] - 0.5f; + float y1 = v1->screen[1] - 0.5f; + + float dx = x1 - x0; + float dy = y1 - y0; + + // Compute dominant axis and subpixel offset + float steps, substep; + if (fabsf(dx) > fabsf(dy)) + { + steps = fabsf(dx); + if (steps < 1.0f) return; + substep = (dx >= 0.0f)? (1.0f - sw_fract(x0)) : sw_fract(x0); + } + else + { + steps = fabsf(dy); + if (steps < 1.0f) return; + substep = (dy >= 0.0f)? (1.0f - sw_fract(y0)) : sw_fract(y0); + } + + // Compute per pixel increments + float xInc = dx/steps; + float yInc = dy/steps; + float stepRcp = 1.0f/steps; +#ifdef SW_ENABLE_DEPTH_TEST + float zInc = (v1->homogeneous[2] - v0->homogeneous[2])*stepRcp; +#endif + float rInc = (v1->color[0] - v0->color[0])*stepRcp; + float gInc = (v1->color[1] - v0->color[1])*stepRcp; + float bInc = (v1->color[2] - v0->color[2])*stepRcp; + float aInc = (v1->color[3] - v0->color[3])*stepRcp; + + // Initializing the interpolation starting values + float x = x0 + xInc*substep; + float y = y0 + yInc*substep; +#ifdef SW_ENABLE_DEPTH_TEST + float z = v0->homogeneous[2] + zInc*substep; +#endif + float r = v0->color[0] + rInc*substep; + float g = v0->color[1] + gInc*substep; + float b = v0->color[2] + bInc*substep; + float a = v0->color[3] + aInc*substep; + + // Start line rasterization + const int fbWidth = RLSW.colorBuffer->width; + uint8_t *cPixels = RLSW.colorBuffer->pixels; +#ifdef SW_ENABLE_DEPTH_TEST + uint8_t *dPixels = RLSW.depthBuffer->pixels; +#endif + + int numPixels = (int)(steps - substep) + 1; + + for (int i = 0; i < numPixels; i++) + { + int px = x; + int py = y; + + int baseOffset = py*fbWidth + px; + uint8_t *cPtr = cPixels + baseOffset*SW_FRAMEBUFFER_COLOR_SIZE; + #ifdef SW_ENABLE_DEPTH_TEST + uint8_t *dPtr = dPixels + baseOffset*SW_FRAMEBUFFER_DEPTH_SIZE; + #endif + + #ifdef SW_ENABLE_DEPTH_TEST + { + // TODO: Implement different depth funcs? + float depth = SW_FRAMEBUFFER_DEPTH_GET(dPtr, 0); + if (z > depth) goto discard; + + // TODO: Implement depth mask + SW_FRAMEBUFFER_DEPTH_SET(dPtr, z, 0); + } + #endif + + float srcColor[4] = {r, g, b, a}; + + #ifdef SW_ENABLE_BLEND + { + float dstColor[4]; + SW_FRAMEBUFFER_COLOR_GET(dstColor, cPtr, 0); + sw_blend_colors(dstColor, srcColor); + SW_FRAMEBUFFER_COLOR_SET(cPtr, dstColor, 0); + } + #else + { + SW_FRAMEBUFFER_COLOR_SET(cPtr, srcColor, 0); + } + #endif + + discard: + x += xInc; + y += yInc; + #ifdef SW_ENABLE_DEPTH_TEST + { + z += zInc; + } + #endif + r += rInc; + g += gInc; + b += bInc; + a += aInc; + } +} + +static void SW_RASTER_LINE_THICK(const sw_vertex_t *v1, const sw_vertex_t *v2) +{ + sw_vertex_t tv1, tv2; + + int x1 = (int)v1->screen[0]; + int y1 = (int)v1->screen[1]; + int x2 = (int)v2->screen[0]; + int y2 = (int)v2->screen[1]; + + int dx = x2 - x1; + int dy = y2 - y1; + + SW_RASTER_LINE(v1, v2); + + if ((dx != 0) && (abs(dy/dx) < 1)) + { + int wy = (int)((RLSW.lineWidth - 1.0f)*abs(dx)/sqrtf(dx*dx + dy*dy)); + wy >>= 1; + for (int i = 1; i <= wy; i++) + { + tv1 = *v1, tv2 = *v2; + tv1.screen[1] -= i; + tv2.screen[1] -= i; + SW_RASTER_LINE(&tv1, &tv2); + tv1 = *v1, tv2 = *v2; + tv1.screen[1] += i; + tv2.screen[1] += i; + SW_RASTER_LINE(&tv1, &tv2); + } + } + else if (dy != 0) + { + int wx = (int)((RLSW.lineWidth - 1.0f)*abs(dy)/sqrtf(dx*dx + dy*dy)); + wx >>= 1; + for (int i = 1; i <= wx; i++) + { + tv1 = *v1, tv2 = *v2; + tv1.screen[0] -= i; + tv2.screen[0] -= i; + SW_RASTER_LINE(&tv1, &tv2); + tv1 = *v1, tv2 = *v2; + tv1.screen[0] += i; + tv2.screen[0] += i; + SW_RASTER_LINE(&tv1, &tv2); + } + } +} + +#endif // RLSW_TEMPLATE_RASTER_LINE +//------------------------------------------------------------------------------------------- + +// Point rasterization functions +//------------------------------------------------------------------------------------------- +#ifdef RLSW_TEMPLATE_RASTER_POINT + +// Options: +// - RLSW_TEMPLATE_RASTER_POINT -> Contains the suffix name +// - SW_ENABLE_DEPTH_TEST +// - SW_ENABLE_BLEND + +#define SW_RASTER_POINT_PIXEL SW_CONCATX(sw_raster_point_pixel_, RLSW_TEMPLATE_RASTER_POINT) +#define SW_RASTER_POINT SW_CONCATX(sw_raster_point_, RLSW_TEMPLATE_RASTER_POINT) + +static void SW_RASTER_POINT_PIXEL(int x, int y, float z, const float color[4]) +{ + int offset = y*RLSW.colorBuffer->width + x; + + #ifdef SW_ENABLE_DEPTH_TEST + { + uint8_t *dPtr = (uint8_t *)(RLSW.depthBuffer->pixels) + offset*SW_FRAMEBUFFER_DEPTH_SIZE; + + // TODO: Implement different depth funcs? + float depth = SW_FRAMEBUFFER_DEPTH_GET(dPtr, 0); + if (z > depth) return; + + // TODO: Implement depth mask + SW_FRAMEBUFFER_DEPTH_SET(dPtr, z, 0); + } + #endif + + uint8_t *cPtr = (uint8_t *)(RLSW.colorBuffer->pixels) + offset*SW_FRAMEBUFFER_COLOR_SIZE; + + #ifdef SW_ENABLE_BLEND + { + float dstColor[4]; + SW_FRAMEBUFFER_COLOR_GET(dstColor, cPtr, 0); + sw_blend_colors(dstColor, color); + SW_FRAMEBUFFER_COLOR_SET(cPtr, dstColor, 0); + } + #else + { + SW_FRAMEBUFFER_COLOR_SET(cPtr, color, 0); + } + #endif +} + +static void SW_RASTER_POINT(const sw_vertex_t *v) +{ + int cx = v->screen[0]; + int cy = v->screen[1]; + float cz = v->homogeneous[2]; + int radius = RLSW.pointRadius; + const float *color = v->color; + + for (int dy = -radius; dy <= radius; dy++) + { + for (int dx = -radius; dx <= radius; dx++) + { + SW_RASTER_POINT_PIXEL(cx + dx, cy + dy, cz, color); + } + } +} + +#endif // RLSW_TEMPLATE_RASTER_POINT +//------------------------------------------------------------------------------------------- diff --git a/src/rlgl.h b/src/rlgl.h index b40b50b43..df5335790 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -838,6 +838,7 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) #define RLSW_IMPLEMENTATION #define SW_MALLOC(sz) RL_MALLOC(sz) + #define SW_CALLOC(n,sz) RL_CALLOC(n, sz) #define SW_REALLOC(ptr, newSz) RL_REALLOC(ptr, newSz) #define SW_FREE(ptr) RL_FREE(ptr) #include "external/rlsw.h" // OpenGL 1.1 software implementation @@ -1857,7 +1858,7 @@ void rlDisableShader(void) // Enable rendering to texture (fbo) void rlEnableFramebuffer(unsigned int id) { -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE)) glBindFramebuffer(GL_FRAMEBUFFER, id); #endif } @@ -1866,7 +1867,7 @@ void rlEnableFramebuffer(unsigned int id) unsigned int rlGetActiveFramebuffer(void) { GLint fboId = 0; -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3)) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE)) glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &fboId); #endif return fboId; @@ -1875,7 +1876,7 @@ unsigned int rlGetActiveFramebuffer(void) // Disable rendering to texture void rlDisableFramebuffer(void) { -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE)) glBindFramebuffer(GL_FRAMEBUFFER, 0); #endif } @@ -1891,7 +1892,7 @@ void rlBlitFramebuffer(int srcX, int srcY, int srcWidth, int srcHeight, int dstX // Bind framebuffer object (fbo) void rlBindFramebuffer(unsigned int target, unsigned int framebuffer) { -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE)) glBindFramebuffer(target, framebuffer); #endif } @@ -3471,6 +3472,13 @@ unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer) TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Depth renderbuffer loaded successfully (%i bits)", id, (RLGL.ExtSupported.maxDepthBits >= 24)? RLGL.ExtSupported.maxDepthBits : 16); } +#elif defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + // NOTE: Renderbuffers are the same type of object as textures in rlsw + // WARNING: Ensure that the depth format is the one specified at rlsw compilation + glGenRenderbuffers(1, &id); + glBindRenderbuffer(GL_RENDERBUFFER, id); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, width, height); + glBindRenderbuffer(GL_RENDERBUFFER, 0); #endif return id; @@ -3771,7 +3779,7 @@ void rlCopyFramebuffer(int x, int y, int width, int height, int format, void *pi #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) unsigned int glInternalFormat, glFormat, glType; rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); // Get OpenGL texture format - swCopyFramebuffer(x, y, width, height, glFormat, glType, pixels); + swReadPixels(x, y, width, height, glFormat, glType, pixels); #endif } @@ -3779,7 +3787,7 @@ void rlCopyFramebuffer(int x, int y, int width, int height, int format, void *pi void rlResizeFramebuffer(int width, int height) { #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) - swResizeFramebuffer(width, height); + swResize(width, height); #endif } @@ -3829,7 +3837,7 @@ unsigned int rlLoadFramebuffer(void) unsigned int fboId = 0; if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return fboId; } -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE)) glGenFramebuffers(1, &fboId); // Create the framebuffer object glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind any framebuffer #endif @@ -3841,7 +3849,7 @@ unsigned int rlLoadFramebuffer(void) // NOTE: Attach type: 0-Color, 1-Depth renderbuffer, 2-Depth texture void rlFramebufferAttach(unsigned int id, unsigned int texId, int attachType, int texType, int mipLevel) { -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE)) glBindFramebuffer(GL_FRAMEBUFFER, id); switch (attachType) @@ -3881,7 +3889,7 @@ bool rlFramebufferComplete(unsigned int id) { bool result = false; -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE)) glBindFramebuffer(GL_FRAMEBUFFER, id); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -3912,7 +3920,7 @@ bool rlFramebufferComplete(unsigned int id) // NOTE: All attached textures/cubemaps/renderbuffers are also deleted void rlUnloadFramebuffer(unsigned int id) { -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE)) // Query depth attachment to automatically delete texture/renderbuffer int depthType = 0; glBindFramebuffer(GL_FRAMEBUFFER, id); // Bind framebuffer to query depth texture type From 6ddf9a18856cea2880d33f2ca216942966cd8f09 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Mar 2026 18:30:48 +0100 Subject: [PATCH 182/319] Code review, format tweaks, defined version to 1.5 --- src/external/rlsw.h | 589 ++++++++++++++++---------------------------- 1 file changed, 208 insertions(+), 381 deletions(-) diff --git a/src/external/rlsw.h b/src/external/rlsw.h index 81d4a54f1..b22bf694b 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rlsw v1.0 - An OpenGL 1.1-style software renderer implementation +* rlsw v1.5 - An OpenGL 1.1-style software renderer implementation * * DESCRIPTION: * rlsw is a custom OpenGL 1.1-style implementation on software, intended to provide all @@ -87,6 +87,8 @@ #ifndef RLSW_H #define RLSW_H +#define RLGL_VERSION "1.5" + #include #include @@ -738,8 +740,8 @@ SWAPI void swTexImage2D(int width, int height, SWformat format, SWtype type, con SWAPI void swTexSubImage2D(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); SWAPI void swTexParameteri(int param, int value); -SWAPI void swGenFramebuffers(int count, uint32_t* framebuffers); -SWAPI void swDeleteFramebuffers(int count, uint32_t* framebuffers); +SWAPI void swGenFramebuffers(int count, uint32_t *framebuffers); +SWAPI void swDeleteFramebuffers(int count, uint32_t *framebuffers); SWAPI void swBindFramebuffer(uint32_t id); SWAPI void swFramebufferTexture2D(SWattachment attach, uint32_t texture); SWAPI SWfbstatus swCheckFramebufferStatus(void); @@ -942,21 +944,21 @@ typedef struct { } sw_texture_t; typedef struct { - sw_texture_t color; - sw_texture_t depth; + sw_texture_t color; // Default framebuffer color texture + sw_texture_t depth; // Default framebuffer depth texture } sw_default_framebuffer_t; typedef struct { - sw_handle_t colorAttachment; - sw_handle_t depthAttachment; + sw_handle_t colorAttachment; // Framebuffer color attachment id + sw_handle_t depthAttachment; // Framebuffer depth attachment id } sw_framebuffer_t; typedef struct { - void *data; // flat storage [capacity*stride] bytes - uint8_t *gen; // generation per slot [capacity] - uint32_t *freeList; // free indices stack [capacity] + void *data; // Flat storage [capacity*stride] bytes + uint8_t *gen; // Generation per slot [capacity] + uint32_t *freeList; // Free indices stack [capacity] int freeCount; - int watermark; // next blank index (starts at 1, skips 0) + int watermark; // Next blank index (starts at 1, skips 0) int capacity; size_t stride; } sw_pool_t; @@ -967,6 +969,7 @@ typedef void (*sw_raster_quad_f)(const sw_vertex_t *v0, const sw_vertex_t *v1, c typedef void (*sw_raster_line_f)(const sw_vertex_t *v0, const sw_vertex_t *v1); typedef void (*sw_raster_point_f)(const sw_vertex_t *v); +// Graphic context data structure typedef struct { sw_default_framebuffer_t framebuffer; // Default framebuffer float clearColor[4]; // Clear color of the framebuffer @@ -1012,18 +1015,18 @@ typedef struct { bool isDirtyMVP; // Indicates if the MVP matrix should be rebuilt sw_handle_t boundFramebufferId; // Framebuffer currently bound - sw_texture_t* colorBuffer; // Color buffer currently bound - sw_texture_t* depthBuffer; // Depth buffer currently bound + sw_texture_t *colorBuffer; // Color buffer currently bound + sw_texture_t *depthBuffer; // Depth buffer currently bound sw_pool_t framebufferPool; // Framebuffer object pool - sw_texture_t* boundTexture; // Texture currently bound + sw_texture_t *boundTexture; // Texture currently bound sw_pool_t texturePool; // Texture object pool - SWfactor srcFactor; - SWfactor dstFactor; + SWfactor srcFactor; // Source blending factor + SWfactor dstFactor; // Destination bleending factor - sw_blend_factor_t srcFactorFunc; - sw_blend_factor_t dstFactorFunc; + sw_blend_factor_t srcFactorFunc; // Source blend function + sw_blend_factor_t dstFactorFunc; // Destination blend function SWface cullFace; // Faces to cull SWerrcode errCode; // Last error code @@ -1242,10 +1245,7 @@ static inline void sw_add_vertex_grad_PTCH(sw_vertex_t *SW_RESTRICT out, const s out->homogeneous[3] += gradients->homogeneous[3]; } -static inline void sw_add_vertex_grad_scaled_PTCH( - sw_vertex_t *SW_RESTRICT out, - const sw_vertex_t *SW_RESTRICT gradients, - float scale) +static inline void sw_add_vertex_grad_scaled_PTCH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT gradients, float scale) { // Add gradients to Position out->position[0] += gradients->position[0]*scale; @@ -1276,13 +1276,13 @@ static inline uint16_t sw_float_to_half_ui(uint32_t ui) int32_t s = (ui >> 16) & 0x8000; int32_t em = ui & 0x7fffffff; - // bias exponent and round to nearest; 112 is relative exponent bias (127-15) + // Bias exponent and round to nearest; 112 is relative exponent bias (127-15) int32_t h = (em - (112 << 23) + (1 << 12)) >> 13; - // underflow: flush to zero; 113 encodes exponent -14 + // Underflow: flush to zero; 113 encodes exponent -14 h = (em < (113 << 23))? 0 : h; - // overflow: infinity; 143 encodes exponent 16 + // Overflow: infinity; 143 encodes exponent 16 h = (em >= (143 << 23))? 0x7c00 : h; // NaN; note that we convert all types of NaN to qNaN @@ -1296,13 +1296,13 @@ static inline uint32_t sw_half_to_float_ui(uint16_t h) uint32_t s = (unsigned)(h & 0x8000) << 16; int32_t em = h & 0x7fff; - // bias exponent and pad mantissa with 0; 112 is relative exponent bias (127-15) + // Bias exponent and pad mantissa with 0; 112 is relative exponent bias (127-15) int32_t r = (em + (112 << 10)) << 13; - // denormal: flush to zero + // Denormal: flush to zero r = (em < (1 << 10))? 0 : r; - // infinity/NaN; note that we preserve NaN payload as a byproduct of unifying inf/nan cases + // Infinity/NaN; note that we preserve NaN payload as a byproduct of unifying inf/nan cases // 112 is an exponent bias fixup; since we already applied it once, applying it twice converts 31 to 255 r += (em >= (31 << 10))? (112 << 23) : 0; @@ -1323,14 +1323,14 @@ static inline float sw_half_to_float(sw_half_t y) return v.f; } -static inline uint8_t sw_expand_1to8(uint32_t v) { return v ? 255 : 0; } +static inline uint8_t sw_expand_1to8(uint32_t v) { return v? 255 : 0; } static inline uint8_t sw_expand_2to8(uint32_t v) { return (uint8_t)(v*85); } static inline uint8_t sw_expand_3to8(uint32_t v) { return (uint8_t)((v << 5) | (v << 2) | (v >> 1)); } static inline uint8_t sw_expand_4to8(uint32_t v) { return (uint8_t)((v << 4) | v); } static inline uint8_t sw_expand_5to8(uint32_t v) { return (uint8_t)((v << 3) | (v >> 2)); } static inline uint8_t sw_expand_6to8(uint32_t v) { return (uint8_t)((v << 2) | (v >> 4)); } -static inline float sw_expand_1tof(uint32_t v) { return v ? 1.0f : 0.0f; } +static inline float sw_expand_1tof(uint32_t v) { return v? 1.0f : 0.0f; } static inline float sw_expand_2tof(uint32_t v) { return (float)v*(1.0f/3.0f); } static inline float sw_expand_3tof(uint32_t v) { return (float)v*(1.0f/7.0f); } static inline float sw_expand_4tof(uint32_t v) { return (float)v*(1.0f/15.0f); } @@ -1344,9 +1344,9 @@ static inline uint32_t sw_compress_8to4(uint8_t v) { return v >> 4; } static inline uint32_t sw_compress_8to5(uint8_t v) { return v >> 3; } static inline uint32_t sw_compress_8to6(uint8_t v) { return v >> 2; } -static inline uint32_t sw_compress_fto1(float v) { return v >= 0.5f ? 1 : 0; } -static inline uint32_t sw_compress_fto2(float v) { return (uint32_t)(v* 3.0f + 0.5f) & 0x03; } -static inline uint32_t sw_compress_fto3(float v) { return (uint32_t)(v* 7.0f + 0.5f) & 0x07; } +static inline uint32_t sw_compress_fto1(float v) { return (v >= 0.5f)? 1 : 0; } +static inline uint32_t sw_compress_fto2(float v) { return (uint32_t)(v*3.0f + 0.5f) & 0x03; } +static inline uint32_t sw_compress_fto3(float v) { return (uint32_t)(v*7.0f + 0.5f) & 0x07; } static inline uint32_t sw_compress_fto4(float v) { return (uint32_t)(v*15.0f + 0.5f) & 0x0F; } static inline uint32_t sw_compress_fto5(float v) { return (uint32_t)(v*31.0f + 0.5f) & 0x1F; } static inline uint32_t sw_compress_fto6(float v) { return (uint32_t)(v*63.0f + 0.5f) & 0x3F; } @@ -1356,7 +1356,7 @@ static inline uint32_t sw_compress_fto6(float v) { return (uint32_t)(v*63.0f + 0 //------------------------------------------------------------------------------------------- static bool sw_pool_init(sw_pool_t *pool, int capacity, size_t stride) { - *pool = (sw_pool_t) { 0 }; + *pool = (sw_pool_t){ 0 }; pool->data = SW_CALLOC(capacity, stride); pool->gen = SW_CALLOC(capacity, sizeof(uint8_t)); @@ -1371,8 +1371,8 @@ static bool sw_pool_init(sw_pool_t *pool, int capacity, size_t stride) } pool->watermark = 1; - pool->capacity = capacity; - pool->stride = stride; + pool->capacity = capacity; + pool->stride = stride; return true; } @@ -1440,7 +1440,7 @@ static inline bool sw_is_texture_valid(sw_handle_t id) return sw_pool_valid(&RLSW.texturePool, id); } -static inline bool sw_is_texture_complete(sw_texture_t* tex) +static inline bool sw_is_texture_complete(sw_texture_t *tex) { return (tex != NULL) && (tex->pixels != NULL); } @@ -1752,48 +1752,20 @@ static inline void sw_pixel_get_color8(uint8_t *SW_RESTRICT color, const void *S { switch (format) { - case SW_PIXELFORMAT_COLOR_GRAYSCALE: - sw_pixel_get_color8_GRAYSCALE(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_GRAYALPHA: - sw_pixel_get_color8_GRAYALPHA(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R3G3B2: - sw_pixel_get_color8_R3G3B2(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R5G6B5: - sw_pixel_get_color8_R5G6B5(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R8G8B8: - sw_pixel_get_color8_R8G8B8(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R5G5B5A1: - sw_pixel_get_color8_R5G5B5A1(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R4G4B4A4: - sw_pixel_get_color8_R4G4B4A4(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R8G8B8A8: - sw_pixel_get_color8_R8G8B8A8(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R32: - sw_pixel_get_color8_R32(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R32G32B32: - sw_pixel_get_color8_R32G32B32(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R32G32B32A32: - sw_pixel_get_color8_R32G32B32A32(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R16: - sw_pixel_get_color8_R16(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R16G16B16: - sw_pixel_get_color8_R16G16B16(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R16G16B16A16: - sw_pixel_get_color8_R16G16B16A16(color, pixels, offset); - break; + case SW_PIXELFORMAT_COLOR_GRAYSCALE: sw_pixel_get_color8_GRAYSCALE(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_GRAYALPHA: sw_pixel_get_color8_GRAYALPHA(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R3G3B2: sw_pixel_get_color8_R3G3B2(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R5G6B5: sw_pixel_get_color8_R5G6B5(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R8G8B8: sw_pixel_get_color8_R8G8B8(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R5G5B5A1: sw_pixel_get_color8_R5G5B5A1(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R4G4B4A4: sw_pixel_get_color8_R4G4B4A4(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R8G8B8A8: sw_pixel_get_color8_R8G8B8A8(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R32: sw_pixel_get_color8_R32(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R32G32B32: sw_pixel_get_color8_R32G32B32(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R32G32B32A32: sw_pixel_get_color8_R32G32B32A32(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R16: sw_pixel_get_color8_R16(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R16G16B16: sw_pixel_get_color8_R16G16B16(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R16G16B16A16: sw_pixel_get_color8_R16G16B16A16(color, pixels, offset); break; case SW_PIXELFORMAT_UNKNOWN: case SW_PIXELFORMAT_DEPTH_D8: @@ -1805,8 +1777,8 @@ static inline void sw_pixel_get_color8(uint8_t *SW_RESTRICT color, const void *S color[1] = 0.0f; color[2] = 0.0f; color[3] = 0.0f; - break; - } + } break; + default: break; } } @@ -1921,55 +1893,27 @@ static inline void sw_pixel_set_color8(void *SW_RESTRICT pixels, const uint8_t * { switch (format) { - case SW_PIXELFORMAT_COLOR_GRAYSCALE: - sw_pixel_set_color8_GRAYSCALE(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_GRAYALPHA: - sw_pixel_set_color8_GRAYALPHA(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R3G3B2: - sw_pixel_set_color8_R3G3B2(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R5G6B5: - sw_pixel_set_color8_R5G6B5(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R8G8B8: - sw_pixel_set_color8_R8G8B8(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R5G5B5A1: - sw_pixel_set_color8_R5G5B5A1(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R4G4B4A4: - sw_pixel_set_color8_R4G4B4A4(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R8G8B8A8: - sw_pixel_set_color8_R8G8B8A8(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R32: - sw_pixel_set_color8_R32(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R32G32B32: - sw_pixel_set_color8_R32G32B32(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R32G32B32A32: - sw_pixel_set_color8_R32G32B32A32(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R16: - sw_pixel_set_color8_R16(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R16G16B16: - sw_pixel_set_color8_R16G16B16(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R16G16B16A16: - sw_pixel_set_color8_R16G16B16A16(pixels, color, offset); - break; + case SW_PIXELFORMAT_COLOR_GRAYSCALE: sw_pixel_set_color8_GRAYSCALE(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_GRAYALPHA: sw_pixel_set_color8_GRAYALPHA(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R3G3B2: sw_pixel_set_color8_R3G3B2(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R5G6B5: sw_pixel_set_color8_R5G6B5(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R8G8B8: sw_pixel_set_color8_R8G8B8(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R5G5B5A1: sw_pixel_set_color8_R5G5B5A1(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R4G4B4A4: sw_pixel_set_color8_R4G4B4A4(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R8G8B8A8: sw_pixel_set_color8_R8G8B8A8(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R32: sw_pixel_set_color8_R32(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R32G32B32: sw_pixel_set_color8_R32G32B32(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R32G32B32A32: sw_pixel_set_color8_R32G32B32A32(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R16: sw_pixel_set_color8_R16(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R16G16B16: sw_pixel_set_color8_R16G16B16(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R16G16B16A16: sw_pixel_set_color8_R16G16B16A16(pixels, color, offset); break; case SW_PIXELFORMAT_UNKNOWN: case SW_PIXELFORMAT_DEPTH_D8: case SW_PIXELFORMAT_DEPTH_D16: case SW_PIXELFORMAT_DEPTH_D32: - case SW_PIXELFORMAT_COUNT: - break; + case SW_PIXELFORMAT_COUNT: break; + default: break; } } @@ -2062,7 +2006,7 @@ static inline void sw_pixel_get_color_R8G8B8A8(float *SW_RESTRICT color, const v _mm_storeu_ps(color, fvals); #elif defined(SW_HAS_RVV) - // TODO: Sample code generated by AI, needs testing and review + // TODO: WARNING: Sample code generated by AI, needs testing and review size_t vl = __riscv_vsetvl_e8m1(4); // Set vector length for 8-bit input elements vuint8m1_t vsrc_u8 = __riscv_vle8_v_u8m1(src, vl); // Load 4 unsigned 8-bit integers vuint32m1_t vsrc_u32 = __riscv_vwcvt_xu_u_v_u32m1(vsrc_u8, vl); // Widen to 32-bit unsigned integers @@ -2136,48 +2080,20 @@ static inline void sw_pixel_get_color(float *SW_RESTRICT color, const void *SW_R { switch (format) { - case SW_PIXELFORMAT_COLOR_GRAYSCALE: - sw_pixel_get_color_GRAYSCALE(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_GRAYALPHA: - sw_pixel_get_color_GRAYALPHA(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R3G3B2: - sw_pixel_get_color_R3G3B2(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R5G6B5: - sw_pixel_get_color_R5G6B5(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R8G8B8: - sw_pixel_get_color_R8G8B8(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R5G5B5A1: - sw_pixel_get_color_R5G5B5A1(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R4G4B4A4: - sw_pixel_get_color_R4G4B4A4(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R8G8B8A8: - sw_pixel_get_color_R8G8B8A8(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R32: - sw_pixel_get_color_R32(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R32G32B32: - sw_pixel_get_color_R32G32B32(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R32G32B32A32: - sw_pixel_get_color_R32G32B32A32(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R16: - sw_pixel_get_color_R16(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R16G16B16: - sw_pixel_get_color_R16G16B16(color, pixels, offset); - break; - case SW_PIXELFORMAT_COLOR_R16G16B16A16: - sw_pixel_get_color_R16G16B16A16(color, pixels, offset); - break; + case SW_PIXELFORMAT_COLOR_GRAYSCALE: sw_pixel_get_color_GRAYSCALE(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_GRAYALPHA: sw_pixel_get_color_GRAYALPHA(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R3G3B2: sw_pixel_get_color_R3G3B2(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R5G6B5: sw_pixel_get_color_R5G6B5(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R8G8B8: sw_pixel_get_color_R8G8B8(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R5G5B5A1: sw_pixel_get_color_R5G5B5A1(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R4G4B4A4: sw_pixel_get_color_R4G4B4A4(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R8G8B8A8: sw_pixel_get_color_R8G8B8A8(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R32: sw_pixel_get_color_R32(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R32G32B32: sw_pixel_get_color_R32G32B32(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R32G32B32A32: sw_pixel_get_color_R32G32B32A32(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R16: sw_pixel_get_color_R16(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R16G16B16: sw_pixel_get_color_R16G16B16(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_R16G16B16A16: sw_pixel_get_color_R16G16B16A16(color, pixels, offset); break; case SW_PIXELFORMAT_UNKNOWN: case SW_PIXELFORMAT_DEPTH_D8: @@ -2189,8 +2105,8 @@ static inline void sw_pixel_get_color(float *SW_RESTRICT color, const void *SW_R color[1] = 0.0f; color[2] = 0.0f; color[3] = 0.0f; - break; - } + } break; + default: break; } } @@ -2277,7 +2193,7 @@ static inline void sw_pixel_set_color_R8G8B8A8(void *SW_RESTRICT pixels, const f _mm_storeu_si32(dst, i8); #elif defined(SW_HAS_RVV) - // TODO: Sample code generated by AI, needs testing and review + // TODO: WARNING: Sample code generated by AI, needs testing and review // REVIEW: It shouldn't perform so many operations; take inspiration from other versions // NOTE: RVV 1.0 specs define the use of __riscv_ prefix for instrinsic functions size_t vl = __riscv_vsetvl_e32m1(4); // Load up to 4 floats into a vector register @@ -2350,55 +2266,26 @@ static inline void sw_pixel_set_color(void *SW_RESTRICT pixels, const float *SW_ { switch (format) { - case SW_PIXELFORMAT_COLOR_GRAYSCALE: - sw_pixel_set_color_GRAYSCALE(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_GRAYALPHA: - sw_pixel_set_color_GRAYALPHA(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R3G3B2: - sw_pixel_set_color_R3G3B2(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R5G6B5: - sw_pixel_set_color_R5G6B5(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R8G8B8: - sw_pixel_set_color_R8G8B8(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R5G5B5A1: - sw_pixel_set_color_R5G5B5A1(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R4G4B4A4: - sw_pixel_set_color_R4G4B4A4(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R8G8B8A8: - sw_pixel_set_color_R8G8B8A8(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R32: - sw_pixel_set_color_R32(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R32G32B32: - sw_pixel_set_color_R32G32B32(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R32G32B32A32: - sw_pixel_set_color_R32G32B32A32(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R16: - sw_pixel_set_color_R16(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R16G16B16: - sw_pixel_set_color_R16G16B16(pixels, color, offset); - break; - case SW_PIXELFORMAT_COLOR_R16G16B16A16: - sw_pixel_set_color_R16G16B16A16(pixels, color, offset); - break; + case SW_PIXELFORMAT_COLOR_GRAYSCALE: sw_pixel_set_color_GRAYSCALE(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_GRAYALPHA: sw_pixel_set_color_GRAYALPHA(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R3G3B2: sw_pixel_set_color_R3G3B2(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R5G6B5: sw_pixel_set_color_R5G6B5(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R8G8B8: sw_pixel_set_color_R8G8B8(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R5G5B5A1: sw_pixel_set_color_R5G5B5A1(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R4G4B4A4: sw_pixel_set_color_R4G4B4A4(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R8G8B8A8: sw_pixel_set_color_R8G8B8A8(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R32: sw_pixel_set_color_R32(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R32G32B32: sw_pixel_set_color_R32G32B32(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R32G32B32A32: sw_pixel_set_color_R32G32B32A32(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R16: sw_pixel_set_color_R16(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R16G16B16: sw_pixel_set_color_R16G16B16(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_R16G16B16A16: sw_pixel_set_color_R16G16B16A16(pixels, color, offset); break; case SW_PIXELFORMAT_UNKNOWN: case SW_PIXELFORMAT_DEPTH_D8: case SW_PIXELFORMAT_DEPTH_D16: case SW_PIXELFORMAT_DEPTH_D32: - case SW_PIXELFORMAT_COUNT: - break; + case SW_PIXELFORMAT_COUNT: break; } } @@ -2421,30 +2308,26 @@ static inline float sw_pixel_get_depth(const void *pixels, uint32_t offset, sw_p { switch (format) { - case SW_PIXELFORMAT_DEPTH_D8: - return sw_pixel_get_depth_D8(pixels, offset); - case SW_PIXELFORMAT_DEPTH_D16: - return sw_pixel_get_depth_D16(pixels, offset); - case SW_PIXELFORMAT_DEPTH_D32: - return sw_pixel_get_depth_D32(pixels, offset); + case SW_PIXELFORMAT_DEPTH_D8: return sw_pixel_get_depth_D8(pixels, offset); + case SW_PIXELFORMAT_DEPTH_D16: return sw_pixel_get_depth_D16(pixels, offset); + case SW_PIXELFORMAT_DEPTH_D32: return sw_pixel_get_depth_D32(pixels, offset); - case SW_PIXELFORMAT_UNKNOWN: - case SW_PIXELFORMAT_COLOR_GRAYSCALE: - case SW_PIXELFORMAT_COLOR_GRAYALPHA: - case SW_PIXELFORMAT_COLOR_R3G3B2: - case SW_PIXELFORMAT_COLOR_R5G6B5: - case SW_PIXELFORMAT_COLOR_R8G8B8: - case SW_PIXELFORMAT_COLOR_R5G5B5A1: - case SW_PIXELFORMAT_COLOR_R4G4B4A4: - case SW_PIXELFORMAT_COLOR_R8G8B8A8: - case SW_PIXELFORMAT_COLOR_R32: - case SW_PIXELFORMAT_COLOR_R32G32B32: - case SW_PIXELFORMAT_COLOR_R32G32B32A32: - case SW_PIXELFORMAT_COLOR_R16: - case SW_PIXELFORMAT_COLOR_R16G16B16: - case SW_PIXELFORMAT_COLOR_R16G16B16A16: - case SW_PIXELFORMAT_COUNT: - break; + case SW_PIXELFORMAT_UNKNOWN: + case SW_PIXELFORMAT_COLOR_GRAYSCALE: + case SW_PIXELFORMAT_COLOR_GRAYALPHA: + case SW_PIXELFORMAT_COLOR_R3G3B2: + case SW_PIXELFORMAT_COLOR_R5G6B5: + case SW_PIXELFORMAT_COLOR_R8G8B8: + case SW_PIXELFORMAT_COLOR_R5G5B5A1: + case SW_PIXELFORMAT_COLOR_R4G4B4A4: + case SW_PIXELFORMAT_COLOR_R8G8B8A8: + case SW_PIXELFORMAT_COLOR_R32: + case SW_PIXELFORMAT_COLOR_R32G32B32: + case SW_PIXELFORMAT_COLOR_R32G32B32A32: + case SW_PIXELFORMAT_COLOR_R16: + case SW_PIXELFORMAT_COLOR_R16G16B16: + case SW_PIXELFORMAT_COLOR_R16G16B16A16: + case SW_PIXELFORMAT_COUNT: break; } return 0.0f; @@ -2469,33 +2352,26 @@ static inline void sw_pixel_set_depth(void *pixels, float depth, uint32_t offset { switch (format) { - case SW_PIXELFORMAT_DEPTH_D8: - sw_pixel_set_depth_D8(pixels, depth, offset); - break; - case SW_PIXELFORMAT_DEPTH_D16: - sw_pixel_set_depth_D16(pixels, depth, offset); - break; - case SW_PIXELFORMAT_DEPTH_D32: - sw_pixel_set_depth_D32(pixels, depth, offset); - break; + case SW_PIXELFORMAT_DEPTH_D8: sw_pixel_set_depth_D8(pixels, depth, offset); break; + case SW_PIXELFORMAT_DEPTH_D16: w_pixel_set_depth_D16(pixels, depth, offset); break; + case SW_PIXELFORMAT_DEPTH_D32: sw_pixel_set_depth_D32(pixels, depth, offset); break; - case SW_PIXELFORMAT_UNKNOWN: - case SW_PIXELFORMAT_COLOR_GRAYSCALE: - case SW_PIXELFORMAT_COLOR_GRAYALPHA: - case SW_PIXELFORMAT_COLOR_R3G3B2: - case SW_PIXELFORMAT_COLOR_R5G6B5: - case SW_PIXELFORMAT_COLOR_R8G8B8: - case SW_PIXELFORMAT_COLOR_R5G5B5A1: - case SW_PIXELFORMAT_COLOR_R4G4B4A4: - case SW_PIXELFORMAT_COLOR_R8G8B8A8: - case SW_PIXELFORMAT_COLOR_R32: - case SW_PIXELFORMAT_COLOR_R32G32B32: - case SW_PIXELFORMAT_COLOR_R32G32B32A32: - case SW_PIXELFORMAT_COLOR_R16: - case SW_PIXELFORMAT_COLOR_R16G16B16: - case SW_PIXELFORMAT_COLOR_R16G16B16A16: - case SW_PIXELFORMAT_COUNT: - break; + case SW_PIXELFORMAT_UNKNOWN: + case SW_PIXELFORMAT_COLOR_GRAYSCALE: + case SW_PIXELFORMAT_COLOR_GRAYALPHA: + case SW_PIXELFORMAT_COLOR_R3G3B2: + case SW_PIXELFORMAT_COLOR_R5G6B5: + case SW_PIXELFORMAT_COLOR_R8G8B8: + case SW_PIXELFORMAT_COLOR_R5G5B5A1: + case SW_PIXELFORMAT_COLOR_R4G4B4A4: + case SW_PIXELFORMAT_COLOR_R8G8B8A8: + case SW_PIXELFORMAT_COLOR_R32: + case SW_PIXELFORMAT_COLOR_R32G32B32: + case SW_PIXELFORMAT_COLOR_R32G32B32A32: + case SW_PIXELFORMAT_COLOR_R16: + case SW_PIXELFORMAT_COLOR_R16G16B16: + case SW_PIXELFORMAT_COLOR_R16G16B16A16: + case SW_PIXELFORMAT_COUNT: break; } } //------------------------------------------------------------------------------------------- @@ -2509,17 +2385,17 @@ static inline bool sw_texture_alloc(sw_texture_t *texture, const void *data, int if (newSize > texture->allocSz) { - void* ptr = SW_REALLOC(texture->pixels, newSize); + void *ptr = SW_REALLOC(texture->pixels, newSize); if (!ptr) { RLSW.errCode = SW_OUT_OF_MEMORY; return false; } texture->allocSz = newSize; texture->pixels = ptr; } - uint8_t* dst = texture->pixels; - const uint8_t* src = data; + uint8_t *dst = texture->pixels; + const uint8_t *src = data; if (data) for (int i = 0; i < newSize; i++) dst[i] = src[i]; - else for (int i = 0; i < newSize; i++) dst[i] = 0; + else for (int i = 0; i < newSize; i++) dst[i] = 0; texture->format = format; texture->width = w; @@ -2635,7 +2511,7 @@ static inline void sw_texture_sample(float *SW_RESTRICT color, const sw_texture_ // Framebuffer management functions //------------------------------------------------------------------------------------------- -static inline bool sw_default_framebuffer_alloc(sw_default_framebuffer_t* fb, int w, int h) +static inline bool sw_default_framebuffer_alloc(sw_default_framebuffer_t *fb, int w, int h) { if (!sw_texture_alloc(&fb->color, NULL, w, h, SW_FRAMEBUFFER_COLOR_FORMAT)) { @@ -2650,7 +2526,7 @@ static inline bool sw_default_framebuffer_alloc(sw_default_framebuffer_t* fb, in return true; } -static inline void sw_default_framebuffer_free(sw_default_framebuffer_t* fb) +static inline void sw_default_framebuffer_free(sw_default_framebuffer_t *fb) { sw_texture_free(&fb->color); sw_texture_free(&fb->depth); @@ -2736,9 +2612,9 @@ static inline void sw_framebuffer_fill_depth(sw_texture_t *depthBuffer, float de } } -static inline void sw_framebuffer_output_fast(void* dst, const sw_texture_t* buffer) +static inline void sw_framebuffer_output_fast(void *dst, const sw_texture_t *buffer) { - int width = buffer->width; + int width = buffer->width; int height = buffer->height; uint8_t *d = (uint8_t *)dst; @@ -2777,7 +2653,7 @@ static inline void sw_framebuffer_output_fast(void* dst, const sw_texture_t* buf #endif } -static inline void sw_framebuffer_output_copy(void* dst, const sw_texture_t* buffer, int x, int y, int w, int h, sw_pixelformat_t format) +static inline void sw_framebuffer_output_copy(void *dst, const sw_texture_t *buffer, int x, int y, int w, int h, sw_pixelformat_t format) { int dstPixelSize = SW_PIXELFORMAT_SIZE[format]; int stride = buffer->width; @@ -2808,15 +2684,12 @@ static inline void sw_framebuffer_output_copy(void* dst, const sw_texture_t* buf } src -= stride*SW_FRAMEBUFFER_COLOR_SIZE; - d += w*dstPixelSize; + d += w*dstPixelSize; } } -static inline void sw_framebuffer_output_blit( - void* dst, const sw_texture_t* buffer, - int xDst, int yDst, int wDst, int hDst, - int xSrc, int ySrc, int wSrc, int hSrc, - sw_pixelformat_t format) +static inline void sw_framebuffer_output_blit(void *dst, const sw_texture_t *buffer, + int xDst, int yDst, int wDst, int hDst, int xSrc, int ySrc, int wSrc, int hSrc, sw_pixelformat_t format) { const uint8_t *srcBase = buffer->pixels; @@ -3692,7 +3565,8 @@ static inline bool sw_line_clip_coord(float q, float p, float *t0, float *t1) static bool sw_line_clip(sw_vertex_t *v0, sw_vertex_t *v1) { float t0 = 0.0f, t1 = 1.0f; - float dH[4], dC[4]; + float dH[4] = { 0 } + float dC[4] = { 0 }; for (int i = 0; i < 4; i++) { @@ -4460,7 +4334,7 @@ void swLoadIdentity(void) void swTranslatef(float x, float y, float z) { - sw_matrix_t mat; + sw_matrix_t mat = { 0 }; sw_matrix_id(mat); mat[12] = x; @@ -4490,7 +4364,7 @@ void swRotatef(float angle, float x, float y, float z) float cosres = cosf(angle); float t = 1.0f - cosres; - sw_matrix_t mat; + sw_matrix_t mat = { 0 }; mat[0] = x*x*t + cosres; mat[1] = y*x*t + z*sinres; @@ -4519,7 +4393,7 @@ void swRotatef(float angle, float x, float y, float z) void swScalef(float x, float y, float z) { - sw_matrix_t mat; + sw_matrix_t mat = { 0 }; mat[0] = x, mat[1] = 0, mat[2] = 0, mat[3] = 0; mat[4] = 0, mat[5] = y, mat[6] = 0, mat[7] = 0; @@ -4540,7 +4414,7 @@ void swMultMatrixf(const float *mat) void swFrustum(double left, double right, double bottom, double top, double znear, double zfar) { - sw_matrix_t mat; + sw_matrix_t mat = { 0 }; double rl = right - left; double tb = top - bottom; @@ -4573,7 +4447,7 @@ void swFrustum(double left, double right, double bottom, double top, double znea void swOrtho(double left, double right, double bottom, double top, double znear, double zfar) { - sw_matrix_t mat; + sw_matrix_t mat = { 0 }; double rl = right - left; double tb = top - bottom; @@ -4695,7 +4569,7 @@ void swVertex4fv(const float *v) void swColor3ub(uint8_t r, uint8_t g, uint8_t b) { - float cv[4]; + float cv[4] = { 0 }; cv[0] = (float)r*SW_INV_255; cv[1] = (float)g*SW_INV_255; cv[2] = (float)b*SW_INV_255; @@ -4706,7 +4580,7 @@ void swColor3ub(uint8_t r, uint8_t g, uint8_t b) void swColor3ubv(const uint8_t *v) { - float cv[4]; + float cv[4] = { 0 }; cv[0] = (float)v[0]*SW_INV_255; cv[1] = (float)v[1]*SW_INV_255; cv[2] = (float)v[2]*SW_INV_255; @@ -4717,7 +4591,7 @@ void swColor3ubv(const uint8_t *v) void swColor3f(float r, float g, float b) { - float cv[4]; + float cv[4] = { 0 }; cv[0] = r; cv[1] = g; cv[2] = b; @@ -4728,7 +4602,7 @@ void swColor3f(float r, float g, float b) void swColor3fv(const float *v) { - float cv[4]; + float cv[4] = { 0 }; cv[0] = v[0]; cv[1] = v[1]; cv[2] = v[2]; @@ -4739,7 +4613,7 @@ void swColor3fv(const float *v) void swColor4ub(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { - float cv[4]; + float cv[4] = { 0 }; cv[0] = (float)r*SW_INV_255; cv[1] = (float)g*SW_INV_255; cv[2] = (float)b*SW_INV_255; @@ -4750,7 +4624,7 @@ void swColor4ub(uint8_t r, uint8_t g, uint8_t b, uint8_t a) void swColor4ubv(const uint8_t *v) { - float cv[4]; + float cv[4] = { 0 }; cv[0] = (float)v[0]*SW_INV_255; cv[1] = (float)v[1]*SW_INV_255; cv[2] = (float)v[2]*SW_INV_255; @@ -4761,7 +4635,7 @@ void swColor4ubv(const uint8_t *v) void swColor4f(float r, float g, float b, float a) { - float cv[4]; + float cv[4] = { 0 }; cv[0] = r; cv[1] = g; cv[2] = b; @@ -4891,18 +4765,10 @@ void swDrawElements(SWdraw mode, int count, int type, const void *indices) switch (type) { - case SW_UNSIGNED_BYTE: - indicesUb = (const uint8_t *)indices; - break; - case SW_UNSIGNED_SHORT: - indicesUs = (const uint16_t *)indices; - break; - case SW_UNSIGNED_INT: - indicesUi = (const uint32_t *)indices; - break; - default: - RLSW.errCode = SW_INVALID_ENUM; - return; + case SW_UNSIGNED_BYTE: indicesUb = (const uint8_t *)indices; break; + case SW_UNSIGNED_SHORT: indicesUs = (const uint16_t *)indices; break; + case SW_UNSIGNED_INT: indicesUi = (const uint32_t *)indices; break; + default: RLSW.errCode = SW_INVALID_ENUM; return; } swBegin(mode); @@ -4992,7 +4858,7 @@ void swDeleteTextures(int count, sw_handle_t *textures) if (tex == RLSW.depthBuffer) RLSW.depthBuffer = NULL; sw_texture_free(tex); - *tex = (sw_texture_t) { 0 }; + *tex = (sw_texture_t){ 0 }; sw_pool_free(&RLSW.texturePool, textures[i]); } @@ -5030,9 +4896,7 @@ void swTexStorage2D(int width, int height, SWinternalformat format) case SW_DEPTH_COMPONENT24: pixelFormat = SW_PIXELFORMAT_DEPTH_D32; break; case SW_DEPTH_COMPONENT32: pixelFormat = SW_PIXELFORMAT_DEPTH_D32; break; case SW_DEPTH_COMPONENT32F: pixelFormat = SW_PIXELFORMAT_DEPTH_D32; break; - default: - RLSW.errCode = SW_INVALID_ENUM; - return; + default: RLSW.errCode = SW_INVALID_ENUM; return; } (void)sw_texture_alloc(RLSW.boundTexture, NULL, width, height, pixelFormat); @@ -5072,8 +4936,8 @@ void swTexSubImage2D(GLint x, GLint y, GLsizei width, GLsizei height, GLenum for const int srcPixelSize = SW_PIXELFORMAT_SIZE[pFormat]; const int dstPixelSize = SW_PIXELFORMAT_SIZE[RLSW.boundTexture->format]; - const uint8_t* srcBytes = (const uint8_t *)pixels; - uint8_t* dstBytes = (uint8_t *)RLSW.boundTexture->pixels; + const uint8_t *srcBytes = (const uint8_t *)pixels; + uint8_t *dstBytes = (uint8_t *)RLSW.boundTexture->pixels; if (pFormat == RLSW.boundTexture->format) { @@ -5114,32 +4978,30 @@ void swTexParameteri(int param, int value) switch (param) { case SW_TEXTURE_MIN_FILTER: + { if (!sw_is_texture_filter_valid(value)) { RLSW.errCode = SW_INVALID_ENUM; return; } RLSW.boundTexture->minFilter = (SWfilter)value; - break; - + } break; case SW_TEXTURE_MAG_FILTER: + { if (!sw_is_texture_filter_valid(value)) { RLSW.errCode = SW_INVALID_ENUM; return; } RLSW.boundTexture->magFilter = (SWfilter)value; - break; - + } break; case SW_TEXTURE_WRAP_S: + { if (!sw_is_texture_wrap_valid(value)) { RLSW.errCode = SW_INVALID_ENUM; return; } RLSW.boundTexture->sWrap = (SWwrap)value; - break; - + } break; case SW_TEXTURE_WRAP_T: + { if (!sw_is_texture_wrap_valid(value)) { RLSW.errCode = SW_INVALID_ENUM; return; } RLSW.boundTexture->tWrap = (SWwrap)value; - break; - - default: - RLSW.errCode = SW_INVALID_ENUM; - break; + } break; + default: RLSW.errCode = SW_INVALID_ENUM; break; } } -void swGenFramebuffers(int count, uint32_t* framebuffers) +void swGenFramebuffers(int count, uint32_t *framebuffers) { if (!count || !framebuffers) return; @@ -5151,7 +5013,7 @@ void swGenFramebuffers(int count, uint32_t* framebuffers) } } -void swDeleteFramebuffers(int count, uint32_t* framebuffers) +void swDeleteFramebuffers(int count, uint32_t *framebuffers) { if (!count || !framebuffers) return; @@ -5181,7 +5043,7 @@ void swBindFramebuffer(uint32_t id) return; } - sw_framebuffer_t* fb = sw_pool_get(&RLSW.framebufferPool, id); + sw_framebuffer_t *fb = sw_pool_get(&RLSW.framebufferPool, id); if (fb == NULL) { RLSW.errCode = SW_INVALID_VALUE; @@ -5201,64 +5063,43 @@ void swFramebufferTexture2D(SWattachment attach, uint32_t texture) return; } - sw_framebuffer_t* fb = sw_pool_get(&RLSW.framebufferPool, RLSW.boundFramebufferId); + sw_framebuffer_t *fb = sw_pool_get(&RLSW.framebufferPool, RLSW.boundFramebufferId); if (fb == NULL) return; // Should never happen switch (attach) { case SW_COLOR_ATTACHMENT: + { fb->colorAttachment = texture; RLSW.colorBuffer = sw_pool_get(&RLSW.texturePool, texture); - break; + } break; case SW_DEPTH_ATTACHMENT: + { fb->depthAttachment = texture; RLSW.depthBuffer = sw_pool_get(&RLSW.texturePool, texture); - break; - default: - RLSW.errCode = SW_INVALID_ENUM; - break; + } break; + default: RLSW.errCode = SW_INVALID_ENUM; break; } } SWfbstatus swCheckFramebufferStatus(void) { - if (RLSW.boundFramebufferId == SW_HANDLE_NULL) - { - return SW_FRAMEBUFFER_COMPLETE; - } + if (RLSW.boundFramebufferId == SW_HANDLE_NULL) return SW_FRAMEBUFFER_COMPLETE; - if (RLSW.colorBuffer == NULL) - { - return SW_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; - } + if (RLSW.colorBuffer == NULL) return SW_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; - if (!sw_is_texture_complete(RLSW.colorBuffer)) - { - return SW_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; - } + if (!sw_is_texture_complete(RLSW.colorBuffer)) return SW_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; - if (RLSW.colorBuffer->format != SW_FRAMEBUFFER_COLOR_FORMAT) - { - return SW_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; - } + if (RLSW.colorBuffer->format != SW_FRAMEBUFFER_COLOR_FORMAT) return SW_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; if (RLSW.depthBuffer) { - if (!sw_is_texture_complete(RLSW.depthBuffer)) - { - return SW_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; - } + if (!sw_is_texture_complete(RLSW.depthBuffer)) return SW_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; - if (RLSW.depthBuffer->format != SW_FRAMEBUFFER_DEPTH_FORMAT) - { - return SW_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; - } + if (RLSW.depthBuffer->format != SW_FRAMEBUFFER_DEPTH_FORMAT) return SW_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; if ((RLSW.colorBuffer->width != RLSW.depthBuffer->width) || - (RLSW.colorBuffer->height != RLSW.depthBuffer->height)) - { - return SW_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; - } + (RLSW.colorBuffer->height != RLSW.depthBuffer->height)) return SW_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; } return SW_FRAMEBUFFER_COMPLETE; @@ -5278,30 +5119,18 @@ void swGetFramebufferAttachmentParameteriv(SWattachment attachment, SWattachget return; } - sw_framebuffer_t* fb = sw_pool_get(&RLSW.framebufferPool, RLSW.boundFramebufferId); + sw_framebuffer_t *fb = sw_pool_get(&RLSW.framebufferPool, RLSW.boundFramebufferId); if (fb == NULL) return; // Should never happen switch (property) { case SW_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: { - switch (attachment) - { - case SW_COLOR_ATTACHMENT: - *v = fb->colorAttachment; - break; - case SW_DEPTH_ATTACHMENT: - *v = fb->depthAttachment; - break; - } - } - break; - - case SW_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: - { - *v = GL_TEXTURE; - break; - } + if (attachment == SW_COLOR_ATTACHMENT) *v = fb->colorAttachment; + else if (attachment == SW_DEPTH_ATTACHMENT) *v = fb->depthAttachment; + } break; + case SW_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: *v = GL_TEXTURE; break; + default: break; } } @@ -5328,7 +5157,7 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t { // Gets the start and end coordinates int xStart = (int)start->screen[0]; - int xEnd = (int)end->screen[0]; + int xEnd = (int)end->screen[0]; // Avoid empty lines if (xStart == xEnd) return; @@ -5538,10 +5367,8 @@ static void SW_RASTER_TRIANGLE(const sw_vertex_t *v0, const sw_vertex_t *v1, con { vLeft.screen[1] = vRight.screen[1] = y; - if (vLeft.screen[0] < vRight.screen[0]) - SW_RASTER_TRIANGLE_SPAN(&vLeft, &vRight, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); - else - SW_RASTER_TRIANGLE_SPAN(&vRight, &vLeft, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); + if (vLeft.screen[0] < vRight.screen[0]) SW_RASTER_TRIANGLE_SPAN(&vLeft, &vRight, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); + else SW_RASTER_TRIANGLE_SPAN(&vRight, &vLeft, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); sw_add_vertex_grad_PTCH(&vLeft, &dVXdy02); vLeft.screen[0] += dXdy02; From 18756bb79d35f79de28e755d9bf763947b1922ca Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Mar 2026 18:33:15 +0100 Subject: [PATCH 183/319] REVIEWED: Software renderer flag, renamed to `GRAPHICS_API_OPENGL_SOFTWARE` Dropped the `11` relative to OpenGL 1.1 because latest `rlsw 1.5` also includes support for FBOs and could potentially implemented other higher level features in the feature, discerning from OpenGL 1.1 limitations --- src/platforms/rcore_desktop_sdl.c | 10 +++---- src/platforms/rcore_desktop_win32.c | 14 +++++----- src/platforms/rcore_drm.c | 18 ++++++------ src/platforms/rcore_memory.c | 4 +-- src/platforms/rcore_web.c | 6 ++-- src/platforms/rcore_web_emscripten.c | 4 +-- src/rlgl.h | 42 ++++++++++++++-------------- src/rmodels.c | 2 +- 8 files changed, 50 insertions(+), 50 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 9b6bea987..0cf18a15d 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -60,7 +60,7 @@ #include "SDL.h" #endif -#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if !defined(GRAPHICS_API_OPENGL_SOFTWARE) #if defined(GRAPHICS_API_OPENGL_ES2) // It seems it does not need to be included to work //#include "SDL_opengles2.h" @@ -1259,7 +1259,7 @@ void DisableCursor(void) // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { -#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if defined(GRAPHICS_API_OPENGL_SOFTWARE) // NOTE: Using a preprocessor condition here because rlCopyFramebuffer() is only declared for software rendering SDL_Surface *surface = SDL_GetWindowSurface(platform.window); rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, surface->pixels); @@ -1981,7 +1981,7 @@ int InitPlatform(void) // NOTE: Some OpenGL context attributes must be set before window creation - if (rlGetVersion() != RL_OPENGL_11_SOFTWARE) + if (rlGetVersion() != RL_OPENGL_SOFTWARE) { // Add the flag telling the window to use an OpenGL context flags |= SDL_WINDOW_OPENGL; @@ -2044,12 +2044,12 @@ int InitPlatform(void) #endif // Init OpenGL context - if (rlGetVersion() != RL_OPENGL_11_SOFTWARE) + if (rlGetVersion() != RL_OPENGL_SOFTWARE) { platform.glContext = SDL_GL_CreateContext(platform.window); } - if ((platform.window != NULL) && ((rlGetVersion() == RL_OPENGL_11_SOFTWARE) || (platform.glContext != NULL))) + if ((platform.window != NULL) && ((rlGetVersion() == RL_OPENGL_SOFTWARE) || (platform.glContext != NULL))) { CORE.Window.ready = true; diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index 1cf8826aa..612800b83 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -71,7 +71,7 @@ #include // Required for alloca() -#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if !defined(GRAPHICS_API_OPENGL_SOFTWARE) #include #endif @@ -1218,7 +1218,7 @@ void SwapScreenBuffer(void) { if (!platform.hdc) abort(); -#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if defined(GRAPHICS_API_OPENGL_SOFTWARE) // Update framebuffer rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, platform.pixels); @@ -1603,7 +1603,7 @@ int InitPlatform(void) // NOTE: Windows GDI object that represents a drawing surface platform.hdc = GetDC(platform.hwnd); - if (rlGetVersion() == RL_OPENGL_11_SOFTWARE) // Using software renderer + if (rlGetVersion() == RL_OPENGL_SOFTWARE) // Using software renderer { // Initialize software framebuffer BITMAPINFO bmi = { 0 }; @@ -1649,11 +1649,11 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); - if (rlGetVersion() == RL_OPENGL_11_SOFTWARE) // Using software renderer + if (rlGetVersion() == RL_OPENGL_SOFTWARE) // Using software renderer { TRACELOG(LOG_INFO, "GL: OpenGL device information:"); TRACELOG(LOG_INFO, " > Vendor: %s", "raylib"); - TRACELOG(LOG_INFO, " > Renderer: %s", "rlsw - OpenGL 1.1 Software Renderer"); + TRACELOG(LOG_INFO, " > Renderer: %s", "rlsw - OpenGL Software Renderer"); TRACELOG(LOG_INFO, " > Version: %s", "1.0"); TRACELOG(LOG_INFO, " > GLSL: %s", "NOT SUPPORTED"); } @@ -1719,7 +1719,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara case WM_DESTROY: { // Clean up for window destruction - if (rlGetVersion() == RL_OPENGL_11_SOFTWARE) // Using software renderer + if (rlGetVersion() == RL_OPENGL_SOFTWARE) // Using software renderer { if (platform.hdcmem) { @@ -1935,7 +1935,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara } break; case WM_PAINT: { - if (rlGetVersion() == RL_OPENGL_11_SOFTWARE) // Using software renderer + if (rlGetVersion() == RL_OPENGL_SOFTWARE) // Using software renderer { PAINTSTRUCT ps = { 0 }; HDC hdc = BeginPaint(hwnd, &ps); diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index a32934aa9..faba8956a 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -65,7 +65,7 @@ #include // Direct Rendering Manager user-level library interface #include // Direct Rendering Manager mode setting (KMS) interface -#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if !defined(GRAPHICS_API_OPENGL_SOFTWARE) #include // Generic Buffer Management (native platform for EGL on DRM) #include "EGL/egl.h" // Native platform windowing system interface #include "EGL/eglext.h" // EGL extensions @@ -111,7 +111,7 @@ typedef struct { int modeIndex; // Index of the used mode of connector->modes uint32_t prevFB; // Previous DRM framebufer (during frame swapping) -#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if !defined(GRAPHICS_API_OPENGL_SOFTWARE) struct gbm_device *gbmDevice; // GBM device struct gbm_surface *gbmSurface; // GBM surface struct gbm_bo *prevBO; // Previous GBM buffer object (during frame swapping) @@ -796,7 +796,7 @@ void SwapScreenBuffer() // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { -#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if !defined(GRAPHICS_API_OPENGL_SOFTWARE) // Hardware rendering buffer swap with EGL eglSwapBuffers(platform.device, platform.surface); @@ -1140,7 +1140,7 @@ int InitPlatform(void) platform.crtc = NULL; platform.prevFB = 0; -#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if !defined(GRAPHICS_API_OPENGL_SOFTWARE) platform.gbmDevice = NULL; platform.gbmSurface = NULL; platform.prevBO = NULL; @@ -1224,7 +1224,7 @@ int InitPlatform(void) // WARNING: Accept CONNECTED, UNKNOWN and even those without encoder_id connectors for software mode if (((con->connection == DRM_MODE_CONNECTED) || (con->connection == DRM_MODE_UNKNOWNCONNECTION)) && (con->count_modes > 0)) { -#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if !defined(GRAPHICS_API_OPENGL_SOFTWARE) // For hardware rendering, an encoder_id is needed if (con->encoder_id) { @@ -1256,7 +1256,7 @@ int InitPlatform(void) return -1; } -#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if !defined(GRAPHICS_API_OPENGL_SOFTWARE) drmModeEncoder *enc = drmModeGetEncoder(platform.fd, platform.connector->encoder_id); if (!enc) { @@ -1367,7 +1367,7 @@ int InitPlatform(void) drmModeFreeResources(res); res = NULL; -#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if !defined(GRAPHICS_API_OPENGL_SOFTWARE) // Hardware rendering initialization with EGL platform.gbmDevice = gbm_create_device(platform.fd); if (!platform.gbmDevice) @@ -1657,7 +1657,7 @@ void ClosePlatform(void) platform.prevFB = 0; } -#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if !defined(GRAPHICS_API_OPENGL_SOFTWARE) if (platform.prevBO) { gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); @@ -1697,7 +1697,7 @@ void ClosePlatform(void) platform.fd = -1; } -#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if !defined(GRAPHICS_API_OPENGL_SOFTWARE) // Close surface, context and display if (platform.device != EGL_NO_DISPLAY) { diff --git a/src/platforms/rcore_memory.c b/src/platforms/rcore_memory.c index 322841439..1512e7bf4 100644 --- a/src/platforms/rcore_memory.c +++ b/src/platforms/rcore_memory.c @@ -486,9 +486,9 @@ void PollInputEvents(void) int InitPlatform(void) { // Memory framebuffer can only work with software renderer - if (rlGetVersion() != RL_OPENGL_11_SOFTWARE) + if (rlGetVersion() != RL_OPENGL_SOFTWARE) { - TRACELOG(LOG_WARNING, "DISPLAY: Memory platform requires software renderer (GRAPHICS_API_OPENGL_11_SOFTWARE)"); + TRACELOG(LOG_WARNING, "DISPLAY: Memory platform requires software renderer (GRAPHICS_API_OPENGL_SOFTWARE)"); TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); return -1; } diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 62b2afda9..8835ecdcc 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -77,7 +77,7 @@ typedef struct { char canvasId[64]; // Keep current canvas id where wasm app is running // NOTE: Useful when trying to run multiple wasms in different canvases in same webpage -#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if defined(GRAPHICS_API_OPENGL_SOFTWARE) unsigned int *pixels; // Pointer to pixel data buffer (RGBA 32bit format) #endif } PlatformData; @@ -949,7 +949,7 @@ void DisableCursor(void) // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { -#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if defined(GRAPHICS_API_OPENGL_SOFTWARE) // Update framebuffer rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, platform.pixels); @@ -1306,7 +1306,7 @@ int InitPlatform(void) // Init fullscreen toggle required var: platform.ourFullscreen = false; -#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if defined(GRAPHICS_API_OPENGL_SOFTWARE) // Avoid creating a WebGL canvas, avoid calling glfwCreateWindow() emscripten_set_canvas_element_size(platform.canvasId, CORE.Window.screen.width, CORE.Window.screen.height); EM_ASM({ diff --git a/src/platforms/rcore_web_emscripten.c b/src/platforms/rcore_web_emscripten.c index 60b647f60..3b6064255 100644 --- a/src/platforms/rcore_web_emscripten.c +++ b/src/platforms/rcore_web_emscripten.c @@ -927,7 +927,7 @@ void DisableCursor(void) // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { -#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if defined(GRAPHICS_API_OPENGL_SOFTWARE) // Update framebuffer rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, platform.pixels); @@ -1188,7 +1188,7 @@ int InitPlatform(void) if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) attribs.antialias = EM_TRUE; // Check selection OpenGL version - if (rlGetVersion() == RL_OPENGL_11_SOFTWARE) + if (rlGetVersion() == RL_OPENGL_SOFTWARE) { // Avoid creating a WebGL canvas, create 2d canvas for software rendering emscripten_set_canvas_element_size(platform.canvasId, CORE.Window.screen.width, CORE.Window.screen.height); diff --git a/src/rlgl.h b/src/rlgl.h index df5335790..15ee58b0c 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -21,7 +21,7 @@ * Internal buffer (and resources) must be manually unloaded calling rlglClose() * * CONFIGURATION: -* #define GRAPHICS_API_OPENGL_11_SOFTWARE +* #define GRAPHICS_API_OPENGL_SOFTWARE * #define GRAPHICS_API_OPENGL_11 * #define GRAPHICS_API_OPENGL_21 * #define GRAPHICS_API_OPENGL_33 @@ -145,7 +145,7 @@ #endif // Security check in case no GRAPHICS_API_OPENGL_* defined -#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) && \ +#if !defined(GRAPHICS_API_OPENGL_SOFTWARE) && \ !defined(GRAPHICS_API_OPENGL_11) && \ !defined(GRAPHICS_API_OPENGL_21) && \ !defined(GRAPHICS_API_OPENGL_33) && \ @@ -156,7 +156,7 @@ #endif // Security check in case multiple GRAPHICS_API_OPENGL_* defined -#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_SOFTWARE) #if defined(GRAPHICS_API_OPENGL_21) #undef GRAPHICS_API_OPENGL_21 #endif @@ -172,7 +172,7 @@ #endif // Software implementation uses OpenGL 1.1 functionality -#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if defined(GRAPHICS_API_OPENGL_SOFTWARE) #define GRAPHICS_API_OPENGL_11 #endif @@ -423,7 +423,7 @@ typedef struct rlRenderBatch { // OpenGL version typedef enum { - RL_OPENGL_11_SOFTWARE = 0, // Software rendering + RL_OPENGL_SOFTWARE = 0, // Software rendering RL_OPENGL_11, // OpenGL 1.1 RL_OPENGL_21, // OpenGL 2.1 (GLSL 120) RL_OPENGL_33, // OpenGL 3.3 (GLSL 330) @@ -835,7 +835,7 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #endif #if defined(GRAPHICS_API_OPENGL_11) - #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + #if defined(GRAPHICS_API_OPENGL_SOFTWARE) #define RLSW_IMPLEMENTATION #define SW_MALLOC(sz) RL_MALLOC(sz) #define SW_CALLOC(n,sz) RL_CALLOC(n, sz) @@ -1858,7 +1858,7 @@ void rlDisableShader(void) // Enable rendering to texture (fbo) void rlEnableFramebuffer(unsigned int id) { -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE)) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_SOFTWARE)) glBindFramebuffer(GL_FRAMEBUFFER, id); #endif } @@ -1867,7 +1867,7 @@ void rlEnableFramebuffer(unsigned int id) unsigned int rlGetActiveFramebuffer(void) { GLint fboId = 0; -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE)) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3) || defined(GRAPHICS_API_OPENGL_SOFTWARE)) glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &fboId); #endif return fboId; @@ -1876,7 +1876,7 @@ unsigned int rlGetActiveFramebuffer(void) // Disable rendering to texture void rlDisableFramebuffer(void) { -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE)) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_SOFTWARE)) glBindFramebuffer(GL_FRAMEBUFFER, 0); #endif } @@ -1892,7 +1892,7 @@ void rlBlitFramebuffer(int srcX, int srcY, int srcWidth, int srcHeight, int dstX // Bind framebuffer object (fbo) void rlBindFramebuffer(unsigned int target, unsigned int framebuffer) { -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE)) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_SOFTWARE)) glBindFramebuffer(target, framebuffer); #endif } @@ -2325,7 +2325,7 @@ void rlglInit(int width, int height) RLGL.State.currentMatrix = &RLGL.State.modelview; #endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 -#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if defined(GRAPHICS_API_OPENGL_SOFTWARE) // Initialize software renderer backend int result = swInit(width, height); if (result == 0) @@ -2387,7 +2387,7 @@ void rlglClose(void) TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Default texture unloaded successfully", RLGL.State.defaultTextureId); #endif -#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if defined(GRAPHICS_API_OPENGL_SOFTWARE) swClose(); // Unload sofware renderer resources #endif isGpuReady = false; @@ -2702,8 +2702,8 @@ int rlGetVersion(void) { int glVersion = 0; -#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) - glVersion = RL_OPENGL_11_SOFTWARE; +#if defined(GRAPHICS_API_OPENGL_SOFTWARE) + glVersion = RL_OPENGL_SOFTWARE; #elif defined(GRAPHICS_API_OPENGL_11) glVersion = RL_OPENGL_11; #endif @@ -3472,7 +3472,7 @@ unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer) TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Depth renderbuffer loaded successfully (%i bits)", id, (RLGL.ExtSupported.maxDepthBits >= 24)? RLGL.ExtSupported.maxDepthBits : 16); } -#elif defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#elif defined(GRAPHICS_API_OPENGL_SOFTWARE) // NOTE: Renderbuffers are the same type of object as textures in rlsw // WARNING: Ensure that the depth format is the one specified at rlsw compilation glGenRenderbuffers(1, &id); @@ -3776,7 +3776,7 @@ void *rlReadTexturePixels(unsigned int id, int width, int height, int format) // Copy framebuffer pixel data to internal buffer void rlCopyFramebuffer(int x, int y, int width, int height, int format, void *pixels) { -#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if defined(GRAPHICS_API_OPENGL_SOFTWARE) unsigned int glInternalFormat, glFormat, glType; rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); // Get OpenGL texture format swReadPixels(x, y, width, height, glFormat, glType, pixels); @@ -3786,7 +3786,7 @@ void rlCopyFramebuffer(int x, int y, int width, int height, int format, void *pi // Resize internal framebuffer void rlResizeFramebuffer(int width, int height) { -#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if defined(GRAPHICS_API_OPENGL_SOFTWARE) swResize(width, height); #endif } @@ -3837,7 +3837,7 @@ unsigned int rlLoadFramebuffer(void) unsigned int fboId = 0; if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return fboId; } -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE)) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_SOFTWARE)) glGenFramebuffers(1, &fboId); // Create the framebuffer object glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind any framebuffer #endif @@ -3849,7 +3849,7 @@ unsigned int rlLoadFramebuffer(void) // NOTE: Attach type: 0-Color, 1-Depth renderbuffer, 2-Depth texture void rlFramebufferAttach(unsigned int id, unsigned int texId, int attachType, int texType, int mipLevel) { -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE)) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_SOFTWARE)) glBindFramebuffer(GL_FRAMEBUFFER, id); switch (attachType) @@ -3889,7 +3889,7 @@ bool rlFramebufferComplete(unsigned int id) { bool result = false; -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE)) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_SOFTWARE)) glBindFramebuffer(GL_FRAMEBUFFER, id); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -3920,7 +3920,7 @@ bool rlFramebufferComplete(unsigned int id) // NOTE: All attached textures/cubemaps/renderbuffers are also deleted void rlUnloadFramebuffer(unsigned int id) { -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE)) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_SOFTWARE)) // Query depth attachment to automatically delete texture/renderbuffer int depthType = 0; glBindFramebuffer(GL_FRAMEBUFFER, id); // Bind framebuffer to query depth texture type diff --git a/src/rmodels.c b/src/rmodels.c index 1c7a5452c..ba28eb839 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1430,7 +1430,7 @@ void UpdateMeshBuffer(Mesh mesh, int index, const void *data, int dataSize, int // Draw a 3d mesh with material and transform void DrawMesh(Mesh mesh, Material material, Matrix transform) { -#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_SOFTWARE) #define GL_VERTEX_ARRAY 0x8074 #define GL_NORMAL_ARRAY 0x8075 #define GL_COLOR_ARRAY 0x8076 From fbce5e75415de04782fb3f33bad52a1f339a951f Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Mar 2026 18:33:53 +0100 Subject: [PATCH 184/319] Bump rlgl version to 6.0, after the low-level shaders API redesign, aligned with `raylib 6.0` --- src/rlgl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 15ee58b0c..13c037132 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rlgl v5.0 - A multi-OpenGL abstraction layer with an immediate-mode style API +* rlgl v6.0 - A multi-OpenGL abstraction layer with an immediate-mode style API * * DESCRIPTION: * An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0, ES 3.0) @@ -107,7 +107,7 @@ #ifndef RLGL_H #define RLGL_H -#define RLGL_VERSION "5.0" +#define RLGL_VERSION "6.0" // Function specifiers in case library is build/used as a shared library // NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll From 48a34d63ede200fd2ae733eeedb537ff0753b0a2 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Mar 2026 18:35:33 +0100 Subject: [PATCH 185/319] Update rlsw.h --- src/external/rlsw.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/external/rlsw.h b/src/external/rlsw.h index b22bf694b..1166e3448 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -87,7 +87,7 @@ #ifndef RLSW_H #define RLSW_H -#define RLGL_VERSION "1.5" +#define RLSW_VERSION "1.5" #include #include From 0de2e8092bf0685e777cf0a97c4f4c059a896173 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Mar 2026 19:38:29 +0100 Subject: [PATCH 186/319] REVIEWED: `GetTime()`, make sure division is with doubles #5668 --- src/platforms/rcore_desktop_sdl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 0cf18a15d..2bf846be0 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1276,7 +1276,7 @@ void SwapScreenBuffer(void) // Get elapsed time measure in seconds double GetTime(void) { - double time = (double)(SDL_GetPerformanceCounter()/SDL_GetPerformanceFrequency()) - CORE.Time.base; + double time = ((double)SDL_GetPerformanceCounter()/(double)SDL_GetPerformanceFrequency()) - CORE.Time.base; return time; } @@ -2125,7 +2125,7 @@ int InitPlatform(void) // Initialize timing system //---------------------------------------------------------------------------- // Get base time from window initialization - CORE.Time.base = (double)(SDL_GetPerformanceCounter()/SDL_GetPerformanceFrequency()); + CORE.Time.base = (double)SDL_GetPerformanceCounter()/(double)SDL_GetPerformanceFrequency(); #if defined(_WIN32) && SUPPORT_WINMM_HIGHRES_TIMER && !SUPPORT_BUSY_WAIT_LOOP SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "1"); // SDL equivalent of timeBeginPeriod() and timeEndPeriod() From d657e8604395a20be6a7717102da60223af41060 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Tue, 17 Mar 2026 20:01:45 +0100 Subject: [PATCH 187/319] [rlsw] Fix typos, update build scripts, and align style (#5669) * fix typo that appeared during re-format * fix build scripts for `GRAPHICS_API_OPENGL_SOFTWARE` * consistency tweak --- cmake/LibraryConfigurations.cmake | 2 +- src/Makefile | 10 +++++----- src/external/rlsw.h | 10 ++++------ 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index 9e7efe792..7af078de0 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -190,7 +190,7 @@ if (NOT ${OPENGL_VERSION} MATCHES "OFF") elseif (${OPENGL_VERSION} MATCHES "ES 3.0") set(GRAPHICS "GRAPHICS_API_OPENGL_ES3") elseif (${OPENGL_VERSION} MATCHES "Software") - set(GRAPHICS "GRAPHICS_API_OPENGL_11_SOFTWARE") + set(GRAPHICS "GRAPHICS_API_OPENGL_SOFTWARE") endif () if (NOT "${SUGGESTED_GRAPHICS}" STREQUAL "" AND NOT "${SUGGESTED_GRAPHICS}" STREQUAL "${GRAPHICS}") message(WARNING "You are overriding the suggested GRAPHICS=${SUGGESTED_GRAPHICS} with ${GRAPHICS}! This may fail.") diff --git a/src/Makefile b/src/Makefile index b67ba3e6a..026888614 100644 --- a/src/Makefile +++ b/src/Makefile @@ -246,7 +246,7 @@ endif # NOTE: By default use OpenGL 3.3 on desktop platforms ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) GRAPHICS ?= GRAPHICS_API_OPENGL_33 - #GRAPHICS = GRAPHICS_API_OPENGL_11_SOFTWARE # Uncomment to use software rendering + #GRAPHICS = GRAPHICS_API_OPENGL_SOFTWARE # Uncomment to use software rendering #GRAPHICS = GRAPHICS_API_OPENGL_11 # Uncomment to use OpenGL 1.1 #GRAPHICS = GRAPHICS_API_OPENGL_21 # Uncomment to use OpenGL 2.1 #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 @@ -257,7 +257,7 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL) endif ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW) GRAPHICS ?= GRAPHICS_API_OPENGL_33 - #GRAPHICS = GRAPHICS_API_OPENGL_11_SOFTWARE # Uncomment to use software rendering + #GRAPHICS = GRAPHICS_API_OPENGL_SOFTWARE # Uncomment to use software rendering #GRAPHICS = GRAPHICS_API_OPENGL_11 # Uncomment to use OpenGL 1.1 #GRAPHICS = GRAPHICS_API_OPENGL_21 # Uncomment to use OpenGL 2.1 #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 @@ -265,7 +265,7 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW) endif ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_WIN32) GRAPHICS ?= GRAPHICS_API_OPENGL_33 - #GRAPHICS = GRAPHICS_API_OPENGL_11_SOFTWARE # Uncomment to use software rendering + #GRAPHICS = GRAPHICS_API_OPENGL_SOFTWARE # Uncomment to use software rendering #GRAPHICS = GRAPHICS_API_OPENGL_11 # Uncomment to use OpenGL 1.1 #GRAPHICS = GRAPHICS_API_OPENGL_21 # Uncomment to use OpenGL 2.1 #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 @@ -273,7 +273,7 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_WIN32) endif ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) GRAPHICS ?= GRAPHICS_API_OPENGL_ES2 - #GRAPHICS = GRAPHICS_API_OPENGL_11_SOFTWARE # Uncomment to use software rendering + #GRAPHICS = GRAPHICS_API_OPENGL_SOFTWARE # Uncomment to use software rendering endif ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) # On HTML5 OpenGL ES 2.0 is used, emscripten translates it to WebGL 1.0 @@ -709,7 +709,7 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) endif ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_WIN32) LDLIBS = -lgdi32 -lwinmm -lshcore - ifneq ($(GRAPHICS),GRAPHICS_API_OPENGL_11_SOFTWARE) + ifneq ($(GRAPHICS),GRAPHICS_API_OPENGL_SOFTWARE) LDLIBS += -lopengl32 endif endif diff --git a/src/external/rlsw.h b/src/external/rlsw.h index 1166e3448..e0700024a 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -2353,7 +2353,7 @@ static inline void sw_pixel_set_depth(void *pixels, float depth, uint32_t offset switch (format) { case SW_PIXELFORMAT_DEPTH_D8: sw_pixel_set_depth_D8(pixels, depth, offset); break; - case SW_PIXELFORMAT_DEPTH_D16: w_pixel_set_depth_D16(pixels, depth, offset); break; + case SW_PIXELFORMAT_DEPTH_D16: sw_pixel_set_depth_D16(pixels, depth, offset); break; case SW_PIXELFORMAT_DEPTH_D32: sw_pixel_set_depth_D32(pixels, depth, offset); break; case SW_PIXELFORMAT_UNKNOWN: @@ -3565,7 +3565,7 @@ static inline bool sw_line_clip_coord(float q, float p, float *t0, float *t1) static bool sw_line_clip(sw_vertex_t *v0, sw_vertex_t *v1) { float t0 = 0.0f, t1 = 1.0f; - float dH[4] = { 0 } + float dH[4] = { 0 }; float dC[4] = { 0 }; for (int i = 0; i < 4; i++) @@ -5345,10 +5345,8 @@ static void SW_RASTER_TRIANGLE(const sw_vertex_t *v0, const sw_vertex_t *v1, con { vLeft.screen[1] = vRight.screen[1] = y; - if (vLeft.screen[0] < vRight.screen[0]) - SW_RASTER_TRIANGLE_SPAN(&vLeft, &vRight, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); - else - SW_RASTER_TRIANGLE_SPAN(&vRight, &vLeft, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); + if (vLeft.screen[0] < vRight.screen[0]) SW_RASTER_TRIANGLE_SPAN(&vLeft, &vRight, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); + else SW_RASTER_TRIANGLE_SPAN(&vRight, &vLeft, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); sw_add_vertex_grad_PTCH(&vLeft, &dVXdy02); vLeft.screen[0] += dXdy02; From 2c39703d13fb895c80ea590cbbbddc2558b18c88 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 18 Mar 2026 13:28:45 +0100 Subject: [PATCH 188/319] RENAMED: `rl_gputex` to `rltexgpu` --- src/external/{rl_gputex.h => rltexgpu.h} | 463 ++++++++++++----------- 1 file changed, 233 insertions(+), 230 deletions(-) rename src/external/{rl_gputex.h => rltexgpu.h} (74%) diff --git a/src/external/rl_gputex.h b/src/external/rltexgpu.h similarity index 74% rename from src/external/rl_gputex.h rename to src/external/rltexgpu.h index 033045bc8..eb4d2b6b1 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rltexgpu.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rl_gputex v1.1 - GPU compressed textures loading and saving +* rltexgpu v1.2 - GPU compressed textures loading and saving * * DESCRIPTION: * @@ -10,41 +10,40 @@ * 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: +* 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 * * TODO: * - Review rl_load_ktx_from_memory() to support KTX v2.2 specs * * CONFIGURATION: * -* #define RL_GPUTEX_SUPPORT_DDS -* #define RL_GPUTEX_SUPPORT_PKM -* #define RL_GPUTEX_SUPPORT_KTX -* #define RL_GPUTEX_SUPPORT_PVR -* #define RL_GPUTEX_SUPPORT_ASTC +* #define RLTEXGPU_SUPPORT_DDS +* #define RLTEXGPU_SUPPORT_PKM +* #define RLTEXGPU_SUPPORT_KTX +* #define RLTEXGPU_SUPPORT_PVR +* #define RLTEXGPU_SUPPORT_ASTC * Define desired file formats to be supported * -* #define RL_GPUTEX_SHOW_LOG_INFO +* #define RLTEXGPU_SHOW_LOG_INFO * Define, if you wish to see warnings generated by the library -* This will include unless you provide your own RL_GPUTEX_LOG -* #define RL_GPUTEX_LOG +* This will include unless you provide your own RLTEXGPU_LOG +* #define RLTEXGPU_LOG * 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_FREE +* #define RLTEXGPU_MALLOC +* #define RLTEXGPU_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 RLTEXGPU_NULL * Define in order to provide your own libc-compliant NULL pointer; * not doing so will include * -* #define RL_GPUTEX_MEMCPY +* #define RLTEXGPU_MEMCPY * Define in order to provide your own libc-compliant 'memcpy' function; * not doing so will include * @@ -53,12 +52,16 @@ * There is no need to do so when statically linking * * VERSIONS HISTORY: +* 1.2 (18-Mar-2026) Renamed library to `rltexgpu` +* Improved usage as standalone linrary +* Decouple logging and memory allocation from raylib +* * 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 +* 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 +* functionality from raylib src/rtextures.c into self-contained library * * LICENSE: zlib/libpng * @@ -81,8 +84,8 @@ * **********************************************************************************************/ -#ifndef RL_GPUTEX_H -#define RL_GPUTEX_H +#ifndef RLTEXGPU_H +#define RLTEXGPU_H #ifndef RLGPUTEXAPI #define RLGPUTEXAPI // Functions defined as 'extern' by default (implicit specifiers) @@ -93,30 +96,30 @@ // 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 + RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha) + RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA, // 8*2 bpp (2 channels) + RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R5G6B5, // 16 bpp + RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R8G8B8, // 24 bpp + RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) + RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha) + RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, // 32 bpp + RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) + RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) + RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) + RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R16, // 16 bpp (1 channel - half float) + RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R16G16B16, // 16*3 bpp (3 channels - half float) + RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16, // 16*4 bpp (4 channels - half float) + RLTEXGPU_PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) + RLTEXGPU_PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) + RLTEXGPU_PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp + RLTEXGPU_PIXELFORMAT_COMPRESSED_DXT5_RGBA, // 8 bpp + RLTEXGPU_PIXELFORMAT_COMPRESSED_ETC1_RGB, // 4 bpp + RLTEXGPU_PIXELFORMAT_COMPRESSED_ETC2_RGB, // 4 bpp + RLTEXGPU_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA, // 8 bpp + RLTEXGPU_PIXELFORMAT_COMPRESSED_PVRT_RGB, // 4 bpp + RLTEXGPU_PIXELFORMAT_COMPRESSED_PVRT_RGBA, // 4 bpp + RLTEXGPU_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA, // 8 bpp + RLTEXGPU_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA // 2 bpp } rlGpuTexPixelFormat; //---------------------------------------------------------------------------------- @@ -139,7 +142,7 @@ RLGPUTEXAPI int rl_save_ktx_to_memory(const char *fileName, void *data, int widt } #endif -#endif // RL_GPUTEX_H +#endif // RLTEXGPU_H /*********************************************************************************** * @@ -147,49 +150,49 @@ RLGPUTEXAPI int rl_save_ktx_to_memory(const char *fileName, void *data, int widt * ************************************************************************************/ -#if defined(RL_GPUTEX_IMPLEMENTATION) +#if defined(RLTEXGPU_IMPLEMENTATION) -#if defined(RL_GPUTEX_SHOW_LOG_INFO) && !defined(RL_GPUTEX_LOG) +#if defined(RLTEXGPU_SHOW_LOG_INFO) && !defined(RLTEXGPU_LOG) #include // Required for: printf() #endif -#if !defined(RL_GPUTEX_MALLOC) || !defined(RL_GPUTEX_NULL) +#if !defined(RLTEXGPU_MALLOC) || !defined(RLTEXGPU_NULL) #include // Required for: NULL, malloc(), calloc(), free() #endif -#if !defined(RL_GPUTEX_MEMCPY) +#if !defined(RLTEXGPU_MEMCPY) #include // Required for: memcpy() #endif -#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" +#if defined(RLTEXGPU_MALLOC) || defined(RLTEXGPU_FREE) + #if !defined(RLTEXGPU_MALLOC) || !defined(RLTEXGPU_FREE) + #warning "RLTEXGPU_MALLOC and RLTEXGPU_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_FREE(ptr) free(ptr) +#if !defined(RLTEXGPU_MALLOC) + #define RLTEXGPU_MALLOC(sz) malloc(sz) + #define RLTEXGPU_FREE(ptr) free(ptr) #endif -#if !defined(RL_GPUTEX_NULL) - #define RL_GPUTEX_NULL NULL +#if !defined(RLTEXGPU_NULL) + #define RLTEXGPU_NULL NULL #endif -#if !defined(RL_GPUTEX_MEMCPY) - #define RL_GPUTEX_MEMCPY(dest, src, num) memcpy(dest, src, num) +#if !defined(RLTEXGPU_MEMCPY) + #define RLTEXGPU_MEMCPY(dest, src, num) memcpy(dest, src, num) #endif // 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_LOG_INFO) - #if !defined(RL_GPUTEX_LOG) - #define RL_GPUTEX_LOG(...) (void)(printf("RL_GPUTEX: WARNING: " __VA_ARGS__), printf("\n")) +// WARNING: This macro expects that newline character is added automatically +// in order to match the functionality of raylib's TRACELOG() +#if defined(RLTEXGPU_SHOW_LOG_INFO) + #if !defined(RLTEXGPU_LOG) + #define RLTEXGPU_LOG(...) (void)(printf("RL_GPUTEX: WARNING: " __VA_ARGS__), printf("\n")) #endif #else - #if defined(RL_GPUTEX_LOG) + #if defined(RLTEXGPU_LOG) // Undefine it first in order to supress warnings about macro redefinition - #undef RL_GPUTEX_LOG + #undef RLTEXGPU_LOG #endif - #define RL_GPUTEX_LOG(...) + #define RLTEXGPU_LOG(...) #endif //---------------------------------------------------------------------------------- @@ -204,11 +207,11 @@ void get_gl_texture_formats(int format, unsigned int *gl_internal_format, unsign //---------------------------------------------------------------------------------- // Module Functions Definition //---------------------------------------------------------------------------------- -#if defined(RL_GPUTEX_SUPPORT_DDS) +#if defined(RLTEXGPU_SUPPORT_DDS) // 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 = RL_GPUTEX_NULL; // Image data pointer + void *image_data = RLTEXGPU_NULL; // Image data pointer int image_pixel_size = 0; // Image pixel size unsigned char *file_data_ptr = (unsigned char *)file_data; @@ -256,7 +259,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ unsigned int reserved2; } dds_header; - if (file_data_ptr != RL_GPUTEX_NULL) + if (file_data_ptr != RLTEXGPU_NULL) { // Verify the type of file unsigned char *dds_header_id = file_data_ptr; @@ -264,7 +267,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_LOG("DDS file data not valid"); + RLTEXGPU_LOG("DDS file data not valid"); } else { @@ -275,8 +278,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_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"); + if (*width % 4 != 0) RLTEXGPU_LOG("DDS file width must be multiple of 4. Image will not display correctly"); + if (*height % 4 != 0) RLTEXGPU_LOG("DDS file height must be multiple of 4. Image will not display correctly"); image_pixel_size = header->width*header->height; @@ -289,11 +292,11 @@ 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_GPUTEX_MALLOC(data_size); + image_data = RLTEXGPU_MALLOC(data_size); - RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); + RLTEXGPU_MEMCPY(image_data, file_data_ptr, data_size); - *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G6B5; + *format = RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R5G6B5; } else if (header->ddspf.flags == 0x41) // With alpha channel { @@ -301,9 +304,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_GPUTEX_MALLOC(data_size); + image_data = RLTEXGPU_MALLOC(data_size); - RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); + RLTEXGPU_MEMCPY(image_data, file_data_ptr, data_size); unsigned char alpha = 0; @@ -315,15 +318,15 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ ((unsigned short *)image_data)[i] += alpha; } - *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1; + *format = RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1; } else if (header->ddspf.a_bit_mask == 0xf000) // 4bit alpha { int data_size = image_pixel_size*sizeof(unsigned short); if (header->mipmap_count > 1) data_size = data_size + data_size/3; - image_data = RL_GPUTEX_MALLOC(data_size); + image_data = RLTEXGPU_MALLOC(data_size); - RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); + RLTEXGPU_MEMCPY(image_data, file_data_ptr, data_size); unsigned char alpha = 0; @@ -335,7 +338,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ ((unsigned short *)image_data)[i] += alpha; } - *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4; + *format = RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4; } } } @@ -343,19 +346,19 @@ 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_GPUTEX_MALLOC(data_size); + image_data = RLTEXGPU_MALLOC(data_size); - RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); + RLTEXGPU_MEMCPY(image_data, file_data_ptr, data_size); - *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8; + *format = RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R8G8B8; } else if ((header->ddspf.flags == 0x41) && (header->ddspf.rgb_bit_count == 32)) // DDS_RGBA, no compressed { 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_GPUTEX_MALLOC(data_size); + image_data = RLTEXGPU_MALLOC(data_size); - RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); + RLTEXGPU_MEMCPY(image_data, file_data_ptr, data_size); unsigned char blue = 0; @@ -369,7 +372,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ ((unsigned char *)image_data)[i + 2] = blue; } - *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + *format = RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; } else if (((header->ddspf.flags == 0x04) || (header->ddspf.flags == 0x05)) && (header->ddspf.fourcc > 0)) // Compressed { @@ -379,19 +382,19 @@ 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_GPUTEX_MALLOC(data_size*sizeof(unsigned char)); + image_data = RLTEXGPU_MALLOC(data_size*sizeof(unsigned char)); - RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); + RLTEXGPU_MEMCPY(image_data, file_data_ptr, data_size); switch (header->ddspf.fourcc) { case FOURCC_DXT1: { - if (header->ddspf.flags == 0x04) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT1_RGB; - else *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT1_RGBA; + if (header->ddspf.flags == 0x04) *format = RLTEXGPU_PIXELFORMAT_COMPRESSED_DXT1_RGB; + else *format = RLTEXGPU_PIXELFORMAT_COMPRESSED_DXT1_RGBA; } break; - case FOURCC_DXT3: *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT3_RGBA; break; - case FOURCC_DXT5: *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT5_RGBA; break; + case FOURCC_DXT3: *format = RLTEXGPU_PIXELFORMAT_COMPRESSED_DXT3_RGBA; break; + case FOURCC_DXT5: *format = RLTEXGPU_PIXELFORMAT_COMPRESSED_DXT5_RGBA; break; default: break; } } @@ -402,13 +405,13 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ } #endif -#if defined(RL_GPUTEX_SUPPORT_PKM) +#if defined(RLTEXGPU_SUPPORT_PKM) // Loading PKM image data (ETC1/ETC2 compression) // NOTE: KTX is the standard Khronos Group compression format (ETC1/ETC2, mipmaps) // 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 = RL_GPUTEX_NULL; // Image data pointer + void *image_data = RLTEXGPU_NULL; // Image data pointer unsigned char *file_data_ptr = (unsigned char *)file_data; @@ -439,13 +442,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 != RL_GPUTEX_NULL) + if (file_data_ptr != RLTEXGPU_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] != ' ')) { - RL_GPUTEX_LOG("PKM file data not valid"); + RLTEXGPU_LOG("PKM file data not valid"); } else { @@ -465,13 +468,13 @@ 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_GPUTEX_MALLOC(data_size*sizeof(unsigned char)); + image_data = RLTEXGPU_MALLOC(data_size*sizeof(unsigned char)); - RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); + RLTEXGPU_MEMCPY(image_data, file_data_ptr, data_size); - 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; + if (header->format == 0) *format = RLTEXGPU_PIXELFORMAT_COMPRESSED_ETC1_RGB; + else if (header->format == 1) *format = RLTEXGPU_PIXELFORMAT_COMPRESSED_ETC2_RGB; + else if (header->format == 3) *format = RLTEXGPU_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA; } } @@ -479,12 +482,12 @@ void *rl_load_pkm_from_memory(const unsigned char *file_data, unsigned int file_ } #endif -#if defined(RL_GPUTEX_SUPPORT_KTX) +#if defined(RLTEXGPU_SUPPORT_KTX) // Load KTX compressed image data (ETC1/ETC2 compression) // 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 = RL_GPUTEX_NULL; // Image data pointer + void *image_data = RLTEXGPU_NULL; // Image data pointer unsigned char *file_data_ptr = (unsigned char *)file_data; @@ -522,14 +525,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 != RL_GPUTEX_NULL) + if (file_data_ptr != RLTEXGPU_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')) { - RL_GPUTEX_LOG("KTX file data not valid"); + RLTEXGPU_LOG("KTX file data not valid"); } else { @@ -544,13 +547,13 @@ 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_GPUTEX_MALLOC(data_size*sizeof(unsigned char)); + image_data = RLTEXGPU_MALLOC(data_size*sizeof(unsigned char)); - RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); + RLTEXGPU_MEMCPY(image_data, file_data_ptr, data_size); - 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; + if (header->gl_internal_format == 0x8D64) *format = RLTEXGPU_PIXELFORMAT_COMPRESSED_ETC1_RGB; + else if (header->gl_internal_format == 0x9274) *format = RLTEXGPU_PIXELFORMAT_COMPRESSED_ETC2_RGB; + else if (header->gl_internal_format == 0x9278) *format = RLTEXGPU_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA; // TODO: Support uncompressed data formats? Right now it returns format = 0! } @@ -608,7 +611,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_MALLOC(data_size); + unsigned char *file_data = RLTEXGPU_MALLOC(data_size); unsigned char *file_data_ptr = file_data; ktx_header header = { 0 }; @@ -620,7 +623,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 - RL_GPUTEX_MEMCPY(header.id, ktx_identifier, 12); // KTX 1.1 signature + RLTEXGPU_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; @@ -642,10 +645,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) RL_GPUTEX_LOG("RL_GPUTEX: GL format not supported for KTX export (%i)", header.gl_format); + if (header.gl_format == -1) RLTEXGPU_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)); + RLTEXGPU_MEMCPY(file_data_ptr, &header, sizeof(ktx_header)); file_data_ptr += sizeof(ktx_header); int temp_width = width; @@ -657,8 +660,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); - 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); + RLTEXGPU_MEMCPY(file_data_ptr, &data_size, sizeof(unsigned int)); + RLTEXGPU_MEMCPY(file_data_ptr + 4, (unsigned char *)data + data_offset, data_size); temp_width /= 2; temp_height /= 2; @@ -671,34 +674,34 @@ 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 != RL_GPUTEX_NULL) + if (file != RLTEXGPU_NULL) { unsigned int count = (unsigned int)fwrite(file_data, sizeof(unsigned char), data_size, file); - 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 + if (count == 0) RLTEXGPU_LOG("FILEIO: [%s] Failed to write file", file_name); + else if (count != data_size) RLTEXGPU_LOG("FILEIO: [%s] File partially written", file_name); + else (void)0; int result = fclose(file); - if (result != 0) RL_GPUTEX_LOG("FILEIO: [%s] Failed to close file", file_name); + if (result != 0) RLTEXGPU_LOG("FILEIO: [%s] Failed to close file", file_name); if (result == 0 && count == data_size) success = true; } - else RL_GPUTEX_LOG("FILEIO: [%s] Failed to open file", file_name); + else RLTEXGPU_LOG("FILEIO: [%s] Failed to open file", file_name); - RL_GPUTEX_FREE(file_data); // Free file data buffer + RLTEXGPU_FREE(file_data); // Free file data buffer // If all data has been written correctly to file, success = 1 return success; } #endif -#if defined(RL_GPUTEX_SUPPORT_PVR) +#if defined(RLTEXGPU_SUPPORT_PVR) // Loading PVR image data (uncompressed or PVRT compression) // 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 = RL_GPUTEX_NULL; // Image data pointer + void *image_data = RLTEXGPU_NULL; // Image data pointer unsigned char *file_data_ptr = (unsigned char *)file_data; @@ -756,7 +759,7 @@ void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_ } PVRMetadata; #endif - if (file_data_ptr != RL_GPUTEX_NULL) + if (file_data_ptr != RLTEXGPU_NULL) { // Check PVR image version unsigned char pvr_version = file_data_ptr[0]; @@ -768,7 +771,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_LOG("PVR file data not valid"); + RLTEXGPU_LOG("PVR file data not valid"); } else { @@ -779,24 +782,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 = 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; + if (((header->channels[0] == 'l') && (header->channels[1] == 0)) && (header->channel_depth[0] == 8)) *format = RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; + else if (((header->channels[0] == 'l') && (header->channels[1] == 'a')) && ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8))) *format = RLTEXGPU_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 = 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; + if ((header->channel_depth[0] == 5) && (header->channel_depth[1] == 5) && (header->channel_depth[2] == 5) && (header->channel_depth[3] == 1)) *format = RLTEXGPU_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 = RLTEXGPU_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 = RLTEXGPU_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 = 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; + if ((header->channel_depth[0] == 5) && (header->channel_depth[1] == 6) && (header->channel_depth[2] == 5)) *format = RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R5G6B5; + else if ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8) && (header->channel_depth[2] == 8)) *format = RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R8G8B8; } } - 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; + else if (header->channels[0] == 2) *format = RLTEXGPU_PIXELFORMAT_COMPRESSED_PVRT_RGB; + else if (header->channels[0] == 3) *format = RLTEXGPU_PIXELFORMAT_COMPRESSED_PVRT_RGBA; file_data_ptr += header->metadata_size; // Skip meta data header @@ -804,36 +807,36 @@ void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_ int bpp = 0; switch (*format) { - 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; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R5G6B5: + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break; + case RLTEXGPU_PIXELFORMAT_COMPRESSED_PVRT_RGB: + case RLTEXGPU_PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break; default: break; } int data_size = (*width)*(*height)*bpp/8; // Total data size in bytes - image_data = RL_GPUTEX_MALLOC(data_size*sizeof(unsigned char)); + image_data = RLTEXGPU_MALLOC(data_size*sizeof(unsigned char)); - RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); + RLTEXGPU_MEMCPY(image_data, file_data_ptr, data_size); } } - else if (pvr_version == 52) RL_GPUTEX_LOG("PVRv2 format not supported, update your files to PVRv3"); + else if (pvr_version == 52) RLTEXGPU_LOG("PVRv2 format not supported, update your files to PVRv3"); } return image_data; } #endif -#if defined(RL_GPUTEX_SUPPORT_ASTC) +#if defined(RLTEXGPU_SUPPORT_ASTC) // 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 = RL_GPUTEX_NULL; // Image data pointer + void *image_data = RLTEXGPU_NULL; // Image data pointer unsigned char *file_data_ptr = (unsigned char *)file_data; @@ -856,13 +859,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 != RL_GPUTEX_NULL) + if (file_data_ptr != RLTEXGPU_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)) { - RL_GPUTEX_LOG("ASTC file data not valid"); + RLTEXGPU_LOG("ASTC file data not valid"); } else { @@ -881,14 +884,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_GPUTEX_MALLOC(data_size*sizeof(unsigned char)); + image_data = RLTEXGPU_MALLOC(data_size*sizeof(unsigned char)); - RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size); + RLTEXGPU_MEMCPY(image_data, file_data_ptr, data_size); - if (bpp == 8) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA; - else if (bpp == 2) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA; + if (bpp == 8) *format = RLTEXGPU_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA; + else if (bpp == 2) *format = RLTEXGPU_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA; } - else RL_GPUTEX_LOG("ASTC block size configuration not supported"); + else RLTEXGPU_LOG("ASTC block size configuration not supported"); } } @@ -907,27 +910,27 @@ static int get_pixel_data_size(int width, int height, int format) switch (format) { - 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; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R5G6B5: + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break; + case RLTEXGPU_PIXELFORMAT_COMPRESSED_DXT1_RGB: + case RLTEXGPU_PIXELFORMAT_COMPRESSED_DXT1_RGBA: + case RLTEXGPU_PIXELFORMAT_COMPRESSED_ETC1_RGB: + case RLTEXGPU_PIXELFORMAT_COMPRESSED_ETC2_RGB: + case RLTEXGPU_PIXELFORMAT_COMPRESSED_PVRT_RGB: + case RLTEXGPU_PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break; + case RLTEXGPU_PIXELFORMAT_COMPRESSED_DXT3_RGBA: + case RLTEXGPU_PIXELFORMAT_COMPRESSED_DXT5_RGBA: + case RLTEXGPU_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: + case RLTEXGPU_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: bpp = 8; break; + case RLTEXGPU_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break; default: break; } @@ -937,8 +940,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 >= 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; + if ((format >= RLTEXGPU_PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < RLTEXGPU_PIXELFORMAT_COMPRESSED_DXT3_RGBA)) data_size = 8; + else if ((format >= RLTEXGPU_PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < RLTEXGPU_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) data_size = 16; } return data_size; @@ -959,69 +962,69 @@ void get_gl_texture_formats(int format, unsigned int *gl_internal_format, unsign { #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; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *gl_internal_format = GL_LUMINANCE; *gl_format = GL_LUMINANCE; *gl_type = GL_UNSIGNED_BYTE; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: *gl_internal_format = GL_LUMINANCE_ALPHA; *gl_format = GL_LUMINANCE_ALPHA; *gl_type = GL_UNSIGNED_BYTE; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R5G6B5: *gl_internal_format = GL_RGB; *gl_format = GL_RGB; *gl_type = GL_UNSIGNED_SHORT_5_6_5; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R8G8B8: *gl_internal_format = GL_RGB; *gl_format = GL_RGB; *gl_type = GL_UNSIGNED_BYTE; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: *gl_internal_format = GL_RGBA; *gl_format = GL_RGBA; *gl_type = GL_UNSIGNED_SHORT_5_5_5_1; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: *gl_internal_format = GL_RGBA; *gl_format = GL_RGBA; *gl_type = GL_UNSIGNED_SHORT_4_4_4_4; break; + case RLTEXGPU_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; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R32: *gl_internal_format = GL_R32F_EXT; *gl_format = GL_RED_EXT; *gl_type = GL_FLOAT; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R32G32B32: *gl_internal_format = GL_RGB32F_EXT; *gl_format = GL_RGB; *gl_type = GL_FLOAT; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: *gl_internal_format = GL_RGBA32F_EXT; *gl_format = GL_RGBA; *gl_type = GL_FLOAT; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R16: *gl_internal_format = GL_R16F_EXT; *gl_format = GL_RED_EXT; *gl_type = GL_HALF_FLOAT; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R16G16B16: *gl_internal_format = GL_RGB16F_EXT; *gl_format = GL_RGB; *gl_type = GL_HALF_FLOAT; break; + case RLTEXGPU_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 + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R32: *gl_internal_format = GL_LUMINANCE; *gl_format = GL_LUMINANCE; *gl_type = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R32G32B32: *gl_internal_format = GL_RGB; *gl_format = GL_RGB; *gl_type = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case RLTEXGPU_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; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R16: *gl_internal_format = GL_LUMINANCE; *gl_format = GL_LUMINANCE; *gl_type = GL_HALF_FLOAT_ARB; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R16G16B16: *gl_internal_format = GL_RGB; *gl_format = GL_RGB; *gl_type = GL_HALF_FLOAT_ARB; break; + case RLTEXGPU_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 + case RLTEXGPU_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 RLTEXGPU_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 RLTEXGPU_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; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *gl_internal_format = GL_R8; *gl_format = GL_RED; *gl_type = GL_UNSIGNED_BYTE; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: *gl_internal_format = GL_RG8; *gl_format = GL_RG; *gl_type = GL_UNSIGNED_BYTE; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R5G6B5: *gl_internal_format = GL_RGB565; *gl_format = GL_RGB; *gl_type = GL_UNSIGNED_SHORT_5_6_5; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R8G8B8: *gl_internal_format = GL_RGB8; *gl_format = GL_RGB; *gl_type = GL_UNSIGNED_BYTE; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: *gl_internal_format = GL_RGB5_A1; *gl_format = GL_RGBA; *gl_type = GL_UNSIGNED_SHORT_5_5_5_1; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: *gl_internal_format = GL_RGBA4; *gl_format = GL_RGBA; *gl_type = GL_UNSIGNED_SHORT_4_4_4_4; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: *gl_internal_format = GL_RGBA8; *gl_format = GL_RGBA; *gl_type = GL_UNSIGNED_BYTE; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R32: *gl_internal_format = GL_R32F; *gl_format = GL_RED; *gl_type = GL_FLOAT; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R32G32B32: *gl_internal_format = GL_RGB32F; *gl_format = GL_RGB; *gl_type = GL_FLOAT; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: *gl_internal_format = GL_RGBA32F; *gl_format = GL_RGBA; *gl_type = GL_FLOAT; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R16: *gl_internal_format = GL_R16F; *gl_format = GL_RED; *gl_type = GL_HALF_FLOAT; break; + case RLTEXGPU_PIXELFORMAT_UNCOMPRESSED_R16G16B16: *gl_internal_format = GL_RGB16F; *gl_format = GL_RGB; *gl_type = GL_HALF_FLOAT; break; + case RLTEXGPU_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 + case RLTEXGPU_PIXELFORMAT_COMPRESSED_DXT1_RGB: *gl_internal_format = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; + case RLTEXGPU_PIXELFORMAT_COMPRESSED_DXT1_RGBA: *gl_internal_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; + case RLTEXGPU_PIXELFORMAT_COMPRESSED_DXT3_RGBA: *gl_internal_format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; + case RLTEXGPU_PIXELFORMAT_COMPRESSED_DXT5_RGBA: *gl_internal_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; + case RLTEXGPU_PIXELFORMAT_COMPRESSED_ETC1_RGB: *gl_internal_format = GL_ETC1_RGB8_OES; break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 + case RLTEXGPU_PIXELFORMAT_COMPRESSED_ETC2_RGB: *gl_internal_format = GL_COMPRESSED_RGB8_ETC2; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case RLTEXGPU_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 RLTEXGPU_PIXELFORMAT_COMPRESSED_PVRT_RGB: *gl_internal_format = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU + case RLTEXGPU_PIXELFORMAT_COMPRESSED_PVRT_RGBA: *gl_internal_format = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU + case RLTEXGPU_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 RLTEXGPU_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; + default: RLTEXGPU_LOG("GPUTEX: Current format not supported (%i)", format); break; } */ } -#endif // RL_GPUTEX_IMPLEMENTATION +#endif // RLTEXGPU_IMPLEMENTATION /* // OpenGL texture data formats From 29e4b775809a52c3952de6873c7619fb95b276c2 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 18 Mar 2026 13:29:36 +0100 Subject: [PATCH 189/319] RENAMED: `rl_gputex` to `rltexgpu` --- CHANGELOG | 7 ++++--- HISTORY.md | 2 +- src/raylib.h | 2 +- src/rtextures.c | 24 ++++++++++++------------ 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index f6a3e8ad1..053ff3018 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -747,12 +747,13 @@ Detailed changes: [rexm] REVIEWED: `Makefile.Web` before trying to rebuild new example for web by @raysan5 [rexm] REVIEWED: Using `Makefile.Web` for specific web versions generation by @raysan5 +[external] RENAMED: `rl_gputex.h` to `rltexgpu.h`, compressed textures loading +[external] REVIEWED: `rltexgpu.h`, make it usable standalone by @raysan5 +[external] REVIEWED: `rltexgpu.h, fix the swizzling in `rl_load_dds_from_memory()` (#5422) by @msmith-codes +[external]`REVIEWED: `rltexgpu.h, decouple logging and memory allocation from raylib by @sleeptightAnsiC [external] REVIEWED: `jar_mod.h`, buffer overflow on memcpy by @LoneAuios [external] REVIEWED: `sdefl` and `sinfl` issues (#5367) by @raysan5 [external] REVIEWED: `sinfl_bsr()`, improvements by @RicoP -[external] REVIEWED: `rl_gputex.h`, make it usable standalone by @raysan5 -[external] REVIEWED: `rl_gputex.h,, fix the swizzling in `rl_load_dds_from_memory()` (#5422) by @msmith-codes -[external]`REVIEWED: `rl_gputex.h,, decouple logging and memory allocation from raylib by @sleeptightAnsiC [external] REVIEWED: `stb_truetype`, fix composite glyph scaling logic (#4811) by @ashishbhattarai [external] UPDATED: dr_libs (#5020) by @Emil2010 [external] UPDATED: miniaudio to v0.11.22 (#4983) by @M374LX diff --git a/HISTORY.md b/HISTORY.md index 4fc5569d5..b68ee44f4 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -419,7 +419,7 @@ Highlights for `raylib 4.5`: - **`NEW` Support QOA audio format (import/export)**: Just a couple of months ago the new [QOA file format](https://qoaformat.org/) was published, a very simple, portable and open source quite-ok-audio file format. raylib already supports it, added to `raudio` module and including audio loading from file, loading from memory, streaming from file, streaming from memory and **exporting to QOA** audio format. **Because simplicity really matters to raylib!** - - **`NEW` Module for compressed textures loading**: [`rl_gputex`](https://github.com/raysan5/raylib/blob/master/src/external/rl_gputex.h), a portable single-file header-only small library to load compressed texture file-formats (DDS, PKM, KTX, PVR, ASTC). Provided functionality is not new to raylib but it was part of the raylib `rtextures` module, now it has been moved into a separate self-contained library, **improving portability**. Note that this module is only intended to **load compressed data from files, ready to be uploaded to GPU**, no compression/decompression functionality is provided. This change is a first step towards a better modularization of raylib library. + - **`NEW` Module for compressed textures loading**: [`rl_gputex`](https://github.com/raysan5/raylib/blob/master/src/external/rltexgpu.h), a portable single-file header-only small library to load compressed texture file-formats (DDS, PKM, KTX, PVR, ASTC). Provided functionality is not new to raylib but it was part of the raylib `rtextures` module, now it has been moved into a separate self-contained library, **improving portability**. Note that this module is only intended to **load compressed data from files, ready to be uploaded to GPU**, no compression/decompression functionality is provided. This change is a first step towards a better modularization of raylib library. - **Reviewed `rlgl` module for automatic limits checking**: Again, [`rlgl`](https://github.com/raysan5/raylib/blob/master/src/rlgl.h) has been reviewed to simplify usage. Now users do not need to worry about reaching the internal render-batch limits when they send their triangles to draw 2d/3d, `rlgl` manages it automatically! This change allows a **great simplification for other modules** like `rshapes`, `rtextures` and `rmodels` that do not need to worry about bufffer overflows and can just define as many vertex as desired! diff --git a/src/raylib.h b/src/raylib.h index 951f1ffb3..2e849c917 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -40,7 +40,7 @@ * [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG) * [rtextures] stb_image_resize2 (Sean Barret) for image resizing algorithms * [rtextures] stb_perlin (Sean Barret) for Perlin Noise image generation -* [rtextures] rl_gputex (Ramon Santamaria) for GPU-compressed texture formats +* [rtextures] rltexgpu (Ramon Santamaria) for GPU-compressed texture formats * [rtext] stb_truetype (Sean Barret) for ttf fonts loading * [rtext] stb_rect_pack (Sean Barret) for rectangles packing * [rmodels] par_shapes (Philip Rideout) for parametric 3d shapes generation diff --git a/src/rtextures.c b/src/rtextures.c index cf19edd1e..45b615cf7 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -100,19 +100,19 @@ #endif #if SUPPORT_FILEFORMAT_DDS - #define RL_GPUTEX_SUPPORT_DDS + #define RLTEXGPU_SUPPORT_DDS #endif #if SUPPORT_FILEFORMAT_PKM - #define RL_GPUTEX_SUPPORT_PKM + #define RLTEXGPU_SUPPORT_PKM #endif #if SUPPORT_FILEFORMAT_KTX - #define RL_GPUTEX_SUPPORT_KTX + #define RLTEXGPU_SUPPORT_KTX #endif #if SUPPORT_FILEFORMAT_PVR - #define RL_GPUTEX_SUPPORT_PVR + #define RLTEXGPU_SUPPORT_PVR #endif #if SUPPORT_FILEFORMAT_ASTC - #define RL_GPUTEX_SUPPORT_ASTC + #define RLTEXGPU_SUPPORT_ASTC #endif // Image fileformats not supported by default @@ -161,13 +161,13 @@ #pragma GCC diagnostic ignored "-Wunused-function" #endif - #define RL_GPUTEX_MALLOC RL_MALLOC - #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) + #define RLTEXGPU_MALLOC RL_MALLOC + #define RLTEXGPU_FREE RL_FREE + #define RLTEXGPU_LOG(...) TRACELOG(LOG_WARNING, "IMAGE: " __VA_ARGS__) + #define RLTEXGPU_SHOW_LOG_INFO + #define RLTEXGPU_IMPLEMENTATION + #include "external/rltexgpu.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 f23319db3c188d23ec22bb207eaf9bd0b8ac7620 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 18 Mar 2026 13:29:51 +0100 Subject: [PATCH 190/319] Update ROADMAP.md --- ROADMAP.md | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 9d333089a..441a811d2 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -13,16 +13,28 @@ Here is a wishlist with features and ideas to improve the library. Note that fea _Current version of raylib is complete and functional but there is always room for improvements._ -**raylib 5.x** - - [ ] `rcore`: Support additional platforms: iOS, consoles? - - [x] `rcore_web`: Avoid GLFW dependency, functionality can be directly implemented using emscripten SDK - - [x] `rlgl`: Review GLSL shaders naming conventions for consistency - - [ ] `textures`: Improve compressed textures support, loading and saving +**raylib 7.0** + - [ ] `rcore_desktop_win32`: Improve new Windows platform backend - inputs, highdpi + - [ ] `rcore_desktop_emscripten`: Improve new Web platform backend - inputs, highdpi + - [ ] `rcore_desktop_cocoa`: Create additional platform backend: macOS + - [ ] `rcore_desktop_x11`: Create additional platform backend: Linux/X11 + - [ ] `rcore_desktop_wayland`: Create additional platform backend: Linux/Wayland + - [ ] `rcore`: Investigate alternative embedded platforms and realtime OSs + - [ ] `rlsw`: Software renderer optimizations, platform-specific, improve SIMD support + - [ ] `rtextures`: Consider moving N-patch system to separate example + - [ ] `rtextures`: Review blending modes system, provide more options or better samples + - [ ] `raudio`: Support microphone input, basic API to read microphone + - [ ] `rltexgpu`: Improve compressed textures support, loading and saving, improve KTX 2.0 + - [ ] `rlobj`: Create OBJ loader, supporting material file separately (low priority) + +**raylib 6.0** + - [x] `rlsw`: New Software Renderer backend, pseudo-OpenGL 1.1 implementation + - [x] `rcore_emscripten`: New emscripten-only backend, avoiding GLFW dependency + - [x] `rlgl`: Review GLSL shaders naming conventions for consistency, redesigned shader API - [x] `rmodels`: Improve 3d objects loading, specially animations (obj, gltf) - [x] `examples`: Review all examples, add more and better code explanations - - [x] Software renderer backend? Maybe using `Image` provided API -**raylib 4.x** +**raylib 5.0** - [x] Split core module into separate platforms? - [x] Redesign gestures system, improve touch inputs management - [x] Redesign camera module (more flexible) ([#1143](https://github.com/raysan5/raylib/issues/1143), https://github.com/raysan5/raylib/discussions/2507) @@ -30,7 +42,6 @@ _Current version of raylib is complete and functional but there is always room f - [x] Focus on HTML5 ([raylib 5k gamejam](https://itch.io/jam/raylib-5k-gamejam)) and embedded platforms (RPI and similar SOCs) - [x] Additional support libraries: [raygui](https://github.com/raysan5/raygui), [rres](https://github.com/raysan5/rres) - **raylib 4.0** - [x] Improved consistency and coherency in raylib API - [x] Continuous Deployment using GitHub Actions From 85df4e7de54031638c8957c5f550b3191e0535e6 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 18 Mar 2026 13:31:09 +0100 Subject: [PATCH 191/319] REVIEWED: `rlsw`, use provided library information --- src/external/rlsw.h | 6 +++--- src/platforms/rcore_desktop_sdl.c | 2 +- src/platforms/rcore_desktop_win32.c | 9 +++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/external/rlsw.h b/src/external/rlsw.h index e0700024a..3c908a82f 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -4050,9 +4050,9 @@ const char *swGetString(SWget name) switch (name) { - case SW_VENDOR: result = "RLSW Header"; break; - case SW_RENDERER: result = "RLSW Software Renderer"; break; - case SW_VERSION: result = "RLSW 1.0"; break; + case SW_VENDOR: result = "RLSW"; break; + case SW_RENDERER: result = "RLSW OpenGL Software Renderer"; break; + case SW_VERSION: result = RLSW_VERSION; break; case SW_EXTENSIONS: result = "None"; break; default: RLSW.errCode = SW_INVALID_ENUM; break; } diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 2bf846be0..92c1979b3 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -2166,7 +2166,7 @@ static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode) return mapScancodeToKey[sdlScancode]; } - return KEY_NULL; // No equivalent key in Raylib + return KEY_NULL; // No equivalent key in raylib } // Get next codepoint in a byte sequence and bytes processed diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index 612800b83..afbbedffc 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -1652,9 +1652,9 @@ int InitPlatform(void) if (rlGetVersion() == RL_OPENGL_SOFTWARE) // Using software renderer { TRACELOG(LOG_INFO, "GL: OpenGL device information:"); - TRACELOG(LOG_INFO, " > Vendor: %s", "raylib"); - TRACELOG(LOG_INFO, " > Renderer: %s", "rlsw - OpenGL Software Renderer"); - TRACELOG(LOG_INFO, " > Version: %s", "1.0"); + TRACELOG(LOG_INFO, " > Vendor: %s", glGetString(GL_VENDOR)); + TRACELOG(LOG_INFO, " > Renderer: %s", glGetString(GL_RENDERER)); + TRACELOG(LOG_INFO, " > Version: %s", glGetString(GL_VERSION)); TRACELOG(LOG_INFO, " > GLSL: %s", "NOT SUPPORTED"); } @@ -1870,7 +1870,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara // WARNING: Don't trust the docs, they say this message can not be obtained if not calling DefWindowProc() // in response to WM_WINDOWPOSCHANGED but looks like when a window is created, // this message can be obtained without getting WM_WINDOWPOSCHANGED - HandleWindowResize(hwnd, &platform.appScreenWidth, &platform.appScreenHeight); + // WARNING: This call fails for Software-Renderer backend + //HandleWindowResize(hwnd, &platform.appScreenWidth, &platform.appScreenHeight); } break; //case WM_MOVE case WM_WINDOWPOSCHANGED: From 528caf8b9dc83c2937d7f3aaf450cfa9371853c6 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 18 Mar 2026 16:21:14 +0100 Subject: [PATCH 192/319] Added some libraries --- projects/VS2022/raylib/raylib.vcxproj | 3 +++ projects/VS2022/raylib/raylib.vcxproj.filters | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/projects/VS2022/raylib/raylib.vcxproj b/projects/VS2022/raylib/raylib.vcxproj index 8cc3fae7a..cf686e498 100644 --- a/projects/VS2022/raylib/raylib.vcxproj +++ b/projects/VS2022/raylib/raylib.vcxproj @@ -585,6 +585,9 @@ + + + diff --git a/projects/VS2022/raylib/raylib.vcxproj.filters b/projects/VS2022/raylib/raylib.vcxproj.filters index 17ac7f9b8..444686eab 100644 --- a/projects/VS2022/raylib/raylib.vcxproj.filters +++ b/projects/VS2022/raylib/raylib.vcxproj.filters @@ -102,6 +102,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + From 2fd058c1e49d17e6976f789769e8ead27ac1396e Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 18 Mar 2026 16:21:18 +0100 Subject: [PATCH 193/319] Update ROADMAP.md --- ROADMAP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ROADMAP.md b/ROADMAP.md index 441a811d2..10754e52b 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -20,7 +20,7 @@ _Current version of raylib is complete and functional but there is always room f - [ ] `rcore_desktop_x11`: Create additional platform backend: Linux/X11 - [ ] `rcore_desktop_wayland`: Create additional platform backend: Linux/Wayland - [ ] `rcore`: Investigate alternative embedded platforms and realtime OSs - - [ ] `rlsw`: Software renderer optimizations, platform-specific, improve SIMD support + - [ ] `rlsw`: Software renderer optimizations: mipmaps, platform-specific SIMD - [ ] `rtextures`: Consider moving N-patch system to separate example - [ ] `rtextures`: Review blending modes system, provide more options or better samples - [ ] `raudio`: Support microphone input, basic API to read microphone From 2a3b28b67a484e188344fc8136bee30ec24e983e Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 18 Mar 2026 16:21:35 +0100 Subject: [PATCH 194/319] Reviewed some comments --- src/external/rlsw.h | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/external/rlsw.h b/src/external/rlsw.h index 3c908a82f..1237c5147 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -937,8 +937,8 @@ typedef struct { int allocSz; // Allocated size SWfilter minFilter; // Minification filter SWfilter magFilter; // Magnification filter - SWwrap sWrap; // texcoord.x wrap mode - SWwrap tWrap; // texcoord.y wrap mode + SWwrap sWrap; // Wrap mode for texcoord.x + SWwrap tWrap; // Wrap mode for texcoord.y float tx; // Texel width float ty; // Texel height } sw_texture_t; @@ -1285,7 +1285,7 @@ static inline uint16_t sw_float_to_half_ui(uint32_t ui) // Overflow: infinity; 143 encodes exponent 16 h = (em >= (143 << 23))? 0x7c00 : h; - // NaN; note that we convert all types of NaN to qNaN + // NaN; note that all types of NaN are converted to qNaN h = (em > (255 << 23))? 0x7e00 : h; return (uint16_t)(s | h); @@ -1302,8 +1302,8 @@ static inline uint32_t sw_half_to_float_ui(uint16_t h) // Denormal: flush to zero r = (em < (1 << 10))? 0 : r; - // Infinity/NaN; note that we preserve NaN payload as a byproduct of unifying inf/nan cases - // 112 is an exponent bias fixup; since we already applied it once, applying it twice converts 31 to 255 + // Infinity/NaN; note that NaN payload is preeserved as a byproduct of unifying inf/nan cases + // 112 is an exponent bias fixup; since it was already applied once, applying it twice converts 31 to 255 r += (em >= (31 << 10))? (112 << 23) : 0; return s | r; @@ -2487,8 +2487,9 @@ static inline void sw_texture_sample_linear(float *SW_RESTRICT color, const sw_t static inline void sw_texture_sample(float *SW_RESTRICT color, const sw_texture_t *SW_RESTRICT tex, float u, float v, float dUdx, float dUdy, float dVdx, float dVdy) { - // Previous method: There is no need to compute the square root - // because using the squared value, the comparison remains (L2 > 1.0f*1.0f) + // NOTE: Commented there is the previous method used + // There was no need to compute the square root because + // using the squared value, the comparison remains (L2 > 1.0f*1.0f) //float du = sqrtf(dUdx*dUdx + dUdy*dUdy); //float dv = sqrtf(dVdx*dVdx + dVdy*dVdy); //float L = (du > dv)? du : dv; @@ -2534,10 +2535,9 @@ static inline void sw_default_framebuffer_free(sw_default_framebuffer_t *fb) static inline void sw_framebuffer_fill_color(sw_texture_t *colorBuffer, const float color[4]) { - // NOTE: MSVC doesn't support VLA, so we allocate the largest possible size - //uint8_t pixel[SW_FRAMEBUFFER_COLOR_SIZE]; - - uint8_t pixel[16]; + // NOTE: MSVC doesn't support VLA, so the largest possible size is allocated: 16 bytes + //uint8_t pixel[SW_FRAMEBUFFER_COLOR_SIZE] = { 0 }; + uint8_t pixel[16] = { 0 }; SW_FRAMEBUFFER_COLOR_SET(pixel, color, 0); uint8_t *dst = (uint8_t *)colorBuffer->pixels; @@ -2574,10 +2574,9 @@ static inline void sw_framebuffer_fill_color(sw_texture_t *colorBuffer, const fl static inline void sw_framebuffer_fill_depth(sw_texture_t *depthBuffer, float depth) { - // NOTE: MSVC doesn't support VLA, so we allocate the largest possible size - //uint8_t pixel[SW_FRAMEBUFFER_DEPTH_SIZE]; - - uint8_t pixel[4]; + // NOTE: MSVC doesn't support VLA, so the largest possible size is allocated: 4 bytes + //uint8_t pixel[SW_FRAMEBUFFER_DEPTH_SIZE] = { 0 }; + uint8_t pixel[4] = { 0 }; SW_FRAMEBUFFER_DEPTH_SET(pixel, depth, 0); uint8_t *dst = (uint8_t *)depthBuffer->pixels; @@ -3740,7 +3739,7 @@ static inline void sw_poly_fill_render(uint32_t state) //------------------------------------------------------------------------------------------- static void sw_immediate_push_vertex(const float position[4], const float color[4], const float texcoord[2]) { - // Check if we are in a valid draw mode + // Check if the draw mode is valid if (!sw_is_draw_mode_valid(RLSW.drawMode)) { RLSW.errCode = SW_INVALID_OPERATION; @@ -4480,7 +4479,7 @@ void swOrtho(double left, double right, double bottom, double top, double znear, void swBegin(SWdraw mode) { - // Check if we can render + // Check if render is possible if (!sw_is_ready_to_render()) { RLSW.errCode = SW_INVALID_OPERATION; From da9e881092469e15bec7e6eae109c615132fd8c5 Mon Sep 17 00:00:00 2001 From: Amine Laaboudi <71769287+0x00650a@users.noreply.github.com> Date: Wed, 18 Mar 2026 16:09:27 -0400 Subject: [PATCH 195/319] Fix uninstall process to remove rcamera header file (#5671) Co-authored-by: 0x00650a --- src/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Makefile b/src/Makefile index 026888614..8f13b4355 100644 --- a/src/Makefile +++ b/src/Makefile @@ -959,6 +959,7 @@ ifeq ($(ROOT),root) endif rm --force --interactive --verbose $(RAYLIB_H_INSTALL_PATH)/raylib.h rm --force --interactive --verbose $(RAYLIB_H_INSTALL_PATH)/raymath.h + rm --force --interactive --verbose $(RAYLIB_H_INSTALL_PATH)/rcamera.h rm --force --interactive --verbose $(RAYLIB_H_INSTALL_PATH)/rlgl.h @echo "raylib development files removed!" else From 7fef65a3fec491551aa3d76a297a99a5b376a973 Mon Sep 17 00:00:00 2001 From: devel Date: Thu, 19 Mar 2026 18:17:27 -0400 Subject: [PATCH 196/319] Make fogColor in the fog tutorial a parameter (#5672) * uniform fogColor fogColor is now a parameter * use new fog color parameter * convert ambient to Vector4 --- examples/shaders/resources/shaders/glsl330/fog.fs | 5 +---- examples/shaders/shaders_fog_rendering.c | 7 ++++++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/shaders/resources/shaders/glsl330/fog.fs b/examples/shaders/resources/shaders/glsl330/fog.fs index 67d0795ec..ac0132ff0 100644 --- a/examples/shaders/resources/shaders/glsl330/fog.fs +++ b/examples/shaders/resources/shaders/glsl330/fog.fs @@ -37,6 +37,7 @@ struct Light { uniform Light lights[MAX_LIGHTS]; uniform vec4 ambient; uniform vec3 viewPos; +uniform vec4 fogColor; uniform float fogDensity; void main() @@ -77,10 +78,6 @@ void main() // Fog calculation float dist = length(viewPos - fragPosition); - // these could be parameters... - const vec4 fogColor = vec4(0.5, 0.5, 0.5, 1.0); - //const float fogDensity = 0.16; - // Exponential fog float fogFactor = 1.0/exp((dist*fogDensity)*(dist*fogDensity)); diff --git a/examples/shaders/shaders_fog_rendering.c b/examples/shaders/shaders_fog_rendering.c index bc0e54ceb..a1044312d 100644 --- a/examples/shaders/shaders_fog_rendering.c +++ b/examples/shaders/shaders_fog_rendering.c @@ -72,8 +72,13 @@ int main(void) shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos"); // Ambient light level + Vector4 ambient = (Vector4){ 0.2f, 0.2f, 0.2f, 1.0f }; int ambientLoc = GetShaderLocation(shader, "ambient"); - SetShaderValue(shader, ambientLoc, (float[4]){ 0.2f, 0.2f, 0.2f, 1.0f }, SHADER_UNIFORM_VEC4); + SetShaderValue(shader, ambientLoc, &ambient, SHADER_UNIFORM_VEC4); + + Vector4 fogColor = ColorNormalize(GRAY); + int fogColorLoc = GetShaderLocation(shader, "fogColor"); + SetShaderValue(shader, fogColorLoc, &fogColor, SHADER_UNIFORM_VEC4); float fogDensity = 0.15f; int fogDensityLoc = GetShaderLocation(shader, "fogDensity"); From bca4f83a024ea8a6062c2d3f5773cbfe978fe289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Mich=C3=A1lek?= Date: Thu, 19 Mar 2026 23:19:44 +0100 Subject: [PATCH 197/319] required change for ESP-IDF 6 (#5674) --- src/external/rlsw.h | 10 ++++++++++ src/rcore.c | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/external/rlsw.h b/src/external/rlsw.h index 1237c5147..2be21def5 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -689,6 +689,7 @@ SWAPI void swClear(uint32_t bitmask); SWAPI void swBlendFunc(SWfactor sfactor, SWfactor dfactor); SWAPI void swPolygonMode(SWpoly mode); SWAPI void swCullFace(SWface face); +SWAPI void *swGetColorBuffer(int *width, int *height); // Restored for ESP-IDF compatibility SWAPI void swPointSize(float size); SWAPI void swLineWidth(float width); @@ -4200,6 +4201,15 @@ void swCullFace(SWface face) RLSW.cullFace = face; } +// Get direct pointer to the default framebuffer's pixel data +// Restored for ESP-IDF compatibility - removed in Raylib 6.0 PR #5655 +void *swGetColorBuffer(int *width, int *height) +{ + if (width) *width = RLSW.colorBuffer->width; + if (height) *height = RLSW.colorBuffer->height; + return RLSW.colorBuffer->pixels; +} + void swPointSize(float size) { RLSW.pointRadius = floorf(size*0.5f); diff --git a/src/rcore.c b/src/rcore.c index de1bb371c..0bb32ba27 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -682,6 +682,18 @@ void InitWindow(int width, int height, const char *title) TRACELOG(LOG_WARNING, "SYSTEM: Failed to initialize platform"); return; } + + // FIX: Initialize render dimensions for embedded platforms + // On desktop platforms (GLFW, SDL, etc.), CORE.Window.render.width/height are + // set during window creation. On embedded platforms with no window manager, + // InitPlatform() doesn't set these values, so we initialize them here from + // the screen dimensions (which are set from the InitWindow parameters). + // This fix is required for embedded platforms. + if (CORE.Window.render.width == 0 || CORE.Window.render.height == 0) + { + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + } //-------------------------------------------------------------- // Initialize rlgl default data (buffers and shaders) From 04c5dc449331bcb9ee27ff1fadae2ea4343f34ca Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Mar 2026 23:35:18 +0100 Subject: [PATCH 198/319] REVIEWED: PR #5674 --- src/external/rlsw.h | 19 +++++++++---------- src/rcore.c | 12 +++++------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/external/rlsw.h b/src/external/rlsw.h index 2be21def5..9d56ce55d 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -671,6 +671,7 @@ SWAPI void swClose(void); SWAPI bool swResize(int w, int h); SWAPI void swReadPixels(int x, int y, int w, int h, SWformat format, SWtype type, void *pixels); SWAPI void swBlitPixels(int xDst, int yDst, int wDst, int hDst, int xSrc, int ySrc, int wSrc, int hSrc, SWformat format, SWtype type, void *pixels); +SWAPI void *swGetColorBuffer(int *width, int *height); // Restored for ESP-IDF compatibility SWAPI void swEnable(SWstate state); SWAPI void swDisable(SWstate state); @@ -689,7 +690,6 @@ SWAPI void swClear(uint32_t bitmask); SWAPI void swBlendFunc(SWfactor sfactor, SWfactor dfactor); SWAPI void swPolygonMode(SWpoly mode); SWAPI void swCullFace(SWface face); -SWAPI void *swGetColorBuffer(int *width, int *height); // Restored for ESP-IDF compatibility SWAPI void swPointSize(float size); SWAPI void swLineWidth(float width); @@ -3952,6 +3952,14 @@ void swBlitPixels(int xDst, int yDst, int wDst, int hDst, int xSrc, int ySrc, in } } +// Get framefuffer pixel data pointer and size +void *swGetColorBuffer(int *width, int *height) +{ + if (width != NULL) *width = RLSW.framebuffer.color->width; + if (height != NULL) *height = RLSW.framebuffer.color->height; + return RLSW.framebuffer.color->pixels; +} + void swEnable(SWstate state) { switch (state) @@ -4201,15 +4209,6 @@ void swCullFace(SWface face) RLSW.cullFace = face; } -// Get direct pointer to the default framebuffer's pixel data -// Restored for ESP-IDF compatibility - removed in Raylib 6.0 PR #5655 -void *swGetColorBuffer(int *width, int *height) -{ - if (width) *width = RLSW.colorBuffer->width; - if (height) *height = RLSW.colorBuffer->height; - return RLSW.colorBuffer->pixels; -} - void swPointSize(float size) { RLSW.pointRadius = floorf(size*0.5f); diff --git a/src/rcore.c b/src/rcore.c index 0bb32ba27..5cdc8aca1 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -683,13 +683,11 @@ void InitWindow(int width, int height, const char *title) return; } - // FIX: Initialize render dimensions for embedded platforms - // On desktop platforms (GLFW, SDL, etc.), CORE.Window.render.width/height are - // set during window creation. On embedded platforms with no window manager, - // InitPlatform() doesn't set these values, so we initialize them here from - // the screen dimensions (which are set from the InitWindow parameters). - // This fix is required for embedded platforms. - if (CORE.Window.render.width == 0 || CORE.Window.render.height == 0) + // Initialize render dimensions for embedded platforms + // NOTE: On desktop platforms (GLFW, SDL, etc.), CORE.Window.render.width/height are set during window creation + // On embedded platforms with no window manager, InitPlatform() doesn't set these values, so they should be initialized + // here from screen dimensions (which are set from the InitWindow parameters) + if ((CORE.Window.render.width == 0) || (CORE.Window.render.height == 0)) { CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; From 7ae46fb2e6b245537e3cbd663d996aedea99fa07 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Thu, 19 Mar 2026 23:39:52 +0100 Subject: [PATCH 199/319] [rlsw] Micro-optimizations, tighter pipeline and cleanup (#5673) * auto generates all combinations of blending factors This adds a macro system that generate a function for each possible combination of blending factors, resulting in 11*11 functions, hence 121. This then allows for only one indirection and function call instead of two previously (assuming the first call was inlined). * rename dispatch tables for consistency * change blend funcs validity check Simplifies the validation of blend functions. Can allow `SW_SRC_ALPHA_SATURATE` as dst factor, but hey * disables blending when it requires alpha and there is none * review immediate rendering functions and attribute layout * prevent state changes during immediate record * reduce number of op for each vertex push + review primitive struct * simplified draw functions * review `sw_vertex_t` removes `float screen[2]`; each step stores the transformed coordinates in `float coord[4]`. This also simplifies vertex interpolation during triangle rasterization. * reduces unnecessary interpolation costs during triangle rasterization + cleanup * extends the simd color conversion to more cases * affine interpolation per blocks * long side check for each triangle line My mistake in a previous commit * style tweaks * select the read function on texture load This removes the per-pixel switch; it's slightly more efficient on my hardware, but probably a poor prediction Should remain profitable or at worst the same * use optionnal LUT for uint8_t -> float conversion * sets internal the number of vertices post-clipping and the epsilon clipping + a little cleanup * moves color conversion to math part * prevents sampling if it's a depth texture that is bound --- src/external/rlsw.h | 2349 +++++++++++++++++++++++-------------------- 1 file changed, 1241 insertions(+), 1108 deletions(-) diff --git a/src/external/rlsw.h b/src/external/rlsw.h index 9d56ce55d..b243f576f 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -153,17 +153,15 @@ #define SW_MAX_TEXTURES 128 #endif -// Under normal circumstances, clipping a polygon can add at most one vertex per clipping plane -// Considering the largest polygon involved is a quadrilateral (4 vertices), -// and that clipping occurs against both the frustum (6 planes) and the scissors (4 planes), -// the maximum number of vertices after clipping is: -// 4 (original vertices) + 6 (frustum planes) + 4 (scissors planes) = 14 -#ifndef SW_MAX_CLIPPED_POLYGON_VERTICES - #define SW_MAX_CLIPPED_POLYGON_VERTICES 14 -#endif - -#ifndef SW_CLIP_EPSILON - #define SW_CLIP_EPSILON 1e-4f +// Enables the use of a lookup table for uint8_t to float conversion +// Requires an additional 1KB of global memory +// Disabled when SIMD intrinsics are enabled +#ifndef SW_USE_COLOR_LUT + #if RLSW_USE_SIMD_INTRINSICS + #define SW_USE_COLOR_LUT false + #else + #define SW_USE_COLOR_LUT true + #endif #endif //---------------------------------------------------------------------------------- @@ -860,6 +858,13 @@ SWAPI void swGetFramebufferAttachmentParameteriv(SWattachment attachment, SWatta #define SW_DEG2RAD (SW_PI/180.0f) #define SW_RAD2DEG (180.0f/SW_PI) +// When clipping a convex polygon against a plane, at most one vertex is added. +// Starting from a quadrilateral (4 vertices), clipped sequentially against +// the frustum (6 planes) then the scissor rectangle (4 planes): +// 4 + 6 + 4 = 14 vertices maximum. +#define SW_MAX_CLIPPED_POLYGON_VERTICES 14 +#define SW_CLIP_EPSILON 1e-4f + #define SW_HANDLE_NULL 0u #define SW_POOL_SLOT_LIVE 0x80u // bit7 of the generation byte #define SW_POOL_SLOT_VER_MASK 0x7Fu // bits6:0 = anti-ABA counter @@ -867,12 +872,12 @@ SWAPI void swGetFramebufferAttachmentParameteriv(SWattachment attachment, SWatta #define SW_CONCAT(a, b) a##b #define SW_CONCATX(a, b) SW_CONCAT(a, b) -#define SW_FRAMEBUFFER_COLOR8_GET(c,p,o) SW_CONCATX(sw_pixel_get_color8_, SW_FRAMEBUFFER_COLOR_TYPE)((c),(p),(o)) -#define SW_FRAMEBUFFER_COLOR_GET(c,p,o) SW_CONCATX(sw_pixel_get_color_, SW_FRAMEBUFFER_COLOR_TYPE)((c),(p),(o)) -#define SW_FRAMEBUFFER_COLOR_SET(p,c,o) SW_CONCATX(sw_pixel_set_color_, SW_FRAMEBUFFER_COLOR_TYPE)((p),(c),(o)) +#define SW_FRAMEBUFFER_COLOR8_GET(c,p,o) SW_CONCATX(sw_pixel_read_color8_, SW_FRAMEBUFFER_COLOR_TYPE)((c),(p),(o)) +#define SW_FRAMEBUFFER_COLOR_GET(c,p,o) SW_CONCATX(sw_pixel_read_color_, SW_FRAMEBUFFER_COLOR_TYPE)((c),(p),(o)) +#define SW_FRAMEBUFFER_COLOR_SET(p,c,o) SW_CONCATX(sw_pixel_write_color_, SW_FRAMEBUFFER_COLOR_TYPE)((p),(c),(o)) -#define SW_FRAMEBUFFER_DEPTH_GET(p,o) SW_CONCATX(sw_pixel_get_depth_, SW_FRAMEBUFFER_DEPTH_TYPE)((p),(o)) -#define SW_FRAMEBUFFER_DEPTH_SET(p,d,o) SW_CONCATX(sw_pixel_set_depth_, SW_FRAMEBUFFER_DEPTH_TYPE)((p),(d),(o)) +#define SW_FRAMEBUFFER_DEPTH_GET(p,o) SW_CONCATX(sw_pixel_read_depth_, SW_FRAMEBUFFER_DEPTH_TYPE)((p),(o)) +#define SW_FRAMEBUFFER_DEPTH_SET(p,d,o) SW_CONCATX(sw_pixel_write_depth_, SW_FRAMEBUFFER_DEPTH_TYPE)((p),(d),(o)) #define SW_FRAMEBUFFER_COLOR_FORMAT SW_CONCATX(SW_PIXELFORMAT_COLOR_, SW_FRAMEBUFFER_COLOR_TYPE) #define SW_FRAMEBUFFER_DEPTH_FORMAT SW_CONCATX(SW_PIXELFORMAT_DEPTH_, SW_FRAMEBUFFER_DEPTH_TYPE) @@ -880,15 +885,15 @@ SWAPI void swGetFramebufferAttachmentParameteriv(SWattachment attachment, SWatta #define SW_FRAMEBUFFER_COLOR_SIZE SW_PIXELFORMAT_SIZE[SW_FRAMEBUFFER_COLOR_FORMAT] #define SW_FRAMEBUFFER_DEPTH_SIZE SW_PIXELFORMAT_SIZE[SW_FRAMEBUFFER_DEPTH_FORMAT] -#define SW_STATE_CHECK(flags) (SW_STATE_CHECK_EX(RLSW.stateFlags, (flags))) -#define SW_STATE_CHECK_EX(state, flags) (((state) & (flags)) == (flags)) - #define SW_STATE_SCISSOR_TEST (1 << 0) #define SW_STATE_TEXTURE_2D (1 << 1) #define SW_STATE_DEPTH_TEST (1 << 2) #define SW_STATE_CULL_FACE (1 << 3) #define SW_STATE_BLEND (1 << 4) +#define SW_BLEND_FLAG_NOOP (1 << 0) +#define SW_BLEND_FLAG_NEEDS_ALPHA (1 << 1) + //---------------------------------------------------------------------------------- // Module Types and Structures Definition //---------------------------------------------------------------------------------- @@ -919,29 +924,56 @@ typedef enum { SW_PIXELFORMAT_COUNT } sw_pixelformat_t; +typedef enum { + SW_PIXEL_ALPHA_NONE = 0, // No transparency + SW_PIXEL_ALPHA_BIN, // Binary transparency + SW_PIXEL_ALPHA_YES, // Contains transparency +} sw_pixel_alpha_t; + +// Forward declarations +typedef struct sw_vertex sw_vertex_t; + +// Pixel getter functions +typedef void (*sw_pixel_read_color8_f)(uint8_t *SW_RESTRICT, const void *SW_RESTRICT, uint32_t); +typedef void (*sw_pixel_read_color_f)(float *SW_RESTRICT, const void *SW_RESTRICT, uint32_t); + +// Pixel setter functions +typedef void (*sw_pixel_write_color8_f)(void *SW_RESTRICT, const uint8_t *SW_RESTRICT, uint32_t); +typedef void (*sw_pixel_write_color_f)(void *SW_RESTRICT, const float *SW_RESTRICT, uint32_t); + +// Color blending function +typedef void (*sw_blend_f)(float *SW_RESTRICT, const float *SW_RESTRICT); + +// Rasterizer functions +typedef void (*sw_raster_triangle_f)(const sw_vertex_t*, const sw_vertex_t*, const sw_vertex_t*); +typedef void (*sw_raster_quad_f)(const sw_vertex_t*, const sw_vertex_t*, const sw_vertex_t*, const sw_vertex_t*); +typedef void (*sw_raster_line_f)(const sw_vertex_t*, const sw_vertex_t*); +typedef void (*sw_raster_point_f)(const sw_vertex_t*); + typedef float sw_matrix_t[4*4]; -typedef struct { +typedef struct sw_vertex { float position[4]; // Position coordinates - float texcoord[2]; // Texture coordinates float color[4]; // Color value (RGBA) - - float homogeneous[4]; // Homogeneous coordinates - float screen[2]; // Screen coordinates + float texcoord[2]; // Texture coordinates + float coord[4]; // Clip space (x,y,z,w) -> NDC (after /w) -> screen space (x,y,z,1/w) } sw_vertex_t; typedef struct { - void *pixels; // Texture pixels - sw_pixelformat_t format; // Texture format - int width, height; // Dimensions of the texture - int wMinus1, hMinus1; // Dimensions minus one - int allocSz; // Allocated size - SWfilter minFilter; // Minification filter - SWfilter magFilter; // Magnification filter - SWwrap sWrap; // Wrap mode for texcoord.x - SWwrap tWrap; // Wrap mode for texcoord.y - float tx; // Texel width - float ty; // Texel height + void *pixels; // Texture pixels + sw_pixel_read_color8_f readColor8; // Texel read RGBA8 + sw_pixel_read_color_f readColor; // Texel read RGBA32F + sw_pixelformat_t format; // Texture format + sw_pixel_alpha_t alpha; // Texture alpha mode + int width, height; // Dimensions of the texture + int wMinus1, hMinus1; // Dimensions minus one + int allocSz; // Allocated size + SWfilter minFilter; // Minification filter + SWfilter magFilter; // Magnification filter + SWwrap sWrap; // Wrap mode for texcoord.x + SWwrap tWrap; // Wrap mode for texcoord.y + float tx; // Texel width + float ty; // Texel height } sw_texture_t; typedef struct { @@ -964,12 +996,6 @@ typedef struct { size_t stride; } sw_pool_t; -typedef void (*sw_blend_factor_t)(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst); -typedef void (*sw_raster_triangle_f)(const sw_vertex_t *v0, const sw_vertex_t *v1, const sw_vertex_t *v2); -typedef void (*sw_raster_quad_f)(const sw_vertex_t *v0, const sw_vertex_t *v1, const sw_vertex_t *v2, const sw_vertex_t *v3); -typedef void (*sw_raster_line_f)(const sw_vertex_t *v0, const sw_vertex_t *v1); -typedef void (*sw_raster_point_f)(const sw_vertex_t *v); - // Graphic context data structure typedef struct { sw_default_framebuffer_t framebuffer; // Default framebuffer @@ -985,20 +1011,20 @@ typedef struct { float scClipMin[2]; // Scissor rectangle minimum renderable point in clip space float scClipMax[2]; // Scissor rectangle maximum renderable point in clip space + struct { + sw_vertex_t buffer[SW_MAX_CLIPPED_POLYGON_VERTICES]; // Buffer used for storing primitive vertices, used for processing and rendering + int vertexCount; // Number of vertices in the primtive buffer + float color[4]; // Current color for the next pushed vertex + float texcoord[2]; // Current texture coordinates for the next push vertex + bool hasColorAlpha; // Flag indicating whether the current primitive contains transparency + } primitive; + struct { float *positions; float *texcoords; uint8_t *colors; } array; - struct { - float texcoord[2]; - float color[4]; - } current; - - sw_vertex_t vertexBuffer[SW_MAX_CLIPPED_POLYGON_VERTICES]; // Buffer used for storing primitive vertices, used for processing and rendering - int vertexCounter; // Number of vertices in 'ctx.vertexBuffer' - SWdraw drawMode; // Current primitive mode (e.g., lines, triangles) SWpoly polyMode; // Current polygon filling mode (e.g., lines, triangles) float pointRadius; // Rasterized point radius @@ -1023,16 +1049,16 @@ typedef struct { sw_texture_t *boundTexture; // Texture currently bound sw_pool_t texturePool; // Texture object pool - SWfactor srcFactor; // Source blending factor - SWfactor dstFactor; // Destination bleending factor - - sw_blend_factor_t srcFactorFunc; // Source blend function - sw_blend_factor_t dstFactorFunc; // Destination blend function + SWfactor srcFactor; // Source blending factor + SWfactor dstFactor; // Destination bleending factor + uint32_t blendFlags; // Flags about the current blend mode + sw_blend_f blendFunc; // Source blend function SWface cullFace; // Faces to cull SWerrcode errCode; // Last error code - uint32_t stateFlags; + uint32_t userState; // User-defined pipeline state + uint32_t rasterState; // Cleaned pipeline state for the rasterizer } sw_context_t; //---------------------------------------------------------------------------------- @@ -1040,9 +1066,35 @@ typedef struct { //---------------------------------------------------------------------------------- static sw_context_t RLSW = { 0 }; +#if SW_USE_COLOR_LUT +static float SW_LUT_UINT8_TO_FLOAT[256] = { 0 }; +#endif + //---------------------------------------------------------------------------------- // Internal Constants Definition //---------------------------------------------------------------------------------- +// Pixel formats that has an alpha channel +static const sw_pixel_alpha_t SW_PIXELFORMAT_ALPHA[SW_PIXELFORMAT_COUNT] = +{ + [SW_PIXELFORMAT_COLOR_GRAYSCALE] = SW_PIXEL_ALPHA_NONE, + [SW_PIXELFORMAT_COLOR_GRAYALPHA] = SW_PIXEL_ALPHA_YES, + [SW_PIXELFORMAT_COLOR_R3G3B2] = SW_PIXEL_ALPHA_NONE, + [SW_PIXELFORMAT_COLOR_R5G6B5] = SW_PIXEL_ALPHA_NONE, + [SW_PIXELFORMAT_COLOR_R8G8B8] = SW_PIXEL_ALPHA_NONE, + [SW_PIXELFORMAT_COLOR_R5G5B5A1] = SW_PIXEL_ALPHA_BIN, + [SW_PIXELFORMAT_COLOR_R4G4B4A4] = SW_PIXEL_ALPHA_YES, + [SW_PIXELFORMAT_COLOR_R8G8B8A8] = SW_PIXEL_ALPHA_YES, + [SW_PIXELFORMAT_COLOR_R32] = SW_PIXEL_ALPHA_NONE, + [SW_PIXELFORMAT_COLOR_R32G32B32] = SW_PIXEL_ALPHA_NONE, + [SW_PIXELFORMAT_COLOR_R32G32B32A32] = SW_PIXEL_ALPHA_YES, + [SW_PIXELFORMAT_COLOR_R16] = SW_PIXEL_ALPHA_NONE, + [SW_PIXELFORMAT_COLOR_R16G16B16] = SW_PIXEL_ALPHA_NONE, + [SW_PIXELFORMAT_COLOR_R16G16B16A16] = SW_PIXEL_ALPHA_YES, + [SW_PIXELFORMAT_DEPTH_D8] = SW_PIXEL_ALPHA_NONE, + [SW_PIXELFORMAT_DEPTH_D16] = SW_PIXEL_ALPHA_NONE, + [SW_PIXELFORMAT_DEPTH_D32] = SW_PIXEL_ALPHA_NONE, +}; + // Pixel formats sizes in bytes static const int SW_PIXELFORMAT_SIZE[SW_PIXELFORMAT_COUNT] = { @@ -1169,7 +1221,7 @@ static inline float sw_luminance(const float *color) return color[0]*0.299f + color[1]*0.587f + color[2]*0.114f; } -static inline void sw_lerp_vertex_PTCH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT a, const sw_vertex_t *SW_RESTRICT b, float t) +static inline void sw_lerp_vertex_PCTH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT a, const sw_vertex_t *SW_RESTRICT b, float t) { const float tInv = 1.0f - t; @@ -1179,96 +1231,123 @@ static inline void sw_lerp_vertex_PTCH(sw_vertex_t *SW_RESTRICT out, const sw_ve out->position[2] = a->position[2]*tInv + b->position[2]*t; out->position[3] = a->position[3]*tInv + b->position[3]*t; - // Texture coordinate interpolation (2 components) - out->texcoord[0] = a->texcoord[0]*tInv + b->texcoord[0]*t; - out->texcoord[1] = a->texcoord[1]*tInv + b->texcoord[1]*t; - // Color interpolation (4 components) out->color[0] = a->color[0]*tInv + b->color[0]*t; out->color[1] = a->color[1]*tInv + b->color[1]*t; out->color[2] = a->color[2]*tInv + b->color[2]*t; out->color[3] = a->color[3]*tInv + b->color[3]*t; - // Homogeneous coordinate interpolation (4 components) - out->homogeneous[0] = a->homogeneous[0]*tInv + b->homogeneous[0]*t; - out->homogeneous[1] = a->homogeneous[1]*tInv + b->homogeneous[1]*t; - out->homogeneous[2] = a->homogeneous[2]*tInv + b->homogeneous[2]*t; - out->homogeneous[3] = a->homogeneous[3]*tInv + b->homogeneous[3]*t; + // Texture coordinate interpolation (2 components) + out->texcoord[0] = a->texcoord[0]*tInv + b->texcoord[0]*t; + out->texcoord[1] = a->texcoord[1]*tInv + b->texcoord[1]*t; + + // Pipeline coordinate interpolation (4 components) + out->coord[0] = a->coord[0]*tInv + b->coord[0]*t; + out->coord[1] = a->coord[1]*tInv + b->coord[1]*t; + out->coord[2] = a->coord[2]*tInv + b->coord[2]*t; + out->coord[3] = a->coord[3]*tInv + b->coord[3]*t; } -static inline void sw_get_vertex_grad_PTCH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT a, const sw_vertex_t *SW_RESTRICT b, float scale) +static inline void sw_get_vertex_grad_CTH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT a, const sw_vertex_t *SW_RESTRICT b, float scale) { - // Calculate gradients for Position - out->position[0] = (b->position[0] - a->position[0])*scale; - out->position[1] = (b->position[1] - a->position[1])*scale; - out->position[2] = (b->position[2] - a->position[2])*scale; - out->position[3] = (b->position[3] - a->position[3])*scale; - - // Calculate gradients for Texture coordinates - out->texcoord[0] = (b->texcoord[0] - a->texcoord[0])*scale; - out->texcoord[1] = (b->texcoord[1] - a->texcoord[1])*scale; - // Calculate gradients for Color out->color[0] = (b->color[0] - a->color[0])*scale; out->color[1] = (b->color[1] - a->color[1])*scale; out->color[2] = (b->color[2] - a->color[2])*scale; out->color[3] = (b->color[3] - a->color[3])*scale; - // Calculate gradients for Homogeneous coordinates - out->homogeneous[0] = (b->homogeneous[0] - a->homogeneous[0])*scale; - out->homogeneous[1] = (b->homogeneous[1] - a->homogeneous[1])*scale; - out->homogeneous[2] = (b->homogeneous[2] - a->homogeneous[2])*scale; - out->homogeneous[3] = (b->homogeneous[3] - a->homogeneous[3])*scale; + // Calculate gradients for Texture coordinates + out->texcoord[0] = (b->texcoord[0] - a->texcoord[0])*scale; + out->texcoord[1] = (b->texcoord[1] - a->texcoord[1])*scale; + + // Calculate gradients for Pipeline coordinates + out->coord[0] = (b->coord[0] - a->coord[0])*scale; + out->coord[1] = (b->coord[1] - a->coord[1])*scale; + out->coord[2] = (b->coord[2] - a->coord[2])*scale; + out->coord[3] = (b->coord[3] - a->coord[3])*scale; } -static inline void sw_add_vertex_grad_PTCH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT gradients) +static inline void sw_add_vertex_grad_CTH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT gradients) { - // Add gradients to Position - out->position[0] += gradients->position[0]; - out->position[1] += gradients->position[1]; - out->position[2] += gradients->position[2]; - out->position[3] += gradients->position[3]; - - // Add gradients to Texture coordinates - out->texcoord[0] += gradients->texcoord[0]; - out->texcoord[1] += gradients->texcoord[1]; - // Add gradients to Color out->color[0] += gradients->color[0]; out->color[1] += gradients->color[1]; out->color[2] += gradients->color[2]; out->color[3] += gradients->color[3]; - // Add gradients to Homogeneous coordinates - out->homogeneous[0] += gradients->homogeneous[0]; - out->homogeneous[1] += gradients->homogeneous[1]; - out->homogeneous[2] += gradients->homogeneous[2]; - out->homogeneous[3] += gradients->homogeneous[3]; + // Add gradients to Texture coordinates + out->texcoord[0] += gradients->texcoord[0]; + out->texcoord[1] += gradients->texcoord[1]; + + // Add gradients to Pipeline coordinates + out->coord[0] += gradients->coord[0]; + out->coord[1] += gradients->coord[1]; + out->coord[2] += gradients->coord[2]; + out->coord[3] += gradients->coord[3]; } -static inline void sw_add_vertex_grad_scaled_PTCH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT gradients, float scale) +static inline void sw_add_vertex_grad_scaled_CTH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT gradients, float scale) { - // Add gradients to Position - out->position[0] += gradients->position[0]*scale; - out->position[1] += gradients->position[1]*scale; - out->position[2] += gradients->position[2]*scale; - out->position[3] += gradients->position[3]*scale; - - // Add gradients to Texture coordinates - out->texcoord[0] += gradients->texcoord[0]*scale; - out->texcoord[1] += gradients->texcoord[1]*scale; - // Add gradients to Color out->color[0] += gradients->color[0]*scale; out->color[1] += gradients->color[1]*scale; out->color[2] += gradients->color[2]*scale; out->color[3] += gradients->color[3]*scale; - // Add gradients to Homogeneous coordinates - out->homogeneous[0] += gradients->homogeneous[0]*scale; - out->homogeneous[1] += gradients->homogeneous[1]*scale; - out->homogeneous[2] += gradients->homogeneous[2]*scale; - out->homogeneous[3] += gradients->homogeneous[3]*scale; + // Add gradients to Texture coordinates + out->texcoord[0] += gradients->texcoord[0]*scale; + out->texcoord[1] += gradients->texcoord[1]*scale; + + // Add gradients to Pipeline coordinates + out->coord[0] += gradients->coord[0]*scale; + out->coord[1] += gradients->coord[1]*scale; + out->coord[2] += gradients->coord[2]*scale; + out->coord[3] += gradients->coord[3]*scale; +} + +static inline void sw_get_vertex_grad_CH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT a, const sw_vertex_t *SW_RESTRICT b, float scale) +{ + // Calculate gradients for Color + out->color[0] = (b->color[0] - a->color[0])*scale; + out->color[1] = (b->color[1] - a->color[1])*scale; + out->color[2] = (b->color[2] - a->color[2])*scale; + out->color[3] = (b->color[3] - a->color[3])*scale; + + // Calculate gradients for Pipeline coordinates + out->coord[0] = (b->coord[0] - a->coord[0])*scale; + out->coord[1] = (b->coord[1] - a->coord[1])*scale; + out->coord[2] = (b->coord[2] - a->coord[2])*scale; + out->coord[3] = (b->coord[3] - a->coord[3])*scale; +} + +static inline void sw_add_vertex_grad_CH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT gradients) +{ + // Add gradients to Color + out->color[0] += gradients->color[0]; + out->color[1] += gradients->color[1]; + out->color[2] += gradients->color[2]; + out->color[3] += gradients->color[3]; + + // Add gradients to Pipeline coordinates + out->coord[0] += gradients->coord[0]; + out->coord[1] += gradients->coord[1]; + out->coord[2] += gradients->coord[2]; + out->coord[3] += gradients->coord[3]; +} + +static inline void sw_add_vertex_grad_scaled_CH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT gradients, float scale) +{ + // Add gradients to Color + out->color[0] += gradients->color[0]*scale; + out->color[1] += gradients->color[1]*scale; + out->color[2] += gradients->color[2]*scale; + out->color[3] += gradients->color[3]*scale; + + // Add gradients to Pipeline coordinates + out->coord[0] += gradients->coord[0]*scale; + out->coord[1] += gradients->coord[1]*scale; + out->coord[2] += gradients->coord[2]*scale; + out->coord[3] += gradients->coord[3]*scale; } // Half conversion functions @@ -1331,13 +1410,6 @@ static inline uint8_t sw_expand_4to8(uint32_t v) { return (uint8_t)((v << 4) | v static inline uint8_t sw_expand_5to8(uint32_t v) { return (uint8_t)((v << 3) | (v >> 2)); } static inline uint8_t sw_expand_6to8(uint32_t v) { return (uint8_t)((v << 2) | (v >> 4)); } -static inline float sw_expand_1tof(uint32_t v) { return v? 1.0f : 0.0f; } -static inline float sw_expand_2tof(uint32_t v) { return (float)v*(1.0f/3.0f); } -static inline float sw_expand_3tof(uint32_t v) { return (float)v*(1.0f/7.0f); } -static inline float sw_expand_4tof(uint32_t v) { return (float)v*(1.0f/15.0f); } -static inline float sw_expand_5tof(uint32_t v) { return (float)v*(1.0f/31.0f); } -static inline float sw_expand_6tof(uint32_t v) { return (float)v*(1.0f/63.0f); } - static inline uint32_t sw_compress_8to1(uint8_t v) { return v >> 7; } static inline uint32_t sw_compress_8to2(uint8_t v) { return v >> 6; } static inline uint32_t sw_compress_8to3(uint8_t v) { return v >> 5; } @@ -1345,19 +1417,106 @@ static inline uint32_t sw_compress_8to4(uint8_t v) { return v >> 4; } static inline uint32_t sw_compress_8to5(uint8_t v) { return v >> 3; } static inline uint32_t sw_compress_8to6(uint8_t v) { return v >> 2; } -static inline uint32_t sw_compress_fto1(float v) { return (v >= 0.5f)? 1 : 0; } -static inline uint32_t sw_compress_fto2(float v) { return (uint32_t)(v*3.0f + 0.5f) & 0x03; } -static inline uint32_t sw_compress_fto3(float v) { return (uint32_t)(v*7.0f + 0.5f) & 0x07; } -static inline uint32_t sw_compress_fto4(float v) { return (uint32_t)(v*15.0f + 0.5f) & 0x0F; } -static inline uint32_t sw_compress_fto5(float v) { return (uint32_t)(v*31.0f + 0.5f) & 0x1F; } -static inline uint32_t sw_compress_fto6(float v) { return (uint32_t)(v*63.0f + 0.5f) & 0x3F; } +static inline void sw_color8_to_color(float *SW_RESTRICT dst, const uint8_t *SW_RESTRICT src) +{ +#if defined(SW_HAS_NEON) + uint8x8_t bytes = vreinterpret_u8_u32(vld1_dup_u32((const uint32_t *)src)); + uint16x8_t words = vmovl_u8(bytes); + uint32x4_t dwords = vmovl_u16(vget_low_u16(words)); + float32x4_t fvals = vmulq_f32(vcvtq_f32_u32(dwords), vdupq_n_f32(SW_INV_255)); + vst1q_f32(dst, fvals); + +#elif defined(SW_HAS_SSE41) + __m128i bytes = _mm_loadu_si32(src); + __m128 fvals = _mm_mul_ps(_mm_cvtepi32_ps(_mm_cvtepu8_epi32(bytes)), _mm_set1_ps(SW_INV_255)); + _mm_storeu_ps(dst, fvals); + +#elif defined(SW_HAS_SSE2) + __m128i zero = _mm_setzero_si128(); + __m128i bytes = _mm_loadu_si32(src); + __m128i words = _mm_unpacklo_epi8(bytes, zero); + __m128i dwords = _mm_unpacklo_epi16(words, zero); + __m128 fvals = _mm_mul_ps(_mm_cvtepi32_ps(dwords), _mm_set1_ps(SW_INV_255)); + _mm_storeu_ps(dst, fvals); + +#elif defined(SW_HAS_RVV) + // TODO: WARNING: Sample code generated by AI, needs testing and review + size_t vl = __riscv_vsetvl_e8m1(4); // Set vector length for 8-bit input elements + vuint8m1_t vsrc_u8 = __riscv_vle8_v_u8m1(src, vl); // Load 4 unsigned 8-bit integers + vuint32m1_t vsrc_u32 = __riscv_vwcvt_xu_u_v_u32m1(vsrc_u8, vl); // Widen to 32-bit unsigned integers + vfloat32m1_t vsrc_f32 = __riscv_vfcvt_f_xu_v_f32m1(vsrc_u32, vl); // Convert to float32 + vfloat32m1_t vnorm = __riscv_vfmul_vf_f32m1(vsrc_f32, SW_INV_255, vl); // Multiply by 1/255.0 to normalize + __riscv_vse32_v_f32m1(dst, vnorm, vl); // Store result + +#elif SW_USE_COLOR_LUT + dst[0] = SW_LUT_UINT8_TO_FLOAT[src[0]]; + dst[1] = SW_LUT_UINT8_TO_FLOAT[src[1]]; + dst[2] = SW_LUT_UINT8_TO_FLOAT[src[2]]; + dst[3] = SW_LUT_UINT8_TO_FLOAT[src[3]]; + +#else + dst[0] = (float)src[0]*SW_INV_255; + dst[1] = (float)src[1]*SW_INV_255; + dst[2] = (float)src[2]*SW_INV_255; + dst[3] = (float)src[3]*SW_INV_255; +#endif +} + +static inline void sw_color_to_color8(uint8_t *SW_RESTRICT dst, const float *SW_RESTRICT src) +{ +#if defined(SW_HAS_NEON) + float32x4_t fvals = vmulq_f32(vld1q_f32(src), vdupq_n_f32(255.0f)); + uint32x4_t i32 = vcvtq_u32_f32(fvals); + uint16x4_t i16 = vmovn_u32(i32); + uint8x8_t i8 = vmovn_u16(vcombine_u16(i16, i16)); + vst1_lane_u32((uint32_t *)dst, vreinterpret_u32_u8(i8), 0); + +#elif defined(SW_HAS_SSE41) + __m128 fvals = _mm_mul_ps(_mm_loadu_ps(src), _mm_set1_ps(255.0f)); + __m128i i32 = _mm_cvttps_epi32(fvals); + __m128i i16 = _mm_packus_epi32(i32, i32); + __m128i i8 = _mm_packus_epi16(i16, i16); + _mm_storeu_si32(dst, i8); + +#elif defined(SW_HAS_SSE2) + __m128 fvals = _mm_mul_ps(_mm_loadu_ps(src), _mm_set1_ps(255.0f)); + __m128i i32 = _mm_cvttps_epi32(fvals); + __m128i i16 = _mm_packs_epi32(i32, i32); + __m128i i8 = _mm_packus_epi16(i16, i16); + _mm_storeu_si32(dst, i8); + +#elif defined(SW_HAS_RVV) + // TODO: WARNING: Sample code generated by AI, needs testing and review + // REVIEW: It shouldn't perform so many operations; take inspiration from other versions + // NOTE: RVV 1.0 specs define the use of __riscv_ prefix for instrinsic functions + size_t vl = __riscv_vsetvl_e32m1(4); // Load up to 4 floats into a vector register + vfloat32m1_t vsrc = __riscv_vle32_v_f32m1(src, vl); // Load float32 values + + // Multiply by 255.0f and add 0.5f for rounding + vfloat32m1_t vscaled = __riscv_vfmul_vf_f32m1(vsrc, 255.0f, vl); + vscaled = __riscv_vfadd_vf_f32m1(vscaled, 0.5f, vl); + + // Convert to unsigned integer (truncate toward zero) + vuint32m1_t vu32 = __riscv_vfcvt_xu_f_v_u32m1(vscaled, vl); + + // Narrow from u32 -> u8 + vuint8m1_t vu8 = __riscv_vnclipu_wx_u8m1(vu32, 0, vl); // Round toward zero + __riscv_vse8_v_u8m1(dst, vu8, vl); // Store result + +#else + dst[0] = (uint8_t)(src[0]*255.0f); + dst[1] = (uint8_t)(src[1]*255.0f); + dst[2] = (uint8_t)(src[2]*255.0f); + dst[3] = (uint8_t)(src[3]*255.0f); +#endif +} //------------------------------------------------------------------------------------------- // Object pool functions //------------------------------------------------------------------------------------------- static bool sw_pool_init(sw_pool_t *pool, int capacity, size_t stride) { - *pool = (sw_pool_t){ 0 }; + *pool = (sw_pool_t) { 0 }; pool->data = SW_CALLOC(capacity, stride); pool->gen = SW_CALLOC(capacity, sizeof(uint8_t)); @@ -1492,51 +1651,6 @@ static inline bool sw_is_face_valid(int face) return (face == SW_FRONT || face == SW_BACK); } -static inline bool sw_is_blend_src_factor_valid(int blend) -{ - bool result = false; - - switch (blend) - { - case SW_ZERO: - case SW_ONE: - case SW_SRC_COLOR: - case SW_ONE_MINUS_SRC_COLOR: - case SW_SRC_ALPHA: - case SW_ONE_MINUS_SRC_ALPHA: - case SW_DST_ALPHA: - case SW_ONE_MINUS_DST_ALPHA: - case SW_DST_COLOR: - case SW_ONE_MINUS_DST_COLOR: - case SW_SRC_ALPHA_SATURATE: result = true; break; - default: break; - } - - return result; -} - -static inline bool sw_is_blend_dst_factor_valid(int blend) -{ - bool result = false; - - switch (blend) - { - case SW_ZERO: - case SW_ONE: - case SW_SRC_COLOR: - case SW_ONE_MINUS_SRC_COLOR: - case SW_SRC_ALPHA: - case SW_ONE_MINUS_SRC_ALPHA: - case SW_DST_ALPHA: - case SW_ONE_MINUS_DST_ALPHA: - case SW_DST_COLOR: - case SW_ONE_MINUS_DST_COLOR: result = true; break; - default: break; - } - - return result; -} - static inline bool sw_is_ready_to_render(void) { return (swCheckFramebufferStatus() == SW_FRAMEBUFFER_COMPLETE); @@ -1623,7 +1737,20 @@ static inline int sw_pixel_get_format(SWformat format, SWtype type) return SW_PIXELFORMAT_UNKNOWN; } -static inline void sw_pixel_get_color8_GRAYSCALE(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline bool sw_pixel_is_depth_format(sw_pixelformat_t format) +{ + switch (format) + { + case SW_PIXELFORMAT_DEPTH_D8: + case SW_PIXELFORMAT_DEPTH_D16: + case SW_PIXELFORMAT_DEPTH_D32: return true; + default: break; + } + + return false; +} + +static inline void sw_pixel_read_color8_GRAYSCALE(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { uint8_t gray = ((const uint8_t *)pixels)[offset]; color[0] = gray; @@ -1632,7 +1759,7 @@ static inline void sw_pixel_get_color8_GRAYSCALE(uint8_t *SW_RESTRICT color, con color[3] = 255; } -static inline void sw_pixel_get_color8_GRAYALPHA(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color8_GRAYALPHA(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { const uint8_t *src = &((const uint8_t *)pixels)[offset*2]; color[0] = src[0]; @@ -1641,7 +1768,7 @@ static inline void sw_pixel_get_color8_GRAYALPHA(uint8_t *SW_RESTRICT color, con color[3] = src[1]; } -static inline void sw_pixel_get_color8_R3G3B2(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color8_R3G3B2(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { uint8_t pixel = ((const uint8_t *)pixels)[offset]; color[0] = sw_expand_3to8((pixel >> 5) & 0x07); @@ -1650,7 +1777,7 @@ static inline void sw_pixel_get_color8_R3G3B2(uint8_t *SW_RESTRICT color, const color[3] = 255; } -static inline void sw_pixel_get_color8_R5G6B5(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color8_R5G6B5(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { uint16_t pixel = ((const uint16_t *)pixels)[offset]; color[0] = sw_expand_5to8((pixel >> 11) & 0x1F); @@ -1659,7 +1786,7 @@ static inline void sw_pixel_get_color8_R5G6B5(uint8_t *SW_RESTRICT color, const color[3] = 255; } -static inline void sw_pixel_get_color8_R8G8B8(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color8_R8G8B8(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { const uint8_t *src = &((const uint8_t *)pixels)[offset*3]; color[0] = src[0]; @@ -1668,7 +1795,7 @@ static inline void sw_pixel_get_color8_R8G8B8(uint8_t *SW_RESTRICT color, const color[3] = 255; } -static inline void sw_pixel_get_color8_R5G5B5A1(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color8_R5G5B5A1(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { uint16_t pixel = ((const uint16_t *)pixels)[offset]; color[0] = sw_expand_5to8((pixel >> 11) & 0x1F); @@ -1677,7 +1804,7 @@ static inline void sw_pixel_get_color8_R5G5B5A1(uint8_t *SW_RESTRICT color, cons color[3] = sw_expand_1to8( pixel & 0x01); } -static inline void sw_pixel_get_color8_R4G4B4A4(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color8_R4G4B4A4(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { uint16_t pixel = ((const uint16_t *)pixels)[offset]; color[0] = sw_expand_4to8((pixel >> 12) & 0x0F); @@ -1686,7 +1813,7 @@ static inline void sw_pixel_get_color8_R4G4B4A4(uint8_t *SW_RESTRICT color, cons color[3] = sw_expand_4to8( pixel & 0x0F); } -static inline void sw_pixel_get_color8_R8G8B8A8(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color8_R8G8B8A8(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { const uint8_t *src = &((const uint8_t *)pixels)[offset*4]; color[0] = src[0]; @@ -1695,7 +1822,7 @@ static inline void sw_pixel_get_color8_R8G8B8A8(uint8_t *SW_RESTRICT color, cons color[3] = src[3]; } -static inline void sw_pixel_get_color8_R32(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color8_R32(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { uint8_t gray = (uint8_t)(((const float *)pixels)[offset]*255.0f); color[0] = gray; @@ -1704,7 +1831,7 @@ static inline void sw_pixel_get_color8_R32(uint8_t *SW_RESTRICT color, const voi color[3] = 255; } -static inline void sw_pixel_get_color8_R32G32B32(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color8_R32G32B32(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { const float *src = &((const float *)pixels)[offset*3]; color[0] = (uint8_t)(src[0]*255.0f); @@ -1713,7 +1840,7 @@ static inline void sw_pixel_get_color8_R32G32B32(uint8_t *SW_RESTRICT color, con color[3] = 255; } -static inline void sw_pixel_get_color8_R32G32B32A32(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color8_R32G32B32A32(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { const float *src = &((const float *)pixels)[offset*4]; color[0] = (uint8_t)(src[0]*255.0f); @@ -1722,7 +1849,7 @@ static inline void sw_pixel_get_color8_R32G32B32A32(uint8_t *SW_RESTRICT color, color[3] = (uint8_t)(src[3]*255.0f); } -static inline void sw_pixel_get_color8_R16(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color8_R16(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { uint8_t gray = (uint8_t)(sw_half_to_float(((const uint16_t *)pixels)[offset])*255.0f); color[0] = gray; @@ -1731,7 +1858,7 @@ static inline void sw_pixel_get_color8_R16(uint8_t *SW_RESTRICT color, const voi color[3] = 255; } -static inline void sw_pixel_get_color8_R16G16B16(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color8_R16G16B16(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { const uint16_t *src = &((const uint16_t *)pixels)[offset*3]; color[0] = (uint8_t)(sw_half_to_float(src[0])*255.0f); @@ -1740,7 +1867,7 @@ static inline void sw_pixel_get_color8_R16G16B16(uint8_t *SW_RESTRICT color, con color[3] = 255; } -static inline void sw_pixel_get_color8_R16G16B16A16(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color8_R16G16B16A16(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { const uint16_t *src = &((const uint16_t *)pixels)[offset*4]; color[0] = (uint8_t)(sw_half_to_float(src[0])*255.0f); @@ -1749,53 +1876,49 @@ static inline void sw_pixel_get_color8_R16G16B16A16(uint8_t *SW_RESTRICT color, color[3] = (uint8_t)(sw_half_to_float(src[3])*255.0f); } -static inline void sw_pixel_get_color8(uint8_t *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset, sw_pixelformat_t format) +static inline sw_pixel_read_color8_f sw_pixel_get_read_color8_func(sw_pixelformat_t format) { switch (format) { - case SW_PIXELFORMAT_COLOR_GRAYSCALE: sw_pixel_get_color8_GRAYSCALE(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_GRAYALPHA: sw_pixel_get_color8_GRAYALPHA(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R3G3B2: sw_pixel_get_color8_R3G3B2(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R5G6B5: sw_pixel_get_color8_R5G6B5(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R8G8B8: sw_pixel_get_color8_R8G8B8(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R5G5B5A1: sw_pixel_get_color8_R5G5B5A1(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R4G4B4A4: sw_pixel_get_color8_R4G4B4A4(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R8G8B8A8: sw_pixel_get_color8_R8G8B8A8(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R32: sw_pixel_get_color8_R32(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R32G32B32: sw_pixel_get_color8_R32G32B32(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R32G32B32A32: sw_pixel_get_color8_R32G32B32A32(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R16: sw_pixel_get_color8_R16(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R16G16B16: sw_pixel_get_color8_R16G16B16(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R16G16B16A16: sw_pixel_get_color8_R16G16B16A16(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_GRAYSCALE: return sw_pixel_read_color8_GRAYSCALE; + case SW_PIXELFORMAT_COLOR_GRAYALPHA: return sw_pixel_read_color8_GRAYALPHA; + case SW_PIXELFORMAT_COLOR_R3G3B2: return sw_pixel_read_color8_R3G3B2; + case SW_PIXELFORMAT_COLOR_R5G6B5: return sw_pixel_read_color8_R5G6B5; + case SW_PIXELFORMAT_COLOR_R8G8B8: return sw_pixel_read_color8_R8G8B8; + case SW_PIXELFORMAT_COLOR_R5G5B5A1: return sw_pixel_read_color8_R5G5B5A1; + case SW_PIXELFORMAT_COLOR_R4G4B4A4: return sw_pixel_read_color8_R4G4B4A4; + case SW_PIXELFORMAT_COLOR_R8G8B8A8: return sw_pixel_read_color8_R8G8B8A8; + case SW_PIXELFORMAT_COLOR_R32: return sw_pixel_read_color8_R32; + case SW_PIXELFORMAT_COLOR_R32G32B32: return sw_pixel_read_color8_R32G32B32; + case SW_PIXELFORMAT_COLOR_R32G32B32A32: return sw_pixel_read_color8_R32G32B32A32; + case SW_PIXELFORMAT_COLOR_R16: return sw_pixel_read_color8_R16; + case SW_PIXELFORMAT_COLOR_R16G16B16: return sw_pixel_read_color8_R16G16B16; + case SW_PIXELFORMAT_COLOR_R16G16B16A16: return sw_pixel_read_color8_R16G16B16A16; case SW_PIXELFORMAT_UNKNOWN: case SW_PIXELFORMAT_DEPTH_D8: case SW_PIXELFORMAT_DEPTH_D16: case SW_PIXELFORMAT_DEPTH_D32: case SW_PIXELFORMAT_COUNT: - { - color[0] = 0.0f; - color[1] = 0.0f; - color[2] = 0.0f; - color[3] = 0.0f; - } break; default: break; } + + return NULL; } -static inline void sw_pixel_set_color8_GRAYSCALE(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color8_GRAYSCALE(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) { ((uint8_t *)pixels)[offset] = sw_luminance8(color); } -static inline void sw_pixel_set_color8_GRAYALPHA(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color8_GRAYALPHA(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) { uint8_t *dst = &((uint8_t *)pixels)[offset*2]; dst[0] = sw_luminance8(color); dst[1] = color[3]; } -static inline void sw_pixel_set_color8_R3G3B2(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color8_R3G3B2(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) { uint8_t pixel = (sw_compress_8to3(color[0]) << 5) | (sw_compress_8to3(color[1]) << 2) @@ -1803,7 +1926,7 @@ static inline void sw_pixel_set_color8_R3G3B2(void *SW_RESTRICT pixels, const ui ((uint8_t *)pixels)[offset] = pixel; } -static inline void sw_pixel_set_color8_R5G6B5(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color8_R5G6B5(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) { uint16_t pixel = (sw_compress_8to5(color[0]) << 11) | (sw_compress_8to6(color[1]) << 5) @@ -1811,7 +1934,7 @@ static inline void sw_pixel_set_color8_R5G6B5(void *SW_RESTRICT pixels, const ui ((uint16_t *)pixels)[offset] = pixel; } -static inline void sw_pixel_set_color8_R8G8B8(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color8_R8G8B8(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) { uint8_t *dst = &((uint8_t *)pixels)[offset*3]; dst[0] = color[0]; @@ -1819,7 +1942,7 @@ static inline void sw_pixel_set_color8_R8G8B8(void *SW_RESTRICT pixels, const ui dst[2] = color[2]; } -static inline void sw_pixel_set_color8_R5G5B5A1(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color8_R5G5B5A1(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) { uint16_t pixel = (sw_compress_8to5(color[0]) << 11) | (sw_compress_8to5(color[1]) << 6) @@ -1828,7 +1951,7 @@ static inline void sw_pixel_set_color8_R5G5B5A1(void *SW_RESTRICT pixels, const ((uint16_t *)pixels)[offset] = pixel; } -static inline void sw_pixel_set_color8_R4G4B4A4(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color8_R4G4B4A4(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) { uint16_t pixel = (sw_compress_8to4(color[0]) << 12) | (sw_compress_8to4(color[1]) << 8) @@ -1837,7 +1960,7 @@ static inline void sw_pixel_set_color8_R4G4B4A4(void *SW_RESTRICT pixels, const ((uint16_t *)pixels)[offset] = pixel; } -static inline void sw_pixel_set_color8_R8G8B8A8(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color8_R8G8B8A8(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) { uint8_t *dst = &((uint8_t *)pixels)[offset*4]; dst[0] = color[0]; @@ -1846,12 +1969,12 @@ static inline void sw_pixel_set_color8_R8G8B8A8(void *SW_RESTRICT pixels, const dst[3] = color[3]; } -static inline void sw_pixel_set_color8_R32(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color8_R32(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) { ((float *)pixels)[offset] = sw_luminance8(color)*SW_INV_255; } -static inline void sw_pixel_set_color8_R32G32B32(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color8_R32G32B32(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) { float *dst = &((float *)pixels)[offset*3]; dst[0] = color[0]*SW_INV_255; @@ -1859,7 +1982,7 @@ static inline void sw_pixel_set_color8_R32G32B32(void *SW_RESTRICT pixels, const dst[2] = color[2]*SW_INV_255; } -static inline void sw_pixel_set_color8_R32G32B32A32(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color8_R32G32B32A32(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) { float *dst = &((float *)pixels)[offset*4]; dst[0] = color[0]*SW_INV_255; @@ -1868,12 +1991,12 @@ static inline void sw_pixel_set_color8_R32G32B32A32(void *SW_RESTRICT pixels, co dst[3] = color[3]*SW_INV_255; } -static inline void sw_pixel_set_color8_R16(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color8_R16(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) { ((uint16_t *)pixels)[offset] = sw_float_to_half(sw_luminance8(color)*SW_INV_255); } -static inline void sw_pixel_set_color8_R16G16B16(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color8_R16G16B16(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) { uint16_t *dst = &((uint16_t *)pixels)[offset*3]; dst[0] = sw_float_to_half(color[0]*SW_INV_255); @@ -1881,7 +2004,7 @@ static inline void sw_pixel_set_color8_R16G16B16(void *SW_RESTRICT pixels, const dst[2] = sw_float_to_half(color[2]*SW_INV_255); } -static inline void sw_pixel_set_color8_R16G16B16A16(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color8_R16G16B16A16(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset) { uint16_t *dst = &((uint16_t *)pixels)[offset*4]; dst[0] = sw_float_to_half(color[0]*SW_INV_255); @@ -1890,35 +2013,37 @@ static inline void sw_pixel_set_color8_R16G16B16A16(void *SW_RESTRICT pixels, co dst[3] = sw_float_to_half(color[3]*SW_INV_255); } -static inline void sw_pixel_set_color8(void *SW_RESTRICT pixels, const uint8_t *SW_RESTRICT color, uint32_t offset, sw_pixelformat_t format) +static inline sw_pixel_write_color8_f sw_pixel_get_write_color8_func(sw_pixelformat_t format) { switch (format) { - case SW_PIXELFORMAT_COLOR_GRAYSCALE: sw_pixel_set_color8_GRAYSCALE(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_GRAYALPHA: sw_pixel_set_color8_GRAYALPHA(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R3G3B2: sw_pixel_set_color8_R3G3B2(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R5G6B5: sw_pixel_set_color8_R5G6B5(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R8G8B8: sw_pixel_set_color8_R8G8B8(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R5G5B5A1: sw_pixel_set_color8_R5G5B5A1(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R4G4B4A4: sw_pixel_set_color8_R4G4B4A4(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R8G8B8A8: sw_pixel_set_color8_R8G8B8A8(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R32: sw_pixel_set_color8_R32(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R32G32B32: sw_pixel_set_color8_R32G32B32(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R32G32B32A32: sw_pixel_set_color8_R32G32B32A32(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R16: sw_pixel_set_color8_R16(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R16G16B16: sw_pixel_set_color8_R16G16B16(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R16G16B16A16: sw_pixel_set_color8_R16G16B16A16(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_GRAYSCALE: return sw_pixel_write_color8_GRAYSCALE; + case SW_PIXELFORMAT_COLOR_GRAYALPHA: return sw_pixel_write_color8_GRAYALPHA; + case SW_PIXELFORMAT_COLOR_R3G3B2: return sw_pixel_write_color8_R3G3B2; + case SW_PIXELFORMAT_COLOR_R5G6B5: return sw_pixel_write_color8_R5G6B5; + case SW_PIXELFORMAT_COLOR_R8G8B8: return sw_pixel_write_color8_R8G8B8; + case SW_PIXELFORMAT_COLOR_R5G5B5A1: return sw_pixel_write_color8_R5G5B5A1; + case SW_PIXELFORMAT_COLOR_R4G4B4A4: return sw_pixel_write_color8_R4G4B4A4; + case SW_PIXELFORMAT_COLOR_R8G8B8A8: return sw_pixel_write_color8_R8G8B8A8; + case SW_PIXELFORMAT_COLOR_R32: return sw_pixel_write_color8_R32; + case SW_PIXELFORMAT_COLOR_R32G32B32: return sw_pixel_write_color8_R32G32B32; + case SW_PIXELFORMAT_COLOR_R32G32B32A32: return sw_pixel_write_color8_R32G32B32A32; + case SW_PIXELFORMAT_COLOR_R16: return sw_pixel_write_color8_R16; + case SW_PIXELFORMAT_COLOR_R16G16B16: return sw_pixel_write_color8_R16G16B16; + case SW_PIXELFORMAT_COLOR_R16G16B16A16: return sw_pixel_write_color8_R16G16B16A16; case SW_PIXELFORMAT_UNKNOWN: case SW_PIXELFORMAT_DEPTH_D8: case SW_PIXELFORMAT_DEPTH_D16: case SW_PIXELFORMAT_DEPTH_D32: - case SW_PIXELFORMAT_COUNT: break; + case SW_PIXELFORMAT_COUNT: default: break; } + + return NULL; } -static inline void sw_pixel_get_color_GRAYSCALE(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color_GRAYSCALE(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { float gray = ((const uint8_t *)pixels)[offset]*SW_INV_255; color[0] = gray; @@ -1927,7 +2052,7 @@ static inline void sw_pixel_get_color_GRAYSCALE(float *SW_RESTRICT color, const color[3] = 1.0f; } -static inline void sw_pixel_get_color_GRAYALPHA(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color_GRAYALPHA(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { const uint8_t *src = &((const uint8_t *)pixels)[offset*2]; float gray = src[0]*SW_INV_255; @@ -1937,93 +2062,47 @@ static inline void sw_pixel_get_color_GRAYALPHA(float *SW_RESTRICT color, const color[3] = src[1]*SW_INV_255; } -static inline void sw_pixel_get_color_R3G3B2(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color_R3G3B2(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { - uint8_t pixel = ((const uint8_t *)pixels)[offset]; - color[0] = sw_expand_3tof((pixel >> 5) & 0x07); - color[1] = sw_expand_3tof((pixel >> 2) & 0x07); - color[2] = sw_expand_2tof( pixel & 0x03); - color[3] = 1.0f; + uint8_t unpack[4]; + sw_pixel_read_color8_R3G3B2(unpack, pixels, offset); + sw_color8_to_color(color, unpack); } -static inline void sw_pixel_get_color_R5G6B5(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color_R5G6B5(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { - uint16_t pixel = ((const uint16_t *)pixels)[offset]; - color[0] = sw_expand_5tof((pixel >> 11) & 0x1F); - color[1] = sw_expand_6tof((pixel >> 5) & 0x3F); - color[2] = sw_expand_5tof( pixel & 0x1F); - color[3] = 1.0f; + uint8_t unpack[4]; + sw_pixel_read_color8_R5G6B5(unpack, pixels, offset); + sw_color8_to_color(color, unpack); } -static inline void sw_pixel_get_color_R8G8B8(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color_R8G8B8(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { - const uint8_t *src = &((const uint8_t *)pixels)[offset*3]; - color[0] = src[0]*SW_INV_255; - color[1] = src[1]*SW_INV_255; - color[2] = src[2]*SW_INV_255; - color[3] = 1.0f; + uint8_t unpack[4]; + sw_pixel_read_color8_R8G8B8(unpack, pixels, offset); + sw_color8_to_color(color, unpack); } -static inline void sw_pixel_get_color_R5G5B5A1(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color_R5G5B5A1(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { - uint16_t pixel = ((const uint16_t *)pixels)[offset]; - color[0] = sw_expand_5tof((pixel >> 11) & 0x1F); - color[1] = sw_expand_5tof((pixel >> 6) & 0x1F); - color[2] = sw_expand_5tof((pixel >> 1) & 0x1F); - color[3] = sw_expand_1tof( pixel & 0x01); + uint8_t unpack[4]; + sw_pixel_read_color8_R5G5B5A1(unpack, pixels, offset); + sw_color8_to_color(color, unpack); } -static inline void sw_pixel_get_color_R4G4B4A4(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color_R4G4B4A4(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { - uint16_t pixel = ((const uint16_t *)pixels)[offset]; - color[0] = sw_expand_4tof((pixel >> 12) & 0x0F); - color[1] = sw_expand_4tof((pixel >> 8) & 0x0F); - color[2] = sw_expand_4tof((pixel >> 4) & 0x0F); - color[3] = sw_expand_4tof( pixel & 0x0F); + uint8_t unpack[4]; + sw_pixel_read_color8_R4G4B4A4(unpack, pixels, offset); + sw_color8_to_color(color, unpack); } -static inline void sw_pixel_get_color_R8G8B8A8(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color_R8G8B8A8(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { - const uint8_t *src = &((const uint8_t *)pixels)[offset*4]; - -#if defined(SW_HAS_NEON) - uint32_t tmp = (uint32_t)src[0] | ((uint32_t)src[1] << 8) | ((uint32_t)src[2] << 16) | ((uint32_t)src[3] << 24); - uint8x8_t bytes = vreinterpret_u8_u32(vdup_n_u32(tmp)); - uint16x8_t words = vmovl_u8(bytes); - uint32x4_t dwords = vmovl_u16(vget_low_u16(words)); - float32x4_t fvals = vmulq_f32(vcvtq_f32_u32(dwords), vdupq_n_f32(SW_INV_255)); - vst1q_f32(color, fvals); - -#elif defined(SW_HAS_SSE41) - __m128i bytes = _mm_loadu_si32(src); - __m128 fvals = _mm_mul_ps(_mm_cvtepi32_ps(_mm_cvtepu8_epi32(bytes)), _mm_set1_ps(SW_INV_255)); - _mm_storeu_ps(color, fvals); - -#elif defined(SW_HAS_SSE2) - __m128i bytes = _mm_loadu_si32(src); - __m128i words = _mm_unpacklo_epi8(bytes, _mm_setzero_si128()); - __m128i dwords = _mm_unpacklo_epi16(words, _mm_setzero_si128()); - __m128 fvals = _mm_mul_ps(_mm_cvtepi32_ps(dwords), _mm_set1_ps(SW_INV_255)); - _mm_storeu_ps(color, fvals); - -#elif defined(SW_HAS_RVV) - // TODO: WARNING: Sample code generated by AI, needs testing and review - size_t vl = __riscv_vsetvl_e8m1(4); // Set vector length for 8-bit input elements - vuint8m1_t vsrc_u8 = __riscv_vle8_v_u8m1(src, vl); // Load 4 unsigned 8-bit integers - vuint32m1_t vsrc_u32 = __riscv_vwcvt_xu_u_v_u32m1(vsrc_u8, vl); // Widen to 32-bit unsigned integers - vfloat32m1_t vsrc_f32 = __riscv_vfcvt_f_xu_v_f32m1(vsrc_u32, vl); // Convert to float32 - vfloat32m1_t vnorm = __riscv_vfmul_vf_f32m1(vsrc_f32, SW_INV_255, vl); // Multiply by 1/255.0 to normalize - __riscv_vse32_v_f32m1(color, vnorm, vl); // Store result - -#else - color[0] = (float)src[0]*SW_INV_255; - color[1] = (float)src[1]*SW_INV_255; - color[2] = (float)src[2]*SW_INV_255; - color[3] = (float)src[3]*SW_INV_255; -#endif + sw_color8_to_color(color, &((const uint8_t *)pixels)[offset*4]); } -static inline void sw_pixel_get_color_R32(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color_R32(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { float val = ((const float *)pixels)[offset]; color[0] = val; @@ -2032,7 +2111,7 @@ static inline void sw_pixel_get_color_R32(float *SW_RESTRICT color, const void * color[3] = 1.0f; } -static inline void sw_pixel_get_color_R32G32B32(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color_R32G32B32(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { const float *src = &((const float *)pixels)[offset*3]; color[0] = src[0]; @@ -2041,7 +2120,7 @@ static inline void sw_pixel_get_color_R32G32B32(float *SW_RESTRICT color, const color[3] = 1.0f; } -static inline void sw_pixel_get_color_R32G32B32A32(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color_R32G32B32A32(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { const float *src = &((const float *)pixels)[offset*4]; color[0] = src[0]; @@ -2050,7 +2129,7 @@ static inline void sw_pixel_get_color_R32G32B32A32(float *SW_RESTRICT color, con color[3] = src[3]; } -static inline void sw_pixel_get_color_R16(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color_R16(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { float val = sw_half_to_float(((const uint16_t *)pixels)[offset]); color[0] = val; @@ -2059,7 +2138,7 @@ static inline void sw_pixel_get_color_R16(float *SW_RESTRICT color, const void * color[3] = 1.0f; } -static inline void sw_pixel_get_color_R16G16B16(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color_R16G16B16(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { const uint16_t *src = &((const uint16_t *)pixels)[offset*3]; color[0] = sw_half_to_float(src[0]); @@ -2068,7 +2147,7 @@ static inline void sw_pixel_get_color_R16G16B16(float *SW_RESTRICT color, const color[3] = 1.0f; } -static inline void sw_pixel_get_color_R16G16B16A16(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) +static inline void sw_pixel_read_color_R16G16B16A16(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset) { const uint16_t *src = &((const uint16_t *)pixels)[offset*4]; color[0] = sw_half_to_float(src[0]); @@ -2077,154 +2156,94 @@ static inline void sw_pixel_get_color_R16G16B16A16(float *SW_RESTRICT color, con color[3] = sw_half_to_float(src[3]); } -static inline void sw_pixel_get_color(float *SW_RESTRICT color, const void *SW_RESTRICT pixels, uint32_t offset, sw_pixelformat_t format) +static inline sw_pixel_read_color_f sw_pixel_get_read_color_func(sw_pixelformat_t format) { switch (format) { - case SW_PIXELFORMAT_COLOR_GRAYSCALE: sw_pixel_get_color_GRAYSCALE(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_GRAYALPHA: sw_pixel_get_color_GRAYALPHA(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R3G3B2: sw_pixel_get_color_R3G3B2(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R5G6B5: sw_pixel_get_color_R5G6B5(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R8G8B8: sw_pixel_get_color_R8G8B8(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R5G5B5A1: sw_pixel_get_color_R5G5B5A1(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R4G4B4A4: sw_pixel_get_color_R4G4B4A4(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R8G8B8A8: sw_pixel_get_color_R8G8B8A8(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R32: sw_pixel_get_color_R32(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R32G32B32: sw_pixel_get_color_R32G32B32(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R32G32B32A32: sw_pixel_get_color_R32G32B32A32(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R16: sw_pixel_get_color_R16(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R16G16B16: sw_pixel_get_color_R16G16B16(color, pixels, offset); break; - case SW_PIXELFORMAT_COLOR_R16G16B16A16: sw_pixel_get_color_R16G16B16A16(color, pixels, offset); break; + case SW_PIXELFORMAT_COLOR_GRAYSCALE: return sw_pixel_read_color_GRAYSCALE; + case SW_PIXELFORMAT_COLOR_GRAYALPHA: return sw_pixel_read_color_GRAYALPHA; + case SW_PIXELFORMAT_COLOR_R3G3B2: return sw_pixel_read_color_R3G3B2; + case SW_PIXELFORMAT_COLOR_R5G6B5: return sw_pixel_read_color_R5G6B5; + case SW_PIXELFORMAT_COLOR_R8G8B8: return sw_pixel_read_color_R8G8B8; + case SW_PIXELFORMAT_COLOR_R5G5B5A1: return sw_pixel_read_color_R5G5B5A1; + case SW_PIXELFORMAT_COLOR_R4G4B4A4: return sw_pixel_read_color_R4G4B4A4; + case SW_PIXELFORMAT_COLOR_R8G8B8A8: return sw_pixel_read_color_R8G8B8A8; + case SW_PIXELFORMAT_COLOR_R32: return sw_pixel_read_color_R32; + case SW_PIXELFORMAT_COLOR_R32G32B32: return sw_pixel_read_color_R32G32B32; + case SW_PIXELFORMAT_COLOR_R32G32B32A32: return sw_pixel_read_color_R32G32B32A32; + case SW_PIXELFORMAT_COLOR_R16: return sw_pixel_read_color_R16; + case SW_PIXELFORMAT_COLOR_R16G16B16: return sw_pixel_read_color_R16G16B16; + case SW_PIXELFORMAT_COLOR_R16G16B16A16: return sw_pixel_read_color_R16G16B16A16; case SW_PIXELFORMAT_UNKNOWN: case SW_PIXELFORMAT_DEPTH_D8: case SW_PIXELFORMAT_DEPTH_D16: case SW_PIXELFORMAT_DEPTH_D32: case SW_PIXELFORMAT_COUNT: - { - color[0] = 0.0f; - color[1] = 0.0f; - color[2] = 0.0f; - color[3] = 0.0f; - } break; default: break; } + + return NULL; } -static inline void sw_pixel_set_color_GRAYSCALE(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color_GRAYSCALE(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) { ((uint8_t *)pixels)[offset] = (uint8_t)(sw_luminance(color)*255.0f); } -static inline void sw_pixel_set_color_GRAYALPHA(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color_GRAYALPHA(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) { uint8_t *dst = &((uint8_t *)pixels)[offset*2]; dst[0] = (uint8_t)(sw_luminance(color)*255.0f); dst[1] = (uint8_t)(color[3]*255.0f); } -static inline void sw_pixel_set_color_R3G3B2(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color_R3G3B2(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) { - uint8_t pixel = (sw_compress_fto3(color[0]) << 5) - | (sw_compress_fto3(color[1]) << 2) - | sw_compress_fto2(color[2]); - ((uint8_t *)pixels)[offset] = pixel; + uint8_t color8[4]; + sw_color_to_color8(color8, color); + sw_pixel_write_color8_R3G3B2(pixels, color8, offset); } -static inline void sw_pixel_set_color_R5G6B5(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color_R5G6B5(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) { - uint16_t pixel = (sw_compress_fto5(color[0]) << 11) - | (sw_compress_fto6(color[1]) << 5) - | sw_compress_fto5(color[2]); - ((uint16_t *)pixels)[offset] = pixel; + uint8_t color8[4]; + sw_color_to_color8(color8, color); + sw_pixel_write_color8_R5G6B5(pixels, color8, offset); } -static inline void sw_pixel_set_color_R8G8B8(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color_R8G8B8(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) { - uint8_t *dst = &((uint8_t *)pixels)[offset*3]; - dst[0] = (uint8_t)(color[0]*255.0f); - dst[1] = (uint8_t)(color[1]*255.0f); - dst[2] = (uint8_t)(color[2]*255.0f); + uint8_t color8[4]; + sw_color_to_color8(color8, color); + sw_pixel_write_color8_R8G8B8(pixels, color8, offset); } -static inline void sw_pixel_set_color_R5G5B5A1(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color_R5G5B5A1(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) { - uint16_t pixel = (sw_compress_fto5(color[0]) << 11) - | (sw_compress_fto5(color[1]) << 6) - | (sw_compress_fto5(color[2]) << 1) - | sw_compress_fto1(color[3]); - ((uint16_t *)pixels)[offset] = pixel; + uint8_t color8[4]; + sw_color_to_color8(color8, color); + sw_pixel_write_color8_R5G5B5A1(pixels, color8, offset); } -static inline void sw_pixel_set_color_R4G4B4A4(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color_R4G4B4A4(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) { - uint16_t pixel = (sw_compress_fto4(color[0]) << 12) - | (sw_compress_fto4(color[1]) << 8) - | (sw_compress_fto4(color[2]) << 4) - | sw_compress_fto4(color[3]); - ((uint16_t *)pixels)[offset] = pixel; + uint8_t color8[4]; + sw_color_to_color8(color8, color); + sw_pixel_write_color8_R4G4B4A4(pixels, color8, offset); } -static inline void sw_pixel_set_color_R8G8B8A8(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color_R8G8B8A8(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) { - uint8_t *dst = &((uint8_t *)pixels)[offset*4]; - -#if defined(SW_HAS_NEON) - float32x4_t fvals = vmulq_f32(vld1q_f32(color), vdupq_n_f32(255.0f)); - uint32x4_t i32 = vcvtq_u32_f32(fvals); - uint16x4_t i16 = vmovn_u32(i32); - uint8x8_t i8 = vmovn_u16(vcombine_u16(i16, i16)); - dst[0] = vget_lane_u8(i8, 0); - dst[1] = vget_lane_u8(i8, 1); - dst[2] = vget_lane_u8(i8, 2); - dst[3] = vget_lane_u8(i8, 3); - -#elif defined(SW_HAS_SSE41) - __m128 fvals = _mm_mul_ps(_mm_loadu_ps(color), _mm_set1_ps(255.0f)); - __m128i i32 = _mm_cvttps_epi32(fvals); - __m128i i16 = _mm_packus_epi32(i32, i32); - __m128i i8 = _mm_packus_epi16(i16, i16); - _mm_storeu_si32(dst, i8); - -#elif defined(SW_HAS_SSE2) - __m128 fvals = _mm_mul_ps(_mm_loadu_ps(color), _mm_set1_ps(255.0f)); - __m128i i32 = _mm_cvttps_epi32(fvals); - __m128i i16 = _mm_packs_epi32(i32, i32); - __m128i i8 = _mm_packus_epi16(i16, i16); - _mm_storeu_si32(dst, i8); - -#elif defined(SW_HAS_RVV) - // TODO: WARNING: Sample code generated by AI, needs testing and review - // REVIEW: It shouldn't perform so many operations; take inspiration from other versions - // NOTE: RVV 1.0 specs define the use of __riscv_ prefix for instrinsic functions - size_t vl = __riscv_vsetvl_e32m1(4); // Load up to 4 floats into a vector register - vfloat32m1_t vsrc = __riscv_vle32_v_f32m1(src, vl); // Load float32 values - - // Multiply by 255.0f and add 0.5f for rounding - vfloat32m1_t vscaled = __riscv_vfmul_vf_f32m1(vsrc, 255.0f, vl); - vscaled = __riscv_vfadd_vf_f32m1(vscaled, 0.5f, vl); - - // Convert to unsigned integer (truncate toward zero) - vuint32m1_t vu32 = __riscv_vfcvt_xu_f_v_u32m1(vscaled, vl); - - // Narrow from u32 -> u8 - vuint8m1_t vu8 = __riscv_vnclipu_wx_u8m1(vu32, 0, vl); // Round toward zero - __riscv_vse8_v_u8m1(dst, vu8, vl); // Store result - -#else - dst[0] = (uint8_t)(color[0]*255.0f); - dst[1] = (uint8_t)(color[1]*255.0f); - dst[2] = (uint8_t)(color[2]*255.0f); - dst[3] = (uint8_t)(color[3]*255.0f); -#endif + sw_color_to_color8(&((uint8_t *)pixels)[offset*4], color); } -static inline void sw_pixel_set_color_R32(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color_R32(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) { ((float *)pixels)[offset] = sw_luminance(color); } -static inline void sw_pixel_set_color_R32G32B32(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color_R32G32B32(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) { float *dst = &((float *)pixels)[offset*3]; dst[0] = color[0]; @@ -2232,7 +2251,7 @@ static inline void sw_pixel_set_color_R32G32B32(void *SW_RESTRICT pixels, const dst[2] = color[2]; } -static inline void sw_pixel_set_color_R32G32B32A32(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color_R32G32B32A32(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) { float *dst = &((float *)pixels)[offset*4]; dst[0] = color[0]; @@ -2241,12 +2260,12 @@ static inline void sw_pixel_set_color_R32G32B32A32(void *SW_RESTRICT pixels, con dst[3] = color[3]; } -static inline void sw_pixel_set_color_R16(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color_R16(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) { ((uint16_t *)pixels)[offset] = sw_float_to_half(sw_luminance(color)); } -static inline void sw_pixel_set_color_R16G16B16(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color_R16G16B16(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) { uint16_t *dst = &((uint16_t *)pixels)[offset*3]; dst[0] = sw_float_to_half(color[0]); @@ -2254,7 +2273,7 @@ static inline void sw_pixel_set_color_R16G16B16(void *SW_RESTRICT pixels, const dst[2] = sw_float_to_half(color[2]); } -static inline void sw_pixel_set_color_R16G16B16A16(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) +static inline void sw_pixel_write_color_R16G16B16A16(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset) { uint16_t *dst = &((uint16_t *)pixels)[offset*4]; dst[0] = sw_float_to_half(color[0]); @@ -2263,124 +2282,72 @@ static inline void sw_pixel_set_color_R16G16B16A16(void *SW_RESTRICT pixels, con dst[3] = sw_float_to_half(color[3]); } -static inline void sw_pixel_set_color(void *SW_RESTRICT pixels, const float *SW_RESTRICT color, uint32_t offset, sw_pixelformat_t format) +static inline sw_pixel_write_color_f sw_pixel_get_write_color_func(sw_pixelformat_t format) { switch (format) { - case SW_PIXELFORMAT_COLOR_GRAYSCALE: sw_pixel_set_color_GRAYSCALE(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_GRAYALPHA: sw_pixel_set_color_GRAYALPHA(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R3G3B2: sw_pixel_set_color_R3G3B2(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R5G6B5: sw_pixel_set_color_R5G6B5(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R8G8B8: sw_pixel_set_color_R8G8B8(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R5G5B5A1: sw_pixel_set_color_R5G5B5A1(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R4G4B4A4: sw_pixel_set_color_R4G4B4A4(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R8G8B8A8: sw_pixel_set_color_R8G8B8A8(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R32: sw_pixel_set_color_R32(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R32G32B32: sw_pixel_set_color_R32G32B32(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R32G32B32A32: sw_pixel_set_color_R32G32B32A32(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R16: sw_pixel_set_color_R16(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R16G16B16: sw_pixel_set_color_R16G16B16(pixels, color, offset); break; - case SW_PIXELFORMAT_COLOR_R16G16B16A16: sw_pixel_set_color_R16G16B16A16(pixels, color, offset); break; + case SW_PIXELFORMAT_COLOR_GRAYSCALE: return sw_pixel_write_color_GRAYSCALE; + case SW_PIXELFORMAT_COLOR_GRAYALPHA: return sw_pixel_write_color_GRAYALPHA; + case SW_PIXELFORMAT_COLOR_R3G3B2: return sw_pixel_write_color_R3G3B2; + case SW_PIXELFORMAT_COLOR_R5G6B5: return sw_pixel_write_color_R5G6B5; + case SW_PIXELFORMAT_COLOR_R8G8B8: return sw_pixel_write_color_R8G8B8; + case SW_PIXELFORMAT_COLOR_R5G5B5A1: return sw_pixel_write_color_R5G5B5A1; + case SW_PIXELFORMAT_COLOR_R4G4B4A4: return sw_pixel_write_color_R4G4B4A4; + case SW_PIXELFORMAT_COLOR_R8G8B8A8: return sw_pixel_write_color_R8G8B8A8; + case SW_PIXELFORMAT_COLOR_R32: return sw_pixel_write_color_R32; + case SW_PIXELFORMAT_COLOR_R32G32B32: return sw_pixel_write_color_R32G32B32; + case SW_PIXELFORMAT_COLOR_R32G32B32A32: return sw_pixel_write_color_R32G32B32A32; + case SW_PIXELFORMAT_COLOR_R16: return sw_pixel_write_color_R16; + case SW_PIXELFORMAT_COLOR_R16G16B16: return sw_pixel_write_color_R16G16B16; + case SW_PIXELFORMAT_COLOR_R16G16B16A16: return sw_pixel_write_color_R16G16B16A16; case SW_PIXELFORMAT_UNKNOWN: case SW_PIXELFORMAT_DEPTH_D8: case SW_PIXELFORMAT_DEPTH_D16: case SW_PIXELFORMAT_DEPTH_D32: - case SW_PIXELFORMAT_COUNT: break; + case SW_PIXELFORMAT_COUNT: + default: break; } + + return NULL; } -static inline float sw_pixel_get_depth_D8(const void *pixels, uint32_t offset) +static inline float sw_pixel_read_depth_D8(const void *pixels, uint32_t offset) { return (float)((uint8_t *)pixels)[offset]*SW_INV_255; } -static inline float sw_pixel_get_depth_D16(const void *pixels, uint32_t offset) +static inline float sw_pixel_read_depth_D16(const void *pixels, uint32_t offset) { return (float)((uint16_t *)pixels)[offset]/UINT16_MAX; } -static inline float sw_pixel_get_depth_D32(const void *pixels, uint32_t offset) +static inline float sw_pixel_read_depth_D32(const void *pixels, uint32_t offset) { return ((float *)pixels)[offset]; } -static inline float sw_pixel_get_depth(const void *pixels, uint32_t offset, sw_pixelformat_t format) -{ - switch (format) - { - case SW_PIXELFORMAT_DEPTH_D8: return sw_pixel_get_depth_D8(pixels, offset); - case SW_PIXELFORMAT_DEPTH_D16: return sw_pixel_get_depth_D16(pixels, offset); - case SW_PIXELFORMAT_DEPTH_D32: return sw_pixel_get_depth_D32(pixels, offset); - - case SW_PIXELFORMAT_UNKNOWN: - case SW_PIXELFORMAT_COLOR_GRAYSCALE: - case SW_PIXELFORMAT_COLOR_GRAYALPHA: - case SW_PIXELFORMAT_COLOR_R3G3B2: - case SW_PIXELFORMAT_COLOR_R5G6B5: - case SW_PIXELFORMAT_COLOR_R8G8B8: - case SW_PIXELFORMAT_COLOR_R5G5B5A1: - case SW_PIXELFORMAT_COLOR_R4G4B4A4: - case SW_PIXELFORMAT_COLOR_R8G8B8A8: - case SW_PIXELFORMAT_COLOR_R32: - case SW_PIXELFORMAT_COLOR_R32G32B32: - case SW_PIXELFORMAT_COLOR_R32G32B32A32: - case SW_PIXELFORMAT_COLOR_R16: - case SW_PIXELFORMAT_COLOR_R16G16B16: - case SW_PIXELFORMAT_COLOR_R16G16B16A16: - case SW_PIXELFORMAT_COUNT: break; - } - - return 0.0f; -} - -static inline void sw_pixel_set_depth_D8(void *pixels, float depth, uint32_t offset) +static inline void sw_pixel_write_depth_D8(void *pixels, float depth, uint32_t offset) { ((uint8_t *)pixels)[offset] = (uint8_t)(depth*UINT8_MAX); } -static inline void sw_pixel_set_depth_D16(void *pixels, float depth, uint32_t offset) +static inline void sw_pixel_write_depth_D16(void *pixels, float depth, uint32_t offset) { ((uint16_t *)pixels)[offset] = (uint16_t)(depth*UINT16_MAX); } -static inline void sw_pixel_set_depth_D32(void *pixels, float depth, uint32_t offset) +static inline void sw_pixel_write_depth_D32(void *pixels, float depth, uint32_t offset) { ((float *)pixels)[offset] = depth; } - -static inline void sw_pixel_set_depth(void *pixels, float depth, uint32_t offset, sw_pixelformat_t format) -{ - switch (format) - { - case SW_PIXELFORMAT_DEPTH_D8: sw_pixel_set_depth_D8(pixels, depth, offset); break; - case SW_PIXELFORMAT_DEPTH_D16: sw_pixel_set_depth_D16(pixels, depth, offset); break; - case SW_PIXELFORMAT_DEPTH_D32: sw_pixel_set_depth_D32(pixels, depth, offset); break; - - case SW_PIXELFORMAT_UNKNOWN: - case SW_PIXELFORMAT_COLOR_GRAYSCALE: - case SW_PIXELFORMAT_COLOR_GRAYALPHA: - case SW_PIXELFORMAT_COLOR_R3G3B2: - case SW_PIXELFORMAT_COLOR_R5G6B5: - case SW_PIXELFORMAT_COLOR_R8G8B8: - case SW_PIXELFORMAT_COLOR_R5G5B5A1: - case SW_PIXELFORMAT_COLOR_R4G4B4A4: - case SW_PIXELFORMAT_COLOR_R8G8B8A8: - case SW_PIXELFORMAT_COLOR_R32: - case SW_PIXELFORMAT_COLOR_R32G32B32: - case SW_PIXELFORMAT_COLOR_R32G32B32A32: - case SW_PIXELFORMAT_COLOR_R16: - case SW_PIXELFORMAT_COLOR_R16G16B16: - case SW_PIXELFORMAT_COLOR_R16G16B16A16: - case SW_PIXELFORMAT_COUNT: break; - } -} //------------------------------------------------------------------------------------------- // Texture functionality //------------------------------------------------------------------------------------------- static inline bool sw_texture_alloc(sw_texture_t *texture, const void *data, int w, int h, sw_pixelformat_t format) { + bool isDepth = sw_pixel_is_depth_format(format); int bpp = SW_PIXELFORMAT_SIZE[format]; int newSize = w*h*bpp; @@ -2395,10 +2362,39 @@ static inline bool sw_texture_alloc(sw_texture_t *texture, const void *data, int uint8_t *dst = texture->pixels; const uint8_t *src = data; - if (data) for (int i = 0; i < newSize; i++) dst[i] = src[i]; - else for (int i = 0; i < newSize; i++) dst[i] = 0; + sw_pixel_read_color8_f readColor8 = NULL; + sw_pixel_read_color_f readColor = NULL; + if (!isDepth) { + readColor8 = sw_pixel_get_read_color8_func(format); + readColor = sw_pixel_get_read_color_func(format); + } + sw_pixel_alpha_t pixelAlpha = SW_PIXELFORMAT_ALPHA[format]; + bool alphaFound = !data; // No data: assume transparency + + if (data && !isDepth) + { + for (int i = 0; i < newSize; i++) dst[i] = src[i]; + + if (pixelAlpha != SW_PIXEL_ALPHA_NONE) + { + for (int i = 0; i < newSize; i += bpp) + { + uint8_t color[4] = { 0 }; + readColor8(color, &src[i], 0); + if (color[3] < 255) { alphaFound = true; break; } + } + } + } + else + { + for (int i = 0; i < newSize; i++) dst[i] = 0; + } + + texture->readColor8 = readColor8; + texture->readColor = readColor; texture->format = format; + texture->alpha = alphaFound? pixelAlpha : SW_PIXEL_ALPHA_NONE; texture->width = w; texture->height = h; texture->wMinus1 = w - 1; @@ -2414,11 +2410,6 @@ static inline void sw_texture_free(sw_texture_t *texture) SW_FREE(texture->pixels); } -static inline void sw_texture_fetch(float *SW_RESTRICT color, const sw_texture_t *SW_RESTRICT tex, int x, int y) -{ - sw_pixel_get_color(color, tex->pixels, y*tex->width + x, tex->format); -} - static inline void sw_texture_sample_nearest(float *SW_RESTRICT color, const sw_texture_t *SW_RESTRICT tex, float u, float v) { u = (tex->sWrap == SW_REPEAT)? sw_fract(u) : sw_saturate(u); @@ -2427,7 +2418,7 @@ static inline void sw_texture_sample_nearest(float *SW_RESTRICT color, const sw_ int x = u*tex->width; int y = v*tex->height; - sw_texture_fetch(color, tex, x, y); + tex->readColor(color, tex->pixels, y*tex->width + x); } static inline void sw_texture_sample_linear(float *SW_RESTRICT color, const sw_texture_t *SW_RESTRICT tex, float u, float v) @@ -2472,10 +2463,10 @@ static inline void sw_texture_sample_linear(float *SW_RESTRICT color, const sw_t } float c00[4], c10[4], c01[4], c11[4]; - sw_texture_fetch(c00, tex, x0, y0); - sw_texture_fetch(c10, tex, x1, y0); - sw_texture_fetch(c01, tex, x0, y1); - sw_texture_fetch(c11, tex, x1, y1); + tex->readColor(c00, tex->pixels, y0*tex->width + x0); + tex->readColor(c10, tex->pixels, y0*tex->width + x1); + tex->readColor(c01, tex->pixels, y1*tex->width + x0); + tex->readColor(c11, tex->pixels, y1*tex->width + x1); for (int i = 0; i < 4; i++) { @@ -2543,7 +2534,7 @@ static inline void sw_framebuffer_fill_color(sw_texture_t *colorBuffer, const fl uint8_t *dst = (uint8_t *)colorBuffer->pixels; - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + if (RLSW.userState & SW_STATE_SCISSOR_TEST) { int xMin = sw_clamp_int(RLSW.scMin[0], 0, colorBuffer->width - 1); int xMax = sw_clamp_int(RLSW.scMax[0], 0, colorBuffer->width - 1); @@ -2582,7 +2573,7 @@ static inline void sw_framebuffer_fill_depth(sw_texture_t *depthBuffer, float de uint8_t *dst = (uint8_t *)depthBuffer->pixels; - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + if (RLSW.userState & SW_STATE_SCISSOR_TEST) { int xMin = sw_clamp_int(RLSW.scMin[0], 0, depthBuffer->width - 1); int xMax = sw_clamp_int(RLSW.scMax[0], 0, depthBuffer->width - 1); @@ -2655,8 +2646,9 @@ static inline void sw_framebuffer_output_fast(void *dst, const sw_texture_t *buf static inline void sw_framebuffer_output_copy(void *dst, const sw_texture_t *buffer, int x, int y, int w, int h, sw_pixelformat_t format) { - int dstPixelSize = SW_PIXELFORMAT_SIZE[format]; int stride = buffer->width; + int dstPixelSize = SW_PIXELFORMAT_SIZE[format]; + sw_pixel_write_color8_f setColor8 = sw_pixel_get_write_color8_func(format); const uint8_t *src = (uint8_t *)(buffer->pixels) + ((y + h - 1)*stride + x)*SW_FRAMEBUFFER_COLOR_SIZE; uint8_t *d = dst; @@ -2678,8 +2670,8 @@ static inline void sw_framebuffer_output_copy(void *dst, const sw_texture_t *buf } #endif - sw_pixel_set_color8(dline, color, 0, format); - line += SW_FRAMEBUFFER_COLOR_SIZE; + setColor8(dline, color, 0); + line += SW_FRAMEBUFFER_COLOR_SIZE; dline += dstPixelSize; } @@ -2695,12 +2687,12 @@ static inline void sw_framebuffer_output_blit(void *dst, const sw_texture_t *buf int fbWidth = buffer->width; int dstPixelSize = SW_PIXELFORMAT_SIZE[format]; + sw_pixel_write_color8_f setColor8 = sw_pixel_get_write_color8_func(format); uint32_t xScale = ((uint32_t)wSrc << 16)/(uint32_t)wDst; uint32_t yScale = ((uint32_t)hSrc << 16)/(uint32_t)hDst; int ySrcLast = ySrc + hSrc - 1; - uint8_t *d = (uint8_t *)dst; for (int dy = 0; dy < hDst; dy++) @@ -2724,7 +2716,7 @@ static inline void sw_framebuffer_output_blit(void *dst, const sw_texture_t *buf } #endif - sw_pixel_set_color8(dline, color, 0, format); + setColor8(dline, color, 0); dline += dstPixelSize; } @@ -2735,86 +2727,138 @@ static inline void sw_framebuffer_output_blit(void *dst, const sw_texture_t *buf // Color blending functionality //------------------------------------------------------------------------------------------- -static inline void sw_factor_zero(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) -{ - factor[0] = factor[1] = factor[2] = factor[3] = 0.0f; +// Blend factor component macros: SW_BF_XXX(src, dst, component_index) +// Each expands to the scalar factor value for one RGBA channel +#define SW_BF_ZERO(s,d,i) 0.0f +#define SW_BF_ONE(s,d,i) 1.0f +#define SW_BF_SRC_COLOR(s,d,i) (s)[i] +#define SW_BF_ONE_MINUS_SRC_COLOR(s,d,i) (1.0f-(s)[i]) +#define SW_BF_SRC_ALPHA(s,d,i) (s)[3] +#define SW_BF_ONE_MINUS_SRC_ALPHA(s,d,i) (1.0f-(s)[3]) +#define SW_BF_DST_ALPHA(s,d,i) (d)[3] +#define SW_BF_ONE_MINUS_DST_ALPHA(s,d,i) (1.0f-(d)[3]) +#define SW_BF_DST_COLOR(s,d,i) (d)[i] +#define SW_BF_ONE_MINUS_DST_COLOR(s,d,i) (1.0f-(d)[i]) +#define SW_BF_SRC_ALPHA_SATURATE(s,d,i) (((i)<3)? 1.0f : (((s)[3]<1.0f)?(s)[3]:1.0f)) + +// Generates one specialized blend function for a (sfactor, dfactor) pair +#define DEFINE_BLEND_FUNC(sn, dn, SF, DF) \ +static void sw_blend_##sn##_##dn(float *SW_RESTRICT dst, const float *SW_RESTRICT src) \ +{ \ + dst[0] = SF(src,dst,0)*src[0] + DF(src,dst,0)*dst[0]; \ + dst[1] = SF(src,dst,1)*src[1] + DF(src,dst,1)*dst[1]; \ + dst[2] = SF(src,dst,2)*src[2] + DF(src,dst,2)*dst[2]; \ + dst[3] = SF(src,dst,3)*src[3] + DF(src,dst,3)*dst[3]; \ } -static inline void sw_factor_one(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +// Master factor list: X(c_name, gl_enum, compact_index, SW_BF_macro) +// compact_index is used to index SW_BLEND_TABLE (GL enums are non contiguous) +#define FOREACH_FACTOR(X) \ + X(ZERO, SW_ZERO, 0, SW_BF_ZERO ) \ + X(ONE, SW_ONE, 1, SW_BF_ONE ) \ + X(SRC_COLOR, SW_SRC_COLOR, 2, SW_BF_SRC_COLOR ) \ + X(ONE_MINUS_SRC_COLOR, SW_ONE_MINUS_SRC_COLOR, 3, SW_BF_ONE_MINUS_SRC_COLOR) \ + X(SRC_ALPHA, SW_SRC_ALPHA, 4, SW_BF_SRC_ALPHA ) \ + X(ONE_MINUS_SRC_ALPHA, SW_ONE_MINUS_SRC_ALPHA, 5, SW_BF_ONE_MINUS_SRC_ALPHA) \ + X(DST_ALPHA, SW_DST_ALPHA, 6, SW_BF_DST_ALPHA ) \ + X(ONE_MINUS_DST_ALPHA, SW_ONE_MINUS_DST_ALPHA, 7, SW_BF_ONE_MINUS_DST_ALPHA) \ + X(DST_COLOR, SW_DST_COLOR, 8, SW_BF_DST_COLOR ) \ + X(ONE_MINUS_DST_COLOR, SW_ONE_MINUS_DST_COLOR, 9, SW_BF_ONE_MINUS_DST_COLOR) \ + X(SRC_ALPHA_SATURATE, SW_SRC_ALPHA_SATURATE, 10, SW_BF_SRC_ALPHA_SATURATE ) + +// Same list but forwards 3 extra args (A, B, C) to X +#define FOREACH_FACTOR_WITH(X, A, B, C) \ + X(ZERO, SW_ZERO, 0, SW_BF_ZERO, A,B,C) \ + X(ONE, SW_ONE, 1, SW_BF_ONE, A,B,C) \ + X(SRC_COLOR, SW_SRC_COLOR, 2, SW_BF_SRC_COLOR, A,B,C) \ + X(ONE_MINUS_SRC_COLOR, SW_ONE_MINUS_SRC_COLOR, 3, SW_BF_ONE_MINUS_SRC_COLOR,A,B,C) \ + X(SRC_ALPHA, SW_SRC_ALPHA, 4, SW_BF_SRC_ALPHA, A,B,C) \ + X(ONE_MINUS_SRC_ALPHA, SW_ONE_MINUS_SRC_ALPHA, 5, SW_BF_ONE_MINUS_SRC_ALPHA,A,B,C) \ + X(DST_ALPHA, SW_DST_ALPHA, 6, SW_BF_DST_ALPHA, A,B,C) \ + X(ONE_MINUS_DST_ALPHA, SW_ONE_MINUS_DST_ALPHA, 7, SW_BF_ONE_MINUS_DST_ALPHA,A,B,C) \ + X(DST_COLOR, SW_DST_COLOR, 8, SW_BF_DST_COLOR, A,B,C) \ + X(ONE_MINUS_DST_COLOR, SW_ONE_MINUS_DST_COLOR, 9, SW_BF_ONE_MINUS_DST_COLOR,A,B,C) \ + X(SRC_ALPHA_SATURATE, SW_SRC_ALPHA_SATURATE, 10, SW_BF_SRC_ALPHA_SATURATE, A,B,C) + +// Inner loop: receives dst factor + forwarded (sn, si, sf) from outer loop +#define GEN_COMBO(dn, de, di, df, sn, si, sf) DEFINE_BLEND_FUNC(sn, dn, sf, df) + +// Outer loop: for each src factor, iterate all dst factors +#define GEN_ROW(sn, se, si, sf) FOREACH_FACTOR_WITH(GEN_COMBO, sn, si, sf) + +// Generates all 121 sw_blend_SFACTOR_DFACTOR functions +FOREACH_FACTOR(GEN_ROW) + +#undef GEN_COMBO +#undef GEN_ROW + +// Inner loop: emits one table entry using compact indices (not GL enum values) +#define GEN_TABLE_ENTRY(dn, de, di, df, sn, si, sf) [si][di] = sw_blend_##sn##_##dn, + +// Outer loop: fills one row of the table for a given src factor +#define GEN_TABLE_ROW(sn, se, si, sf) FOREACH_FACTOR_WITH(GEN_TABLE_ENTRY, sn, si, sf) + +// 2D dispatch table indexed by compact src/dst factor indices +#define SW_BLEND_FACTOR_COUNT 11 +static const sw_blend_f SW_BLEND_TABLE[SW_BLEND_FACTOR_COUNT][SW_BLEND_FACTOR_COUNT] = { + FOREACH_FACTOR(GEN_TABLE_ROW) +}; + +#undef GEN_TABLE_ENTRY +#undef GEN_TABLE_ROW + +// Maps a GL blend factor enum to its compact table index +static inline int sw_blend_factor_index(SWfactor f) { - factor[0] = factor[1] = factor[2] = factor[3] = 1.0f; + switch (f) { + case SW_ZERO: return 0; + case SW_ONE: return 1; + case SW_SRC_COLOR: return 2; + case SW_ONE_MINUS_SRC_COLOR: return 3; + case SW_SRC_ALPHA: return 4; + case SW_ONE_MINUS_SRC_ALPHA: return 5; + case SW_DST_ALPHA: return 6; + case SW_ONE_MINUS_DST_ALPHA: return 7; + case SW_DST_COLOR: return 8; + case SW_ONE_MINUS_DST_COLOR: return 9; + case SW_SRC_ALPHA_SATURATE: return 10; + default: return -1; + } } -static inline void sw_factor_src_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +static bool sw_blend_factor_needs_alpha(SWfactor f) { - factor[0] = src[0]; factor[1] = src[1]; factor[2] = src[2]; factor[3] = src[3]; + switch (f) { + case SW_SRC_ALPHA: + case SW_ONE_MINUS_SRC_ALPHA: + case SW_DST_ALPHA: + case SW_ONE_MINUS_DST_ALPHA: + case SW_SRC_ALPHA_SATURATE: return true; + default: break; + } + return false; } -static inline void sw_factor_one_minus_src_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +static uint32_t sw_blend_compute_flags(SWfactor src, SWfactor dst) { - factor[0] = 1.0f - src[0]; factor[1] = 1.0f - src[1]; - factor[2] = 1.0f - src[2]; factor[3] = 1.0f - src[3]; -} + uint32_t flags = 0; -static inline void sw_factor_src_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) -{ - factor[0] = factor[1] = factor[2] = factor[3] = src[3]; -} + // Blend is a no-op: result = src*1 + dst*0 = src + if (src == SW_ONE && dst == SW_ZERO) flags |= SW_BLEND_FLAG_NOOP; -static inline void sw_factor_one_minus_src_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) -{ - float invAlpha = 1.0f - src[3]; - factor[0] = factor[1] = factor[2] = factor[3] = invAlpha; -} + // Factors that depend on the alpha channel + if (sw_blend_factor_needs_alpha(src) || sw_blend_factor_needs_alpha(dst)) flags |= SW_BLEND_FLAG_NEEDS_ALPHA; -static inline void sw_factor_dst_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) -{ - factor[0] = factor[1] = factor[2] = factor[3] = dst[3]; -} - -static inline void sw_factor_one_minus_dst_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) -{ - float invAlpha = 1.0f - dst[3]; - factor[0] = factor[1] = factor[2] = factor[3] = invAlpha; -} - -static inline void sw_factor_dst_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) -{ - factor[0] = dst[0]; factor[1] = dst[1]; factor[2] = dst[2]; factor[3] = dst[3]; -} - -static inline void sw_factor_one_minus_dst_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) -{ - factor[0] = 1.0f - dst[0]; factor[1] = 1.0f - dst[1]; - factor[2] = 1.0f - dst[2]; factor[3] = 1.0f - dst[3]; -} - -static inline void sw_factor_src_alpha_saturate(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) -{ - factor[0] = factor[1] = factor[2] = 1.0f; - factor[3] = (src[3] < 1.0f)? src[3] : 1.0f; -} - -static inline void sw_blend_colors(float *SW_RESTRICT dst/*[4]*/, const float *SW_RESTRICT src/*[4]*/) -{ - float srcFactor[4], dstFactor[4]; - - RLSW.srcFactorFunc(srcFactor, src, dst); - RLSW.dstFactorFunc(dstFactor, src, dst); - - dst[0] = srcFactor[0]*src[0] + dstFactor[0]*dst[0]; - dst[1] = srcFactor[1]*src[1] + dstFactor[1]*dst[1]; - dst[2] = srcFactor[2]*src[2] + dstFactor[2]*dst[2]; - dst[3] = srcFactor[3]*src[3] + dstFactor[3]*dst[3]; + return flags; } //------------------------------------------------------------------------------------------- // Projection helper functions //------------------------------------------------------------------------------------------- -static inline void sw_project_ndc_to_screen(float screen[2], const float ndc[4]) +static inline void sw_project_ndc_to_screen(float ndc[4]) { - screen[0] = RLSW.vpCenter[0] + ndc[0]*RLSW.vpHalf[0] + 0.5f; - screen[1] = RLSW.vpCenter[1] + ndc[1]*RLSW.vpHalf[1] + 0.5f; + ndc[0] = RLSW.vpCenter[0] + ndc[0]*RLSW.vpHalf[0] + 0.5f; + ndc[1] = RLSW.vpCenter[1] + ndc[1]*RLSW.vpHalf[1] + 0.5f; } //------------------------------------------------------------------------------------------- @@ -2827,17 +2871,17 @@ static int sw_clip_##name( int n) \ { \ const sw_vertex_t *prev = &input[n - 1]; \ - int prevInside = FUNC_IS_INSIDE(prev->homogeneous); \ + int prevInside = FUNC_IS_INSIDE(prev->coord); \ int outputCount = 0; \ \ for (int i = 0; i < n; i++) { \ const sw_vertex_t *curr = &input[i]; \ - int currInside = FUNC_IS_INSIDE(curr->homogeneous); \ + int currInside = FUNC_IS_INSIDE(curr->coord); \ \ /* If transition between interior/exterior, calculate intersection point */ \ if (prevInside != currInside) { \ - float t = FUNC_COMPUTE_T(prev->homogeneous, curr->homogeneous); \ - sw_lerp_vertex_PTCH(&output[outputCount++], prev, curr, t); \ + float t = FUNC_COMPUTE_T(prev->coord, curr->coord); \ + sw_lerp_vertex_PCTH(&output[outputCount++], prev, curr, t); \ } \ \ /* If current vertex inside, add it */ \ @@ -2924,7 +2968,7 @@ static bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES] CLIP_AGAINST_PLANE(sw_clip_z_pos); CLIP_AGAINST_PLANE(sw_clip_z_neg); - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + if (RLSW.userState & SW_STATE_SCISSOR_TEST) { CLIP_AGAINST_PLANE(sw_clip_scissor_x_min); CLIP_AGAINST_PLANE(sw_clip_scissor_x_max); @@ -3025,7 +3069,7 @@ static bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES] // Dispatch table (auto-generated from SW_RASTER_VARIANTS) #define SW_TABLE_ENTRY(NAME, FLAGS) [FLAGS] = sw_raster_triangle_##NAME, - static const sw_raster_triangle_f SW_RASTER_TRIANGLE_FUNCS[] = { + static const sw_raster_triangle_f SW_RASTER_TRIANGLE_TABLE[] = { SW_RASTER_VARIANTS(SW_TABLE_ENTRY) }; #undef SW_TABLE_ENTRY @@ -3124,7 +3168,7 @@ static bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES] // Dispatch table (auto-generated from SW_RASTER_VARIANTS) #define SW_TABLE_ENTRY(NAME, FLAGS) [FLAGS] = sw_raster_quad_##NAME, - static const sw_raster_quad_f SW_RASTER_QUAD_FUNCS[] = { + static const sw_raster_quad_f SW_RASTER_QUAD_TABLE[] = { SW_RASTER_VARIANTS(SW_TABLE_ENTRY) }; #undef SW_TABLE_ENTRY @@ -3189,8 +3233,8 @@ static bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES] // Dispatch table (auto-generated from SW_RASTER_VARIANTS) #define SW_TABLE_ENTRY0(NAME, FLAGS) [FLAGS] = sw_raster_line_##NAME, #define SW_TABLE_ENTRY1(NAME, FLAGS) [FLAGS] = sw_raster_line_thick_##NAME, - static const sw_raster_line_f SW_RASTER_LINE_FUNCS[] = { SW_RASTER_VARIANTS(SW_TABLE_ENTRY0) }; - static const sw_raster_line_f SW_RASTER_LINE_THICK_FUNCS[] = { SW_RASTER_VARIANTS(SW_TABLE_ENTRY1) }; + static const sw_raster_line_f SW_RASTER_LINE_TABLE[] = { SW_RASTER_VARIANTS(SW_TABLE_ENTRY0) }; + static const sw_raster_line_f SW_RASTER_LINE_THICK_TABLE[] = { SW_RASTER_VARIANTS(SW_TABLE_ENTRY1) }; #undef SW_TABLE_ENTRY0 #undef SW_TABLE_ENTRY1 @@ -3252,7 +3296,7 @@ static bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES] // Dispatch table (auto-generated from SW_RASTER_VARIANTS) #define SW_TABLE_ENTRY(NAME, FLAGS) [FLAGS] = sw_raster_point_##NAME, - static const sw_raster_point_f SW_RASTER_POINT_FUNCS[] = { + static const sw_raster_point_f SW_RASTER_POINT_TABLE[] = { SW_RASTER_VARIANTS(SW_TABLE_ENTRY) }; #undef SW_TABLE_ENTRY @@ -3269,21 +3313,21 @@ static inline bool sw_triangle_face_culling(void) { // NOTE: Face culling is done before clipping to avoid unnecessary computations // To handle triangles crossing the w=0 plane correctly, - // the winding order test is performeed in homogeneous coordinates directly, + // the winding order test is performeed in clip space directly, // before the perspective division (division by w) // This test determines the orientation of the triangle in the (x,y,w) plane, // which corresponds to the projected 2D winding order sign, // even with negative w values - // Preload homogeneous coordinates into local variables - const float *h0 = RLSW.vertexBuffer[0].homogeneous; - const float *h1 = RLSW.vertexBuffer[1].homogeneous; - const float *h2 = RLSW.vertexBuffer[2].homogeneous; + // Preload clip coordinates into local variables + const float *h0 = RLSW.primitive.buffer[0].coord; + const float *h1 = RLSW.primitive.buffer[1].coord; + const float *h2 = RLSW.primitive.buffer[2].coord; // Compute a value proportional to the signed area in the projected 2D plane, - // calculated directly using homogeneous coordinates BEFORE division by w + // calculated directly using clip coordinates BEFORE division by w // This is the determinant of the matrix formed by the (x, y, w) components - // of the vertices, which correctly captures the winding order in homogeneous + // of the vertices, which correctly captures the winding order in clip // space and its relationship to the projected 2D winding order, even with // negative w values // The determinant formula used here is: @@ -3313,8 +3357,8 @@ static inline bool sw_triangle_face_culling(void) static void sw_triangle_clip_and_project(void) { - sw_vertex_t *polygon = RLSW.vertexBuffer; - int *vertexCounter = &RLSW.vertexCounter; + sw_vertex_t *polygon = RLSW.primitive.buffer; + int *vertexCounter = &RLSW.primitive.vertexCount; if (sw_polygon_clip(polygon, vertexCounter)) { @@ -3325,13 +3369,13 @@ static void sw_triangle_clip_and_project(void) // Calculation of the reciprocal of W for normalization // as well as perspective-correct attributes - const float wRcp = 1.0f/v->homogeneous[3]; - v->homogeneous[3] = wRcp; + const float wRcp = 1.0f/v->coord[3]; + v->coord[3] = wRcp; // Division of XYZ coordinates by weight - v->homogeneous[0] *= wRcp; - v->homogeneous[1] *= wRcp; - v->homogeneous[2] *= wRcp; + v->coord[0] *= wRcp; + v->coord[1] *= wRcp; + v->coord[2] *= wRcp; // Division of texture coordinates (perspective-correct) v->texcoord[0] *= wRcp; @@ -3344,29 +3388,29 @@ static void sw_triangle_clip_and_project(void) v->color[3] *= wRcp; // Transformation to screen space - sw_project_ndc_to_screen(v->screen, v->homogeneous); + sw_project_ndc_to_screen(v->coord); } } } static void sw_triangle_render(uint32_t state) { - if (RLSW.stateFlags & SW_STATE_CULL_FACE) + if (RLSW.userState & SW_STATE_CULL_FACE) { if (!sw_triangle_face_culling()) return; } sw_triangle_clip_and_project(); - if (RLSW.vertexCounter < 3) return; + if (RLSW.primitive.vertexCount < 3) return; state &= SW_RASTER_TRIANGLE_STATE_MASK; - for (int i = 0; i < RLSW.vertexCounter - 2; i++) + for (int i = 0; i < RLSW.primitive.vertexCount - 2; i++) { - SW_RASTER_TRIANGLE_FUNCS[state]( - &RLSW.vertexBuffer[0], - &RLSW.vertexBuffer[i + 1], - &RLSW.vertexBuffer[i + 2] + SW_RASTER_TRIANGLE_TABLE[state]( + &RLSW.primitive.buffer[0], + &RLSW.primitive.buffer[i + 1], + &RLSW.primitive.buffer[i + 2] ); } } @@ -3378,27 +3422,27 @@ static inline bool sw_quad_face_culling(void) { // NOTE: Face culling is done before clipping to avoid unnecessary computations // To handle quads crossing the w=0 plane correctly, - // the winding order test is performed in homogeneous coordinates directly, + // the winding order test is performed in clip space directly, // before the perspective division (division by w) // For a convex quad with vertices P0, P1, P2, P3 in sequential order, // the winding order of the quad is the same as the winding order - // of the triangle P0 P1 P2. The homogeneous triangle is used on + // of the triangle P0 P1 P2. The triangle in clip space is used on // winding test on this first triangle - // Preload homogeneous coordinates into local variables - const float *h0 = RLSW.vertexBuffer[0].homogeneous; - const float *h1 = RLSW.vertexBuffer[1].homogeneous; - const float *h2 = RLSW.vertexBuffer[2].homogeneous; + // Preload clip coordinates into local variables + const float *h0 = RLSW.primitive.buffer[0].coord; + const float *h1 = RLSW.primitive.buffer[1].coord; + const float *h2 = RLSW.primitive.buffer[2].coord; // NOTE: h3 is not needed for this test - // const float *h3 = RLSW.vertexBuffer[3].homogeneous; + // const float *h3 = RLSW.primitive.buffer[3].coord; // Compute a value proportional to the signed area of the triangle P0 P1 P2 - // in the projected 2D plane, calculated directly using homogeneous coordinates + // in the projected 2D plane, calculated directly using clip coordinates // BEFORE division by w // This is the determinant of the matrix formed by the (x, y, w) components // of the vertices P0, P1, and P2. Its sign correctly indicates the winding order - // in homogeneous space and its relationship to the projected 2D winding order, + // in clip space and its relationship to the projected 2D winding order, // even with negative w values // The determinant formula used here is: // h0.x*(h1.y*h2.w - h2.y*h1.w) + @@ -3428,8 +3472,8 @@ static inline bool sw_quad_face_culling(void) static void sw_quad_clip_and_project(void) { - sw_vertex_t *polygon = RLSW.vertexBuffer; - int *vertexCounter = &RLSW.vertexCounter; + sw_vertex_t *polygon = RLSW.primitive.buffer; + int *vertexCounter = &RLSW.primitive.vertexCount; if (sw_polygon_clip(polygon, vertexCounter)) { @@ -3440,13 +3484,13 @@ static void sw_quad_clip_and_project(void) // Calculation of the reciprocal of W for normalization // as well as perspective-correct attributes - const float wRcp = 1.0f/v->homogeneous[3]; - v->homogeneous[3] = wRcp; + const float wRcp = 1.0f/v->coord[3]; + v->coord[3] = wRcp; // Division of XYZ coordinates by weight - v->homogeneous[0] *= wRcp; - v->homogeneous[1] *= wRcp; - v->homogeneous[2] *= wRcp; + v->coord[0] *= wRcp; + v->coord[1] *= wRcp; + v->coord[2] *= wRcp; // Division of texture coordinates (perspective-correct) v->texcoord[0] *= wRcp; @@ -3459,7 +3503,7 @@ static void sw_quad_clip_and_project(void) v->color[3] *= wRcp; // Transformation to screen space - sw_project_ndc_to_screen(v->screen, v->homogeneous); + sw_project_ndc_to_screen(v->coord); } } } @@ -3471,17 +3515,17 @@ static bool sw_quad_is_axis_aligned(void) // so it's required for all vertices to have homogeneous w = 1.0 for (int i = 0; i < 4; i++) { - if (RLSW.vertexBuffer[i].homogeneous[3] != 1.0f) return false; + if (RLSW.primitive.buffer[i].coord[3] != 1.0f) return false; } // Epsilon tolerance in screen space (pixels) const float epsilon = 0.5f; // Fetch screen-space positions for the four quad vertices - const float *p0 = RLSW.vertexBuffer[0].screen; - const float *p1 = RLSW.vertexBuffer[1].screen; - const float *p2 = RLSW.vertexBuffer[2].screen; - const float *p3 = RLSW.vertexBuffer[3].screen; + const float *p0 = RLSW.primitive.buffer[0].coord; + const float *p1 = RLSW.primitive.buffer[1].coord; + const float *p2 = RLSW.primitive.buffer[2].coord; + const float *p3 = RLSW.primitive.buffer[3].coord; // Compute edge vectors between consecutive vertices // These define the four sides of the quad in screen space @@ -3502,33 +3546,33 @@ static bool sw_quad_is_axis_aligned(void) static void sw_quad_render(uint32_t state) { - if (RLSW.stateFlags & SW_STATE_CULL_FACE) + if (RLSW.userState & SW_STATE_CULL_FACE) { if (!sw_quad_face_culling()) return; } sw_quad_clip_and_project(); - if (RLSW.vertexCounter < 3) return; + if (RLSW.primitive.vertexCount < 3) return; state &= SW_RASTER_QUAD_STATE_MASK; - if ((RLSW.vertexCounter == 4) && sw_quad_is_axis_aligned()) + if ((RLSW.primitive.vertexCount == 4) && sw_quad_is_axis_aligned()) { - SW_RASTER_QUAD_FUNCS[state]( - &RLSW.vertexBuffer[0], - &RLSW.vertexBuffer[1], - &RLSW.vertexBuffer[2], - &RLSW.vertexBuffer[3] + SW_RASTER_QUAD_TABLE[state]( + &RLSW.primitive.buffer[0], + &RLSW.primitive.buffer[1], + &RLSW.primitive.buffer[2], + &RLSW.primitive.buffer[3] ); } else { - for (int i = 0; i < RLSW.vertexCounter - 2; i++) + for (int i = 0; i < RLSW.primitive.vertexCount - 2; i++) { - SW_RASTER_TRIANGLE_FUNCS[state]( - &RLSW.vertexBuffer[0], - &RLSW.vertexBuffer[i + 1], - &RLSW.vertexBuffer[i + 2] + SW_RASTER_TRIANGLE_TABLE[state]( + &RLSW.primitive.buffer[0], + &RLSW.primitive.buffer[i + 1], + &RLSW.primitive.buffer[i + 2] ); } } @@ -3570,25 +3614,25 @@ static bool sw_line_clip(sw_vertex_t *v0, sw_vertex_t *v1) for (int i = 0; i < 4; i++) { - dH[i] = v1->homogeneous[i] - v0->homogeneous[i]; + dH[i] = v1->coord[i] - v0->coord[i]; dC[i] = v1->color[i] - v0->color[i]; } // Clipping Liang-Barsky - if (!sw_line_clip_coord(v0->homogeneous[3] - v0->homogeneous[0], -dH[3] + dH[0], &t0, &t1)) return false; - if (!sw_line_clip_coord(v0->homogeneous[3] + v0->homogeneous[0], -dH[3] - dH[0], &t0, &t1)) return false; - if (!sw_line_clip_coord(v0->homogeneous[3] - v0->homogeneous[1], -dH[3] + dH[1], &t0, &t1)) return false; - if (!sw_line_clip_coord(v0->homogeneous[3] + v0->homogeneous[1], -dH[3] - dH[1], &t0, &t1)) return false; - if (!sw_line_clip_coord(v0->homogeneous[3] - v0->homogeneous[2], -dH[3] + dH[2], &t0, &t1)) return false; - if (!sw_line_clip_coord(v0->homogeneous[3] + v0->homogeneous[2], -dH[3] - dH[2], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->coord[3] - v0->coord[0], -dH[3] + dH[0], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->coord[3] + v0->coord[0], -dH[3] - dH[0], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->coord[3] - v0->coord[1], -dH[3] + dH[1], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->coord[3] + v0->coord[1], -dH[3] - dH[1], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->coord[3] - v0->coord[2], -dH[3] + dH[2], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->coord[3] + v0->coord[2], -dH[3] - dH[2], &t0, &t1)) return false; // Clipping Scissor - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + if (RLSW.userState & SW_STATE_SCISSOR_TEST) { - if (!sw_line_clip_coord(v0->homogeneous[0] - RLSW.scClipMin[0]*v0->homogeneous[3], RLSW.scClipMin[0]*dH[3] - dH[0], &t0, &t1)) return false; - if (!sw_line_clip_coord(RLSW.scClipMax[0]*v0->homogeneous[3] - v0->homogeneous[0], dH[0] - RLSW.scClipMax[0]*dH[3], &t0, &t1)) return false; - if (!sw_line_clip_coord(v0->homogeneous[1] - RLSW.scClipMin[1]*v0->homogeneous[3], RLSW.scClipMin[1]*dH[3] - dH[1], &t0, &t1)) return false; - if (!sw_line_clip_coord(RLSW.scClipMax[1]*v0->homogeneous[3] - v0->homogeneous[1], dH[1] - RLSW.scClipMax[1]*dH[3], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->coord[0] - RLSW.scClipMin[0]*v0->coord[3], RLSW.scClipMin[0]*dH[3] - dH[0], &t0, &t1)) return false; + if (!sw_line_clip_coord(RLSW.scClipMax[0]*v0->coord[3] - v0->coord[0], dH[0] - RLSW.scClipMax[0]*dH[3], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->coord[1] - RLSW.scClipMin[1]*v0->coord[3], RLSW.scClipMin[1]*dH[3] - dH[1], &t0, &t1)) return false; + if (!sw_line_clip_coord(RLSW.scClipMax[1]*v0->coord[3] - v0->coord[1], dH[1] - RLSW.scClipMax[1]*dH[3], &t0, &t1)) return false; } // Interpolation of new coordinates @@ -3596,7 +3640,7 @@ static bool sw_line_clip(sw_vertex_t *v0, sw_vertex_t *v1) { for (int i = 0; i < 4; i++) { - v1->homogeneous[i] = v0->homogeneous[i] + t1*dH[i]; + v1->coord[i] = v0->coord[i] + t1*dH[i]; v1->color[i] = v0->color[i] + t1*dC[i]; } } @@ -3605,7 +3649,7 @@ static bool sw_line_clip(sw_vertex_t *v0, sw_vertex_t *v1) { for (int i = 0; i < 4; i++) { - v0->homogeneous[i] += t0*dH[i]; + v0->coord[i] += t0*dH[i]; v0->color[i] += t0*dC[i]; } } @@ -3617,25 +3661,25 @@ static bool sw_line_clip_and_project(sw_vertex_t *v0, sw_vertex_t *v1) { if (!sw_line_clip(v0, v1)) return false; - // Convert homogeneous coordinates to NDC - v0->homogeneous[3] = 1.0f/v0->homogeneous[3]; - v1->homogeneous[3] = 1.0f/v1->homogeneous[3]; + // Convert clip coordinates to NDC + v0->coord[3] = 1.0f/v0->coord[3]; + v1->coord[3] = 1.0f/v1->coord[3]; for (int i = 0; i < 3; i++) { - v0->homogeneous[i] *= v0->homogeneous[3]; - v1->homogeneous[i] *= v1->homogeneous[3]; + v0->coord[i] *= v0->coord[3]; + v1->coord[i] *= v1->coord[3]; } // Convert NDC coordinates to screen space - sw_project_ndc_to_screen(v0->screen, v0->homogeneous); - sw_project_ndc_to_screen(v1->screen, v1->homogeneous); + sw_project_ndc_to_screen(v0->coord); + sw_project_ndc_to_screen(v1->coord); // NDC +1.0 projects to exactly (width + 0.5f), which truncates out of bounds // The clamp is at most 0.5px on a boundary endpoint, it's visually imperceptible - v0->screen[0] = sw_clamp(v0->screen[0], 0.0f, (float)(RLSW.colorBuffer->width - 1) + 0.5f); - v0->screen[1] = sw_clamp(v0->screen[1], 0.0f, (float)(RLSW.colorBuffer->height - 1) + 0.5f); - v1->screen[0] = sw_clamp(v1->screen[0], 0.0f, (float)(RLSW.colorBuffer->width - 1) + 0.5f); - v1->screen[1] = sw_clamp(v1->screen[1], 0.0f, (float)(RLSW.colorBuffer->height - 1) + 0.5f); + v0->coord[0] = sw_clamp(v0->coord[0], 0.0f, (float)(RLSW.colorBuffer->width - 1) + 0.5f); + v0->coord[1] = sw_clamp(v0->coord[1], 0.0f, (float)(RLSW.colorBuffer->height - 1) + 0.5f); + v1->coord[0] = sw_clamp(v1->coord[0], 0.0f, (float)(RLSW.colorBuffer->width - 1) + 0.5f); + v1->coord[1] = sw_clamp(v1->coord[1], 0.0f, (float)(RLSW.colorBuffer->height - 1) + 0.5f); return true; } @@ -3648,11 +3692,11 @@ static void sw_line_render(uint32_t state, sw_vertex_t *vertices) if (RLSW.lineWidth >= 2.0f) { - SW_RASTER_LINE_THICK_FUNCS[state](&vertices[0], &vertices[1]); + SW_RASTER_LINE_THICK_TABLE[state](&vertices[0], &vertices[1]); } else { - SW_RASTER_LINE_FUNCS[state](&vertices[0], &vertices[1]); + SW_RASTER_LINE_TABLE[state](&vertices[0], &vertices[1]); } } //------------------------------------------------------------------------------------------- @@ -3661,25 +3705,25 @@ static void sw_line_render(uint32_t state, sw_vertex_t *vertices) //------------------------------------------------------------------------------------------- static bool sw_point_clip_and_project(sw_vertex_t *v) { - if (v->homogeneous[3] != 1.0f) + if (v->coord[3] != 1.0f) { for (int_fast8_t i = 0; i < 3; i++) { - if ((v->homogeneous[i] < -v->homogeneous[3]) || (v->homogeneous[i] > v->homogeneous[3])) return false; + if ((v->coord[i] < -v->coord[3]) || (v->coord[i] > v->coord[3])) return false; } - v->homogeneous[3] = 1.0f/v->homogeneous[3]; - v->homogeneous[0] *= v->homogeneous[3]; - v->homogeneous[1] *= v->homogeneous[3]; - v->homogeneous[2] *= v->homogeneous[3]; + v->coord[3] = 1.0f/v->coord[3]; + v->coord[0] *= v->coord[3]; + v->coord[1] *= v->coord[3]; + v->coord[2] *= v->coord[3]; } - sw_project_ndc_to_screen(v->screen, v->homogeneous); + sw_project_ndc_to_screen(v->coord); int min[2] = { 0, 0 }; int max[2] = { RLSW.colorBuffer->width, RLSW.colorBuffer->height }; - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + if (RLSW.userState & SW_STATE_SCISSOR_TEST) { min[0] = sw_clamp_int(RLSW.scMin[0], 0, RLSW.colorBuffer->width); min[1] = sw_clamp_int(RLSW.scMin[1], 0, RLSW.colorBuffer->height); @@ -3687,8 +3731,8 @@ static bool sw_point_clip_and_project(sw_vertex_t *v) max[1] = sw_clamp_int(RLSW.scMax[1], 0, RLSW.colorBuffer->height); } - bool insideX = (v->screen[0] - RLSW.pointRadius < max[0]) && (v->screen[0] + RLSW.pointRadius > min[0]); - bool insideY = (v->screen[1] - RLSW.pointRadius < max[1]) && (v->screen[1] + RLSW.pointRadius > min[1]); + bool insideX = (v->coord[0] - RLSW.pointRadius < max[0]) && (v->coord[0] + RLSW.pointRadius > min[0]); + bool insideY = (v->coord[1] - RLSW.pointRadius < max[1]) && (v->coord[1] + RLSW.pointRadius > min[1]); return (insideX && insideY); } @@ -3697,7 +3741,7 @@ static void sw_point_render(uint32_t state, sw_vertex_t *v) { if (!sw_point_clip_and_project(v)) return; state &= SW_RASTER_POINT_STATE_MASK; - SW_RASTER_POINT_FUNCS[state](v); + SW_RASTER_POINT_TABLE[state](v); } //------------------------------------------------------------------------------------------- @@ -3705,13 +3749,13 @@ static void sw_point_render(uint32_t state, sw_vertex_t *v) //------------------------------------------------------------------------------------------- static inline void sw_poly_point_render(uint32_t state) { - for (int i = 0; i < RLSW.vertexCounter; i++) sw_point_render(state, &RLSW.vertexBuffer[i]); + for (int i = 0; i < RLSW.primitive.vertexCount; i++) sw_point_render(state, &RLSW.primitive.buffer[i]); } static inline void sw_poly_line_render(uint32_t state) { - const sw_vertex_t *vertices = RLSW.vertexBuffer; - int cm1 = RLSW.vertexCounter - 1; + const sw_vertex_t *vertices = RLSW.primitive.buffer; + int cm1 = RLSW.primitive.vertexCount - 1; for (int i = 0; i < cm1; i++) { @@ -3727,8 +3771,8 @@ static inline void sw_poly_fill_render(uint32_t state) { switch (RLSW.drawMode) { - case SW_POINTS: sw_point_render(state, &RLSW.vertexBuffer[0]); break; - case SW_LINES: sw_line_render(state, RLSW.vertexBuffer); break; + case SW_POINTS: sw_point_render(state, &RLSW.primitive.buffer[0]); break; + case SW_LINES: sw_line_render(state, RLSW.primitive.buffer); break; case SW_TRIANGLES: sw_triangle_render(state); break; case SW_QUADS: sw_quad_render(state); break; default: break; @@ -3738,7 +3782,57 @@ static inline void sw_poly_fill_render(uint32_t state) // Immediate rendering logic //------------------------------------------------------------------------------------------- -static void sw_immediate_push_vertex(const float position[4], const float color[4], const float texcoord[2]) +static void sw_immediate_begin(SWdraw mode) +{ + // NOTE: Any checks to ensure command recording can start + // must be performed before calling this function. + + // Recalculate the MVP if this is needed + if (RLSW.isDirtyMVP) + { + sw_matrix_mul_rst(RLSW.matMVP, + RLSW.stackModelview[RLSW.stackModelviewCounter - 1], + RLSW.stackProjection[RLSW.stackProjectionCounter - 1]); + + RLSW.isDirtyMVP = false; + } + + // Disable pipeline states that are incompatible with the global state + uint32_t state = RLSW.userState; + if (!sw_is_texture_complete(RLSW.depthBuffer)) state &= ~SW_STATE_DEPTH_TEST; + if (!sw_is_texture_complete(RLSW.boundTexture)) state &= ~SW_STATE_TEXTURE_2D; + else if (sw_pixel_is_depth_format(RLSW.boundTexture->format)) state &= ~SW_STATE_TEXTURE_2D; + + // Initialize required values + RLSW.primitive.hasColorAlpha = false; + RLSW.primitive.vertexCount = 0; + RLSW.rasterState = state; + RLSW.drawMode = mode; +} + +static bool sw_immediate_is_active(void) +{ + return (RLSW.drawMode != SW_DRAW_INVALID); +} + +static void sw_immediate_set_color(const float color[4]) +{ + RLSW.primitive.color[0] = color[0]; + RLSW.primitive.color[1] = color[1]; + RLSW.primitive.color[2] = color[2]; + RLSW.primitive.color[3] = color[3]; + + RLSW.primitive.hasColorAlpha |= (color[3] < 1.0f); +} + +static void sw_immediate_set_texcoord(const float texcoord[2]) +{ + const float *m = RLSW.stackTexture[RLSW.stackTextureCounter - 1]; + RLSW.primitive.texcoord[0] = m[0]*texcoord[0] + m[4]*texcoord[1] + m[12]; + RLSW.primitive.texcoord[1] = m[1]*texcoord[0] + m[5]*texcoord[1] + m[13]; +} + +static void sw_immediate_push_vertex(const float position[4]) { // Check if the draw mode is valid if (!sw_is_draw_mode_valid(RLSW.drawMode)) @@ -3748,29 +3842,32 @@ static void sw_immediate_push_vertex(const float position[4], const float color[ } // Copy the attributes in the current vertex - sw_vertex_t *vertex = &RLSW.vertexBuffer[RLSW.vertexCounter++]; - for (int i = 0; i < 4; i++) - { - vertex->position[i] = position[i]; - if (i < 2) vertex->texcoord[i] = texcoord[i]; - vertex->color[i] = color[i]; - } + sw_vertex_t *vertex = &RLSW.primitive.buffer[RLSW.primitive.vertexCount++]; + for (int i = 0; i < 4; i++) vertex->position[i] = position[i]; + for (int i = 0; i < 4; i++) vertex->color[i] = RLSW.primitive.color[i]; + for (int i = 0; i < 2; i++) vertex->texcoord[i] = RLSW.primitive.texcoord[i]; - // Calculate homogeneous coordinates + // Calculate clip coordinates const float *m = RLSW.matMVP, *v = vertex->position; - vertex->homogeneous[0] = m[0]*v[0] + m[4]*v[1] + m[8]*v[2] + m[12]*v[3]; - vertex->homogeneous[1] = m[1]*v[0] + m[5]*v[1] + m[9]*v[2] + m[13]*v[3]; - vertex->homogeneous[2] = m[2]*v[0] + m[6]*v[1] + m[10]*v[2] + m[14]*v[3]; - vertex->homogeneous[3] = m[3]*v[0] + m[7]*v[1] + m[11]*v[2] + m[15]*v[3]; + vertex->coord[0] = m[0]*v[0] + m[4]*v[1] + m[8]*v[2] + m[12]*v[3]; + vertex->coord[1] = m[1]*v[0] + m[5]*v[1] + m[9]*v[2] + m[13]*v[3]; + vertex->coord[2] = m[2]*v[0] + m[6]*v[1] + m[10]*v[2] + m[14]*v[3]; + vertex->coord[3] = m[3]*v[0] + m[7]*v[1] + m[11]*v[2] + m[15]*v[3]; // Immediate rendering of the primitive if the required number is reached - if (RLSW.vertexCounter == SW_PRIMITIVE_VERTEX_COUNT[RLSW.drawMode]) + if (RLSW.primitive.vertexCount == SW_PRIMITIVE_VERTEX_COUNT[RLSW.drawMode]) { - // Restricts the pipeline state to the minimum required - uint32_t state = RLSW.stateFlags; - if (!sw_is_texture_complete(RLSW.depthBuffer)) state &= ~SW_STATE_DEPTH_TEST; - if (!sw_is_texture_complete(RLSW.boundTexture)) state &= ~SW_STATE_TEXTURE_2D; - if ((RLSW.srcFactor == SW_ONE) && (RLSW.dstFactor == SW_ZERO)) state &= ~SW_STATE_BLEND; + uint32_t state = RLSW.rasterState; + + // Reduces blend mode costs when it's possible + if (state & SW_STATE_BLEND) + { + if (RLSW.blendFlags & SW_BLEND_FLAG_NOOP) state &= ~SW_STATE_BLEND; + else if ((RLSW.blendFlags & SW_BLEND_FLAG_NEEDS_ALPHA) && (!RLSW.primitive.hasColorAlpha)) + { + if (!(state & SW_STATE_TEXTURE_2D) || (RLSW.boundTexture->alpha == SW_PIXEL_ALPHA_NONE)) state &= ~SW_STATE_BLEND; + } + } switch (RLSW.polyMode) { @@ -3780,10 +3877,15 @@ static void sw_immediate_push_vertex(const float position[4], const float color[ default: break; } - RLSW.vertexCounter = 0; + RLSW.primitive.hasColorAlpha = false; + RLSW.primitive.vertexCount = 0; } } +static void sw_immediate_end(void) +{ + RLSW.drawMode = SW_DRAW_INVALID; +} //------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------- @@ -3834,24 +3936,26 @@ bool swInit(int w, int h) RLSW.stackTextureCounter = 1; RLSW.isDirtyMVP = false; - RLSW.current.texcoord[0] = 0.0f; - RLSW.current.texcoord[1] = 0.0f; + RLSW.primitive.texcoord[0] = 0.0f; + RLSW.primitive.texcoord[1] = 0.0f; - RLSW.current.color[0] = 1.0f; - RLSW.current.color[1] = 1.0f; - RLSW.current.color[2] = 1.0f; - RLSW.current.color[3] = 1.0f; + RLSW.primitive.color[0] = 1.0f; + RLSW.primitive.color[1] = 1.0f; + RLSW.primitive.color[2] = 1.0f; + RLSW.primitive.color[3] = 1.0f; RLSW.srcFactor = SW_SRC_ALPHA; RLSW.dstFactor = SW_ONE_MINUS_SRC_ALPHA; - - RLSW.srcFactorFunc = sw_factor_src_alpha; - RLSW.dstFactorFunc = sw_factor_one_minus_src_alpha; + RLSW.blendFunc = sw_blend_SRC_ALPHA_ONE_MINUS_SRC_ALPHA; RLSW.drawMode = SW_DRAW_INVALID; RLSW.polyMode = SW_FILL; RLSW.cullFace = SW_BACK; +#if SW_USE_COLOR_LUT + for (int i = 0; i < 256; i++) SW_LUT_UINT8_TO_FLOAT[i] = (float)i*SW_INV_255; +#endif + SW_LOG("INFO: RLSW: Software renderer initialized successfully\n"); #if defined(SW_HAS_FMA_AVX) && defined(SW_HAS_FMA_AVX2) SW_LOG("INFO: RLSW: Using SIMD instructions: FMA AVX\n"); @@ -3964,11 +4068,11 @@ void swEnable(SWstate state) { switch (state) { - case SW_SCISSOR_TEST: RLSW.stateFlags |= SW_STATE_SCISSOR_TEST; break; - case SW_TEXTURE_2D: RLSW.stateFlags |= SW_STATE_TEXTURE_2D; break; - case SW_DEPTH_TEST: RLSW.stateFlags |= SW_STATE_DEPTH_TEST; break; - case SW_CULL_FACE: RLSW.stateFlags |= SW_STATE_CULL_FACE; break; - case SW_BLEND: RLSW.stateFlags |= SW_STATE_BLEND; break; + case SW_SCISSOR_TEST: RLSW.userState |= SW_STATE_SCISSOR_TEST; break; + case SW_TEXTURE_2D: RLSW.userState |= SW_STATE_TEXTURE_2D; break; + case SW_DEPTH_TEST: RLSW.userState |= SW_STATE_DEPTH_TEST; break; + case SW_CULL_FACE: RLSW.userState |= SW_STATE_CULL_FACE; break; + case SW_BLEND: RLSW.userState |= SW_STATE_BLEND; break; default: RLSW.errCode = SW_INVALID_ENUM; break; } } @@ -3977,11 +4081,11 @@ void swDisable(SWstate state) { switch (state) { - case SW_SCISSOR_TEST: RLSW.stateFlags &= ~SW_STATE_SCISSOR_TEST; break; - case SW_TEXTURE_2D: RLSW.stateFlags &= ~SW_STATE_TEXTURE_2D; break; - case SW_DEPTH_TEST: RLSW.stateFlags &= ~SW_STATE_DEPTH_TEST; break; - case SW_CULL_FACE: RLSW.stateFlags &= ~SW_STATE_CULL_FACE; break; - case SW_BLEND: RLSW.stateFlags &= ~SW_STATE_BLEND; break; + case SW_SCISSOR_TEST: RLSW.userState &= ~SW_STATE_SCISSOR_TEST; break; + case SW_TEXTURE_2D: RLSW.userState &= ~SW_STATE_TEXTURE_2D; break; + case SW_DEPTH_TEST: RLSW.userState &= ~SW_STATE_DEPTH_TEST; break; + case SW_CULL_FACE: RLSW.userState &= ~SW_STATE_CULL_FACE; break; + case SW_BLEND: RLSW.userState &= ~SW_STATE_BLEND; break; default: RLSW.errCode = SW_INVALID_ENUM; break; } } @@ -4015,15 +4119,15 @@ void swGetFloatv(SWget name, float *v) } break; case SW_CURRENT_COLOR: { - v[0] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[0]; - v[1] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[1]; - v[2] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[2]; - v[3] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[3]; + v[0] = RLSW.primitive.buffer[RLSW.primitive.vertexCount - 1].color[0]; + v[1] = RLSW.primitive.buffer[RLSW.primitive.vertexCount - 1].color[1]; + v[2] = RLSW.primitive.buffer[RLSW.primitive.vertexCount - 1].color[2]; + v[3] = RLSW.primitive.buffer[RLSW.primitive.vertexCount - 1].color[3]; } break; case SW_CURRENT_TEXTURE_COORDS: { - v[0] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].texcoord[0]; - v[1] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].texcoord[1]; + v[0] = RLSW.primitive.buffer[RLSW.primitive.vertexCount - 1].texcoord[0]; + v[1] = RLSW.primitive.buffer[RLSW.primitive.vertexCount - 1].texcoord[1]; } break; case SW_POINT_SIZE: { @@ -4131,60 +4235,27 @@ void swClear(uint32_t bitmask) if ((bitmask & (SW_COLOR_BUFFER_BIT)) && (RLSW.colorBuffer != NULL) && (RLSW.colorBuffer->pixels != NULL)) { - int size = RLSW.colorBuffer->width*RLSW.colorBuffer->height; sw_framebuffer_fill_color(RLSW.colorBuffer, RLSW.clearColor); } if ((bitmask & (SW_DEPTH_BUFFER_BIT)) && (RLSW.depthBuffer != NULL) && (RLSW.depthBuffer->pixels != NULL)) { - int size = RLSW.depthBuffer->width*RLSW.depthBuffer->height; sw_framebuffer_fill_depth(RLSW.depthBuffer, RLSW.clearDepth); } } void swBlendFunc(SWfactor sfactor, SWfactor dfactor) { - if (!sw_is_blend_src_factor_valid(sfactor) || - !sw_is_blend_dst_factor_valid(dfactor)) - { - RLSW.errCode = SW_INVALID_ENUM; - return; - } + int sIndex = sw_blend_factor_index(sfactor); + if (sIndex < 0) { RLSW.errCode = SW_INVALID_ENUM; return; } + + int dIndex = sw_blend_factor_index(dfactor); + if (dIndex < 0) { RLSW.errCode = SW_INVALID_ENUM; return; } RLSW.srcFactor = sfactor; RLSW.dstFactor = dfactor; - - switch (sfactor) - { - case SW_ZERO: RLSW.srcFactorFunc = sw_factor_zero; break; - case SW_ONE: RLSW.srcFactorFunc = sw_factor_one; break; - case SW_SRC_COLOR: RLSW.srcFactorFunc = sw_factor_src_color; break; - case SW_ONE_MINUS_SRC_COLOR: RLSW.srcFactorFunc = sw_factor_one_minus_src_color; break; - case SW_SRC_ALPHA: RLSW.srcFactorFunc = sw_factor_src_alpha; break; - case SW_ONE_MINUS_SRC_ALPHA: RLSW.srcFactorFunc = sw_factor_one_minus_src_alpha; break; - case SW_DST_ALPHA: RLSW.srcFactorFunc = sw_factor_dst_alpha; break; - case SW_ONE_MINUS_DST_ALPHA: RLSW.srcFactorFunc = sw_factor_one_minus_dst_alpha; break; - case SW_DST_COLOR: RLSW.srcFactorFunc = sw_factor_dst_color; break; - case SW_ONE_MINUS_DST_COLOR: RLSW.srcFactorFunc = sw_factor_one_minus_dst_color; break; - case SW_SRC_ALPHA_SATURATE: RLSW.srcFactorFunc = sw_factor_src_alpha_saturate; break; - default: break; - } - - switch (dfactor) - { - case SW_ZERO: RLSW.dstFactorFunc = sw_factor_zero; break; - case SW_ONE: RLSW.dstFactorFunc = sw_factor_one; break; - case SW_SRC_COLOR: RLSW.dstFactorFunc = sw_factor_src_color; break; - case SW_ONE_MINUS_SRC_COLOR: RLSW.dstFactorFunc = sw_factor_one_minus_src_color; break; - case SW_SRC_ALPHA: RLSW.dstFactorFunc = sw_factor_src_alpha; break; - case SW_ONE_MINUS_SRC_ALPHA: RLSW.dstFactorFunc = sw_factor_one_minus_src_alpha; break; - case SW_DST_ALPHA: RLSW.dstFactorFunc = sw_factor_dst_alpha; break; - case SW_ONE_MINUS_DST_ALPHA: RLSW.dstFactorFunc = sw_factor_one_minus_dst_alpha; break; - case SW_DST_COLOR: RLSW.dstFactorFunc = sw_factor_dst_color; break; - case SW_ONE_MINUS_DST_COLOR: RLSW.dstFactorFunc = sw_factor_one_minus_dst_color; break; - case SW_SRC_ALPHA_SATURATE: break; - default: break; - } + RLSW.blendFlags = sw_blend_compute_flags(sfactor, dfactor); + RLSW.blendFunc = SW_BLEND_TABLE[sIndex][dIndex]; } void swPolygonMode(SWpoly mode) @@ -4502,77 +4573,79 @@ void swBegin(SWdraw mode) return; } - // Recalculate the MVP if this is needed - if (RLSW.isDirtyMVP) + // Ensures that a recording has not already started (spec) + if (sw_immediate_is_active()) { - sw_matrix_mul_rst(RLSW.matMVP, - RLSW.stackModelview[RLSW.stackModelviewCounter - 1], - RLSW.stackProjection[RLSW.stackProjectionCounter - 1]); - - RLSW.isDirtyMVP = false; + RLSW.errCode = SW_INVALID_OPERATION; + return; } - // Initialize required values - RLSW.vertexCounter = 0; - RLSW.drawMode = mode; + sw_immediate_begin(mode); } void swEnd(void) { - RLSW.drawMode = SW_DRAW_INVALID; + // Ensures that a recording has already started (spec) + if (!sw_immediate_is_active()) + { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + + sw_immediate_end(); } void swVertex2i(int x, int y) { const float v[4] = { (float)x, (float)y, 0.0f, 1.0f }; - sw_immediate_push_vertex(v, RLSW.current.color, RLSW.current.texcoord); + sw_immediate_push_vertex(v); } void swVertex2f(float x, float y) { const float v[4] = { x, y, 0.0f, 1.0f }; - sw_immediate_push_vertex(v, RLSW.current.color, RLSW.current.texcoord); + sw_immediate_push_vertex(v); } void swVertex2fv(const float *v) { const float v4[4] = { v[0], v[1], 0.0f, 1.0f }; - sw_immediate_push_vertex(v4, RLSW.current.color, RLSW.current.texcoord); + sw_immediate_push_vertex(v4); } void swVertex3i(int x, int y, int z) { const float v[4] = { (float)x, (float)y, (float)z, 1.0f }; - sw_immediate_push_vertex(v, RLSW.current.color, RLSW.current.texcoord); + sw_immediate_push_vertex(v); } void swVertex3f(float x, float y, float z) { const float v[4] = { x, y, z, 1.0f }; - sw_immediate_push_vertex(v, RLSW.current.color, RLSW.current.texcoord); + sw_immediate_push_vertex(v); } void swVertex3fv(const float *v) { const float v4[4] = { v[0], v[1], v[2], 1.0f }; - sw_immediate_push_vertex(v4, RLSW.current.color, RLSW.current.texcoord); + sw_immediate_push_vertex(v4); } void swVertex4i(int x, int y, int z, int w) { const float v[4] = { (float)x, (float)y, (float)z, (float)w }; - sw_immediate_push_vertex(v, RLSW.current.color, RLSW.current.texcoord); + sw_immediate_push_vertex(v); } void swVertex4f(float x, float y, float z, float w) { const float v[4] = { x, y, z, w }; - sw_immediate_push_vertex(v, RLSW.current.color, RLSW.current.texcoord); + sw_immediate_push_vertex(v); } void swVertex4fv(const float *v) { - sw_immediate_push_vertex(v, RLSW.current.color, RLSW.current.texcoord); + sw_immediate_push_vertex(v); } void swColor3ub(uint8_t r, uint8_t g, uint8_t b) @@ -4583,7 +4656,7 @@ void swColor3ub(uint8_t r, uint8_t g, uint8_t b) cv[2] = (float)b*SW_INV_255; cv[3] = 1.0f; - swColor4fv(cv); + sw_immediate_set_color(cv); } void swColor3ubv(const uint8_t *v) @@ -4594,7 +4667,7 @@ void swColor3ubv(const uint8_t *v) cv[2] = (float)v[2]*SW_INV_255; cv[3] = 1.0f; - swColor4fv(cv); + sw_immediate_set_color(cv); } void swColor3f(float r, float g, float b) @@ -4605,7 +4678,7 @@ void swColor3f(float r, float g, float b) cv[2] = b; cv[3] = 1.0f; - swColor4fv(cv); + sw_immediate_set_color(cv); } void swColor3fv(const float *v) @@ -4616,7 +4689,7 @@ void swColor3fv(const float *v) cv[2] = v[2]; cv[3] = 1.0f; - swColor4fv(cv); + sw_immediate_set_color(cv); } void swColor4ub(uint8_t r, uint8_t g, uint8_t b, uint8_t a) @@ -4627,7 +4700,7 @@ void swColor4ub(uint8_t r, uint8_t g, uint8_t b, uint8_t a) cv[2] = (float)b*SW_INV_255; cv[3] = (float)a*SW_INV_255; - swColor4fv(cv); + sw_immediate_set_color(cv); } void swColor4ubv(const uint8_t *v) @@ -4638,7 +4711,7 @@ void swColor4ubv(const uint8_t *v) cv[2] = (float)v[2]*SW_INV_255; cv[3] = (float)v[3]*SW_INV_255; - swColor4fv(cv); + sw_immediate_set_color(cv); } void swColor4f(float r, float g, float b, float a) @@ -4649,32 +4722,33 @@ void swColor4f(float r, float g, float b, float a) cv[2] = b; cv[3] = a; - swColor4fv(cv); + sw_immediate_set_color(cv); } void swColor4fv(const float *v) { - for (int i = 0; i < 4; i++) RLSW.current.color[i] = v[i]; + sw_immediate_set_color(v); } void swTexCoord2f(float u, float v) { - const float *m = RLSW.stackTexture[RLSW.stackTextureCounter - 1]; - - RLSW.current.texcoord[0] = m[0]*u + m[4]*v + m[12]; - RLSW.current.texcoord[1] = m[1]*u + m[5]*v + m[13]; + const float texcoord[2] = { u, v }; + sw_immediate_set_texcoord(texcoord); } void swTexCoord2fv(const float *v) { - const float *m = RLSW.stackTexture[RLSW.stackTextureCounter - 1]; - - RLSW.current.texcoord[0] = m[0]*v[0] + m[4]*v[1] + m[12]; - RLSW.current.texcoord[1] = m[1]*v[0] + m[5]*v[1] + m[13]; + sw_immediate_set_texcoord(v); } void swBindArray(SWarray type, void *buffer) { + if (sw_immediate_is_active()) + { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + switch (type) { case SW_VERTEX_ARRAY: RLSW.array.positions = (float *)buffer; break; @@ -4686,76 +4760,46 @@ void swBindArray(SWarray type, void *buffer) void swDrawArrays(SWdraw mode, int offset, int count) { - if ((!sw_is_ready_to_render()) || (RLSW.array.positions == NULL)) + if ((sw_immediate_is_active()) || (!sw_is_ready_to_render()) || (RLSW.array.positions == NULL)) { RLSW.errCode = SW_INVALID_OPERATION; return; } - swBegin(mode); + sw_immediate_begin(mode); { - const float *texMatrix = RLSW.stackTexture[RLSW.stackTextureCounter - 1]; - const float *defaultTexcoord = RLSW.current.texcoord; - const float *defaultColor = RLSW.current.color; - const float *positions = RLSW.array.positions; const float *texcoords = RLSW.array.texcoords; const uint8_t *colors = RLSW.array.colors; int end = offset + count; - for (int i = offset; i < end; i++) { - float u, v; - if (texcoords) - { - int idx = 2*i; - u = texcoords[idx]; - v = texcoords[idx + 1]; - } - else - { - u = defaultTexcoord[0]; - v = defaultTexcoord[1]; - } - - float texcoord[2]; - texcoord[0] = texMatrix[0]*u + texMatrix[4]*v + texMatrix[12]; - texcoord[1] = texMatrix[1]*u + texMatrix[5]*v + texMatrix[13]; - - float color[4] = { - defaultColor[0], - defaultColor[1], - defaultColor[2], - defaultColor[3] - }; + if (texcoords) sw_immediate_set_texcoord(&texcoords[2*i]); if (colors) { - int idx = 4*i; - color[0] *= (float)colors[idx]*SW_INV_255; - color[1] *= (float)colors[idx + 1]*SW_INV_255; - color[2] *= (float)colors[idx + 2]*SW_INV_255; - color[3] *= (float)colors[idx + 3]*SW_INV_255; + const uint8_t *c = &colors[4*i]; + float color[4] = { + (float)c[0]*SW_INV_255, + (float)c[1]*SW_INV_255, + (float)c[2]*SW_INV_255, + (float)c[3]*SW_INV_255, + }; + sw_immediate_set_color(color); } - int idx = 3*i; - float position[4] = { - positions[idx], - positions[idx + 1], - positions[idx + 2], - 1.0f - }; - - sw_immediate_push_vertex(position, color, texcoord); + const float *p = &positions[3*i]; + float position[4] = { p[0], p[1], p[2], 1.0f }; + sw_immediate_push_vertex(position); } } - swEnd(); + sw_immediate_end(); } void swDrawElements(SWdraw mode, int count, int type, const void *indices) { - if ((!sw_is_ready_to_render()) || (RLSW.array.positions == NULL)) + if ((sw_immediate_is_active()) || (!sw_is_ready_to_render()) || (RLSW.array.positions == NULL)) { RLSW.errCode = SW_INVALID_OPERATION; return; @@ -4767,82 +4811,56 @@ void swDrawElements(SWdraw mode, int count, int type, const void *indices) return; } - const uint8_t *indicesUb = NULL; - const uint16_t *indicesUs = NULL; - const uint32_t *indicesUi = NULL; - - switch (type) + if ((type != SW_UNSIGNED_BYTE) && (type != SW_UNSIGNED_SHORT) && (type != SW_UNSIGNED_INT)) { - case SW_UNSIGNED_BYTE: indicesUb = (const uint8_t *)indices; break; - case SW_UNSIGNED_SHORT: indicesUs = (const uint16_t *)indices; break; - case SW_UNSIGNED_INT: indicesUi = (const uint32_t *)indices; break; - default: RLSW.errCode = SW_INVALID_ENUM; return; + RLSW.errCode = SW_INVALID_ENUM; + return; } - swBegin(mode); + sw_immediate_begin(mode); { - const float *texMatrix = RLSW.stackTexture[RLSW.stackTextureCounter - 1]; - const float *defaultTexcoord = RLSW.current.texcoord; - const float *defaultColor = RLSW.current.color; - const float *positions = RLSW.array.positions; const float *texcoords = RLSW.array.texcoords; const uint8_t *colors = RLSW.array.colors; + const uint8_t *indicesUb = (type == SW_UNSIGNED_BYTE)? indices : NULL; + const uint16_t *indicesUs = (type == SW_UNSIGNED_SHORT)? indices : NULL; + const uint32_t *indicesUi = (type == SW_UNSIGNED_INT)? indices : NULL; + for (int i = 0; i < count; i++) { - int index = indicesUb? indicesUb[i] : - (indicesUs? indicesUs[i] : indicesUi[i]); + uint32_t index = indicesUb? (uint32_t)indicesUb[i] : (indicesUs? (uint32_t)indicesUs[i] : (uint32_t)indicesUi[i]); - float u, v; - if (texcoords) - { - int idx = 2*index; - u = texcoords[idx]; - v = texcoords[idx + 1]; - } - else - { - u = defaultTexcoord[0]; - v = defaultTexcoord[1]; - } - - float texcoord[2]; - texcoord[0] = texMatrix[0]*u + texMatrix[4]*v + texMatrix[12]; - texcoord[1] = texMatrix[1]*u + texMatrix[5]*v + texMatrix[13]; - - float color[4] = { - defaultColor[0], - defaultColor[1], - defaultColor[2], - defaultColor[3] - }; + if (texcoords) sw_immediate_set_texcoord(&texcoords[2*index]); if (colors) { - int idx = 4*index; - color[0] *= (float)colors[idx]*SW_INV_255; - color[1] *= (float)colors[idx + 1]*SW_INV_255; - color[2] *= (float)colors[idx + 2]*SW_INV_255; - color[3] *= (float)colors[idx + 3]*SW_INV_255; + const uint8_t *c = &colors[4*index]; + float color[4] = { + (float)c[0]*SW_INV_255, + (float)c[1]*SW_INV_255, + (float)c[2]*SW_INV_255, + (float)c[3]*SW_INV_255, + }; + sw_immediate_set_color(color); } - int idx = 3*index; - float position[4] = { - positions[idx], - positions[idx + 1], - positions[idx + 2], - 1.0f - }; - - sw_immediate_push_vertex(position, color, texcoord); + const float *p = &positions[3*index]; + float position[4] = { p[0], p[1], p[2], 1.0f }; + sw_immediate_push_vertex(position); } } - swEnd(); + sw_immediate_end(); } void swGenTextures(int count, sw_handle_t *textures) { + if (sw_immediate_is_active()) + { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + if (!count || !textures) return; for (int i = 0; i < count; i++) @@ -4855,6 +4873,12 @@ void swGenTextures(int count, sw_handle_t *textures) void swDeleteTextures(int count, sw_handle_t *textures) { + if (sw_immediate_is_active()) + { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + if (!count || !textures) return; for (int i = 0; i < count; i++) @@ -4866,7 +4890,7 @@ void swDeleteTextures(int count, sw_handle_t *textures) if (tex == RLSW.depthBuffer) RLSW.depthBuffer = NULL; sw_texture_free(tex); - *tex = (sw_texture_t){ 0 }; + *tex = (sw_texture_t) { 0 }; sw_pool_free(&RLSW.texturePool, textures[i]); } @@ -4874,14 +4898,35 @@ void swDeleteTextures(int count, sw_handle_t *textures) void swBindTexture(sw_handle_t id) { - if (id == SW_HANDLE_NULL) { RLSW.boundTexture = NULL; return; } - if (!sw_is_texture_valid(id)) { RLSW.errCode = SW_INVALID_VALUE; return; } + if (sw_immediate_is_active()) + { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + + if (id == SW_HANDLE_NULL) + { + RLSW.boundTexture = NULL; + return; + } + + if (!sw_is_texture_valid(id)) + { + RLSW.errCode = SW_INVALID_VALUE; + return; + } RLSW.boundTexture = sw_pool_get(&RLSW.texturePool, id); } void swTexStorage2D(int width, int height, SWinternalformat format) { + if (sw_immediate_is_active()) + { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + if (RLSW.boundTexture == NULL) return; int pixelFormat = SW_PIXELFORMAT_UNKNOWN; @@ -4912,6 +4957,12 @@ void swTexStorage2D(int width, int height, SWinternalformat format) void swTexImage2D(int width, int height, SWformat format, SWtype type, const void *data) { + if (sw_immediate_is_active()) + { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + if (RLSW.boundTexture == NULL) return; int pixelFormat = sw_pixel_get_format(format, type); @@ -4922,6 +4973,12 @@ void swTexImage2D(int width, int height, SWformat format, SWtype type, const voi void swTexSubImage2D(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) { + if (sw_immediate_is_active()) + { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + if (!sw_is_texture_complete(RLSW.boundTexture) || (!pixels) || (width <= 0) || (height <= 0)) { RLSW.errCode = SW_INVALID_VALUE; @@ -4934,20 +4991,28 @@ void swTexSubImage2D(GLint x, GLint y, GLsizei width, GLsizei height, GLenum for return; } - sw_pixelformat_t pFormat = sw_pixel_get_format(format, type); - if ((pFormat <= 0) || (pFormat >= SW_PIXELFORMAT_COUNT)) + sw_pixel_write_color_f writeColor = sw_pixel_get_write_color_func(RLSW.boundTexture->format); + if (writeColor == NULL) // probably a depth format + { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + + sw_pixelformat_t srcPixelFormat = sw_pixel_get_format(format, type); + sw_pixel_read_color_f readColor = sw_pixel_get_read_color_func(srcPixelFormat); + if (readColor == NULL) { RLSW.errCode = SW_INVALID_ENUM; return; } - const int srcPixelSize = SW_PIXELFORMAT_SIZE[pFormat]; + const int srcPixelSize = SW_PIXELFORMAT_SIZE[srcPixelFormat]; const int dstPixelSize = SW_PIXELFORMAT_SIZE[RLSW.boundTexture->format]; const uint8_t *srcBytes = (const uint8_t *)pixels; uint8_t *dstBytes = (uint8_t *)RLSW.boundTexture->pixels; - if (pFormat == RLSW.boundTexture->format) + if (srcPixelFormat == RLSW.boundTexture->format) { const int rowSize = width*dstPixelSize; const int dstStride = RLSW.boundTexture->width*dstPixelSize; @@ -4965,6 +5030,8 @@ void swTexSubImage2D(GLint x, GLint y, GLsizei width, GLsizei height, GLenum for return; } + bool alphaFound = false; + for (int j = 0; j < height; ++j) { for (int i = 0; i < width; ++i) @@ -4973,14 +5040,24 @@ void swTexSubImage2D(GLint x, GLint y, GLsizei width, GLsizei height, GLenum for const int srcPixelOffset = ((j*width) + i)*srcPixelSize; const int dstPixelOffset = (((y + j)*RLSW.boundTexture->width) + (x + i))*dstPixelSize; - sw_pixel_get_color(color, srcBytes, srcPixelOffset, pFormat); - sw_pixel_set_color(dstBytes, color, dstPixelOffset, RLSW.boundTexture->format); + readColor(color, srcBytes, srcPixelOffset); + alphaFound |= (color[3] < 1.0f); + + writeColor(dstBytes, color, dstPixelOffset); } } + + RLSW.boundTexture->alpha = alphaFound? SW_PIXELFORMAT_ALPHA[srcPixelFormat] : SW_PIXEL_ALPHA_NONE; } void swTexParameteri(int param, int value) { + if (sw_immediate_is_active()) + { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + if (RLSW.boundTexture == NULL) return; switch (param) @@ -5011,6 +5088,12 @@ void swTexParameteri(int param, int value) void swGenFramebuffers(int count, uint32_t *framebuffers) { + if (sw_immediate_is_active()) + { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + if (!count || !framebuffers) return; for (int i = 0; i < count; i++) @@ -5023,6 +5106,12 @@ void swGenFramebuffers(int count, uint32_t *framebuffers) void swDeleteFramebuffers(int count, uint32_t *framebuffers) { + if (sw_immediate_is_active()) + { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + if (!count || !framebuffers) return; for (int i = 0; i < count; i++) @@ -5043,6 +5132,12 @@ void swDeleteFramebuffers(int count, uint32_t *framebuffers) void swBindFramebuffer(uint32_t id) { + if (sw_immediate_is_active()) + { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + if (id == SW_HANDLE_NULL) { RLSW.boundFramebufferId = SW_HANDLE_NULL; @@ -5065,7 +5160,7 @@ void swBindFramebuffer(uint32_t id) void swFramebufferTexture2D(SWattachment attach, uint32_t texture) { - if (RLSW.boundFramebufferId == SW_HANDLE_NULL) + if ((sw_immediate_is_active()) || (RLSW.boundFramebufferId == SW_HANDLE_NULL)) { RLSW.errCode = SW_INVALID_OPERATION; // not really standard but hey return; @@ -5161,152 +5256,202 @@ void swGetFramebufferAttachmentParameteriv(SWattachment attachment, SWattachget #define SW_RASTER_TRIANGLE_SPAN SW_CONCATX(sw_raster_triangle_span_, RLSW_TEMPLATE_RASTER_TRIANGLE) #define SW_RASTER_TRIANGLE SW_CONCATX(sw_raster_triangle_, RLSW_TEMPLATE_RASTER_TRIANGLE) +#ifdef SW_ENABLE_TEXTURE + #define SW_GET_GRAD sw_get_vertex_grad_CTH + #define SW_ADD_GRAD sw_add_vertex_grad_CTH + #define SW_ADD_GRAD_SCALED sw_add_vertex_grad_scaled_CTH +#else + #define SW_GET_GRAD sw_get_vertex_grad_CH + #define SW_ADD_GRAD sw_add_vertex_grad_CH + #define SW_ADD_GRAD_SCALED sw_add_vertex_grad_scaled_CH +#endif + static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t *end, float dUdy, float dVdy) { - // Gets the start and end coordinates - int xStart = (int)start->screen[0]; - int xEnd = (int)end->screen[0]; - - // Avoid empty lines + // Gets the start/end coordinates and skip empty lines + int xStart = (int)start->coord[0]; + int xEnd = (int)end->coord[0]; if (xStart == xEnd) return; - // Compute the subpixel distance to traverse before the first pixel - float xSubstep = 1.0f - sw_fract(start->screen[0]); - // Compute the inverse horizontal distance along the X axis - float dxRcp = 1.0f/(end->screen[0] - start->screen[0]); + float dxRcp = 1.0f/(end->coord[0] - start->coord[0]); // Compute the interpolation steps along the X axis -#ifdef SW_ENABLE_DEPTH_TEST - float dZdx = (end->homogeneous[2] - start->homogeneous[2])*dxRcp; -#endif - float dWdx = (end->homogeneous[3] - start->homogeneous[3])*dxRcp; + float dWdx = (end->coord[3] - start->coord[3])*dxRcp; float dCdx[4] = { (end->color[0] - start->color[0])*dxRcp, (end->color[1] - start->color[1])*dxRcp, (end->color[2] - start->color[2])*dxRcp, (end->color[3] - start->color[3])*dxRcp }; +#ifdef SW_ENABLE_DEPTH_TEST + float dZdx = (end->coord[2] - start->coord[2])*dxRcp; +#endif #ifdef SW_ENABLE_TEXTURE float dUdx = (end->texcoord[0] - start->texcoord[0])*dxRcp; float dVdx = (end->texcoord[1] - start->texcoord[1])*dxRcp; #endif + // Compute the subpixel distance to traverse before the first pixel + float xSubstep = 1.0f - sw_fract(start->coord[0]); + // Initializing the interpolation starting values -#ifdef SW_ENABLE_DEPTH_TEST - float z = start->homogeneous[2] + dZdx*xSubstep; -#endif - float w = start->homogeneous[3] + dWdx*xSubstep; + float w = start->coord[3] + dWdx*xSubstep; float color[4] = { start->color[0] + dCdx[0]*xSubstep, start->color[1] + dCdx[1]*xSubstep, start->color[2] + dCdx[2]*xSubstep, start->color[3] + dCdx[3]*xSubstep }; +#ifdef SW_ENABLE_DEPTH_TEST + float z = start->coord[2] + dZdx*xSubstep; +#endif #ifdef SW_ENABLE_TEXTURE float u = start->texcoord[0] + dUdx*xSubstep; float v = start->texcoord[1] + dVdx*xSubstep; #endif // Pre-calculate the starting pointers for the framebuffer row - int y = (int)start->screen[1]; + int y = (int)start->coord[1]; int baseOffset = y*RLSW.colorBuffer->width + xStart; uint8_t *cPtr = (uint8_t *)(RLSW.colorBuffer->pixels) + baseOffset*SW_FRAMEBUFFER_COLOR_SIZE; #ifdef SW_ENABLE_DEPTH_TEST uint8_t *dPtr = (uint8_t *)(RLSW.depthBuffer->pixels) + baseOffset*SW_FRAMEBUFFER_DEPTH_SIZE; #endif - // Scanline rasterization - for (int x = xStart; x < xEnd; x++) +#define SW_AFFINE_BLOCK 16 + + int x = xStart; + while (x < xEnd) { - float wRcp = 1.0f/w; + // Clamp last block to remaining pixels + int blockEnd = x + SW_AFFINE_BLOCK; + if (blockEnd > xEnd) blockEnd = xEnd; + float blockLenF = (float)(blockEnd - x); + float blockLenRcp = 1.0f/blockLenF; + + // Only 2 '1/w' here; none inside the pixel loop + float wRcpA = 1.0f/w; + float wB = w + dWdx*blockLenF; + float wRcpB = 1.0f/wB; + + // Perspective-correct color at both block endpoints, then affine gradient float srcColor[4] = { - color[0]*wRcp, - color[1]*wRcp, - color[2]*wRcp, - color[3]*wRcp + color[0]*wRcpA, + color[1]*wRcpA, + color[2]*wRcpA, + color[3]*wRcpA + }; + float dSrcColordx[4] = { + ((color[0] + dCdx[0]*blockLenF)*wRcpB - srcColor[0])*blockLenRcp, + ((color[1] + dCdx[1]*blockLenF)*wRcpB - srcColor[1])*blockLenRcp, + ((color[2] + dCdx[2]*blockLenF)*wRcpB - srcColor[2])*blockLenRcp, + ((color[3] + dCdx[3]*blockLenF)*wRcpB - srcColor[3])*blockLenRcp }; - #ifdef SW_ENABLE_DEPTH_TEST + #ifdef SW_ENABLE_TEXTURE + // Perspective-correct UVs at both endpoints, then affine gradient + float uAffine = u*wRcpA; + float vAffine = v*wRcpA; + float dUaffine = ((u + dUdx*blockLenF)*wRcpB - uAffine)*blockLenRcp; + float dVaffine = ((v + dVdx*blockLenF)*wRcpB - vAffine)*blockLenRcp; + #endif + + // Inner span pixel loop + for (; x < blockEnd; x++) { - /* TODO: Implement different depth funcs? */ - float depth = SW_FRAMEBUFFER_DEPTH_GET(dPtr, 0); - if (z > depth) goto discard; + #ifdef SW_ENABLE_DEPTH_TEST + { + float depth = SW_FRAMEBUFFER_DEPTH_GET(dPtr, 0); + if (z > depth) goto discard; + SW_FRAMEBUFFER_DEPTH_SET(dPtr, z, 0); + } + #endif - /* TODO: Implement depth mask */ - SW_FRAMEBUFFER_DEPTH_SET(dPtr, z, 0); + #ifdef SW_ENABLE_TEXTURE + { + float texColor[4]; + sw_texture_sample(texColor, RLSW.boundTexture, uAffine, vAffine, dUdx, dUdy, dVdx, dVdy); + float finalColor[4] = { + srcColor[0]*texColor[0], + srcColor[1]*texColor[1], + srcColor[2]*texColor[2], + srcColor[3]*texColor[3] + }; + #ifdef SW_ENABLE_BLEND + { + float dstColor[4]; + SW_FRAMEBUFFER_COLOR_GET(dstColor, cPtr, 0); + RLSW.blendFunc(dstColor, finalColor); + SW_FRAMEBUFFER_COLOR_SET(cPtr, dstColor, 0); + } + #else + SW_FRAMEBUFFER_COLOR_SET(cPtr, finalColor, 0); + #endif + } + #else + { + #ifdef SW_ENABLE_BLEND + { + float dstColor[4]; + SW_FRAMEBUFFER_COLOR_GET(dstColor, cPtr, 0); + RLSW.blendFunc(dstColor, srcColor); + SW_FRAMEBUFFER_COLOR_SET(cPtr, dstColor, 0); + } + #else + SW_FRAMEBUFFER_COLOR_SET(cPtr, srcColor, 0); + #endif + } + #endif + + discard: + srcColor[0] += dSrcColordx[0]; + srcColor[1] += dSrcColordx[1]; + srcColor[2] += dSrcColordx[2]; + srcColor[3] += dSrcColordx[3]; + cPtr += SW_FRAMEBUFFER_COLOR_SIZE; + + #ifdef SW_ENABLE_DEPTH_TEST + { + z += dZdx; + dPtr += SW_FRAMEBUFFER_DEPTH_SIZE; + } + #endif + + #ifdef SW_ENABLE_TEXTURE + { + uAffine += dUaffine; + vAffine += dVaffine; + } + #endif } - #endif + // Advance perspective-space accumulators by the full block width + w = wB; + color[0] += dCdx[0]*blockLenF; + color[1] += dCdx[1]*blockLenF; + color[2] += dCdx[2]*blockLenF; + color[3] += dCdx[3]*blockLenF; #ifdef SW_ENABLE_TEXTURE - { - float texColor[4]; - float s = u*wRcp; - float t = v*wRcp; - sw_texture_sample(texColor, RLSW.boundTexture, s, t, dUdx, dUdy, dVdx, dVdy); - srcColor[0] *= texColor[0]; - srcColor[1] *= texColor[1]; - srcColor[2] *= texColor[2]; - srcColor[3] *= texColor[3]; - } - #endif - - #ifdef SW_ENABLE_BLEND - { - float dstColor[4]; - SW_FRAMEBUFFER_COLOR_GET(dstColor, cPtr, 0); - sw_blend_colors(dstColor, srcColor); - SW_FRAMEBUFFER_COLOR_SET(cPtr, dstColor, 0); - } - #else - { - SW_FRAMEBUFFER_COLOR_SET(cPtr, srcColor, 0); - } - #endif - - // Increment the interpolation parameter, UVs, and pointers - discard: - #ifdef SW_ENABLE_DEPTH_TEST - { - z += dZdx; - } - #endif - - w += dWdx; - - color[0] += dCdx[0]; - color[1] += dCdx[1]; - color[2] += dCdx[2]; - color[3] += dCdx[3]; - - #ifdef SW_ENABLE_TEXTURE - { - u += dUdx; - v += dVdx; - } - #endif - - cPtr += SW_FRAMEBUFFER_COLOR_SIZE; - - #ifdef SW_ENABLE_DEPTH_TEST - { - dPtr += SW_FRAMEBUFFER_DEPTH_SIZE; - } + u += dUdx*blockLenF; + v += dVdx*blockLenF; #endif } + +#undef SW_AFFINE_BLOCK } static void SW_RASTER_TRIANGLE(const sw_vertex_t *v0, const sw_vertex_t *v1, const sw_vertex_t *v2) { // Swap vertices by increasing Y - if (v0->screen[1] > v1->screen[1]) { const sw_vertex_t *tmp = v0; v0 = v1; v1 = tmp; } - if (v1->screen[1] > v2->screen[1]) { const sw_vertex_t *tmp = v1; v1 = v2; v2 = tmp; } - if (v0->screen[1] > v1->screen[1]) { const sw_vertex_t *tmp = v0; v0 = v1; v1 = tmp; } + if (v0->coord[1] > v1->coord[1]) { const sw_vertex_t *tmp = v0; v0 = v1; v1 = tmp; } + if (v1->coord[1] > v2->coord[1]) { const sw_vertex_t *tmp = v1; v1 = v2; v2 = tmp; } + if (v0->coord[1] > v1->coord[1]) { const sw_vertex_t *tmp = v0; v0 = v1; v1 = tmp; } // Extracting coordinates from the sorted vertices - float x0 = v0->screen[0], y0 = v0->screen[1]; - float x1 = v1->screen[0], y1 = v1->screen[1]; - float x2 = v2->screen[0], y2 = v2->screen[1]; + float x0 = v0->coord[0], y0 = v0->coord[1]; + float x1 = v1->coord[0], y1 = v1->coord[1]; + float x2 = v2->coord[0], y2 = v2->coord[1]; // Compute height differences float h02 = y2 - y0; @@ -5315,75 +5460,64 @@ static void SW_RASTER_TRIANGLE(const sw_vertex_t *v0, const sw_vertex_t *v1, con if (h02 < 1e-6f) return; - // Precompute the inverse values without additional checks + // Inverse edge dy for per-edge dV/dy (scanline interpolation) float h02Rcp = 1.0f/h02; float h01Rcp = (h01 > 1e-6f)? 1.0f/h01 : 0.0f; float h12Rcp = (h12 > 1e-6f)? 1.0f/h12 : 0.0f; - // Pre-calculation of slopes - float dXdy02 = (x2 - x0)*h02Rcp; - float dXdy01 = (x1 - x0)*h01Rcp; - float dXdy12 = (x2 - x1)*h12Rcp; + // Compute gradients for each side of the triangle + sw_vertex_t dVXdy02, dVXdy01, dVXdy12; + SW_GET_GRAD(&dVXdy02, v0, v2, h02Rcp); + SW_GET_GRAD(&dVXdy01, v0, v1, h01Rcp); + SW_GET_GRAD(&dVXdy12, v1, v2, h12Rcp); // Y subpixel correction float y0Substep = 1.0f - sw_fract(y0); float y1Substep = 1.0f - sw_fract(y1); + // Get a copy of vertices for interpolation and apply substep correction + sw_vertex_t lVert = *v0, rVert = *v0; + SW_ADD_GRAD_SCALED(&lVert, &dVXdy02, y0Substep); + SW_ADD_GRAD_SCALED(&rVert, &dVXdy01, y0Substep); + // Y bounds (vertical clipping) int yTop = (int)y0; int yMid = (int)y1; int yBot = (int)y2; - // Compute gradients for each side of the triangle - sw_vertex_t dVXdy02, dVXdy01, dVXdy12; - sw_get_vertex_grad_PTCH(&dVXdy02, v0, v2, h02Rcp); - sw_get_vertex_grad_PTCH(&dVXdy01, v0, v1, h01Rcp); - sw_get_vertex_grad_PTCH(&dVXdy12, v1, v2, h12Rcp); - - // Get a copy of vertices for interpolation and apply substep correction - sw_vertex_t vLeft = *v0, vRight = *v0; - sw_add_vertex_grad_scaled_PTCH(&vLeft, &dVXdy02, y0Substep); - sw_add_vertex_grad_scaled_PTCH(&vRight, &dVXdy01, y0Substep); - - vLeft.screen[0] += dXdy02*y0Substep; - vRight.screen[0] += dXdy01*y0Substep; - // Scanline for the upper part of the triangle for (int y = yTop; y < yMid; y++) { - vLeft.screen[1] = vRight.screen[1] = y; - - if (vLeft.screen[0] < vRight.screen[0]) SW_RASTER_TRIANGLE_SPAN(&vLeft, &vRight, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); - else SW_RASTER_TRIANGLE_SPAN(&vRight, &vLeft, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); - - sw_add_vertex_grad_PTCH(&vLeft, &dVXdy02); - vLeft.screen[0] += dXdy02; - - sw_add_vertex_grad_PTCH(&vRight, &dVXdy01); - vRight.screen[0] += dXdy01; + lVert.coord[1] = rVert.coord[1] = y; + bool longSideIsLeft = (lVert.coord[0] < rVert.coord[0]); + const sw_vertex_t *a = longSideIsLeft? &lVert : &rVert; + const sw_vertex_t *b = longSideIsLeft? &rVert : &lVert; + SW_RASTER_TRIANGLE_SPAN(a, b, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); + SW_ADD_GRAD(&lVert, &dVXdy02); + SW_ADD_GRAD(&rVert, &dVXdy01); } // Get a copy of next right for interpolation and apply substep correction - vRight = *v1; - sw_add_vertex_grad_scaled_PTCH(&vRight, &dVXdy12, y1Substep); - vRight.screen[0] += dXdy12*y1Substep; + rVert = *v1; + SW_ADD_GRAD_SCALED(&rVert, &dVXdy12, y1Substep); // Scanline for the lower part of the triangle for (int y = yMid; y < yBot; y++) { - vLeft.screen[1] = vRight.screen[1] = y; - - if (vLeft.screen[0] < vRight.screen[0]) SW_RASTER_TRIANGLE_SPAN(&vLeft, &vRight, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); - else SW_RASTER_TRIANGLE_SPAN(&vRight, &vLeft, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); - - sw_add_vertex_grad_PTCH(&vLeft, &dVXdy02); - vLeft.screen[0] += dXdy02; - - sw_add_vertex_grad_PTCH(&vRight, &dVXdy12); - vRight.screen[0] += dXdy12; + lVert.coord[1] = rVert.coord[1] = y; + bool longSideIsLeft = (lVert.coord[0] < rVert.coord[0]); + const sw_vertex_t *a = longSideIsLeft? &lVert : &rVert; + const sw_vertex_t *b = longSideIsLeft? &rVert : &lVert; + SW_RASTER_TRIANGLE_SPAN(a, b, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); + SW_ADD_GRAD(&lVert, &dVXdy02); + SW_ADD_GRAD(&rVert, &dVXdy12); } } +#undef SW_GET_GRAD +#undef SW_ADD_GRAD +#undef SW_ADD_GRAD_SCALED + #endif // RLSW_TEMPLATE_RASTER_TRIANGLE //------------------------------------------------------------------------------------------- @@ -5399,9 +5533,8 @@ static void SW_RASTER_TRIANGLE(const sw_vertex_t *v0, const sw_vertex_t *v1, con #define SW_RASTER_QUAD SW_CONCATX(sw_raster_quad_, RLSW_TEMPLATE_RASTER_QUAD) -// REVIEW: Could a perfectly aligned quad, where one of the four points has a different depth, -// still appear perfectly aligned from a certain point of view? -// Because in that case, it's still needed to perform perspective division for textures and colors... +// NOTE: This function should only render affine axis-aligned quads +// No perspective divide is applied after interpolation static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b, const sw_vertex_t *c, const sw_vertex_t *d) @@ -5412,18 +5545,18 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b, const sw_vertex_t *tl = verts[0], *tr = verts[0], *br = verts[0], *bl = verts[0]; for (int i = 1; i < 4; i++) { - float sum = verts[i]->screen[0] + verts[i]->screen[1]; - float diff = verts[i]->screen[0] - verts[i]->screen[1]; - if (sum < tl->screen[0] + tl->screen[1]) tl = verts[i]; - if (diff > tr->screen[0] - tr->screen[1]) tr = verts[i]; - if (sum > br->screen[0] + br->screen[1]) br = verts[i]; - if (diff < bl->screen[0] - bl->screen[1]) bl = verts[i]; + float sum = verts[i]->coord[0] + verts[i]->coord[1]; + float diff = verts[i]->coord[0] - verts[i]->coord[1]; + if (sum < tl->coord[0] + tl->coord[1]) tl = verts[i]; + if (diff > tr->coord[0] - tr->coord[1]) tr = verts[i]; + if (sum > br->coord[0] + br->coord[1]) br = verts[i]; + if (diff < bl->coord[0] - bl->coord[1]) bl = verts[i]; } - int xMin = (int)tl->screen[0]; - int yMin = (int)tl->screen[1]; - int xMax = (int)br->screen[0]; - int yMax = (int)br->screen[1]; + int xMin = (int)tl->coord[0]; + int yMin = (int)tl->coord[1]; + int xMax = (int)br->coord[0]; + int yMax = (int)br->coord[1]; float w = (float)(xMax - xMin); float h = (float)(yMax - yMin); @@ -5433,8 +5566,8 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b, float hRcp = 1.0f/h; // Subpixel corrections - float xSubstep = 1.0f - sw_fract(tl->screen[0]); - float ySubstep = 1.0f - sw_fract(tl->screen[1]); + float xSubstep = 1.0f - sw_fract(tl->coord[0]); + float ySubstep = 1.0f - sw_fract(tl->coord[1]); // Gradients along X (tl->tr) and Y (tl->bl) float dCdx[4] = { @@ -5451,9 +5584,9 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b, }; #ifdef SW_ENABLE_DEPTH_TEST - float dZdx = (tr->homogeneous[2] - tl->homogeneous[2])*wRcp; - float dZdy = (bl->homogeneous[2] - tl->homogeneous[2])*hRcp; - float zRow = tl->homogeneous[2] + dZdx*xSubstep + dZdy*ySubstep; + float dZdx = (tr->coord[2] - tl->coord[2])*wRcp; + float dZdy = (bl->coord[2] - tl->coord[2])*hRcp; + float zRow = tl->coord[2] + dZdx*xSubstep + dZdy*ySubstep; #endif #ifdef SW_ENABLE_TEXTURE @@ -5519,7 +5652,7 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b, { float dstColor[4]; SW_FRAMEBUFFER_COLOR_GET(dstColor, cPtr, 0); - sw_blend_colors(dstColor, srcColor); + RLSW.blendFunc(dstColor, srcColor); SW_FRAMEBUFFER_COLOR_SET(cPtr, dstColor, 0); } #else @@ -5589,10 +5722,10 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b, static void SW_RASTER_LINE(const sw_vertex_t *v0, const sw_vertex_t *v1) { // Convert from pixel-center convention (n+0.5) to pixel-origin convention (n) - float x0 = v0->screen[0] - 0.5f; - float y0 = v0->screen[1] - 0.5f; - float x1 = v1->screen[0] - 0.5f; - float y1 = v1->screen[1] - 0.5f; + float x0 = v0->coord[0] - 0.5f; + float y0 = v0->coord[1] - 0.5f; + float x1 = v1->coord[0] - 0.5f; + float y1 = v1->coord[1] - 0.5f; float dx = x1 - x0; float dy = y1 - y0; @@ -5617,7 +5750,7 @@ static void SW_RASTER_LINE(const sw_vertex_t *v0, const sw_vertex_t *v1) float yInc = dy/steps; float stepRcp = 1.0f/steps; #ifdef SW_ENABLE_DEPTH_TEST - float zInc = (v1->homogeneous[2] - v0->homogeneous[2])*stepRcp; + float zInc = (v1->coord[2] - v0->coord[2])*stepRcp; #endif float rInc = (v1->color[0] - v0->color[0])*stepRcp; float gInc = (v1->color[1] - v0->color[1])*stepRcp; @@ -5628,7 +5761,7 @@ static void SW_RASTER_LINE(const sw_vertex_t *v0, const sw_vertex_t *v1) float x = x0 + xInc*substep; float y = y0 + yInc*substep; #ifdef SW_ENABLE_DEPTH_TEST - float z = v0->homogeneous[2] + zInc*substep; + float z = v0->coord[2] + zInc*substep; #endif float r = v0->color[0] + rInc*substep; float g = v0->color[1] + gInc*substep; @@ -5672,7 +5805,7 @@ static void SW_RASTER_LINE(const sw_vertex_t *v0, const sw_vertex_t *v1) { float dstColor[4]; SW_FRAMEBUFFER_COLOR_GET(dstColor, cPtr, 0); - sw_blend_colors(dstColor, srcColor); + RLSW.blendFunc(dstColor, srcColor); SW_FRAMEBUFFER_COLOR_SET(cPtr, dstColor, 0); } #else @@ -5696,19 +5829,19 @@ static void SW_RASTER_LINE(const sw_vertex_t *v0, const sw_vertex_t *v1) } } -static void SW_RASTER_LINE_THICK(const sw_vertex_t *v1, const sw_vertex_t *v2) +static void SW_RASTER_LINE_THICK(const sw_vertex_t *v0, const sw_vertex_t *v1) { - sw_vertex_t tv1, tv2; + sw_vertex_t tv0, tv1; - int x1 = (int)v1->screen[0]; - int y1 = (int)v1->screen[1]; - int x2 = (int)v2->screen[0]; - int y2 = (int)v2->screen[1]; + int x0 = (int)v0->coord[0]; + int y0 = (int)v0->coord[1]; + int x1 = (int)v1->coord[0]; + int y1 = (int)v1->coord[1]; - int dx = x2 - x1; - int dy = y2 - y1; + int dx = x1 - x0; + int dy = y1 - y0; - SW_RASTER_LINE(v1, v2); + SW_RASTER_LINE(v0, v1); if ((dx != 0) && (abs(dy/dx) < 1)) { @@ -5716,14 +5849,14 @@ static void SW_RASTER_LINE_THICK(const sw_vertex_t *v1, const sw_vertex_t *v2) wy >>= 1; for (int i = 1; i <= wy; i++) { - tv1 = *v1, tv2 = *v2; - tv1.screen[1] -= i; - tv2.screen[1] -= i; - SW_RASTER_LINE(&tv1, &tv2); - tv1 = *v1, tv2 = *v2; - tv1.screen[1] += i; - tv2.screen[1] += i; - SW_RASTER_LINE(&tv1, &tv2); + tv0 = *v0, tv1 = *v1; + tv0.coord[1] -= i; + tv1.coord[1] -= i; + SW_RASTER_LINE(&tv0, &tv1); + tv0 = *v0, tv1 = *v1; + tv0.coord[1] += i; + tv1.coord[1] += i; + SW_RASTER_LINE(&tv0, &tv1); } } else if (dy != 0) @@ -5732,14 +5865,14 @@ static void SW_RASTER_LINE_THICK(const sw_vertex_t *v1, const sw_vertex_t *v2) wx >>= 1; for (int i = 1; i <= wx; i++) { - tv1 = *v1, tv2 = *v2; - tv1.screen[0] -= i; - tv2.screen[0] -= i; - SW_RASTER_LINE(&tv1, &tv2); - tv1 = *v1, tv2 = *v2; - tv1.screen[0] += i; - tv2.screen[0] += i; - SW_RASTER_LINE(&tv1, &tv2); + tv0 = *v0, tv1 = *v1; + tv0.coord[0] -= i; + tv1.coord[0] -= i; + SW_RASTER_LINE(&tv0, &tv1); + tv0 = *v0, tv1 = *v1; + tv0.coord[0] += i; + tv1.coord[0] += i; + SW_RASTER_LINE(&tv0, &tv1); } } } @@ -5782,7 +5915,7 @@ static void SW_RASTER_POINT_PIXEL(int x, int y, float z, const float color[4]) { float dstColor[4]; SW_FRAMEBUFFER_COLOR_GET(dstColor, cPtr, 0); - sw_blend_colors(dstColor, color); + RLSW.blendFunc(dstColor, color); SW_FRAMEBUFFER_COLOR_SET(cPtr, dstColor, 0); } #else @@ -5794,9 +5927,9 @@ static void SW_RASTER_POINT_PIXEL(int x, int y, float z, const float color[4]) static void SW_RASTER_POINT(const sw_vertex_t *v) { - int cx = v->screen[0]; - int cy = v->screen[1]; - float cz = v->homogeneous[2]; + int cx = v->coord[0]; + int cy = v->coord[1]; + float cz = v->coord[2]; int radius = RLSW.pointRadius; const float *color = v->color; From 8783e66e21cd640ea496a65c79ca6e5f78eed60e Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Mar 2026 23:42:20 +0100 Subject: [PATCH 200/319] Update rlsw.h --- src/external/rlsw.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/external/rlsw.h b/src/external/rlsw.h index b243f576f..e4b481a0b 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -4059,9 +4059,9 @@ void swBlitPixels(int xDst, int yDst, int wDst, int hDst, int xSrc, int ySrc, in // Get framefuffer pixel data pointer and size void *swGetColorBuffer(int *width, int *height) { - if (width != NULL) *width = RLSW.framebuffer.color->width; - if (height != NULL) *height = RLSW.framebuffer.color->height; - return RLSW.framebuffer.color->pixels; + if (width != NULL) *width = RLSW.framebuffer.color.width; + if (height != NULL) *height = RLSW.framebuffer.color.height; + return RLSW.framebuffer.color.pixels; } void swEnable(SWstate state) From 8a685877eb321fe0eaeabab7b8ec69d2f8a0064d Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Fri, 20 Mar 2026 12:07:50 +0100 Subject: [PATCH 201/319] [rlsw] Remove position attribute from `sw_vertex_t` (#5677) * review `sw_vertex_t` * fix comment * fix pop matrix --- src/external/rlsw.h | 402 ++++++++++++++++++++++---------------------- 1 file changed, 198 insertions(+), 204 deletions(-) diff --git a/src/external/rlsw.h b/src/external/rlsw.h index e4b481a0b..8472b03e9 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -953,10 +953,9 @@ typedef void (*sw_raster_point_f)(const sw_vertex_t*); typedef float sw_matrix_t[4*4]; typedef struct sw_vertex { - float position[4]; // Position coordinates + float position[4]; // Clip space (x,y,z,w) -> NDC (after /w) -> screen space (x,y,z,1/w) float color[4]; // Color value (RGBA) float texcoord[2]; // Texture coordinates - float coord[4]; // Clip space (x,y,z,w) -> NDC (after /w) -> screen space (x,y,z,1/w) } sw_vertex_t; typedef struct { @@ -1221,7 +1220,7 @@ static inline float sw_luminance(const float *color) return color[0]*0.299f + color[1]*0.587f + color[2]*0.114f; } -static inline void sw_lerp_vertex_PCTH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT a, const sw_vertex_t *SW_RESTRICT b, float t) +static inline void sw_lerp_vertex_PCT(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT a, const sw_vertex_t *SW_RESTRICT b, float t) { const float tInv = 1.0f - t; @@ -1240,16 +1239,16 @@ static inline void sw_lerp_vertex_PCTH(sw_vertex_t *SW_RESTRICT out, const sw_ve // Texture coordinate interpolation (2 components) out->texcoord[0] = a->texcoord[0]*tInv + b->texcoord[0]*t; out->texcoord[1] = a->texcoord[1]*tInv + b->texcoord[1]*t; - - // Pipeline coordinate interpolation (4 components) - out->coord[0] = a->coord[0]*tInv + b->coord[0]*t; - out->coord[1] = a->coord[1]*tInv + b->coord[1]*t; - out->coord[2] = a->coord[2]*tInv + b->coord[2]*t; - out->coord[3] = a->coord[3]*tInv + b->coord[3]*t; } -static inline void sw_get_vertex_grad_CTH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT a, const sw_vertex_t *SW_RESTRICT b, float scale) +static inline void sw_get_vertex_grad_PCT(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT a, const sw_vertex_t *SW_RESTRICT b, float scale) { + // Calculate gradients for Position + out->position[0] = (b->position[0] - a->position[0])*scale; + out->position[1] = (b->position[1] - a->position[1])*scale; + out->position[2] = (b->position[2] - a->position[2])*scale; + out->position[3] = (b->position[3] - a->position[3])*scale; + // Calculate gradients for Color out->color[0] = (b->color[0] - a->color[0])*scale; out->color[1] = (b->color[1] - a->color[1])*scale; @@ -1259,16 +1258,16 @@ static inline void sw_get_vertex_grad_CTH(sw_vertex_t *SW_RESTRICT out, const sw // Calculate gradients for Texture coordinates out->texcoord[0] = (b->texcoord[0] - a->texcoord[0])*scale; out->texcoord[1] = (b->texcoord[1] - a->texcoord[1])*scale; - - // Calculate gradients for Pipeline coordinates - out->coord[0] = (b->coord[0] - a->coord[0])*scale; - out->coord[1] = (b->coord[1] - a->coord[1])*scale; - out->coord[2] = (b->coord[2] - a->coord[2])*scale; - out->coord[3] = (b->coord[3] - a->coord[3])*scale; } -static inline void sw_add_vertex_grad_CTH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT gradients) +static inline void sw_add_vertex_grad_PCT(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT gradients) { + // Add gradients to Position + out->position[0] += gradients->position[0]; + out->position[1] += gradients->position[1]; + out->position[2] += gradients->position[2]; + out->position[3] += gradients->position[3]; + // Add gradients to Color out->color[0] += gradients->color[0]; out->color[1] += gradients->color[1]; @@ -1278,16 +1277,16 @@ static inline void sw_add_vertex_grad_CTH(sw_vertex_t *SW_RESTRICT out, const sw // Add gradients to Texture coordinates out->texcoord[0] += gradients->texcoord[0]; out->texcoord[1] += gradients->texcoord[1]; - - // Add gradients to Pipeline coordinates - out->coord[0] += gradients->coord[0]; - out->coord[1] += gradients->coord[1]; - out->coord[2] += gradients->coord[2]; - out->coord[3] += gradients->coord[3]; } -static inline void sw_add_vertex_grad_scaled_CTH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT gradients, float scale) +static inline void sw_add_vertex_grad_scaled_PCT(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT gradients, float scale) { + // Add gradients to Position + out->position[0] += gradients->position[0]*scale; + out->position[1] += gradients->position[1]*scale; + out->position[2] += gradients->position[2]*scale; + out->position[3] += gradients->position[3]*scale; + // Add gradients to Color out->color[0] += gradients->color[0]*scale; out->color[1] += gradients->color[1]*scale; @@ -1297,57 +1296,51 @@ static inline void sw_add_vertex_grad_scaled_CTH(sw_vertex_t *SW_RESTRICT out, c // Add gradients to Texture coordinates out->texcoord[0] += gradients->texcoord[0]*scale; out->texcoord[1] += gradients->texcoord[1]*scale; - - // Add gradients to Pipeline coordinates - out->coord[0] += gradients->coord[0]*scale; - out->coord[1] += gradients->coord[1]*scale; - out->coord[2] += gradients->coord[2]*scale; - out->coord[3] += gradients->coord[3]*scale; } -static inline void sw_get_vertex_grad_CH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT a, const sw_vertex_t *SW_RESTRICT b, float scale) +static inline void sw_get_vertex_grad_PC(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT a, const sw_vertex_t *SW_RESTRICT b, float scale) { + // Calculate gradients for Position + out->position[0] = (b->position[0] - a->position[0])*scale; + out->position[1] = (b->position[1] - a->position[1])*scale; + out->position[2] = (b->position[2] - a->position[2])*scale; + out->position[3] = (b->position[3] - a->position[3])*scale; + // Calculate gradients for Color out->color[0] = (b->color[0] - a->color[0])*scale; out->color[1] = (b->color[1] - a->color[1])*scale; out->color[2] = (b->color[2] - a->color[2])*scale; out->color[3] = (b->color[3] - a->color[3])*scale; - - // Calculate gradients for Pipeline coordinates - out->coord[0] = (b->coord[0] - a->coord[0])*scale; - out->coord[1] = (b->coord[1] - a->coord[1])*scale; - out->coord[2] = (b->coord[2] - a->coord[2])*scale; - out->coord[3] = (b->coord[3] - a->coord[3])*scale; } -static inline void sw_add_vertex_grad_CH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT gradients) +static inline void sw_add_vertex_grad_PC(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT gradients) { + // Add gradients to Position + out->position[0] += gradients->position[0]; + out->position[1] += gradients->position[1]; + out->position[2] += gradients->position[2]; + out->position[3] += gradients->position[3]; + // Add gradients to Color out->color[0] += gradients->color[0]; out->color[1] += gradients->color[1]; out->color[2] += gradients->color[2]; out->color[3] += gradients->color[3]; - - // Add gradients to Pipeline coordinates - out->coord[0] += gradients->coord[0]; - out->coord[1] += gradients->coord[1]; - out->coord[2] += gradients->coord[2]; - out->coord[3] += gradients->coord[3]; } -static inline void sw_add_vertex_grad_scaled_CH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT gradients, float scale) +static inline void sw_add_vertex_grad_scaled_PC(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT gradients, float scale) { + // Add gradients to Position + out->position[0] += gradients->position[0]*scale; + out->position[1] += gradients->position[1]*scale; + out->position[2] += gradients->position[2]*scale; + out->position[3] += gradients->position[3]*scale; + // Add gradients to Color out->color[0] += gradients->color[0]*scale; out->color[1] += gradients->color[1]*scale; out->color[2] += gradients->color[2]*scale; out->color[3] += gradients->color[3]*scale; - - // Add gradients to Pipeline coordinates - out->coord[0] += gradients->coord[0]*scale; - out->coord[1] += gradients->coord[1]*scale; - out->coord[2] += gradients->coord[2]*scale; - out->coord[3] += gradients->coord[3]*scale; } // Half conversion functions @@ -2871,17 +2864,17 @@ static int sw_clip_##name( int n) \ { \ const sw_vertex_t *prev = &input[n - 1]; \ - int prevInside = FUNC_IS_INSIDE(prev->coord); \ + int prevInside = FUNC_IS_INSIDE(prev->position); \ int outputCount = 0; \ \ for (int i = 0; i < n; i++) { \ const sw_vertex_t *curr = &input[i]; \ - int currInside = FUNC_IS_INSIDE(curr->coord); \ + int currInside = FUNC_IS_INSIDE(curr->position); \ \ /* If transition between interior/exterior, calculate intersection point */ \ if (prevInside != currInside) { \ - float t = FUNC_COMPUTE_T(prev->coord, curr->coord); \ - sw_lerp_vertex_PCTH(&output[outputCount++], prev, curr, t); \ + float t = FUNC_COMPUTE_T(prev->position, curr->position); \ + sw_lerp_vertex_PCT(&output[outputCount++], prev, curr, t); \ } \ \ /* If current vertex inside, add it */ \ @@ -3320,9 +3313,9 @@ static inline bool sw_triangle_face_culling(void) // even with negative w values // Preload clip coordinates into local variables - const float *h0 = RLSW.primitive.buffer[0].coord; - const float *h1 = RLSW.primitive.buffer[1].coord; - const float *h2 = RLSW.primitive.buffer[2].coord; + const float *p0 = RLSW.primitive.buffer[0].position; + const float *p1 = RLSW.primitive.buffer[1].position; + const float *p2 = RLSW.primitive.buffer[2].position; // Compute a value proportional to the signed area in the projected 2D plane, // calculated directly using clip coordinates BEFORE division by w @@ -3331,28 +3324,28 @@ static inline bool sw_triangle_face_culling(void) // space and its relationship to the projected 2D winding order, even with // negative w values // The determinant formula used here is: - // h0.x*(h1.y*h2.w - h2.y*h1.w) + - // h1.x*(h2.y*h0.w - h0.y*h2.w) + - // h2.x*(h0.y*h1.w - h1.y*h0.w) + // p0.x*(p1.y*p2.w - p2.y*p1.w) + + // p1.x*(p2.y*p0.w - p0.y*p2.w) + + // p2.x*(p0.y*p1.w - p1.y*p0.w) - const float hSgnArea = - h0[0]*(h1[1]*h2[3] - h2[1]*h1[3]) + - h1[0]*(h2[1]*h0[3] - h0[1]*h2[3]) + - h2[0]*(h0[1]*h1[3] - h1[1]*h0[3]); + const float sgnArea = + p0[0]*(p1[1]*p2[3] - p2[1]*p1[3]) + + p1[0]*(p2[1]*p0[3] - p0[1]*p2[3]) + + p2[0]*(p0[1]*p1[3] - p1[1]*p0[3]); // Discard the triangle if its winding order (determined by the sign // of the homogeneous area/determinant) matches the culled direction - // A positive hSgnArea typically corresponds to a counter-clockwise + // A positive sgnArea typically corresponds to a counter-clockwise // winding in the projected space when all w > 0 // This test is robust for points with w > 0 or w < 0, correctly // capturing the change in orientation when crossing the w=0 plane // The culling logic remains the same based on the signed area/determinant - // A value of 0 for hSgnArea means the points are collinear in (x, y, w) + // A value of 0 for sgnArea means the points are collinear in (x, y, w) // space, which corresponds to a degenerate triangle projection // Such triangles are typically not culled by this test (0 < 0 is false, 0 > 0 is false) // and should be handled by the clipper if necessary - return (RLSW.cullFace == SW_FRONT)? (hSgnArea < 0) : (hSgnArea > 0); // Cull if winding is "clockwise" : "counter-clockwise" + return (RLSW.cullFace == SW_FRONT)? (sgnArea < 0) : (sgnArea > 0); // Cull if winding is "clockwise" : "counter-clockwise" } static void sw_triangle_clip_and_project(void) @@ -3369,13 +3362,13 @@ static void sw_triangle_clip_and_project(void) // Calculation of the reciprocal of W for normalization // as well as perspective-correct attributes - const float wRcp = 1.0f/v->coord[3]; - v->coord[3] = wRcp; + const float wRcp = 1.0f/v->position[3]; // Division of XYZ coordinates by weight - v->coord[0] *= wRcp; - v->coord[1] *= wRcp; - v->coord[2] *= wRcp; + v->position[0] *= wRcp; + v->position[1] *= wRcp; + v->position[2] *= wRcp; + v->position[3] = wRcp; // Division of texture coordinates (perspective-correct) v->texcoord[0] *= wRcp; @@ -3388,7 +3381,7 @@ static void sw_triangle_clip_and_project(void) v->color[3] *= wRcp; // Transformation to screen space - sw_project_ndc_to_screen(v->coord); + sw_project_ndc_to_screen(v->position); } } } @@ -3430,12 +3423,12 @@ static inline bool sw_quad_face_culling(void) // winding test on this first triangle // Preload clip coordinates into local variables - const float *h0 = RLSW.primitive.buffer[0].coord; - const float *h1 = RLSW.primitive.buffer[1].coord; - const float *h2 = RLSW.primitive.buffer[2].coord; + const float *p0 = RLSW.primitive.buffer[0].position; + const float *p1 = RLSW.primitive.buffer[1].position; + const float *p2 = RLSW.primitive.buffer[2].position; // NOTE: h3 is not needed for this test - // const float *h3 = RLSW.primitive.buffer[3].coord; + // const float *h3 = RLSW.primitive.buffer[3].position; // Compute a value proportional to the signed area of the triangle P0 P1 P2 // in the projected 2D plane, calculated directly using clip coordinates @@ -3445,29 +3438,29 @@ static inline bool sw_quad_face_culling(void) // in clip space and its relationship to the projected 2D winding order, // even with negative w values // The determinant formula used here is: - // h0.x*(h1.y*h2.w - h2.y*h1.w) + - // h1.x*(h2.y*h0.w - h0.y*h2.w) + - // h2.x*(h0.y*h1.w - h1.y*h0.w) + // p0.x*(p1.y*p2.w - p2.y*p1.w) + + // p1.x*(p2.y*p0.w - p0.y*p2.w) + + // p2.x*(p0.y*p1.w - p1.y*p0.w) - const float hSgnArea = - h0[0]*(h1[1]*h2[3] - h2[1]*h1[3]) + - h1[0]*(h2[1]*h0[3] - h0[1]*h2[3]) + - h2[0]*(h0[1]*h1[3] - h1[1]*h0[3]); + const float sgnArea = + p0[0]*(p1[1]*p2[3] - p2[1]*p1[3]) + + p1[0]*(p2[1]*p0[3] - p0[1]*p2[3]) + + p2[0]*(p0[1]*p1[3] - p1[1]*p0[3]); // Perform face culling based on the winding order determined by the sign // of the homogeneous area/determinant of triangle P0 P1 P2 // This test is robust for points with w > 0 or w < 0 within the triangle, // correctly capturing the change in orientation when crossing the w=0 plane - // A positive hSgnArea typically corresponds to a counter-clockwise + // A positive sgnArea typically corresponds to a counter-clockwise // winding in the projected space when all w > 0 - // A value of 0 for hSgnArea means P0, P1, P2 are collinear in (x, y, w) + // A value of 0 for sgnArea means P0, P1, P2 are collinear in (x, y, w) // space, which corresponds to a degenerate triangle projection // Such quads might also be degenerate or non-planar. They are typically // not culled by this test (0 < 0 is false, 0 > 0 is false) // and should be handled by the clipper if necessary - return (RLSW.cullFace == SW_FRONT)? (hSgnArea < 0.0f) : (hSgnArea > 0.0f); // Cull if winding is "clockwise" : "counter-clockwise" + return (RLSW.cullFace == SW_FRONT)? (sgnArea < 0.0f) : (sgnArea > 0.0f); // Cull if winding is "clockwise" : "counter-clockwise" } static void sw_quad_clip_and_project(void) @@ -3484,13 +3477,13 @@ static void sw_quad_clip_and_project(void) // Calculation of the reciprocal of W for normalization // as well as perspective-correct attributes - const float wRcp = 1.0f/v->coord[3]; - v->coord[3] = wRcp; + const float wRcp = 1.0f/v->position[3]; // Division of XYZ coordinates by weight - v->coord[0] *= wRcp; - v->coord[1] *= wRcp; - v->coord[2] *= wRcp; + v->position[0] *= wRcp; + v->position[1] *= wRcp; + v->position[2] *= wRcp; + v->position[3] = wRcp; // Division of texture coordinates (perspective-correct) v->texcoord[0] *= wRcp; @@ -3503,7 +3496,7 @@ static void sw_quad_clip_and_project(void) v->color[3] *= wRcp; // Transformation to screen space - sw_project_ndc_to_screen(v->coord); + sw_project_ndc_to_screen(v->position); } } } @@ -3515,17 +3508,17 @@ static bool sw_quad_is_axis_aligned(void) // so it's required for all vertices to have homogeneous w = 1.0 for (int i = 0; i < 4; i++) { - if (RLSW.primitive.buffer[i].coord[3] != 1.0f) return false; + if (RLSW.primitive.buffer[i].position[3] != 1.0f) return false; } // Epsilon tolerance in screen space (pixels) const float epsilon = 0.5f; // Fetch screen-space positions for the four quad vertices - const float *p0 = RLSW.primitive.buffer[0].coord; - const float *p1 = RLSW.primitive.buffer[1].coord; - const float *p2 = RLSW.primitive.buffer[2].coord; - const float *p3 = RLSW.primitive.buffer[3].coord; + const float *p0 = RLSW.primitive.buffer[0].position; + const float *p1 = RLSW.primitive.buffer[1].position; + const float *p2 = RLSW.primitive.buffer[2].position; + const float *p3 = RLSW.primitive.buffer[3].position; // Compute edge vectors between consecutive vertices // These define the four sides of the quad in screen space @@ -3614,25 +3607,25 @@ static bool sw_line_clip(sw_vertex_t *v0, sw_vertex_t *v1) for (int i = 0; i < 4; i++) { - dH[i] = v1->coord[i] - v0->coord[i]; + dH[i] = v1->position[i] - v0->position[i]; dC[i] = v1->color[i] - v0->color[i]; } // Clipping Liang-Barsky - if (!sw_line_clip_coord(v0->coord[3] - v0->coord[0], -dH[3] + dH[0], &t0, &t1)) return false; - if (!sw_line_clip_coord(v0->coord[3] + v0->coord[0], -dH[3] - dH[0], &t0, &t1)) return false; - if (!sw_line_clip_coord(v0->coord[3] - v0->coord[1], -dH[3] + dH[1], &t0, &t1)) return false; - if (!sw_line_clip_coord(v0->coord[3] + v0->coord[1], -dH[3] - dH[1], &t0, &t1)) return false; - if (!sw_line_clip_coord(v0->coord[3] - v0->coord[2], -dH[3] + dH[2], &t0, &t1)) return false; - if (!sw_line_clip_coord(v0->coord[3] + v0->coord[2], -dH[3] - dH[2], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->position[3] - v0->position[0], -dH[3] + dH[0], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->position[3] + v0->position[0], -dH[3] - dH[0], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->position[3] - v0->position[1], -dH[3] + dH[1], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->position[3] + v0->position[1], -dH[3] - dH[1], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->position[3] - v0->position[2], -dH[3] + dH[2], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->position[3] + v0->position[2], -dH[3] - dH[2], &t0, &t1)) return false; // Clipping Scissor if (RLSW.userState & SW_STATE_SCISSOR_TEST) { - if (!sw_line_clip_coord(v0->coord[0] - RLSW.scClipMin[0]*v0->coord[3], RLSW.scClipMin[0]*dH[3] - dH[0], &t0, &t1)) return false; - if (!sw_line_clip_coord(RLSW.scClipMax[0]*v0->coord[3] - v0->coord[0], dH[0] - RLSW.scClipMax[0]*dH[3], &t0, &t1)) return false; - if (!sw_line_clip_coord(v0->coord[1] - RLSW.scClipMin[1]*v0->coord[3], RLSW.scClipMin[1]*dH[3] - dH[1], &t0, &t1)) return false; - if (!sw_line_clip_coord(RLSW.scClipMax[1]*v0->coord[3] - v0->coord[1], dH[1] - RLSW.scClipMax[1]*dH[3], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->position[0] - RLSW.scClipMin[0]*v0->position[3], RLSW.scClipMin[0]*dH[3] - dH[0], &t0, &t1)) return false; + if (!sw_line_clip_coord(RLSW.scClipMax[0]*v0->position[3] - v0->position[0], dH[0] - RLSW.scClipMax[0]*dH[3], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->position[1] - RLSW.scClipMin[1]*v0->position[3], RLSW.scClipMin[1]*dH[3] - dH[1], &t0, &t1)) return false; + if (!sw_line_clip_coord(RLSW.scClipMax[1]*v0->position[3] - v0->position[1], dH[1] - RLSW.scClipMax[1]*dH[3], &t0, &t1)) return false; } // Interpolation of new coordinates @@ -3640,7 +3633,7 @@ static bool sw_line_clip(sw_vertex_t *v0, sw_vertex_t *v1) { for (int i = 0; i < 4; i++) { - v1->coord[i] = v0->coord[i] + t1*dH[i]; + v1->position[i] = v0->position[i] + t1*dH[i]; v1->color[i] = v0->color[i] + t1*dC[i]; } } @@ -3649,7 +3642,7 @@ static bool sw_line_clip(sw_vertex_t *v0, sw_vertex_t *v1) { for (int i = 0; i < 4; i++) { - v0->coord[i] += t0*dH[i]; + v0->position[i] += t0*dH[i]; v0->color[i] += t0*dC[i]; } } @@ -3662,24 +3655,24 @@ static bool sw_line_clip_and_project(sw_vertex_t *v0, sw_vertex_t *v1) if (!sw_line_clip(v0, v1)) return false; // Convert clip coordinates to NDC - v0->coord[3] = 1.0f/v0->coord[3]; - v1->coord[3] = 1.0f/v1->coord[3]; + v0->position[3] = 1.0f/v0->position[3]; + v1->position[3] = 1.0f/v1->position[3]; for (int i = 0; i < 3; i++) { - v0->coord[i] *= v0->coord[3]; - v1->coord[i] *= v1->coord[3]; + v0->position[i] *= v0->position[3]; + v1->position[i] *= v1->position[3]; } // Convert NDC coordinates to screen space - sw_project_ndc_to_screen(v0->coord); - sw_project_ndc_to_screen(v1->coord); + sw_project_ndc_to_screen(v0->position); + sw_project_ndc_to_screen(v1->position); // NDC +1.0 projects to exactly (width + 0.5f), which truncates out of bounds // The clamp is at most 0.5px on a boundary endpoint, it's visually imperceptible - v0->coord[0] = sw_clamp(v0->coord[0], 0.0f, (float)(RLSW.colorBuffer->width - 1) + 0.5f); - v0->coord[1] = sw_clamp(v0->coord[1], 0.0f, (float)(RLSW.colorBuffer->height - 1) + 0.5f); - v1->coord[0] = sw_clamp(v1->coord[0], 0.0f, (float)(RLSW.colorBuffer->width - 1) + 0.5f); - v1->coord[1] = sw_clamp(v1->coord[1], 0.0f, (float)(RLSW.colorBuffer->height - 1) + 0.5f); + v0->position[0] = sw_clamp(v0->position[0], 0.0f, (float)(RLSW.colorBuffer->width - 1) + 0.5f); + v0->position[1] = sw_clamp(v0->position[1], 0.0f, (float)(RLSW.colorBuffer->height - 1) + 0.5f); + v1->position[0] = sw_clamp(v1->position[0], 0.0f, (float)(RLSW.colorBuffer->width - 1) + 0.5f); + v1->position[1] = sw_clamp(v1->position[1], 0.0f, (float)(RLSW.colorBuffer->height - 1) + 0.5f); return true; } @@ -3705,20 +3698,20 @@ static void sw_line_render(uint32_t state, sw_vertex_t *vertices) //------------------------------------------------------------------------------------------- static bool sw_point_clip_and_project(sw_vertex_t *v) { - if (v->coord[3] != 1.0f) + if (v->position[3] != 1.0f) { for (int_fast8_t i = 0; i < 3; i++) { - if ((v->coord[i] < -v->coord[3]) || (v->coord[i] > v->coord[3])) return false; + if ((v->position[i] < -v->position[3]) || (v->position[i] > v->position[3])) return false; } - v->coord[3] = 1.0f/v->coord[3]; - v->coord[0] *= v->coord[3]; - v->coord[1] *= v->coord[3]; - v->coord[2] *= v->coord[3]; + v->position[3] = 1.0f/v->position[3]; + v->position[0] *= v->position[3]; + v->position[1] *= v->position[3]; + v->position[2] *= v->position[3]; } - sw_project_ndc_to_screen(v->coord); + sw_project_ndc_to_screen(v->position); int min[2] = { 0, 0 }; int max[2] = { RLSW.colorBuffer->width, RLSW.colorBuffer->height }; @@ -3731,8 +3724,8 @@ static bool sw_point_clip_and_project(sw_vertex_t *v) max[1] = sw_clamp_int(RLSW.scMax[1], 0, RLSW.colorBuffer->height); } - bool insideX = (v->coord[0] - RLSW.pointRadius < max[0]) && (v->coord[0] + RLSW.pointRadius > min[0]); - bool insideY = (v->coord[1] - RLSW.pointRadius < max[1]) && (v->coord[1] + RLSW.pointRadius > min[1]); + bool insideX = (v->position[0] - RLSW.pointRadius < max[0]) && (v->position[0] + RLSW.pointRadius > min[0]); + bool insideY = (v->position[1] - RLSW.pointRadius < max[1]) && (v->position[1] + RLSW.pointRadius > min[1]); return (insideX && insideY); } @@ -3841,18 +3834,19 @@ static void sw_immediate_push_vertex(const float position[4]) return; } - // Copy the attributes in the current vertex + // Gets the current vertex sw_vertex_t *vertex = &RLSW.primitive.buffer[RLSW.primitive.vertexCount++]; - for (int i = 0; i < 4; i++) vertex->position[i] = position[i]; - for (int i = 0; i < 4; i++) vertex->color[i] = RLSW.primitive.color[i]; - for (int i = 0; i < 2; i++) vertex->texcoord[i] = RLSW.primitive.texcoord[i]; // Calculate clip coordinates - const float *m = RLSW.matMVP, *v = vertex->position; - vertex->coord[0] = m[0]*v[0] + m[4]*v[1] + m[8]*v[2] + m[12]*v[3]; - vertex->coord[1] = m[1]*v[0] + m[5]*v[1] + m[9]*v[2] + m[13]*v[3]; - vertex->coord[2] = m[2]*v[0] + m[6]*v[1] + m[10]*v[2] + m[14]*v[3]; - vertex->coord[3] = m[3]*v[0] + m[7]*v[1] + m[11]*v[2] + m[15]*v[3]; + const float *m = RLSW.matMVP; + vertex->position[0] = m[0]*position[0] + m[4]*position[1] + m[8]*position[2] + m[12]*position[3]; + vertex->position[1] = m[1]*position[0] + m[5]*position[1] + m[9]*position[2] + m[13]*position[3]; + vertex->position[2] = m[2]*position[0] + m[6]*position[1] + m[10]*position[2] + m[14]*position[3]; + vertex->position[3] = m[3]*position[0] + m[7]*position[1] + m[11]*position[2] + m[15]*position[3]; + + // Copy the attributes in the current vertex + for (int i = 0; i < 4; i++) vertex->color[i] = RLSW.primitive.color[i]; + for (int i = 0; i < 2; i++) vertex->texcoord[i] = RLSW.primitive.texcoord[i]; // Immediate rendering of the primitive if the required number is reached if (RLSW.primitive.vertexCount == SW_PRIMITIVE_VERTEX_COUNT[RLSW.drawMode]) @@ -4377,7 +4371,7 @@ void swPopMatrix(void) return; } - RLSW.currentMatrix = &RLSW.stackProjection[--RLSW.stackProjectionCounter]; + RLSW.currentMatrix = &RLSW.stackProjection[(--RLSW.stackProjectionCounter) - 1]; RLSW.isDirtyMVP = true; //< The MVP is considered to have been changed } break; case SW_MODELVIEW: @@ -4388,7 +4382,7 @@ void swPopMatrix(void) return; } - RLSW.currentMatrix = &RLSW.stackModelview[--RLSW.stackModelviewCounter]; + RLSW.currentMatrix = &RLSW.stackModelview[(--RLSW.stackModelviewCounter) - 1]; RLSW.isDirtyMVP = true; //< The MVP is considered to have been changed } break; case SW_TEXTURE: @@ -4399,7 +4393,7 @@ void swPopMatrix(void) return; } - RLSW.currentMatrix = &RLSW.stackTexture[--RLSW.stackTextureCounter]; + RLSW.currentMatrix = &RLSW.stackTexture[(--RLSW.stackTextureCounter) - 1]; } break; default: break; } @@ -5257,27 +5251,27 @@ void swGetFramebufferAttachmentParameteriv(SWattachment attachment, SWattachget #define SW_RASTER_TRIANGLE SW_CONCATX(sw_raster_triangle_, RLSW_TEMPLATE_RASTER_TRIANGLE) #ifdef SW_ENABLE_TEXTURE - #define SW_GET_GRAD sw_get_vertex_grad_CTH - #define SW_ADD_GRAD sw_add_vertex_grad_CTH - #define SW_ADD_GRAD_SCALED sw_add_vertex_grad_scaled_CTH + #define SW_GET_GRAD sw_get_vertex_grad_PCT + #define SW_ADD_GRAD sw_add_vertex_grad_PCT + #define SW_ADD_GRAD_SCALED sw_add_vertex_grad_scaled_PCT #else - #define SW_GET_GRAD sw_get_vertex_grad_CH - #define SW_ADD_GRAD sw_add_vertex_grad_CH - #define SW_ADD_GRAD_SCALED sw_add_vertex_grad_scaled_CH + #define SW_GET_GRAD sw_get_vertex_grad_PC + #define SW_ADD_GRAD sw_add_vertex_grad_PC + #define SW_ADD_GRAD_SCALED sw_add_vertex_grad_scaled_PC #endif static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t *end, float dUdy, float dVdy) { // Gets the start/end coordinates and skip empty lines - int xStart = (int)start->coord[0]; - int xEnd = (int)end->coord[0]; + int xStart = (int)start->position[0]; + int xEnd = (int)end->position[0]; if (xStart == xEnd) return; // Compute the inverse horizontal distance along the X axis - float dxRcp = 1.0f/(end->coord[0] - start->coord[0]); + float dxRcp = 1.0f/(end->position[0] - start->position[0]); // Compute the interpolation steps along the X axis - float dWdx = (end->coord[3] - start->coord[3])*dxRcp; + float dWdx = (end->position[3] - start->position[3])*dxRcp; float dCdx[4] = { (end->color[0] - start->color[0])*dxRcp, (end->color[1] - start->color[1])*dxRcp, @@ -5285,7 +5279,7 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t (end->color[3] - start->color[3])*dxRcp }; #ifdef SW_ENABLE_DEPTH_TEST - float dZdx = (end->coord[2] - start->coord[2])*dxRcp; + float dZdx = (end->position[2] - start->position[2])*dxRcp; #endif #ifdef SW_ENABLE_TEXTURE float dUdx = (end->texcoord[0] - start->texcoord[0])*dxRcp; @@ -5293,10 +5287,10 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t #endif // Compute the subpixel distance to traverse before the first pixel - float xSubstep = 1.0f - sw_fract(start->coord[0]); + float xSubstep = 1.0f - sw_fract(start->position[0]); // Initializing the interpolation starting values - float w = start->coord[3] + dWdx*xSubstep; + float w = start->position[3] + dWdx*xSubstep; float color[4] = { start->color[0] + dCdx[0]*xSubstep, start->color[1] + dCdx[1]*xSubstep, @@ -5304,7 +5298,7 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t start->color[3] + dCdx[3]*xSubstep }; #ifdef SW_ENABLE_DEPTH_TEST - float z = start->coord[2] + dZdx*xSubstep; + float z = start->position[2] + dZdx*xSubstep; #endif #ifdef SW_ENABLE_TEXTURE float u = start->texcoord[0] + dUdx*xSubstep; @@ -5312,7 +5306,7 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t #endif // Pre-calculate the starting pointers for the framebuffer row - int y = (int)start->coord[1]; + int y = (int)start->position[1]; int baseOffset = y*RLSW.colorBuffer->width + xStart; uint8_t *cPtr = (uint8_t *)(RLSW.colorBuffer->pixels) + baseOffset*SW_FRAMEBUFFER_COLOR_SIZE; #ifdef SW_ENABLE_DEPTH_TEST @@ -5444,14 +5438,14 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t static void SW_RASTER_TRIANGLE(const sw_vertex_t *v0, const sw_vertex_t *v1, const sw_vertex_t *v2) { // Swap vertices by increasing Y - if (v0->coord[1] > v1->coord[1]) { const sw_vertex_t *tmp = v0; v0 = v1; v1 = tmp; } - if (v1->coord[1] > v2->coord[1]) { const sw_vertex_t *tmp = v1; v1 = v2; v2 = tmp; } - if (v0->coord[1] > v1->coord[1]) { const sw_vertex_t *tmp = v0; v0 = v1; v1 = tmp; } + if (v0->position[1] > v1->position[1]) { const sw_vertex_t *tmp = v0; v0 = v1; v1 = tmp; } + if (v1->position[1] > v2->position[1]) { const sw_vertex_t *tmp = v1; v1 = v2; v2 = tmp; } + if (v0->position[1] > v1->position[1]) { const sw_vertex_t *tmp = v0; v0 = v1; v1 = tmp; } // Extracting coordinates from the sorted vertices - float x0 = v0->coord[0], y0 = v0->coord[1]; - float x1 = v1->coord[0], y1 = v1->coord[1]; - float x2 = v2->coord[0], y2 = v2->coord[1]; + float x0 = v0->position[0], y0 = v0->position[1]; + float x1 = v1->position[0], y1 = v1->position[1]; + float x2 = v2->position[0], y2 = v2->position[1]; // Compute height differences float h02 = y2 - y0; @@ -5488,8 +5482,8 @@ static void SW_RASTER_TRIANGLE(const sw_vertex_t *v0, const sw_vertex_t *v1, con // Scanline for the upper part of the triangle for (int y = yTop; y < yMid; y++) { - lVert.coord[1] = rVert.coord[1] = y; - bool longSideIsLeft = (lVert.coord[0] < rVert.coord[0]); + lVert.position[1] = rVert.position[1] = y; + bool longSideIsLeft = (lVert.position[0] < rVert.position[0]); const sw_vertex_t *a = longSideIsLeft? &lVert : &rVert; const sw_vertex_t *b = longSideIsLeft? &rVert : &lVert; SW_RASTER_TRIANGLE_SPAN(a, b, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); @@ -5504,8 +5498,8 @@ static void SW_RASTER_TRIANGLE(const sw_vertex_t *v0, const sw_vertex_t *v1, con // Scanline for the lower part of the triangle for (int y = yMid; y < yBot; y++) { - lVert.coord[1] = rVert.coord[1] = y; - bool longSideIsLeft = (lVert.coord[0] < rVert.coord[0]); + lVert.position[1] = rVert.position[1] = y; + bool longSideIsLeft = (lVert.position[0] < rVert.position[0]); const sw_vertex_t *a = longSideIsLeft? &lVert : &rVert; const sw_vertex_t *b = longSideIsLeft? &rVert : &lVert; SW_RASTER_TRIANGLE_SPAN(a, b, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); @@ -5545,18 +5539,18 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b, const sw_vertex_t *tl = verts[0], *tr = verts[0], *br = verts[0], *bl = verts[0]; for (int i = 1; i < 4; i++) { - float sum = verts[i]->coord[0] + verts[i]->coord[1]; - float diff = verts[i]->coord[0] - verts[i]->coord[1]; - if (sum < tl->coord[0] + tl->coord[1]) tl = verts[i]; - if (diff > tr->coord[0] - tr->coord[1]) tr = verts[i]; - if (sum > br->coord[0] + br->coord[1]) br = verts[i]; - if (diff < bl->coord[0] - bl->coord[1]) bl = verts[i]; + float sum = verts[i]->position[0] + verts[i]->position[1]; + float diff = verts[i]->position[0] - verts[i]->position[1]; + if (sum < tl->position[0] + tl->position[1]) tl = verts[i]; + if (diff > tr->position[0] - tr->position[1]) tr = verts[i]; + if (sum > br->position[0] + br->position[1]) br = verts[i]; + if (diff < bl->position[0] - bl->position[1]) bl = verts[i]; } - int xMin = (int)tl->coord[0]; - int yMin = (int)tl->coord[1]; - int xMax = (int)br->coord[0]; - int yMax = (int)br->coord[1]; + int xMin = (int)tl->position[0]; + int yMin = (int)tl->position[1]; + int xMax = (int)br->position[0]; + int yMax = (int)br->position[1]; float w = (float)(xMax - xMin); float h = (float)(yMax - yMin); @@ -5566,8 +5560,8 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b, float hRcp = 1.0f/h; // Subpixel corrections - float xSubstep = 1.0f - sw_fract(tl->coord[0]); - float ySubstep = 1.0f - sw_fract(tl->coord[1]); + float xSubstep = 1.0f - sw_fract(tl->position[0]); + float ySubstep = 1.0f - sw_fract(tl->position[1]); // Gradients along X (tl->tr) and Y (tl->bl) float dCdx[4] = { @@ -5584,9 +5578,9 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b, }; #ifdef SW_ENABLE_DEPTH_TEST - float dZdx = (tr->coord[2] - tl->coord[2])*wRcp; - float dZdy = (bl->coord[2] - tl->coord[2])*hRcp; - float zRow = tl->coord[2] + dZdx*xSubstep + dZdy*ySubstep; + float dZdx = (tr->position[2] - tl->position[2])*wRcp; + float dZdy = (bl->position[2] - tl->position[2])*hRcp; + float zRow = tl->position[2] + dZdx*xSubstep + dZdy*ySubstep; #endif #ifdef SW_ENABLE_TEXTURE @@ -5722,10 +5716,10 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b, static void SW_RASTER_LINE(const sw_vertex_t *v0, const sw_vertex_t *v1) { // Convert from pixel-center convention (n+0.5) to pixel-origin convention (n) - float x0 = v0->coord[0] - 0.5f; - float y0 = v0->coord[1] - 0.5f; - float x1 = v1->coord[0] - 0.5f; - float y1 = v1->coord[1] - 0.5f; + float x0 = v0->position[0] - 0.5f; + float y0 = v0->position[1] - 0.5f; + float x1 = v1->position[0] - 0.5f; + float y1 = v1->position[1] - 0.5f; float dx = x1 - x0; float dy = y1 - y0; @@ -5750,7 +5744,7 @@ static void SW_RASTER_LINE(const sw_vertex_t *v0, const sw_vertex_t *v1) float yInc = dy/steps; float stepRcp = 1.0f/steps; #ifdef SW_ENABLE_DEPTH_TEST - float zInc = (v1->coord[2] - v0->coord[2])*stepRcp; + float zInc = (v1->position[2] - v0->position[2])*stepRcp; #endif float rInc = (v1->color[0] - v0->color[0])*stepRcp; float gInc = (v1->color[1] - v0->color[1])*stepRcp; @@ -5761,7 +5755,7 @@ static void SW_RASTER_LINE(const sw_vertex_t *v0, const sw_vertex_t *v1) float x = x0 + xInc*substep; float y = y0 + yInc*substep; #ifdef SW_ENABLE_DEPTH_TEST - float z = v0->coord[2] + zInc*substep; + float z = v0->position[2] + zInc*substep; #endif float r = v0->color[0] + rInc*substep; float g = v0->color[1] + gInc*substep; @@ -5833,10 +5827,10 @@ static void SW_RASTER_LINE_THICK(const sw_vertex_t *v0, const sw_vertex_t *v1) { sw_vertex_t tv0, tv1; - int x0 = (int)v0->coord[0]; - int y0 = (int)v0->coord[1]; - int x1 = (int)v1->coord[0]; - int y1 = (int)v1->coord[1]; + int x0 = (int)v0->position[0]; + int y0 = (int)v0->position[1]; + int x1 = (int)v1->position[0]; + int y1 = (int)v1->position[1]; int dx = x1 - x0; int dy = y1 - y0; @@ -5850,12 +5844,12 @@ static void SW_RASTER_LINE_THICK(const sw_vertex_t *v0, const sw_vertex_t *v1) for (int i = 1; i <= wy; i++) { tv0 = *v0, tv1 = *v1; - tv0.coord[1] -= i; - tv1.coord[1] -= i; + tv0.position[1] -= i; + tv1.position[1] -= i; SW_RASTER_LINE(&tv0, &tv1); tv0 = *v0, tv1 = *v1; - tv0.coord[1] += i; - tv1.coord[1] += i; + tv0.position[1] += i; + tv1.position[1] += i; SW_RASTER_LINE(&tv0, &tv1); } } @@ -5866,12 +5860,12 @@ static void SW_RASTER_LINE_THICK(const sw_vertex_t *v0, const sw_vertex_t *v1) for (int i = 1; i <= wx; i++) { tv0 = *v0, tv1 = *v1; - tv0.coord[0] -= i; - tv1.coord[0] -= i; + tv0.position[0] -= i; + tv1.position[0] -= i; SW_RASTER_LINE(&tv0, &tv1); tv0 = *v0, tv1 = *v1; - tv0.coord[0] += i; - tv1.coord[0] += i; + tv0.position[0] += i; + tv1.position[0] += i; SW_RASTER_LINE(&tv0, &tv1); } } @@ -5927,9 +5921,9 @@ static void SW_RASTER_POINT_PIXEL(int x, int y, float z, const float color[4]) static void SW_RASTER_POINT(const sw_vertex_t *v) { - int cx = v->coord[0]; - int cy = v->coord[1]; - float cz = v->coord[2]; + int cx = v->position[0]; + int cy = v->position[1]; + float cz = v->position[2]; int radius = RLSW.pointRadius; const float *color = v->color; From 1b084ff9f256bea3406255322fce4618e0b250f0 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Sun, 22 Mar 2026 03:06:03 -0500 Subject: [PATCH 202/319] fixed error in readme, found by discord user the.enemy (#5680) --- examples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index 43e2dcc63..d48641528 100644 --- a/examples/README.md +++ b/examples/README.md @@ -223,7 +223,7 @@ Examples using raylib models functionality, including models loading/generation ### category: shaders [35] -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. +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.h) module. | example | image | difficulty
level | version
created | last version
updated | original
developer | |-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| From 22cc2554b121453a9414d5390ab695ff2d6a97d2 Mon Sep 17 00:00:00 2001 From: Jopestpe <47086979+Jopestpe@users.noreply.github.com> Date: Mon, 23 Mar 2026 07:28:28 -0300 Subject: [PATCH 203/319] Fix Memory out of bounds in [shapes] example - ball physics (#5683) Update screenshot to shapes_bouncing_ball.png --- examples/shapes/shapes_ball_physics.c | 6 ++++-- examples/shapes/shapes_bouncing_ball.png | Bin 15523 -> 9564 bytes 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/shapes/shapes_ball_physics.c b/examples/shapes/shapes_ball_physics.c index f01a7c8e1..9aba0cf57 100644 --- a/examples/shapes/shapes_ball_physics.c +++ b/examples/shapes/shapes_ball_physics.c @@ -45,7 +45,8 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [shapes] example - ball physics"); - Ball balls[MAX_BALLS] = {{ + Ball *balls = (Ball*)malloc(sizeof(Ball)*MAX_BALLS); + balls[0] = (Ball){ .pos = { GetScreenWidth()/2.0f, GetScreenHeight()/2.0f }, .vel = { 200, 200 }, .ppos = { 0 }, @@ -54,7 +55,7 @@ int main(void) .elasticity = 0.9f, .color = BLUE, .grabbed = false - }}; + }; int ballCount = 1; Ball *grabbedBall = NULL; // A pointer to the current ball that is grabbed @@ -214,6 +215,7 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- + free(balls); CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/examples/shapes/shapes_bouncing_ball.png b/examples/shapes/shapes_bouncing_ball.png index 9d98e3a889d13b73e276354be935e906f5f63143..a9555b5c7f0df8cb569c7c5ad612b883d4aee3ca 100644 GIT binary patch literal 9564 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 literal 15523 zcmeHOeN+=y7N3L^q67`%N`eIv5mt9uWKF1+Z-FKt1yi8arCOq;5CSR-5q8l^KnQ-s z0xezGxVYjN6bg!hF3_cbph6Lr0RctPvWgo%1go(16FMIRCYpfTdQQ)tGylxtOon;) z{_cIhci->6*(dPl8_hMF3jlzTkGEF<0O+v*fOMX!58pBUxVQlToW(v~Yl8mt(Z|o8 zj14Br-m^C&gKQla36+!&2`r3~AUZ?qg_Mw5BQgl(79W(=h^TTc7khb}JI|z6`;YKH zlo%#K%nZoOMVA@w|DZGON#!=@blOmf*>GB?4TuTtI_!f@xR03-^oyE6TC}CAzN&5i zGG$0zdQwMb;o*$ldW8WeEht{#drM@))QUeGm>P33=v%@Iyrc_apvOa+8gxj=F{~e` zQy#v;G|Kt(SogXj*NwyMu5Tc&vo7SNOftw}fdyN*6rQJ0%SF14ZQo5|`bFtZAQ;+t z4WccJby zw2DdhIKC$DUkft|4{xw+A6RHBdAjdxS~tfTj7Zet4(y^VmMU+N%JWBlda||tj>2zUUONMw%Y>*R@~UQ;ZW5(pFE8AYIg@Ctzffdat`NM%K!K%k&5u!&qC za)HPN{C%`>2~m3U>r%9+w|Pa==w@k(2e{Veb{+rAQ( zj(RjyBkDYoINsHLjgKLRZ_Xdhz2b2$viu3I9U-^JcMO>D-~C@D^ds| zGomGYl!}^A_)_MpwF~W$``IQ)&(Q6w&unki?-j;f7geHy*paQ6+Ejvv0H0E%pCoMp zeQbM1w&Ov&YBRrUa6r#sQ^P=GYT)y7R>=y5T>k~gPneMRL^QUm9()B&N~ex+U^Jhi zq|^vSkJ95@d8OURh%i|o@#+5`vl*Fw;VfRb8?W>l`Wjm-%$qgTBmtG?#TS-6{Jr7= zYHEfjYPW@k-6^SnixUORO^1dYko=ZfkmVuOyPvz6*}jnbRve1|z-p-eHfWv>y@@^i zTTFJmh(}NU9sS5V{LRR?N_43ESc*NtM-t91X&O+$Spw#M26=Ijk=>(EifI2<<7G0S zlg_%rWWrQw^EZ@`Vz7+grr`$Oan&L+xDyuHHM!ehNGsyC zpdkH(TLdpjlK`t0ldGz#f=-u|mov7_D{l5WcYtxp>g%+_!^f5i3;O&6>crd$b?xJg zHH^4!_yrQr9d|TfUYqq8wKHp#fVM@AbS}BHYRrU=8BGYpc?2(>we%yM zeX_D|mi}=xrVgrC7sPue;ls*hEf~HMe3Pr-O`Qh8D^@yYbhyrEX2nfgm8h<~;PsN& z>JwXib=gGZ0+9C+vI}+?j zup_~a1Un*3NT5KVKzyozepE$#utD@A5JH5zStazy_P(ZUpO~zZC+#*okM80x6Y%Pc z98!e+$ycMTK5#i3bJ_iZDLe7}4F|zvaY_Eeq_yi*->(rT)#J73pnd(pt_w4_d;#h_Q-jZ zl9`T8*n*qk56q6G0L5W@qbo?CQkXrvw{(@YU+)16#GxrtyY7!xc|6Or_>ErQE$FWY zpaq{ywu@!!v6DM0ZHozV4?ejAG;&zkKF&slcTIx(t;#yH1>S9uUb#0i6B5!^RV+_$ zJ3CxHWF2=mxlJP7HW581KBwB-?>||?#w-WdMx&A1fc4)gn2e$wOy%P*H%@#WIU+E; z1-*57s=o(Jb}`v%uwUv%OAJYce@{+r>MQ37=Nv5i+;M?#QNn2E>XUMC`x{=YfUrG3byWEG~1@c@>uEhMh;I2Tt;`F%X zR?q3CCT$pN2sp#Qz>{#|QL^K#6@$Dg)3KJ(tB_gRyR>Mm_8Fw1*~ zn{wRRA;kE@C6JQ^^%`I7nAdrenviN*!D%eppD6w^t^Q)2>fHuWWQ01VVOUIW%oNkx zU8h1O(q%vBvJW=0)e1W#gq~D-!Tc#7{x8q4P0y}ZI)VWJ-pTyEp@-}u;E#_1pLhJd JPI?HFe*~UJXl(!h From 83e35ba1706cfb2f4d404ee87be7998adbfa7842 Mon Sep 17 00:00:00 2001 From: Levent Kaya <42411502+lvntky@users.noreply.github.com> Date: Mon, 23 Mar 2026 13:30:28 +0300 Subject: [PATCH 204/319] [rcore] refactor possible data loss on FileMove() (#5682) --- src/rcore.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 5cdc8aca1..c83a1c4ae 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2274,14 +2274,16 @@ int FileCopy(const char *srcPath, const char *dstPath) // NOTE: If dst directories do not exists they are created int FileMove(const char *srcPath, const char *dstPath) { - int result = 0; + int result = -1; if (FileExists(srcPath)) { - FileCopy(srcPath, dstPath); - FileRemove(srcPath); + if (FileCopy(srcPath, dstPath) == 0) + result = FileRemove(srcPath); + else + TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to copy file to [%s]", srcPath, dstPath); } - else result = -1; + else TRACELOG(LOG_WARNING, "FILEIO: [%s] Source file does not exist", srcPath); return result; } From 1e74aba7c37095a337d4ba3881459ae3609db530 Mon Sep 17 00:00:00 2001 From: Krzysztof Szenk Date: Mon, 23 Mar 2026 11:31:06 +0100 Subject: [PATCH 205/319] WebRGFW: remapping mouse/touch position to canvas pixel coordinates (#5679) * RGFW also requires RGBA8 images as window icons, as raylib already reports in raylib.h * LibraryConfigurations.cmake: exchanged MATCHES -> STREQUAL in platform choosing if-statements * WebRGFW: remapping mouse/touch position to canvas pixel coordinates * fix 'typo' * has to be done like that because of += in case of captured mouse --- src/platforms/rcore_desktop_rgfw.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 87ad7d7c4..7817c7874 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -1447,15 +1447,37 @@ void PollInputEvents(void) } break; case RGFW_mousePosChanged: { + float event_x = 0.0f, event_y = 0.0f; if (RGFW_window_isCaptured(platform.window)) { - CORE.Input.Mouse.currentPosition.x += (float)rgfw_event.mouse.vecX; - CORE.Input.Mouse.currentPosition.y += (float)rgfw_event.mouse.vecY; + event_x = (float)rgfw_event.mouse.vecX; + event_y = (float)rgfw_event.mouse.vecY; } else { - CORE.Input.Mouse.currentPosition.x = (float)rgfw_event.mouse.x; - CORE.Input.Mouse.currentPosition.y = (float)rgfw_event.mouse.y; + event_x = (float)rgfw_event.mouse.x; + event_y = (float)rgfw_event.mouse.y; + } + +#if defined(__EMSCRIPTEN__) + { + double canvasWidth = 0.0; + double canvasHeight = 0.0; + emscripten_get_element_css_size("#canvas", &canvasWidth, &canvasHeight); + event_x *= ((float)GetScreenWidth() / (float)canvasWidth); + event_y *= ((float)GetScreenHeight() / (float)canvasHeight); + } +#endif + + if (RGFW_window_isCaptured(platform.window)) + { + CORE.Input.Mouse.currentPosition.x += event_x; + CORE.Input.Mouse.currentPosition.y += event_y; + } + else + { + CORE.Input.Mouse.currentPosition.x = event_x; + CORE.Input.Mouse.currentPosition.y = event_y; } CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; From 644e4adbe2026aa38bbf85e760d351806385d883 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 23 Mar 2026 11:51:37 +0100 Subject: [PATCH 206/319] Update rcore.c --- src/rcore.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index c83a1c4ae..894e00832 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2278,10 +2278,8 @@ int FileMove(const char *srcPath, const char *dstPath) if (FileExists(srcPath)) { - if (FileCopy(srcPath, dstPath) == 0) - result = FileRemove(srcPath); - else - TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to copy file to [%s]", srcPath, dstPath); + if (FileCopy(srcPath, dstPath) == 0) result = FileRemove(srcPath); + else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to copy file to [%s]", srcPath, dstPath); } else TRACELOG(LOG_WARNING, "FILEIO: [%s] Source file does not exist", srcPath); From c1dbfc87f315e5525cb853afb87b301e9bf34743 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 23 Mar 2026 11:52:01 +0100 Subject: [PATCH 207/319] REVIEWED: PR #5679 --- src/platforms/rcore_desktop_rgfw.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 7817c7874..fb1fc6cf6 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -1447,28 +1447,26 @@ void PollInputEvents(void) } break; case RGFW_mousePosChanged: { - float event_x = 0.0f, event_y = 0.0f; + float mouseX = 0.0f; + float mouseY = 0.0f; if (RGFW_window_isCaptured(platform.window)) { - event_x = (float)rgfw_event.mouse.vecX; - event_y = (float)rgfw_event.mouse.vecY; + mouseX = (float)rgfw_event.mouse.vecX; + mouseY = (float)rgfw_event.mouse.vecY; } else { - event_x = (float)rgfw_event.mouse.x; - event_y = (float)rgfw_event.mouse.y; + mouseX = (float)rgfw_event.mouse.x; + mouseY = (float)rgfw_event.mouse.y; } #if defined(__EMSCRIPTEN__) - { - double canvasWidth = 0.0; - double canvasHeight = 0.0; - emscripten_get_element_css_size("#canvas", &canvasWidth, &canvasHeight); - event_x *= ((float)GetScreenWidth() / (float)canvasWidth); - event_y *= ((float)GetScreenHeight() / (float)canvasHeight); - } + double canvasWidth = 0.0; + double canvasHeight = 0.0; + emscripten_get_element_css_size("#canvas", &canvasWidth, &canvasHeight); + mouseX *= ((float)GetScreenWidth()/(float)canvasWidth); + mouseY *= ((float)GetScreenHeight()/(float)canvasHeight); #endif - if (RGFW_window_isCaptured(platform.window)) { CORE.Input.Mouse.currentPosition.x += event_x; From 7e8aca00b69ce416e29dfd0de8b8204811473f00 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 23 Mar 2026 11:53:31 +0100 Subject: [PATCH 208/319] Update textures_raw_data.c --- examples/textures/textures_raw_data.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/examples/textures/textures_raw_data.c b/examples/textures/textures_raw_data.c index c38983b53..49efc890e 100644 --- a/examples/textures/textures_raw_data.c +++ b/examples/textures/textures_raw_data.c @@ -17,8 +17,6 @@ #include "raylib.h" -#include // Required for: malloc() and free() - //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -39,26 +37,31 @@ int main(void) UnloadImage(fudesumiRaw); // Unload CPU (RAM) image data // Generate a checked texture by code - int width = 960; - int height = 480; + int imWidth = 960; + int imHeight = 480; // Dynamic memory allocation to store pixels data (Color type) - Color *pixels = (Color *)malloc(width*height*sizeof(Color)); + // WARNING: Using raylib provided MemAlloc() that uses default raylib + // internal memory allocator, so this data can be freed using UnloadImage() + // that also uses raylib internal memory de-allocator + Color *pixels = (Color *)MemAlloc(imWidth*imHeight*sizeof(Color)); - for (int y = 0; y < height; y++) + for (int y = 0; y < imHeight; y++) { - for (int x = 0; x < width; x++) + for (int x = 0; x < imWidth; x++) { - if (((x/32+y/32)/1)%2 == 0) pixels[y*width + x] = ORANGE; - else pixels[y*width + x] = GOLD; + if (((x/32+y/32)/1)%2 == 0) pixels[y*imWidth + x] = ORANGE; + else pixels[y*imWidth + x] = GOLD; } } // Load pixels data into an image structure and create texture + // NOTE: We can assign pixels directly to data because Color is R8G8B8A8 + // data structure defining that pixelformat, format must be set properly Image checkedIm = { - .data = pixels, // We can assign pixels directly to data - .width = width, - .height = height, + .data = pixels, + .width = imWidth, + .height = imHeight, .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, .mipmaps = 1 }; From ca1baca7c2847e6f19fa774f02cf3fec6a0b4211 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 23 Mar 2026 11:53:57 +0100 Subject: [PATCH 209/319] REVIEWED: examples memory allocators, using raylib provided macros --- examples/core/core_random_sequence.c | 2 +- examples/shapes/shapes_ball_physics.c | 94 +++++++++++++------------ examples/text/text_codepoints_loading.c | 4 +- examples/textures/textures_bunnymark.c | 4 +- examples/textures/textures_fog_of_war.c | 8 +-- 5 files changed, 59 insertions(+), 53 deletions(-) diff --git a/examples/core/core_random_sequence.c b/examples/core/core_random_sequence.c index 19d44461e..32ec3001b 100644 --- a/examples/core/core_random_sequence.c +++ b/examples/core/core_random_sequence.c @@ -111,7 +111,7 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - free(rectangles); + RL_FREE(rectangles); CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/examples/shapes/shapes_ball_physics.c b/examples/shapes/shapes_ball_physics.c index 9aba0cf57..9c18ab66e 100644 --- a/examples/shapes/shapes_ball_physics.c +++ b/examples/shapes/shapes_ball_physics.c @@ -17,15 +17,19 @@ #include "raylib.h" -#include -#include +#include // Required for: malloc(), free() +#include // Required for: hypot() -#define MAX_BALLS 5000 // Maximum quantity of balls +#define MAX_BALLS 5000 // Maximum quantity of balls +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +// Ball data type typedef struct Ball { - Vector2 pos; // Position - Vector2 vel; // Velocity - Vector2 ppos; // Previous position + Vector2 position; + Vector2 speed; + Vector2 prevPosition; float radius; float friction; float elasticity; @@ -45,11 +49,13 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [shapes] example - ball physics"); - Ball *balls = (Ball*)malloc(sizeof(Ball)*MAX_BALLS); + Ball *balls = (Ball*)RL_MALLOC(sizeof(Ball)*MAX_BALLS); + + // Init first ball in the array balls[0] = (Ball){ - .pos = { GetScreenWidth()/2.0f, GetScreenHeight()/2.0f }, - .vel = { 200, 200 }, - .ppos = { 0 }, + .position = { GetScreenWidth()/2.0f, GetScreenHeight()/2.0f }, + .speed = { 200, 200 }, + .prevPosition = { 0 }, .radius = 40, .friction = 0.99f, .elasticity = 0.9f, @@ -58,12 +64,12 @@ int main(void) }; int ballCount = 1; - Ball *grabbedBall = NULL; // A pointer to the current ball that is grabbed - Vector2 pressOffset = { 0 }; // Mouse press offset relative to the ball that grabbedd + Ball *grabbedBall = NULL; // A pointer to the current ball that is grabbed + Vector2 pressOffset = { 0 }; // Mouse press offset relative to the ball that grabbedd - float gravity = 100; // World gravity + float gravity = 100; // World gravity - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //--------------------------------------------------------------------------------------- // Main game loop @@ -80,8 +86,8 @@ int main(void) for (int i = ballCount - 1; i >= 0; i--) { Ball *ball = &balls[i]; - pressOffset.x = mousePos.x - ball->pos.x; - pressOffset.y = mousePos.y - ball->pos.y; + pressOffset.x = mousePos.x - ball->position.x; + pressOffset.y = mousePos.y - ball->position.y; // If the distance between the ball position and the mouse press position // is less than or equal to the ball radius, the event occurred inside the ball @@ -110,9 +116,9 @@ int main(void) if (ballCount < MAX_BALLS) { balls[ballCount++] = (Ball){ - .pos = mousePos, - .vel = { (float)GetRandomValue(-300, 300), (float)GetRandomValue(-300, 300) }, - .ppos = { 0 }, + .position = mousePos, + .speed = { (float)GetRandomValue(-300, 300), (float)GetRandomValue(-300, 300) }, + .prevPosition = { 0 }, .radius = 20.0f + (float)GetRandomValue(0, 30), .friction = 0.99f, .elasticity = 0.9f, @@ -127,7 +133,7 @@ int main(void) { for (int i = 0; i < ballCount; i++) { - if (!balls[i].grabbed) balls[i].vel = (Vector2){ (float)GetRandomValue(-2000, 2000), (float)GetRandomValue(-2000, 2000) }; + if (!balls[i].grabbed) balls[i].speed = (Vector2){ (float)GetRandomValue(-2000, 2000), (float)GetRandomValue(-2000, 2000) }; } } @@ -143,49 +149,49 @@ int main(void) if (!ball->grabbed) { // Ball repositioning using the velocity - ball->pos.x += ball->vel.x * delta; - ball->pos.y += ball->vel.y * delta; + ball->position.x += ball->speed.x * delta; + ball->position.y += ball->speed.y * delta; // Does the ball hit the screen right boundary? - if ((ball->pos.x + ball->radius) >= screenWidth) + if ((ball->position.x + ball->radius) >= screenWidth) { - ball->pos.x = screenWidth - ball->radius; // Ball repositioning - ball->vel.x = -ball->vel.x*ball->elasticity; // Elasticity makes the ball lose 10% of its velocity on hit + ball->position.x = screenWidth - ball->radius; // Ball repositioning + ball->speed.x = -ball->speed.x*ball->elasticity; // Elasticity makes the ball lose 10% of its velocity on hit } // Does the ball hit the screen left boundary? - else if ((ball->pos.x - ball->radius) <= 0) + else if ((ball->position.x - ball->radius) <= 0) { - ball->pos.x = ball->radius; - ball->vel.x = -ball->vel.x*ball->elasticity; + ball->position.x = ball->radius; + ball->speed.x = -ball->speed.x*ball->elasticity; } // The same for y axis - if ((ball->pos.y + ball->radius) >= screenHeight) + if ((ball->position.y + ball->radius) >= screenHeight) { - ball->pos.y = screenHeight - ball->radius; - ball->vel.y = -ball->vel.y*ball->elasticity; + ball->position.y = screenHeight - ball->radius; + ball->speed.y = -ball->speed.y*ball->elasticity; } - else if ((ball->pos.y - ball->radius) <= 0) + else if ((ball->position.y - ball->radius) <= 0) { - ball->pos.y = ball->radius; - ball->vel.y = -ball->vel.y*ball->elasticity; + ball->position.y = ball->radius; + ball->speed.y = -ball->speed.y*ball->elasticity; } // Friction makes the ball lose 1% of its velocity each frame - ball->vel.x = ball->vel.x*ball->friction; + ball->speed.x = ball->speed.x*ball->friction; // Gravity affects only the y axis - ball->vel.y = ball->vel.y*ball->friction + gravity; + ball->speed.y = ball->speed.y*ball->friction + gravity; } else { // Ball repositioning using the mouse position - ball->pos.x = mousePos.x - pressOffset.x; - ball->pos.y = mousePos.y - pressOffset.y; + ball->position.x = mousePos.x - pressOffset.x; + ball->position.y = mousePos.y - pressOffset.y; // While the ball is grabbed, recalculates its velocity - ball->vel.x = (ball->pos.x - ball->ppos.x)/delta; - ball->vel.y = (ball->pos.y - ball->ppos.y)/delta; - ball->ppos = ball->pos; + ball->speed.x = (ball->position.x - ball->prevPosition.x)/delta; + ball->speed.y = (ball->position.y - ball->prevPosition.y)/delta; + ball->prevPosition = ball->position; } } //---------------------------------------------------------------------------------- @@ -198,8 +204,8 @@ int main(void) for (int i = 0; i < ballCount; i++) { - DrawCircleV(balls[i].pos, balls[i].radius, balls[i].color); - DrawCircleLinesV(balls[i].pos, balls[i].radius, BLACK); + DrawCircleV(balls[i].position, balls[i].radius, balls[i].color); + DrawCircleLinesV(balls[i].position, balls[i].radius, BLACK); } DrawText("grab a ball by pressing with the mouse and throw it by releasing", 10, 10, 10, DARKGRAY); @@ -215,7 +221,7 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - free(balls); + RL_FREE(balls); CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/examples/text/text_codepoints_loading.c b/examples/text/text_codepoints_loading.c index 1cd82d60e..1da8cf93f 100644 --- a/examples/text/text_codepoints_loading.c +++ b/examples/text/text_codepoints_loading.c @@ -61,7 +61,7 @@ int main(void) SetTextLineSpacing(20); // Set line spacing for multiline text (when line breaks are included '\n') // Free codepoints, atlas has already been generated - free(codepointsNoDups); + RL_FREE(codepointsNoDups); bool showFontAtlas = false; @@ -139,7 +139,7 @@ int main(void) static int *CodepointRemoveDuplicates(int *codepoints, int codepointCount, int *codepointsResultCount) { int codepointsNoDupsCount = codepointCount; - int *codepointsNoDups = (int *)calloc(codepointCount, sizeof(int)); + int *codepointsNoDups = (int *)RL_CALLOC(codepointCount, sizeof(int)); memcpy(codepointsNoDups, codepoints, codepointCount*sizeof(int)); // Remove duplicates diff --git a/examples/textures/textures_bunnymark.c b/examples/textures/textures_bunnymark.c index a04741227..64e55db43 100644 --- a/examples/textures/textures_bunnymark.c +++ b/examples/textures/textures_bunnymark.c @@ -47,7 +47,7 @@ int main(void) // Load bunny texture Texture2D texBunny = LoadTexture("resources/raybunny.png"); - Bunny *bunnies = (Bunny *)malloc(MAX_BUNNIES*sizeof(Bunny)); // Bunnies array + Bunny *bunnies = (Bunny *)RL_MALLOC(MAX_BUNNIES*sizeof(Bunny)); // Bunnies array int bunniesCount = 0; // Bunnies counter @@ -126,7 +126,7 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - free(bunnies); // Unload bunnies data array + RL_FREE(bunnies); // Unload bunnies data array UnloadTexture(texBunny); // Unload bunny texture diff --git a/examples/textures/textures_fog_of_war.c b/examples/textures/textures_fog_of_war.c index 1354d7124..e4ed2b60a 100644 --- a/examples/textures/textures_fog_of_war.c +++ b/examples/textures/textures_fog_of_war.c @@ -51,8 +51,8 @@ int main(void) // NOTE: We can have up to 256 values for tile ids and for tile fog state, // probably we don't need that many values for fog state, it can be optimized // to use only 2 bits per fog state (reducing size by 4) but logic will be a bit more complex - map.tileIds = (unsigned char *)calloc(map.tilesX*map.tilesY, sizeof(unsigned char)); - map.tileFog = (unsigned char *)calloc(map.tilesX*map.tilesY, sizeof(unsigned char)); + map.tileIds = (unsigned char *)RL_CALLOC(map.tilesX*map.tilesY, sizeof(unsigned char)); + map.tileFog = (unsigned char *)RL_CALLOC(map.tilesX*map.tilesY, sizeof(unsigned char)); // Load map tiles (generating 2 random tile ids for testing) // NOTE: Map tile ids should be probably loaded from an external map file @@ -149,8 +149,8 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - free(map.tileIds); // Free allocated map tile ids - free(map.tileFog); // Free allocated map tile fog state + RL_FREE(map.tileIds); // Free allocated map tile ids + RL_FREE(map.tileFog); // Free allocated map tile fog state UnloadRenderTexture(fogOfWar); // Unload render texture From e71633a0be295c91ff8a039e67deee6fef590237 Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Mon, 23 Mar 2026 13:05:23 +0000 Subject: [PATCH 210/319] Set textureWrapRepeat to avoid issue on web version (#5684) --- examples/models/models_yaw_pitch_roll.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/models/models_yaw_pitch_roll.c b/examples/models/models_yaw_pitch_roll.c index c19aafdcd..af0f06daf 100644 --- a/examples/models/models_yaw_pitch_roll.c +++ b/examples/models/models_yaw_pitch_roll.c @@ -41,6 +41,9 @@ int main(void) Model model = LoadModel("resources/models/obj/plane.obj"); // Load model Texture2D texture = LoadTexture("resources/models/obj/plane_diffuse.png"); // Load model texture + + SetTextureWrap(texture, TEXTURE_WRAP_REPEAT); // Force Repeat to avoid issue on Web version + model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture; // Set map diffuse texture float pitch = 0.0f; From 6cf71f565ca987ce465adfd7c01d3fa94250f3b3 Mon Sep 17 00:00:00 2001 From: Charlie Tonneslan Date: Mon, 23 Mar 2026 12:17:50 -0400 Subject: [PATCH 211/319] Fix typo: dependant -> dependent in rlgl.h and rexm.c (#5685) 5 instances in src/rlgl.h comments and 1 in tools/rexm/rexm.c. Skipped vendored raygui.h files in examples/. --- src/rlgl.h | 10 +++++----- tools/rexm/rexm.c | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 13c037132..ce10b2f64 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -880,7 +880,7 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #elif defined(GRAPHICS_API_OPENGL_ES2) // NOTE: OpenGL ES 2.0 can be enabled on Desktop platforms, // in that case, functions are loaded from a custom glad for OpenGL ES 2.0 - // TODO: OpenGL ES 2.0 support shouldn't be platform-dependant, neither require GLAD + // TODO: OpenGL ES 2.0 support shouldn't be platform-dependent, neither require GLAD #if defined(PLATFORM_DESKTOP_GLFW) || defined(PLATFORM_DESKTOP_SDL) #define GLAD_GLES2_IMPLEMENTATION #include "external/glad_gles2.h" @@ -893,7 +893,7 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad // It seems OpenGL ES 2.0 instancing entry points are not defined on Raspberry Pi // provided headers (despite being defined in official Khronos GLES2 headers) - // TODO: Avoid raylib platform-dependant code on rlgl, it should be a completely portable library + // TODO: Avoid raylib platform-dependent code on rlgl, it should be a completely portable library #if defined(PLATFORM_DRM) typedef void (GL_APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount); typedef void (GL_APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); @@ -1392,7 +1392,7 @@ void rlFrustum(double left, double right, double bottom, double top, double znea void rlOrtho(double left, double right, double bottom, double top, double znear, double zfar) { // NOTE: If left-right and top-botton values are equal it could create a division by zero, - // response to it is platform/compiler dependant + // response to it is platform/compiler dependent Matrix matOrtho = { 0 }; float rl = (float)(right - left); @@ -1509,7 +1509,7 @@ void rlBegin(int mode) // Finish vertex providing void rlEnd(void) { - // NOTE: Depth increment is dependant on rlOrtho(): z-near and z-far values, + // NOTE: Depth increment is dependent on rlOrtho(): z-near and z-far values, // as well as depth buffer bit-depth (16bit or 24bit or 32bit) // Correct increment formula would be: depthInc = (zfar - znear)/pow(2, bits) RLGL.currentBatch->currentDepth += (1.0f/20000.0f); @@ -1902,7 +1902,7 @@ void rlBindFramebuffer(unsigned int target, unsigned int framebuffer) void rlActiveDrawBuffers(int count) { #if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3)) - // NOTE: Maximum number of draw buffers supported is implementation dependant, + // NOTE: Maximum number of draw buffers supported is implementation dependent, // it can be queried with glGet*() but it must be at least 8 //GLint maxDrawBuffers = 0; //glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers); diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 49d4dce9a..e4881b633 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -1669,7 +1669,7 @@ int main(int argc, char *argv[]) FileRemove(TextFormat("%s/%s/%s.original.c", exBasePath, exCategory, exName)); // STEP 3: Run example with required arguments - // NOTE: Not easy to retrieve process return value from system(), it's platform dependant + // NOTE: Not easy to retrieve process return value from system(), it's platform dependent ChangeDirectory(TextFormat("%s/%s", exBasePath, exCategory)); #if defined(_WIN32) From 0f1e14a600df058d807ed5e26cddf2529674073d Mon Sep 17 00:00:00 2001 From: Charlie Tonneslan Date: Mon, 23 Mar 2026 12:58:45 -0400 Subject: [PATCH 212/319] Fix typo: dependant -> dependent in rlgl.h and rexm.c (#5685) 5 instances in src/rlgl.h comments and 1 in tools/rexm/rexm.c. Skipped vendored raygui.h files in examples/. From 52d2158f498dabee0f866a62bee88efd2d9fb77d Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 23 Mar 2026 18:28:09 +0100 Subject: [PATCH 213/319] Update ROADMAP.md --- ROADMAP.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ROADMAP.md b/ROADMAP.md index 10754e52b..91d2299c3 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -23,6 +23,7 @@ _Current version of raylib is complete and functional but there is always room f - [ ] `rlsw`: Software renderer optimizations: mipmaps, platform-specific SIMD - [ ] `rtextures`: Consider moving N-patch system to separate example - [ ] `rtextures`: Review blending modes system, provide more options or better samples + - [ ] `rtext`: Investigate the recently opened [`Slug`](https://sluglibrary.com/) font rendering algorithm - [ ] `raudio`: Support microphone input, basic API to read microphone - [ ] `rltexgpu`: Improve compressed textures support, loading and saving, improve KTX 2.0 - [ ] `rlobj`: Create OBJ loader, supporting material file separately (low priority) From cb05ef7f03673da0b54f0ef49b9ba4a0bbebf37b Mon Sep 17 00:00:00 2001 From: Krzysztof Szenk Date: Mon, 23 Mar 2026 22:22:21 +0100 Subject: [PATCH 214/319] [rcore_desktop_rgfw] [emscripten] fix typo (#5687) * RGFW also requires RGBA8 images as window icons, as raylib already reports in raylib.h * LibraryConfigurations.cmake: exchanged MATCHES -> STREQUAL in platform choosing if-statements * WebRGFW: remapping mouse/touch position to canvas pixel coordinates * fix 'typo' * has to be done like that because of += in case of captured mouse * [rcore_desktop_rgfw] [emscripten] fix typo --- src/platforms/rcore_desktop_rgfw.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index fb1fc6cf6..51a73e1ad 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -1469,13 +1469,13 @@ void PollInputEvents(void) #endif if (RGFW_window_isCaptured(platform.window)) { - CORE.Input.Mouse.currentPosition.x += event_x; - CORE.Input.Mouse.currentPosition.y += event_y; + CORE.Input.Mouse.currentPosition.x += mouseX; + CORE.Input.Mouse.currentPosition.y += mouseY; } else { - CORE.Input.Mouse.currentPosition.x = event_x; - CORE.Input.Mouse.currentPosition.y = event_y; + CORE.Input.Mouse.currentPosition.x = mouseX; + CORE.Input.Mouse.currentPosition.y = mouseY; } CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; From 5dd4036ed0065e8e89a1ee84d89d3a18bb7a3d59 Mon Sep 17 00:00:00 2001 From: Jordi Santonja <77529699+JordSant@users.noreply.github.com> Date: Tue, 24 Mar 2026 08:20:37 +0100 Subject: [PATCH 215/319] Fix UI Cellular Automata (#5688) --- examples/textures/textures_cellular_automata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/textures/textures_cellular_automata.c b/examples/textures/textures_cellular_automata.c index 802de3611..affeeda93 100644 --- a/examples/textures/textures_cellular_automata.c +++ b/examples/textures/textures_cellular_automata.c @@ -165,7 +165,7 @@ int main(void) // If the mouse is on this preset, highlight it if (mouseInCell == i + 8) - DrawRectangleLinesEx((Rectangle) { 2 + (presetsSizeX + 2.0f)*((float)i/2), + DrawRectangleLinesEx((Rectangle) { 2 + (presetsSizeX + 2.0f)*(i/2), (presetsSizeY + 2.0f)*(i%2), presetsSizeX + 4.0f, presetsSizeY + 4.0f }, 3, RED); } From 0f0983c06565b3a7ec7c2248a8361037cf3e4d24 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 25 Mar 2026 16:51:02 +0100 Subject: [PATCH 216/319] Remove trailing spaces --- examples/audio/audio_raw_stream.c | 6 +- examples/core/core_highdpi_testbed.c | 2 +- examples/core/core_keyboard_testbed.c | 34 +++++------ .../models/models_animation_blend_custom.c | 42 +++++++------- examples/models/models_animation_blending.c | 28 ++++----- examples/models/models_animation_timing.c | 10 ++-- examples/models/models_basic_voxel.c | 10 ++-- examples/models/models_skybox_rendering.c | 2 +- examples/models/models_yaw_pitch_roll.c | 2 +- examples/shaders/shaders_cel_shading.c | 16 ++--- examples/shaders/shaders_deferred_rendering.c | 4 +- examples/shaders/shaders_game_of_life.c | 18 +++--- examples/shapes/shapes_ball_physics.c | 12 ++-- examples/shapes/shapes_easings_testbed.c | 2 +- examples/shapes/shapes_hilbert_curve.c | 30 +++++----- examples/shapes/shapes_penrose_tile.c | 30 +++++----- examples/text/text_strings_management.c | 28 ++++----- examples/textures/textures_clipboard_image.c | 12 ++-- examples/textures/textures_magnifying_glass.c | 12 ++-- examples/textures/textures_screen_buffer.c | 8 +-- src/external/rlsw.h | 58 +++++++++---------- src/rcore.c | 2 +- src/rtext.c | 1 - 23 files changed, 184 insertions(+), 185 deletions(-) diff --git a/examples/audio/audio_raw_stream.c b/examples/audio/audio_raw_stream.c index 64c9b5a50..1978511ed 100644 --- a/examples/audio/audio_raw_stream.c +++ b/examples/audio/audio_raw_stream.c @@ -110,16 +110,16 @@ int main(void) //---------------------------------------------------------------------------------- BeginDrawing(); ClearBackground(RAYWHITE); - + DrawText(TextFormat("sine frequency: %i", sineFrequency), screenWidth - 220, 10, 20, RED); DrawText(TextFormat("pan: %.2f", pan), screenWidth - 220, 30, 20, RED); DrawText("Up/down to change frequency", 10, 10, 20, DARKGRAY); DrawText("Left/right to pan", 10, 30, 20, DARKGRAY); - + int windowStart = (GetTime() - sineStartTime)*SAMPLE_RATE; int windowSize = 0.1f*SAMPLE_RATE; int wavelength = SAMPLE_RATE/sineFrequency; - + // Draw a sine wave with the same frequency as the one being sent to the audio stream for (int i = 0; i < screenWidth; i++) { diff --git a/examples/core/core_highdpi_testbed.c b/examples/core/core_highdpi_testbed.c index 601d12e8c..246ef4b42 100644 --- a/examples/core/core_highdpi_testbed.c +++ b/examples/core/core_highdpi_testbed.c @@ -73,7 +73,7 @@ int main(void) } // Draw UI info - DrawText(TextFormat("CURRENT MONITOR: %i/%i (%ix%i)", currentMonitor + 1, GetMonitorCount(), + DrawText(TextFormat("CURRENT MONITOR: %i/%i (%ix%i)", currentMonitor + 1, GetMonitorCount(), GetMonitorWidth(currentMonitor), GetMonitorHeight(currentMonitor)), 50, 50, 20, DARKGRAY); DrawText(TextFormat("WINDOW POSITION: %ix%i", (int)windowPos.x, (int)windowPos.y), 50, 90, 20, DARKGRAY); DrawText(TextFormat("SCREEN SIZE: %ix%i", GetScreenWidth(), GetScreenHeight()), 50, 130, 20, DARKGRAY); diff --git a/examples/core/core_keyboard_testbed.c b/examples/core/core_keyboard_testbed.c index 2ea5c361a..f65260b59 100644 --- a/examples/core/core_keyboard_testbed.c +++ b/examples/core/core_keyboard_testbed.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★☆☆] 2/4 * -* NOTE: raylib defined keys refer to ENG-US Keyboard layout, +* NOTE: raylib defined keys refer to ENG-US Keyboard layout, * mapping to other layouts is up to the user * * Example originally created with raylib 5.6, last time updated with raylib 5.6 @@ -43,20 +43,20 @@ int main(void) int line01KeyWidths[15] = { 0 }; for (int i = 0; i < 15; i++) line01KeyWidths[i] = 45; line01KeyWidths[13] = 62; // PRINTSCREEN - int line01Keys[15] = { - KEY_ESCAPE, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, - KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, - KEY_F12, KEY_PRINT_SCREEN, KEY_PAUSE + int line01Keys[15] = { + KEY_ESCAPE, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, + KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, + KEY_F12, KEY_PRINT_SCREEN, KEY_PAUSE }; - + // Keyboard line 02 int line02KeyWidths[15] = { 0 }; for (int i = 0; i < 15; i++) line02KeyWidths[i] = 45; line02KeyWidths[0] = 25; // GRAVE line02KeyWidths[13] = 82; // BACKSPACE - int line02Keys[15] = { - KEY_GRAVE, KEY_ONE, KEY_TWO, KEY_THREE, KEY_FOUR, - KEY_FIVE, KEY_SIX, KEY_SEVEN, KEY_EIGHT, KEY_NINE, + int line02Keys[15] = { + KEY_GRAVE, KEY_ONE, KEY_TWO, KEY_THREE, KEY_FOUR, + KEY_FIVE, KEY_SIX, KEY_SEVEN, KEY_EIGHT, KEY_NINE, KEY_ZERO, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, KEY_DELETE }; // Keyboard line 03 @@ -103,7 +103,7 @@ int main(void) KEY_SPACE, KEY_RIGHT_ALT, 162, KEY_NULL, KEY_RIGHT_CONTROL, KEY_LEFT, KEY_DOWN, KEY_RIGHT }; - + Vector2 keyboardOffset = { 26, 80 }; SetTargetFPS(60); @@ -128,23 +128,23 @@ int main(void) ClearBackground(RAYWHITE); DrawText("KEYBOARD LAYOUT: ENG-US", 26, 38, 20, LIGHTGRAY); - + // Keyboard line 01 - 15 keys // ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, IMP, CLOSE - for (int i = 0, recOffsetX = 0; i < 15; i++) + for (int i = 0, recOffsetX = 0; i < 15; i++) { GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y, (float)line01KeyWidths[i], 30.0f }, line01Keys[i]); recOffsetX += line01KeyWidths[i] + KEY_REC_SPACING; } - + // Keyboard line 02 - 15 keys // `, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, -, =, BACKSPACE, DEL - for (int i = 0, recOffsetX = 0; i < 15; i++) + for (int i = 0, recOffsetX = 0; i < 15; i++) { GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + KEY_REC_SPACING, (float)line02KeyWidths[i], 38.0f }, line02Keys[i]); recOffsetX += line02KeyWidths[i] + KEY_REC_SPACING; } - + // Keyboard line 03 - 15 keys // TAB, Q, W, E, R, T, Y, U, I, O, P, [, ], \, INS for (int i = 0, recOffsetX = 0; i < 15; i++) @@ -324,8 +324,8 @@ static void GuiKeyboardKey(Rectangle bounds, int key) DrawText(GetKeyText(key), (int)(bounds.x + 4), (int)(bounds.y + 4), 10, DARKGRAY); } } - - if (CheckCollisionPointRec(GetMousePosition(), bounds)) + + if (CheckCollisionPointRec(GetMousePosition(), bounds)) { DrawRectangleRec(bounds, Fade(RED, 0.2f)); DrawRectangleLinesEx(bounds, 3.0f, RED); diff --git a/examples/models/models_animation_blend_custom.c b/examples/models/models_animation_blend_custom.c index 8262f6ac2..018012e9b 100644 --- a/examples/models/models_animation_blend_custom.c +++ b/examples/models/models_animation_blend_custom.c @@ -43,7 +43,7 @@ // Module Functions Declaration //------------------------------------------------------------------------------------ static bool IsUpperBodyBone(const char *boneName); -static void UpdateModelAnimationBones(Model *model, ModelAnimation *anim1, int frame1, +static void UpdateModelAnimationBones(Model *model, ModelAnimation *anim1, int frame1, ModelAnimation *anim2, int frame2, float blend, bool upperBodyBlend); //------------------------------------------------------------------------------------ @@ -86,7 +86,7 @@ int main(void) int animIndex1 = 3; // Attack animation (index 3) int animCurrentFrame0 = 0; int animCurrentFrame1 = 0; - + // Validate indices if (animIndex0 >= animCount) animIndex0 = 0; if (animIndex1 >= animCount) animIndex1 = (animCount > 1) ? 1 : 0; @@ -109,7 +109,7 @@ int main(void) // Update animation frames ModelAnimation anim0 = anims[animIndex0]; ModelAnimation anim1 = anims[animIndex1]; - + animCurrentFrame0 = (animCurrentFrame0 + 1)%anim0.keyframeCount; animCurrentFrame1 = (animCurrentFrame1 + 1)%anim1.keyframeCount; @@ -117,11 +117,11 @@ int main(void) // When upperBodyBlend is ON: upper body = attack (1.0), lower body = walk (0.0) // When upperBodyBlend is OFF: uniform blend at 0.5 (50% walk, 50% attack) float blendFactor = (upperBodyBlend? 1.0f : 0.5f); - UpdateModelAnimationBones(&model, &anim0, animCurrentFrame0, + UpdateModelAnimationBones(&model, &anim0, animCurrentFrame0, &anim1, animCurrentFrame1, blendFactor, upperBodyBlend); // raylib provided animation blending function - //UpdateModelAnimationEx(model, anim0, (float)animCurrentFrame0, + //UpdateModelAnimationEx(model, anim0, (float)animCurrentFrame0, // anim1, (float)animCurrentFrame1, blendFactor); //---------------------------------------------------------------------------------- @@ -142,8 +142,8 @@ int main(void) // Draw UI DrawText(TextFormat("ANIM 0: %s", anim0.name), 10, 10, 20, GRAY); DrawText(TextFormat("ANIM 1: %s", anim1.name), 10, 40, 20, GRAY); - DrawText(TextFormat("[SPACE] Toggle blending mode: %s", - upperBodyBlend? "Upper/Lower Body Blending" : "Uniform Blending"), + DrawText(TextFormat("[SPACE] Toggle blending mode: %s", + upperBodyBlend? "Upper/Lower Body Blending" : "Uniform Blending"), 10, GetScreenHeight() - 30, 20, DARKGRAY); EndDrawing(); @@ -180,7 +180,7 @@ static bool IsUpperBodyBone(const char *boneName) { return true; } - + // Check if bone name contains upper body keywords if (strstr(boneName, "spine") != NULL || strstr(boneName, "chest") != NULL || strstr(boneName, "neck") != NULL || strstr(boneName, "head") != NULL || @@ -189,12 +189,12 @@ static bool IsUpperBodyBone(const char *boneName) { return true; } - + return false; } // Blend two animations per-bone with selective upper/lower body blending -static void UpdateModelAnimationBones(Model *model, ModelAnimation *anim0, int frame0, +static void UpdateModelAnimationBones(Model *model, ModelAnimation *anim0, int frame0, ModelAnimation *anim1, int frame1, float blend, bool upperBodyBlend) { // Validate inputs @@ -204,59 +204,59 @@ static void UpdateModelAnimationBones(Model *model, ModelAnimation *anim0, int f { // Clamp blend factor to [0, 1] blend = fminf(1.0f, fmaxf(0.0f, blend)); - + // Ensure frame indices are valid if (frame0 >= anim0->keyframeCount) frame0 = anim0->keyframeCount - 1; if (frame1 >= anim1->keyframeCount) frame1 = anim1->keyframeCount - 1; if (frame0 < 0) frame0 = 0; if (frame1 < 0) frame1 = 0; - + // Get bone count (use minimum of all to be safe) int boneCount = model->skeleton.boneCount; if (anim0->boneCount < boneCount) boneCount = anim0->boneCount; if (anim1->boneCount < boneCount) boneCount = anim1->boneCount; - + // Blend each bone for (int boneIndex = 0; boneIndex < boneCount; boneIndex++) { // Determine blend factor for this bone float boneBlendFactor = blend; - + // If upper body blending is enabled, use different blend factors for upper vs lower body if (upperBodyBlend) { const char *boneName = model->skeleton.bones[boneIndex].name; bool isUpperBody = IsUpperBodyBone(boneName); - + // Upper body: use anim1 (attack), Lower body: use anim0 (walk) // blend = 0.0 means full anim0 (walk), 1.0 means full anim1 (attack) if (isUpperBody) boneBlendFactor = blend; // Upper body: blend towards anim1 (attack) else boneBlendFactor = 1.0f - blend; // Lower body: blend towards anim0 (walk) - invert the blend } - + // Get transforms from both animations Transform *bindTransform = &model->skeleton.bindPose[boneIndex]; Transform *animTransform0 = &anim0->keyframePoses[frame0][boneIndex]; Transform *animTransform1 = &anim1->keyframePoses[frame1][boneIndex]; - + // Blend the transforms Transform blended = { 0 }; blended.translation = Vector3Lerp(animTransform0->translation, animTransform1->translation, boneBlendFactor); blended.rotation = QuaternionSlerp(animTransform0->rotation, animTransform1->rotation, boneBlendFactor); blended.scale = Vector3Lerp(animTransform0->scale, animTransform1->scale, boneBlendFactor); - + // Convert bind pose to matrix Matrix bindMatrix = MatrixMultiply(MatrixMultiply( MatrixScale(bindTransform->scale.x, bindTransform->scale.y, bindTransform->scale.z), QuaternionToMatrix(bindTransform->rotation)), MatrixTranslate(bindTransform->translation.x, bindTransform->translation.y, bindTransform->translation.z)); - + // Convert blended transform to matrix Matrix blendedMatrix = MatrixMultiply(MatrixMultiply( MatrixScale(blended.scale.x, blended.scale.y, blended.scale.z), QuaternionToMatrix(blended.rotation)), MatrixTranslate(blended.translation.x, blended.translation.y, blended.translation.z)); - + // Calculate final bone matrix (similar to UpdateModelAnimationBones) model->boneMatrices[boneIndex] = MatrixMultiply(MatrixInvert(bindMatrix), blendedMatrix); } @@ -276,7 +276,7 @@ static void UpdateModelAnimationBones(Model *model, ModelAnimation *anim0, int f bool bufferUpdateRequired = false; // Flag to check when anim vertex information is updated // Skip if missing bone data or missing anim buffers initialization - if ((mesh.boneWeights == NULL) || (mesh.boneIndices == NULL) || + if ((mesh.boneWeights == NULL) || (mesh.boneIndices == NULL) || (mesh.animVertices == NULL) || (mesh.animNormals == NULL)) continue; for (int vCounter = 0; vCounter < vertexValuesCount; vCounter += 3) diff --git a/examples/models/models_animation_blending.c b/examples/models/models_animation_blending.c index e79067c3d..63eba61cd 100644 --- a/examples/models/models_animation_blending.c +++ b/examples/models/models_animation_blending.c @@ -3,7 +3,7 @@ * raylib [models] example - animation blending * * Example complexity rating: [★★★★] 4/4 -* +* * Example originally created with raylib 5.5, last time updated with raylib 6.0 * * Example contributed by Kirandeep (@Kirandeep-Singh-Khehra) and reviewed by Ramon Santamaria (@raysan5) @@ -57,7 +57,7 @@ int main(void) // WARNING: It requires SUPPORT_GPU_SKINNING enabled on raylib (disabled by default) Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION), TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION)); - + // Assign skinning shader to all materials shaders //for (int i = 0; i < model.materialCount; i++) model.materials[i].shader = skinningShader; @@ -105,7 +105,7 @@ int main(void) // Update //---------------------------------------------------------------------------------- UpdateCamera(&camera, CAMERA_ORBITAL); - + if (IsKeyPressed(KEY_P)) animPause = !animPause; if (!animPause) @@ -127,7 +127,7 @@ int main(void) } // Set animation transition - animTransition = true; + animTransition = true; animBlendTimeCounter = 0.0f; animBlendFactor = 0.0f; } @@ -182,7 +182,7 @@ int main(void) animCurrentFrame0 += animFrameSpeed0; if (animCurrentFrame0 >= anims[animIndex0].keyframeCount) animCurrentFrame0 = 0.0f; UpdateModelAnimation(model, anims[animIndex0], animCurrentFrame0); - //UpdateModelAnimationEx(model, anims[animIndex0], animCurrentFrame0, + //UpdateModelAnimationEx(model, anims[animIndex0], animCurrentFrame0, // anims[animIndex1], animCurrentFrame1, 0.0f); // Same as above, first animation frame blend } else if (currentAnimPlaying == 1) @@ -191,7 +191,7 @@ int main(void) animCurrentFrame1 += animFrameSpeed1; if (animCurrentFrame1 >= anims[animIndex1].keyframeCount) animCurrentFrame1 = 0.0f; UpdateModelAnimation(model, anims[animIndex1], animCurrentFrame1); - //UpdateModelAnimationEx(model, anims[animIndex0], animCurrentFrame0, + //UpdateModelAnimationEx(model, anims[animIndex0], animCurrentFrame0, // anims[animIndex1], animCurrentFrame1, 1.0f); // Same as above, second animation frame blend } } @@ -213,7 +213,7 @@ int main(void) DrawModel(model, position, 1.0f, WHITE); // Draw animated model DrawGrid(10, 1.0f); - + EndMode3D(); if (animTransition) DrawText("ANIM TRANSITION BLENDING!", 170, 50, 30, BLUE); @@ -221,18 +221,18 @@ int main(void) // Draw UI elements //--------------------------------------------------------------------------------------------- if (dropdownEditMode0) GuiDisable(); - GuiSlider((Rectangle){ 10, 38, 160, 12 }, + GuiSlider((Rectangle){ 10, 38, 160, 12 }, NULL, TextFormat("x%.1f", animFrameSpeed0), &animFrameSpeed0, 0.1f, 2.0f); GuiEnable(); if (dropdownEditMode1) GuiDisable(); - GuiSlider((Rectangle){ GetScreenWidth() - 170.0f, 38, 160, 12 }, + GuiSlider((Rectangle){ GetScreenWidth() - 170.0f, 38, 160, 12 }, TextFormat("%.1fx", animFrameSpeed1), NULL, &animFrameSpeed1, 0.1f, 2.0f); GuiEnable(); // Draw animation selectors for blending transition - // NOTE: Transition does not start until requested + // NOTE: Transition does not start until requested GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 1); - if (GuiDropdownBox((Rectangle){ 10, 10, 160, 24 }, TextJoin(animNames, animCount, ";"), + if (GuiDropdownBox((Rectangle){ 10, 10, 160, 24 }, TextJoin(animNames, animCount, ";"), &animIndex0, dropdownEditMode0)) dropdownEditMode0 = !dropdownEditMode0; // Blending process progress bar @@ -249,7 +249,7 @@ int main(void) TextFormat("FRAME: %.2f / %i", animFrameProgress0, anims[animIndex0].keyframeCount), &animFrameProgress0, 0.0f, (float)anims[animIndex0].keyframeCount); for (int i = 0; i < anims[animIndex0].keyframeCount; i++) - DrawRectangle(60 + (int)(((float)(GetScreenWidth() - 180)/(float)anims[animIndex0].keyframeCount)*(float)i), + DrawRectangle(60 + (int)(((float)(GetScreenWidth() - 180)/(float)anims[animIndex0].keyframeCount)*(float)i), GetScreenHeight() - 60, 1, 20, BLUE); // Draw playing timeline with keyframes for anim1[] @@ -257,7 +257,7 @@ int main(void) TextFormat("FRAME: %.2f / %i", animFrameProgress1, anims[animIndex1].keyframeCount), &animFrameProgress1, 0.0f, (float)anims[animIndex1].keyframeCount); for (int i = 0; i < anims[animIndex1].keyframeCount; i++) - DrawRectangle(60 + (int)(((float)(GetScreenWidth() - 180)/(float)anims[animIndex1].keyframeCount)*(float)i), + DrawRectangle(60 + (int)(((float)(GetScreenWidth() - 180)/(float)anims[animIndex1].keyframeCount)*(float)i), GetScreenHeight() - 30, 1, 20, BLUE); //--------------------------------------------------------------------------------------------- @@ -270,7 +270,7 @@ int main(void) UnloadModelAnimations(anims, animCount); // Unload model animation UnloadModel(model); // Unload model and meshes/material UnloadShader(skinningShader); // Unload GPU skinning shader - + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_animation_timing.c b/examples/models/models_animation_timing.c index a302322fe..b88deb49d 100644 --- a/examples/models/models_animation_timing.c +++ b/examples/models/models_animation_timing.c @@ -94,26 +94,26 @@ int main(void) BeginMode3D(camera); DrawModel(model, position, 1.0f, WHITE); - + DrawGrid(10, 1.0f); - + EndMode3D(); // Draw UI, select anim and playing speed GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 1); - if (GuiDropdownBox((Rectangle){ 10, 10, 140, 24 }, TextJoin(animNames, animCount, ";"), + if (GuiDropdownBox((Rectangle){ 10, 10, 140, 24 }, TextJoin(animNames, animCount, ";"), &animIndex, dropdownEditMode)) dropdownEditMode = !dropdownEditMode; GuiSlider((Rectangle){ 260, 10, 500, 24 }, "FRAME SPEED: ", TextFormat("x%.1f", animFrameSpeed), &animFrameSpeed, 0.1f, 2.0f); // Draw playing timeline with keyframes - GuiLabel((Rectangle){ 10, GetScreenHeight() - 64.0f, GetScreenWidth() - 20.0f, 24 }, + GuiLabel((Rectangle){ 10, GetScreenHeight() - 64.0f, GetScreenWidth() - 20.0f, 24 }, TextFormat("CURRENT FRAME: %.2f / %i", animFrameProgress, anims[animIndex].keyframeCount)); GuiProgressBar((Rectangle){ 10, GetScreenHeight() - 40.0f, GetScreenWidth() - 20.0f, 24 }, NULL, NULL, &animFrameProgress, 0.0f, (float)anims[animIndex].keyframeCount); for (int i = 0; i < anims[animIndex].keyframeCount; i++) - DrawRectangle(10 + (int)(((float)(GetScreenWidth() - 20)/(float)anims[animIndex].keyframeCount)*(float)i), + DrawRectangle(10 + (int)(((float)(GetScreenWidth() - 20)/(float)anims[animIndex].keyframeCount)*(float)i), GetScreenHeight() - 40, 1, 24, BLUE); EndDrawing(); diff --git a/examples/models/models_basic_voxel.c b/examples/models/models_basic_voxel.c index 2e2756f2b..127a7e989 100644 --- a/examples/models/models_basic_voxel.c +++ b/examples/models/models_basic_voxel.c @@ -60,7 +60,7 @@ int main(void) } } } - + SetTargetFPS(60); //-------------------------------------------------------------------------------------- @@ -109,7 +109,7 @@ int main(void) } } } - + // Remove the closest voxel if one was hit if (voxelFound) { @@ -145,9 +145,9 @@ int main(void) } } } - + EndMode3D(); - + // Draw reference point for raycasting to delete blocks DrawCircle(GetScreenWidth()/2, GetScreenHeight()/2, 4, RED); @@ -161,7 +161,7 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- UnloadModel(cubeModel); - + CloseWindow(); //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_skybox_rendering.c b/examples/models/models_skybox_rendering.c index 9359e03dc..c8d10dc25 100644 --- a/examples/models/models_skybox_rendering.c +++ b/examples/models/models_skybox_rendering.c @@ -92,7 +92,7 @@ int main(void) } else { - // TODO: WARNING: On PLATFORM_WEB it requires a big amount of memory to process input image + // TODO: WARNING: On PLATFORM_WEB it requires a big amount of memory to process input image // and generate the required cubemap image to be passed to rlLoadTextureCubemap() Image image = LoadImage("resources/skybox.png"); skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = LoadTextureCubemap(image, CUBEMAP_LAYOUT_AUTO_DETECT); diff --git a/examples/models/models_yaw_pitch_roll.c b/examples/models/models_yaw_pitch_roll.c index af0f06daf..76074cca4 100644 --- a/examples/models/models_yaw_pitch_roll.c +++ b/examples/models/models_yaw_pitch_roll.c @@ -41,7 +41,7 @@ int main(void) Model model = LoadModel("resources/models/obj/plane.obj"); // Load model Texture2D texture = LoadTexture("resources/models/obj/plane_diffuse.png"); // Load model texture - + SetTextureWrap(texture, TEXTURE_WRAP_REPEAT); // Force Repeat to avoid issue on Web version model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture; // Set map diffuse texture diff --git a/examples/shaders/shaders_cel_shading.c b/examples/shaders/shaders_cel_shading.c index 133b8db9e..beda73bca 100644 --- a/examples/shaders/shaders_cel_shading.c +++ b/examples/shaders/shaders_cel_shading.c @@ -52,7 +52,7 @@ int main(void) camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; camera.fovy = 45.0f; camera.projection = CAMERA_PERSPECTIVE; - + // Load model Model model = LoadModel("resources/models/old_car_new.glb"); @@ -61,7 +61,7 @@ int main(void) TextFormat("resources/shaders/glsl%i/cel.vs", GLSL_VERSION), TextFormat("resources/shaders/glsl%i/cel.fs", GLSL_VERSION)); celShader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(celShader, "viewPos"); - + // Apply cel shader to model, keep copy of default shader Shader defaultShader = model.materials[0].shader; model.materials[0].shader = celShader; @@ -134,16 +134,16 @@ int main(void) // Outline pass: cull front faces, draw extruded back faces as silhouette float thickness = 0.005f; SetShaderValue(outlineShader, outlineThicknessLoc, &thickness, SHADER_UNIFORM_FLOAT); - + rlSetCullFace(RL_CULL_FACE_FRONT); - + model.materials[0].shader = outlineShader; - + DrawModel(model, Vector3Zero(), 0.75f, WHITE); - + if (celEnabled) model.materials[0].shader = celShader; // Apply cel shader to model else model.materials[0].shader = defaultShader; // Apply default shader to model - + rlSetCullFace(RL_CULL_FACE_BACK); } @@ -167,7 +167,7 @@ int main(void) UnloadModel(model); UnloadShader(celShader); UnloadShader(outlineShader); - + CloseWindow(); //-------------------------------------------------------------------------------------- diff --git a/examples/shaders/shaders_deferred_rendering.c b/examples/shaders/shaders_deferred_rendering.c index 4b03b69a4..ea27f9b8b 100644 --- a/examples/shaders/shaders_deferred_rendering.c +++ b/examples/shaders/shaders_deferred_rendering.c @@ -210,7 +210,7 @@ int main(void) rlClearColor(0, 0, 0, 0); rlClearScreenBuffers(); // Clear color and depth buffer rlDisableColorBlend(); - + BeginMode3D(camera); // NOTE: We have to use rlEnableShader here. `BeginShaderMode` or thus `rlSetShader` // will not work, as they won't immediately load the shader program @@ -226,7 +226,7 @@ int main(void) } rlDisableShader(); EndMode3D(); - + rlEnableColorBlend(); // Go back to the default framebufferId (0) and draw our deferred shading diff --git a/examples/shaders/shaders_game_of_life.c b/examples/shaders/shaders_game_of_life.c index 251095e2b..a09d889ac 100644 --- a/examples/shaders/shaders_game_of_life.c +++ b/examples/shaders/shaders_game_of_life.c @@ -59,7 +59,7 @@ int main(void) //-------------------------------------------------------------------------------------- const int screenWidth = 800; const int screenHeight = 450; - + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - game of life"); const int menuWidth = 100; @@ -81,7 +81,7 @@ int main(void) { "Puffer train", { 0.1f, 0.5f } }, { "Glider Gun", { 0.2f, 0.2f } }, { "Breeder", { 0.1f, 0.5f } }, { "Random", { 0.5f, 0.5f } } }; - + const int numberOfPresets = sizeof(presetPatterns)/sizeof(presetPatterns[0]); int zoom = 1; @@ -90,7 +90,7 @@ int main(void) int framesPerStep = 1; int frame = 0; - int preset = -1; // No button pressed for preset + int preset = -1; // No button pressed for preset int mode = MODE_RUN; // Starting mode: running bool buttonZoomIn = false; // Button states: false not pressed bool buttonZomOut = false; @@ -185,7 +185,7 @@ int main(void) EndTextureMode(); imageToDraw = (Image*)RL_MALLOC(sizeof(Image)); *imageToDraw = LoadImageFromTexture(worldOnScreen.texture); - + UnloadRenderTexture(worldOnScreen); } @@ -199,9 +199,9 @@ int main(void) if (mouseY >= sizeInWorldY) mouseY = sizeInWorldY - 1; if (firstColor == -1) firstColor = (GetImageColor(*imageToDraw, mouseX, mouseY).r < 5)? 0 : 1; const int prevColor = (GetImageColor(*imageToDraw, mouseX, mouseY).r < 5)? 0 : 1; - + ImageDrawPixel(imageToDraw, mouseX, mouseY, (firstColor) ? BLACK : RAYWHITE); - + if (prevColor != firstColor) UpdateTextureRec(currentWorld->texture, (Rectangle){ floorf(offsetX), floorf(offsetY), (float)(sizeInWorldX), (float)(sizeInWorldY) }, imageToDraw->data); } else firstColor = -1; @@ -228,7 +228,7 @@ int main(void) BeginTextureMode(*currentWorld); ClearBackground(RAYWHITE); EndTextureMode(); - + UpdateTextureRec(currentWorld->texture, (Rectangle){ worldWidth*presetPatterns[preset].position.x - pattern.width/2.0f, worldHeight*presetPatterns[preset].position.y - pattern.height/2.0f, (float)(pattern.width), (float)(pattern.height) }, pattern.data); @@ -256,7 +256,7 @@ int main(void) } UnloadImage(pattern); - + mode = MODE_PAUSE; offsetX = worldWidth*presetPatterns[preset].position.x - (float)windowWidth/zoom/2.0f; offsetY = worldHeight*presetPatterns[preset].position.y - (float)windowHeight/zoom/2.0f; @@ -293,7 +293,7 @@ int main(void) // Draw to screen //---------------------------------------------------------------------------------- BeginDrawing(); - + DrawTexturePro(currentWorld->texture, textureSourceToScreen, textureOnScreen, (Vector2){ 0, 0 }, 0.0f, WHITE); DrawLine(windowWidth, 0, windowWidth, screenHeight, (Color){ 218, 218, 218, 255 }); diff --git a/examples/shapes/shapes_ball_physics.c b/examples/shapes/shapes_ball_physics.c index 9c18ab66e..2a4cc8f18 100644 --- a/examples/shapes/shapes_ball_physics.c +++ b/examples/shapes/shapes_ball_physics.c @@ -31,7 +31,7 @@ typedef struct Ball { Vector2 speed; Vector2 prevPosition; float radius; - float friction; + float friction; float elasticity; Color color; bool grabbed; @@ -62,7 +62,7 @@ int main(void) .color = BLUE, .grabbed = false }; - + int ballCount = 1; Ball *grabbedBall = NULL; // A pointer to the current ball that is grabbed Vector2 pressOffset = { 0 }; // Mouse press offset relative to the ball that grabbedd @@ -157,10 +157,10 @@ int main(void) { ball->position.x = screenWidth - ball->radius; // Ball repositioning ball->speed.x = -ball->speed.x*ball->elasticity; // Elasticity makes the ball lose 10% of its velocity on hit - } + } // Does the ball hit the screen left boundary? else if ((ball->position.x - ball->radius) <= 0) - { + { ball->position.x = ball->radius; ball->speed.x = -ball->speed.x*ball->elasticity; } @@ -170,9 +170,9 @@ int main(void) { ball->position.y = screenHeight - ball->radius; ball->speed.y = -ball->speed.y*ball->elasticity; - } + } else if ((ball->position.y - ball->radius) <= 0) - { + { ball->position.y = ball->radius; ball->speed.y = -ball->speed.y*ball->elasticity; } diff --git a/examples/shapes/shapes_easings_testbed.c b/examples/shapes/shapes_easings_testbed.c index 1c9fcf741..6912422ea 100644 --- a/examples/shapes/shapes_easings_testbed.c +++ b/examples/shapes/shapes_easings_testbed.c @@ -72,7 +72,7 @@ typedef struct EasingFuncs { // Module Functions Declaration //------------------------------------------------------------------------------------ // Function used when "no easing" is selected for any axis -static float NoEase(float t, float b, float c, float d); +static float NoEase(float t, float b, float c, float d); //------------------------------------------------------------------------------------ // Global Variables Definition diff --git a/examples/shapes/shapes_hilbert_curve.c b/examples/shapes/shapes_hilbert_curve.c index 759fe93fd..64270fa24 100644 --- a/examples/shapes/shapes_hilbert_curve.c +++ b/examples/shapes/shapes_hilbert_curve.c @@ -46,13 +46,13 @@ int main(void) float size = (float)GetScreenHeight(); int strokeCount = 0; Vector2 *hilbertPath = LoadHilbertPath(order, size, &strokeCount); - + int prevOrder = order; int prevSize = (int)size; // NOTE: Size from slider is float but for comparison we use int int counter = 0; float thick = 2.0f; bool animate = true; - + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -68,19 +68,19 @@ int main(void) { UnloadHilbertPath(hilbertPath); hilbertPath = LoadHilbertPath(order, size, &strokeCount); - + if (animate) counter = 0; else counter = strokeCount; - + prevOrder = order; prevSize = (int)size; } //---------------------------------------------------------------------------------- - + // Draw //-------------------------------------------------------------------------- BeginDrawing(); - + ClearBackground(RAYWHITE); if (counter < strokeCount) @@ -90,7 +90,7 @@ int main(void) { DrawLineEx(hilbertPath[i], hilbertPath[i - 1], thick, ColorFromHSV(((float)i/strokeCount)*360.0f, 1.0f, 1.0f)); } - + counter += 1; } else @@ -101,13 +101,13 @@ int main(void) DrawLineEx(hilbertPath[i], hilbertPath[i - 1], thick, ColorFromHSV(((float)i/strokeCount)*360.0f, 1.0f, 1.0f)); } } - + // Draw UI using raygui GuiCheckBox((Rectangle){ 450, 50, 20, 20 }, "ANIMATE GENERATION ON CHANGE", &animate); GuiSpinner((Rectangle){ 585, 100, 180, 30 }, "HILBERT CURVE ORDER: ", &order, 2, 8, false); GuiSlider((Rectangle){ 524, 150, 240, 24 }, "THICKNESS: ", NULL, &thick, 1.0f, 10.0f); GuiSlider((Rectangle){ 524, 190, 240, 24 }, "TOTAL SIZE: ", NULL, &size, 10.0f, GetScreenHeight()*1.5f); - + EndDrawing(); //-------------------------------------------------------------------------- } @@ -116,7 +116,7 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- UnloadHilbertPath(hilbertPath); - + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; @@ -133,14 +133,14 @@ static Vector2 *LoadHilbertPath(int order, float size, int *strokeCount) *strokeCount = N*N; Vector2 *hilbertPath = (Vector2 *)RL_CALLOC(*strokeCount, sizeof(Vector2)); - + for (int i = 0; i < *strokeCount; i++) { hilbertPath[i] = ComputeHilbertStep(order, i); hilbertPath[i].x = hilbertPath[i].x*len + len/2.0f; hilbertPath[i].y = hilbertPath[i].y*len + len/2.0f; } - + return hilbertPath; } @@ -160,7 +160,7 @@ static Vector2 ComputeHilbertStep(int order, int index) [2] = { .x = 1, .y = 1 }, [3] = { .x = 1, .y = 0 }, }; - + int hilbertIndex = index&3; Vector2 vect = hilbertPoints[hilbertIndex]; float temp = 0.0f; @@ -171,7 +171,7 @@ static Vector2 ComputeHilbertStep(int order, int index) index = index >> 2; hilbertIndex = index&3; len = 1 << j; - + switch (hilbertIndex) { case 0: @@ -191,6 +191,6 @@ static Vector2 ComputeHilbertStep(int order, int index) default: break; } } - + return vect; } diff --git a/examples/shapes/shapes_penrose_tile.c b/examples/shapes/shapes_penrose_tile.c index 0221a10b3..74d4bb717 100644 --- a/examples/shapes/shapes_penrose_tile.c +++ b/examples/shapes/shapes_penrose_tile.c @@ -106,7 +106,7 @@ int main(void) if (generations > 0) rebuild = true; } } - + if (rebuild) { RL_FREE(ls.production); // Free previous production for re-creation @@ -118,15 +118,15 @@ int main(void) // Draw //---------------------------------------------------------------------------------- BeginDrawing(); - + ClearBackground( RAYWHITE ); - + if (generations > 0) DrawPenroseLSystem(&ls); - + DrawText("penrose l-system", 10, 10, 20, DARKGRAY); DrawText("press up or down to change generations", 10, 30, 20, DARKGRAY); DrawText(TextFormat("generations: %d", generations), 10, 50, 20, DARKGRAY); - + EndDrawing(); //---------------------------------------------------------------------------------- } @@ -171,11 +171,11 @@ static PenroseLSystem CreatePenroseLSystem(float drawLength) .drawLength = drawLength, .theta = 36.0f // Degrees }; - + ls.production = (char *)RL_MALLOC(sizeof(char)*STR_MAX_SIZE); ls.production[0] = '\0'; strncpy(ls.production, "[X]++[X]++[X]++[X]++[X]", STR_MAX_SIZE); - + return ls; } @@ -211,7 +211,7 @@ static void BuildProductionStep(PenroseLSystem *ls) ls->drawLength *= 0.5f; strncpy(ls->production, newProduction, STR_MAX_SIZE); - + RL_FREE(newProduction); } @@ -228,9 +228,9 @@ static void DrawPenroseLSystem(PenroseLSystem *ls) int repeats = 1; int productionLength = (int)strnlen(ls->production, STR_MAX_SIZE); ls->steps += 12; - + if (ls->steps > productionLength) ls->steps = productionLength; - + for (int i = 0; i < ls->steps; i++) { char step = ls->production[i]; @@ -244,24 +244,24 @@ static void DrawPenroseLSystem(PenroseLSystem *ls) turtle.origin.y += ls->drawLength*sinf(radAngle); Vector2 startPosScreen = { startPosWorld.x + screenCenter.x, startPosWorld.y + screenCenter.y }; Vector2 endPosScreen = { turtle.origin.x + screenCenter.x, turtle.origin.y + screenCenter.y }; - + DrawLineEx(startPosScreen, endPosScreen, 2, Fade(BLACK, 0.2f)); } - + repeats = 1; - } + } else if (step == '+') { for (int j = 0; j < repeats; j++) turtle.angle += ls->theta; repeats = 1; - } + } else if (step == '-') { for (int j = 0; j < repeats; j++) turtle.angle += -ls->theta; repeats = 1; - } + } else if (step == '[') PushTurtleState(turtle); else if (step == ']') turtle = PopTurtleState(); else if ((step >= 48) && (step <= 57)) repeats = (int) step - 48; diff --git a/examples/text/text_strings_management.c b/examples/text/text_strings_management.c index 60f0941f3..449677eb6 100644 --- a/examples/text/text_strings_management.c +++ b/examples/text/text_strings_management.c @@ -33,7 +33,7 @@ typedef struct TextParticle { Vector2 ppos; // Previous position float padding; float borderWidth; - float friction; + float friction; float elasticity; Color color; bool grabbed; @@ -118,7 +118,7 @@ int main(void) if (IsKeyDown(KEY_LEFT_SHIFT)) { ShatterTextParticle(tp, i, textParticles, &particleCount); - } + } else { SliceTextParticle(tp, i, TextLength(tp->text)/2, textParticles, &particleCount); @@ -158,33 +158,33 @@ int main(void) TextParticle *tp = &textParticles[i]; // The text particle is not grabbed - if (!tp->grabbed) + if (!tp->grabbed) { // text particle repositioning using the velocity tp->rect.x += tp->vel.x * delta; tp->rect.y += tp->vel.y * delta; // Does the text particle hit the screen right boundary? - if ((tp->rect.x + tp->rect.width) >= screenWidth) + if ((tp->rect.x + tp->rect.width) >= screenWidth) { tp->rect.x = screenWidth - tp->rect.width; // Text particle repositioning tp->vel.x = -tp->vel.x*tp->elasticity; // Elasticity makes the text particle lose 10% of its velocity on hit - } + } // Does the text particle hit the screen left boundary? else if (tp->rect.x <= 0) - { + { tp->rect.x = 0.0f; tp->vel.x = -tp->vel.x*tp->elasticity; } // The same for y axis - if ((tp->rect.y + tp->rect.height) >= screenHeight) + if ((tp->rect.y + tp->rect.height) >= screenHeight) { tp->rect.y = screenHeight - tp->rect.height; tp->vel.y = -tp->vel.y*tp->elasticity; - } - else if (tp->rect.y <= 0) - { + } + else if (tp->rect.y <= 0) + { tp->rect.y = 0.0f; tp->vel.y = -tp->vel.y*tp->elasticity; } @@ -264,9 +264,9 @@ int main(void) void PrepareFirstTextParticle(const char* text, TextParticle *tps, int *particleCount) { tps[0] = CreateTextParticle( - text, - GetScreenWidth()/2.0f, - GetScreenHeight()/2.0f, + text, + GetScreenWidth()/2.0f, + GetScreenHeight()/2.0f, RAYWHITE ); *particleCount = 1; @@ -317,7 +317,7 @@ void SliceTextParticleByChar(TextParticle *tp, char charToSlice, TextParticle *t { int tokenCount = 0; char **tokens = TextSplit(tp->text, charToSlice, &tokenCount); - + if (tokenCount > 1) { int textLength = TextLength(tp->text); diff --git a/examples/textures/textures_clipboard_image.c b/examples/textures/textures_clipboard_image.c index 1e9ec0846..16501be31 100644 --- a/examples/textures/textures_clipboard_image.c +++ b/examples/textures/textures_clipboard_image.c @@ -51,7 +51,7 @@ int main(void) { // Unload textures to avoid memory leaks for (int i = 0; i < MAX_TEXTURE_COLLECTION; i++) UnloadTexture(collection[i].texture); - + currentCollectionIndex = 0; } @@ -59,7 +59,7 @@ int main(void) (currentCollectionIndex < MAX_TEXTURE_COLLECTION)) { Image image = GetClipboardImage(); - + if (IsImageValid(image)) { collection[currentCollectionIndex].texture = LoadTextureFromImage(image); @@ -76,15 +76,15 @@ int main(void) BeginDrawing(); ClearBackground(RAYWHITE); - + for (int i = 0; i < currentCollectionIndex; i++) { if (IsTextureValid(collection[i].texture)) { - DrawTexturePro(collection[i].texture, - (Rectangle){0,0,collection[i].texture.width, collection[i].texture.height}, + DrawTexturePro(collection[i].texture, + (Rectangle){0,0,collection[i].texture.width, collection[i].texture.height}, (Rectangle){collection[i].position.x,collection[i].position.y,collection[i].texture.width, collection[i].texture.height}, - (Vector2){collection[i].texture.width*0.5f, collection[i].texture.height*0.5f}, + (Vector2){collection[i].texture.width*0.5f, collection[i].texture.height*0.5f}, 0.0f, WHITE); } } diff --git a/examples/textures/textures_magnifying_glass.c b/examples/textures/textures_magnifying_glass.c index 12cd0cdee..e6d5d6316 100644 --- a/examples/textures/textures_magnifying_glass.c +++ b/examples/textures/textures_magnifying_glass.c @@ -3,7 +3,7 @@ * raylib textures example - magnifying glass * * Example complexity rating: [★★★☆] 3/4 -* +* * Example originally created with raylib 5.6, last time updated with raylib 5.6 * * Example contributed by Luke Vaughan (@badram) and reviewed by Ramon Santamaria (@raysan5) @@ -29,18 +29,18 @@ int main(void) const int screenHeight = 450; InitWindow(screenWidth, screenHeight, "raylib [textures] example - magnifying glass"); - + Texture2D bunny = LoadTexture("resources/raybunny.png"); Texture2D parrots = LoadTexture("resources/parrots.png"); - + // Use image draw to generate a mask texture instead of loading it from a file. Image circle = GenImageColor(256, 256, BLANK); ImageDrawCircle(&circle, 128, 128, 128, WHITE); Texture2D mask = LoadTextureFromImage(circle); // Copy the mask image from RAM to VRAM UnloadImage(circle); // Unload the image from RAM - + RenderTexture2D magnifiedWorld = LoadRenderTexture(256, 256); - + Camera2D camera = { 0 }; // Set magnifying glass zoom camera.zoom = 2; @@ -71,7 +71,7 @@ int main(void) DrawTexture(parrots, 144, 33, WHITE); DrawText("Use the magnifying glass to find hidden bunnies!", 154, 6, 20, BLACK); - // Render to a the magnifying glass + // Render to a the magnifying glass BeginTextureMode(magnifiedWorld); ClearBackground(RAYWHITE); diff --git a/examples/textures/textures_screen_buffer.c b/examples/textures/textures_screen_buffer.c index 9c0f25031..928b1a929 100644 --- a/examples/textures/textures_screen_buffer.c +++ b/examples/textures/textures_screen_buffer.c @@ -52,7 +52,7 @@ int main(void) float hue = t*t; float saturation = t; float value = t; - + palette[i] = ColorFromHSV(250.0f + 150.0f*hue, saturation, value); } @@ -92,14 +92,14 @@ int main(void) { unsigned int i = x + y*imageWidth; unsigned char colorIndex = indexBuffer[i]; - + if (colorIndex != 0) { // Move pixel a row above indexBuffer[i] = 0; int moveX = GetRandomValue(0, 2) - 1; int newX = x + moveX; - + if ((newX > 0) && (newX < imageWidth)) { unsigned int iabove = i - imageWidth + moveX; @@ -130,7 +130,7 @@ int main(void) // Draw //---------------------------------------------------------------------------------- BeginDrawing(); - + ClearBackground(RAYWHITE); DrawTextureEx(screenTexture, (Vector2){ 0, 0 }, 0.0f, 2.0f, WHITE); diff --git a/src/external/rlsw.h b/src/external/rlsw.h index 8472b03e9..0342211d2 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -2281,7 +2281,7 @@ static inline sw_pixel_write_color_f sw_pixel_get_write_color_func(sw_pixelforma { case SW_PIXELFORMAT_COLOR_GRAYSCALE: return sw_pixel_write_color_GRAYSCALE; case SW_PIXELFORMAT_COLOR_GRAYALPHA: return sw_pixel_write_color_GRAYALPHA; - case SW_PIXELFORMAT_COLOR_R3G3B2: return sw_pixel_write_color_R3G3B2; + case SW_PIXELFORMAT_COLOR_R3G3B2: return sw_pixel_write_color_R3G3B2; case SW_PIXELFORMAT_COLOR_R5G6B5: return sw_pixel_write_color_R5G6B5; case SW_PIXELFORMAT_COLOR_R8G8B8: return sw_pixel_write_color_R8G8B8; case SW_PIXELFORMAT_COLOR_R5G5B5A1: return sw_pixel_write_color_R5G5B5A1; @@ -2472,7 +2472,7 @@ static inline void sw_texture_sample_linear(float *SW_RESTRICT color, const sw_t static inline void sw_texture_sample(float *SW_RESTRICT color, const sw_texture_t *SW_RESTRICT tex, float u, float v, float dUdx, float dUdy, float dVdx, float dVdy) { - // NOTE: Commented there is the previous method used + // NOTE: Commented there is the previous method used // There was no need to compute the square root because // using the squared value, the comparison remains (L2 > 1.0f*1.0f) //float du = sqrtf(dUdx*dUdx + dUdy*dUdy); @@ -2673,7 +2673,7 @@ static inline void sw_framebuffer_output_copy(void *dst, const sw_texture_t *buf } } -static inline void sw_framebuffer_output_blit(void *dst, const sw_texture_t *buffer, +static inline void sw_framebuffer_output_blit(void *dst, const sw_texture_t *buffer, int xDst, int yDst, int wDst, int hDst, int xSrc, int ySrc, int wSrc, int hSrc, sw_pixelformat_t format) { const uint8_t *srcBase = buffer->pixels; @@ -3007,25 +3007,25 @@ static bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES] #define RLSW_TEMPLATE_RASTER_TRIANGLE BASE #include __FILE__ // IWYU pragma: keep #undef RLSW_TEMPLATE_RASTER_TRIANGLE - + #define RLSW_TEMPLATE_RASTER_TRIANGLE TEX #define SW_ENABLE_TEXTURE #include __FILE__ #undef SW_ENABLE_TEXTURE #undef RLSW_TEMPLATE_RASTER_TRIANGLE - + #define RLSW_TEMPLATE_RASTER_TRIANGLE DEPTH #define SW_ENABLE_DEPTH_TEST #include __FILE__ #undef SW_ENABLE_DEPTH_TEST #undef RLSW_TEMPLATE_RASTER_TRIANGLE - + #define RLSW_TEMPLATE_RASTER_TRIANGLE BLEND #define SW_ENABLE_BLEND #include __FILE__ #undef SW_ENABLE_BLEND #undef RLSW_TEMPLATE_RASTER_TRIANGLE - + #define RLSW_TEMPLATE_RASTER_TRIANGLE TEX_DEPTH #define SW_ENABLE_TEXTURE #define SW_ENABLE_DEPTH_TEST @@ -3033,7 +3033,7 @@ static bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES] #undef SW_ENABLE_DEPTH_TEST #undef SW_ENABLE_TEXTURE #undef RLSW_TEMPLATE_RASTER_TRIANGLE - + #define RLSW_TEMPLATE_RASTER_TRIANGLE TEX_BLEND #define SW_ENABLE_TEXTURE #define SW_ENABLE_BLEND @@ -3041,7 +3041,7 @@ static bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES] #undef SW_ENABLE_BLEND #undef SW_ENABLE_TEXTURE #undef RLSW_TEMPLATE_RASTER_TRIANGLE - + #define RLSW_TEMPLATE_RASTER_TRIANGLE DEPTH_BLEND #define SW_ENABLE_DEPTH_TEST #define SW_ENABLE_BLEND @@ -3049,7 +3049,7 @@ static bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES] #undef SW_ENABLE_BLEND #undef SW_ENABLE_DEPTH_TEST #undef RLSW_TEMPLATE_RASTER_TRIANGLE - + #define RLSW_TEMPLATE_RASTER_TRIANGLE TEX_DEPTH_BLEND #define SW_ENABLE_TEXTURE #define SW_ENABLE_DEPTH_TEST @@ -3059,7 +3059,7 @@ static bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES] #undef SW_ENABLE_DEPTH_TEST #undef SW_ENABLE_TEXTURE #undef RLSW_TEMPLATE_RASTER_TRIANGLE - + // Dispatch table (auto-generated from SW_RASTER_VARIANTS) #define SW_TABLE_ENTRY(NAME, FLAGS) [FLAGS] = sw_raster_triangle_##NAME, static const sw_raster_triangle_f SW_RASTER_TRIANGLE_TABLE[] = { @@ -3106,25 +3106,25 @@ static bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES] #define RLSW_TEMPLATE_RASTER_QUAD BASE #include __FILE__ // IWYU pragma: keep #undef RLSW_TEMPLATE_RASTER_QUAD - + #define RLSW_TEMPLATE_RASTER_QUAD TEX #define SW_ENABLE_TEXTURE #include __FILE__ #undef SW_ENABLE_TEXTURE #undef RLSW_TEMPLATE_RASTER_QUAD - + #define RLSW_TEMPLATE_RASTER_QUAD DEPTH #define SW_ENABLE_DEPTH_TEST #include __FILE__ #undef SW_ENABLE_DEPTH_TEST #undef RLSW_TEMPLATE_RASTER_QUAD - + #define RLSW_TEMPLATE_RASTER_QUAD BLEND #define SW_ENABLE_BLEND #include __FILE__ #undef SW_ENABLE_BLEND #undef RLSW_TEMPLATE_RASTER_QUAD - + #define RLSW_TEMPLATE_RASTER_QUAD TEX_DEPTH #define SW_ENABLE_TEXTURE #define SW_ENABLE_DEPTH_TEST @@ -3132,7 +3132,7 @@ static bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES] #undef SW_ENABLE_DEPTH_TEST #undef SW_ENABLE_TEXTURE #undef RLSW_TEMPLATE_RASTER_QUAD - + #define RLSW_TEMPLATE_RASTER_QUAD TEX_BLEND #define SW_ENABLE_TEXTURE #define SW_ENABLE_BLEND @@ -3140,7 +3140,7 @@ static bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES] #undef SW_ENABLE_BLEND #undef SW_ENABLE_TEXTURE #undef RLSW_TEMPLATE_RASTER_QUAD - + #define RLSW_TEMPLATE_RASTER_QUAD DEPTH_BLEND #define SW_ENABLE_DEPTH_TEST #define SW_ENABLE_BLEND @@ -3148,7 +3148,7 @@ static bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES] #undef SW_ENABLE_BLEND #undef SW_ENABLE_DEPTH_TEST #undef RLSW_TEMPLATE_RASTER_QUAD - + #define RLSW_TEMPLATE_RASTER_QUAD TEX_DEPTH_BLEND #define SW_ENABLE_TEXTURE #define SW_ENABLE_DEPTH_TEST @@ -3158,7 +3158,7 @@ static bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES] #undef SW_ENABLE_DEPTH_TEST #undef SW_ENABLE_TEXTURE #undef RLSW_TEMPLATE_RASTER_QUAD - + // Dispatch table (auto-generated from SW_RASTER_VARIANTS) #define SW_TABLE_ENTRY(NAME, FLAGS) [FLAGS] = sw_raster_quad_##NAME, static const sw_raster_quad_f SW_RASTER_QUAD_TABLE[] = { @@ -3202,19 +3202,19 @@ static bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES] #define RLSW_TEMPLATE_RASTER_LINE BASE #include __FILE__ // IWYU pragma: keep #undef RLSW_TEMPLATE_RASTER_LINE - + #define RLSW_TEMPLATE_RASTER_LINE DEPTH #define SW_ENABLE_DEPTH_TEST #include __FILE__ #undef SW_ENABLE_DEPTH_TEST #undef RLSW_TEMPLATE_RASTER_LINE - + #define RLSW_TEMPLATE_RASTER_LINE BLEND #define SW_ENABLE_BLEND #include __FILE__ #undef SW_ENABLE_BLEND #undef RLSW_TEMPLATE_RASTER_LINE - + #define RLSW_TEMPLATE_RASTER_LINE DEPTH_BLEND #define SW_ENABLE_DEPTH_TEST #define SW_ENABLE_BLEND @@ -3222,7 +3222,7 @@ static bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES] #undef SW_ENABLE_BLEND #undef SW_ENABLE_DEPTH_TEST #undef RLSW_TEMPLATE_RASTER_LINE - + // Dispatch table (auto-generated from SW_RASTER_VARIANTS) #define SW_TABLE_ENTRY0(NAME, FLAGS) [FLAGS] = sw_raster_line_##NAME, #define SW_TABLE_ENTRY1(NAME, FLAGS) [FLAGS] = sw_raster_line_thick_##NAME, @@ -3266,19 +3266,19 @@ static bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES] #define RLSW_TEMPLATE_RASTER_POINT BASE #include __FILE__ // IWYU pragma: keep #undef RLSW_TEMPLATE_RASTER_POINT - + #define RLSW_TEMPLATE_RASTER_POINT DEPTH #define SW_ENABLE_DEPTH_TEST #include __FILE__ #undef SW_ENABLE_DEPTH_TEST #undef RLSW_TEMPLATE_RASTER_POINT - + #define RLSW_TEMPLATE_RASTER_POINT BLEND #define SW_ENABLE_BLEND #include __FILE__ #undef SW_ENABLE_BLEND #undef RLSW_TEMPLATE_RASTER_POINT - + #define RLSW_TEMPLATE_RASTER_POINT DEPTH_BLEND #define SW_ENABLE_DEPTH_TEST #define SW_ENABLE_BLEND @@ -3286,7 +3286,7 @@ static bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES] #undef SW_ENABLE_BLEND #undef SW_ENABLE_DEPTH_TEST #undef RLSW_TEMPLATE_RASTER_POINT - + // Dispatch table (auto-generated from SW_RASTER_VARIANTS) #define SW_TABLE_ENTRY(NAME, FLAGS) [FLAGS] = sw_raster_point_##NAME, static const sw_raster_point_f SW_RASTER_POINT_TABLE[] = { @@ -5171,7 +5171,7 @@ void swFramebufferTexture2D(SWattachment attach, uint32_t texture) RLSW.colorBuffer = sw_pool_get(&RLSW.texturePool, texture); } break; case SW_DEPTH_ATTACHMENT: - { + { fb->depthAttachment = texture; RLSW.depthBuffer = sw_pool_get(&RLSW.texturePool, texture); } break; @@ -5262,7 +5262,7 @@ void swGetFramebufferAttachmentParameteriv(SWattachment attachment, SWattachget static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t *end, float dUdy, float dVdy) { - // Gets the start/end coordinates and skip empty lines + // Gets the start/end coordinates and skip empty lines int xStart = (int)start->position[0]; int xEnd = (int)end->position[0]; if (xStart == xEnd) return; diff --git a/src/rcore.c b/src/rcore.c index 894e00832..87df4d5d4 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -684,7 +684,7 @@ void InitWindow(int width, int height, const char *title) } // Initialize render dimensions for embedded platforms - // NOTE: On desktop platforms (GLFW, SDL, etc.), CORE.Window.render.width/height are set during window creation + // NOTE: On desktop platforms (GLFW, SDL, etc.), CORE.Window.render.width/height are set during window creation // On embedded platforms with no window manager, InitPlatform() doesn't set these values, so they should be initialized // here from screen dimensions (which are set from the InitWindow parameters) if ((CORE.Window.render.width == 0) || (CORE.Window.render.height == 0)) diff --git a/src/rtext.c b/src/rtext.c index 936ab9c16..16fe8e75f 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -2265,7 +2265,6 @@ int GetCodepoint(const char *text, int *codepointSize) 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ - int codepoint = 0x3f; // Codepoint (defaults to '?') *codepointSize = 1; if (text == NULL) return codepoint; From bd3a35ca21a9f1181e8e67909e926902ed705270 Mon Sep 17 00:00:00 2001 From: Lewis Lee <105315997+LewisLee26@users.noreply.github.com> Date: Thu, 26 Mar 2026 08:20:44 +0000 Subject: [PATCH 217/319] [build.zig.zon] Fix: replace deleted hexops/xcode-frameworks with pkg.machengine.org mirror (#5693) --- build.zig.zon | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index dbbde3aad..247386fb1 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -7,8 +7,8 @@ .dependencies = .{ .xcode_frameworks = .{ - .url = "git+https://github.com/hexops/xcode-frameworks#9a45f3ac977fd25dff77e58c6de1870b6808c4a7", - .hash = "N-V-__8AABHMqAWYuRdIlflwi8gksPnlUMQBiSxAqQAAZFms", + .url = "https://pkg.machengine.org/xcode-frameworks/9a45f3ac977fd25dff77e58c6de1870b6808c4a7.tar.gz", + .hash = "122098b9174895f9708bc824b0f9e550c401892c40a900006459acf2cbf78acd99bb", .lazy = true, }, .emsdk = .{ From a693365bf2597b081d64ec12a816fe32ad563aa6 Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Thu, 26 Mar 2026 17:29:05 +0000 Subject: [PATCH 218/319] Move DrawModelPoints methods to example - point rendering (#5697) --- examples/models/models_point_rendering.c | 33 ++- .../raylib_npp_parser/raylib_npp.xml | 20 +- .../raylib_npp_parser/raylib_to_parse.h | 2 - src/raylib.h | 2 - src/rmodels.c | 26 -- tools/rlparser/output/raylib_api.json | 54 ---- tools/rlparser/output/raylib_api.lua | 24 -- tools/rlparser/output/raylib_api.txt | 236 ++++++++---------- tools/rlparser/output/raylib_api.xml | 14 -- 9 files changed, 141 insertions(+), 270 deletions(-) diff --git a/examples/models/models_point_rendering.c b/examples/models/models_point_rendering.c index 71b907225..fd8d1b9b2 100644 --- a/examples/models/models_point_rendering.c +++ b/examples/models/models_point_rendering.c @@ -16,6 +16,7 @@ ********************************************************************************************/ #include "raylib.h" +#include "rlgl.h" #include // Required for: rand() #include // Required for: cosf(), sinf() @@ -26,8 +27,9 @@ //------------------------------------------------------------------------------------ // Module Functions Declaration //------------------------------------------------------------------------------------ -// Generate mesh using points -static Mesh GenMeshPoints(int numPoints); +static Mesh GenMeshPoints(int numPoints); // Generate mesh using points +void DrawModelPoints(Model model, Vector3 position, float scale, Color tint); // Draw a model as points +void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model as points with extended parameters //------------------------------------------------------------------------------------ // Program main entry point @@ -184,3 +186,30 @@ static Mesh GenMeshPoints(int numPoints) return mesh; } + + +// Draw a model points +// WARNING: OpenGL ES 2.0 does not support point mode drawing +void DrawModelPoints(Model model, Vector3 position, float scale, Color tint) +{ + rlEnablePointMode(); + rlDisableBackfaceCulling(); + + DrawModel(model, position, scale, tint); + + rlEnableBackfaceCulling(); + rlDisablePointMode(); +} + +// Draw a model points +// WARNING: OpenGL ES 2.0 does not support point mode drawing +void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint) +{ + rlEnablePointMode(); + rlDisableBackfaceCulling(); + + DrawModelEx(model, position, rotationAxis, rotationAngle, scale, tint); + + rlEnableBackfaceCulling(); + rlDisablePointMode(); +} diff --git a/projects/Notepad++/raylib_npp_parser/raylib_npp.xml b/projects/Notepad++/raylib_npp_parser/raylib_npp.xml index 4c6362162..ee965bd76 100644 --- a/projects/Notepad++/raylib_npp_parser/raylib_npp.xml +++ b/projects/Notepad++/raylib_npp_parser/raylib_npp.xml @@ -2960,24 +2960,6 @@ - - - - - - - - - - - - - - - - - - @@ -3013,7 +2995,7 @@ - + diff --git a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h index 27e9b79a2..1bd26fbc3 100644 --- a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h +++ b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h @@ -600,8 +600,6 @@ RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint); RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set) RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters -RLAPI void DrawModelPoints(Model model, Vector3 position, float scale, Color tint); // Draw a model as points -RLAPI void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model as points with extended parameters RLAPI void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires) RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint); // Draw a billboard texture RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source diff --git a/src/raylib.h b/src/raylib.h index 2e849c917..0c2a0beda 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1596,8 +1596,6 @@ RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint); RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set) RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters -RLAPI void DrawModelPoints(Model model, Vector3 position, float scale, Color tint); // Draw a model as points -RLAPI void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model as points with extended parameters RLAPI void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires) RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint); // Draw a billboard texture RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source diff --git a/src/rmodels.c b/src/rmodels.c index ba28eb839..0b31d224b 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3961,32 +3961,6 @@ void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rlDisableWireMode(); } -// Draw a model points -// WARNING: OpenGL ES 2.0 does not support point mode drawing -void DrawModelPoints(Model model, Vector3 position, float scale, Color tint) -{ - rlEnablePointMode(); - rlDisableBackfaceCulling(); - - DrawModel(model, position, scale, tint); - - rlEnableBackfaceCulling(); - rlDisablePointMode(); -} - -// Draw a model points -// WARNING: OpenGL ES 2.0 does not support point mode drawing -void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint) -{ - rlEnablePointMode(); - rlDisableBackfaceCulling(); - - DrawModelEx(model, position, rotationAxis, rotationAngle, scale, tint); - - rlEnableBackfaceCulling(); - rlDisablePointMode(); -} - // Draw a billboard void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint) { diff --git a/tools/rlparser/output/raylib_api.json b/tools/rlparser/output/raylib_api.json index 082e349b7..d4ff5598b 100644 --- a/tools/rlparser/output/raylib_api.json +++ b/tools/rlparser/output/raylib_api.json @@ -10820,60 +10820,6 @@ } ] }, - { - "name": "DrawModelPoints", - "description": "Draw a model as points", - "returnType": "void", - "params": [ - { - "type": "Model", - "name": "model" - }, - { - "type": "Vector3", - "name": "position" - }, - { - "type": "float", - "name": "scale" - }, - { - "type": "Color", - "name": "tint" - } - ] - }, - { - "name": "DrawModelPointsEx", - "description": "Draw a model as points with extended parameters", - "returnType": "void", - "params": [ - { - "type": "Model", - "name": "model" - }, - { - "type": "Vector3", - "name": "position" - }, - { - "type": "Vector3", - "name": "rotationAxis" - }, - { - "type": "float", - "name": "rotationAngle" - }, - { - "type": "Vector3", - "name": "scale" - }, - { - "type": "Color", - "name": "tint" - } - ] - }, { "name": "DrawBoundingBox", "description": "Draw bounding box (wires)", diff --git a/tools/rlparser/output/raylib_api.lua b/tools/rlparser/output/raylib_api.lua index b68ab7563..5e22916b3 100644 --- a/tools/rlparser/output/raylib_api.lua +++ b/tools/rlparser/output/raylib_api.lua @@ -7493,30 +7493,6 @@ return { {type = "Color", name = "tint"} } }, - { - name = "DrawModelPoints", - description = "Draw a model as points", - returnType = "void", - params = { - {type = "Model", name = "model"}, - {type = "Vector3", name = "position"}, - {type = "float", name = "scale"}, - {type = "Color", name = "tint"} - } - }, - { - name = "DrawModelPointsEx", - description = "Draw a model as points with extended parameters", - returnType = "void", - params = { - {type = "Model", name = "model"}, - {type = "Vector3", name = "position"}, - {type = "Vector3", name = "rotationAxis"}, - {type = "float", name = "rotationAngle"}, - {type = "Vector3", name = "scale"}, - {type = "Color", name = "tint"} - } - }, { name = "DrawBoundingBox", description = "Draw bounding box (wires)", diff --git a/tools/rlparser/output/raylib_api.txt b/tools/rlparser/output/raylib_api.txt index 2213d3def..929a3a30e 100644 --- a/tools/rlparser/output/raylib_api.txt +++ b/tools/rlparser/output/raylib_api.txt @@ -4126,31 +4126,13 @@ Function 488: DrawModelWiresEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 489: DrawModelPoints() (4 input parameters) - Name: DrawModelPoints - Return type: void - Description: Draw a model as points - Param[1]: model (type: Model) - Param[2]: position (type: Vector3) - Param[3]: scale (type: float) - Param[4]: tint (type: Color) -Function 490: DrawModelPointsEx() (6 input parameters) - Name: DrawModelPointsEx - Return type: void - Description: Draw a model as points with extended parameters - Param[1]: model (type: Model) - Param[2]: position (type: Vector3) - Param[3]: rotationAxis (type: Vector3) - Param[4]: rotationAngle (type: float) - Param[5]: scale (type: Vector3) - Param[6]: tint (type: Color) -Function 491: DrawBoundingBox() (2 input parameters) +Function 489: 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 492: DrawBillboard() (5 input parameters) +Function 490: DrawBillboard() (5 input parameters) Name: DrawBillboard Return type: void Description: Draw a billboard texture @@ -4159,7 +4141,7 @@ Function 492: DrawBillboard() (5 input parameters) Param[3]: position (type: Vector3) Param[4]: scale (type: float) Param[5]: tint (type: Color) -Function 493: DrawBillboardRec() (6 input parameters) +Function 491: DrawBillboardRec() (6 input parameters) Name: DrawBillboardRec Return type: void Description: Draw a billboard texture defined by source @@ -4169,7 +4151,7 @@ Function 493: DrawBillboardRec() (6 input parameters) Param[4]: position (type: Vector3) Param[5]: size (type: Vector2) Param[6]: tint (type: Color) -Function 494: DrawBillboardPro() (9 input parameters) +Function 492: DrawBillboardPro() (9 input parameters) Name: DrawBillboardPro Return type: void Description: Draw a billboard texture defined by source and rotation @@ -4182,13 +4164,13 @@ Function 494: DrawBillboardPro() (9 input parameters) Param[7]: origin (type: Vector2) Param[8]: rotation (type: float) Param[9]: tint (type: Color) -Function 495: UploadMesh() (2 input parameters) +Function 493: 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 496: UpdateMeshBuffer() (5 input parameters) +Function 494: UpdateMeshBuffer() (5 input parameters) Name: UpdateMeshBuffer Return type: void Description: Update mesh vertex data in GPU for a specific buffer index @@ -4197,19 +4179,19 @@ Function 496: UpdateMeshBuffer() (5 input parameters) Param[3]: data (type: const void *) Param[4]: dataSize (type: int) Param[5]: offset (type: int) -Function 497: UnloadMesh() (1 input parameters) +Function 495: UnloadMesh() (1 input parameters) Name: UnloadMesh Return type: void Description: Unload mesh data from CPU and GPU Param[1]: mesh (type: Mesh) -Function 498: DrawMesh() (3 input parameters) +Function 496: 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 499: DrawMeshInstanced() (4 input parameters) +Function 497: DrawMeshInstanced() (4 input parameters) Name: DrawMeshInstanced Return type: void Description: Draw multiple mesh instances with material and different transforms @@ -4217,35 +4199,35 @@ Function 499: DrawMeshInstanced() (4 input parameters) Param[2]: material (type: Material) Param[3]: transforms (type: const Matrix *) Param[4]: instances (type: int) -Function 500: GetMeshBoundingBox() (1 input parameters) +Function 498: GetMeshBoundingBox() (1 input parameters) Name: GetMeshBoundingBox Return type: BoundingBox Description: Compute mesh bounding box limits Param[1]: mesh (type: Mesh) -Function 501: GenMeshTangents() (1 input parameters) +Function 499: GenMeshTangents() (1 input parameters) Name: GenMeshTangents Return type: void Description: Compute mesh tangents Param[1]: mesh (type: Mesh *) -Function 502: ExportMesh() (2 input parameters) +Function 500: 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 503: ExportMeshAsCode() (2 input parameters) +Function 501: 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 504: GenMeshPoly() (2 input parameters) +Function 502: GenMeshPoly() (2 input parameters) Name: GenMeshPoly Return type: Mesh Description: Generate polygonal mesh Param[1]: sides (type: int) Param[2]: radius (type: float) -Function 505: GenMeshPlane() (4 input parameters) +Function 503: GenMeshPlane() (4 input parameters) Name: GenMeshPlane Return type: Mesh Description: Generate plane mesh (with subdivisions) @@ -4253,42 +4235,42 @@ Function 505: GenMeshPlane() (4 input parameters) Param[2]: length (type: float) Param[3]: resX (type: int) Param[4]: resZ (type: int) -Function 506: GenMeshCube() (3 input parameters) +Function 504: 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 507: GenMeshSphere() (3 input parameters) +Function 505: 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 508: GenMeshHemiSphere() (3 input parameters) +Function 506: 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 509: GenMeshCylinder() (3 input parameters) +Function 507: 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 510: GenMeshCone() (3 input parameters) +Function 508: 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 511: GenMeshTorus() (4 input parameters) +Function 509: GenMeshTorus() (4 input parameters) Name: GenMeshTorus Return type: Mesh Description: Generate torus mesh @@ -4296,7 +4278,7 @@ Function 511: GenMeshTorus() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 512: GenMeshKnot() (4 input parameters) +Function 510: GenMeshKnot() (4 input parameters) Name: GenMeshKnot Return type: Mesh Description: Generate trefoil knot mesh @@ -4304,67 +4286,67 @@ Function 512: GenMeshKnot() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 513: GenMeshHeightmap() (2 input parameters) +Function 511: 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 514: GenMeshCubicmap() (2 input parameters) +Function 512: 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 515: LoadMaterials() (2 input parameters) +Function 513: 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 516: LoadMaterialDefault() (0 input parameters) +Function 514: LoadMaterialDefault() (0 input parameters) Name: LoadMaterialDefault Return type: Material Description: Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) No input parameters -Function 517: IsMaterialValid() (1 input parameters) +Function 515: 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 518: UnloadMaterial() (1 input parameters) +Function 516: UnloadMaterial() (1 input parameters) Name: UnloadMaterial Return type: void Description: Unload material from GPU memory (VRAM) Param[1]: material (type: Material) -Function 519: SetMaterialTexture() (3 input parameters) +Function 517: 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 520: SetModelMeshMaterial() (3 input parameters) +Function 518: 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 521: LoadModelAnimations() (2 input parameters) +Function 519: 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 522: UpdateModelAnimation() (3 input parameters) +Function 520: UpdateModelAnimation() (3 input parameters) Name: UpdateModelAnimation Return type: void Description: Update model animation pose (vertex buffers and bone matrices) Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: float) -Function 523: UpdateModelAnimationEx() (6 input parameters) +Function 521: UpdateModelAnimationEx() (6 input parameters) Name: UpdateModelAnimationEx Return type: void Description: Update model animation pose, blending two animations @@ -4374,19 +4356,19 @@ Function 523: UpdateModelAnimationEx() (6 input parameters) Param[4]: animB (type: ModelAnimation) Param[5]: frameB (type: float) Param[6]: blend (type: float) -Function 524: UnloadModelAnimations() (2 input parameters) +Function 522: 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 525: IsModelAnimationValid() (2 input parameters) +Function 523: 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 526: CheckCollisionSpheres() (4 input parameters) +Function 524: CheckCollisionSpheres() (4 input parameters) Name: CheckCollisionSpheres Return type: bool Description: Check collision between two spheres @@ -4394,40 +4376,40 @@ Function 526: CheckCollisionSpheres() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector3) Param[4]: radius2 (type: float) -Function 527: CheckCollisionBoxes() (2 input parameters) +Function 525: 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 528: CheckCollisionBoxSphere() (3 input parameters) +Function 526: 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 529: GetRayCollisionSphere() (3 input parameters) +Function 527: 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 530: GetRayCollisionBox() (2 input parameters) +Function 528: 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 531: GetRayCollisionMesh() (3 input parameters) +Function 529: 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 532: GetRayCollisionTriangle() (4 input parameters) +Function 530: GetRayCollisionTriangle() (4 input parameters) Name: GetRayCollisionTriangle Return type: RayCollision Description: Get collision info between ray and triangle @@ -4435,7 +4417,7 @@ Function 532: GetRayCollisionTriangle() (4 input parameters) Param[2]: p1 (type: Vector3) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) -Function 533: GetRayCollisionQuad() (5 input parameters) +Function 531: GetRayCollisionQuad() (5 input parameters) Name: GetRayCollisionQuad Return type: RayCollision Description: Get collision info between ray and quad @@ -4444,158 +4426,158 @@ Function 533: GetRayCollisionQuad() (5 input parameters) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) Param[5]: p4 (type: Vector3) -Function 534: InitAudioDevice() (0 input parameters) +Function 532: InitAudioDevice() (0 input parameters) Name: InitAudioDevice Return type: void Description: Initialize audio device and context No input parameters -Function 535: CloseAudioDevice() (0 input parameters) +Function 533: CloseAudioDevice() (0 input parameters) Name: CloseAudioDevice Return type: void Description: Close the audio device and context No input parameters -Function 536: IsAudioDeviceReady() (0 input parameters) +Function 534: IsAudioDeviceReady() (0 input parameters) Name: IsAudioDeviceReady Return type: bool Description: Check if audio device has been initialized successfully No input parameters -Function 537: SetMasterVolume() (1 input parameters) +Function 535: SetMasterVolume() (1 input parameters) Name: SetMasterVolume Return type: void Description: Set master volume (listener) Param[1]: volume (type: float) -Function 538: GetMasterVolume() (0 input parameters) +Function 536: GetMasterVolume() (0 input parameters) Name: GetMasterVolume Return type: float Description: Get master volume (listener) No input parameters -Function 539: LoadWave() (1 input parameters) +Function 537: LoadWave() (1 input parameters) Name: LoadWave Return type: Wave Description: Load wave data from file Param[1]: fileName (type: const char *) -Function 540: LoadWaveFromMemory() (3 input parameters) +Function 538: 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 541: IsWaveValid() (1 input parameters) +Function 539: 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 542: LoadSound() (1 input parameters) +Function 540: LoadSound() (1 input parameters) Name: LoadSound Return type: Sound Description: Load sound from file Param[1]: fileName (type: const char *) -Function 543: LoadSoundFromWave() (1 input parameters) +Function 541: LoadSoundFromWave() (1 input parameters) Name: LoadSoundFromWave Return type: Sound Description: Load sound from wave data Param[1]: wave (type: Wave) -Function 544: LoadSoundAlias() (1 input parameters) +Function 542: 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 545: IsSoundValid() (1 input parameters) +Function 543: 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 546: UpdateSound() (3 input parameters) +Function 544: UpdateSound() (3 input parameters) Name: UpdateSound Return type: void Description: Update sound buffer with new data (default data format: 32 bit float, stereo) Param[1]: sound (type: Sound) Param[2]: data (type: const void *) Param[3]: sampleCount (type: int) -Function 547: UnloadWave() (1 input parameters) +Function 545: UnloadWave() (1 input parameters) Name: UnloadWave Return type: void Description: Unload wave data Param[1]: wave (type: Wave) -Function 548: UnloadSound() (1 input parameters) +Function 546: UnloadSound() (1 input parameters) Name: UnloadSound Return type: void Description: Unload sound Param[1]: sound (type: Sound) -Function 549: UnloadSoundAlias() (1 input parameters) +Function 547: 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 550: ExportWave() (2 input parameters) +Function 548: 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 551: ExportWaveAsCode() (2 input parameters) +Function 549: 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 552: PlaySound() (1 input parameters) +Function 550: PlaySound() (1 input parameters) Name: PlaySound Return type: void Description: Play a sound Param[1]: sound (type: Sound) -Function 553: StopSound() (1 input parameters) +Function 551: StopSound() (1 input parameters) Name: StopSound Return type: void Description: Stop playing a sound Param[1]: sound (type: Sound) -Function 554: PauseSound() (1 input parameters) +Function 552: PauseSound() (1 input parameters) Name: PauseSound Return type: void Description: Pause a sound Param[1]: sound (type: Sound) -Function 555: ResumeSound() (1 input parameters) +Function 553: ResumeSound() (1 input parameters) Name: ResumeSound Return type: void Description: Resume a paused sound Param[1]: sound (type: Sound) -Function 556: IsSoundPlaying() (1 input parameters) +Function 554: IsSoundPlaying() (1 input parameters) Name: IsSoundPlaying Return type: bool Description: Check if a sound is currently playing Param[1]: sound (type: Sound) -Function 557: SetSoundVolume() (2 input parameters) +Function 555: 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 558: SetSoundPitch() (2 input parameters) +Function 556: 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 559: SetSoundPan() (2 input parameters) +Function 557: SetSoundPan() (2 input parameters) Name: SetSoundPan Return type: void Description: Set pan for a sound (-1.0 left, 0.0 center, 1.0 right) Param[1]: sound (type: Sound) Param[2]: pan (type: float) -Function 560: WaveCopy() (1 input parameters) +Function 558: WaveCopy() (1 input parameters) Name: WaveCopy Return type: Wave Description: Copy a wave to a new wave Param[1]: wave (type: Wave) -Function 561: WaveCrop() (3 input parameters) +Function 559: 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 562: WaveFormat() (4 input parameters) +Function 560: WaveFormat() (4 input parameters) Name: WaveFormat Return type: void Description: Convert wave data to desired format @@ -4603,203 +4585,203 @@ Function 562: WaveFormat() (4 input parameters) Param[2]: sampleRate (type: int) Param[3]: sampleSize (type: int) Param[4]: channels (type: int) -Function 563: LoadWaveSamples() (1 input parameters) +Function 561: 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 564: UnloadWaveSamples() (1 input parameters) +Function 562: UnloadWaveSamples() (1 input parameters) Name: UnloadWaveSamples Return type: void Description: Unload samples data loaded with LoadWaveSamples() Param[1]: samples (type: float *) -Function 565: LoadMusicStream() (1 input parameters) +Function 563: LoadMusicStream() (1 input parameters) Name: LoadMusicStream Return type: Music Description: Load music stream from file Param[1]: fileName (type: const char *) -Function 566: LoadMusicStreamFromMemory() (3 input parameters) +Function 564: 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 567: IsMusicValid() (1 input parameters) +Function 565: 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 568: UnloadMusicStream() (1 input parameters) +Function 566: UnloadMusicStream() (1 input parameters) Name: UnloadMusicStream Return type: void Description: Unload music stream Param[1]: music (type: Music) -Function 569: PlayMusicStream() (1 input parameters) +Function 567: PlayMusicStream() (1 input parameters) Name: PlayMusicStream Return type: void Description: Start music playing Param[1]: music (type: Music) -Function 570: IsMusicStreamPlaying() (1 input parameters) +Function 568: IsMusicStreamPlaying() (1 input parameters) Name: IsMusicStreamPlaying Return type: bool Description: Check if music is playing Param[1]: music (type: Music) -Function 571: UpdateMusicStream() (1 input parameters) +Function 569: UpdateMusicStream() (1 input parameters) Name: UpdateMusicStream Return type: void Description: Updates buffers for music streaming Param[1]: music (type: Music) -Function 572: StopMusicStream() (1 input parameters) +Function 570: StopMusicStream() (1 input parameters) Name: StopMusicStream Return type: void Description: Stop music playing Param[1]: music (type: Music) -Function 573: PauseMusicStream() (1 input parameters) +Function 571: PauseMusicStream() (1 input parameters) Name: PauseMusicStream Return type: void Description: Pause music playing Param[1]: music (type: Music) -Function 574: ResumeMusicStream() (1 input parameters) +Function 572: ResumeMusicStream() (1 input parameters) Name: ResumeMusicStream Return type: void Description: Resume playing paused music Param[1]: music (type: Music) -Function 575: SeekMusicStream() (2 input parameters) +Function 573: 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 576: SetMusicVolume() (2 input parameters) +Function 574: 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 577: SetMusicPitch() (2 input parameters) +Function 575: 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 578: SetMusicPan() (2 input parameters) +Function 576: SetMusicPan() (2 input parameters) Name: SetMusicPan Return type: void Description: Set pan for a music (-1.0 left, 0.0 center, 1.0 right) Param[1]: music (type: Music) Param[2]: pan (type: float) -Function 579: GetMusicTimeLength() (1 input parameters) +Function 577: GetMusicTimeLength() (1 input parameters) Name: GetMusicTimeLength Return type: float Description: Get music time length (in seconds) Param[1]: music (type: Music) -Function 580: GetMusicTimePlayed() (1 input parameters) +Function 578: GetMusicTimePlayed() (1 input parameters) Name: GetMusicTimePlayed Return type: float Description: Get current music time played (in seconds) Param[1]: music (type: Music) -Function 581: LoadAudioStream() (3 input parameters) +Function 579: 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 582: IsAudioStreamValid() (1 input parameters) +Function 580: 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 583: UnloadAudioStream() (1 input parameters) +Function 581: UnloadAudioStream() (1 input parameters) Name: UnloadAudioStream Return type: void Description: Unload audio stream and free memory Param[1]: stream (type: AudioStream) -Function 584: UpdateAudioStream() (3 input parameters) +Function 582: 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 585: IsAudioStreamProcessed() (1 input parameters) +Function 583: IsAudioStreamProcessed() (1 input parameters) Name: IsAudioStreamProcessed Return type: bool Description: Check if any audio stream buffers requires refill Param[1]: stream (type: AudioStream) -Function 586: PlayAudioStream() (1 input parameters) +Function 584: PlayAudioStream() (1 input parameters) Name: PlayAudioStream Return type: void Description: Play audio stream Param[1]: stream (type: AudioStream) -Function 587: PauseAudioStream() (1 input parameters) +Function 585: PauseAudioStream() (1 input parameters) Name: PauseAudioStream Return type: void Description: Pause audio stream Param[1]: stream (type: AudioStream) -Function 588: ResumeAudioStream() (1 input parameters) +Function 586: ResumeAudioStream() (1 input parameters) Name: ResumeAudioStream Return type: void Description: Resume audio stream Param[1]: stream (type: AudioStream) -Function 589: IsAudioStreamPlaying() (1 input parameters) +Function 587: IsAudioStreamPlaying() (1 input parameters) Name: IsAudioStreamPlaying Return type: bool Description: Check if audio stream is playing Param[1]: stream (type: AudioStream) -Function 590: StopAudioStream() (1 input parameters) +Function 588: StopAudioStream() (1 input parameters) Name: StopAudioStream Return type: void Description: Stop audio stream Param[1]: stream (type: AudioStream) -Function 591: SetAudioStreamVolume() (2 input parameters) +Function 589: 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 592: SetAudioStreamPitch() (2 input parameters) +Function 590: 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 593: SetAudioStreamPan() (2 input parameters) +Function 591: SetAudioStreamPan() (2 input parameters) Name: SetAudioStreamPan Return type: void Description: Set pan for audio stream (-1.0 to 1.0 range, 0.0 is centered) Param[1]: stream (type: AudioStream) Param[2]: pan (type: float) -Function 594: SetAudioStreamBufferSizeDefault() (1 input parameters) +Function 592: SetAudioStreamBufferSizeDefault() (1 input parameters) Name: SetAudioStreamBufferSizeDefault Return type: void Description: Default size for new audio streams Param[1]: size (type: int) -Function 595: SetAudioStreamCallback() (2 input parameters) +Function 593: 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 596: AttachAudioStreamProcessor() (2 input parameters) +Function 594: 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 597: DetachAudioStreamProcessor() (2 input parameters) +Function 595: 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 598: AttachAudioMixedProcessor() (1 input parameters) +Function 596: 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 599: DetachAudioMixedProcessor() (1 input parameters) +Function 597: DetachAudioMixedProcessor() (1 input parameters) Name: DetachAudioMixedProcessor Return type: void Description: Detach audio stream processor from the entire audio pipeline diff --git a/tools/rlparser/output/raylib_api.xml b/tools/rlparser/output/raylib_api.xml index 2ec474efd..3816c4dfc 100644 --- a/tools/rlparser/output/raylib_api.xml +++ b/tools/rlparser/output/raylib_api.xml @@ -2755,20 +2755,6 @@
- - - - - - - - - - - - - - From d7bd56ef1b97b10568a1272ed0aaf4f347d519f5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 17:29:18 +0000 Subject: [PATCH 219/319] rlparser: update raylib_api.* by CI --- tools/rlparser/output/raylib_api.txt | 2 +- tools/rlparser/output/raylib_api.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/rlparser/output/raylib_api.txt b/tools/rlparser/output/raylib_api.txt index 929a3a30e..7943b28f3 100644 --- a/tools/rlparser/output/raylib_api.txt +++ b/tools/rlparser/output/raylib_api.txt @@ -1000,7 +1000,7 @@ Callback 006: AudioCallback() (2 input parameters) Param[1]: bufferData (type: void *) Param[2]: frames (type: unsigned int) -Functions found: 599 +Functions found: 597 Function 001: InitWindow() (3 input parameters) Name: InitWindow diff --git a/tools/rlparser/output/raylib_api.xml b/tools/rlparser/output/raylib_api.xml index 3816c4dfc..408ff18c7 100644 --- a/tools/rlparser/output/raylib_api.xml +++ b/tools/rlparser/output/raylib_api.xml @@ -682,7 +682,7 @@ - + From 51f4741912b25ddf9931c01aa594562a7b02b2de Mon Sep 17 00:00:00 2001 From: Antonio Jose Ramos Marquez Date: Fri, 27 Mar 2026 09:25:26 +0100 Subject: [PATCH 220/319] [rmodel] fix for devices without VAO support (#5692) --- src/rmodels.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index 0b31d224b..c78930f03 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1281,7 +1281,6 @@ void UploadMesh(Mesh *mesh, bool dynamic) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) mesh->vaoId = rlLoadVertexArray(); - if (mesh->vaoId == 0) return; rlEnableVertexArray(mesh->vaoId); From 98d6e24c442e1c884d5f60590061aa0fa468778a Mon Sep 17 00:00:00 2001 From: mjt Date: Fri, 27 Mar 2026 10:26:01 +0200 Subject: [PATCH 221/319] remove comment (#5696) --- examples/shaders/resources/shaders/glsl100/normalmap.fs | 2 +- examples/shaders/resources/shaders/glsl120/normalmap.fs | 2 +- examples/shaders/resources/shaders/glsl330/normalmap.fs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/shaders/resources/shaders/glsl100/normalmap.fs b/examples/shaders/resources/shaders/glsl100/normalmap.fs index a1b992811..6628f2006 100644 --- a/examples/shaders/resources/shaders/glsl100/normalmap.fs +++ b/examples/shaders/resources/shaders/glsl100/normalmap.fs @@ -52,7 +52,7 @@ void main() float specCo = 0.0; - if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewDir, reflect(-lightDir, normal))), specularExponent); // 16 refers to shine + if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewDir, reflect(-lightDir, normal))), specularExponent); specular += specCo; diff --git a/examples/shaders/resources/shaders/glsl120/normalmap.fs b/examples/shaders/resources/shaders/glsl120/normalmap.fs index 9e7ba5e19..8b12ca226 100644 --- a/examples/shaders/resources/shaders/glsl120/normalmap.fs +++ b/examples/shaders/resources/shaders/glsl120/normalmap.fs @@ -50,7 +50,7 @@ void main() float specCo = 0.0; - if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewDir, reflect(-lightDir, normal))), specularExponent); // 16 refers to shine + if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewDir, reflect(-lightDir, normal))), specularExponent); specular += specCo; diff --git a/examples/shaders/resources/shaders/glsl330/normalmap.fs b/examples/shaders/resources/shaders/glsl330/normalmap.fs index 697423701..005446448 100644 --- a/examples/shaders/resources/shaders/glsl330/normalmap.fs +++ b/examples/shaders/resources/shaders/glsl330/normalmap.fs @@ -54,7 +54,7 @@ void main() float specCo = 0.0; - if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewDir, reflect(-lightDir, normal))), specularExponent); // 16 refers to shine + if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewDir, reflect(-lightDir, normal))), specularExponent); specular += specCo; From ba83bd33f30513b3baa9c20a02d02969a62f446b Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Fri, 27 Mar 2026 09:26:53 +0100 Subject: [PATCH 222/319] simplify `CheckCollisionSpheres` using `Vector3DistanceSqr` (#5695) --- src/rmodels.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index c78930f03..a662a2dc2 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -4085,7 +4085,8 @@ bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, floa */ // Check for distances squared to avoid sqrtf() - if (Vector3DotProduct(Vector3Subtract(center2, center1), Vector3Subtract(center2, center1)) <= (radius1 + radius2)*(radius1 + radius2)) collision = true; + float radSum = radius1 + radius2; + if (Vector3DistanceSqr(center1, center2) <= radSum*radSum) collision = true; return collision; } From 8a7aff509dd13315930884bc8374a44b6b1ead28 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 27 Mar 2026 21:56:29 +0100 Subject: [PATCH 223/319] WARNING: BREAKING: REDESIGNED: `TextInsert()`, `TextReplace()`, `TextReplaceBetween()`, using static buffers Redesign has been conditioned by the usage of those functions on `rexm`, `rpc` and other tools; for many use cases a static buffer is enough and way more comfortable to use. ADDED: `TextInsertAlloc()`, `TextReplaceAlloc()`, `TextReplaceBetweenAlloc()`, alternatives with internal allocations --- CHANGELOG | 28 +++++++--- src/raylib.h | 13 +++-- src/rcore.c | 2 +- src/rtext.c | 139 ++++++++++++++++++++++++++++++++++++++++++++-- tools/rexm/rexm.c | 53 ++++++++---------- 5 files changed, 185 insertions(+), 50 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 053ff3018..cf9bf238d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -21,11 +21,17 @@ KEY CHANGES: Detailed changes: +[rcore] ADDED: `FileRename()`, by @raysan5 +[rcore] ADDED: `FileRemove()`, by @raysan5 +[rcore] ADDED: `FileCopy()`, by @raysan5 +[rcore] ADDED: `FileMove()`, by @raysan5 +[rcore] ADDED: `FileTextReplace()`, by @raysan5 +[rcore] ADDED: `FileTextFindIndex()`, by @raysan5 +[rcore] ADDED: `ComputeSHA256()` (#5264) by @iamahuman1395 +[rcore] ADDED: `GetKeyName()` (#4544) by @lecongsebastien [rcore] ADDED: Logging and file-system functionality from `utils` (#4551) by @raysan5 -WARNING- [rcore] ADDED: Flags set/clear macros: FLAG_SET, FLAG_CLEAR, FLAG_IS_SET (#5169) by @raysan5 [rcore] ADDED: Warnings in case of no platform backend defined by @raysan5 -[rcore] ADDED: `ComputeSHA256()` (#5264) by @iamahuman1395 -[rcore] ADDED: `GetKeyName()` (#4544) by @lecongsebastien [rcore] REMOVED: GIF recording option, added example by @raysan5 -WARNING- [rcore] REMOVED: `CORE.Window.fullscreen` variable, using available flag instead by @raysan5 [rcore] REMOVED: `SetupFramebuffer()`, most platforms do not need it any more by @raysan5 @@ -111,7 +117,7 @@ Detailed changes: [rcore][RGFW] ADDED: New backend option: `PLATFORM_WEB_RGFW` and update RGFW (#4480) by @colleagueRiley [rcore][RGFW] REVIEWED: Added missing Right Control key by @M374LX [rcore][RGFW] REVIEWED: Changed `RGFW_window_eventWait` timeout to -1 by @doggymangc -[rcore][RGFW] REVIEWED: Duplicate entries reemoved from `keyMappingRGFW` (#5242) by @iamahuman1395 +[rcore][RGFW] REVIEWED: Duplicate entries removed from `keyMappingRGFW` (#5242) by @iamahuman1395 [rcore][RGFW] REVIEWED: Fix Escape always closing the window by @M374LX [rcore][RGFW] REVIEWED: Forward declare the windows stuff, prevents failures in GCC (#5269) by @Jeffm2501 [rcore][RGFW] REVIEWED: Requires RGBA8 images as window icons (#5431) by @crisserpl2 @@ -198,7 +204,7 @@ Detailed changes: [rlgl] REVIEWED: `rlActiveDrawBuffers`, fix for OpenGL ES 3.0 (#4605) by @Bigfoot71 [rlgl] REVIEWED: `rlGetPixelDataSize()`, correct compressed data size calculation per blocks #5416 by @raysan5 [rlgl] REVIEWED: `instranceTransform` shader location index #4538 (#4579) by @meadiode -[rlgl] REVIEWED: `SetShaderValueTexture()`, updatee comments (#4703) by @pejorativefox +[rlgl] REVIEWED: `SetShaderValueTexture()`, update comments (#4703) by @pejorativefox [rlgl] REDESIGNED: Avoid program crash if GPU data is tried to be loaded before `InitWindow()` #4751 by @raysan5 -WARNING- [rlgl] REDESIGNED: Shader loading API function names for more consistency #5631 by @raysan5 -WARNING- [rlsw] ADDED: `swGetColorBuffer()` for convenience by @raysan5 @@ -239,6 +245,13 @@ Detailed changes: [rtextures] REVIEWED: `ImageDrawLineEx(), to be able to draw even numbered thicknesses (#5042) by @Sir-Irk [rtextures] REVIEWED: `ImageBlurGaussian()`, fix integer overflow in cast (#5037) by @garrisonhh [rtext] ADDED: `LoadTextLines()`/`UnloadTextLines()` by @raysan5 +[rtext] ADDED: `TextInsertAlloc()`, returns memory allocated string by @raysan5 +[rtext] ADDED: `TextReplace()`, using static buffer by @raysan5 +[rtext] ADDED: `TextReplaceAlloc()`, returns memory allocated string by @raysan5 +[rtext] ADDED: `TextReplaceBetween()`, using static buffer by @raysan5 +[rtext] ADDED: `TextReplaceBetweenAlloc()`, returns memory allocated string by @raysan5 +[rtext] ADDED: `GetTextBetween()`, using static buffer by @raysan5 +[rtext] ADDED: `TextRemoveSpaces()`, using static buffer by @raysan5 [rtext] ADDED: `MeasureTextCodepoints()` for direct measurement of codepoints (#5623) by @creeperblin [rtext] RENAMED: Variable names for consistency, `textLength` (length in bytes) vs `textSize` (measure in pixels) by @raysan5 [rtext] REVIEWED: Support default font loading with no GPU enabled, to be used with Image API @@ -263,11 +276,10 @@ Detailed changes: [rtext] REVIEWED: `GetCodepointCount()`, misuse of cast (#4741) by @sleeptightAnsiC [rtext] REVIEWED: `GenImageFontAtlas()`, memory corruption and invalid size calculation (#5602) by @konakona418 [rtext] REVIEWED: `TextInsert()`, fix bug on insertion (#5644) by @CrackedPixel +[rtext] REVIEWED: `TextInsert()`, use static buffer, easier to use by @raysan5 -WARNING- [rtext] REVIEWED: `TextJoin()`, convert `const char **` to `char**` by @raysan5 -[rtext] REVIEWED: `TextReplace()` and `TextLength()`, avoid using `strcpy()` by @raysan5 -[rtext] REVIEWED: `TextReplace()` by @raysan5 [rtext] REVIEWED: `TextReplace()`, improvements (#5511) by @belan2470 -[rtext] REVIEWED: `TextReplaceBetween()` by @raysan5 +[rtext] REVIEWED: `TextReplace()` and `TextLength()`, avoid using `strcpy()` by @raysan5 [rtext] REVIEWED: `TextSubtext(), fixes (#4759) by @veins1 [rtext] REVIEWED: `TextToPascal()`, fix issue by @raysan5 [rtext] REVIEWED: `TextToFloat()`, remove removed inaccurate comment (#4596) by @hexmaster111 @@ -735,7 +747,7 @@ Detailed changes: [rexm] REVIEWED: `ScanExampleResources()` avoid resources to be saved by the program by @raysan5 [rexm] REVIEWED: `UpdateSourceMetadata()` and `TextReplaceBetween()` by @raysan5 [rexm] REVIEWED: `examples.js` example addition working by @raysan5 -[rexm] REVIEWED: `add` command logic for existing examplee addition by @raysan5 +[rexm] REVIEWED: `add` command logic for existing example addition by @raysan5 [rexm] REVIEWED: Replace example name on project file by @raysan5 [rexm] REVIEWED: Report issues if logs can not be loaded by @raysan5 [rexm] REVIEWED: VS project adding to solution by @raysan5 diff --git a/src/raylib.h b/src/raylib.h index 0c2a0beda..ac5bc414e 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1359,7 +1359,7 @@ RLAPI Image LoadImageFromScreen(void); RLAPI bool IsImageValid(Image image); // Check if an image is valid (data and parameters) RLAPI void UnloadImage(Image image); // Unload image from CPU memory (RAM) RLAPI bool ExportImage(Image image, const char *fileName); // Export image data to file, returns true on success -RLAPI unsigned char *ExportImageToMemory(Image image, const char *fileType, int *fileSize); // Export image to memory buffer +RLAPI unsigned char *ExportImageToMemory(Image image, const char *fileType, int *fileSize); // Export image to memory buffer, memory must be MemFree() RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes, returns true on success // Image generation functions @@ -1528,7 +1528,7 @@ RLAPI const char *CodepointToUTF8(int codepoint, int *utf8Size); // Text strings management functions (no UTF-8 strings, only byte chars) // WARNING 1: Most of these functions use internal static buffers[], it's recommended to store returned data on user-side for re-use -// WARNING 2: Some strings allocate memory internally for the returned strings, those strings must be free by user using MemFree() +// WARNING 2: Some functions allocate memory internally for the returned strings, those strings must be freed by user using MemFree() RLAPI char **LoadTextLines(const char *text, int *count); // Load text as separate lines ('\n') RLAPI void UnloadTextLines(char **text, int lineCount); // Unload text lines RLAPI int TextCopy(char *dst, const char *src); // Copy one string to another, returns bytes copied @@ -1538,9 +1538,12 @@ RLAPI const char *TextFormat(const char *text, ...); RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string RLAPI const char *TextRemoveSpaces(const char *text); // Remove text spaces, concat words RLAPI char *GetTextBetween(const char *text, const char *begin, const char *end); // Get text between two strings -RLAPI char *TextReplace(const char *text, const char *search, const char *replacement); // Replace text string (WARNING: memory must be freed!) -RLAPI char *TextReplaceBetween(const char *text, const char *begin, const char *end, const char *replacement); // Replace text between two specific strings (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 *TextReplace(const char *text, const char *search, const char *replacement); // Replace text string with new string +RLAPI char *TextReplaceAlloc(const char *text, const char *search, const char *replacement); // Replace text string with new string, memory must be MemFree() +RLAPI char *TextReplaceBetween(const char *text, const char *begin, const char *end, const char *replacement); // Replace text between two specific strings +RLAPI char *TextReplaceBetweenAlloc(const char *text, const char *begin, const char *end, const char *replacement); // Replace text between two specific strings, memory must be MemFree() +RLAPI char *TextInsert(const char *text, const char *insert, int position); // Insert text in a defined byte position +RLAPI char *TextInsertAlloc(const char *text, const char *insert, int position); // Insert text in a defined byte position, memory must be MemFree() 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 diff --git a/src/rcore.c b/src/rcore.c index 87df4d5d4..3454b65ba 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2298,7 +2298,7 @@ int FileTextReplace(const char *fileName, const char *search, const char *replac if (FileExists(fileName)) { fileText = LoadFileText(fileName); - fileTextUpdated = TextReplace(fileText, search, replacement); + fileTextUpdated = TextReplaceAlloc(fileText, search, replacement); result = SaveFileText(fileName, fileTextUpdated); MemFree(fileTextUpdated); UnloadFileText(fileText); diff --git a/src/rtext.c b/src/rtext.c index 16fe8e75f..a6ca65113 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1736,8 +1736,6 @@ const char *TextRemoveSpaces(const char *text) // Get text between two strings char *GetTextBetween(const char *text, const char *begin, const char *end) { - #define MAX_TEXT_BETWEEN_SIZE 1024 - static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); @@ -1752,8 +1750,8 @@ char *GetTextBetween(const char *text, const char *begin, const char *end) { endIndex += (beginIndex + beginLen); int len = (endIndex - beginIndex - beginLen); - if (len < (MAX_TEXT_BETWEEN_SIZE - 1)) strncpy(buffer, text + beginIndex + beginLen, len); - else strncpy(buffer, text + beginIndex + beginLen, MAX_TEXT_BETWEEN_SIZE - 1); + if (len < (MAX_TEXT_BUFFER_LENGTH - 1)) strncpy(buffer, text + beginIndex + beginLen, len); + else strncpy(buffer, text + beginIndex + beginLen, MAX_TEXT_BUFFER_LENGTH - 1); } } @@ -1762,8 +1760,74 @@ char *GetTextBetween(const char *text, const char *begin, const char *end) // Replace text string // REQUIRES: strstr(), strncpy() -// WARNING: Allocated memory must be manually freed +// NOTE: Limited text replace functionality, using static string char *TextReplace(const char *text, const char *search, const char *replacement) +{ + static char result[MAX_TEXT_BUFFER_LENGTH] = { 0 }; + memset(result, 0, MAX_TEXT_BUFFER_LENGTH); + + if ((text != NULL) && (search != NULL) && (search[0] != '\0')) + { + if (replacement == NULL) replacement = ""; + + char *insertPoint = NULL; // Next insert point + char *tempPtr = NULL; // Temp pointer + int textLen = 0; // Text string length + int searchLen = 0; // Search string length of (the string to remove) + int replaceLen = 0; // Replacement length (the string to replace by) + int lastReplacePos = 0; // Distance between next search and end of last replace + int count = 0; // Number of replacements + + textLen = TextLength(text); + searchLen = TextLength(search); + replaceLen = TextLength(replacement); + + // Count the number of replacements needed + insertPoint = (char *)text; + for (count = 0; (tempPtr = strstr(insertPoint, search)); count++) insertPoint = tempPtr + searchLen; + + if ((textLen + count*(replaceLen - searchLen)) < (MAX_TEXT_BUFFER_LENGTH - 1)) + { + // TODO: Allow copying data replaced up to maximum buffer size and stop + + tempPtr = result; // Point to result start + + // First time through the loop, all the variable are set correctly from here on, + // - 'temp' points to the end of the result string + // - 'insertPoint' points to the next occurrence of replace in text + // - 'text' points to the remainder of text after "end of replace" + while (count > 0) + { + insertPoint = (char *)strstr(text, search); + lastReplacePos = (int)(insertPoint - text); + + memcpy(tempPtr, text, lastReplacePos); + tempPtr += lastReplacePos; + + if (replaceLen > 0) + { + memcpy(tempPtr, replacement, replaceLen); + tempPtr += replaceLen; + } + + text += (lastReplacePos + searchLen); // Move to next "end of replace" + count--; + } + + // Copy remaind text part after replacement to result (pointed by moving temp) + // NOTE: Text pointer internal copy has been updated along the process + strncpy(tempPtr, text, TextLength(text)); + } + else TRACELOG(LOG_WARNING, "Text with replacement is longer than internal buffer, use TextReplaceAlloc()"); + } + + return result; +} + +// Replace text string +// REQUIRES: strstr(), strncpy() +// WARNING: Allocated memory must be manually freed +char *TextReplaceAlloc(const char *text, const char *search, const char *replacement) { char *result = NULL; @@ -1825,11 +1889,46 @@ char *TextReplace(const char *text, const char *search, const char *replacement) return result; } +// Replace text between two specific strings +// REQUIRES: strncpy() +// NOTE: If (replacement == NULL) removes "begin"[ ]"end" text +char *TextReplaceBetween(const char *text, const char *begin, const char *end, const char *replacement) +{ + static char result[MAX_TEXT_BUFFER_LENGTH] = { 0 }; + memset(result, 0, MAX_TEXT_BUFFER_LENGTH); + + if ((text != NULL) && (begin != NULL) && (end != NULL)) + { + int beginIndex = TextFindIndex(text, begin); + + if (beginIndex > -1) + { + int beginLen = TextLength(begin); + int endIndex = TextFindIndex(text + beginIndex + beginLen, end); + + if (endIndex > -1) + { + endIndex += (beginIndex + beginLen); + + int textLen = TextLength(text); + int replaceLen = (replacement == NULL)? 0 : TextLength(replacement); + int toreplaceLen = endIndex - beginIndex - beginLen; + + strncpy(result, text, beginIndex + beginLen); // Copy first text part + if (replacement != NULL) strncpy(result + beginIndex + beginLen, replacement, replaceLen); // Copy replacement (if provided) + strncpy(result + beginIndex + beginLen + replaceLen, text + endIndex, textLen - endIndex); // Copy end text part + } + } + } + + return result; +} + // Replace text between two specific strings // REQUIRES: strncpy() // NOTE: If (replacement == NULL) remove "begin"[ ]"end" text // WARNING: Returned string must be freed by user -char *TextReplaceBetween(const char *text, const char *begin, const char *end, const char *replacement) +char *TextReplaceBetweenAlloc(const char *text, const char *begin, const char *end, const char *replacement) { char *result = NULL; @@ -1864,6 +1963,34 @@ char *TextReplaceBetween(const char *text, const char *begin, const char *end, c // Insert text in a specific position, moves all text forward // WARNING: Allocated memory must be manually freed char *TextInsert(const char *text, const char *insert, int position) +{ + static char result[MAX_TEXT_BUFFER_LENGTH] = { 0 }; + memset(result, 0, MAX_TEXT_BUFFER_LENGTH); + + if ((text != NULL) && (insert != NULL)) + { + int textLen = TextLength(text); + int insertLen = TextLength(insert); + + if ((textLen + insertLen) < (MAX_TEXT_BUFFER_LENGTH - 1)) + { + // TODO: Allow copying data inserted up to maximum buffer size and stop + + for (int i = 0; i < position; i++) result[i] = text[i]; + for (int i = position; i < insertLen + position; i++) result[i] = insert[i - position]; + for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i]; + + result[textLen + insertLen] = '\0'; // Add EOL + } + else TRACELOG(LOG_WARNING, "Text with inserted string is longer than internal buffer, use TextInserExt()"); + } + + return result; +} + +// Insert text in a specific position, moves all text forward +// WARNING: Allocated memory must be manually freed +char *TextInsertAlloc(const char *text, const char *insert, int position) { char *result = NULL; diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index e4881b633..48a621115 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -469,12 +469,12 @@ int main(int argc, char *argv[]) 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"); - //TextReplace(newExample, "@", "@raysan5"); - //TextReplace(newExample, "", 2025); - //TextReplace(newExample, "", 2025); + exTextUpdated[0] = TextReplaceAlloc(exText + exIndex, "", exCategory); + exTextUpdated[1] = TextReplaceAlloc(exTextUpdated[0], "", exName + strlen(exCategory) + 1); + //TextReplaceAlloc(newExample, "", "Ray"); + //TextReplaceAlloc(newExample, "@", "@raysan5"); + //TextReplaceAlloc(newExample, "", 2025); + //TextReplaceAlloc(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; } @@ -541,8 +541,6 @@ int main(int argc, char *argv[]) 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 @@ -863,9 +861,7 @@ 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])); - FileRemove(TextFormat("%s/%s/%s", exBasePath, exCategory, resPathUpdated)); - RL_FREE(resPathUpdated); + FileRemove(TextFormat("%s/%s/%s", exBasePath, exCategory, TextReplace(resPaths[r], "glsl%i", TextFormat("glsl%i", glslVer[v])))); } } else FileRemove(TextFormat("%s/%s/%s", exBasePath, exCategory, resPaths[r])); @@ -1164,7 +1160,6 @@ int main(int argc, char *argv[]) // Logging missing resources for convenience LOG("WARNING: [%s] Missing resource: %s\n", exInfo->name, resPathUpdated); } - RL_FREE(resPathUpdated); } } else @@ -1577,13 +1572,13 @@ int main(int argc, char *argv[]) " SaveFileText(\"outputLogFileName\", logText);\n" " emscripten_run_script(\"saveFileFromMEMFSToDisk('outputLogFileName','outputLogFileName')\");\n\n" " return 0"; - char *returnReplaceTextUpdated = TextReplace(returnReplaceText, "outputLogFileName", TextFormat("%s.log", exName)); + char *returnReplaceTextUpdated = TextReplacEx(returnReplaceText, "outputLogFileName", TextFormat("%s.log", exName)); char *srcTextUpdated[4] = { 0 }; - srcTextUpdated[0] = TextReplace(srcText, "int main(void)\n{", mainReplaceText); - srcTextUpdated[1] = TextReplace(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)"); - srcTextUpdated[2] = TextReplace(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;"); - srcTextUpdated[3] = TextReplace(srcTextUpdated[2], " return 0", returnReplaceTextUpdated); + srcTextUpdated[0] = TextReplacEx(srcText, "int main(void)\n{", mainReplaceText); + srcTextUpdated[1] = TextReplacEx(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)"); + srcTextUpdated[2] = TextReplacEx(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;"); + srcTextUpdated[3] = TextReplacEx(srcTextUpdated[2], " return 0", returnReplaceTextUpdated); MemFree(returnReplaceTextUpdated); UnloadFileText(srcText); @@ -1633,9 +1628,9 @@ int main(int argc, char *argv[]) " if ((argc > 1) && (argc == 3) && (strcmp(argv[1], \"--frames\") != 0)) requestedTestFrames = atoi(argv[2]);\n"; char *srcTextUpdated[3] = { 0 }; - srcTextUpdated[0] = TextReplace(srcText, "int main(void)\n{", mainReplaceText); - srcTextUpdated[1] = TextReplace(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)"); - srcTextUpdated[2] = TextReplace(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;"); + srcTextUpdated[0] = TextReplacEx(srcText, "int main(void)\n{", mainReplaceText); + srcTextUpdated[1] = TextReplacEx(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)"); + srcTextUpdated[2] = TextReplacEx(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;"); UnloadFileText(srcText); SaveFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), srcTextUpdated[2]); @@ -2042,10 +2037,8 @@ static int UpdateRequiredFiles(void) // 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); + strcpy(resPaths[r], TextReplace(resPaths[r], "glsl%i", "glsl100")); } if (r < (resPathCount - 1)) @@ -2815,7 +2808,7 @@ static void UpdateSourceMetadata(const char *exSrcPath, const rlExampleInfo *inf // Update example header title (line #3 - ALWAYS) // String: "* raylib [shaders] example - texture drawing" - exTextUpdated[0] = TextReplaceBetween(exTextUpdatedPtr, "* raylib [", "\n", + exTextUpdated[0] = TextReplaceBetweenAlloc(exTextUpdatedPtr, "* raylib [", "\n", TextFormat("%s] example - %s", info->category, exNameFormated)); if (exTextUpdated[0] != NULL) exTextUpdatedPtr = exTextUpdated[0]; @@ -2829,13 +2822,13 @@ static void UpdateSourceMetadata(const char *exSrcPath, const rlExampleInfo *inf if (i < info->stars) strcpy(starsText + 3*i, "★"); else strcpy(starsText + 3*i, "☆"); } - exTextUpdated[1] = TextReplaceBetween(exTextUpdatedPtr, "* Example complexity rating: [", "/4\n", + exTextUpdated[1] = TextReplaceBetweenAlloc(exTextUpdatedPtr, "* Example complexity rating: [", "/4\n", TextFormat("%s] %i", starsText, info->stars)); if (exTextUpdated[1] != NULL) exTextUpdatedPtr = exTextUpdated[1]; // Update example creation/update raylib versions // String: "* Example originally created with raylib 2.0, last time updated with raylib 3.7 - exTextUpdated[2] = TextReplaceBetween(exTextUpdatedPtr, "* Example originally created with raylib ", "\n", + exTextUpdated[2] = TextReplaceBetweenAlloc(exTextUpdatedPtr, "* Example originally created with raylib ", "\n", TextFormat("%s, last time updated with raylib %s", info->verCreated, info->verUpdated)); if (exTextUpdated[2] != NULL) exTextUpdatedPtr = exTextUpdated[2]; @@ -2843,27 +2836,27 @@ static void UpdateSourceMetadata(const char *exSrcPath, const rlExampleInfo *inf // String: "* Copyright (c) 2019-2026 Contributor Name (@github_user) and Ramon Santamaria (@raysan5)" if (info->yearCreated == info->yearReviewed) { - exTextUpdated[3] = TextReplaceBetween(exTextUpdatedPtr, "Copyright (c) ", ")", + exTextUpdated[3] = TextReplaceBetweenAlloc(exTextUpdatedPtr, "Copyright (c) ", ")", TextFormat("%i %s (@%s", info->yearCreated, info->author, info->authorGitHub)); if (exTextUpdated[3] != NULL) exTextUpdatedPtr = exTextUpdated[3]; } else { - exTextUpdated[3] = TextReplaceBetween(exTextUpdatedPtr, "Copyright (c) ", ")", + exTextUpdated[3] = TextReplaceBetweenAlloc(exTextUpdatedPtr, "Copyright (c) ", ")", TextFormat("%i-%i %s (@%s", info->yearCreated, info->yearReviewed, info->author, info->authorGitHub)); if (exTextUpdated[3] != NULL) exTextUpdatedPtr = exTextUpdated[3]; } // Update window title // String: "InitWindow(screenWidth, screenHeight, "raylib [shaders] example - texture drawing");" - exTextUpdated[4] = TextReplaceBetween(exTextUpdated[3], "InitWindow(screenWidth, screenHeight, \"", "\");", + exTextUpdated[4] = TextReplaceBetweenAlloc(exTextUpdated[3], "InitWindow(screenWidth, screenHeight, \"", "\");", TextFormat("raylib [%s] example - %s", info->category, exNameFormated)); if (exTextUpdated[4] != NULL) exTextUpdatedPtr = exTextUpdated[4]; // Update contributors names // String: "* Example contributed by Contributor Name (@github_user) and reviewed by Ramon Santamaria (@raysan5)" // WARNING: Not all examples are contributed by someone, so the result of this replace can be NULL (string not found) - exTextUpdated[5] = TextReplaceBetween(exTextUpdatedPtr, "* Example contributed by ", ")", + exTextUpdated[5] = TextReplaceBetweenAlloc(exTextUpdatedPtr, "* Example contributed by ", ")", TextFormat("%s (@%s", info->author, info->authorGitHub)); if (exTextUpdated[5] != NULL) exTextUpdatedPtr = exTextUpdated[5]; From 990a5e0cb47c3fd5f89cc3b33cffec68326a66f3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 27 Mar 2026 20:56:45 +0000 Subject: [PATCH 224/319] rlparser: update raylib_api.* by CI --- tools/rlparser/output/raylib_api.json | 69 +++++- tools/rlparser/output/raylib_api.lua | 39 ++- tools/rlparser/output/raylib_api.txt | 336 ++++++++++++++------------ tools/rlparser/output/raylib_api.xml | 26 +- 4 files changed, 300 insertions(+), 170 deletions(-) diff --git a/tools/rlparser/output/raylib_api.json b/tools/rlparser/output/raylib_api.json index d4ff5598b..1fbc87a9d 100644 --- a/tools/rlparser/output/raylib_api.json +++ b/tools/rlparser/output/raylib_api.json @@ -7231,7 +7231,7 @@ }, { "name": "ExportImageToMemory", - "description": "Export image to memory buffer", + "description": "Export image to memory buffer, memory must be MemFree()", "returnType": "unsigned char *", "params": [ { @@ -9946,7 +9946,26 @@ }, { "name": "TextReplace", - "description": "Replace text string (WARNING: memory must be freed!)", + "description": "Replace text string with new string", + "returnType": "char *", + "params": [ + { + "type": "const char *", + "name": "text" + }, + { + "type": "const char *", + "name": "search" + }, + { + "type": "const char *", + "name": "replacement" + } + ] + }, + { + "name": "TextReplaceAlloc", + "description": "Replace text string with new string, memory must be MemFree()", "returnType": "char *", "params": [ { @@ -9965,7 +9984,30 @@ }, { "name": "TextReplaceBetween", - "description": "Replace text between two specific strings (WARNING: memory must be freed!)", + "description": "Replace text between two specific strings", + "returnType": "char *", + "params": [ + { + "type": "const char *", + "name": "text" + }, + { + "type": "const char *", + "name": "begin" + }, + { + "type": "const char *", + "name": "end" + }, + { + "type": "const char *", + "name": "replacement" + } + ] + }, + { + "name": "TextReplaceBetweenAlloc", + "description": "Replace text between two specific strings, memory must be MemFree()", "returnType": "char *", "params": [ { @@ -9988,7 +10030,26 @@ }, { "name": "TextInsert", - "description": "Insert text in a position (WARNING: memory must be freed!)", + "description": "Insert text in a defined byte position", + "returnType": "char *", + "params": [ + { + "type": "const char *", + "name": "text" + }, + { + "type": "const char *", + "name": "insert" + }, + { + "type": "int", + "name": "position" + } + ] + }, + { + "name": "TextInsertAlloc", + "description": "Insert text in a defined byte position, memory must be MemFree()", "returnType": "char *", "params": [ { diff --git a/tools/rlparser/output/raylib_api.lua b/tools/rlparser/output/raylib_api.lua index 5e22916b3..398e1b177 100644 --- a/tools/rlparser/output/raylib_api.lua +++ b/tools/rlparser/output/raylib_api.lua @@ -5596,7 +5596,7 @@ return { }, { name = "ExportImageToMemory", - description = "Export image to memory buffer", + description = "Export image to memory buffer, memory must be MemFree()", returnType = "unsigned char *", params = { {type = "Image", name = "image"}, @@ -7045,7 +7045,17 @@ return { }, { name = "TextReplace", - description = "Replace text string (WARNING: memory must be freed!)", + description = "Replace text string with new string", + returnType = "char *", + params = { + {type = "const char *", name = "text"}, + {type = "const char *", name = "search"}, + {type = "const char *", name = "replacement"} + } + }, + { + name = "TextReplaceAlloc", + description = "Replace text string with new string, memory must be MemFree()", returnType = "char *", params = { {type = "const char *", name = "text"}, @@ -7055,7 +7065,18 @@ return { }, { name = "TextReplaceBetween", - description = "Replace text between two specific strings (WARNING: memory must be freed!)", + description = "Replace text between two specific strings", + returnType = "char *", + params = { + {type = "const char *", name = "text"}, + {type = "const char *", name = "begin"}, + {type = "const char *", name = "end"}, + {type = "const char *", name = "replacement"} + } + }, + { + name = "TextReplaceBetweenAlloc", + description = "Replace text between two specific strings, memory must be MemFree()", returnType = "char *", params = { {type = "const char *", name = "text"}, @@ -7066,7 +7087,17 @@ return { }, { name = "TextInsert", - description = "Insert text in a position (WARNING: memory must be freed!)", + description = "Insert text in a defined byte position", + returnType = "char *", + params = { + {type = "const char *", name = "text"}, + {type = "const char *", name = "insert"}, + {type = "int", name = "position"} + } + }, + { + name = "TextInsertAlloc", + description = "Insert text in a defined byte position, memory must be MemFree()", returnType = "char *", params = { {type = "const char *", name = "text"}, diff --git a/tools/rlparser/output/raylib_api.txt b/tools/rlparser/output/raylib_api.txt index 7943b28f3..d3cd7bcdc 100644 --- a/tools/rlparser/output/raylib_api.txt +++ b/tools/rlparser/output/raylib_api.txt @@ -1000,7 +1000,7 @@ Callback 006: AudioCallback() (2 input parameters) Param[1]: bufferData (type: void *) Param[2]: frames (type: unsigned int) -Functions found: 597 +Functions found: 600 Function 001: InitWindow() (3 input parameters) Name: InitWindow @@ -2800,7 +2800,7 @@ Function 297: ExportImage() (2 input parameters) Function 298: ExportImageToMemory() (3 input parameters) Name: ExportImageToMemory Return type: unsigned char * - Description: Export image to memory buffer + Description: Export image to memory buffer, memory must be MemFree() Param[1]: image (type: Image) Param[2]: fileType (type: const char *) Param[3]: fileSize (type: int *) @@ -3811,101 +3811,123 @@ Function 444: GetTextBetween() (3 input parameters) Function 445: TextReplace() (3 input parameters) Name: TextReplace Return type: char * - Description: Replace text string (WARNING: memory must be freed!) + Description: Replace text string with new string Param[1]: text (type: const char *) Param[2]: search (type: const char *) Param[3]: replacement (type: const char *) -Function 446: TextReplaceBetween() (4 input parameters) +Function 446: TextReplaceAlloc() (3 input parameters) + Name: TextReplaceAlloc + Return type: char * + Description: Replace text string with new string, memory must be MemFree() + Param[1]: text (type: const char *) + Param[2]: search (type: const char *) + Param[3]: replacement (type: const char *) +Function 447: TextReplaceBetween() (4 input parameters) Name: TextReplaceBetween Return type: char * - Description: Replace text between two specific strings (WARNING: memory must be freed!) + Description: Replace text between two specific strings Param[1]: text (type: const char *) Param[2]: begin (type: const char *) Param[3]: end (type: const char *) Param[4]: replacement (type: const char *) -Function 447: TextInsert() (3 input parameters) +Function 448: TextReplaceBetweenAlloc() (4 input parameters) + Name: TextReplaceBetweenAlloc + Return type: char * + Description: Replace text between two specific strings, memory must be MemFree() + Param[1]: text (type: const char *) + Param[2]: begin (type: const char *) + Param[3]: end (type: const char *) + Param[4]: replacement (type: const char *) +Function 449: TextInsert() (3 input parameters) Name: TextInsert Return type: char * - Description: Insert text in a position (WARNING: memory must be freed!) + Description: Insert text in a defined byte position Param[1]: text (type: const char *) Param[2]: insert (type: const char *) Param[3]: position (type: int) -Function 448: TextJoin() (3 input parameters) +Function 450: TextInsertAlloc() (3 input parameters) + Name: TextInsertAlloc + Return type: char * + Description: Insert text in a defined byte position, memory must be MemFree() + Param[1]: text (type: const char *) + Param[2]: insert (type: const char *) + Param[3]: position (type: int) +Function 451: 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 449: TextSplit() (3 input parameters) +Function 452: TextSplit() (3 input parameters) Name: TextSplit Return type: char ** 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 *) -Function 450: TextAppend() (3 input parameters) +Function 453: 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 451: TextFindIndex() (2 input parameters) +Function 454: TextFindIndex() (2 input parameters) Name: TextFindIndex Return type: int Description: Find first text occurrence within a string, -1 if not found Param[1]: text (type: const char *) Param[2]: search (type: const char *) -Function 452: TextToUpper() (1 input parameters) +Function 455: TextToUpper() (1 input parameters) Name: TextToUpper Return type: char * Description: Get upper case version of provided string Param[1]: text (type: const char *) -Function 453: TextToLower() (1 input parameters) +Function 456: TextToLower() (1 input parameters) Name: TextToLower Return type: char * Description: Get lower case version of provided string Param[1]: text (type: const char *) -Function 454: TextToPascal() (1 input parameters) +Function 457: 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 455: TextToSnake() (1 input parameters) +Function 458: 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 456: TextToCamel() (1 input parameters) +Function 459: 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 457: TextToInteger() (1 input parameters) +Function 460: TextToInteger() (1 input parameters) Name: TextToInteger Return type: int Description: Get integer value from text Param[1]: text (type: const char *) -Function 458: TextToFloat() (1 input parameters) +Function 461: TextToFloat() (1 input parameters) Name: TextToFloat Return type: float Description: Get float value from text Param[1]: text (type: const char *) -Function 459: DrawLine3D() (3 input parameters) +Function 462: 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 460: DrawPoint3D() (2 input parameters) +Function 463: 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 461: DrawCircle3D() (5 input parameters) +Function 464: DrawCircle3D() (5 input parameters) Name: DrawCircle3D Return type: void Description: Draw a circle in 3D world space @@ -3914,7 +3936,7 @@ Function 461: DrawCircle3D() (5 input parameters) Param[3]: rotationAxis (type: Vector3) Param[4]: rotationAngle (type: float) Param[5]: color (type: Color) -Function 462: DrawTriangle3D() (4 input parameters) +Function 465: DrawTriangle3D() (4 input parameters) Name: DrawTriangle3D Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -3922,14 +3944,14 @@ Function 462: DrawTriangle3D() (4 input parameters) Param[2]: v2 (type: Vector3) Param[3]: v3 (type: Vector3) Param[4]: color (type: Color) -Function 463: DrawTriangleStrip3D() (3 input parameters) +Function 466: 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 464: DrawCube() (5 input parameters) +Function 467: DrawCube() (5 input parameters) Name: DrawCube Return type: void Description: Draw cube @@ -3938,14 +3960,14 @@ Function 464: DrawCube() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 465: DrawCubeV() (3 input parameters) +Function 468: 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 466: DrawCubeWires() (5 input parameters) +Function 469: DrawCubeWires() (5 input parameters) Name: DrawCubeWires Return type: void Description: Draw cube wires @@ -3954,21 +3976,21 @@ Function 466: DrawCubeWires() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 467: DrawCubeWiresV() (3 input parameters) +Function 470: 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 468: DrawSphere() (3 input parameters) +Function 471: 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 469: DrawSphereEx() (5 input parameters) +Function 472: DrawSphereEx() (5 input parameters) Name: DrawSphereEx Return type: void Description: Draw sphere with extended parameters @@ -3977,7 +3999,7 @@ Function 469: DrawSphereEx() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 470: DrawSphereWires() (5 input parameters) +Function 473: DrawSphereWires() (5 input parameters) Name: DrawSphereWires Return type: void Description: Draw sphere wires @@ -3986,7 +4008,7 @@ Function 470: DrawSphereWires() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 471: DrawCylinder() (6 input parameters) +Function 474: DrawCylinder() (6 input parameters) Name: DrawCylinder Return type: void Description: Draw a cylinder/cone @@ -3996,7 +4018,7 @@ Function 471: DrawCylinder() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 472: DrawCylinderEx() (6 input parameters) +Function 475: DrawCylinderEx() (6 input parameters) Name: DrawCylinderEx Return type: void Description: Draw a cylinder with base at startPos and top at endPos @@ -4006,7 +4028,7 @@ Function 472: DrawCylinderEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 473: DrawCylinderWires() (6 input parameters) +Function 476: DrawCylinderWires() (6 input parameters) Name: DrawCylinderWires Return type: void Description: Draw a cylinder/cone wires @@ -4016,7 +4038,7 @@ Function 473: DrawCylinderWires() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 474: DrawCylinderWiresEx() (6 input parameters) +Function 477: DrawCylinderWiresEx() (6 input parameters) Name: DrawCylinderWiresEx Return type: void Description: Draw a cylinder wires with base at startPos and top at endPos @@ -4026,7 +4048,7 @@ Function 474: DrawCylinderWiresEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 475: DrawCapsule() (6 input parameters) +Function 478: DrawCapsule() (6 input parameters) Name: DrawCapsule Return type: void Description: Draw a capsule with the center of its sphere caps at startPos and endPos @@ -4036,7 +4058,7 @@ Function 475: DrawCapsule() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 476: DrawCapsuleWires() (6 input parameters) +Function 479: DrawCapsuleWires() (6 input parameters) Name: DrawCapsuleWires Return type: void Description: Draw capsule wireframe with the center of its sphere caps at startPos and endPos @@ -4046,51 +4068,51 @@ Function 476: DrawCapsuleWires() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 477: DrawPlane() (3 input parameters) +Function 480: 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 478: DrawRay() (2 input parameters) +Function 481: 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 479: DrawGrid() (2 input parameters) +Function 482: 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 480: LoadModel() (1 input parameters) +Function 483: LoadModel() (1 input parameters) Name: LoadModel Return type: Model Description: Load model from files (meshes and materials) Param[1]: fileName (type: const char *) -Function 481: LoadModelFromMesh() (1 input parameters) +Function 484: LoadModelFromMesh() (1 input parameters) Name: LoadModelFromMesh Return type: Model Description: Load model from generated mesh (default material) Param[1]: mesh (type: Mesh) -Function 482: IsModelValid() (1 input parameters) +Function 485: 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 483: UnloadModel() (1 input parameters) +Function 486: 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 484: GetModelBoundingBox() (1 input parameters) +Function 487: GetModelBoundingBox() (1 input parameters) Name: GetModelBoundingBox Return type: BoundingBox Description: Compute model bounding box limits (considers all meshes) Param[1]: model (type: Model) -Function 485: DrawModel() (4 input parameters) +Function 488: DrawModel() (4 input parameters) Name: DrawModel Return type: void Description: Draw a model (with texture if set) @@ -4098,7 +4120,7 @@ Function 485: DrawModel() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 486: DrawModelEx() (6 input parameters) +Function 489: DrawModelEx() (6 input parameters) Name: DrawModelEx Return type: void Description: Draw a model with extended parameters @@ -4108,7 +4130,7 @@ Function 486: DrawModelEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 487: DrawModelWires() (4 input parameters) +Function 490: DrawModelWires() (4 input parameters) Name: DrawModelWires Return type: void Description: Draw a model wires (with texture if set) @@ -4116,7 +4138,7 @@ Function 487: DrawModelWires() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 488: DrawModelWiresEx() (6 input parameters) +Function 491: DrawModelWiresEx() (6 input parameters) Name: DrawModelWiresEx Return type: void Description: Draw a model wires (with texture if set) with extended parameters @@ -4126,13 +4148,13 @@ Function 488: DrawModelWiresEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 489: DrawBoundingBox() (2 input parameters) +Function 492: 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 490: DrawBillboard() (5 input parameters) +Function 493: DrawBillboard() (5 input parameters) Name: DrawBillboard Return type: void Description: Draw a billboard texture @@ -4141,7 +4163,7 @@ Function 490: DrawBillboard() (5 input parameters) Param[3]: position (type: Vector3) Param[4]: scale (type: float) Param[5]: tint (type: Color) -Function 491: DrawBillboardRec() (6 input parameters) +Function 494: DrawBillboardRec() (6 input parameters) Name: DrawBillboardRec Return type: void Description: Draw a billboard texture defined by source @@ -4151,7 +4173,7 @@ Function 491: DrawBillboardRec() (6 input parameters) Param[4]: position (type: Vector3) Param[5]: size (type: Vector2) Param[6]: tint (type: Color) -Function 492: DrawBillboardPro() (9 input parameters) +Function 495: DrawBillboardPro() (9 input parameters) Name: DrawBillboardPro Return type: void Description: Draw a billboard texture defined by source and rotation @@ -4164,13 +4186,13 @@ Function 492: DrawBillboardPro() (9 input parameters) Param[7]: origin (type: Vector2) Param[8]: rotation (type: float) Param[9]: tint (type: Color) -Function 493: UploadMesh() (2 input parameters) +Function 496: 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 494: UpdateMeshBuffer() (5 input parameters) +Function 497: UpdateMeshBuffer() (5 input parameters) Name: UpdateMeshBuffer Return type: void Description: Update mesh vertex data in GPU for a specific buffer index @@ -4179,19 +4201,19 @@ Function 494: UpdateMeshBuffer() (5 input parameters) Param[3]: data (type: const void *) Param[4]: dataSize (type: int) Param[5]: offset (type: int) -Function 495: UnloadMesh() (1 input parameters) +Function 498: UnloadMesh() (1 input parameters) Name: UnloadMesh Return type: void Description: Unload mesh data from CPU and GPU Param[1]: mesh (type: Mesh) -Function 496: DrawMesh() (3 input parameters) +Function 499: 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 497: DrawMeshInstanced() (4 input parameters) +Function 500: DrawMeshInstanced() (4 input parameters) Name: DrawMeshInstanced Return type: void Description: Draw multiple mesh instances with material and different transforms @@ -4199,35 +4221,35 @@ Function 497: DrawMeshInstanced() (4 input parameters) Param[2]: material (type: Material) Param[3]: transforms (type: const Matrix *) Param[4]: instances (type: int) -Function 498: GetMeshBoundingBox() (1 input parameters) +Function 501: GetMeshBoundingBox() (1 input parameters) Name: GetMeshBoundingBox Return type: BoundingBox Description: Compute mesh bounding box limits Param[1]: mesh (type: Mesh) -Function 499: GenMeshTangents() (1 input parameters) +Function 502: GenMeshTangents() (1 input parameters) Name: GenMeshTangents Return type: void Description: Compute mesh tangents Param[1]: mesh (type: Mesh *) -Function 500: ExportMesh() (2 input parameters) +Function 503: 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 501: ExportMeshAsCode() (2 input parameters) +Function 504: 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 502: GenMeshPoly() (2 input parameters) +Function 505: GenMeshPoly() (2 input parameters) Name: GenMeshPoly Return type: Mesh Description: Generate polygonal mesh Param[1]: sides (type: int) Param[2]: radius (type: float) -Function 503: GenMeshPlane() (4 input parameters) +Function 506: GenMeshPlane() (4 input parameters) Name: GenMeshPlane Return type: Mesh Description: Generate plane mesh (with subdivisions) @@ -4235,42 +4257,42 @@ Function 503: GenMeshPlane() (4 input parameters) Param[2]: length (type: float) Param[3]: resX (type: int) Param[4]: resZ (type: int) -Function 504: GenMeshCube() (3 input parameters) +Function 507: 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 505: GenMeshSphere() (3 input parameters) +Function 508: 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 506: GenMeshHemiSphere() (3 input parameters) +Function 509: 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 507: GenMeshCylinder() (3 input parameters) +Function 510: 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 508: GenMeshCone() (3 input parameters) +Function 511: 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 509: GenMeshTorus() (4 input parameters) +Function 512: GenMeshTorus() (4 input parameters) Name: GenMeshTorus Return type: Mesh Description: Generate torus mesh @@ -4278,7 +4300,7 @@ Function 509: GenMeshTorus() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 510: GenMeshKnot() (4 input parameters) +Function 513: GenMeshKnot() (4 input parameters) Name: GenMeshKnot Return type: Mesh Description: Generate trefoil knot mesh @@ -4286,67 +4308,67 @@ Function 510: GenMeshKnot() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 511: GenMeshHeightmap() (2 input parameters) +Function 514: 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 512: GenMeshCubicmap() (2 input parameters) +Function 515: 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 513: LoadMaterials() (2 input parameters) +Function 516: 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 514: LoadMaterialDefault() (0 input parameters) +Function 517: LoadMaterialDefault() (0 input parameters) Name: LoadMaterialDefault Return type: Material Description: Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) No input parameters -Function 515: IsMaterialValid() (1 input parameters) +Function 518: 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 516: UnloadMaterial() (1 input parameters) +Function 519: UnloadMaterial() (1 input parameters) Name: UnloadMaterial Return type: void Description: Unload material from GPU memory (VRAM) Param[1]: material (type: Material) -Function 517: SetMaterialTexture() (3 input parameters) +Function 520: 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 518: SetModelMeshMaterial() (3 input parameters) +Function 521: 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 519: LoadModelAnimations() (2 input parameters) +Function 522: 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 520: UpdateModelAnimation() (3 input parameters) +Function 523: UpdateModelAnimation() (3 input parameters) Name: UpdateModelAnimation Return type: void Description: Update model animation pose (vertex buffers and bone matrices) Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: float) -Function 521: UpdateModelAnimationEx() (6 input parameters) +Function 524: UpdateModelAnimationEx() (6 input parameters) Name: UpdateModelAnimationEx Return type: void Description: Update model animation pose, blending two animations @@ -4356,19 +4378,19 @@ Function 521: UpdateModelAnimationEx() (6 input parameters) Param[4]: animB (type: ModelAnimation) Param[5]: frameB (type: float) Param[6]: blend (type: float) -Function 522: UnloadModelAnimations() (2 input parameters) +Function 525: 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 523: IsModelAnimationValid() (2 input parameters) +Function 526: 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 524: CheckCollisionSpheres() (4 input parameters) +Function 527: CheckCollisionSpheres() (4 input parameters) Name: CheckCollisionSpheres Return type: bool Description: Check collision between two spheres @@ -4376,40 +4398,40 @@ Function 524: CheckCollisionSpheres() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector3) Param[4]: radius2 (type: float) -Function 525: CheckCollisionBoxes() (2 input parameters) +Function 528: 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 526: CheckCollisionBoxSphere() (3 input parameters) +Function 529: 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 527: GetRayCollisionSphere() (3 input parameters) +Function 530: 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 528: GetRayCollisionBox() (2 input parameters) +Function 531: 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 529: GetRayCollisionMesh() (3 input parameters) +Function 532: 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 530: GetRayCollisionTriangle() (4 input parameters) +Function 533: GetRayCollisionTriangle() (4 input parameters) Name: GetRayCollisionTriangle Return type: RayCollision Description: Get collision info between ray and triangle @@ -4417,7 +4439,7 @@ Function 530: GetRayCollisionTriangle() (4 input parameters) Param[2]: p1 (type: Vector3) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) -Function 531: GetRayCollisionQuad() (5 input parameters) +Function 534: GetRayCollisionQuad() (5 input parameters) Name: GetRayCollisionQuad Return type: RayCollision Description: Get collision info between ray and quad @@ -4426,158 +4448,158 @@ Function 531: GetRayCollisionQuad() (5 input parameters) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) Param[5]: p4 (type: Vector3) -Function 532: InitAudioDevice() (0 input parameters) +Function 535: InitAudioDevice() (0 input parameters) Name: InitAudioDevice Return type: void Description: Initialize audio device and context No input parameters -Function 533: CloseAudioDevice() (0 input parameters) +Function 536: CloseAudioDevice() (0 input parameters) Name: CloseAudioDevice Return type: void Description: Close the audio device and context No input parameters -Function 534: IsAudioDeviceReady() (0 input parameters) +Function 537: IsAudioDeviceReady() (0 input parameters) Name: IsAudioDeviceReady Return type: bool Description: Check if audio device has been initialized successfully No input parameters -Function 535: SetMasterVolume() (1 input parameters) +Function 538: SetMasterVolume() (1 input parameters) Name: SetMasterVolume Return type: void Description: Set master volume (listener) Param[1]: volume (type: float) -Function 536: GetMasterVolume() (0 input parameters) +Function 539: GetMasterVolume() (0 input parameters) Name: GetMasterVolume Return type: float Description: Get master volume (listener) No input parameters -Function 537: LoadWave() (1 input parameters) +Function 540: LoadWave() (1 input parameters) Name: LoadWave Return type: Wave Description: Load wave data from file Param[1]: fileName (type: const char *) -Function 538: LoadWaveFromMemory() (3 input parameters) +Function 541: 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 539: IsWaveValid() (1 input parameters) +Function 542: 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 540: LoadSound() (1 input parameters) +Function 543: LoadSound() (1 input parameters) Name: LoadSound Return type: Sound Description: Load sound from file Param[1]: fileName (type: const char *) -Function 541: LoadSoundFromWave() (1 input parameters) +Function 544: LoadSoundFromWave() (1 input parameters) Name: LoadSoundFromWave Return type: Sound Description: Load sound from wave data Param[1]: wave (type: Wave) -Function 542: LoadSoundAlias() (1 input parameters) +Function 545: 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 543: IsSoundValid() (1 input parameters) +Function 546: 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 544: UpdateSound() (3 input parameters) +Function 547: UpdateSound() (3 input parameters) Name: UpdateSound Return type: void Description: Update sound buffer with new data (default data format: 32 bit float, stereo) Param[1]: sound (type: Sound) Param[2]: data (type: const void *) Param[3]: sampleCount (type: int) -Function 545: UnloadWave() (1 input parameters) +Function 548: UnloadWave() (1 input parameters) Name: UnloadWave Return type: void Description: Unload wave data Param[1]: wave (type: Wave) -Function 546: UnloadSound() (1 input parameters) +Function 549: UnloadSound() (1 input parameters) Name: UnloadSound Return type: void Description: Unload sound Param[1]: sound (type: Sound) -Function 547: UnloadSoundAlias() (1 input parameters) +Function 550: 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 548: ExportWave() (2 input parameters) +Function 551: 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 549: ExportWaveAsCode() (2 input parameters) +Function 552: 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 550: PlaySound() (1 input parameters) +Function 553: PlaySound() (1 input parameters) Name: PlaySound Return type: void Description: Play a sound Param[1]: sound (type: Sound) -Function 551: StopSound() (1 input parameters) +Function 554: StopSound() (1 input parameters) Name: StopSound Return type: void Description: Stop playing a sound Param[1]: sound (type: Sound) -Function 552: PauseSound() (1 input parameters) +Function 555: PauseSound() (1 input parameters) Name: PauseSound Return type: void Description: Pause a sound Param[1]: sound (type: Sound) -Function 553: ResumeSound() (1 input parameters) +Function 556: ResumeSound() (1 input parameters) Name: ResumeSound Return type: void Description: Resume a paused sound Param[1]: sound (type: Sound) -Function 554: IsSoundPlaying() (1 input parameters) +Function 557: IsSoundPlaying() (1 input parameters) Name: IsSoundPlaying Return type: bool Description: Check if a sound is currently playing Param[1]: sound (type: Sound) -Function 555: SetSoundVolume() (2 input parameters) +Function 558: 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 556: SetSoundPitch() (2 input parameters) +Function 559: 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 557: SetSoundPan() (2 input parameters) +Function 560: SetSoundPan() (2 input parameters) Name: SetSoundPan Return type: void Description: Set pan for a sound (-1.0 left, 0.0 center, 1.0 right) Param[1]: sound (type: Sound) Param[2]: pan (type: float) -Function 558: WaveCopy() (1 input parameters) +Function 561: WaveCopy() (1 input parameters) Name: WaveCopy Return type: Wave Description: Copy a wave to a new wave Param[1]: wave (type: Wave) -Function 559: WaveCrop() (3 input parameters) +Function 562: 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 560: WaveFormat() (4 input parameters) +Function 563: WaveFormat() (4 input parameters) Name: WaveFormat Return type: void Description: Convert wave data to desired format @@ -4585,203 +4607,203 @@ Function 560: WaveFormat() (4 input parameters) Param[2]: sampleRate (type: int) Param[3]: sampleSize (type: int) Param[4]: channels (type: int) -Function 561: LoadWaveSamples() (1 input parameters) +Function 564: 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 562: UnloadWaveSamples() (1 input parameters) +Function 565: UnloadWaveSamples() (1 input parameters) Name: UnloadWaveSamples Return type: void Description: Unload samples data loaded with LoadWaveSamples() Param[1]: samples (type: float *) -Function 563: LoadMusicStream() (1 input parameters) +Function 566: LoadMusicStream() (1 input parameters) Name: LoadMusicStream Return type: Music Description: Load music stream from file Param[1]: fileName (type: const char *) -Function 564: LoadMusicStreamFromMemory() (3 input parameters) +Function 567: 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 565: IsMusicValid() (1 input parameters) +Function 568: 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 566: UnloadMusicStream() (1 input parameters) +Function 569: UnloadMusicStream() (1 input parameters) Name: UnloadMusicStream Return type: void Description: Unload music stream Param[1]: music (type: Music) -Function 567: PlayMusicStream() (1 input parameters) +Function 570: PlayMusicStream() (1 input parameters) Name: PlayMusicStream Return type: void Description: Start music playing Param[1]: music (type: Music) -Function 568: IsMusicStreamPlaying() (1 input parameters) +Function 571: IsMusicStreamPlaying() (1 input parameters) Name: IsMusicStreamPlaying Return type: bool Description: Check if music is playing Param[1]: music (type: Music) -Function 569: UpdateMusicStream() (1 input parameters) +Function 572: UpdateMusicStream() (1 input parameters) Name: UpdateMusicStream Return type: void Description: Updates buffers for music streaming Param[1]: music (type: Music) -Function 570: StopMusicStream() (1 input parameters) +Function 573: StopMusicStream() (1 input parameters) Name: StopMusicStream Return type: void Description: Stop music playing Param[1]: music (type: Music) -Function 571: PauseMusicStream() (1 input parameters) +Function 574: PauseMusicStream() (1 input parameters) Name: PauseMusicStream Return type: void Description: Pause music playing Param[1]: music (type: Music) -Function 572: ResumeMusicStream() (1 input parameters) +Function 575: ResumeMusicStream() (1 input parameters) Name: ResumeMusicStream Return type: void Description: Resume playing paused music Param[1]: music (type: Music) -Function 573: SeekMusicStream() (2 input parameters) +Function 576: 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 574: SetMusicVolume() (2 input parameters) +Function 577: 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 575: SetMusicPitch() (2 input parameters) +Function 578: 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 576: SetMusicPan() (2 input parameters) +Function 579: SetMusicPan() (2 input parameters) Name: SetMusicPan Return type: void Description: Set pan for a music (-1.0 left, 0.0 center, 1.0 right) Param[1]: music (type: Music) Param[2]: pan (type: float) -Function 577: GetMusicTimeLength() (1 input parameters) +Function 580: GetMusicTimeLength() (1 input parameters) Name: GetMusicTimeLength Return type: float Description: Get music time length (in seconds) Param[1]: music (type: Music) -Function 578: GetMusicTimePlayed() (1 input parameters) +Function 581: GetMusicTimePlayed() (1 input parameters) Name: GetMusicTimePlayed Return type: float Description: Get current music time played (in seconds) Param[1]: music (type: Music) -Function 579: LoadAudioStream() (3 input parameters) +Function 582: 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 580: IsAudioStreamValid() (1 input parameters) +Function 583: 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 581: UnloadAudioStream() (1 input parameters) +Function 584: UnloadAudioStream() (1 input parameters) Name: UnloadAudioStream Return type: void Description: Unload audio stream and free memory Param[1]: stream (type: AudioStream) -Function 582: UpdateAudioStream() (3 input parameters) +Function 585: 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 583: IsAudioStreamProcessed() (1 input parameters) +Function 586: IsAudioStreamProcessed() (1 input parameters) Name: IsAudioStreamProcessed Return type: bool Description: Check if any audio stream buffers requires refill Param[1]: stream (type: AudioStream) -Function 584: PlayAudioStream() (1 input parameters) +Function 587: PlayAudioStream() (1 input parameters) Name: PlayAudioStream Return type: void Description: Play audio stream Param[1]: stream (type: AudioStream) -Function 585: PauseAudioStream() (1 input parameters) +Function 588: PauseAudioStream() (1 input parameters) Name: PauseAudioStream Return type: void Description: Pause audio stream Param[1]: stream (type: AudioStream) -Function 586: ResumeAudioStream() (1 input parameters) +Function 589: ResumeAudioStream() (1 input parameters) Name: ResumeAudioStream Return type: void Description: Resume audio stream Param[1]: stream (type: AudioStream) -Function 587: IsAudioStreamPlaying() (1 input parameters) +Function 590: IsAudioStreamPlaying() (1 input parameters) Name: IsAudioStreamPlaying Return type: bool Description: Check if audio stream is playing Param[1]: stream (type: AudioStream) -Function 588: StopAudioStream() (1 input parameters) +Function 591: StopAudioStream() (1 input parameters) Name: StopAudioStream Return type: void Description: Stop audio stream Param[1]: stream (type: AudioStream) -Function 589: SetAudioStreamVolume() (2 input parameters) +Function 592: 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 590: SetAudioStreamPitch() (2 input parameters) +Function 593: 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 591: SetAudioStreamPan() (2 input parameters) +Function 594: SetAudioStreamPan() (2 input parameters) Name: SetAudioStreamPan Return type: void Description: Set pan for audio stream (-1.0 to 1.0 range, 0.0 is centered) Param[1]: stream (type: AudioStream) Param[2]: pan (type: float) -Function 592: SetAudioStreamBufferSizeDefault() (1 input parameters) +Function 595: SetAudioStreamBufferSizeDefault() (1 input parameters) Name: SetAudioStreamBufferSizeDefault Return type: void Description: Default size for new audio streams Param[1]: size (type: int) -Function 593: SetAudioStreamCallback() (2 input parameters) +Function 596: 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 594: AttachAudioStreamProcessor() (2 input parameters) +Function 597: 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 595: DetachAudioStreamProcessor() (2 input parameters) +Function 598: 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 596: AttachAudioMixedProcessor() (1 input parameters) +Function 599: 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 597: DetachAudioMixedProcessor() (1 input parameters) +Function 600: DetachAudioMixedProcessor() (1 input parameters) Name: DetachAudioMixedProcessor Return type: void Description: Detach audio stream processor from the entire audio pipeline diff --git a/tools/rlparser/output/raylib_api.xml b/tools/rlparser/output/raylib_api.xml index 408ff18c7..62d8bdfaf 100644 --- a/tools/rlparser/output/raylib_api.xml +++ b/tools/rlparser/output/raylib_api.xml @@ -682,7 +682,7 @@ - + @@ -1809,7 +1809,7 @@ - + @@ -2525,18 +2525,34 @@ - + - + + + + + + - + + + + + + + + + + + + From 4142c89baab4639cfcf9a51a37a37d14f0dbefba Mon Sep 17 00:00:00 2001 From: mjt Date: Fri, 27 Mar 2026 22:58:28 +0200 Subject: [PATCH 225/319] Update rcore.c comment (#5700) --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 3454b65ba..dc31a103b 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1227,7 +1227,7 @@ void UnloadVrStereoConfig(VrStereoConfig config) //---------------------------------------------------------------------------------- // Load shader from files and bind default locations -// NOTE: If shader string is NULL, using default vertex/fragment shaders +// NOTE: If shader filename is NULL, using default vertex/fragment shaders Shader LoadShader(const char *vsFileName, const char *fsFileName) { Shader shader = { 0 }; From 5fad835ff1e8d473ee3a41ab365a93e548f2b5a3 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 28 Mar 2026 23:54:51 +0100 Subject: [PATCH 226/319] 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 dc31a103b..c7ff22951 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -821,7 +821,7 @@ int GetRenderWidth(void) { int width = 0; - if (CORE.Window.usingFbo) return CORE.Window.currentFbo.width; + if (CORE.Window.usingFbo) width = CORE.Window.currentFbo.width; else width = CORE.Window.render.width; return width; @@ -832,7 +832,7 @@ int GetRenderHeight(void) { int height = 0; - if (CORE.Window.usingFbo) return CORE.Window.currentFbo.height; + if (CORE.Window.usingFbo) height = CORE.Window.currentFbo.height; else height = CORE.Window.render.height; return height; From 8c44ea503281a4a3e90a1c13522ee0a94b8107a1 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Mar 2026 00:37:01 +0100 Subject: [PATCH 227/319] Code gardening REVIEWED: Some early returns, avoid if possible REVIEWED: Some return variable names, for consistency, rename `success` to `result` --- src/raudio.c | 107 +++++++++++++++++++++++++----------------------- src/rcore.c | 54 ++++++++++++------------ src/rlgl.h | 6 +-- src/rmodels.c | 14 +++---- src/rshapes.c | 50 ++++++++++++---------- src/rtext.c | 66 ++++++++++++++--------------- src/rtextures.c | 70 +++++++++++++++---------------- 7 files changed, 188 insertions(+), 179 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 937c33ac3..00e819466 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -963,20 +963,18 @@ Sound LoadSoundFromWave(Wave wave) if (frameCount == 0) TRACELOG(LOG_WARNING, "SOUND: Failed to get frame count for format conversion"); AudioBuffer *audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, frameCount, AUDIO_BUFFER_USAGE_STATIC); - if (audioBuffer == NULL) + if (audioBuffer != NULL) { - TRACELOG(LOG_WARNING, "SOUND: Failed to create buffer"); - return sound; // early return to avoid dereferencing the audioBuffer null pointer + frameCount = (ma_uint32)ma_convert_frames(audioBuffer->data, frameCount, AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, wave.data, frameCountIn, formatIn, wave.channels, wave.sampleRate); + if (frameCount == 0) TRACELOG(LOG_WARNING, "SOUND: Failed format conversion"); + + sound.frameCount = frameCount; + sound.stream.sampleRate = AUDIO.System.device.sampleRate; + sound.stream.sampleSize = 32; + sound.stream.channels = AUDIO_DEVICE_CHANNELS; + sound.stream.buffer = audioBuffer; } - - frameCount = (ma_uint32)ma_convert_frames(audioBuffer->data, frameCount, AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, wave.data, frameCountIn, formatIn, wave.channels, wave.sampleRate); - if (frameCount == 0) TRACELOG(LOG_WARNING, "SOUND: Failed format conversion"); - - sound.frameCount = frameCount; - sound.stream.sampleRate = AUDIO.System.device.sampleRate; - sound.stream.sampleSize = 32; - sound.stream.channels = AUDIO_DEVICE_CHANNELS; - sound.stream.buffer = audioBuffer; + else TRACELOG(LOG_WARNING, "SOUND: Failed to create buffer"); } return sound; @@ -992,25 +990,23 @@ Sound LoadSoundAlias(Sound source) { AudioBuffer *audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, 0, AUDIO_BUFFER_USAGE_STATIC); - if (audioBuffer == NULL) + if (audioBuffer != NULL) { - TRACELOG(LOG_WARNING, "SOUND: Failed to create buffer"); - return sound; // Early return to avoid dereferencing the audioBuffer null pointer + audioBuffer->sizeInFrames = source.stream.buffer->sizeInFrames; + audioBuffer->data = source.stream.buffer->data; + + // Initalize the buffer as if it was new + audioBuffer->volume = 1.0f; + audioBuffer->pitch = 1.0f; + audioBuffer->pan = 0.0f; // Center + + sound.frameCount = source.frameCount; + sound.stream.sampleRate = AUDIO.System.device.sampleRate; + sound.stream.sampleSize = 32; + sound.stream.channels = AUDIO_DEVICE_CHANNELS; + sound.stream.buffer = audioBuffer; } - - audioBuffer->sizeInFrames = source.stream.buffer->sizeInFrames; - audioBuffer->data = source.stream.buffer->data; - - // Initalize the buffer as if it was new - audioBuffer->volume = 1.0f; - audioBuffer->pitch = 1.0f; - audioBuffer->pan = 0.0f; // Center - - sound.frameCount = source.frameCount; - sound.stream.sampleRate = AUDIO.System.device.sampleRate; - sound.stream.sampleSize = 32; - sound.stream.channels = AUDIO_DEVICE_CHANNELS; - sound.stream.buffer = audioBuffer; + else TRACELOG(LOG_WARNING, "SOUND: Failed to create buffer"); } return sound; @@ -1071,7 +1067,7 @@ void UpdateSound(Sound sound, const void *data, int frameCount) // Export wave data to file bool ExportWave(Wave wave, const char *fileName) { - bool success = false; + bool result = false; if (false) { } #if SUPPORT_FILEFORMAT_WAV @@ -1088,11 +1084,11 @@ bool ExportWave(Wave wave, const char *fileName) void *fileData = NULL; size_t fileDataSize = 0; - success = drwav_init_memory_write(&wav, &fileData, &fileDataSize, &format, NULL); - if (success) success = (int)drwav_write_pcm_frames(&wav, wave.frameCount, wave.data); + result = drwav_init_memory_write(&wav, &fileData, &fileDataSize, &format, NULL); + if (result) result = (int)drwav_write_pcm_frames(&wav, wave.frameCount, wave.data); drwav_result result = drwav_uninit(&wav); - if (result == DRWAV_SUCCESS) success = SaveFileData(fileName, (unsigned char *)fileData, (unsigned int)fileDataSize); + if (result == DRWAV_SUCCESS) result = SaveFileData(fileName, (unsigned char *)fileData, (unsigned int)fileDataSize); drwav_free(fileData, NULL); } @@ -1108,7 +1104,7 @@ bool ExportWave(Wave wave, const char *fileName) qoa.samples = wave.frameCount; int bytesWritten = qoa_write(fileName, (const short *)wave.data, &qoa); - if (bytesWritten > 0) success = true; + if (bytesWritten > 0) result = true; } else TRACELOG(LOG_WARNING, "AUDIO: Wave data must be 16 bit per sample for QOA format export"); } @@ -1117,19 +1113,19 @@ bool ExportWave(Wave wave, const char *fileName) { // Export raw sample data (without header) // NOTE: It's up to the user to track wave parameters - success = SaveFileData(fileName, wave.data, wave.frameCount*wave.channels*wave.sampleSize/8); + result = SaveFileData(fileName, wave.data, wave.frameCount*wave.channels*wave.sampleSize/8); } - if (success) TRACELOG(LOG_INFO, "FILEIO: [%s] Wave data exported successfully", fileName); + if (result) TRACELOG(LOG_INFO, "FILEIO: [%s] Wave data exported successfully", fileName); else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export wave data", fileName); - return success; + return result; } // Export wave sample data to code (.h) bool ExportWaveAsCode(Wave wave, const char *fileName) { - bool success = false; + bool result = false; #ifndef TEXT_BYTES_PER_LINE #define TEXT_BYTES_PER_LINE 20 @@ -1183,14 +1179,14 @@ bool ExportWaveAsCode(Wave wave, const char *fileName) } // NOTE: Text data length exported is determined by '\0' (NULL) character - success = SaveFileText(fileName, txtData); + result = SaveFileText(fileName, txtData); RL_FREE(txtData); - if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Wave as code exported successfully", fileName); + if (result != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Wave as code exported successfully", fileName); else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export wave as code", fileName); - return success; + return result; } // Play a sound @@ -2187,12 +2183,15 @@ void UpdateAudioStream(AudioStream stream, const void *data, int frameCount) // Check if any audio stream buffers requires refill bool IsAudioStreamProcessed(AudioStream stream) { - if (stream.buffer == NULL) return false; - bool result = false; - ma_mutex_lock(&AUDIO.System.lock); - result = stream.buffer->isSubBufferProcessed[0] || stream.buffer->isSubBufferProcessed[1]; - ma_mutex_unlock(&AUDIO.System.lock); + + if (stream.buffer != NULL) + { + ma_mutex_lock(&AUDIO.System.lock); + result = stream.buffer->isSubBufferProcessed[0] || stream.buffer->isSubBufferProcessed[1]; + ma_mutex_unlock(&AUDIO.System.lock); + } + return result; } @@ -2885,6 +2884,8 @@ static unsigned char *LoadFileData(const char *fileName, int *dataSize) // Save data to file from buffer static bool SaveFileData(const char *fileName, void *data, int dataSize) { + bool result = true; + if (fileName != NULL) { FILE *file = fopen(fileName, "wb"); @@ -2902,21 +2903,23 @@ static bool SaveFileData(const char *fileName, void *data, int dataSize) else { TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName); - return false; + result = false; } } else { TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); - return false; + result = false; } - return true; + return result; } // Save text data to file (write), string must be '\0' terminated static bool SaveFileText(const char *fileName, char *text) { + bool result = true; + if (fileName != NULL) { FILE *file = fopen(fileName, "wt"); @@ -2934,16 +2937,16 @@ static bool SaveFileText(const char *fileName, char *text) else { TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName); - return false; + result = false; } } else { TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); - return false; + result = false; } - return true; + return result; } #endif diff --git a/src/rcore.c b/src/rcore.c index c7ff22951..1f6699af9 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1621,18 +1621,20 @@ int GetFPS(void) for (int i = 0; i < FPS_CAPTURE_FRAMES_COUNT; i++) history[i] = 0; } - if (fpsFrame == 0) return 0; - - if ((GetTime() - last) > FPS_STEP) + if (fpsFrame != 0) { - last = (float)GetTime(); - index = (index + 1)%FPS_CAPTURE_FRAMES_COUNT; - average -= history[index]; - history[index] = fpsFrame/FPS_CAPTURE_FRAMES_COUNT; - average += history[index]; - } + if ((GetTime() - last) > FPS_STEP) + { + last = (float)GetTime(); + index = (index + 1)%FPS_CAPTURE_FRAMES_COUNT; + average -= history[index]; + history[index] = fpsFrame/FPS_CAPTURE_FRAMES_COUNT; + average += history[index]; + } - fps = (int)roundf(1.0f/average); + fps = (int)roundf(1.0f/average); + } + else fps = 0; #endif return fps; @@ -2025,7 +2027,7 @@ void UnloadFileData(unsigned char *data) // Save data to file from buffer bool SaveFileData(const char *fileName, void *data, int dataSize) { - bool success = false; + bool result = false; if (fileName != NULL) { @@ -2043,20 +2045,20 @@ bool SaveFileData(const char *fileName, void *data, int dataSize) else if (count != dataSize) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName); else TRACELOG(LOG_INFO, "FILEIO: [%s] File saved successfully", fileName); - int result = fclose(file); - if (result == 0) success = true; + int closed = fclose(file); + if (closed == 0) result = true; } else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName); } else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); - return success; + return result; } // Export data to code (.h), returns true on success bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName) { - bool success = false; + bool result = false; #ifndef TEXT_BYTES_PER_LINE #define TEXT_BYTES_PER_LINE 20 @@ -2096,14 +2098,14 @@ bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileN byteCount += sprintf(txtData + byteCount, "0x%x };\n", data[dataSize - 1]); // NOTE: Text data size exported is determined by '\0' (NULL) character - success = SaveFileText(fileName, txtData); + result = SaveFileText(fileName, txtData); RL_FREE(txtData); - if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Data as code exported successfully", fileName); + if (result != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Data as code exported successfully", fileName); else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export data as code", fileName); - return success; + return result; } // Load text data from file, returns a '\0' terminated string @@ -2166,7 +2168,7 @@ void UnloadFileText(char *text) // Save text data to file (write), string must be '\0' terminated bool SaveFileText(const char *fileName, const char *text) { - bool success = false; + bool result = false; if (fileName != NULL) { @@ -2181,14 +2183,14 @@ bool SaveFileText(const char *fileName, const char *text) if (count < 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write text file", fileName); else TRACELOG(LOG_INFO, "FILEIO: [%s] Text file saved successfully", fileName); - int result = fclose(file); - if (result == 0) success = true; + int closed = fclose(file); + if (closed == 0) result = true; } else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName); } else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); - return success; + return result; } // File access custom callbacks @@ -3628,7 +3630,7 @@ void UnloadAutomationEventList(AutomationEventList list) // Export automation events list as text file bool ExportAutomationEventList(AutomationEventList list, const char *fileName) { - bool success = false; + bool result = false; #if SUPPORT_AUTOMATION_EVENTS // Export events as binary file @@ -3646,7 +3648,7 @@ bool ExportAutomationEventList(AutomationEventList list, const char *fileName) memcpy(binBuffer + offset, list.events, sizeof(AutomationEvent)*list.count); offset += sizeof(AutomationEvent)*list.count; - success = SaveFileData(TextFormat("%s.rae",fileName), binBuffer, binarySize); + result = SaveFileData(TextFormat("%s.rae",fileName), binBuffer, binarySize); RL_FREE(binBuffer); } */ @@ -3677,12 +3679,12 @@ bool ExportAutomationEventList(AutomationEventList list, const char *fileName) } // NOTE: Text data size exported is determined by '\0' (NULL) character - success = SaveFileText(fileName, txtData); + result = SaveFileText(fileName, txtData); RL_FREE(txtData); #endif - return success; + return result; } // Setup automation event list to record to diff --git a/src/rlgl.h b/src/rlgl.h index ce10b2f64..845f75d26 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -4647,14 +4647,14 @@ void rlUpdateShaderBuffer(unsigned int id, const void *data, unsigned int dataSi // Get SSBO buffer size unsigned int rlGetShaderBufferSize(unsigned int id) { + unsigned int result = 0; #if defined(GRAPHICS_API_OPENGL_43) GLint64 size = 0; glBindBuffer(GL_SHADER_STORAGE_BUFFER, id); glGetBufferParameteri64v(GL_SHADER_STORAGE_BUFFER, GL_BUFFER_SIZE, &size); - return (size > 0)? (unsigned int)size : 0; -#else - return 0; + if (size > 0) result = (unsigned int)size; #endif + return result; } // Read SSBO buffer data (GPU->CPU) diff --git a/src/rmodels.c b/src/rmodels.c index a662a2dc2..6c1e0482e 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1949,7 +1949,7 @@ void UnloadMesh(Mesh mesh) // Export mesh data to file bool ExportMesh(Mesh mesh, const char *fileName) { - bool success = false; + bool result = false; if (IsFileExtension(fileName, ".obj")) { @@ -2013,7 +2013,7 @@ bool ExportMesh(Mesh mesh, const char *fileName) } // NOTE: Text data length exported is determined by '\0' (NULL) character - success = SaveFileText(fileName, txtData); + result = SaveFileText(fileName, txtData); RL_FREE(txtData); } @@ -2022,13 +2022,13 @@ bool ExportMesh(Mesh mesh, const char *fileName) // TODO: Support additional file formats to export mesh vertex data } - return success; + return result; } // Export mesh as code file (.h) defining multiple arrays of vertex attributes bool ExportMeshAsCode(Mesh mesh, const char *fileName) { - bool success = false; + bool result = false; #ifndef TEXT_BYTES_PER_LINE #define TEXT_BYTES_PER_LINE 20 @@ -2112,14 +2112,14 @@ bool ExportMeshAsCode(Mesh mesh, const char *fileName) //----------------------------------------------------------------------------------------- // NOTE: Text data size exported is determined by '\0' (NULL) character - success = SaveFileText(fileName, txtData); + result = SaveFileText(fileName, txtData); RL_FREE(txtData); - //if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image as code exported successfully", fileName); + //if (result != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image as code exported successfully", fileName); //else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image as code", fileName); - return success; + return result; } #if SUPPORT_FILEFORMAT_OBJ || SUPPORT_FILEFORMAT_MTL diff --git a/src/rshapes.c b/src/rshapes.c index e24689040..268da45f1 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -2348,16 +2348,18 @@ bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec) float dx = fabsf(center.x - recCenterX); float dy = fabsf(center.y - recCenterY); - if (dx > (rec.width/2.0f + radius)) { return false; } - if (dy > (rec.height/2.0f + radius)) { return false; } + if ((dx <= (rec.width/2.0f + radius)) && (dy <= (rec.height/2.0f + radius))) + { + if (dx <= (rec.width/2.0f)) collision = true; + else if (dy <= (rec.height/2.0f)) collision = true; + else + { + float cornerDistanceSq = (dx - rec.width/2.0f)*(dx - rec.width/2.0f) + + (dy - rec.height/2.0f)*(dy - rec.height/2.0f); - if (dx <= (rec.width/2.0f)) { return true; } - if (dy <= (rec.height/2.0f)) { return true; } - - float cornerDistanceSq = (dx - rec.width/2.0f)*(dx - rec.width/2.0f) + - (dy - rec.height/2.0f)*(dy - rec.height/2.0f); - - collision = (cornerDistanceSq <= (radius*radius)); + collision = (cornerDistanceSq <= (radius*radius)); + } + } return collision; } @@ -2418,25 +2420,31 @@ bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshol // Check if circle collides with a line created between two points [p1] and [p2] bool CheckCollisionCircleLine(Vector2 center, float radius, Vector2 p1, Vector2 p2) { + bool collision = false; + float dx = p1.x - p2.x; float dy = p1.y - p2.y; if ((fabsf(dx) + fabsf(dy)) <= FLT_EPSILON) { - return CheckCollisionCircles(p1, 0, center, radius); + collision = CheckCollisionCircles(p1, 0, center, radius); + } + else + { + float lengthSQ = ((dx*dx) + (dy*dy)); + float dotProduct = (((center.x - p1.x)*(p2.x - p1.x)) + ((center.y - p1.y)*(p2.y - p1.y)))/(lengthSQ); + + if (dotProduct > 1.0f) dotProduct = 1.0f; + else if (dotProduct < 0.0f) dotProduct = 0.0f; + + float dx2 = (p1.x - (dotProduct*(dx))) - center.x; + float dy2 = (p1.y - (dotProduct*(dy))) - center.y; + float distanceSQ = ((dx2*dx2) + (dy2*dy2)); + + if (distanceSQ <= radius*radius) collision = true; } - float lengthSQ = ((dx*dx) + (dy*dy)); - float dotProduct = (((center.x - p1.x)*(p2.x - p1.x)) + ((center.y - p1.y)*(p2.y - p1.y)))/(lengthSQ); - - if (dotProduct > 1.0f) dotProduct = 1.0f; - else if (dotProduct < 0.0f) dotProduct = 0.0f; - - float dx2 = (p1.x - (dotProduct*(dx))) - center.x; - float dy2 = (p1.y - (dotProduct*(dy))) - center.y; - float distanceSQ = ((dx2*dx2) + (dy2*dy2)); - - return (distanceSQ <= radius*radius); + return collision; } // Get collision rectangle for two rectangles collision diff --git a/src/rtext.c b/src/rtext.c index a6ca65113..3f4c993c3 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -447,7 +447,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) int j = 0; while (((lineSpacing + j) < image.height) && - !COLOR_EQUAL(pixels[(lineSpacing + j)*image.width + charSpacing], key)) j++; + !COLOR_EQUAL(pixels[(lineSpacing + j)*image.width + charSpacing], key)) j++; charHeight = j; @@ -1017,7 +1017,7 @@ void UnloadFont(Font font) // Export font as code file, returns true on success bool ExportFontAsCode(Font font, const char *fileName) { - bool success = false; + bool result = false; #ifndef TEXT_BYTES_PER_LINE #define TEXT_BYTES_PER_LINE 20 @@ -1159,14 +1159,14 @@ bool ExportFontAsCode(Font font, const char *fileName) UnloadImage(image); // NOTE: Text data size exported is determined by '\0' (NULL) character - success = SaveFileText(fileName, txtData); + result = SaveFileText(fileName, txtData); RL_FREE(txtData); - if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Font as code exported successfully", fileName); + if (result != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Font as code exported successfully", fileName); else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export font as code", fileName); - return success; + return result; } // Draw current FPS @@ -1395,8 +1395,7 @@ Vector2 MeasureTextCodepoints(Font font, const int *codepoints, int length, floa { Vector2 textSize = { 0 }; - // Security check - if ((font.texture.id == 0) || (codepoints == NULL) || (length == 0)) return textSize; + if ((font.texture.id == 0) || (codepoints == NULL) || (length == 0)) return textSize; // Security check float textWidth = 0.0f; // Used to count longer text line width @@ -1699,17 +1698,18 @@ const char *TextSubtext(const char *text, int position, int length) { int textLength = TextLength(text); - if (position >= textLength) return buffer; // First char is already '\0' by memset + if (position < textLength) + { + int maxLength = textLength - position; + if (length > maxLength) length = maxLength; + if (length >= MAX_TEXT_BUFFER_LENGTH) length = MAX_TEXT_BUFFER_LENGTH - 1; - int maxLength = textLength - position; - if (length > maxLength) length = maxLength; - if (length >= MAX_TEXT_BUFFER_LENGTH) length = MAX_TEXT_BUFFER_LENGTH - 1; + // NOTE: Alternative: memcpy(buffer, text + position, length) - // NOTE: Alternative: memcpy(buffer, text + position, length) + for (int c = 0; c < length; c++) buffer[c] = text[position + c]; - for (int c = 0; c < length; c++) buffer[c] = text[position + c]; - - buffer[length] = '\0'; + buffer[length] = '\0'; + } } return buffer; @@ -1763,8 +1763,8 @@ char *GetTextBetween(const char *text, const char *begin, const char *end) // NOTE: Limited text replace functionality, using static string char *TextReplace(const char *text, const char *search, const char *replacement) { - static char result[MAX_TEXT_BUFFER_LENGTH] = { 0 }; - memset(result, 0, MAX_TEXT_BUFFER_LENGTH); + static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; + memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); if ((text != NULL) && (search != NULL) && (search[0] != '\0')) { @@ -1790,7 +1790,7 @@ char *TextReplace(const char *text, const char *search, const char *replacement) { // TODO: Allow copying data replaced up to maximum buffer size and stop - tempPtr = result; // Point to result start + tempPtr = buffer; // Point to result start // First time through the loop, all the variable are set correctly from here on, // - 'temp' points to the end of the result string @@ -1821,7 +1821,7 @@ char *TextReplace(const char *text, const char *search, const char *replacement) else TRACELOG(LOG_WARNING, "Text with replacement is longer than internal buffer, use TextReplaceAlloc()"); } - return result; + return buffer; } // Replace text string @@ -1894,8 +1894,8 @@ char *TextReplaceAlloc(const char *text, const char *search, const char *replace // NOTE: If (replacement == NULL) removes "begin"[ ]"end" text char *TextReplaceBetween(const char *text, const char *begin, const char *end, const char *replacement) { - static char result[MAX_TEXT_BUFFER_LENGTH] = { 0 }; - memset(result, 0, MAX_TEXT_BUFFER_LENGTH); + static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; + memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); if ((text != NULL) && (begin != NULL) && (end != NULL)) { @@ -1912,16 +1912,16 @@ char *TextReplaceBetween(const char *text, const char *begin, const char *end, c int textLen = TextLength(text); int replaceLen = (replacement == NULL)? 0 : TextLength(replacement); - int toreplaceLen = endIndex - beginIndex - beginLen; + //int toreplaceLen = endIndex - beginIndex - beginLen; - strncpy(result, text, beginIndex + beginLen); // Copy first text part - if (replacement != NULL) strncpy(result + beginIndex + beginLen, replacement, replaceLen); // Copy replacement (if provided) - strncpy(result + beginIndex + beginLen + replaceLen, text + endIndex, textLen - endIndex); // Copy end text part + strncpy(buffer, text, beginIndex + beginLen); // Copy first text part + if (replacement != NULL) strncpy(buffer + beginIndex + beginLen, replacement, replaceLen); // Copy replacement (if provided) + strncpy(buffer + beginIndex + beginLen + replaceLen, text + endIndex, textLen - endIndex); // Copy end text part } } } - return result; + return buffer; } // Replace text between two specific strings @@ -1964,8 +1964,8 @@ char *TextReplaceBetweenAlloc(const char *text, const char *begin, const char *e // WARNING: Allocated memory must be manually freed char *TextInsert(const char *text, const char *insert, int position) { - static char result[MAX_TEXT_BUFFER_LENGTH] = { 0 }; - memset(result, 0, MAX_TEXT_BUFFER_LENGTH); + static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; + memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); if ((text != NULL) && (insert != NULL)) { @@ -1976,16 +1976,16 @@ char *TextInsert(const char *text, const char *insert, int position) { // TODO: Allow copying data inserted up to maximum buffer size and stop - for (int i = 0; i < position; i++) result[i] = text[i]; - for (int i = position; i < insertLen + position; i++) result[i] = insert[i - position]; - for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i]; + for (int i = 0; i < position; i++) buffer[i] = text[i]; + for (int i = position; i < insertLen + position; i++) buffer[i] = insert[i - position]; + for (int i = (insertLen + position); i < (textLen + insertLen); i++) buffer[i] = text[i]; - result[textLen + insertLen] = '\0'; // Add EOL + buffer[textLen + insertLen] = '\0'; // Add EOL } else TRACELOG(LOG_WARNING, "Text with inserted string is longer than internal buffer, use TextInserExt()"); } - return result; + return buffer; } // Insert text in a specific position, moves all text forward diff --git a/src/rtextures.c b/src/rtextures.c index 45b615cf7..b13f697d1 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -376,8 +376,7 @@ Image LoadImageAnimFromMemory(const char *fileType, const unsigned char *fileDat Image image = { 0 }; int frameCount = 0; - // Security check for input data - if ((fileType == NULL) || (fileData == NULL) || (dataSize == 0)) return image; + if ((fileType == NULL) || (fileData == NULL) || (dataSize == 0)) return image; // Security check #if SUPPORT_FILEFORMAT_GIF if ((strcmp(fileType, ".gif") == 0) || (strcmp(fileType, ".GIF") == 0)) @@ -621,8 +620,7 @@ bool ExportImage(Image image, const char *fileName) { int result = 0; - // Security check for input data - if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return result; + if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return result; // Security check #if SUPPORT_IMAGE_EXPORT int channels = 4; @@ -709,8 +707,7 @@ unsigned char *ExportImageToMemory(Image image, const char *fileType, int *dataS unsigned char *fileData = NULL; *dataSize = 0; - // Security check for input data - if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return NULL; + if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return fileData; // Security check int channels = 4; @@ -734,7 +731,7 @@ unsigned char *ExportImageToMemory(Image image, const char *fileType, int *dataS // Export image as code file (.h) defining an array of bytes bool ExportImageAsCode(Image image, const char *fileName) { - bool success = false; + bool result = false; #ifndef TEXT_BYTES_PER_LINE #define TEXT_BYTES_PER_LINE 20 @@ -774,14 +771,14 @@ bool ExportImageAsCode(Image image, const char *fileName) byteCount += sprintf(txtData + byteCount, "0x%x };\n", ((unsigned char *)image.data)[dataSize - 1]); // NOTE: Text data size exported is determined by '\0' (NULL) character - success = SaveFileText(fileName, txtData); + result = SaveFileText(fileName, txtData); RL_FREE(txtData); - if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image as code exported successfully", fileName); + if (result != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image as code exported successfully", fileName); else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image as code", fileName); - return success; + return result; } //------------------------------------------------------------------------------------ @@ -1536,8 +1533,7 @@ Image ImageFromChannel(Image image, int selectedChannel) { Image result = { 0 }; - // Security check to avoid program crash - if ((image.data == NULL) || (image.width == 0) || (image.height == 0)) return result; + if ((image.data == NULL) || (image.width == 0) || (image.height == 0)) return result; // Security check // Check selected channel is valid if (selectedChannel < 0) @@ -2937,7 +2933,7 @@ void ImageColorReplace(Image *image, Color color, Color replace) // NOTE: Memory allocated should be freed using UnloadImageColors(); Color *LoadImageColors(Image image) { - if ((image.width == 0) || (image.height == 0)) return NULL; + if ((image.width == 0) || (image.height == 0)) return NULL; // Security check Color *pixels = (Color *)RL_MALLOC(image.width*image.height*sizeof(Color)); @@ -4903,44 +4899,44 @@ Vector3 ColorToHSV(Color color) float max = 0.0f; float delta = 0.0f; - min = rgb.x < rgb.y? rgb.x : rgb.y; - min = min < rgb.z? min : rgb.z; + min = (rgb.x < rgb.y)? rgb.x : rgb.y; + min = (min < rgb.z)? min : rgb.z; - max = rgb.x > rgb.y? rgb.x : rgb.y; - max = max > rgb.z? max : rgb.z; + max = (rgb.x > rgb.y)? rgb.x : rgb.y; + max = (max > rgb.z)? max : rgb.z; - hsv.z = max; // Value + hsv.z = max; // Value delta = max - min; if (delta < 0.00001f) { hsv.y = 0.0f; - hsv.x = 0.0f; // Undefined, maybe NAN? + hsv.x = 0.0f; // Undefined, maybe NAN? return hsv; } if (max > 0.0f) { // NOTE: If max is 0, this divide would cause a crash - hsv.y = (delta/max); // Saturation + hsv.y = (delta/max); // Saturation } else { // NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined hsv.y = 0.0f; - hsv.x = NAN; // Undefined + hsv.x = NAN; // Undefined return hsv; } // NOTE: Comparing float values could not work properly - if (rgb.x >= max) hsv.x = (rgb.y - rgb.z)/delta; // Between yellow & magenta + if (rgb.x >= max) hsv.x = (rgb.y - rgb.z)/delta; // Between yellow & magenta else { - if (rgb.y >= max) hsv.x = 2.0f + (rgb.z - rgb.x)/delta; // Between cyan & yellow - else hsv.x = 4.0f + (rgb.x - rgb.y)/delta; // Between magenta & cyan + if (rgb.y >= max) hsv.x = 2.0f + (rgb.z - rgb.x)/delta; // Between cyan & yellow + else hsv.x = 4.0f + (rgb.x - rgb.y)/delta; // Between magenta & cyan } - hsv.x *= 60.0f; // Convert to degrees + hsv.x *= 60.0f; // Convert to degrees if (hsv.x < 0.0f) hsv.x += 360.0f; @@ -5093,7 +5089,7 @@ Color ColorAlpha(Color color, float alpha) // Get src alpha-blended into dst color with tint Color ColorAlphaBlend(Color dst, Color src, Color tint) { - Color out = WHITE; + Color result = WHITE; // Apply color tint to source color src.r = (unsigned char)(((unsigned int)src.r*((unsigned int)tint.r+1)) >> 8); @@ -5104,24 +5100,24 @@ Color ColorAlphaBlend(Color dst, Color src, Color tint) //#define COLORALPHABLEND_FLOAT #define COLORALPHABLEND_INTEGERS #if defined(COLORALPHABLEND_INTEGERS) - if (src.a == 0) out = dst; - else if (src.a == 255) out = src; + if (src.a == 0) result = dst; + else if (src.a == 255) result = src; else { unsigned int alpha = (unsigned int)src.a + 1; // Shifting by 8 (dividing by 256), so need to take that excess into account - out.a = (unsigned char)(((unsigned int)alpha*256 + (unsigned int)dst.a*(256 - alpha)) >> 8); + result.a = (unsigned char)(((unsigned int)alpha*256 + (unsigned int)dst.a*(256 - alpha)) >> 8); - if (out.a > 0) + if (result.a > 0) { - out.r = (unsigned char)((((unsigned int)src.r*alpha*256 + (unsigned int)dst.r*(unsigned int)dst.a*(256 - alpha))/out.a) >> 8); - out.g = (unsigned char)((((unsigned int)src.g*alpha*256 + (unsigned int)dst.g*(unsigned int)dst.a*(256 - alpha))/out.a) >> 8); - out.b = (unsigned char)((((unsigned int)src.b*alpha*256 + (unsigned int)dst.b*(unsigned int)dst.a*(256 - alpha))/out.a) >> 8); + result.r = (unsigned char)((((unsigned int)src.r*alpha*256 + (unsigned int)dst.r*(unsigned int)dst.a*(256 - alpha))/result.a) >> 8); + result.g = (unsigned char)((((unsigned int)src.g*alpha*256 + (unsigned int)dst.g*(unsigned int)dst.a*(256 - alpha))/result.a) >> 8); + result.b = (unsigned char)((((unsigned int)src.b*alpha*256 + (unsigned int)dst.b*(unsigned int)dst.a*(256 - alpha))/result.a) >> 8); } } #endif #if defined(COLORALPHABLEND_FLOAT) - if (src.a == 0) out = dst; - else if (src.a == 255) out = src; + if (src.a == 0) result = dst; + else if (src.a == 255) result = src; else { Vector4 fdst = ColorNormalize(dst); @@ -5138,11 +5134,11 @@ Color ColorAlphaBlend(Color dst, Color src, Color tint) fout.z = (fsrc.z*fsrc.w + fdst.z*fdst.w*(1 - fsrc.w))/fout.w; } - out = (Color){ (unsigned char)(fout.x*255.0f), (unsigned char)(fout.y*255.0f), (unsigned char)(fout.z*255.0f), (unsigned char)(fout.w*255.0f) }; + result = (Color){ (unsigned char)(fout.x*255.0f), (unsigned char)(fout.y*255.0f), (unsigned char)(fout.z*255.0f), (unsigned char)(fout.w*255.0f) }; } #endif - return out; + return result; } // Get color lerp interpolation between two colors, factor [0.0f..1.0f] From 5915584cd6cd8120429b70e224d6e80c8c1abadf Mon Sep 17 00:00:00 2001 From: Mr Josh Date: Sun, 29 Mar 2026 10:38:45 +1100 Subject: [PATCH 228/319] Fix zig build and minor improvements (#5702) - Added zig-pkg directory to gitignore - Brought min zig version to recent nightly release these improvements were completed on - For linux build, only add rglfw.c if the platform is glfw - Add a None option to the LinuxDisplayBackend - Fixed xcode-frameworks dependency and update to using most recent commit hash --- .gitignore | 1 + build.zig | 8 ++++++-- build.zig.zon | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 00a751a73..a0fa8db1c 100644 --- a/.gitignore +++ b/.gitignore @@ -118,6 +118,7 @@ GTAGS # Zig programming language .zig-cache/ zig-cache/ +zig-pkg/ zig-out/ build/ build-*/ diff --git a/build.zig b/build.zig index 3dc95bc83..ec254debf 100644 --- a/build.zig +++ b/build.zig @@ -2,7 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); /// Minimum supported version of Zig -const min_ver = "0.16.0-dev.2349+204fa8959"; +const min_ver = "0.16.0-dev.3013+abd131e33"; const emccOutputDir = "zig-out" ++ std.fs.path.sep_str ++ "htmlout" ++ std.fs.path.sep_str; const emccOutputFile = "index.html"; @@ -264,7 +264,10 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. setDesktopPlatform(raylib, .android); } else { - try c_source_files.append(b.allocator, "src/rglfw.c"); + switch (options.platform) { + .glfw => try c_source_files.append(b.allocator, "src/rglfw.c"), + .rgfw, .sdl, .drm, .android => {}, + } if (options.linux_display_backend == .X11 or options.linux_display_backend == .Both) { raylib.root_module.addCMacro("_GLFW_X11", ""); @@ -434,6 +437,7 @@ pub const OpenglVersion = enum { }; pub const LinuxDisplayBackend = enum { + None, X11, Wayland, Both, diff --git a/build.zig.zon b/build.zig.zon index 247386fb1..ee5a84c68 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -7,8 +7,8 @@ .dependencies = .{ .xcode_frameworks = .{ - .url = "https://pkg.machengine.org/xcode-frameworks/9a45f3ac977fd25dff77e58c6de1870b6808c4a7.tar.gz", - .hash = "122098b9174895f9708bc824b0f9e550c401892c40a900006459acf2cbf78acd99bb", + .url = "https://pkg.machengine.org/xcode-frameworks/8a1cfb373587ea4c9bb1468b7c986462d8d4e10e.tar.gz", + .hash = "N-V-__8AALShqgXkvqYU6f__FrA22SMWmi2TXCJjNTO1m8XJ", .lazy = true, }, .emsdk = .{ From bb78e98a7158c54177921e436a0f82c228c830b9 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Sat, 28 Mar 2026 16:39:23 -0700 Subject: [PATCH 229/319] Remove forced normalization of normal vectors in rlNormal3f, assume the user has set the correct input for the shader. (#5703) --- src/rlgl.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rlgl.h b/src/rlgl.h index 845f75d26..21899e6f6 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1613,7 +1613,9 @@ void rlNormal3f(float x, float y, float z) normaly = RLGL.State.transform.m1*x + RLGL.State.transform.m5*y + RLGL.State.transform.m9*z; normalz = RLGL.State.transform.m2*x + RLGL.State.transform.m6*y + RLGL.State.transform.m10*z; } - float length = sqrtf(normalx*normalx + normaly*normaly + normalz*normalz); + +/* Normalize the vector if required. Default behavior assumes the normal vector is in the correct space for what the shader expects. + float length = sqrtf(normalx * normalx + normaly * normaly + normalz * normalz); if (length != 0.0f) { float ilength = 1.0f/length; @@ -1621,6 +1623,7 @@ void rlNormal3f(float x, float y, float z) normaly *= ilength; normalz *= ilength; } +*/ RLGL.State.normalx = normalx; RLGL.State.normaly = normaly; RLGL.State.normalz = normalz; From 29ded51ea437fe0639fd3b6408066ed3a5b931e6 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Mar 2026 00:43:50 +0100 Subject: [PATCH 230/319] Update rlgl.h --- src/rlgl.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 21899e6f6..e97bad36a 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1614,8 +1614,11 @@ void rlNormal3f(float x, float y, float z) normalz = RLGL.State.transform.m2*x + RLGL.State.transform.m6*y + RLGL.State.transform.m10*z; } -/* Normalize the vector if required. Default behavior assumes the normal vector is in the correct space for what the shader expects. - float length = sqrtf(normalx * normalx + normaly * normaly + normalz * normalz); + // NOTE: Default behavior assumes the normal vector is in the correct space for what the shader expects, + // it could be not normalized to 0.0f..1.0f, magnitud can be useed for some effects + /* + // WARNING: Vector normalization if required + float length = sqrtf(normalx*normalx + normaly*normaly + normalz*normalz); if (length != 0.0f) { float ilength = 1.0f/length; @@ -1623,7 +1626,7 @@ void rlNormal3f(float x, float y, float z) normaly *= ilength; normalz *= ilength; } -*/ + */ RLGL.State.normalx = normalx; RLGL.State.normaly = normaly; RLGL.State.normalz = normalz; From fb0f83bc9169f9e36c23440b0623b97c71d6a13c Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Mar 2026 01:10:29 +0100 Subject: [PATCH 231/319] Remove trailing spaces on shaders --- .../audio/resources/shaders/glsl100/fft.fs | 6 +-- .../audio/resources/shaders/glsl120/fft.fs | 6 +-- .../audio/resources/shaders/glsl330/fft.fs | 6 +-- .../resources/shaders/glsl100/skinning.fs | 2 +- .../resources/shaders/glsl100/skinning.vs | 10 ++--- .../resources/shaders/glsl120/skinning.fs | 2 +- .../resources/shaders/glsl120/skinning.vs | 10 ++--- .../resources/shaders/glsl330/skinning.vs | 10 ++--- .../resources/shaders/glsl100/ascii.fs | 4 +- .../shaders/glsl100/deferred_shading.vs | 2 +- .../resources/shaders/glsl100/depth_write.fs | 4 +- .../resources/shaders/glsl100/gbuffer.fs | 6 +-- .../resources/shaders/glsl100/gbuffer.vs | 2 +- .../shaders/glsl100/hybrid_raster.fs | 2 +- .../shaders/glsl100/hybrid_raymarch.fs | 36 ++++++++--------- .../shaders/glsl100/mandelbrot_set.fs | 2 +- .../shaders/glsl100/palette_switch.fs | 4 +- .../shaders/resources/shaders/glsl100/pbr.fs | 14 +++---- .../resources/shaders/glsl100/shadowmap.fs | 4 +- .../resources/shaders/glsl120/ascii.fs | 4 +- .../shaders/glsl120/deferred_shading.vs | 2 +- .../resources/shaders/glsl120/depth_write.fs | 4 +- .../resources/shaders/glsl120/gbuffer.fs | 6 +-- .../resources/shaders/glsl120/gbuffer.vs | 2 +- .../shaders/glsl120/hybrid_raster.fs | 2 +- .../shaders/glsl120/hybrid_raymarch.fs | 34 ++++++++-------- .../shaders/resources/shaders/glsl120/pbr.fs | 14 +++---- .../resources/shaders/glsl120/shadowmap.fs | 4 +- .../resources/shaders/glsl330/ascii.fs | 4 +- .../shaders/resources/shaders/glsl330/base.fs | 2 +- .../resources/shaders/glsl330/depth_write.fs | 2 +- .../resources/shaders/glsl330/gbuffer.vs | 2 +- .../shaders/glsl330/hybrid_raster.fs | 2 +- .../shaders/glsl330/hybrid_raymarch.fs | 40 +++++++++---------- .../shaders/glsl330/palette_switch.fs | 2 +- .../shaders/resources/shaders/glsl330/pbr.fs | 14 +++---- .../shaders/glsl330/vertex_displacement.vs | 2 +- 37 files changed, 137 insertions(+), 137 deletions(-) diff --git a/examples/audio/resources/shaders/glsl100/fft.fs b/examples/audio/resources/shaders/glsl100/fft.fs index a97bf336b..15f6fef0f 100644 --- a/examples/audio/resources/shaders/glsl100/fft.fs +++ b/examples/audio/resources/shaders/glsl100/fft.fs @@ -23,15 +23,15 @@ void main() float localX = mod(fragCoord.x, cellWidth); float barWidth = cellWidth - 1.0; vec4 color = WHITE; - + if (localX <= barWidth) { float sampleX = (binIndex + 0.5)/NUM_OF_BINS; vec2 sampleCoord = vec2(sampleX, FFT_ROW); float amplitude = texture2D(iChannel0, sampleCoord).r; // Only filled the red channel, all channels left open for alternative use - + if (fragTexCoord.y < amplitude) color = BLACK; } - + gl_FragColor = color; } diff --git a/examples/audio/resources/shaders/glsl120/fft.fs b/examples/audio/resources/shaders/glsl120/fft.fs index bab5d533b..3048d6c1f 100644 --- a/examples/audio/resources/shaders/glsl120/fft.fs +++ b/examples/audio/resources/shaders/glsl120/fft.fs @@ -21,15 +21,15 @@ void main() float localX = mod(fragCoord.x, cellWidth); float barWidth = cellWidth - 1.0; vec4 color = WHITE; - + if (localX <= barWidth) { float sampleX = (binIndex + 0.5)/NUM_OF_BINS; vec2 sampleCoord = vec2(sampleX, FFT_ROW); float amplitude = texture2D(iChannel0, sampleCoord).r; // Only filled the red channel, all channels left open for alternative use - + if (fragTexCoord.y < amplitude) color = BLACK; } - + gl_FragColor = color; } diff --git a/examples/audio/resources/shaders/glsl330/fft.fs b/examples/audio/resources/shaders/glsl330/fft.fs index 20b2e9dfa..f355ced65 100644 --- a/examples/audio/resources/shaders/glsl330/fft.fs +++ b/examples/audio/resources/shaders/glsl330/fft.fs @@ -21,15 +21,15 @@ void main() float localX = mod(fragCoord.x, cellWidth); float barWidth = cellWidth - 1.0; vec4 color = WHITE; - + if (localX <= barWidth) { float sampleX = (binIndex + 0.5)/NUM_OF_BINS; vec2 sampleCoord = vec2(sampleX, FFT_ROW); float amplitude = texture(iChannel0, sampleCoord).r; // Only filled the red channel, all channels left open for alternative use - + if (fragTexCoord.y < amplitude) color = BLACK; } - + finalColor = color; } diff --git a/examples/models/resources/shaders/glsl100/skinning.fs b/examples/models/resources/shaders/glsl100/skinning.fs index 96bcabe01..7feb097dd 100644 --- a/examples/models/resources/shaders/glsl100/skinning.fs +++ b/examples/models/resources/shaders/glsl100/skinning.fs @@ -14,7 +14,7 @@ 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/glsl100/skinning.vs b/examples/models/resources/shaders/glsl100/skinning.vs index 344ad3f95..cb726eb5a 100644 --- a/examples/models/resources/shaders/glsl100/skinning.vs +++ b/examples/models/resources/shaders/glsl100/skinning.vs @@ -23,7 +23,7 @@ void main() int boneIndex1 = int(vertexBoneIndices.y); int boneIndex2 = int(vertexBoneIndices.z); int boneIndex3 = int(vertexBoneIndices.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), @@ -45,13 +45,13 @@ void main() 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.y*(boneMatrixTransposed1*vec4(vertexPosition, 1.0)) + + vertexBoneWeights.z*(boneMatrixTransposed2*vec4(vertexPosition, 1.0)) + vertexBoneWeights.w*(boneMatrixTransposed3*vec4(vertexPosition, 1.0)); - + fragTexCoord = vertexTexCoord; fragColor = vertexColor; diff --git a/examples/models/resources/shaders/glsl120/skinning.fs b/examples/models/resources/shaders/glsl120/skinning.fs index 44c6314d1..5ea45cbac 100644 --- a/examples/models/resources/shaders/glsl120/skinning.fs +++ b/examples/models/resources/shaders/glsl120/skinning.fs @@ -12,7 +12,7 @@ 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 index 1d9855074..02815f515 100644 --- a/examples/models/resources/shaders/glsl120/skinning.vs +++ b/examples/models/resources/shaders/glsl120/skinning.vs @@ -23,7 +23,7 @@ void main() int boneIndex1 = int(vertexBoneIndices.y); int boneIndex2 = int(vertexBoneIndices.z); int boneIndex3 = int(vertexBoneIndices.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), @@ -45,13 +45,13 @@ void main() 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.y*(boneMatrixTransposed1*vec4(vertexPosition, 1.0)) + + vertexBoneWeights.z*(boneMatrixTransposed2*vec4(vertexPosition, 1.0)) + vertexBoneWeights.w*(boneMatrixTransposed3*vec4(vertexPosition, 1.0)); - + fragTexCoord = vertexTexCoord; fragColor = vertexColor; diff --git a/examples/models/resources/shaders/glsl330/skinning.vs b/examples/models/resources/shaders/glsl330/skinning.vs index 9b4ffbbfa..406d16f56 100644 --- a/examples/models/resources/shaders/glsl330/skinning.vs +++ b/examples/models/resources/shaders/glsl330/skinning.vs @@ -26,17 +26,17 @@ void main() int boneIndex1 = int(vertexBoneIndices.y); int boneIndex2 = int(vertexBoneIndices.z); int boneIndex3 = int(vertexBoneIndices.w); - + vec4 skinnedPosition = vertexBoneWeights.x*(boneMatrices[boneIndex0]*vec4(vertexPosition, 1.0)) + - vertexBoneWeights.y*(boneMatrices[boneIndex1]*vec4(vertexPosition, 1.0)) + - vertexBoneWeights.z*(boneMatrices[boneIndex2]*vec4(vertexPosition, 1.0)) + + vertexBoneWeights.y*(boneMatrices[boneIndex1]*vec4(vertexPosition, 1.0)) + + vertexBoneWeights.z*(boneMatrices[boneIndex2]*vec4(vertexPosition, 1.0)) + vertexBoneWeights.w*(boneMatrices[boneIndex3]*vec4(vertexPosition, 1.0)); vec4 skinnedNormal = vertexBoneWeights.x*(boneMatrices[boneIndex0]*vec4(vertexNormal, 0.0)) + - vertexBoneWeights.y*(boneMatrices[boneIndex1]*vec4(vertexNormal, 0.0)) + - vertexBoneWeights.z*(boneMatrices[boneIndex2]*vec4(vertexNormal, 0.0)) + + vertexBoneWeights.y*(boneMatrices[boneIndex1]*vec4(vertexNormal, 0.0)) + + vertexBoneWeights.z*(boneMatrices[boneIndex2]*vec4(vertexNormal, 0.0)) + vertexBoneWeights.w*(boneMatrices[boneIndex3]*vec4(vertexNormal, 0.0)); skinnedNormal.w = 0.0; diff --git a/examples/shaders/resources/shaders/glsl100/ascii.fs b/examples/shaders/resources/shaders/glsl100/ascii.fs index 11b46e471..2e325c0ed 100644 --- a/examples/shaders/resources/shaders/glsl100/ascii.fs +++ b/examples/shaders/resources/shaders/glsl100/ascii.fs @@ -58,12 +58,12 @@ void main() float gray = GreyScale(cellColor); float n = 4096.0; - + // Character set from https://www.shadertoy.com/view/lssGDj // Create new bitmaps https://thrill-project.com/archiv/coding/bitmap/ if (gray > 0.2) n = 65600.0; // : if (gray > 0.3) n = 18725316.0; // v - if (gray > 0.4) n = 15255086.0; // o + if (gray > 0.4) n = 15255086.0; // o if (gray > 0.5) n = 13121101.0; // & if (gray > 0.6) n = 15252014.0; // 8 if (gray > 0.7) n = 13195790.0; // @ diff --git a/examples/shaders/resources/shaders/glsl100/deferred_shading.vs b/examples/shaders/resources/shaders/glsl100/deferred_shading.vs index bb108ce09..bd5bed5b1 100644 --- a/examples/shaders/resources/shaders/glsl100/deferred_shading.vs +++ b/examples/shaders/resources/shaders/glsl100/deferred_shading.vs @@ -12,5 +12,5 @@ void main() fragTexCoord = vertexTexCoord; // Calculate final vertex position - gl_Position = vec4(vertexPosition, 1.0); + gl_Position = vec4(vertexPosition, 1.0); } diff --git a/examples/shaders/resources/shaders/glsl100/depth_write.fs b/examples/shaders/resources/shaders/glsl100/depth_write.fs index 15095db11..55d3a2d87 100644 --- a/examples/shaders/resources/shaders/glsl100/depth_write.fs +++ b/examples/shaders/resources/shaders/glsl100/depth_write.fs @@ -1,5 +1,5 @@ #version 100 -#extension GL_EXT_frag_depth : enable +#extension GL_EXT_frag_depth : enable precision mediump float; @@ -14,7 +14,7 @@ uniform vec4 colDiffuse; void main() { vec4 texelColor = texture2D(texture0, fragTexCoord); - + gl_FragColor = texelColor*colDiffuse*fragColor; gl_FragDepthEXT = 1.0 - gl_FragCoord.z; } diff --git a/examples/shaders/resources/shaders/glsl100/gbuffer.fs b/examples/shaders/resources/shaders/glsl100/gbuffer.fs index 2945c5ddd..deb3d9ff3 100644 --- a/examples/shaders/resources/shaders/glsl100/gbuffer.fs +++ b/examples/shaders/resources/shaders/glsl100/gbuffer.fs @@ -24,13 +24,13 @@ 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/glsl100/gbuffer.vs b/examples/shaders/resources/shaders/glsl100/gbuffer.vs index 2791ce5ee..0df010b8c 100644 --- a/examples/shaders/resources/shaders/glsl100/gbuffer.vs +++ b/examples/shaders/resources/shaders/glsl100/gbuffer.vs @@ -48,7 +48,7 @@ void main() { // Calculate vertex attributes for fragment shader vec4 worldPos = matModel*vec4(vertexPosition, 1.0); - fragPosition = worldPos.xyz; + fragPosition = worldPos.xyz; fragTexCoord = vertexTexCoord; fragColor = vertexColor; diff --git a/examples/shaders/resources/shaders/glsl100/hybrid_raster.fs b/examples/shaders/resources/shaders/glsl100/hybrid_raster.fs index 9658b3819..4f4f4a675 100644 --- a/examples/shaders/resources/shaders/glsl100/hybrid_raster.fs +++ b/examples/shaders/resources/shaders/glsl100/hybrid_raster.fs @@ -1,7 +1,7 @@ #version 100 #extension GL_EXT_frag_depth : enable // Extension required for writing depth - + precision mediump float; // Precision required for OpenGL ES2 (WebGL) varying vec2 fragTexCoord; diff --git a/examples/shaders/resources/shaders/glsl100/hybrid_raymarch.fs b/examples/shaders/resources/shaders/glsl100/hybrid_raymarch.fs index e3287fc04..f13902d12 100644 --- a/examples/shaders/resources/shaders/glsl100/hybrid_raymarch.fs +++ b/examples/shaders/resources/shaders/glsl100/hybrid_raymarch.fs @@ -32,12 +32,12 @@ 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, + 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)); @@ -56,9 +56,9 @@ float sdSixWayCutHollowSphere(vec3 p, float r, float h, float t) } vec2 q = vec2(length(ap.yz), ap.x); - + float w = sqrt(r*r-h*h); - + return ((h*q.xtmax) break; vec2 h = map(ro+rd*t); if (abs(h.x) < (0.0001*t)) - { - res = vec2(t,h.y); + { + res = vec2(t,h.y); break; } t += h.x; @@ -146,9 +146,9 @@ float calcSoftshadow(in vec3 ro, in vec3 rd, in float mint, in float tmax) 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 + + 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); } @@ -176,15 +176,15 @@ float checkersGradBox(in vec2 p) // 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; + 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; @@ -194,11 +194,11 @@ vec4 render(in vec3 ro, in vec3 rd) 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 + + // 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); @@ -208,7 +208,7 @@ vec4 render(in vec3 ro, in vec3 rd) // lighting float occ = calcAO(pos, nor); - + vec3 lin = vec3(0.0); // sun @@ -249,7 +249,7 @@ vec4 render(in vec3 ro, in vec3 rd) 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)); @@ -289,7 +289,7 @@ void main() color = res.xyz; depth = CalcDepth(rd,res.w); } - + gl_FragColor = vec4(color , 1.0); gl_FragDepthEXT = depth; } \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl100/mandelbrot_set.fs b/examples/shaders/resources/shaders/glsl100/mandelbrot_set.fs index 7a89e86b0..b0125a5f6 100644 --- a/examples/shaders/resources/shaders/glsl100/mandelbrot_set.fs +++ b/examples/shaders/resources/shaders/glsl100/mandelbrot_set.fs @@ -48,7 +48,7 @@ void main() float normR = float(iter - (iter/55)*55)/55.0; float normG = float(iter - (iter/69)*69)/69.0; float normB = float(iter - (iter/40)*40)/40.0; - + gl_FragColor = vec4(sin(normR*PI), sin(normG*PI), sin(normB*PI), 1.0); return; } diff --git a/examples/shaders/resources/shaders/glsl100/palette_switch.fs b/examples/shaders/resources/shaders/glsl100/palette_switch.fs index d8d696d4f..a2dcdb912 100644 --- a/examples/shaders/resources/shaders/glsl100/palette_switch.fs +++ b/examples/shaders/resources/shaders/glsl100/palette_switch.fs @@ -21,7 +21,7 @@ void main() // 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); - + ivec3 color = ivec3(0); // NOTE: On GLSL 100 we are not allowed to index a uniform array by a variable value, @@ -34,7 +34,7 @@ void main() else if (index == 5) color = palette[5]; else if (index == 6) color = palette[6]; else if (index == 7) color = palette[7]; - + //gl_FragColor = texture2D(palette, texelColor.xy); // Alternative to ivec3 // Calculate final fragment color. Note that the palette color components diff --git a/examples/shaders/resources/shaders/glsl100/pbr.fs b/examples/shaders/resources/shaders/glsl100/pbr.fs index 48688ebe9..28212bf63 100644 --- a/examples/shaders/resources/shaders/glsl100/pbr.fs +++ b/examples/shaders/resources/shaders/glsl100/pbr.fs @@ -83,11 +83,11 @@ vec3 ComputePBR() { vec3 albedo = texture2D(albedoMap, vec2(fragTexCoord.x*tiling.x + offset.x, fragTexCoord.y*tiling.y + offset.y)).rgb; albedo = vec3(albedoColor.x*albedo.x, albedoColor.y*albedo.y, albedoColor.z*albedo.z); - + float metallic = clamp(metallicValue, 0.0, 1.0); float roughness = clamp(roughnessValue, 0.0, 1.0); float ao = clamp(aoValue, 0.0, 1.0); - + if (useTexMRA == 1) { vec4 mra = texture2D(mraMap, vec2(fragTexCoord.x*tiling.x + offset.x, fragTexCoord.y*tiling.y + offset.y)); @@ -132,18 +132,18 @@ vec3 ComputePBR() vec3 F = SchlickFresnel(hDotV, baseRefl); // Fresnel proportion of specular reflectance vec3 spec = (D*G*F)/(4.0*nDotV*nDotL); - + // Difuse and spec light can't be above 1.0 // kD = 1.0 - kS diffuse component is equal 1.0 - spec comonent vec3 kD = vec3(1.0) - F; - + // Mult kD by the inverse of metallnes, only non-metals should have diffuse light kD *= 1.0 - metallic; lightAccum += ((kD*albedo.rgb/PI + spec)*radiance*nDotL)*float(lights[i].enabled); // Angle of light has impact on result } - + vec3 ambientFinal = (ambientColor + albedo)*ambient*0.5; - + return (ambientFinal + lightAccum*ao + emissive); } @@ -153,7 +153,7 @@ void main() // HDR tonemapping color = pow(color, color + vec3(1.0)); - + // Gamma correction color = pow(color, vec3(1.0/2.2)); diff --git a/examples/shaders/resources/shaders/glsl100/shadowmap.fs b/examples/shaders/resources/shaders/glsl100/shadowmap.fs index 7a0e806a5..c65d20e44 100644 --- a/examples/shaders/resources/shaders/glsl100/shadowmap.fs +++ b/examples/shaders/resources/shaders/glsl100/shadowmap.fs @@ -60,7 +60,7 @@ void main() 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 @@ -74,7 +74,7 @@ void main() 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 diff --git a/examples/shaders/resources/shaders/glsl120/ascii.fs b/examples/shaders/resources/shaders/glsl120/ascii.fs index e4e9927b9..44ff399ae 100644 --- a/examples/shaders/resources/shaders/glsl120/ascii.fs +++ b/examples/shaders/resources/shaders/glsl120/ascii.fs @@ -56,12 +56,12 @@ void main() float gray = GreyScale(cellColor); float n = 4096.0; - + // Character set from https://www.shadertoy.com/view/lssGDj // Create new bitmaps https://thrill-project.com/archiv/coding/bitmap/ if (gray > 0.2) n = 65600.0; // : if (gray > 0.3) n = 18725316.0; // v - if (gray > 0.4) n = 15255086.0; // o + if (gray > 0.4) n = 15255086.0; // o if (gray > 0.5) n = 13121101.0; // & if (gray > 0.6) n = 15252014.0; // 8 if (gray > 0.7) n = 13195790.0; // @ diff --git a/examples/shaders/resources/shaders/glsl120/deferred_shading.vs b/examples/shaders/resources/shaders/glsl120/deferred_shading.vs index daece19e5..d08e909a9 100644 --- a/examples/shaders/resources/shaders/glsl120/deferred_shading.vs +++ b/examples/shaders/resources/shaders/glsl120/deferred_shading.vs @@ -12,5 +12,5 @@ void main() fragTexCoord = vertexTexCoord; // Calculate final vertex position - gl_Position = vec4(vertexPosition, 1.0); + gl_Position = vec4(vertexPosition, 1.0); } diff --git a/examples/shaders/resources/shaders/glsl120/depth_write.fs b/examples/shaders/resources/shaders/glsl120/depth_write.fs index a2bc38516..897b5ccd0 100644 --- a/examples/shaders/resources/shaders/glsl120/depth_write.fs +++ b/examples/shaders/resources/shaders/glsl120/depth_write.fs @@ -1,6 +1,6 @@ #version 120 -#extension GL_EXT_frag_depth : enable +#extension GL_EXT_frag_depth : enable varying vec2 fragTexCoord; varying vec4 fragColor; @@ -11,7 +11,7 @@ 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 diff --git a/examples/shaders/resources/shaders/glsl120/gbuffer.fs b/examples/shaders/resources/shaders/glsl120/gbuffer.fs index a826d7916..509558e24 100644 --- a/examples/shaders/resources/shaders/glsl120/gbuffer.fs +++ b/examples/shaders/resources/shaders/glsl120/gbuffer.fs @@ -22,13 +22,13 @@ 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 index adc1dcd1a..502dc6792 100644 --- a/examples/shaders/resources/shaders/glsl120/gbuffer.vs +++ b/examples/shaders/resources/shaders/glsl120/gbuffer.vs @@ -48,7 +48,7 @@ void main() { // Calculate vertex attributes for fragment shader vec4 worldPos = matModel*vec4(vertexPosition, 1.0); - fragPosition = worldPos.xyz; + fragPosition = worldPos.xyz; fragTexCoord = vertexTexCoord; fragColor = vertexColor; diff --git a/examples/shaders/resources/shaders/glsl120/hybrid_raster.fs b/examples/shaders/resources/shaders/glsl120/hybrid_raster.fs index e4c7e2eca..74e17eee9 100644 --- a/examples/shaders/resources/shaders/glsl120/hybrid_raster.fs +++ b/examples/shaders/resources/shaders/glsl120/hybrid_raster.fs @@ -1,6 +1,6 @@ #version 120 -#extension GL_EXT_frag_depth : enable // Extension required for writing depth +#extension GL_EXT_frag_depth : enable // Extension required for writing depth varying vec2 fragTexCoord; varying vec4 fragColor; diff --git a/examples/shaders/resources/shaders/glsl120/hybrid_raymarch.fs b/examples/shaders/resources/shaders/glsl120/hybrid_raymarch.fs index 6090df6b1..7a097feff 100644 --- a/examples/shaders/resources/shaders/glsl120/hybrid_raymarch.fs +++ b/examples/shaders/resources/shaders/glsl120/hybrid_raymarch.fs @@ -30,12 +30,12 @@ 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, + 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)); @@ -54,9 +54,9 @@ float sdSixWayCutHollowSphere(vec3 p, float r, float h, float t) } vec2 q = vec2(length(ap.yz), ap.x); - + float w = sqrt(r*r-h*h); - + return ((h*q.xtmax) break; vec2 h = map(ro+rd*t); if (abs(h.x) < (0.0001*t)) - { - res = vec2(t,h.y); + { + res = vec2(t,h.y); break; } t += h.x; @@ -144,9 +144,9 @@ float calcSoftshadow(in vec3 ro, in vec3 rd, in float mint, in float tmax) 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 + + 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); } @@ -174,15 +174,15 @@ float checkersGradBox(in vec2 p) // 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; + 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; @@ -192,11 +192,11 @@ vec4 render(in vec3 ro, in vec3 rd) 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 + + // 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); @@ -206,7 +206,7 @@ vec4 render(in vec3 ro, in vec3 rd) // lighting float occ = calcAO(pos, nor); - + vec3 lin = vec3(0.0); // sun @@ -247,7 +247,7 @@ vec4 render(in vec3 ro, in vec3 rd) 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)); diff --git a/examples/shaders/resources/shaders/glsl120/pbr.fs b/examples/shaders/resources/shaders/glsl120/pbr.fs index 63241709b..b36b76bee 100644 --- a/examples/shaders/resources/shaders/glsl120/pbr.fs +++ b/examples/shaders/resources/shaders/glsl120/pbr.fs @@ -81,11 +81,11 @@ vec3 ComputePBR() { vec3 albedo = texture2D(albedoMap, vec2(fragTexCoord.x*tiling.x + offset.x, fragTexCoord.y*tiling.y + offset.y)).rgb; albedo = vec3(albedoColor.x*albedo.x, albedoColor.y*albedo.y, albedoColor.z*albedo.z); - + float metallic = clamp(metallicValue, 0.0, 1.0); float roughness = clamp(roughnessValue, 0.0, 1.0); float ao = clamp(aoValue, 0.0, 1.0); - + if (useTexMRA == 1) { vec4 mra = texture2D(mraMap, vec2(fragTexCoord.x*tiling.x + offset.x, fragTexCoord.y*tiling.y + offset.y)); @@ -130,18 +130,18 @@ vec3 ComputePBR() vec3 F = SchlickFresnel(hDotV, baseRefl); // Fresnel proportion of specular reflectance vec3 spec = (D*G*F)/(4.0*nDotV*nDotL); - + // Difuse and spec light can't be above 1.0 // kD = 1.0 - kS diffuse component is equal 1.0 - spec comonent vec3 kD = vec3(1.0) - F; - + // Mult kD by the inverse of metallnes, only non-metals should have diffuse light kD *= 1.0 - metallic; lightAccum += ((kD*albedo.rgb/PI + spec)*radiance*nDotL)*float(lights[i].enabled); // Angle of light has impact on result } - + vec3 ambientFinal = (ambientColor + albedo)*ambient*0.5; - + return (ambientFinal + lightAccum*ao + emissive); } @@ -151,7 +151,7 @@ void main() // HDR tonemapping color = pow(color, color + vec3(1.0)); - + // Gamma correction color = pow(color, vec3(1.0/2.2)); diff --git a/examples/shaders/resources/shaders/glsl120/shadowmap.fs b/examples/shaders/resources/shaders/glsl120/shadowmap.fs index 354c4a2c5..a0f8e9425 100644 --- a/examples/shaders/resources/shaders/glsl120/shadowmap.fs +++ b/examples/shaders/resources/shaders/glsl120/shadowmap.fs @@ -58,7 +58,7 @@ void main() 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 @@ -72,7 +72,7 @@ void main() 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 diff --git a/examples/shaders/resources/shaders/glsl330/ascii.fs b/examples/shaders/resources/shaders/glsl330/ascii.fs index 3934c5dc1..8a6cc01d9 100644 --- a/examples/shaders/resources/shaders/glsl330/ascii.fs +++ b/examples/shaders/resources/shaders/glsl330/ascii.fs @@ -52,12 +52,12 @@ void main() float gray = GreyScale(cellColor); int n = 4096; - + // Character set from https://www.shadertoy.com/view/lssGDj // Create new bitmaps https://thrill-project.com/archiv/coding/bitmap/ if (gray > 0.2) n = 65600; // : if (gray > 0.3) n = 18725316; // v - if (gray > 0.4) n = 15255086; // o + if (gray > 0.4) n = 15255086; // o if (gray > 0.5) n = 13121101; // & if (gray > 0.6) n = 15252014; // 8 if (gray > 0.7) n = 13195790; // @ diff --git a/examples/shaders/resources/shaders/glsl330/base.fs b/examples/shaders/resources/shaders/glsl330/base.fs index e8bf9e2d2..abf5dec6b 100644 --- a/examples/shaders/resources/shaders/glsl330/base.fs +++ b/examples/shaders/resources/shaders/glsl330/base.fs @@ -20,7 +20,7 @@ void main() // NOTE: Implement here your fragment shader code - // final color is the color from the texture + // final color is the color from the texture // times the tint color (colDiffuse) // times the fragment color (interpolated vertex color) finalColor = texelColor*colDiffuse*fragColor; diff --git a/examples/shaders/resources/shaders/glsl330/depth_write.fs b/examples/shaders/resources/shaders/glsl330/depth_write.fs index f0e07beec..31f716784 100644 --- a/examples/shaders/resources/shaders/glsl330/depth_write.fs +++ b/examples/shaders/resources/shaders/glsl330/depth_write.fs @@ -14,7 +14,7 @@ out vec4 finalColor; void main() { vec4 texelColor = texture(texture0, fragTexCoord); - + finalColor = texelColor*colDiffuse*fragColor; gl_FragDepth = 1.0 - finalColor.z; } diff --git a/examples/shaders/resources/shaders/glsl330/gbuffer.vs b/examples/shaders/resources/shaders/glsl330/gbuffer.vs index 5af4448e6..e39779e27 100644 --- a/examples/shaders/resources/shaders/glsl330/gbuffer.vs +++ b/examples/shaders/resources/shaders/glsl330/gbuffer.vs @@ -14,7 +14,7 @@ uniform mat4 matProjection; void main() { vec4 worldPos = matModel*vec4(vertexPosition, 1.0); - fragPosition = worldPos.xyz; + fragPosition = worldPos.xyz; fragTexCoord = vertexTexCoord; mat3 normalMatrix = transpose(inverse(mat3(matModel))); diff --git a/examples/shaders/resources/shaders/glsl330/hybrid_raster.fs b/examples/shaders/resources/shaders/glsl330/hybrid_raster.fs index 0b94dbdef..a31920bbf 100644 --- a/examples/shaders/resources/shaders/glsl330/hybrid_raster.fs +++ b/examples/shaders/resources/shaders/glsl330/hybrid_raster.fs @@ -16,7 +16,7 @@ out vec4 finalColor; void main() { vec4 texelColor = texture(texture0, fragTexCoord); - + finalColor = texelColor*colDiffuse*fragColor; gl_FragDepth = finalColor.z; } \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl330/hybrid_raymarch.fs b/examples/shaders/resources/shaders/glsl330/hybrid_raymarch.fs index 073fef4f1..48fba9af8 100644 --- a/examples/shaders/resources/shaders/glsl330/hybrid_raymarch.fs +++ b/examples/shaders/resources/shaders/glsl330/hybrid_raymarch.fs @@ -33,7 +33,7 @@ float sdHorseshoe(in vec3 p, in vec2 c, in float r, in float le, vec2 w) 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)); @@ -54,7 +54,7 @@ float sdSixWayCutHollowSphere(vec3 p, float r, float h, float t) vec2 q = vec2(length(ap.yz), ap.x); float w = sqrt(r*r-h*h); - + return ((h*q.x < w*q.y)? length(q - vec2(w, h)) : abs(length(q) - r)) - t; } @@ -79,7 +79,7 @@ vec2 map(in vec3 pos) { vec2 res = vec2(sdHorseshoe(pos - vec3(-1.0, 0.08, 1.0), vec2(cos(1.3), sin(1.3)), 0.2, 0.3, vec2(0.03,0.5)), 11.5); res = opU(res, vec2(sdSixWayCutHollowSphere(pos-vec3(0.0, 1.0, 0.0), 4.0, 3.5, 0.5), 4.5)); - + return res; } @@ -105,8 +105,8 @@ vec2 raycast(in vec3 ro, in vec3 rd) if (t > tmax) break; vec2 h = map(ro + rd*t); if (abs(h.x )< (0.0001*t)) - { - res = vec2(t, h.y); + { + res = vec2(t, h.y); break; } t += h.x; @@ -131,9 +131,9 @@ float calcSoftshadow(in vec3 ro, in vec3 rd, in float mint, in float tmax) 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); } @@ -141,9 +141,9 @@ float calcSoftshadow(in vec3 ro, in vec3 rd, in float mint, in float tmax) 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 + + 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); } @@ -160,7 +160,7 @@ float calcAO(in vec3 pos, in vec3 nor) sca *= 0.95; if (occ>0.35) break; } - + return clamp(1.0 - 3.0*occ, 0.0, 1.0)*(0.5+0.5*nor.y); } @@ -172,15 +172,15 @@ float checkersGradBox(in vec2 p) // 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); + 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; @@ -190,11 +190,11 @@ vec4 render(in vec3 ro, in vec3 rd) 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 + + // 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); @@ -204,7 +204,7 @@ vec4 render(in vec3 ro, in vec3 rd) // lighting float occ = calcAO(pos, nor); - + vec3 lin = vec3(0.0); // sun @@ -245,7 +245,7 @@ vec4 render(in vec3 ro, in vec3 rd) 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)); @@ -285,7 +285,7 @@ void main() color = res.xyz; depth = CalcDepth(rd,res.w); } - + finalColor = vec4(color , 1.0); gl_FragDepth = depth; } \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl330/palette_switch.fs b/examples/shaders/resources/shaders/glsl330/palette_switch.fs index 2db8880d1..9bd77e863 100644 --- a/examples/shaders/resources/shaders/glsl330/palette_switch.fs +++ b/examples/shaders/resources/shaders/glsl330/palette_switch.fs @@ -24,7 +24,7 @@ void main() // to the palette index by scaling up from [0..1] to [0..255] int index = int(texelColor.r*255.0); ivec3 color = palette[index]; - + //finalColor = texture(palette, texelColor.xy); // Alternative to ivec3 // Calculate final fragment color. Note that the palette color components diff --git a/examples/shaders/resources/shaders/glsl330/pbr.fs b/examples/shaders/resources/shaders/glsl330/pbr.fs index d439841df..ceaead308 100644 --- a/examples/shaders/resources/shaders/glsl330/pbr.fs +++ b/examples/shaders/resources/shaders/glsl330/pbr.fs @@ -84,11 +84,11 @@ vec3 ComputePBR() { vec3 albedo = texture(albedoMap,vec2(fragTexCoord.x*tiling.x + offset.x, fragTexCoord.y*tiling.y + offset.y)).rgb; albedo = vec3(albedoColor.x*albedo.x, albedoColor.y*albedo.y, albedoColor.z*albedo.z); - + float metallic = clamp(metallicValue, 0.0, 1.0); float roughness = clamp(roughnessValue, 0.0, 1.0); float ao = clamp(aoValue, 0.0, 1.0); - + if (useTexMRA == 1) { vec4 mra = texture(mraMap, vec2(fragTexCoord.x*tiling.x + offset.x, fragTexCoord.y*tiling.y + offset.y)); @@ -133,18 +133,18 @@ vec3 ComputePBR() vec3 F = SchlickFresnel(hDotV, baseRefl); // Fresnel proportion of specular reflectance vec3 spec = (D*G*F)/(4.0*nDotV*nDotL); - + // Difuse and spec light can't be above 1.0 // kD = 1.0 - kS diffuse component is equal 1.0 - spec comonent vec3 kD = vec3(1.0) - F; - + // Mult kD by the inverse of metallnes, only non-metals should have diffuse light kD *= 1.0 - metallic; lightAccum += ((kD*albedo.rgb/PI + spec)*radiance*nDotL)*lights[i].enabled; // Angle of light has impact on result } - + vec3 ambientFinal = (ambientColor + albedo)*ambient*0.5; - + return (ambientFinal + lightAccum*ao + emissive); } @@ -154,7 +154,7 @@ void main() // HDR tonemapping color = pow(color, color + vec3(1.0)); - + // Gamma correction color = pow(color, vec3(1.0/2.2)); diff --git a/examples/shaders/resources/shaders/glsl330/vertex_displacement.vs b/examples/shaders/resources/shaders/glsl330/vertex_displacement.vs index 080ad473a..b324cbd57 100644 --- a/examples/shaders/resources/shaders/glsl330/vertex_displacement.vs +++ b/examples/shaders/resources/shaders/glsl330/vertex_displacement.vs @@ -11,7 +11,7 @@ uniform mat4 mvp; uniform mat4 matModel; uniform mat4 matNormal; -uniform float time; +uniform float time; uniform sampler2D perlinNoiseMap; From da93ec4a2145f2ae6ac9319923c8a8820df24a7d Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Mar 2026 01:17:25 +0100 Subject: [PATCH 232/319] Remove trailing spaces --- .github/workflows/build_android.yml | 18 ++++---- .github/workflows/build_examples_linux.yml | 8 ++-- .github/workflows/build_examples_windows.yml | 2 +- .github/workflows/build_linux.yml | 20 ++++---- .github/workflows/build_macos.yml | 30 ++++++------ .github/workflows/update_examples.yml | 4 +- projects/4coder/main.c | 2 +- projects/Geany/core_basic_window.c | 4 +- .../raylib_npp_parser/raylib_npp_parser.c | 46 +++++++++---------- src/raudio.c | 8 ++-- src/rshapes.c | 2 +- src/rtext.c | 4 +- tools/rlparser/rlparser.c | 4 +- 13 files changed, 76 insertions(+), 76 deletions(-) diff --git a/.github/workflows/build_android.yml b/.github/workflows/build_android.yml index 6993eb292..c87807847 100644 --- a/.github/workflows/build_android.yml +++ b/.github/workflows/build_android.yml @@ -28,20 +28,20 @@ jobs: max-parallel: 1 matrix: ARCH: ["arm64", "x86_64"] - + env: RELEASE_NAME: raylib-dev_android_api29_${{ matrix.ARCH }} - + steps: - name: Checkout uses: actions/checkout@master - + - name: Setup Release Version run: | echo "RELEASE_NAME=raylib-${{ github.event.release.tag_name }}_android_api29_${{ matrix.ARCH }}" >> $GITHUB_ENV shell: bash if: github.event_name == 'release' && github.event.action == 'published' - + - name: Setup Android NDK id: setup-ndk uses: nttld/setup-ndk@v1 @@ -52,7 +52,7 @@ jobs: ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} - name: Setup Environment - run: | + run: | mkdir build cd build mkdir ${{ env.RELEASE_NAME }} @@ -60,7 +60,7 @@ jobs: mkdir include mkdir lib cd ../.. - + # Generating static + shared library for 64bit arquitectures and API version 29 - name: Build Library run: | @@ -69,7 +69,7 @@ jobs: make PLATFORM=PLATFORM_ANDROID ANDROID_ARCH=${{ matrix.ARCH }} ANDROID_API_VERSION=29 ANDROID_NDK=${{ env.ANDROID_NDK_HOME }} RAYLIB_LIBTYPE=SHARED RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" -B cd .. shell: cmd - + - name: Generate Artifacts run: | cp -v ./src/raylib.h ./build/${{ env.RELEASE_NAME }}/include @@ -80,7 +80,7 @@ jobs: cp -v ./LICENSE ./build/${{ env.RELEASE_NAME }}/LICENSE cd build tar -czvf ${{ env.RELEASE_NAME }}.tar.gz ${{ env.RELEASE_NAME }} - + - name: Upload Artifacts uses: actions/upload-artifact@v4 with: @@ -88,7 +88,7 @@ jobs: path: | ./build/${{ env.RELEASE_NAME }} !./build/${{ env.RELEASE_NAME }}.tar.gz - + - name: Upload Artifact to Release uses: softprops/action-gh-release@v1 with: diff --git a/.github/workflows/build_examples_linux.yml b/.github/workflows/build_examples_linux.yml index 5f029e73f..c293efd1d 100644 --- a/.github/workflows/build_examples_linux.yml +++ b/.github/workflows/build_examples_linux.yml @@ -23,18 +23,18 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - + - name: Setup Environment - run: | + run: | sudo apt-get update -qq 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 - + - name: Build Library run: | cd src make PLATFORM=PLATFORM_DESKTOP CC=gcc RAYLIB_LIBTYPE=STATIC cd .. - + - name: Build Examples run: | cd examples diff --git a/.github/workflows/build_examples_windows.yml b/.github/workflows/build_examples_windows.yml index 6530931e6..4cc2e84b9 100644 --- a/.github/workflows/build_examples_windows.yml +++ b/.github/workflows/build_examples_windows.yml @@ -13,7 +13,7 @@ on: - 'src/**' - 'examples/**' - '.github/workflows/windows_examples.yml' - + permissions: contents: read diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml index b101750bc..0fc32d374 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_linux.yml @@ -42,23 +42,23 @@ jobs: ARCH_NAME: "arm64" COMPILER_PATH: "/usr/bin" runner: "ubuntu-24.04-arm" - + runs-on: ${{ matrix.runner }} env: RELEASE_NAME: raylib-dev_linux_${{ matrix.ARCH_NAME }} - + steps: - name: Checkout code uses: actions/checkout@master - + - name: Setup Release Version run: | echo "RELEASE_NAME=raylib-${{ github.event.release.tag_name }}_linux_${{ matrix.ARCH_NAME }}" >> $GITHUB_ENV shell: bash if: github.event_name == 'release' && github.event.action == 'published' - + - name: Setup Environment - run: | + run: | sudo apt-get update -qq 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 @@ -74,7 +74,7 @@ jobs: 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) run: | @@ -91,7 +91,7 @@ jobs: 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 == 'x86_64' - + - name: Build Library (64-bit ARM) run: | cd src @@ -99,7 +99,7 @@ jobs: 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: | cp -v ./src/raylib.h ./build/${{ env.RELEASE_NAME }}/include @@ -110,7 +110,7 @@ jobs: cp -v ./LICENSE ./build/${{ env.RELEASE_NAME }}/LICENSE cd build tar -czvf ${{ env.RELEASE_NAME }}.tar.gz ${{ env.RELEASE_NAME }} - + - name: Upload Artifacts uses: actions/upload-artifact@v4 with: @@ -118,7 +118,7 @@ jobs: path: | ./build/${{ env.RELEASE_NAME }} !./build/${{ env.RELEASE_NAME }}.tar.gz - + - name: Upload Artifact to Release uses: softprops/action-gh-release@v1 with: diff --git a/.github/workflows/build_macos.yml b/.github/workflows/build_macos.yml index f27140107..08ea0188a 100644 --- a/.github/workflows/build_macos.yml +++ b/.github/workflows/build_macos.yml @@ -23,14 +23,14 @@ jobs: permissions: contents: write # for actions/upload-release-asset to upload release asset runs-on: macos-latest - + env: RELEASE_NAME: raylib-dev_macos - + steps: - name: Checkout uses: actions/checkout@master - + - name: Setup Release Version run: | echo "RELEASE_NAME=raylib-${{ github.event.release.tag_name }}_macos" >> $GITHUB_ENV @@ -38,7 +38,7 @@ jobs: if: github.event_name == 'release' && github.event.action == 'published' - name: Setup Environment - run: | + run: | mkdir build cd build mkdir ${{ env.RELEASE_NAME }} @@ -46,47 +46,47 @@ jobs: mkdir include mkdir lib cd ../.. - + # Generating static + shared library, note that i386 architecture is deprecated # Defining GL_SILENCE_DEPRECATION because OpenGL is deprecated on macOS - name: Build Library run: | cd src clang --version - + # Extract version numbers from Makefile brew install grep RAYLIB_API_VERSION=`ggrep -Po 'RAYLIB_API_VERSION\s*=\s\K(.*)' Makefile` RAYLIB_VERSION=`ggrep -Po 'RAYLIB_VERSION\s*=\s\K(.*)' Makefile` - + # Build raylib x86_64 static make PLATFORM=PLATFORM_DESKTOP RAYLIB_LIBTYPE=STATIC CUSTOM_CFLAGS="-target x86_64-apple-macos10.12 -DGL_SILENCE_DEPRECATION" mv libraylib.a /tmp/libraylib_x86_64.a make clean - + # Build raylib arm64 static make PLATFORM=PLATFORM_DESKTOP RAYLIB_LIBTYPE=STATIC CUSTOM_CFLAGS="-target arm64-apple-macos11 -DGL_SILENCE_DEPRECATION" -B mv libraylib.a /tmp/libraylib_arm64.a make clean - + # Join x86_64 and arm64 static lipo -create -output ../build/${{ env.RELEASE_NAME }}/lib/libraylib.a /tmp/libraylib_x86_64.a /tmp/libraylib_arm64.a - + # Build raylib x86_64 dynamic make PLATFORM=PLATFORM_DESKTOP RAYLIB_LIBTYPE=SHARED CUSTOM_CFLAGS="-target x86_64-apple-macos10.12 -DGL_SILENCE_DEPRECATION" CUSTOM_LDFLAGS="-target x86_64-apple-macos10.12" -B mv libraylib.${RAYLIB_VERSION}.dylib /tmp/libraylib_x86_64.${RAYLIB_VERSION}.dylib make clean - + # Build raylib arm64 dynamic make PLATFORM=PLATFORM_DESKTOP RAYLIB_LIBTYPE=SHARED CUSTOM_CFLAGS="-target arm64-apple-macos11 -DGL_SILENCE_DEPRECATION" CUSTOM_LDFLAGS="-target arm64-apple-macos11" -B mv libraylib.${RAYLIB_VERSION}.dylib /tmp/libraylib_arm64.${RAYLIB_VERSION}.dylib - + # Join x86_64 and arm64 dynamic lipo -create -output ../build/${{ env.RELEASE_NAME }}/lib/libraylib.${RAYLIB_VERSION}.dylib /tmp/libraylib_x86_64.${RAYLIB_VERSION}.dylib /tmp/libraylib_arm64.${RAYLIB_VERSION}.dylib ln -sv libraylib.${RAYLIB_VERSION}.dylib ../build/${{ env.RELEASE_NAME }}/lib/libraylib.dylib ln -sv libraylib.${RAYLIB_VERSION}.dylib ../build/${{ env.RELEASE_NAME }}/lib/libraylib.${RAYLIB_API_VERSION}.dylib cd .. - + - name: Generate Artifacts run: | cp -v ./src/raylib.h ./build/${{ env.RELEASE_NAME }}/include @@ -97,7 +97,7 @@ jobs: cp -v ./LICENSE ./build/${{ env.RELEASE_NAME }}/LICENSE cd build tar -czvf ${{ env.RELEASE_NAME }}.tar.gz ${{ env.RELEASE_NAME }} - + - name: Upload Artifacts uses: actions/upload-artifact@v4 with: @@ -105,7 +105,7 @@ jobs: path: | ./build/${{ env.RELEASE_NAME }} !./build/${{ env.RELEASE_NAME }}.tar.gz - + - name: Upload Artifact to Release uses: softprops/action-gh-release@v1 with: diff --git a/.github/workflows/update_examples.yml b/.github/workflows/update_examples.yml index c4ada2bd1..fe8e4c48f 100644 --- a/.github/workflows/update_examples.yml +++ b/.github/workflows/update_examples.yml @@ -12,11 +12,11 @@ on: jobs: build: runs-on: ubuntu-latest - + steps: - name: Checkout uses: actions/checkout@v4 - + - name: Setup emsdk uses: mymindstorm/setup-emsdk@v14 with: diff --git a/projects/4coder/main.c b/projects/4coder/main.c index e33f9a1db..78e1519cb 100644 --- a/projects/4coder/main.c +++ b/projects/4coder/main.c @@ -34,7 +34,7 @@ int main() DrawFPS(10, 10); EndDrawing(); } - + CloseWindow(); return 0; } diff --git a/projects/Geany/core_basic_window.c b/projects/Geany/core_basic_window.c index 486f899cb..8ec817532 100644 --- a/projects/Geany/core_basic_window.c +++ b/projects/Geany/core_basic_window.c @@ -19,7 +19,7 @@ int main() int screenHeight = 450; InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window"); - + SetTargetFPS(60); //-------------------------------------------------------------------------------------- @@ -44,7 +44,7 @@ int main() } // De-Initialization - //-------------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------------- CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/projects/Notepad++/raylib_npp_parser/raylib_npp_parser.c b/projects/Notepad++/raylib_npp_parser/raylib_npp_parser.c index e2e256c98..a7a6663c0 100644 --- a/projects/Notepad++/raylib_npp_parser/raylib_npp_parser.c +++ b/projects/Notepad++/raylib_npp_parser/raylib_npp_parser.c @@ -15,9 +15,9 @@ - + NOTE: Generated XML text should be copied inside raylib\Notepad++\plugins\APIs\c.xml - + WARNING: Be careful with functions that split parameters into several lines, it breaks the process! LICENSE: zlib/libpng @@ -42,13 +42,13 @@ int main(int argc, char *argv[]) { FILE *rFile = fopen(argv[1], "rt"); FILE *rxmlFile = fopen("raylib_npp.xml", "wt"); - + if ((rFile == NULL) || (rxmlFile == NULL)) { printf("File could not be opened.\n"); return 0; } - + char *buffer = (char *)calloc(MAX_BUFFER_SIZE, 1); int count = 0; @@ -56,7 +56,7 @@ int main(int argc, char *argv[]) { // Read one full line fgets(buffer, MAX_BUFFER_SIZE, rFile); - + if (buffer[0] == '/') fprintf(rxmlFile, " \n", strlen(buffer) - 3, buffer + 2); else if (buffer[0] == '\n') fprintf(rxmlFile, "%s", buffer); // Direct copy of code comments else if (strncmp(buffer, "RLAPI", 5) == 0) // raylib function declaration @@ -65,33 +65,33 @@ int main(int argc, char *argv[]) char funcTypeAux[64]; char funcName[64]; char funcDesc[256]; - + char params[128]; char paramType[16][16]; char paramName[16][32]; - + int index = 0; char *ptr = NULL; - + sscanf(buffer, "RLAPI %s %[^(]s", funcType, funcName); - + if (strcmp(funcType, "const") == 0) - { + { sscanf(buffer, "RLAPI %s %s %[^(]s", funcType, funcTypeAux, funcName); strcat(funcType, " "); strcat(funcType, funcTypeAux); } - + ptr = strchr(buffer, '/'); index = (int)(ptr - buffer); - + sscanf(buffer + index, "%[^\n]s", funcDesc); // Read function comment after declaration - + ptr = strchr(buffer, '('); - + if (ptr != NULL) index = (int)(ptr - buffer); else printf("Character not found!\n"); - + sscanf(buffer + (index + 1), "%[^)]s", params); // Read what's inside '(' and ')' // Scan params string for number of func params, type and name @@ -102,23 +102,23 @@ int main(int argc, char *argv[]) if ((funcName[0] == '*') && (funcName[1] == '*')) fprintf(rxmlFile, " \n", funcName + 2); else if (funcName[0] == '*') fprintf(rxmlFile, " \n", funcName + 1); else fprintf(rxmlFile, " \n", funcName); - + fprintf(rxmlFile, " ", funcType, funcDesc + 3); - + bool paramsVoid = false; - + char paramConst[8][16]; while (paramPtr[paramsCount] != NULL) { sscanf(paramPtr[paramsCount], "%s %s\n", paramType[paramsCount], paramName[paramsCount]); - + if (strcmp(paramType[paramsCount], "void") == 0) { paramsVoid = true; break; } - + if ((strcmp(paramType[paramsCount], "const") == 0) || (strcmp(paramType[paramsCount], "unsigned") == 0)) { sscanf(paramPtr[paramsCount], "%s %s %s\n", paramConst[paramsCount], paramType[paramsCount], paramName[paramsCount]); @@ -130,17 +130,17 @@ int main(int argc, char *argv[]) paramsCount++; paramPtr[paramsCount] = strtok(NULL, ","); } - + fprintf(rxmlFile, "%s\n", paramsVoid ? "" : "\n "); fprintf(rxmlFile, " \n"); count++; printf("Function processed %02i: %s\n", count, funcName); - + memset(buffer, 0, MAX_BUFFER_SIZE); } } - + free(buffer); fclose(rFile); fclose(rxmlFile); diff --git a/src/raudio.c b/src/raudio.c index 00e819466..cd37ca305 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -2184,14 +2184,14 @@ void UpdateAudioStream(AudioStream stream, const void *data, int frameCount) bool IsAudioStreamProcessed(AudioStream stream) { bool result = false; - + if (stream.buffer != NULL) { ma_mutex_lock(&AUDIO.System.lock); result = stream.buffer->isSubBufferProcessed[0] || stream.buffer->isSubBufferProcessed[1]; ma_mutex_unlock(&AUDIO.System.lock); } - + return result; } @@ -2885,7 +2885,7 @@ static unsigned char *LoadFileData(const char *fileName, int *dataSize) static bool SaveFileData(const char *fileName, void *data, int dataSize) { bool result = true; - + if (fileName != NULL) { FILE *file = fopen(fileName, "wb"); @@ -2919,7 +2919,7 @@ static bool SaveFileData(const char *fileName, void *data, int dataSize) static bool SaveFileText(const char *fileName, char *text) { bool result = true; - + if (fileName != NULL) { FILE *file = fopen(fileName, "wt"); diff --git a/src/rshapes.c b/src/rshapes.c index 268da45f1..0a927a1d7 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -2440,7 +2440,7 @@ bool CheckCollisionCircleLine(Vector2 center, float radius, Vector2 p1, Vector2 float dx2 = (p1.x - (dotProduct*(dx))) - center.x; float dy2 = (p1.y - (dotProduct*(dy))) - center.y; float distanceSQ = ((dx2*dx2) + (dy2*dy2)); - + if (distanceSQ <= radius*radius) collision = true; } diff --git a/src/rtext.c b/src/rtext.c index 3f4c993c3..f71991e33 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1785,7 +1785,7 @@ char *TextReplace(const char *text, const char *search, const char *replacement) // Count the number of replacements needed insertPoint = (char *)text; for (count = 0; (tempPtr = strstr(insertPoint, search)); count++) insertPoint = tempPtr + searchLen; - + if ((textLen + count*(replaceLen - searchLen)) < (MAX_TEXT_BUFFER_LENGTH - 1)) { // TODO: Allow copying data replaced up to maximum buffer size and stop @@ -1971,7 +1971,7 @@ char *TextInsert(const char *text, const char *insert, int position) { int textLen = TextLength(text); int insertLen = TextLength(insert); - + if ((textLen + insertLen) < (MAX_TEXT_BUFFER_LENGTH - 1)) { // TODO: Allow copying data inserted up to maximum buffer size and stop diff --git a/tools/rlparser/rlparser.c b/tools/rlparser/rlparser.c index e69f7ad56..c2aad0794 100644 --- a/tools/rlparser/rlparser.c +++ b/tools/rlparser/rlparser.c @@ -9,10 +9,10 @@ - struct AliasInfo - struct EnumInfo - struct FunctionInfo - + WARNING: This parser is specifically designed to work with raylib.h, and has some contraints in that regards. Still, it can also work with other header files that follow same file structure - conventions as raylib.h: rlgl.h, raymath.h, raygui.h, reasings.h + conventions as raylib.h: rlgl.h, raymath.h, raygui.h, reasings.h CONSTRAINTS: This parser is specifically designed to work with raylib.h, so, it has some constraints: From e3dcb144bc30e7e039bc36cafd499eb3ed68c39d Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Mar 2026 01:33:27 +0100 Subject: [PATCH 233/319] Update rexm.c --- tools/rexm/rexm.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 48a621115..d54760023 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -1572,13 +1572,13 @@ int main(int argc, char *argv[]) " SaveFileText(\"outputLogFileName\", logText);\n" " emscripten_run_script(\"saveFileFromMEMFSToDisk('outputLogFileName','outputLogFileName')\");\n\n" " return 0"; - char *returnReplaceTextUpdated = TextReplacEx(returnReplaceText, "outputLogFileName", TextFormat("%s.log", exName)); + char *returnReplaceTextUpdated = TextReplaceAlloc(returnReplaceText, "outputLogFileName", TextFormat("%s.log", exName)); char *srcTextUpdated[4] = { 0 }; - srcTextUpdated[0] = TextReplacEx(srcText, "int main(void)\n{", mainReplaceText); - srcTextUpdated[1] = TextReplacEx(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)"); - srcTextUpdated[2] = TextReplacEx(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;"); - srcTextUpdated[3] = TextReplacEx(srcTextUpdated[2], " return 0", returnReplaceTextUpdated); + srcTextUpdated[0] = TextReplaceAlloc(srcText, "int main(void)\n{", mainReplaceText); + srcTextUpdated[1] = TextReplaceAlloc(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)"); + srcTextUpdated[2] = TextReplaceAlloc(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;"); + srcTextUpdated[3] = TextReplaceAlloc(srcTextUpdated[2], " return 0", returnReplaceTextUpdated); MemFree(returnReplaceTextUpdated); UnloadFileText(srcText); @@ -1628,9 +1628,9 @@ int main(int argc, char *argv[]) " if ((argc > 1) && (argc == 3) && (strcmp(argv[1], \"--frames\") != 0)) requestedTestFrames = atoi(argv[2]);\n"; char *srcTextUpdated[3] = { 0 }; - srcTextUpdated[0] = TextReplacEx(srcText, "int main(void)\n{", mainReplaceText); - srcTextUpdated[1] = TextReplacEx(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)"); - srcTextUpdated[2] = TextReplacEx(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;"); + srcTextUpdated[0] = TextReplaceAlloc(srcText, "int main(void)\n{", mainReplaceText); + srcTextUpdated[1] = TextReplaceAlloc(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)"); + srcTextUpdated[2] = TextReplaceAlloc(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;"); UnloadFileText(srcText); SaveFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), srcTextUpdated[2]); From d3cc78d9d79b3a786e0815fc19b94702cade6b44 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Mar 2026 01:44:33 +0100 Subject: [PATCH 234/319] Update rexm.c --- tools/rexm/rexm.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index d54760023..b160a9be1 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -2037,8 +2037,10 @@ static int UpdateRequiredFiles(void) // In this case, we focus on web building for: glsl100 if (TextFindIndex(resPaths[r], "glsl%i") > -1) { + char *resPathUpdated = TextReplaceAlloc(resPaths[r], "glsl%i", "glsl100"); memset(resPaths[r], 0, 256); - strcpy(resPaths[r], TextReplace(resPaths[r], "glsl%i", "glsl100")); + strcpy(resPaths[r], resPathUpdated); + RL_FREE(resPathUpdated); } if (r < (resPathCount - 1)) @@ -2140,7 +2142,7 @@ static int UpdateRequiredFiles(void) { 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"); + "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.h) module.\n\n"); } else if (i == 6) // "audio" { @@ -2903,14 +2905,14 @@ static void UpdateWebMetadata(const char *exHtmlPath, const char *exFilePath) UnloadFileText(exText); // Update example.html required text - exHtmlTextUpdated[0] = TextReplace(exHtmlText, "raylib web game", exTitle); - exHtmlTextUpdated[1] = TextReplace(exHtmlTextUpdated[0], "New raylib web videogame, developed using raylib videogames library", exDescription); - exHtmlTextUpdated[2] = TextReplace(exHtmlTextUpdated[1], "https://www.raylib.com/common/raylib_logo.png", + exHtmlTextUpdated[0] = TextReplaceAlloc(exHtmlText, "raylib web game", exTitle); + exHtmlTextUpdated[1] = TextReplaceAlloc(exHtmlTextUpdated[0], "New raylib web videogame, developed using raylib videogames library", exDescription); + exHtmlTextUpdated[2] = TextReplaceAlloc(exHtmlTextUpdated[1], "https://www.raylib.com/common/raylib_logo.png", TextFormat("https://raw.githubusercontent.com/raysan5/raylib/master/examples/%s/%s.png", exCategory, exName)); - exHtmlTextUpdated[3] = TextReplace(exHtmlTextUpdated[2], "https://www.raylib.com/games.html", + exHtmlTextUpdated[3] = TextReplaceAlloc(exHtmlTextUpdated[2], "https://www.raylib.com/games.html", TextFormat("https://www.raylib.com/examples/%s/%s.html", exCategory, exName)); - exHtmlTextUpdated[4] = TextReplace(exHtmlTextUpdated[3], "raylib - example", TextFormat("raylib - %s", exName)); // og:site_name - exHtmlTextUpdated[5] = TextReplace(exHtmlTextUpdated[4], "https://github.com/raysan5/raylib", + exHtmlTextUpdated[4] = TextReplaceAlloc(exHtmlTextUpdated[3], "raylib - example", TextFormat("raylib - %s", exName)); // og:site_name + exHtmlTextUpdated[5] = TextReplaceAlloc(exHtmlTextUpdated[4], "https://github.com/raysan5/raylib", TextFormat("https://github.com/raysan5/raylib/blob/master/examples/%s/%s.c", exCategory, exName)); SaveFileText(exHtmlPathCopy, exHtmlTextUpdated[5]); From a1bf8d9c752041423a245a21bce73274a9215c9e Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Mar 2026 21:17:51 +0200 Subject: [PATCH 235/319] Update cgltf.h --- src/external/cgltf.h | 211 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 185 insertions(+), 26 deletions(-) diff --git a/src/external/cgltf.h b/src/external/cgltf.h index 36fd644e1..316a11dfd 100644 --- a/src/external/cgltf.h +++ b/src/external/cgltf.h @@ -1,7 +1,7 @@ /** * cgltf - a single-file glTF 2.0 parser written in C99. * - * Version: 1.14 + * Version: 1.15 * * Website: https://github.com/jkuhlmann/cgltf * @@ -141,7 +141,7 @@ typedef struct cgltf_memory_options typedef struct cgltf_file_options { cgltf_result(*read)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, const char* path, cgltf_size* size, void** data); - void (*release)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data); + void (*release)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data, cgltf_size size); void* user_data; } cgltf_file_options; @@ -296,6 +296,7 @@ typedef enum cgltf_meshopt_compression_filter { cgltf_meshopt_compression_filter_octahedral, cgltf_meshopt_compression_filter_quaternion, cgltf_meshopt_compression_filter_exponential, + cgltf_meshopt_compression_filter_color, cgltf_meshopt_compression_filter_max_enum } cgltf_meshopt_compression_filter; @@ -308,6 +309,7 @@ typedef struct cgltf_meshopt_compression cgltf_size count; cgltf_meshopt_compression_mode mode; cgltf_meshopt_compression_filter filter; + cgltf_bool is_khr; } cgltf_meshopt_compression; typedef struct cgltf_buffer_view @@ -376,13 +378,29 @@ typedef struct cgltf_image cgltf_extension* extensions; } cgltf_image; +typedef enum cgltf_filter_type { + cgltf_filter_type_undefined = 0, + cgltf_filter_type_nearest = 9728, + cgltf_filter_type_linear = 9729, + cgltf_filter_type_nearest_mipmap_nearest = 9984, + cgltf_filter_type_linear_mipmap_nearest = 9985, + cgltf_filter_type_nearest_mipmap_linear = 9986, + cgltf_filter_type_linear_mipmap_linear = 9987 +} cgltf_filter_type; + +typedef enum cgltf_wrap_mode { + cgltf_wrap_mode_clamp_to_edge = 33071, + cgltf_wrap_mode_mirrored_repeat = 33648, + cgltf_wrap_mode_repeat = 10497 +} cgltf_wrap_mode; + typedef struct cgltf_sampler { char* name; - cgltf_int mag_filter; - cgltf_int min_filter; - cgltf_int wrap_s; - cgltf_int wrap_t; + cgltf_filter_type mag_filter; + cgltf_filter_type min_filter; + cgltf_wrap_mode wrap_s; + cgltf_wrap_mode wrap_t; cgltf_extras extras; cgltf_size extensions_count; cgltf_extension* extensions; @@ -500,6 +518,14 @@ typedef struct cgltf_iridescence cgltf_texture_view iridescence_thickness_texture; } cgltf_iridescence; +typedef struct cgltf_diffuse_transmission +{ + cgltf_texture_view diffuse_transmission_texture; + cgltf_float diffuse_transmission_factor; + cgltf_float diffuse_transmission_color_factor[3]; + cgltf_texture_view diffuse_transmission_color_texture; +} cgltf_diffuse_transmission; + typedef struct cgltf_anisotropy { cgltf_float anisotropy_strength; @@ -525,6 +551,7 @@ typedef struct cgltf_material cgltf_bool has_sheen; cgltf_bool has_emissive_strength; cgltf_bool has_iridescence; + cgltf_bool has_diffuse_transmission; cgltf_bool has_anisotropy; cgltf_bool has_dispersion; cgltf_pbr_metallic_roughness pbr_metallic_roughness; @@ -537,6 +564,7 @@ typedef struct cgltf_material cgltf_volume volume; cgltf_emissive_strength emissive_strength; cgltf_iridescence iridescence; + cgltf_diffuse_transmission diffuse_transmission; cgltf_anisotropy anisotropy; cgltf_dispersion dispersion; cgltf_texture_view normal_texture; @@ -743,6 +771,7 @@ typedef struct cgltf_data { cgltf_file_type file_type; void* file_data; + cgltf_size file_size; cgltf_asset asset; @@ -844,6 +873,8 @@ void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix) const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view); +const cgltf_accessor* cgltf_find_accessor(const cgltf_primitive* prim, cgltf_attribute_type type, cgltf_int index); + cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size); cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size); cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index); @@ -1071,9 +1102,10 @@ static cgltf_result cgltf_default_file_read(const struct cgltf_memory_options* m return cgltf_result_success; } -static void cgltf_default_file_release(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data) +static void cgltf_default_file_release(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data, cgltf_size size) { (void)file_options; + (void)size; void (*memfree)(void*, void*) = memory_options->free_func ? memory_options->free_func : &cgltf_default_free; memfree(memory_options->user_data, data); } @@ -1220,7 +1252,7 @@ cgltf_result cgltf_parse_file(const cgltf_options* options, const char* path, cg } cgltf_result (*file_read)(const struct cgltf_memory_options*, const struct cgltf_file_options*, const char*, cgltf_size*, void**) = options->file.read ? options->file.read : &cgltf_default_file_read; - void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data) = options->file.release ? options->file.release : cgltf_default_file_release; + void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data, cgltf_size size) = options->file.release ? options->file.release : cgltf_default_file_release; void* file_data = NULL; cgltf_size file_size = 0; @@ -1234,11 +1266,12 @@ cgltf_result cgltf_parse_file(const cgltf_options* options, const char* path, cg if (result != cgltf_result_success) { - file_release(&options->memory, &options->file, file_data); + file_release(&options->memory, &options->file, file_data, file_size); return result; } (*out_data)->file_data = file_data; + (*out_data)->file_size = file_size; return cgltf_result_success; } @@ -1630,8 +1663,8 @@ cgltf_result cgltf_validate(cgltf_data* data) CGLTF_ASSERT_IF((mc->mode == cgltf_meshopt_compression_mode_triangles || mc->mode == cgltf_meshopt_compression_mode_indices) && mc->filter != cgltf_meshopt_compression_filter_none, cgltf_result_invalid_gltf); CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_octahedral && mc->stride != 4 && mc->stride != 8, cgltf_result_invalid_gltf); - CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_quaternion && mc->stride != 8, cgltf_result_invalid_gltf); + CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_color && mc->stride != 4 && mc->stride != 8, cgltf_result_invalid_gltf); } } @@ -1822,7 +1855,7 @@ void cgltf_free(cgltf_data* data) return; } - void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data) = data->file.release ? data->file.release : cgltf_default_file_release; + void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data, cgltf_size size) = data->file.release ? data->file.release : cgltf_default_file_release; data->memory.free_func(data->memory.user_data, data->asset.copyright); data->memory.free_func(data->memory.user_data, data->asset.generator); @@ -1857,7 +1890,7 @@ void cgltf_free(cgltf_data* data) if (data->buffers[i].data_free_method == cgltf_data_free_method_file_release) { - file_release(&data->memory, &data->file, data->buffers[i].data); + file_release(&data->memory, &data->file, data->buffers[i].data, data->buffers[i].size); } else if (data->buffers[i].data_free_method == cgltf_data_free_method_memory_free) { @@ -2096,7 +2129,7 @@ void cgltf_free(cgltf_data* data) data->memory.free_func(data->memory.user_data, data->extensions_required); - file_release(&data->memory, &data->file, data->file_data); + file_release(&data->memory, &data->file, data->file_data, data->file_size); data->memory.free_func(data->memory.user_data, data); } @@ -2312,11 +2345,58 @@ const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view) return result; } +const cgltf_accessor* cgltf_find_accessor(const cgltf_primitive* prim, cgltf_attribute_type type, cgltf_int index) +{ + for (cgltf_size i = 0; i < prim->attributes_count; ++i) + { + const cgltf_attribute* attr = &prim->attributes[i]; + if (attr->type == type && attr->index == index) + return attr->data; + } + + return NULL; +} + +static const uint8_t* cgltf_find_sparse_index(const cgltf_accessor* accessor, cgltf_size needle) +{ + const cgltf_accessor_sparse* sparse = &accessor->sparse; + const uint8_t* index_data = cgltf_buffer_view_data(sparse->indices_buffer_view); + const uint8_t* value_data = cgltf_buffer_view_data(sparse->values_buffer_view); + + if (index_data == NULL || value_data == NULL) + return NULL; + + index_data += sparse->indices_byte_offset; + value_data += sparse->values_byte_offset; + + cgltf_size index_stride = cgltf_component_size(sparse->indices_component_type); + + cgltf_size offset = 0; + cgltf_size length = sparse->count; + + while (length) + { + cgltf_size rem = length % 2; + length /= 2; + + cgltf_size index = cgltf_component_read_index(index_data + (offset + length) * index_stride, sparse->indices_component_type); + offset += index < needle ? length + rem : 0; + } + + if (offset == sparse->count) + return NULL; + + cgltf_size index = cgltf_component_read_index(index_data + offset * index_stride, sparse->indices_component_type); + return index == needle ? value_data + offset * accessor->stride : NULL; +} + cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size) { if (accessor->is_sparse) { - return 0; + const uint8_t* element = cgltf_find_sparse_index(accessor, index); + if (element) + return cgltf_element_read_float(element, accessor->type, accessor->component_type, accessor->normalized, out, element_size); } if (accessor->buffer_view == NULL) { @@ -2460,11 +2540,13 @@ cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size i { if (accessor->is_sparse) { - return 0; + const uint8_t* element = cgltf_find_sparse_index(accessor, index); + if (element) + return cgltf_element_read_uint(element, accessor->type, accessor->component_type, out, element_size); } if (accessor->buffer_view == NULL) { - memset(out, 0, element_size * sizeof( cgltf_uint )); + memset(out, 0, element_size * sizeof(cgltf_uint)); return 1; } const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); @@ -2480,7 +2562,9 @@ cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size { if (accessor->is_sparse) { - return 0; // This is an error case, but we can't communicate the error with existing interface. + const uint8_t* element = cgltf_find_sparse_index(accessor, index); + if (element) + return cgltf_component_read_index(element, accessor->component_type); } if (accessor->buffer_view == NULL) { @@ -2598,7 +2682,10 @@ cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, void* o return accessor->count; } - index_count = accessor->count < index_count ? accessor->count : index_count; + cgltf_size numbers_per_element = cgltf_num_components(accessor->type); + cgltf_size available_numbers = accessor->count * numbers_per_element; + + index_count = available_numbers < index_count ? available_numbers : index_count; cgltf_size index_component_size = cgltf_component_size(accessor->component_type); if (accessor->is_sparse) @@ -2620,15 +2707,23 @@ cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, void* o } element += accessor->offset; - if (index_component_size == out_component_size && accessor->stride == out_component_size) + if (index_component_size == out_component_size && accessor->stride == out_component_size * numbers_per_element) { memcpy(out, element, index_count * index_component_size); return index_count; } + // Data couldn't be copied with memcpy due to stride being larger than the component size. + // OR // The component size of the output array is larger than the component size of the index data, so index data will be padded. switch (out_component_size) { + case 1: + for (cgltf_size index = 0; index < index_count; index++, element += accessor->stride) + { + ((uint8_t*)out)[index] = (uint8_t)cgltf_component_read_index(element, accessor->component_type); + } + break; case 2: for (cgltf_size index = 0; index < index_count; index++, element += accessor->stride) { @@ -2642,7 +2737,7 @@ cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, void* o } break; default: - break; + return 0; } return index_count; @@ -4278,6 +4373,52 @@ static int cgltf_parse_json_iridescence(cgltf_options* options, jsmntok_t const* return i; } +static int cgltf_parse_json_diffuse_transmission(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_diffuse_transmission* out_diff_transmission) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + // Defaults + cgltf_fill_float_array(out_diff_transmission->diffuse_transmission_color_factor, 3, 1.0f); + out_diff_transmission->diffuse_transmission_factor = 0.f; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "diffuseTransmissionFactor") == 0) + { + ++i; + out_diff_transmission->diffuse_transmission_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "diffuseTransmissionTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_diff_transmission->diffuse_transmission_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "diffuseTransmissionColorFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_diff_transmission->diffuse_transmission_color_factor, 3); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "diffuseTransmissionColorTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_diff_transmission->diffuse_transmission_color_texture); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + static int cgltf_parse_json_anisotropy(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_anisotropy* out_anisotropy) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); @@ -4406,8 +4547,8 @@ static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tok (void)options; CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); - out_sampler->wrap_s = 10497; - out_sampler->wrap_t = 10497; + out_sampler->wrap_s = cgltf_wrap_mode_repeat; + out_sampler->wrap_t = cgltf_wrap_mode_repeat; int size = tokens[i].size; ++i; @@ -4424,28 +4565,28 @@ static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tok { ++i; out_sampler->mag_filter - = cgltf_json_to_int(tokens + i, json_chunk); + = (cgltf_filter_type)cgltf_json_to_int(tokens + i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens + i, json_chunk, "minFilter") == 0) { ++i; out_sampler->min_filter - = cgltf_json_to_int(tokens + i, json_chunk); + = (cgltf_filter_type)cgltf_json_to_int(tokens + i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapS") == 0) { ++i; out_sampler->wrap_s - = cgltf_json_to_int(tokens + i, json_chunk); + = (cgltf_wrap_mode)cgltf_json_to_int(tokens + i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapT") == 0) { ++i; out_sampler->wrap_t - = cgltf_json_to_int(tokens + i, json_chunk); + = (cgltf_wrap_mode)cgltf_json_to_int(tokens + i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) @@ -4766,6 +4907,11 @@ static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* to out_material->has_iridescence = 1; i = cgltf_parse_json_iridescence(options, tokens, i + 1, json_chunk, &out_material->iridescence); } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_diffuse_transmission") == 0) + { + out_material->has_diffuse_transmission = 1; + i = cgltf_parse_json_diffuse_transmission(options, tokens, i + 1, json_chunk, &out_material->diffuse_transmission); + } else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_anisotropy") == 0) { out_material->has_anisotropy = 1; @@ -4974,6 +5120,10 @@ static int cgltf_parse_json_meshopt_compression(cgltf_options* options, jsmntok_ { out_meshopt_compression->filter = cgltf_meshopt_compression_filter_exponential; } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "COLOR") == 0) + { + out_meshopt_compression->filter = cgltf_meshopt_compression_filter_color; + } ++i; } else @@ -5084,6 +5234,12 @@ static int cgltf_parse_json_buffer_view(cgltf_options* options, jsmntok_t const* out_buffer_view->has_meshopt_compression = 1; i = cgltf_parse_json_meshopt_compression(options, tokens, i + 1, json_chunk, &out_buffer_view->meshopt_compression); } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_meshopt_compression") == 0) + { + out_buffer_view->has_meshopt_compression = 1; + out_buffer_view->meshopt_compression.is_khr = 1; + i = cgltf_parse_json_meshopt_compression(options, tokens, i + 1, json_chunk, &out_buffer_view->meshopt_compression); + } else { i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_buffer_view->extensions[out_buffer_view->extensions_count++])); @@ -6629,6 +6785,9 @@ static int cgltf_fixup_pointers(cgltf_data* data) CGLTF_PTRFIXUP(data->materials[i].iridescence.iridescence_texture.texture, data->textures, data->textures_count); CGLTF_PTRFIXUP(data->materials[i].iridescence.iridescence_thickness_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].diffuse_transmission.diffuse_transmission_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].diffuse_transmission.diffuse_transmission_color_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].anisotropy.anisotropy_texture.texture, data->textures, data->textures_count); } From 0b87a35e5a6ba6a663daeba7cc2e73dd2cbc1c4d Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Mar 2026 21:18:28 +0200 Subject: [PATCH 236/319] Update dr_flac.h --- src/external/dr_flac.h | 306 +++++++++++++++++++++++++++-------------- 1 file changed, 205 insertions(+), 101 deletions(-) diff --git a/src/external/dr_flac.h b/src/external/dr_flac.h index 497fcddd5..2891194c7 100644 --- a/src/external/dr_flac.h +++ b/src/external/dr_flac.h @@ -1,6 +1,6 @@ /* FLAC audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. -dr_flac - v0.13.0 - TBD +dr_flac - v0.13.3 - 2026-01-17 David Reid - mackron@gmail.com @@ -126,7 +126,7 @@ extern "C" { #define DRFLAC_VERSION_MAJOR 0 #define DRFLAC_VERSION_MINOR 13 -#define DRFLAC_VERSION_REVISION 0 +#define DRFLAC_VERSION_REVISION 3 #define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) #include /* For size_t. */ @@ -331,6 +331,12 @@ typedef struct */ drflac_uint32 type; + /* The size in bytes of the block and the buffer pointed to by pRawData if it's non-NULL. */ + drflac_uint32 rawDataSize; + + /* The offset in the stream of the raw data. */ + drflac_uint64 rawDataOffset; + /* A pointer to the raw data. This points to a temporary buffer so don't hold on to it. It's best to not modify the contents of this buffer. Use the structures below for more meaningful and structured @@ -338,9 +344,6 @@ typedef struct */ const void* pRawData; - /* The size in bytes of the block and the buffer pointed to by pRawData if it's non-NULL. */ - drflac_uint32 rawDataSize; - union { drflac_streaminfo streaminfo; @@ -392,6 +395,7 @@ typedef struct drflac_uint32 colorDepth; drflac_uint32 indexColorCount; drflac_uint32 pictureDataSize; + drflac_uint64 pictureDataOffset; /* Offset from the start of the stream. */ const drflac_uint8* pPictureData; } picture; } data; @@ -2712,9 +2716,17 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) #if defined(__GNUC__) || defined(__clang__) #if defined(DRFLAC_X64) { + /* + A note on lzcnt. + + We check for the presence of the lzcnt instruction at runtime before calling this function, but we still generate this code. I have had + a report where the assembler does not recognize the lzcnt instruction. To work around this we are going to use `rep; bsr` instead which + has an identical byte encoding as lzcnt, and should hopefully improve compatibility with older assemblers. + */ drflac_uint64 r; __asm__ __volatile__ ( - "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" + "rep; bsr{q %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" + /*"lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"*/ ); return (drflac_uint32)r; @@ -2723,12 +2735,13 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) { drflac_uint32 r; __asm__ __volatile__ ( - "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" + "rep; bsr{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" + /*"lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"*/ ); return r; } - #elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !defined(DRFLAC_64BIT) /* <-- I haven't tested 64-bit inline assembly, so only enabling this for the 32-bit build for now. */ + #elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !(defined(__thumb__) && !defined(__thumb2__)) && !defined(DRFLAC_64BIT) /* <-- I haven't tested 64-bit inline assembly, so only enabling this for the 32-bit build for now. */ { unsigned int r; __asm__ __volatile__ ( @@ -6434,8 +6447,9 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d runningFilePos += 4; metadata.type = blockType; - metadata.pRawData = NULL; metadata.rawDataSize = 0; + metadata.rawDataOffset = runningFilePos; + metadata.pRawData = NULL; switch (blockType) { @@ -6712,59 +6726,151 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d } if (onMeta) { - void* pRawData; - const char* pRunningData; - const char* pRunningDataEnd; + drflac_bool32 result = DRFLAC_TRUE; + drflac_uint32 blockSizeRemaining = blockSize; + char* pMime = NULL; + char* pDescription = NULL; + void* pPictureData = NULL; - pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return DRFLAC_FALSE; + if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.type, 4) != 4) { + result = DRFLAC_FALSE; + goto done_flac; + } + blockSizeRemaining -= 4; + metadata.data.picture.type = drflac__be2host_32(metadata.data.picture.type); + + + if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.mimeLength, 4) != 4) { + result = DRFLAC_FALSE; + goto done_flac; + } + blockSizeRemaining -= 4; + metadata.data.picture.mimeLength = drflac__be2host_32(metadata.data.picture.mimeLength); + + pMime = (char*)drflac__malloc_from_callbacks(metadata.data.picture.mimeLength + 1, pAllocationCallbacks); /* +1 for null terminator. */ + if (pMime == NULL) { + result = DRFLAC_FALSE; + goto done_flac; } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + if (blockSizeRemaining < metadata.data.picture.mimeLength || onRead(pUserData, pMime, metadata.data.picture.mimeLength) != metadata.data.picture.mimeLength) { + result = DRFLAC_FALSE; + goto done_flac; + } + blockSizeRemaining -= metadata.data.picture.mimeLength; + pMime[metadata.data.picture.mimeLength] = '\0'; /* Null terminate for safety. */ + metadata.data.picture.mime = (const char*)pMime; + + + if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.descriptionLength, 4) != 4) { + result = DRFLAC_FALSE; + goto done_flac; + } + blockSizeRemaining -= 4; + metadata.data.picture.descriptionLength = drflac__be2host_32(metadata.data.picture.descriptionLength); + + pDescription = (char*)drflac__malloc_from_callbacks(metadata.data.picture.descriptionLength + 1, pAllocationCallbacks); /* +1 for null terminator. */ + if (pDescription == NULL) { + result = DRFLAC_FALSE; + goto done_flac; } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - - pRunningData = (const char*)pRawData; - pRunningDataEnd = (const char*)pRawData + blockSize; - - metadata.data.picture.type = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.mimeLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - - /* Need space for the rest of the block */ - if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + if (blockSizeRemaining < metadata.data.picture.descriptionLength || onRead(pUserData, pDescription, metadata.data.picture.descriptionLength) != metadata.data.picture.descriptionLength) { + result = DRFLAC_FALSE; + goto done_flac; } - metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; - metadata.data.picture.descriptionLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + blockSizeRemaining -= metadata.data.picture.descriptionLength; + pDescription[metadata.data.picture.descriptionLength] = '\0'; /* Null terminate for safety. */ + metadata.data.picture.description = (const char*)pDescription; - /* Need space for the rest of the block */ - if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + + if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.width, 4) != 4) { + result = DRFLAC_FALSE; + goto done_flac; } - metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; - metadata.data.picture.width = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.height = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.colorDepth = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.indexColorCount = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.pictureDataSize = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData; + blockSizeRemaining -= 4; + metadata.data.picture.width = drflac__be2host_32(metadata.data.picture.width); - /* Need space for the picture after the block */ - if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) { /* <-- Note the order of operations to avoid overflow to a valid value */ - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.height, 4) != 4) { + result = DRFLAC_FALSE; + goto done_flac; + } + blockSizeRemaining -= 4; + metadata.data.picture.height = drflac__be2host_32(metadata.data.picture.height); + + if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.colorDepth, 4) != 4) { + result = DRFLAC_FALSE; + goto done_flac; + } + blockSizeRemaining -= 4; + metadata.data.picture.colorDepth = drflac__be2host_32(metadata.data.picture.colorDepth); + + if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.indexColorCount, 4) != 4) { + result = DRFLAC_FALSE; + goto done_flac; + } + blockSizeRemaining -= 4; + metadata.data.picture.indexColorCount = drflac__be2host_32(metadata.data.picture.indexColorCount); + + + /* Picture data. */ + if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.pictureDataSize, 4) != 4) { + result = DRFLAC_FALSE; + goto done_flac; + } + blockSizeRemaining -= 4; + metadata.data.picture.pictureDataSize = drflac__be2host_32(metadata.data.picture.pictureDataSize); + + if (blockSizeRemaining < metadata.data.picture.pictureDataSize) { + result = DRFLAC_FALSE; + goto done_flac; } - onMeta(pUserDataMD, &metadata); + /* For the actual image data we want to store the offset to the start of the stream. */ + metadata.data.picture.pictureDataOffset = runningFilePos + (blockSize - blockSizeRemaining); - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + /* + For the allocation of image data, we can allow memory allocation to fail, in which case we just leave + the pointer as null. If it fails, we need to fall back to seeking past the image data. + */ + #ifndef DR_FLAC_NO_PICTURE_METADATA_MALLOC + pPictureData = drflac__malloc_from_callbacks(metadata.data.picture.pictureDataSize, pAllocationCallbacks); + if (pPictureData != NULL) { + if (onRead(pUserData, pPictureData, metadata.data.picture.pictureDataSize) != metadata.data.picture.pictureDataSize) { + result = DRFLAC_FALSE; + goto done_flac; + } + } else + #endif + { + /* Allocation failed. We need to seek past the picture data. */ + if (!onSeek(pUserData, metadata.data.picture.pictureDataSize, DRFLAC_SEEK_CUR)) { + result = DRFLAC_FALSE; + goto done_flac; + } + } + + blockSizeRemaining -= metadata.data.picture.pictureDataSize; + (void)blockSizeRemaining; + + metadata.data.picture.pPictureData = (const drflac_uint8*)pPictureData; + + + /* Only fire the callback if we actually have a way to read the image data. We must have either a valid offset, or a valid data pointer. */ + if (metadata.data.picture.pictureDataOffset != 0 || metadata.data.picture.pPictureData != NULL) { + onMeta(pUserDataMD, &metadata); + } else { + /* Don't have a valid offset or data pointer, so just pretend we don't have a picture metadata. */ + } + + done_flac: + drflac__free_from_callbacks(pMime, pAllocationCallbacks); + drflac__free_from_callbacks(pDescription, pAllocationCallbacks); + drflac__free_from_callbacks(pPictureData, pAllocationCallbacks); + + if (result != DRFLAC_TRUE) { + return DRFLAC_FALSE; + } } } break; @@ -6800,13 +6906,16 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d */ if (onMeta) { void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return DRFLAC_FALSE; - } - - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + if (pRawData != NULL) { + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + } else { + /* Allocation failed. We need to seek past the block. */ + if (!onSeek(pUserData, blockSize, DRFLAC_SEEK_CUR)) { + return DRFLAC_FALSE; + } } metadata.pRawData = pRawData; @@ -8699,7 +8808,7 @@ static drflac_bool32 drflac__on_tell_stdio(void* pUserData, drflac_int64* pCurso DRFLAC_ASSERT(pFileStdio != NULL); DRFLAC_ASSERT(pCursor != NULL); -#if defined(_WIN32) +#if defined(_WIN32) && !defined(NXDK) #if defined(_MSC_VER) && _MSC_VER > 1200 result = _ftelli64(pFileStdio); #else @@ -8821,8 +8930,6 @@ static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_ DRFLAC_ASSERT(memoryStream != NULL); - newCursor = memoryStream->currentReadPos; - if (origin == DRFLAC_SEEK_SET) { newCursor = 0; } else if (origin == DRFLAC_SEEK_CUR) { @@ -11702,58 +11809,43 @@ static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned { \ type* pSampleData = NULL; \ drflac_uint64 totalPCMFrameCount; \ + type buffer[4096]; \ + drflac_uint64 pcmFramesRead; \ + size_t sampleDataBufferSize = sizeof(buffer); \ \ DRFLAC_ASSERT(pFlac != NULL); \ \ - totalPCMFrameCount = pFlac->totalPCMFrameCount; \ + totalPCMFrameCount = 0; \ \ - if (totalPCMFrameCount == 0) { \ - type buffer[4096]; \ - drflac_uint64 pcmFramesRead; \ - size_t sampleDataBufferSize = sizeof(buffer); \ + pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ + if (pSampleData == NULL) { \ + goto on_error; \ + } \ \ - pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ - if (pSampleData == NULL) { \ - goto on_error; \ - } \ + while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ + if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \ + type* pNewSampleData; \ + size_t newSampleDataBufferSize; \ \ - while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ - if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \ - type* pNewSampleData; \ - size_t newSampleDataBufferSize; \ - \ - newSampleDataBufferSize = sampleDataBufferSize * 2; \ - pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \ - if (pNewSampleData == NULL) { \ - drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \ - goto on_error; \ - } \ - \ - sampleDataBufferSize = newSampleDataBufferSize; \ - pSampleData = pNewSampleData; \ + newSampleDataBufferSize = sampleDataBufferSize * 2; \ + pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \ + if (pNewSampleData == NULL) { \ + drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \ + goto on_error; \ } \ \ - DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ - totalPCMFrameCount += pcmFramesRead; \ + sampleDataBufferSize = newSampleDataBufferSize; \ + pSampleData = pNewSampleData; \ } \ \ - /* At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to \ - protect those ears from random noise! */ \ - DRFLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \ - } else { \ - drflac_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \ - if (dataSize > (drflac_uint64)DRFLAC_SIZE_MAX) { \ - goto on_error; /* The decoded data is too big. */ \ - } \ - \ - pSampleData = (type*)drflac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); /* <-- Safe cast as per the check above. */ \ - if (pSampleData == NULL) { \ - goto on_error; \ - } \ - \ - totalPCMFrameCount = drflac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \ + DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ + totalPCMFrameCount += pcmFramesRead; \ } \ \ + /* At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to \ + protect those ears from random noise! */ \ + DRFLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \ + \ if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ if (channelsOut) *channelsOut = pFlac->channels; \ if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \ @@ -12077,7 +12169,19 @@ DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterat /* REVISION HISTORY ================ -v0.13.0 - TBD +v0.13.3 - 2026-01-17 + - Fix a compiler compatibility issue with some inlined assembly. + - Fix a compilation warning. + +v0.13.2 - 2025-12-02 + - Improve robustness of the parsing of picture metadata to improve support for memory constrained embedded devices. + - Fix a warning about an assigned by unused variable. + - Improvements to drflac_open_and_read_pcm_frames_*() and family to avoid excessively large memory allocations from malformed files. + +v0.13.1 - 2025-09-10 + - Fix an error with the NXDK build. + +v0.13.0 - 2025-07-23 - 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 From b09da8fce8e2f31cdd0b5a98c9187d2fec49aca3 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Mar 2026 21:18:56 +0200 Subject: [PATCH 237/319] Update dr_mp3.h --- src/external/dr_mp3.h | 171 ++++++++++++++++++++++++++---------------- 1 file changed, 108 insertions(+), 63 deletions(-) diff --git a/src/external/dr_mp3.h b/src/external/dr_mp3.h index cc033ce87..c0969c476 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.7.0 - TBD +dr_mp3 - v0.7.4 - TBD David Reid - mackron@gmail.com @@ -72,7 +72,7 @@ extern "C" { #define DRMP3_VERSION_MAJOR 0 #define DRMP3_VERSION_MINOR 7 -#define DRMP3_VERSION_REVISION 0 +#define DRMP3_VERSION_REVISION 4 #define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) #include /* For size_t. */ @@ -257,16 +257,45 @@ typedef struct Low Level Push API ================== */ +#define DRMP3_MAX_BITRESERVOIR_BYTES 511 +#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */ +#define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE /* MUST be >= 320000/8/32000*1152 = 1440 */ + typedef struct { int frame_bytes, channels, sample_rate, layer, bitrate_kbps; } drmp3dec_frame_info; +typedef struct +{ + const drmp3_uint8 *buf; + int pos, limit; +} drmp3_bs; + +typedef struct +{ + const drmp3_uint8 *sfbtab; + drmp3_uint16 part_23_length, big_values, scalefac_compress; + drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; + drmp3_uint8 table_select[3], region_count[3], subblock_gain[3]; + drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi; +} drmp3_L3_gr_info; + +typedef struct +{ + drmp3_bs bs; + drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES]; + drmp3_L3_gr_info gr_info[4]; + float grbuf[2][576], scf[40], syn[18 + 15][2*32]; + drmp3_uint8 ist_pos[2][39]; +} drmp3dec_scratch; + typedef struct { float mdct_overlap[2][9*32], qmf_state[15*2*32]; int reserv, free_format_bytes; drmp3_uint8 header[4], reserv_buf[511]; + drmp3dec_scratch scratch; } drmp3dec; /* Initializes a low level decoder. */ @@ -592,14 +621,10 @@ DRMP3_API const char* drmp3_version_string(void) #define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset))) -#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */ #ifndef DRMP3_MAX_FRAME_SYNC_MATCHES #define DRMP3_MAX_FRAME_SYNC_MATCHES 10 #endif -#define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE /* MUST be >= 320000/8/32000*1152 = 1440 */ - -#define DRMP3_MAX_BITRESERVOIR_BYTES 511 #define DRMP3_SHORT_BLOCK_TYPE 2 #define DRMP3_STOP_BLOCK_TYPE 3 #define DRMP3_MODE_MONO 3 @@ -632,8 +657,10 @@ 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) || defined(_M_ARM64EC)) -/* x64 always have SSE2, arm64 always have neon, no need for generic code */ +#if !defined(DR_MP3_ONLY_SIMD) && ((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__))) +#define DR_MP3_ONLY_SIMD +#endif +#if !defined(DR_MP3_ONLY_SIMD) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) #define DR_MP3_ONLY_SIMD #endif @@ -655,7 +682,7 @@ DRMP3_API const char* drmp3_version_string(void) #define DRMP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) #define DRMP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) typedef __m128 drmp3_f4; -#if defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD) +#if (defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD)) && !defined(__clang__) #define drmp3_cpuid __cpuid #else static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType) @@ -779,11 +806,7 @@ static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_ar #define DRMP3_FREE(p) free((p)) #endif -typedef struct -{ - const drmp3_uint8 *buf; - int pos, limit; -} drmp3_bs; + typedef struct { @@ -796,24 +819,6 @@ typedef struct drmp3_uint8 tab_offset, code_tab_width, band_count; } drmp3_L12_subband_alloc; -typedef struct -{ - const drmp3_uint8 *sfbtab; - drmp3_uint16 part_23_length, big_values, scalefac_compress; - drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; - drmp3_uint8 table_select[3], region_count[3], subblock_gain[3]; - drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi; -} drmp3_L3_gr_info; - -typedef struct -{ - drmp3_bs bs; - drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES]; - drmp3_L3_gr_info gr_info[4]; - float grbuf[2][576], scf[40], syn[18 + 15][2*32]; - drmp3_uint8 ist_pos[2][39]; -} drmp3dec_scratch; - static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes) { bs->buf = data; @@ -1227,6 +1232,14 @@ static float drmp3_L3_ldexp_q2(float y, int exp_q2) return y; } +/* +I've had reports of GCC 14 throwing an incorrect -Wstringop-overflow warning here. This is an attempt +to silence this warning. +*/ +#if (defined(__GNUC__) && (__GNUC__ >= 13)) && !defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstringop-overflow" +#endif static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *ist_pos, drmp3_bs *bs, const drmp3_L3_gr_info *gr, float *scf, int ch) { static const drmp3_uint8 g_scf_partitions[3][28] = { @@ -1288,6 +1301,9 @@ static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *is scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); } } +#if (defined(__GNUC__) && (__GNUC__ >= 13)) && !defined(__clang__) + #pragma GCC diagnostic pop +#endif static const float g_drmp3_pow43[129 + 16] = { 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, @@ -2299,7 +2315,6 @@ DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int m int i = 0, igr, frame_size = 0, success = 1; const drmp3_uint8 *hdr; drmp3_bs bs_frame[1]; - drmp3dec_scratch scratch; if (mp3_bytes > 4 && dec->header[0] == 0xff && drmp3_hdr_compare(dec->header, mp3)) { @@ -2336,23 +2351,23 @@ DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int m if (info->layer == 3) { - int main_data_begin = drmp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); + int main_data_begin = drmp3_L3_read_side_info(bs_frame, dec->scratch.gr_info, hdr); if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) { drmp3dec_init(dec); return 0; } - success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); + success = drmp3_L3_restore_reservoir(dec, bs_frame, &dec->scratch, main_data_begin); if (success && pcm != NULL) { for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels)) { - DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); - drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); + DRMP3_ZERO_MEMORY(dec->scratch.grbuf[0], 576*2*sizeof(float)); + drmp3_L3_decode(dec, &dec->scratch, dec->scratch.gr_info + igr*info->channels, info->channels); + drmp3d_synth_granule(dec->qmf_state, dec->scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, dec->scratch.syn[0]); } } - drmp3_L3_save_reservoir(dec, &scratch); + drmp3_L3_save_reservoir(dec, &dec->scratch); } else { #ifdef DR_MP3_ONLY_MP3 @@ -2366,15 +2381,15 @@ DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int m drmp3_L12_read_scale_info(hdr, bs_frame, sci); - DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + DRMP3_ZERO_MEMORY(dec->scratch.grbuf[0], 576*2*sizeof(float)); for (i = 0, igr = 0; igr < 3; igr++) { - if (12 == (i += drmp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) + if (12 == (i += drmp3_L12_dequantize_granule(dec->scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) { i = 0; - drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); - drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); - DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + drmp3_L12_apply_scf_384(sci, sci->scf + igr, dec->scratch.grbuf[0]); + drmp3d_synth_granule(dec->qmf_state, dec->scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, dec->scratch.syn[0]); + DRMP3_ZERO_MEMORY(dec->scratch.grbuf[0], 576*2*sizeof(float)); pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels); } if (bs_frame->pos > bs_frame->limit) @@ -3005,23 +3020,27 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm ((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); - } + if (32 + tagSize < streamLen) { + 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); + drmp3_free(pTagData, pAllocationCallbacks); + } } } + } else { + /* The tag size is larger than the stream. Invalid APE tag. */ } } } @@ -3153,7 +3172,6 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm { 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); @@ -3164,6 +3182,7 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm if (drmp3_L3_read_side_info(&bs, grInfo, pFirstFrameData) >= 0) { drmp3_bool32 isXing = DRMP3_FALSE; drmp3_bool32 isInfo = DRMP3_FALSE; + const drmp3_uint8* pTagData; const drmp3_uint8* pTagDataBeg; pTagDataBeg = pFirstFrameData + DRMP3_HDR_SIZE + (bs.pos/8); @@ -3246,6 +3265,13 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm /* 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; + + /* + The internal decoder needs to be reset to clear out any state. If we don't reset this state, it's possible for + there to be inconsistencies in the number of samples read when reading to the end of the stream depending on + whether or not the caller seeks to the start of the stream. + */ + drmp3dec_init(&pMP3->decoder); } } else { /* Failed to read the side info. */ @@ -3307,8 +3333,6 @@ static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3 DRMP3_ASSERT(pMP3 != NULL); - newCursor = pMP3->memory.currentReadPos; - if (origin == DRMP3_SEEK_SET) { newCursor = 0; } else if (origin == DRMP3_SEEK_CUR) { @@ -3981,7 +4005,7 @@ static drmp3_bool32 drmp3__on_tell_stdio(void* pUserData, drmp3_int64* pCursor) DRMP3_ASSERT(pFileStdio != NULL); DRMP3_ASSERT(pCursor != NULL); -#if defined(_WIN32) +#if defined(_WIN32) && !defined(NXDK) #if defined(_MSC_VER) && _MSC_VER > 1200 result = _ftelli64(pFileStdio); #else @@ -4780,6 +4804,8 @@ static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, pNewFrames = (float*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); if (pNewFrames == NULL) { drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); + pFrames = NULL; + totalFramesRead = 0; break; } @@ -4847,6 +4873,8 @@ static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pC pNewFrames = (drmp3_int16*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); if (pNewFrames == NULL) { drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); + pFrames = NULL; + totalFramesRead = 0; break; } @@ -4981,7 +5009,24 @@ DIFFERENCES BETWEEN minimp3 AND dr_mp3 /* REVISION HISTORY ================ -v0.7.0 - TBD +v0.7.4 - TBD + - Improvements to SIMD detection. + +v0.7.3 - 2026-01-17 + - Fix an error in drmp3_open_and_read_pcm_frames_s16() and family when memory allocation fails. + - Fix some compilation warnings. + +v0.7.2 - 2025-12-02 + - Reduce stack space to improve robustness on embedded systems. + - Fix a compilation error with MSVC Clang toolset relating to cpuid. + - Fix an error with APE tag parsing. + +v0.7.1 - 2025-09-10 + - Silence a warning with GCC. + - Fix an error with the NXDK build. + - Fix a decoding inconsistency when seeking. Prior to this change, reading to the end of the stream immediately after initializing will result in a different number of samples read than if the stream is seeked to the start and read to the end. + +v0.7.0 - 2025-07-23 - 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 From adc4c9b875de3179c02494967ae92ccc10b246f0 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Mar 2026 21:19:31 +0200 Subject: [PATCH 238/319] Update dr_wav.h --- src/external/dr_wav.h | 133 ++++++++++++++++++++++++++++++------------ 1 file changed, 95 insertions(+), 38 deletions(-) diff --git a/src/external/dr_wav.h b/src/external/dr_wav.h index 7a7e02246..c7bd89fa3 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.14.0 - TBD +dr_wav - v0.14.5 - 2026-03-03 David Reid - mackron@gmail.com @@ -147,7 +147,7 @@ extern "C" { #define DRWAV_VERSION_MAJOR 0 #define DRWAV_VERSION_MINOR 14 -#define DRWAV_VERSION_REVISION 0 +#define DRWAV_VERSION_REVISION 5 #define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) #include /* For size_t. */ @@ -2189,6 +2189,22 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) { drwav_uint32 iSampleLoop; + drwav_uint32 loopCount; + drwav_uint32 calculatedLoopCount; + + /* + When we calcualted the amount of memory required for the "smpl" chunk we excluded the chunk entirely + if the loop count in the header did not match with the calculated count based on the size of the + chunk. When this happens, the second stage will still hit this path but the `pMetadata` will be + non-null, but will either be pointing at the very end of the allocation or at the start of another + chunk. We need to check the loop counts for consistency *before* dereferencing the pMetadata object + so it's consistent with how we do it in the first stage. + */ + loopCount = drwav_bytes_to_u32(smplHeaderData + 28); + calculatedLoopCount = (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES; + if (loopCount != calculatedLoopCount) { + return totalBytesRead; + } pMetadata->type = drwav_metadata_type_smpl; pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0); @@ -2205,7 +2221,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars The loop count needs to be validated against the size of the chunk for safety so we don't attempt to read over the boundary of the chunk. */ - if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES) { + if (pMetadata->data.smpl.sampleLoopCount == calculatedLoopCount) { pMetadata->data.smpl.pLoops = (drwav_smpl_loop*)drwav__metadata_get_memory(pParser, sizeof(drwav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, DRWAV_METADATA_ALIGNMENT); for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) { @@ -2230,6 +2246,15 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars drwav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); } + } else { + /* + Getting here means the loop count in the header does not match up with the size of the + chunk. Clear out the data to zero just to be safe. + + This should never actually get hit because we check for it above, but keeping this here + for added safety. + */ + DRWAV_ZERO_OBJECT(&pMetadata->data.smpl); } } @@ -3605,7 +3630,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on 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; + return DRWAV_FALSE; } } else { chunkSize += header.paddingSize; /* <-- Make sure we seek past the padding. */ @@ -5285,7 +5310,7 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_tell_stdio(void* pUserData, drwav_int64* pC DRWAV_ASSERT(pFileStdio != NULL); DRWAV_ASSERT(pCursor != NULL); -#if defined(_WIN32) +#if defined(_WIN32) && !defined(NXDK) #if defined(_MSC_VER) && _MSC_VER > 1200 result = _ftelli64(pFileStdio); #else @@ -5492,8 +5517,6 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, dr DRWAV_ASSERT(pWav != NULL); - newCursor = pWav->memoryStream.currentReadPos; - if (origin == DRWAV_SEEK_SET) { newCursor = 0; } else if (origin == DRWAV_SEEK_CUR) { @@ -5566,8 +5589,6 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offs DRWAV_ASSERT(pWav != NULL); - newCursor = pWav->memoryStreamWrite.currentWritePos; - if (origin == DRWAV_SEEK_SET) { newCursor = 0; } else if (origin == DRWAV_SEEK_CUR) { @@ -5576,7 +5597,7 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offs newCursor = (drwav_int64)pWav->memoryStreamWrite.dataSize; } else { DRWAV_ASSERT(!"Invalid seek origin"); - return DRWAV_INVALID_ARGS; + return DRWAV_FALSE; } newCursor += offset; @@ -6258,12 +6279,12 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav { drwav_uint64 totalFramesRead = 0; - static drwav_int32 adaptationTable[] = { + static const 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 }; + static const drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; + static const drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; DRWAV_ASSERT(pWav != NULL); DRWAV_ASSERT(framesToRead > 0); @@ -6292,7 +6313,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav 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)) { + if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= drwav_countof(coeff2Table)) { return totalFramesRead; /* Invalid file. */ } } else { @@ -6319,7 +6340,8 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav 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)) { + if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= drwav_countof(coeff2Table) || + pWav->msadpcm.predictor[1] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= drwav_countof(coeff2Table)) { return totalFramesRead; /* Invalid file. */ } } @@ -6373,15 +6395,17 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav drwav_int32 newSample0; drwav_int32 newSample1; + /* The predictor is read from the file and then indexed into a table. Check that it's in bounds. */ + if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= drwav_countof(coeff2Table)) { + return totalFramesRead; + } + newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; newSample0 += nibble0 * pWav->msadpcm.delta[0]; newSample0 = drwav_clamp(newSample0, -32768, 32767); - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } - + pWav->msadpcm.delta[0] = (drwav_int32)drwav_clamp(((drwav_int64)adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8, 16, 0x7FFFFFFF); + pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; pWav->msadpcm.prevFrames[0][1] = newSample0; @@ -6390,15 +6414,11 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav newSample1 += nibble1 * pWav->msadpcm.delta[0]; newSample1 = drwav_clamp(newSample1, -32768, 32767); - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } + pWav->msadpcm.delta[0] = (drwav_int32)drwav_clamp(((drwav_int64)adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8, 16, 0x7FFFFFFF); pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; pWav->msadpcm.prevFrames[0][1] = newSample1; - pWav->msadpcm.cachedFrames[2] = newSample0; pWav->msadpcm.cachedFrames[3] = newSample1; pWav->msadpcm.cachedFrameCount = 2; @@ -6408,28 +6428,30 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav drwav_int32 newSample1; /* Left. */ + if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= drwav_countof(coeff2Table)) { + return totalFramesRead; /* Out of bounds. Invalid file. */ + } + newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; newSample0 += nibble0 * pWav->msadpcm.delta[0]; newSample0 = drwav_clamp(newSample0, -32768, 32767); - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } + pWav->msadpcm.delta[0] = (drwav_int32)drwav_clamp(((drwav_int64)adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8, 16, 0x7FFFFFFF); pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; pWav->msadpcm.prevFrames[0][1] = newSample0; /* Right. */ + if (pWav->msadpcm.predictor[1] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= drwav_countof(coeff2Table)) { + return totalFramesRead; /* Out of bounds. Invalid file. */ + } + newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8; newSample1 += nibble1 * pWav->msadpcm.delta[1]; newSample1 = drwav_clamp(newSample1, -32768, 32767); - pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8; - if (pWav->msadpcm.delta[1] < 16) { - pWav->msadpcm.delta[1] = 16; - } + pWav->msadpcm.delta[1] = (drwav_int32)drwav_clamp(((drwav_int64)adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8, 16, 0x7FFFFFFF); pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1]; pWav->msadpcm.prevFrames[1][1] = newSample1; @@ -6451,12 +6473,12 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin drwav_uint64 totalFramesRead = 0; drwav_uint32 iChannel; - static drwav_int32 indexTable[16] = { + static const drwav_int32 indexTable[16] = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8 }; - static drwav_int32 stepTable[89] = { + static const drwav_int32 stepTable[89] = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, @@ -6606,7 +6628,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin #ifndef DR_WAV_NO_CONVERSION_API -static unsigned short g_drwavAlawTable[256] = { +static const unsigned short g_drwavAlawTable[256] = { 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, @@ -6625,7 +6647,7 @@ static unsigned short g_drwavAlawTable[256] = { 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 }; -static unsigned short g_drwavMulawTable[256] = { +static const unsigned short g_drwavMulawTable[256] = { 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, @@ -8053,6 +8075,12 @@ DRWAV_PRIVATE drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, uns DRWAV_ASSERT(pWav != NULL); + /* Check for overflow before multiplication. */ + if (pWav->channels == 0 || pWav->totalPCMFrameCount > DRWAV_SIZE_MAX / pWav->channels / sizeof(drwav_int16)) { + drwav_uninit(pWav); + return NULL; /* Overflow or invalid channels. */ + } + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16); if (sampleDataSize > DRWAV_SIZE_MAX) { drwav_uninit(pWav); @@ -8095,6 +8123,12 @@ DRWAV_PRIVATE float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned DRWAV_ASSERT(pWav != NULL); + /* Check for overflow before multiplication. */ + if (pWav->channels == 0 || pWav->totalPCMFrameCount > DRWAV_SIZE_MAX / pWav->channels / sizeof(float)) { + drwav_uninit(pWav); + return NULL; /* Overflow or invalid channels. */ + } + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float); if (sampleDataSize > DRWAV_SIZE_MAX) { drwav_uninit(pWav); @@ -8137,6 +8171,12 @@ DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, uns DRWAV_ASSERT(pWav != NULL); + /* Check for overflow before multiplication. */ + if (pWav->channels == 0 || pWav->totalPCMFrameCount > DRWAV_SIZE_MAX / pWav->channels / sizeof(drwav_int32)) { + drwav_uninit(pWav); + return NULL; /* Overflow or invalid channels. */ + } + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32); if (sampleDataSize > DRWAV_SIZE_MAX) { drwav_uninit(pWav); @@ -8517,7 +8557,24 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) /* REVISION HISTORY ================ -v0.14.0 - TBD +v0.14.5 - 2026-03-03 + - Fix a crash when loading files with a malformed "smpl" chunk. + - Fix a signed overflow bug with the MS-ADPCM decoder. + +v0.14.4 - 2026-01-17 + - Fix some compilation warnings. + +v0.14.3 - 2025-12-14 + - Fix a possible out-of-bounds read when reading from MS-ADPCM encoded files. + - Fix a possible integer overflow error. + +v0.14.2 - 2025-12-02 + - Fix a compilation warning. + +v0.14.1 - 2025-09-10 + - Fix an error with the NXDK build. + +v0.14.0 - 2025-07-23 - 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 From d5326fe880b26c941f967aedf9ece48c99554cad Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Mar 2026 21:20:23 +0200 Subject: [PATCH 239/319] Update m3d.h --- src/external/m3d.h | 53 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/src/external/m3d.h b/src/external/m3d.h index 6abaad142..8bba18325 100644 --- a/src/external/m3d.h +++ b/src/external/m3d.h @@ -236,7 +236,8 @@ typedef struct { #ifdef M3D_ASCII #define M3D_PROPERTYDEF(f,i,n) { (f), (i), (char*)(n) } char *key; -#else +#endif +#ifndef M3D_ASCII #define M3D_PROPERTYDEF(f,i,n) { (f), (i) } #endif } m3dpd_t; @@ -414,7 +415,8 @@ typedef struct { #ifdef M3D_ASCII #define M3D_CMDDEF(t,n,p,a,b,c,d,e,f,g,h) { (char*)(n), (p), { (a), (b), (c), (d), (e), (f), (g), (h) } } char *key; -#else +#endif +#ifndef M3D_ASCII #define M3D_CMDDEF(t,n,p,a,b,c,d,e,f,g,h) { (p), { (a), (b), (c), (d), (e), (f), (g), (h) } } #endif uint8_t p; @@ -674,6 +676,10 @@ static m3dcd_t m3d_commandtypes[] = { #include #include +/* we'll need this with M3D_NOTEXTURE */ +char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); + +#ifndef M3D_NOTEXTURE #if !defined(M3D_NOIMPORTER) && !defined(STBI_INCLUDE_STB_IMAGE_H) /* PNG decompressor from @@ -1868,6 +1874,11 @@ static void *_m3dstbi__png_load(_m3dstbi__context *s, int *x, int *y, int *comp, #if !defined(M3D_NOIMPORTER) && defined(STBI_INCLUDE_STB_IMAGE_H) && !defined(STB_IMAGE_IMPLEMENTATION) #error "stb_image.h included without STB_IMAGE_IMPLEMENTATION. Sorry, we need some stuff defined inside the ifguard for proper integration" #endif +#else +#if !defined(STBI_INCLUDE_STB_IMAGE_H) || defined(STBI_NO_ZLIB) +#error "stb_image.h not included or STBI_NO_ZLIB defined. Sorry, we need its zlib implementation for proper integration" +#endif +#endif /* M3D_NOTEXTURE */ #if defined(M3D_EXPORTER) && !defined(INCLUDE_STB_IMAGE_WRITE_H) /* zlib_compressor from @@ -2168,9 +2179,11 @@ M3D_INDEX _m3d_gettx(m3d_t *model, m3dread_t readfilecb, m3dfree_t freecb, char unsigned int i, len = 0; unsigned char *buff = NULL; char *fn2; +#ifndef M3D_NOTEXTURE unsigned int w, h; stbi__context s; stbi__result_info ri; +#endif /* failsafe */ if(!fn || !*fn) return M3D_UNDEF; @@ -2209,13 +2222,17 @@ M3D_INDEX _m3d_gettx(m3d_t *model, m3dread_t readfilecb, m3dfree_t freecb, char if(!model->texture) { if(buff && freecb) (*freecb)(buff); model->errcode = M3D_ERR_ALLOC; + model->numtexture = 0; return M3D_UNDEF; } + memset(&model->texture[i], 0, sizeof(m3dtx_t)); model->texture[i].name = fn; - model->texture[i].w = model->texture[i].h = 0; model->texture[i].d = NULL; if(buff) { if(buff[0] == 0x89 && buff[1] == 'P' && buff[2] == 'N' && buff[3] == 'G') { - s.read_from_callbacks = 0; +#ifndef M3D_NOTEXTURE + /* return pixel buffer of the decoded texture */ + memset(&s, 0, sizeof(s)); + memset(&ri, 0, sizeof(ri)); s.img_buffer = s.img_buffer_original = (unsigned char *) buff; s.img_buffer_end = s.img_buffer_original_end = (unsigned char *) buff+len; /* don't use model->texture[i].w directly, it's a uint16_t */ @@ -2225,6 +2242,16 @@ M3D_INDEX _m3d_gettx(m3d_t *model, m3dread_t readfilecb, m3dfree_t freecb, char model->texture[i].w = w; model->texture[i].h = h; model->texture[i].f = (uint8_t)len; +#else + /* return only the raw undecoded texture */ + if((model->texture[i].d = (uint8_t*)M3D_MALLOC(len))) { + memcpy(model->texture[i].d, buff, len); + model->texture[i].w = len & 0xffff; + model->texture[i].h = (len >> 16) & 0xffff; + model->texture[i].f = 0; + } else + model->errcode = M3D_ERR_ALLOC; +#endif } else { #ifdef M3D_TX_INTERP if((model->errcode = M3D_TX_INTERP(fn, buff, len, &model->texture[i])) != M3D_SUCCESS) { @@ -4280,7 +4307,7 @@ m3db_t *m3d_pose(m3d_t *model, M3D_INDEX actionid, uint32_t msec) if(l != msec) { model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, (model->numvertex + 2 * model->numbone) * sizeof(m3dv_t)); if(!model->vertex) { - free(ret); + M3D_FREE(ret); model->errcode = M3D_ERR_ALLOC; return NULL; } @@ -4492,7 +4519,7 @@ void m3d_free(m3d_t *model) if(model->label) M3D_FREE(model->label); if(model->inlined) M3D_FREE(model->inlined); if(model->extra) M3D_FREE(model->extra); - free(model); + M3D_FREE(model); } #endif @@ -4568,15 +4595,15 @@ static uint32_t _m3d_stridx(m3dstr_t *str, uint32_t numstr, char *s) safe = _m3d_safestr(s, 0); if(!safe) return 0; if(!*safe) { - free(safe); + M3D_FREE(safe); return 0; } for(i = 0; i < numstr; i++) if(!strcmp(str[i].str, s)) { - free(safe); + M3D_FREE(safe); return str[i].offs; } - free(safe); + M3D_FREE(safe); } return 0; } @@ -4889,7 +4916,7 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size if(cmd->type == m3dc_mesh) { if(numgrp + 2 < maxgrp) { maxgrp += 1024; - grpidx = (uint32_t*)realloc(grpidx, maxgrp * sizeof(uint32_t)); + grpidx = (uint32_t*)M3D_REALLOC(grpidx, maxgrp * sizeof(uint32_t)); if(!grpidx) goto memerr; if(!numgrp) { grpidx[0] = 0; @@ -5194,7 +5221,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); if(sa) M3D_FREE(sa); if(sd) M3D_FREE(sd); if(out) M3D_FREE(out); - if(opa) free(opa); + if(opa) M3D_FREE(opa); if(h) M3D_FREE(h); M3D_LOG("Out of memory"); model->errcode = M3D_ERR_ALLOC; @@ -6285,7 +6312,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); if(skin) M3D_FREE(skin); if(str) M3D_FREE(str); if(vrtx) M3D_FREE(vrtx); - if(opa) free(opa); + if(opa) M3D_FREE(opa); if(h) M3D_FREE(h); return out; } @@ -6310,7 +6337,7 @@ namespace M3D { public: Model() { - this->model = (m3d_t*)malloc(sizeof(m3d_t)); memset(this->model, 0, sizeof(m3d_t)); + this->model = (m3d_t*)M3D_MALLOC(sizeof(m3d_t)); memset(this->model, 0, sizeof(m3d_t)); } Model(_unused const std::string &data, _unused m3dread_t ReadFileCB, _unused m3dfree_t FreeCB, _unused M3D::Model mtllib) { From e5f0c9f8b1dce384534ad802c343c40ba4956b67 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Mar 2026 21:21:11 +0200 Subject: [PATCH 240/319] Update qoa.h --- src/external/qoa.h | 60 +++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/external/qoa.h b/src/external/qoa.h index 57b85d131..e11c84269 100644 --- a/src/external/qoa.h +++ b/src/external/qoa.h @@ -31,7 +31,7 @@ struct { struct { char magic[4]; // magic bytes "qoaf" uint32_t samples; // samples per channel in this file - } file_header; + } file_header; struct { struct { @@ -39,12 +39,12 @@ struct { uint24_t samplerate; // samplerate in hz uint16_t fsamples; // samples per channel in this frame uint16_t fsize; // frame size (includes this header) - } frame_header; + } frame_header; struct { int16_t history[4]; // most recent last int16_t weights[4]; // most recent last - } lms_state[num_channels]; + } lms_state[num_channels]; qoa_slice_t slices[256][num_channels]; @@ -66,7 +66,7 @@ frame may contain between 1 .. 256 (inclusive) slices per channel. The last slice (for each channel) in the last frame may contain less than 20 samples; the slice still must be 8 bytes wide, with the unused samples zeroed out. -Channels are interleaved per slice. E.g. for 2 channel stereo: +Channels are interleaved per slice. E.g. for 2 channel stereo: slice[0] = L, slice[1] = R, slice[2] = L, slice[3] = R ... A valid QOA file or stream must have at least one frame. Each frame must contain @@ -74,7 +74,7 @@ at least one channel and one sample with a samplerate between 1 .. 16777215 (inclusive). If the total number of samples is not known by the encoder, the samples in the -file header may be set to 0x00000000 to indicate that the encoder is +file header may be set to 0x00000000 to indicate that the encoder is "streaming". In a streaming context, the samplerate and number of channels may differ from frame to frame. For static files (those with samples set to a non-zero value), each frame must have the same number of channels and same @@ -88,15 +88,15 @@ counts 1 .. 8 is: 1. Mono 2. L, R - 3. L, R, C - 4. FL, FR, B/SL, B/SR - 5. FL, FR, C, B/SL, B/SR + 3. L, R, C + 4. FL, FR, B/SL, B/SR + 5. FL, FR, C, B/SL, B/SR 6. FL, FR, C, LFE, B/SL, B/SR - 7. FL, FR, C, LFE, B, SL, SR + 7. FL, FR, C, LFE, B, SL, SR 8. FL, FR, C, LFE, BL, BR, SL, SR QOA predicts each audio sample based on the previously decoded ones using a -"Sign-Sign Least Mean Squares Filter" (LMS). This prediction plus the +"Sign-Sign Least Mean Squares Filter" (LMS). This prediction plus the dequantized residual forms the final output sample. */ @@ -178,9 +178,9 @@ typedef unsigned long long qoa_uint64_t; /* The quant_tab provides an index into the dequant_tab for residuals in the -range of -8 .. 8. It maps this range to just 3bits and becomes less accurate at -the higher end. Note that the residual zero is identical to the lowest positive -value. This is mostly fine, since the qoa_div() function always rounds away +range of -8 .. 8. It maps this range to just 3bits and becomes less accurate at +the higher end. Note that the residual zero is identical to the lowest positive +value. This is mostly fine, since the qoa_div() function always rounds away from zero. */ static const int qoa_quant_tab[17] = { @@ -193,8 +193,8 @@ static const int qoa_quant_tab[17] = { /* We have 16 different scalefactors. Like the quantized residuals these become less accurate at the higher end. In theory, the highest scalefactor that we would need to encode the highest 16bit residual is (2**16)/8 = 8192. However we -rely on the LMS filter to predict samples accurately enough that a maximum -residual of one quarter of the 16 bit range is sufficient. I.e. with the +rely on the LMS filter to predict samples accurately enough that a maximum +residual of one quarter of the 16 bit range is sufficient. I.e. with the scalefactor 2048 times the quant range of 8 we can encode residuals up to 2**14. The scalefactor values are computed as: @@ -205,9 +205,9 @@ static const int qoa_scalefactor_tab[16] = { }; -/* The reciprocal_tab maps each of the 16 scalefactors to their rounded -reciprocals 1/scalefactor. This allows us to calculate the scaled residuals in -the encoder with just one multiplication instead of an expensive division. We +/* The reciprocal_tab maps each of the 16 scalefactors to their rounded +reciprocals 1/scalefactor. This allows us to calculate the scaled residuals in +the encoder with just one multiplication instead of an expensive division. We do this in .16 fixed point with integers, instead of floats. The reciprocal_tab is computed as: @@ -218,11 +218,11 @@ static const int qoa_reciprocal_tab[16] = { }; -/* The dequant_tab maps each of the scalefactors and quantized residuals to +/* The dequant_tab maps each of the scalefactors and quantized residuals to their unscaled & dequantized version. Since qoa_div rounds away from the zero, the smallest entries are mapped to 3/4 -instead of 1. The dequant_tab assumes the following dequantized values for each +instead of 1. The dequant_tab assumes the following dequantized values for each of the quant_tab indices and is computed as: float dqt[8] = {0.75, -0.75, 2.5, -2.5, 4.5, -4.5, 7, -7}; dequant_tab[s][q] <- round_ties_away_from_zero(scalefactor_tab[s] * dqt[q]) @@ -258,7 +258,7 @@ adjusting 4 weights based on the residual of the previous prediction. The next sample is predicted as the sum of (weight[i] * history[i]). The adjustment of the weights is done with a "Sign-Sign-LMS" that adds or -subtracts the residual to each weight, based on the corresponding sample from +subtracts the residual to each weight, based on the corresponding sample from the history. This, surprisingly, is sufficient to get worthwhile predictions. This is all done with fixed point integers. Hence the right-shifts when updating @@ -285,8 +285,8 @@ static void qoa_lms_update(qoa_lms_t *lms, int sample, int residual) { } -/* qoa_div() implements a rounding division, but avoids rounding to zero for -small numbers. E.g. 0.1 will be rounded to 1. Note that 0 itself still +/* qoa_div() implements a rounding division, but avoids rounding to zero for +small numbers. E.g. 0.1 will be rounded to 1. Note that 0 itself still returns as 0, which is handled in the qoa_quant_tab[]. qoa_div() takes an index into the .16 fixed point qoa_reciprocal_tab as an argument, so it can do the division with a cheaper integer multiplication. */ @@ -385,10 +385,10 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned for (unsigned int c = 0; c < channels; c++) { int slice_len = qoa_clamp(QOA_SLICE_LEN, 0, frame_len - sample_index); int slice_start = sample_index * channels + c; - int slice_end = (sample_index + slice_len) * channels + c; + int slice_end = (sample_index + slice_len) * channels + c; - /* Brute for search for the best scalefactor. Just go through all - 16 scalefactors, encode all samples for the current slice and + /* Brute force search for the best scalefactor. Just go through all + 16 scalefactors, encode all samples for the current slice and meassure the total squared error. */ qoa_uint64_t best_rank = -1; #ifdef QOA_RECORD_TOTAL_ERROR @@ -402,7 +402,7 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned /* There is a strong correlation between the scalefactors of neighboring slices. As an optimization, start testing the best scalefactor of the previous slice first. */ - int scalefactor = (sfi + prev_scalefactor[c]) % 16; + int scalefactor = (sfi + prev_scalefactor[c]) & (16 - 1); /* We have to reset the LMS state to the last known good one before trying each scalefactor, as each pass updates the LMS @@ -500,7 +500,7 @@ void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len) num_frames * QOA_LMS_LEN * 4 * qoa->channels + /* 4 * 4 bytes lms state per channel */ num_slices * 8 * qoa->channels; /* 8 byte slices */ - unsigned char *bytes = (unsigned char *)QOA_MALLOC(encoded_size); + unsigned char *bytes = QOA_MALLOC(encoded_size); for (unsigned int c = 0; c < qoa->channels; c++) { /* Set the initial LMS weights to {0, 0, -1, 2}. This helps with the @@ -657,7 +657,7 @@ short *qoa_decode(const unsigned char *bytes, int size, qoa_desc *qoa) { /* Calculate the required size of the sample buffer and allocate */ int total_samples = qoa->samples * qoa->channels; - short *sample_data = (short *)QOA_MALLOC(total_samples * sizeof(short)); + short *sample_data = QOA_MALLOC(total_samples * sizeof(short)); unsigned int sample_index = 0; unsigned int frame_len; @@ -733,7 +733,7 @@ void *qoa_read(const char *filename, qoa_desc *qoa) { bytes_read = fread(data, 1, size, f); fclose(f); - sample_data = qoa_decode((const unsigned char *)data, bytes_read, qoa); + sample_data = qoa_decode(data, bytes_read, qoa); QOA_FREE(data); return sample_data; } From 4c79c6837bd479fa938fc81eb61c6574fac078f1 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Mar 2026 21:21:16 +0200 Subject: [PATCH 241/319] Update qoi.h --- src/external/qoi.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/external/qoi.h b/src/external/qoi.h index f2800b0cc..e09d3a43d 100644 --- a/src/external/qoi.h +++ b/src/external/qoi.h @@ -427,7 +427,7 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { run = 0; } - index_pos = QOI_COLOR_HASH(px) % 64; + index_pos = QOI_COLOR_HASH(px) & (64 - 1); if (index[index_pos].v == px.v) { bytes[p++] = QOI_OP_INDEX | index_pos; @@ -574,7 +574,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { run = (b1 & 0x3f); } - index[QOI_COLOR_HASH(px) % 64] = px; + index[QOI_COLOR_HASH(px) & (64 - 1)] = px; } pixels[px_pos + 0] = px.rgba.r; From e9231bc4f1b3df2c1eec24cb535130d2e6aabd6a Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Mar 2026 21:24:20 +0200 Subject: [PATCH 242/319] Update stb_image_resize2.h --- src/external/stb_image_resize2.h | 662 +++++++++++++++++-------------- 1 file changed, 370 insertions(+), 292 deletions(-) diff --git a/src/external/stb_image_resize2.h b/src/external/stb_image_resize2.h index 2f2627463..079897658 100644 --- a/src/external/stb_image_resize2.h +++ b/src/external/stb_image_resize2.h @@ -1,4 +1,4 @@ -/* stb_image_resize2 - v2.12 - public domain image resizing +/* stb_image_resize2 - v2.18 - public domain image resizing by Jeff Roberts (v2) and Jorge L Rodriguez http://github.com/nothings/stb @@ -141,13 +141,13 @@ COLOR+ALPHA buffer types tell the resizer to do. When you use the pixel layouts STBIR_RGBA, STBIR_BGRA, STBIR_ARGB, - STBIR_ABGR, STBIR_RX, or STBIR_XR you are telling us that the pixels are + STBIR_ABGR, STBIR_RA, or STBIR_AR you are telling us that the pixels are non-premultiplied. In these cases, the resizer will alpha weight the colors (effectively creating the premultiplied image), do the filtering, and then convert back to non-premult on exit. - When you use the pixel layouts STBIR_RGBA_PM, STBIR_RGBA_PM, STBIR_RGBA_PM, - STBIR_RGBA_PM, STBIR_RX_PM or STBIR_XR_PM, you are telling that the pixels + When you use the pixel layouts STBIR_RGBA_PM, STBIR_BGRA_PM, STBIR_ARGB_PM, + STBIR_ABGR_PM, STBIR_RA_PM or STBIR_AR_PM, you are telling that the pixels ARE premultiplied. In this case, the resizer doesn't have to do the premultipling - it can filter directly on the input. This about twice as fast as the non-premultiplied case, so it's the right option if your data is @@ -254,7 +254,7 @@ using the stbir_set_filter_callbacks function. PROGRESS - For interactive use with slow resize operations, you can use the the + For interactive use with slow resize operations, you can use the scanline callbacks in the extended API. It would have to be a *very* large image resample to need progress though - we're very fast. @@ -307,6 +307,8 @@ some pixel reconversion, but probably dwarfed by things falling out of cache. Probably also something possible with alternating between scattering and gathering at high resize scales? + * Should we have a multiple MIPs at the same time function (could keep + more memory in cache during multiple resizes)? * Rewrite the coefficient generator to do many at once. * AVX-512 vertical kernels - worried about downclocking here. * Convert the reincludes to macros when we know they aren't changing. @@ -327,6 +329,24 @@ Nathan Reed: warning fixes for 1.0 REVISIONS + 2.18 (2026-03-25) fixed coefficient calculation when skipping a coefficient off + the left side of the window, added non-aligned access safe + memcpy mode for scalar path, fixed various typos, and fixed + define error in the float clamp output mode. + 2.17 (2025-10-25) silly format bug in easy-to-use APIs. + 2.16 (2025-10-21) fixed the easy-to-use APIs to allow inverted bitmaps (negative + strides), fix vertical filter kernel callback, fix threaded + gather buffer priming (and assert). + (thanks adipose, TainZerL, and Harrison Green) + 2.15 (2025-07-17) fixed an assert in debug mode when using floats with input + callbacks, work around GCC warning when adding to null ptr + (thanks Johannes Spohr and Pyry Kovanen). + 2.14 (2025-05-09) fixed a bug using downsampling gather horizontal first, and + scatter with vertical first. + 2.13 (2025-02-27) fixed a bug when using input callbacks, turned off simd for + tiny-c, fixed some variables that should have been static, + fixes a bug when calculating temp memory with resizes that + exceed 2GB of temp memory (very large resizes). 2.12 (2024-10-18) fix incorrect use of user_data with STBIR_FREE 2.11 (2024-09-08) fix harmless asan warnings in 2-channel and 3-channel mode with AVX-2, fix some weird scaling edge conditions with @@ -382,62 +402,6 @@ typedef uint32_t stbir_uint32; typedef uint64_t stbir_uint64; #endif -#ifdef _M_IX86_FP -#if ( _M_IX86_FP >= 1 ) -#ifndef STBIR_SSE -#define STBIR_SSE -#endif -#endif -#endif - -#if defined(_x86_64) || defined( __x86_64__ ) || defined( _M_X64 ) || defined(__x86_64) || defined(_M_AMD64) || defined(__SSE2__) || defined(STBIR_SSE) || defined(STBIR_SSE2) - #ifndef STBIR_SSE2 - #define STBIR_SSE2 - #endif - #if defined(__AVX__) || defined(STBIR_AVX2) - #ifndef STBIR_AVX - #ifndef STBIR_NO_AVX - #define STBIR_AVX - #endif - #endif - #endif - #if defined(__AVX2__) || defined(STBIR_AVX2) - #ifndef STBIR_NO_AVX2 - #ifndef STBIR_AVX2 - #define STBIR_AVX2 - #endif - #if defined( _MSC_VER ) && !defined(__clang__) - #ifndef STBIR_FP16C // FP16C instructions are on all AVX2 cpus, so we can autoselect it here on microsoft - clang needs -m16c - #define STBIR_FP16C - #endif - #endif - #endif - #endif - #ifdef __F16C__ - #ifndef STBIR_FP16C // turn on FP16C instructions if the define is set (for clang and gcc) - #define STBIR_FP16C - #endif - #endif -#endif - -#if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) || ((__ARM_NEON_FP & 4) != 0) || defined(__ARM_NEON__) -#ifndef STBIR_NEON -#define STBIR_NEON -#endif -#endif - -#if defined(_M_ARM) || defined(__arm__) -#ifdef STBIR_USE_FMA -#undef STBIR_USE_FMA // no FMA for 32-bit arm on MSVC -#endif -#endif - -#if defined(__wasm__) && defined(__wasm_simd128__) -#ifndef STBIR_WASM -#define STBIR_WASM -#endif -#endif - #ifndef STBIRDEF #ifdef STB_IMAGE_RESIZE_STATIC #define STBIRDEF static @@ -1036,7 +1000,7 @@ typedef struct char no_cache_straddle[64]; } stbir__per_split_info; -typedef void stbir__decode_pixels_func( float * decode, int width_times_channels, void const * input ); +typedef float * stbir__decode_pixels_func( float * decode, int width_times_channels, void const * input ); typedef void stbir__alpha_weight_func( float * decode_buffer, int width_times_channels ); typedef void stbir__horizontal_gather_channels_func( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ); @@ -1099,8 +1063,8 @@ struct stbir__info #define stbir__max_uint8_as_float 255.0f #define stbir__max_uint16_as_float 65535.0f -#define stbir__max_uint8_as_float_inverted (1.0f/255.0f) -#define stbir__max_uint16_as_float_inverted (1.0f/65535.0f) +#define stbir__max_uint8_as_float_inverted 3.9215689e-03f // (1.0f/255.0f) +#define stbir__max_uint16_as_float_inverted 1.5259022e-05f // (1.0f/65535.0f) #define stbir__small_float ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) // min/max friendly @@ -1205,6 +1169,69 @@ static stbir__inline stbir_uint8 stbir__linear_to_srgb_uchar(float in) #define STBIR_FORCE_MINIMUM_SCANLINES_FOR_SPLITS 4 // when threading, what is the minimum number of scanlines for a split? #endif +#define STBIR_INPUT_CALLBACK_PADDING 3 + +#ifdef _M_IX86_FP +#if ( _M_IX86_FP >= 1 ) +#ifndef STBIR_SSE +#define STBIR_SSE +#endif +#endif +#endif + +#ifdef __TINYC__ + // tiny c has no intrinsics yet - this can become a version check if they add them + #define STBIR_NO_SIMD +#endif + +#if defined(_x86_64) || defined( __x86_64__ ) || defined( _M_X64 ) || defined(__x86_64) || defined(_M_AMD64) || defined(__SSE2__) || defined(STBIR_SSE) || defined(STBIR_SSE2) + #ifndef STBIR_SSE2 + #define STBIR_SSE2 + #endif + #if defined(__AVX__) || defined(STBIR_AVX2) + #ifndef STBIR_AVX + #ifndef STBIR_NO_AVX + #define STBIR_AVX + #endif + #endif + #endif + #if defined(__AVX2__) || defined(STBIR_AVX2) + #ifndef STBIR_NO_AVX2 + #ifndef STBIR_AVX2 + #define STBIR_AVX2 + #endif + #if defined( _MSC_VER ) && !defined(__clang__) + #ifndef STBIR_FP16C // FP16C instructions are on all AVX2 cpus, so we can autoselect it here on microsoft - clang needs -mf16c + #define STBIR_FP16C + #endif + #endif + #endif + #endif + #ifdef __F16C__ + #ifndef STBIR_FP16C // turn on FP16C instructions if the define is set (for clang and gcc) + #define STBIR_FP16C + #endif + #endif +#endif + +#if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) || ((__ARM_NEON_FP & 4) != 0) || defined(__ARM_NEON__) +#ifndef STBIR_NEON +#define STBIR_NEON +#endif +#endif + +#if defined(_M_ARM) || defined(__arm__) +#ifdef STBIR_USE_FMA +#undef STBIR_USE_FMA // no FMA for 32-bit arm on MSVC +#endif +#endif + +#if defined(__wasm__) && defined(__wasm_simd128__) +#ifndef STBIR_WASM +#define STBIR_WASM +#endif +#endif + // restrict pointers for the output pointers, other loop and unroll control #if defined( _MSC_VER ) && !defined(__clang__) #define STBIR_STREAMOUT_PTR( star ) star __restrict @@ -1451,8 +1478,8 @@ static stbir__inline stbir_uint8 stbir__linear_to_srgb_uchar(float in) #include #define stbir__simdf_pack_to_8words(out,reg0,reg1) out = _mm_packus_epi32(_mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg0,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())), _mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg1,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps()))) #else - STBIR__SIMDI_CONST(stbir__s32_32768, 32768); - STBIR__SIMDI_CONST(stbir__s16_32768, ((32768<<16)|32768)); + static STBIR__SIMDI_CONST(stbir__s32_32768, 32768); + static STBIR__SIMDI_CONST(stbir__s16_32768, ((32768<<16)|32768)); #define stbir__simdf_pack_to_8words(out,reg0,reg1) \ { \ @@ -2545,7 +2572,7 @@ static const STBIR__SIMDF_CONST(STBIR_simd_point5, 0.5f); static const STBIR__SIMDF_CONST(STBIR_ones, 1.0f); static const STBIR__SIMDI_CONST(STBIR_almost_zero, (127 - 13) << 23); static const STBIR__SIMDI_CONST(STBIR_almost_one, 0x3f7fffff); -static const STBIR__SIMDI_CONST(STBIR_mastissa_mask, 0xff); +static const STBIR__SIMDI_CONST(STBIR_mantissa_mask, 0xff); static const STBIR__SIMDI_CONST(STBIR_topscale, 0x02000000); // Basically, in simd mode, we unroll the proper amount, and we don't want @@ -2816,16 +2843,34 @@ static void stbir_overlapping_memcpy( void * dest, void const * src, size_t byte char STBIR_SIMD_STREAMOUT_PTR( * ) s_end = ((char*) src) + bytes; ptrdiff_t ofs_to_dest = (char*)dest - (char*)src; - if ( ofs_to_dest >= 8 ) // is the overlap more than 8 away? + if ( ofs_to_dest >= 8 ) // is the overlap more than 8 away { char STBIR_SIMD_STREAMOUT_PTR( * ) s_end8 = ((char*) src) + (bytes&~7); - STBIR_NO_UNROLL_LOOP_START - do + + if ( ( ( ((ptrdiff_t)dest)|((ptrdiff_t)src) ) & 7 ) == 0 ) // is it 8byte aligned? { - STBIR_NO_UNROLL(sd); - *(stbir_uint64*)( sd + ofs_to_dest ) = *(stbir_uint64*) sd; - sd += 8; - } while ( sd < s_end8 ); + STBIR_NO_UNROLL_LOOP_START + do + { + STBIR_NO_UNROLL(sd); + *(stbir_uint64*)( sd + ofs_to_dest ) = *(stbir_uint64*) sd; + sd += 8; + } while ( sd < s_end8 ); + } + else + { + STBIR_NO_UNROLL_LOOP_START + do + { + int a,b; + STBIR_NO_UNROLL(sd); + a = ((int*)sd)[0]; + b = ((int*)sd)[1]; + ((int*)( sd + ofs_to_dest ))[0] = a; + ((int*)( sd + ofs_to_dest ))[1] = b; + sd += 8; + } while ( sd < s_end8 ); + } if ( sd == s_end ) return; @@ -3217,10 +3262,9 @@ static void stbir__get_extents( stbir__sampler * samp, stbir__extents * scanline newspan->n0 = -left_margin; newspan->n1 = ( max_left - min_left ) - left_margin; scanline_extents->edge_sizes[0] = 0; // don't need to copy the left margin, since we are directly decoding into the margin - return; } - - // if we can't merge the min_left range, add it as a second range + // if we can't merge the min_right range, add it as a second range + else if ( ( right_margin ) && ( min_right != 0x7fffffff ) ) { stbir__span * newspan = scanline_extents->spans + 1; @@ -3235,7 +3279,14 @@ static void stbir__get_extents( stbir__sampler * samp, stbir__extents * scanline newspan->n0 = scanline_extents->spans[1].n1 + 1; newspan->n1 = scanline_extents->spans[1].n1 + 1 + ( max_right - min_right ); scanline_extents->edge_sizes[1] = 0; // don't need to copy the right margin, since we are directly decoding into the margin - return; + } + + // sort the spans into write output order + if ( ( scanline_extents->spans[1].n1 > scanline_extents->spans[1].n0 ) && ( scanline_extents->spans[0].n0 > scanline_extents->spans[1].n0 ) ) + { + stbir__span tspan = scanline_extents->spans[0]; + scanline_extents->spans[0] = scanline_extents->spans[1]; + scanline_extents->spans[1] = tspan; } } @@ -3328,23 +3379,29 @@ static void stbir__calculate_coefficients_for_gather_upsample( float out_filter_ static void stbir__insert_coeff( stbir__contributors * contribs, float * coeffs, int new_pixel, float new_coeff, int max_width ) { - if ( new_pixel <= contribs->n1 ) // before the end + if ( contribs->n1 < contribs->n0 ) // this first clause should never happen, but handle in case + { + contribs->n0 = contribs->n1 = new_pixel; + coeffs[0] = new_coeff; + } + else if ( new_pixel <= contribs->n1 ) // before the end { if ( new_pixel < contribs->n0 ) // before the front? { if ( ( contribs->n1 - new_pixel + 1 ) <= max_width ) { int j, o = contribs->n0 - new_pixel; - for ( j = contribs->n1 - contribs->n0 ; j <= 0 ; j-- ) + for ( j = contribs->n1 - contribs->n0 ; j >= 0 ; j-- ) coeffs[ j + o ] = coeffs[ j ]; - for ( j = 1 ; j < o ; j-- ) - coeffs[ j ] = coeffs[ 0 ]; + for ( j = 1 ; j < o ; j++ ) + coeffs[ j ] = 0; coeffs[ 0 ] = new_coeff; contribs->n0 = new_pixel; } } else { + // add new weight to existing coeff if already there coeffs[ new_pixel - contribs->n0 ] += new_coeff; } } @@ -3791,7 +3848,7 @@ static int stbir__pack_coefficients( int num_contributors, stbir__contributors* } } - // some horizontal routines read one float off the end (which is then masked off), so put in a sentinal so we don't read an snan or denormal + // some horizontal routines read one float off the end (which is then masked off), so put in a sentinel so we don't read an snan or denormal coefficents[ widest * num_contributors ] = 8888.0f; // the minimum we might read for unrolled filters widths is 12. So, we need to @@ -4560,7 +4617,8 @@ static void stbir__decode_scanline(stbir__info const * stbir_info, int n, float int row = stbir__edge_wrap(edge_vertical, n, stbir_info->vertical.scale_info.input_full_size); const void* input_plane_data = ( (char *) stbir_info->input_data ) + (size_t)row * (size_t) stbir_info->input_stride_bytes; stbir__span const * spans = stbir_info->scanline_extents.spans; - float* full_decode_buffer = output_buffer - stbir_info->scanline_extents.conservative.n0 * effective_channels; + float * full_decode_buffer = output_buffer - stbir_info->scanline_extents.conservative.n0 * effective_channels; + float * last_decoded = 0; // if we are on edge_zero, and we get in here with an out of bounds n, then the calculate filters has failed STBIR_ASSERT( !(edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->vertical.scale_info.input_full_size)) ); @@ -4588,12 +4646,12 @@ static void stbir__decode_scanline(stbir__info const * stbir_info, int n, float if ( stbir_info->in_pixels_cb ) { // call the callback with a temp buffer (that they can choose to use or not). the temp is just right aligned memory in the decode_buffer itself - input_data = stbir_info->in_pixels_cb( ( (char*) end_decode ) - ( width * input_sample_in_bytes ), input_plane_data, width, spans->pixel_offset_for_input, row, stbir_info->user_data ); + input_data = stbir_info->in_pixels_cb( ( (char*) end_decode ) - ( width * input_sample_in_bytes ) + ( ( stbir_info->input_type != STBIR_TYPE_FLOAT ) ? ( sizeof(float)*STBIR_INPUT_CALLBACK_PADDING ) : 0 ), input_plane_data, width, spans->pixel_offset_for_input, row, stbir_info->user_data ); } STBIR_PROFILE_START( decode ); // convert the pixels info the float decode_buffer, (we index from end_decode, so that when channelsdecode_pixels( (float*)end_decode - width_times_channels, width_times_channels, input_data ); + last_decoded = stbir_info->decode_pixels( (float*)end_decode - width_times_channels, width_times_channels, input_data ); STBIR_PROFILE_END( decode ); if (stbir_info->alpha_weight) @@ -4628,9 +4686,19 @@ static void stbir__decode_scanline(stbir__info const * stbir_info, int n, float float * marg = full_decode_buffer + x * effective_channels; float const * src = full_decode_buffer + stbir__edge_wrap(edge_horizontal, x, input_full_size) * effective_channels; STBIR_MEMCPY( marg, src, margin * effective_channels * sizeof(float) ); + if ( e == 1 ) last_decoded = marg + margin * effective_channels; } } } + + // some of the horizontal gathers read one float off the edge (which is masked out), but we force a zero here to make sure no NaNs leak in + // (we can't pre-zero it, because the input callback can use that area as padding) + last_decoded[0] = 0.0f; + + // we clear this extra float, because the final output pixel filter kernel might have used one less coeff than the max filter width + // when this happens, we do read that pixel from the input, so it too could be Nan, so just zero an extra one. + // this fits because each scanline is padded by three floats (STBIR_INPUT_CALLBACK_PADDING) + last_decoded[1] = 0.0f; } @@ -6209,6 +6277,8 @@ static void stbir__resample_vertical_gather(stbir__info const * stbir_info, stbi if ( vertical_first ) { // Now resample the gathered vertical data in the horizontal axis into the encode buffer + decode_buffer[ width_times_channels ] = 0.0f; // clear two over for horizontals with a remnant of 3 + decode_buffer[ width_times_channels+1 ] = 0.0f; stbir__resample_horizontal_gather(stbir_info, encode_buffer, decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); } @@ -6380,6 +6450,8 @@ static void stbir__vertical_scatter_loop( stbir__info const * stbir_info, stbir_ void * scanline_scatter_buffer; void * scanline_scatter_buffer_end; int on_first_input_y, last_input_y; + int width = (stbir_info->vertical_first) ? ( stbir_info->scanline_extents.conservative.n1-stbir_info->scanline_extents.conservative.n0+1 ) : stbir_info->horizontal.scale_info.output_sub_size; + int width_times_channels = stbir_info->effective_channels * width; STBIR_ASSERT( !stbir_info->vertical.is_gather ); @@ -6414,7 +6486,12 @@ static void stbir__vertical_scatter_loop( stbir__info const * stbir_info, stbir_ // mark all the buffers as empty to start for( y = 0 ; y < stbir_info->ring_buffer_num_entries ; y++ ) - stbir__get_ring_buffer_entry( stbir_info, split_info, y )[0] = STBIR__FLOAT_EMPTY_MARKER; // only used on scatter + { + float * decode_buffer = stbir__get_ring_buffer_entry( stbir_info, split_info, y ); + decode_buffer[ width_times_channels ] = 0.0f; // clear two over for horizontals with a remnant of 3 + decode_buffer[ width_times_channels+1 ] = 0.0f; + decode_buffer[0] = STBIR__FLOAT_EMPTY_MARKER; // only used on scatter + } // do the loop in input space on_first_input_y = 1; last_input_y = start_input_y; @@ -6562,7 +6639,7 @@ static void stbir__set_sampler(stbir__sampler * samp, stbir_filter filter, stbir samp->num_contributors = stbir__get_contributors(samp, samp->is_gather); samp->contributors_size = samp->num_contributors * sizeof(stbir__contributors); - samp->coefficients_size = samp->num_contributors * samp->coefficient_width * sizeof(float) + sizeof(float); // extra sizeof(float) is padding + samp->coefficients_size = samp->num_contributors * samp->coefficient_width * sizeof(float) + sizeof(float)*STBIR_INPUT_CALLBACK_PADDING; // extra sizeof(float) is padding samp->gather_prescatter_contributors = 0; samp->gather_prescatter_coefficients = 0; @@ -6667,7 +6744,7 @@ static void stbir__get_conservative_extents( stbir__sampler * samp, stbir__contr } } -static void stbir__get_split_info( stbir__per_split_info* split_info, int splits, int output_height, int vertical_pixel_margin, int input_full_height ) +static void stbir__get_split_info( stbir__per_split_info* split_info, int splits, int output_height, int vertical_pixel_margin, int input_full_height, int is_gather, stbir__contributors * contribs ) { int i, cur; int left = output_height; @@ -6676,9 +6753,58 @@ static void stbir__get_split_info( stbir__per_split_info* split_info, int splits for( i = 0 ; i < splits ; i++ ) { int each; + split_info[i].start_output_y = cur; each = left / ( splits - i ); split_info[i].end_output_y = cur + each; + + // ok, when we are gathering, we need to make sure we are starting on a y offset that doesn't have + // a "special" set of coefficients. Basically, with exactly the right filter at exactly the right + // resize at exactly the right phase, some of the coefficents can be zero. When they are zero, we + // don't process them at all. But this leads to a tricky thing with the thread splits, where we + // might have a set of two coeffs like this for example: (4,4) and (3,6). The 4,4 means there was + // just one single coeff because things worked out perfectly (normally, they all have 4 coeffs + // like the range 3,6. The problem is that if we start right on the (4,4) on a brand new thread, + // then when we get to (3,6), we don't have the "3" sample in memory (because we didn't load + // it on the initial (4,4) range because it didn't have a 3 (we only add new samples that are + // larger than our existing samples - it's just how the eviction works). So, our solution here + // is pretty simple, if we start right on a range that has samples that start earlier, then we + // simply bump up our previous thread split range to include it, and then start this threads + // range with the smaller sample. It just moves one scanline from one thread split to another, + // so that we end with the unusual one, instead of start with it. To do this, we check 2-4 + // sample at each thread split start and then occassionally move them. + + if ( ( is_gather ) && ( i ) ) + { + stbir__contributors * small_contribs; + int j, smallest, stop, start_n0; + stbir__contributors * split_contribs = contribs + cur; + + // scan for a max of 3x the filter width or until the next thread split + stop = vertical_pixel_margin * 3; + if ( each < stop ) + stop = each; + + // loops a few times before early out + smallest = 0; + small_contribs = split_contribs; + start_n0 = small_contribs->n0; + for( j = 1 ; j <= stop ; j++ ) + { + ++split_contribs; + if ( split_contribs->n0 > start_n0 ) + break; + if ( split_contribs->n0 < small_contribs->n0 ) + { + small_contribs = split_contribs; + smallest = j; + } + } + + split_info[i-1].end_output_y += smallest; + split_info[i].start_output_y += smallest; + } + cur += each; left -= each; @@ -6774,45 +6900,45 @@ static float stbir__compute_weights[5][STBIR_RESIZE_CLASSIFICATIONS][4]= // 5 = { 0.56250f, 0.59375f, 0.00000f, 0.96875f }, { 1.00000f, 0.06250f, 0.00000f, 1.00000f }, { 0.00000f, 0.09375f, 1.00000f, 1.00000f }, - { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 1.00000f, 1.00000f, 0.31250f, 1.00000f }, { 0.03125f, 0.12500f, 1.00000f, 1.00000f }, - { 0.06250f, 0.12500f, 0.00000f, 1.00000f }, + { 1.00000f, 1.00000f, 0.06250f, 1.00000f }, { 0.00000f, 1.00000f, 0.00000f, 0.03125f }, }, { { 0.00000f, 0.84375f, 0.00000f, 0.03125f }, { 0.09375f, 0.93750f, 0.00000f, 0.78125f }, { 0.87500f, 0.21875f, 0.00000f, 0.96875f }, { 0.09375f, 0.09375f, 1.00000f, 1.00000f }, - { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 0.00000f, 0.84375f, 0.00000f, 0.03125f }, { 0.03125f, 0.12500f, 1.00000f, 1.00000f }, - { 0.06250f, 0.12500f, 0.00000f, 1.00000f }, + { 1.00000f, 1.00000f, 0.06250f, 1.00000f }, { 0.00000f, 1.00000f, 0.00000f, 0.53125f }, }, { { 0.00000f, 0.53125f, 0.00000f, 0.03125f }, { 0.06250f, 0.96875f, 0.00000f, 0.53125f }, { 0.87500f, 0.18750f, 0.00000f, 0.93750f }, { 0.00000f, 0.09375f, 1.00000f, 1.00000f }, - { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 0.00000f, 0.53125f, 0.00000f, 0.03125f }, { 0.03125f, 0.12500f, 1.00000f, 1.00000f }, - { 0.06250f, 0.12500f, 0.00000f, 1.00000f }, + { 1.00000f, 1.00000f, 0.06250f, 1.00000f }, { 0.00000f, 1.00000f, 0.00000f, 0.56250f }, }, { { 0.00000f, 0.50000f, 0.00000f, 0.71875f }, { 0.06250f, 0.84375f, 0.00000f, 0.87500f }, { 1.00000f, 0.50000f, 0.50000f, 0.96875f }, { 1.00000f, 0.09375f, 0.31250f, 0.50000f }, - { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 0.00000f, 0.50000f, 0.00000f, 0.71875f }, { 1.00000f, 0.03125f, 0.03125f, 0.53125f }, - { 0.18750f, 0.12500f, 0.00000f, 1.00000f }, + { 1.00000f, 1.00000f, 0.06250f, 1.00000f }, { 0.00000f, 1.00000f, 0.03125f, 0.18750f }, }, { { 0.00000f, 0.59375f, 0.00000f, 0.96875f }, { 0.06250f, 0.81250f, 0.06250f, 0.59375f }, { 0.75000f, 0.43750f, 0.12500f, 0.96875f }, { 0.87500f, 0.06250f, 0.18750f, 0.43750f }, - { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 0.00000f, 0.59375f, 0.00000f, 0.96875f }, { 0.15625f, 0.12500f, 1.00000f, 1.00000f }, - { 0.06250f, 0.12500f, 0.00000f, 1.00000f }, + { 1.00000f, 1.00000f, 0.06250f, 1.00000f }, { 0.00000f, 1.00000f, 0.03125f, 0.34375f }, } }; @@ -6866,16 +6992,16 @@ static int stbir__should_do_vertical_first( float weights_table[STBIR_RESIZE_CLA // categorize the resize into buckets if ( ( vertical_output_size <= 4 ) || ( horizontal_output_size <= 4 ) ) v_classification = ( vertical_output_size < horizontal_output_size ) ? 6 : 7; + else if ( ( !is_gather ) && ( ( vertical_output_size <= 16 ) || ( horizontal_output_size <= 16 ) ) ) + v_classification = 4; else if ( vertical_scale <= 1.0f ) v_classification = ( is_gather ) ? 1 : 0; else if ( vertical_scale <= 2.0f) v_classification = 2; else if ( vertical_scale <= 3.0f) v_classification = 3; - else if ( vertical_scale <= 4.0f) - v_classification = 5; - else - v_classification = 6; + else + v_classification = 5; // everything bigger than 3x // use the right weights weights = weights_table[ v_classification ]; @@ -6927,7 +7053,8 @@ static stbir__info * stbir__alloc_internal_mem_and_build_samplers( stbir__sample void * alloced = 0; size_t alloced_total = 0; int vertical_first; - int decode_buffer_size, ring_buffer_length_bytes, ring_buffer_size, vertical_buffer_size, alloc_ring_buffer_num_entries; + size_t decode_buffer_size, ring_buffer_length_bytes, ring_buffer_size, vertical_buffer_size; + int alloc_ring_buffer_num_entries; int alpha_weighting_type = 0; // 0=none, 1=simple, 2=fancy int conservative_split_output_size = stbir__get_max_split( splits, vertical->scale_info.output_sub_size ); @@ -6972,14 +7099,16 @@ static stbir__info * stbir__alloc_internal_mem_and_build_samplers( stbir__sample vertical_first = stbir__should_do_vertical_first( stbir__compute_weights[ (int)stbir_channel_count_index[ effective_channels ] ], horizontal->filter_pixel_width, horizontal->scale_info.scale, horizontal->scale_info.output_sub_size, vertical->filter_pixel_width, vertical->scale_info.scale, vertical->scale_info.output_sub_size, vertical->is_gather, STBIR__V_FIRST_INFO_POINTER ); // sometimes read one float off in some of the unrolled loops (with a weight of zero coeff, so it doesn't have an effect) - decode_buffer_size = ( conservative->n1 - conservative->n0 + 1 ) * effective_channels * sizeof(float) + sizeof(float); // extra float for padding + // we use a few extra floats instead of just 1, so that input callback buffer can overlap with the decode buffer without + // the conversion routines overwriting the callback input data. + decode_buffer_size = ( conservative->n1 - conservative->n0 + 1 ) * effective_channels * sizeof(float) + sizeof(float)*STBIR_INPUT_CALLBACK_PADDING; // extra floats for input callback stagger #if defined( STBIR__SEPARATE_ALLOCATIONS ) && defined(STBIR_SIMD8) if ( effective_channels == 3 ) decode_buffer_size += sizeof(float); // avx in 3 channel mode needs one float at the start of the buffer (only with separate allocations) #endif - ring_buffer_length_bytes = horizontal->scale_info.output_sub_size * effective_channels * sizeof(float) + sizeof(float); // extra float for padding + ring_buffer_length_bytes = (size_t)horizontal->scale_info.output_sub_size * (size_t)effective_channels * sizeof(float) + sizeof(float)*STBIR_INPUT_CALLBACK_PADDING; // extra floats for padding // if we do vertical first, the ring buffer holds a whole decoded line if ( vertical_first ) @@ -6994,13 +7123,13 @@ static stbir__info * stbir__alloc_internal_mem_and_build_samplers( stbir__sample if ( ( !vertical->is_gather ) && ( alloc_ring_buffer_num_entries > conservative_split_output_size ) ) alloc_ring_buffer_num_entries = conservative_split_output_size; - ring_buffer_size = alloc_ring_buffer_num_entries * ring_buffer_length_bytes; + ring_buffer_size = (size_t)alloc_ring_buffer_num_entries * (size_t)ring_buffer_length_bytes; // The vertical buffer is used differently, depending on whether we are scattering // the vertical scanlines, or gathering them. // If scattering, it's used at the temp buffer to accumulate each output. // If gathering, it's just the output buffer. - vertical_buffer_size = horizontal->scale_info.output_sub_size * effective_channels * sizeof(float) + sizeof(float); // extra float for padding + vertical_buffer_size = (size_t)horizontal->scale_info.output_sub_size * (size_t)effective_channels * sizeof(float) + sizeof(float); // extra float for padding // we make two passes through this loop, 1st to add everything up, 2nd to allocate and init for(;;) @@ -7013,7 +7142,7 @@ static stbir__info * stbir__alloc_internal_mem_and_build_samplers( stbir__sample #ifdef STBIR__SEPARATE_ALLOCATIONS #define STBIR__NEXT_PTR( ptr, size, ntype ) if ( alloced ) { void * p = STBIR_MALLOC( size, user_data); if ( p == 0 ) { stbir__free_internal_mem( info ); return 0; } (ptr) = (ntype*)p; } #else - #define STBIR__NEXT_PTR( ptr, size, ntype ) advance_mem = (void*) ( ( ((size_t)advance_mem) + 15 ) & ~15 ); if ( alloced ) ptr = (ntype*)advance_mem; advance_mem = ((char*)advance_mem) + (size); + #define STBIR__NEXT_PTR( ptr, size, ntype ) advance_mem = (void*) ( ( ((size_t)advance_mem) + 15 ) & ~15 ); if ( alloced ) ptr = (ntype*)advance_mem; advance_mem = (char*)(((size_t)advance_mem) + (size)); #endif STBIR__NEXT_PTR( info, sizeof( stbir__info ), stbir__info ); @@ -7036,9 +7165,9 @@ static stbir__info * stbir__alloc_internal_mem_and_build_samplers( stbir__sample info->offset_x = new_x; info->offset_y = new_y; - info->alloc_ring_buffer_num_entries = alloc_ring_buffer_num_entries; + info->alloc_ring_buffer_num_entries = (int)alloc_ring_buffer_num_entries; info->ring_buffer_num_entries = 0; - info->ring_buffer_length_bytes = ring_buffer_length_bytes; + info->ring_buffer_length_bytes = (int)ring_buffer_length_bytes; info->splits = splits; info->vertical_first = vertical_first; @@ -7119,14 +7248,14 @@ static stbir__info * stbir__alloc_internal_mem_and_build_samplers( stbir__sample // alloc memory for to-be-pivoted coeffs (if necessary) if ( vertical->is_gather == 0 ) { - int both; - int temp_mem_amt; + size_t both; + size_t temp_mem_amt; // when in vertical scatter mode, we first build the coefficients in gather mode, and then pivot after, // that means we need two buffers, so we try to use the decode buffer and ring buffer for this. if that // is too small, we just allocate extra memory to use as this temp. - both = vertical->gather_prescatter_contributors_size + vertical->gather_prescatter_coefficients_size; + both = (size_t)vertical->gather_prescatter_contributors_size + (size_t)vertical->gather_prescatter_coefficients_size; #ifdef STBIR__SEPARATE_ALLOCATIONS temp_mem_amt = decode_buffer_size; @@ -7136,7 +7265,7 @@ static stbir__info * stbir__alloc_internal_mem_and_build_samplers( stbir__sample --temp_mem_amt; // avx in 3 channel mode needs one float at the start of the buffer #endif #else - temp_mem_amt = ( decode_buffer_size + ring_buffer_size + vertical_buffer_size ) * splits; + temp_mem_amt = (size_t)( decode_buffer_size + ring_buffer_size + vertical_buffer_size ) * (size_t)splits; #endif if ( temp_mem_amt >= both ) { @@ -7222,7 +7351,7 @@ static stbir__info * stbir__alloc_internal_mem_and_build_samplers( stbir__sample } // setup the vertical split ranges - stbir__get_split_info( info->split_info, info->splits, info->vertical.scale_info.output_sub_size, info->vertical.filter_pixel_margin, info->vertical.scale_info.input_full_size ); + stbir__get_split_info( info->split_info, info->splits, info->vertical.scale_info.output_sub_size, info->vertical.filter_pixel_margin, info->vertical.scale_info.input_full_size, info->vertical.is_gather, info->vertical.contributors ); // now we know precisely how many entries we need info->ring_buffer_num_entries = info->vertical.extent_info.widest; @@ -7231,39 +7360,7 @@ static stbir__info * stbir__alloc_internal_mem_and_build_samplers( stbir__sample if ( ( !info->vertical.is_gather ) && ( info->ring_buffer_num_entries > conservative_split_output_size ) ) info->ring_buffer_num_entries = conservative_split_output_size; STBIR_ASSERT( info->ring_buffer_num_entries <= info->alloc_ring_buffer_num_entries ); - - // a few of the horizontal gather functions read past the end of the decode (but mask it out), - // so put in normal values so no snans or denormals accidentally sneak in (also, in the ring - // buffer for vertical first) - for( i = 0 ; i < splits ; i++ ) - { - int t, ofs, start; - - ofs = decode_buffer_size / 4; - - #if defined( STBIR__SEPARATE_ALLOCATIONS ) && defined(STBIR_SIMD8) - if ( effective_channels == 3 ) - --ofs; // avx in 3 channel mode needs one float at the start of the buffer, so we snap back for clearing - #endif - - start = ofs - 4; - if ( start < 0 ) start = 0; - - for( t = start ; t < ofs; t++ ) - info->split_info[i].decode_buffer[ t ] = 9999.0f; - - if ( vertical_first ) - { - int j; - for( j = 0; j < info->ring_buffer_num_entries ; j++ ) - { - for( t = start ; t < ofs; t++ ) - stbir__get_ring_buffer_entry( info, info->split_info + i, j )[ t ] = 9999.0f; - } - } - } } - #undef STBIR__NEXT_PTR @@ -7528,7 +7625,7 @@ static int stbir__double_to_rational(double f, stbir_uint32 limit, stbir_uint32 numer_estimate = temp; } - // we didn't fine anything good enough for float, use a full range estimate + // we didn't find anything good enough for float, use a full range estimate if ( limit_denom ) { numer_estimate= (stbir_uint64)( f * (double)limit + 0.5 ); @@ -7818,7 +7915,7 @@ static int stbir__perform_build( STBIR_RESIZE * resize, int splits ) stbir__set_sampler(&horizontal, resize->horizontal_filter, resize->horizontal_filter_kernel, resize->horizontal_filter_support, resize->horizontal_edge, &horizontal.scale_info, 1, resize->user_data ); stbir__get_conservative_extents( &horizontal, &conservative, resize->user_data ); - stbir__set_sampler(&vertical, resize->vertical_filter, resize->horizontal_filter_kernel, resize->vertical_filter_support, resize->vertical_edge, &vertical.scale_info, 0, resize->user_data ); + stbir__set_sampler(&vertical, resize->vertical_filter, resize->vertical_filter_kernel, resize->vertical_filter_support, resize->vertical_edge, &vertical.scale_info, 0, resize->user_data ); if ( ( vertical.scale_info.output_sub_size / splits ) < STBIR_FORCE_MINIMUM_SCANLINES_FOR_SPLITS ) // each split should be a minimum of 4 scanlines (handwavey choice) { @@ -7849,7 +7946,7 @@ static int stbir__perform_build( STBIR_RESIZE * resize, int splits ) return 0; } -void stbir_free_samplers( STBIR_RESIZE * resize ) +STBIRDEF void stbir_free_samplers( STBIR_RESIZE * resize ) { if ( resize->samplers ) { @@ -7943,138 +8040,64 @@ STBIRDEF int stbir_resize_extended_split( STBIR_RESIZE * resize, int split_start return stbir__perform_resize( resize->samplers, split_start, split_count ); } -static int stbir__check_output_stuff( void ** ret_ptr, int * ret_pitch, void * output_pixels, int type_size, int output_w, int output_h, int output_stride_in_bytes, stbir_internal_pixel_layout pixel_layout ) + +static void * stbir_quick_resize_helper( const void *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout, stbir_datatype data_type, stbir_edge edge, stbir_filter filter ) { - size_t size; - int pitch; - void * ptr; + STBIR_RESIZE resize; + int scanline_output_in_bytes; + int positive_output_stride_in_bytes; + void * start_ptr; + void * free_ptr; - pitch = output_w * type_size * stbir__pixel_channels[ pixel_layout ]; - if ( pitch == 0 ) + scanline_output_in_bytes = output_w * stbir__type_size[ data_type ] * stbir__pixel_channels[ stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ]; + if ( scanline_output_in_bytes == 0 ) return 0; + // if zero stride, use scanline output if ( output_stride_in_bytes == 0 ) - output_stride_in_bytes = pitch; + output_stride_in_bytes = scanline_output_in_bytes; - if ( output_stride_in_bytes < pitch ) + // abs value for inverted images (negative pitches) + positive_output_stride_in_bytes = output_stride_in_bytes; + if ( positive_output_stride_in_bytes < 0 ) + positive_output_stride_in_bytes = -positive_output_stride_in_bytes; + + // is the requested stride smaller than the scanline output? if so, just fail + if ( positive_output_stride_in_bytes < scanline_output_in_bytes ) return 0; - size = (size_t)output_stride_in_bytes * (size_t)output_h; - if ( size == 0 ) - return 0; - - *ret_ptr = 0; - *ret_pitch = output_stride_in_bytes; + start_ptr = output_pixels; + free_ptr = 0; // no free pointer, since they passed buffer to use + // did they pass a zero for the dest? if so, allocate the buffer if ( output_pixels == 0 ) { - ptr = STBIR_MALLOC( size, 0 ); + size_t size; + char * ptr; + + size = (size_t)positive_output_stride_in_bytes * (size_t)output_h; + if ( size == 0 ) + return 0; + + ptr = (char*) STBIR_MALLOC( size, 0 ); if ( ptr == 0 ) return 0; - *ret_ptr = ptr; - *ret_pitch = pitch; + free_ptr = ptr; + + // point at the last scanline, if they requested a flipped image + if ( output_stride_in_bytes < 0 ) + start_ptr = ptr + ( (size_t)positive_output_stride_in_bytes * (size_t)( output_h - 1 ) ); + else + start_ptr = ptr; } - return 1; -} - - -STBIRDEF unsigned char * stbir_resize_uint8_linear( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_pixel_layout pixel_layout ) -{ - STBIR_RESIZE resize; - unsigned char * optr; - int opitch; - - if ( !stbir__check_output_stuff( (void**)&optr, &opitch, output_pixels, sizeof( unsigned char ), output_w, output_h, output_stride_in_bytes, stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ) ) - return 0; - + // ok, now do the resize stbir_resize_init( &resize, input_pixels, input_w, input_h, input_stride_in_bytes, - (optr) ? optr : output_pixels, output_w, output_h, opitch, - pixel_layout, STBIR_TYPE_UINT8 ); - - if ( !stbir_resize_extended( &resize ) ) - { - if ( optr ) - STBIR_FREE( optr, 0 ); - return 0; - } - - return (optr) ? optr : output_pixels; -} - -STBIRDEF unsigned char * stbir_resize_uint8_srgb( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_pixel_layout pixel_layout ) -{ - STBIR_RESIZE resize; - unsigned char * optr; - int opitch; - - if ( !stbir__check_output_stuff( (void**)&optr, &opitch, output_pixels, sizeof( unsigned char ), output_w, output_h, output_stride_in_bytes, stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ) ) - return 0; - - stbir_resize_init( &resize, - input_pixels, input_w, input_h, input_stride_in_bytes, - (optr) ? optr : output_pixels, output_w, output_h, opitch, - pixel_layout, STBIR_TYPE_UINT8_SRGB ); - - if ( !stbir_resize_extended( &resize ) ) - { - if ( optr ) - STBIR_FREE( optr, 0 ); - return 0; - } - - return (optr) ? optr : output_pixels; -} - - -STBIRDEF float * stbir_resize_float_linear( const float *input_pixels , int input_w , int input_h, int input_stride_in_bytes, - float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_pixel_layout pixel_layout ) -{ - STBIR_RESIZE resize; - float * optr; - int opitch; - - if ( !stbir__check_output_stuff( (void**)&optr, &opitch, output_pixels, sizeof( float ), output_w, output_h, output_stride_in_bytes, stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ) ) - return 0; - - stbir_resize_init( &resize, - input_pixels, input_w, input_h, input_stride_in_bytes, - (optr) ? optr : output_pixels, output_w, output_h, opitch, - pixel_layout, STBIR_TYPE_FLOAT ); - - if ( !stbir_resize_extended( &resize ) ) - { - if ( optr ) - STBIR_FREE( optr, 0 ); - return 0; - } - - return (optr) ? optr : output_pixels; -} - - -STBIRDEF void * stbir_resize( const void *input_pixels , int input_w , int input_h, int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_pixel_layout pixel_layout, stbir_datatype data_type, - stbir_edge edge, stbir_filter filter ) -{ - STBIR_RESIZE resize; - float * optr; - int opitch; - - if ( !stbir__check_output_stuff( (void**)&optr, &opitch, output_pixels, stbir__type_size[data_type], output_w, output_h, output_stride_in_bytes, stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ) ) - return 0; - - stbir_resize_init( &resize, - input_pixels, input_w, input_h, input_stride_in_bytes, - (optr) ? optr : output_pixels, output_w, output_h, output_stride_in_bytes, + start_ptr, output_w, output_h, output_stride_in_bytes, pixel_layout, data_type ); resize.horizontal_edge = edge; @@ -8084,19 +8107,60 @@ STBIRDEF void * stbir_resize( const void *input_pixels , int input_w , int input if ( !stbir_resize_extended( &resize ) ) { - if ( optr ) - STBIR_FREE( optr, 0 ); + if ( free_ptr ) + STBIR_FREE( free_ptr, 0 ); return 0; } - return (optr) ? optr : output_pixels; + return (free_ptr) ? free_ptr : start_ptr; +} + + + +STBIRDEF unsigned char * stbir_resize_uint8_linear( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout ) +{ + return (unsigned char *) stbir_quick_resize_helper( input_pixels , input_w , input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + pixel_layout, STBIR_TYPE_UINT8, STBIR_EDGE_CLAMP, STBIR_FILTER_DEFAULT ); +} + +STBIRDEF unsigned char * stbir_resize_uint8_srgb( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout ) +{ + return (unsigned char *) stbir_quick_resize_helper( input_pixels , input_w , input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + pixel_layout, STBIR_TYPE_UINT8_SRGB, STBIR_EDGE_CLAMP, STBIR_FILTER_DEFAULT ); +} + + +STBIRDEF float * stbir_resize_float_linear( const float *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout ) +{ + return (float *) stbir_quick_resize_helper( input_pixels , input_w , input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + pixel_layout, STBIR_TYPE_FLOAT, STBIR_EDGE_CLAMP, STBIR_FILTER_DEFAULT ); +} + + +STBIRDEF void * stbir_resize( const void *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout, stbir_datatype data_type, + stbir_edge edge, stbir_filter filter ) +{ + return (void *) stbir_quick_resize_helper( input_pixels , input_w , input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + pixel_layout, data_type, edge, filter ); } #ifdef STBIR_PROFILE STBIRDEF void stbir_resize_build_profile_info( STBIR_PROFILE_INFO * info, STBIR_RESIZE const * resize ) { - static char const * bdescriptions[6] = { "Building", "Allocating", "Horizontal sampler", "Vertical sampler", "Coefficient cleanup", "Coefficient piovot" } ; + static char const * bdescriptions[6] = { "Building", "Allocating", "Horizontal sampler", "Vertical sampler", "Coefficient cleanup", "Coefficient pivot" } ; stbir__info* samp = resize->samplers; int i; @@ -8147,7 +8211,7 @@ STBIRDEF void stbir_resize_split_profile_info( STBIR_PROFILE_INFO * info, STBIR_ info->clocks[i] = sum; } - info->total_clocks = split_info->profile.named.total; + info->total_clocks = split_info->profile.named.total; info->descriptions = descriptions; info->count = STBIR__ARRAY_SIZE( descriptions ); } @@ -8226,7 +8290,7 @@ STBIRDEF void stbir_resize_extended_profile_info( STBIR_PROFILE_INFO * info, STB #define stbir__encode_simdfX_unflip stbir__encode_simdf4_unflip #endif -static void STBIR__CODER_NAME( stbir__decode_uint8_linear_scaled )( float * decodep, int width_times_channels, void const * inputp ) +static float * STBIR__CODER_NAME( stbir__decode_uint8_linear_scaled )( float * decodep, int width_times_channels, void const * inputp ) { float STBIR_STREAMOUT_PTR( * ) decode = decodep; float * decode_end = (float*) decode + width_times_channels; @@ -8286,7 +8350,7 @@ static void STBIR__CODER_NAME( stbir__decode_uint8_linear_scaled )( float * deco decode = decode_end; // backup and do last couple input = end_input_m16; } - return; + return decode_end + 16; } #endif @@ -8324,6 +8388,8 @@ static void STBIR__CODER_NAME( stbir__decode_uint8_linear_scaled )( float * deco input += stbir__coder_min_num; } #endif + + return decode_end; } static void STBIR__CODER_NAME( stbir__encode_uint8_linear_scaled )( void * outputp, int width_times_channels, float const * encode ) @@ -8443,7 +8509,7 @@ static void STBIR__CODER_NAME( stbir__encode_uint8_linear_scaled )( void * outpu #endif } -static void STBIR__CODER_NAME(stbir__decode_uint8_linear)( float * decodep, int width_times_channels, void const * inputp ) +static float * STBIR__CODER_NAME(stbir__decode_uint8_linear)( float * decodep, int width_times_channels, void const * inputp ) { float STBIR_STREAMOUT_PTR( * ) decode = decodep; float * decode_end = (float*) decode + width_times_channels; @@ -8497,7 +8563,7 @@ static void STBIR__CODER_NAME(stbir__decode_uint8_linear)( float * decodep, int decode = decode_end; // backup and do last couple input = end_input_m16; } - return; + return decode_end + 16; } #endif @@ -8535,6 +8601,7 @@ static void STBIR__CODER_NAME(stbir__decode_uint8_linear)( float * decodep, int input += stbir__coder_min_num; } #endif + return decode_end; } static void STBIR__CODER_NAME( stbir__encode_uint8_linear )( void * outputp, int width_times_channels, float const * encode ) @@ -8636,10 +8703,10 @@ static void STBIR__CODER_NAME( stbir__encode_uint8_linear )( void * outputp, int #endif } -static void STBIR__CODER_NAME(stbir__decode_uint8_srgb)( float * decodep, int width_times_channels, void const * inputp ) +static float * STBIR__CODER_NAME(stbir__decode_uint8_srgb)( float * decodep, int width_times_channels, void const * inputp ) { float STBIR_STREAMOUT_PTR( * ) decode = decodep; - float const * decode_end = (float*) decode + width_times_channels; + float * decode_end = (float*) decode + width_times_channels; unsigned char const * input = (unsigned char const *)inputp; // try to do blocks of 4 when you can @@ -8674,6 +8741,7 @@ static void STBIR__CODER_NAME(stbir__decode_uint8_srgb)( float * decodep, int wi input += stbir__coder_min_num; } #endif + return decode_end; } #define stbir__min_max_shift20( i, f ) \ @@ -8691,7 +8759,7 @@ static void STBIR__CODER_NAME(stbir__decode_uint8_srgb)( float * decodep, int wi { \ stbir__simdi temp; \ stbir__simdi_32shr( temp, stbir_simdi_castf( f ), 12 ) ; \ - stbir__simdi_and( temp, temp, STBIR__CONSTI(STBIR_mastissa_mask) ); \ + stbir__simdi_and( temp, temp, STBIR__CONSTI(STBIR_mantissa_mask) ); \ stbir__simdi_or( temp, temp, STBIR__CONSTI(STBIR_topscale) ); \ stbir__simdi_16madd( i, i, temp ); \ stbir__simdi_32shr( i, i, 16 ); \ @@ -8826,11 +8894,12 @@ static void STBIR__CODER_NAME( stbir__encode_uint8_srgb )( void * outputp, int w #if ( stbir__coder_min_num == 4 ) || ( ( stbir__coder_min_num == 1 ) && ( !defined(stbir__decode_swizzle) ) ) -static void STBIR__CODER_NAME(stbir__decode_uint8_srgb4_linearalpha)( float * decodep, int width_times_channels, void const * inputp ) +static float * STBIR__CODER_NAME(stbir__decode_uint8_srgb4_linearalpha)( float * decodep, int width_times_channels, void const * inputp ) { float STBIR_STREAMOUT_PTR( * ) decode = decodep; - float const * decode_end = (float*) decode + width_times_channels; + float * decode_end = (float*) decode + width_times_channels; unsigned char const * input = (unsigned char const *)inputp; + do { decode[0] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0] ]; decode[1] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order1] ]; @@ -8839,6 +8908,7 @@ static void STBIR__CODER_NAME(stbir__decode_uint8_srgb4_linearalpha)( float * de input += 4; decode += 4; } while( decode < decode_end ); + return decode_end; } @@ -8911,11 +8981,12 @@ static void STBIR__CODER_NAME( stbir__encode_uint8_srgb4_linearalpha )( void * o #if ( stbir__coder_min_num == 2 ) || ( ( stbir__coder_min_num == 1 ) && ( !defined(stbir__decode_swizzle) ) ) -static void STBIR__CODER_NAME(stbir__decode_uint8_srgb2_linearalpha)( float * decodep, int width_times_channels, void const * inputp ) +static float * STBIR__CODER_NAME(stbir__decode_uint8_srgb2_linearalpha)( float * decodep, int width_times_channels, void const * inputp ) { float STBIR_STREAMOUT_PTR( * ) decode = decodep; - float const * decode_end = (float*) decode + width_times_channels; + float * decode_end = (float*) decode + width_times_channels; unsigned char const * input = (unsigned char const *)inputp; + decode += 4; while( decode <= decode_end ) { @@ -8929,9 +9000,10 @@ static void STBIR__CODER_NAME(stbir__decode_uint8_srgb2_linearalpha)( float * de decode -= 4; if( decode < decode_end ) { - decode[0] = stbir__srgb_uchar_to_linear_float[ stbir__decode_order0 ]; + decode[0] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0] ]; decode[1] = ( (float) input[stbir__decode_order1] ) * stbir__max_uint8_as_float_inverted; } + return decode_end; } static void STBIR__CODER_NAME( stbir__encode_uint8_srgb2_linearalpha )( void * outputp, int width_times_channels, float const * encode ) @@ -8997,7 +9069,7 @@ static void STBIR__CODER_NAME( stbir__encode_uint8_srgb2_linearalpha )( void * o #endif -static void STBIR__CODER_NAME(stbir__decode_uint16_linear_scaled)( float * decodep, int width_times_channels, void const * inputp ) +static float * STBIR__CODER_NAME(stbir__decode_uint16_linear_scaled)( float * decodep, int width_times_channels, void const * inputp ) { float STBIR_STREAMOUT_PTR( * ) decode = decodep; float * decode_end = (float*) decode + width_times_channels; @@ -9045,7 +9117,7 @@ static void STBIR__CODER_NAME(stbir__decode_uint16_linear_scaled)( float * decod decode = decode_end; // backup and do last couple input = end_input_m8; } - return; + return decode_end + 8; } #endif @@ -9083,6 +9155,7 @@ static void STBIR__CODER_NAME(stbir__decode_uint16_linear_scaled)( float * decod input += stbir__coder_min_num; } #endif + return decode_end; } @@ -9202,7 +9275,7 @@ static void STBIR__CODER_NAME(stbir__encode_uint16_linear_scaled)( void * output #endif } -static void STBIR__CODER_NAME(stbir__decode_uint16_linear)( float * decodep, int width_times_channels, void const * inputp ) +static float * STBIR__CODER_NAME(stbir__decode_uint16_linear)( float * decodep, int width_times_channels, void const * inputp ) { float STBIR_STREAMOUT_PTR( * ) decode = decodep; float * decode_end = (float*) decode + width_times_channels; @@ -9247,7 +9320,7 @@ static void STBIR__CODER_NAME(stbir__decode_uint16_linear)( float * decodep, int decode = decode_end; // backup and do last couple input = end_input_m8; } - return; + return decode_end + 8; } #endif @@ -9285,6 +9358,7 @@ static void STBIR__CODER_NAME(stbir__decode_uint16_linear)( float * decodep, int input += stbir__coder_min_num; } #endif + return decode_end; } static void STBIR__CODER_NAME(stbir__encode_uint16_linear)( void * outputp, int width_times_channels, float const * encode ) @@ -9385,7 +9459,7 @@ static void STBIR__CODER_NAME(stbir__encode_uint16_linear)( void * outputp, int #endif } -static void STBIR__CODER_NAME(stbir__decode_half_float_linear)( float * decodep, int width_times_channels, void const * inputp ) +static float * STBIR__CODER_NAME(stbir__decode_half_float_linear)( float * decodep, int width_times_channels, void const * inputp ) { float STBIR_STREAMOUT_PTR( * ) decode = decodep; float * decode_end = (float*) decode + width_times_channels; @@ -9431,7 +9505,7 @@ static void STBIR__CODER_NAME(stbir__decode_half_float_linear)( float * decodep, decode = decode_end; // backup and do last couple input = end_input_m8; } - return; + return decode_end + 8; } #endif @@ -9469,6 +9543,7 @@ static void STBIR__CODER_NAME(stbir__decode_half_float_linear)( float * decodep, input += stbir__coder_min_num; } #endif + return decode_end; } static void STBIR__CODER_NAME( stbir__encode_half_float_linear )( void * outputp, int width_times_channels, float const * encode ) @@ -9555,7 +9630,7 @@ static void STBIR__CODER_NAME( stbir__encode_half_float_linear )( void * outputp #endif } -static void STBIR__CODER_NAME(stbir__decode_float_linear)( float * decodep, int width_times_channels, void const * inputp ) +static float * STBIR__CODER_NAME(stbir__decode_float_linear)( float * decodep, int width_times_channels, void const * inputp ) { #ifdef stbir__decode_swizzle float STBIR_STREAMOUT_PTR( * ) decode = decodep; @@ -9609,7 +9684,7 @@ static void STBIR__CODER_NAME(stbir__decode_float_linear)( float * decodep, int decode = decode_end; // backup and do last couple input = end_input_m16; } - return; + return decode_end + 16; } #endif @@ -9647,18 +9722,21 @@ static void STBIR__CODER_NAME(stbir__decode_float_linear)( float * decodep, int input += stbir__coder_min_num; } #endif + return decode_end; #else if ( (void*)decodep != inputp ) STBIR_MEMCPY( decodep, inputp, width_times_channels * sizeof( float ) ); + return decodep + width_times_channels; + #endif } static void STBIR__CODER_NAME( stbir__encode_float_linear )( void * outputp, int width_times_channels, float const * encode ) { - #if !defined( STBIR_FLOAT_HIGH_CLAMP ) && !defined(STBIR_FLOAT_LO_CLAMP) && !defined(stbir__decode_swizzle) + #if !defined( STBIR_FLOAT_HIGH_CLAMP ) && !defined(STBIR_FLOAT_LOW_CLAMP) && !defined(stbir__decode_swizzle) if ( (void*)outputp != (void*) encode ) STBIR_MEMCPY( outputp, encode, width_times_channels * sizeof( float ) ); @@ -9713,7 +9791,7 @@ static void STBIR__CODER_NAME( stbir__encode_float_linear )( void * outputp, int stbir__simdfX_store( output+stbir__simdfX_float_count, e1 ); encode += stbir__simdfX_float_count * 2; output += stbir__simdfX_float_count * 2; - if ( output < end_output ) + if ( output <= end_output ) continue; if ( output == ( end_output + ( stbir__simdfX_float_count * 2 ) ) ) break; From 6c0134bb5c4309713cccacf06ad03a4c06bcc2af Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Mar 2026 21:24:24 +0200 Subject: [PATCH 243/319] Create cgltf_write.h --- src/external/cgltf_write.h | 1565 ++++++++++++++++++++++++++++++++++++ 1 file changed, 1565 insertions(+) create mode 100644 src/external/cgltf_write.h diff --git a/src/external/cgltf_write.h b/src/external/cgltf_write.h new file mode 100644 index 000000000..4c060da79 --- /dev/null +++ b/src/external/cgltf_write.h @@ -0,0 +1,1565 @@ +/** + * cgltf_write - a single-file glTF 2.0 writer written in C99. + * + * Version: 1.15 + * + * Website: https://github.com/jkuhlmann/cgltf + * + * Distributed under the MIT License, see notice at the end of this file. + * + * Building: + * Include this file where you need the struct and function + * declarations. Have exactly one source file where you define + * `CGLTF_WRITE_IMPLEMENTATION` before including this file to get the + * function definitions. + * + * Reference: + * `cgltf_result cgltf_write_file(const cgltf_options* options, const char* + * path, const cgltf_data* data)` writes a glTF data to the given file path. + * If `options->type` is `cgltf_file_type_glb`, both JSON content and binary + * buffer of the given glTF data will be written in a GLB format. + * Otherwise, only the JSON part will be written. + * External buffers and images are not written out. `data` is not deallocated. + * + * `cgltf_size cgltf_write(const cgltf_options* options, char* buffer, + * cgltf_size size, const cgltf_data* data)` writes JSON into the given memory + * buffer. Returns the number of bytes written to `buffer`, including a null + * terminator. If buffer is null, returns the number of bytes that would have + * been written. `data` is not deallocated. + */ +#ifndef CGLTF_WRITE_H_INCLUDED__ +#define CGLTF_WRITE_H_INCLUDED__ + +#include "cgltf.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +cgltf_result cgltf_write_file(const cgltf_options* options, const char* path, const cgltf_data* data); +cgltf_size cgltf_write(const cgltf_options* options, char* buffer, cgltf_size size, const cgltf_data* data); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef CGLTF_WRITE_H_INCLUDED__ */ + +/* + * + * Stop now, if you are only interested in the API. + * Below, you find the implementation. + * + */ + +#if defined(__INTELLISENSE__) || defined(__JETBRAINS_IDE__) +/* This makes MSVC/CLion intellisense work. */ +#define CGLTF_WRITE_IMPLEMENTATION +#endif + +#ifdef CGLTF_WRITE_IMPLEMENTATION + +#include +#include +#include +#include +#include +#include + +#define CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM (1 << 0) +#define CGLTF_EXTENSION_FLAG_MATERIALS_UNLIT (1 << 1) +#define CGLTF_EXTENSION_FLAG_SPECULAR_GLOSSINESS (1 << 2) +#define CGLTF_EXTENSION_FLAG_LIGHTS_PUNCTUAL (1 << 3) +#define CGLTF_EXTENSION_FLAG_DRACO_MESH_COMPRESSION (1 << 4) +#define CGLTF_EXTENSION_FLAG_MATERIALS_CLEARCOAT (1 << 5) +#define CGLTF_EXTENSION_FLAG_MATERIALS_IOR (1 << 6) +#define CGLTF_EXTENSION_FLAG_MATERIALS_SPECULAR (1 << 7) +#define CGLTF_EXTENSION_FLAG_MATERIALS_TRANSMISSION (1 << 8) +#define CGLTF_EXTENSION_FLAG_MATERIALS_SHEEN (1 << 9) +#define CGLTF_EXTENSION_FLAG_MATERIALS_VARIANTS (1 << 10) +#define CGLTF_EXTENSION_FLAG_MATERIALS_VOLUME (1 << 11) +#define CGLTF_EXTENSION_FLAG_TEXTURE_BASISU (1 << 12) +#define CGLTF_EXTENSION_FLAG_MATERIALS_EMISSIVE_STRENGTH (1 << 13) +#define CGLTF_EXTENSION_FLAG_MESH_GPU_INSTANCING (1 << 14) +#define CGLTF_EXTENSION_FLAG_MATERIALS_IRIDESCENCE (1 << 15) +#define CGLTF_EXTENSION_FLAG_MATERIALS_ANISOTROPY (1 << 16) +#define CGLTF_EXTENSION_FLAG_MATERIALS_DISPERSION (1 << 17) +#define CGLTF_EXTENSION_FLAG_TEXTURE_WEBP (1 << 18) +#define CGLTF_EXTENSION_FLAG_MATERIALS_DIFFUSE_TRANSMISSION (1 << 19) + +typedef struct { + char* buffer; + cgltf_size buffer_size; + cgltf_size remaining; + char* cursor; + cgltf_size tmp; + cgltf_size chars_written; + const cgltf_data* data; + int depth; + const char* indent; + int needs_comma; + uint32_t extension_flags; + uint32_t required_extension_flags; +} cgltf_write_context; + +#define CGLTF_MIN(a, b) (a < b ? a : b) + +#ifdef FLT_DECIMAL_DIG + // FLT_DECIMAL_DIG is C11 + #define CGLTF_DECIMAL_DIG (FLT_DECIMAL_DIG) +#else + #define CGLTF_DECIMAL_DIG 9 +#endif + +#define CGLTF_SPRINTF(...) { \ + assert(context->cursor || (!context->cursor && context->remaining == 0)); \ + context->tmp = snprintf ( context->cursor, context->remaining, __VA_ARGS__ ); \ + context->chars_written += context->tmp; \ + if (context->cursor) { \ + context->cursor += context->tmp; \ + context->remaining -= context->tmp; \ + } } + +#define CGLTF_SNPRINTF(length, ...) { \ + assert(context->cursor || (!context->cursor && context->remaining == 0)); \ + context->tmp = snprintf ( context->cursor, CGLTF_MIN(length + 1, context->remaining), __VA_ARGS__ ); \ + context->chars_written += length; \ + if (context->cursor) { \ + context->cursor += length; \ + context->remaining -= length; \ + } } + +#define CGLTF_WRITE_IDXPROP(label, val, start) if (val) { \ + cgltf_write_indent(context); \ + CGLTF_SPRINTF("\"%s\": %d", label, (int) (val - start)); \ + context->needs_comma = 1; } + +#define CGLTF_WRITE_IDXARRPROP(label, dim, vals, start) if (vals) { \ + cgltf_write_indent(context); \ + CGLTF_SPRINTF("\"%s\": [", label); \ + for (int i = 0; i < (int)(dim); ++i) { \ + int idx = (int) (vals[i] - start); \ + if (i != 0) CGLTF_SPRINTF(","); \ + CGLTF_SPRINTF(" %d", idx); \ + } \ + CGLTF_SPRINTF(" ]"); \ + context->needs_comma = 1; } + +#define CGLTF_WRITE_TEXTURE_INFO(label, info) if (info.texture) { \ + cgltf_write_line(context, "\"" label "\": {"); \ + CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \ + cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \ + if (info.has_transform) { \ + context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \ + cgltf_write_texture_transform(context, &info.transform); \ + } \ + cgltf_write_line(context, "}"); } + +#define CGLTF_WRITE_NORMAL_TEXTURE_INFO(label, info) if (info.texture) { \ + cgltf_write_line(context, "\"" label "\": {"); \ + CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \ + cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \ + cgltf_write_floatprop(context, "scale", info.scale, 1.0f); \ + if (info.has_transform) { \ + context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \ + cgltf_write_texture_transform(context, &info.transform); \ + } \ + cgltf_write_line(context, "}"); } + +#define CGLTF_WRITE_OCCLUSION_TEXTURE_INFO(label, info) if (info.texture) { \ + cgltf_write_line(context, "\"" label "\": {"); \ + CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \ + cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \ + cgltf_write_floatprop(context, "strength", info.scale, 1.0f); \ + if (info.has_transform) { \ + context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \ + cgltf_write_texture_transform(context, &info.transform); \ + } \ + cgltf_write_line(context, "}"); } + +#ifndef CGLTF_CONSTS +#define GlbHeaderSize 12 +#define GlbChunkHeaderSize 8 +static const uint32_t GlbVersion = 2; +static const uint32_t GlbMagic = 0x46546C67; +static const uint32_t GlbMagicJsonChunk = 0x4E4F534A; +static const uint32_t GlbMagicBinChunk = 0x004E4942; +#define CGLTF_CONSTS +#endif + +static void cgltf_write_indent(cgltf_write_context* context) +{ + if (context->needs_comma) + { + CGLTF_SPRINTF(",\n"); + context->needs_comma = 0; + } + else + { + CGLTF_SPRINTF("\n"); + } + for (int i = 0; i < context->depth; ++i) + { + CGLTF_SPRINTF("%s", context->indent); + } +} + +static void cgltf_write_line(cgltf_write_context* context, const char* line) +{ + if (line[0] == ']' || line[0] == '}') + { + --context->depth; + context->needs_comma = 0; + } + cgltf_write_indent(context); + CGLTF_SPRINTF("%s", line); + cgltf_size last = (cgltf_size)(strlen(line) - 1); + if (line[0] == ']' || line[0] == '}') + { + context->needs_comma = 1; + } + if (line[last] == '[' || line[last] == '{') + { + ++context->depth; + context->needs_comma = 0; + } +} + +static void cgltf_write_strprop(cgltf_write_context* context, const char* label, const char* val) +{ + if (val) + { + cgltf_write_indent(context); + CGLTF_SPRINTF("\"%s\": \"%s\"", label, val); + context->needs_comma = 1; + } +} + +static void cgltf_write_extras(cgltf_write_context* context, const cgltf_extras* extras) +{ + if (extras->data) + { + cgltf_write_indent(context); + CGLTF_SPRINTF("\"extras\": %s", extras->data); + context->needs_comma = 1; + } + else + { + cgltf_size length = extras->end_offset - extras->start_offset; + if (length > 0 && context->data->json) + { + char* json_string = ((char*) context->data->json) + extras->start_offset; + cgltf_write_indent(context); + CGLTF_SPRINTF("%s", "\"extras\": "); + CGLTF_SNPRINTF(length, "%.*s", (int)(extras->end_offset - extras->start_offset), json_string); + context->needs_comma = 1; + } + } +} + +static void cgltf_write_stritem(cgltf_write_context* context, const char* item) +{ + cgltf_write_indent(context); + CGLTF_SPRINTF("\"%s\"", item); + context->needs_comma = 1; +} + +static void cgltf_write_intprop(cgltf_write_context* context, const char* label, int val, int def) +{ + if (val != def) + { + cgltf_write_indent(context); + CGLTF_SPRINTF("\"%s\": %d", label, val); + context->needs_comma = 1; + } +} + +static void cgltf_write_sizeprop(cgltf_write_context* context, const char* label, cgltf_size val, cgltf_size def) +{ + if (val != def) + { + cgltf_write_indent(context); + CGLTF_SPRINTF("\"%s\": %zu", label, val); + context->needs_comma = 1; + } +} + +static void cgltf_write_floatprop(cgltf_write_context* context, const char* label, float val, float def) +{ + if (val != def) + { + cgltf_write_indent(context); + CGLTF_SPRINTF("\"%s\": ", label); + CGLTF_SPRINTF("%.*g", CGLTF_DECIMAL_DIG, val); + context->needs_comma = 1; + + if (context->cursor) + { + char *decimal_comma = strchr(context->cursor - context->tmp, ','); + if (decimal_comma) + { + *decimal_comma = '.'; + } + } + } +} + +static void cgltf_write_boolprop_optional(cgltf_write_context* context, const char* label, bool val, bool def) +{ + if (val != def) + { + cgltf_write_indent(context); + CGLTF_SPRINTF("\"%s\": %s", label, val ? "true" : "false"); + context->needs_comma = 1; + } +} + +static void cgltf_write_floatarrayprop(cgltf_write_context* context, const char* label, const cgltf_float* vals, cgltf_size dim) +{ + cgltf_write_indent(context); + CGLTF_SPRINTF("\"%s\": [", label); + for (cgltf_size i = 0; i < dim; ++i) + { + if (i != 0) + { + CGLTF_SPRINTF(", %.*g", CGLTF_DECIMAL_DIG, vals[i]); + } + else + { + CGLTF_SPRINTF("%.*g", CGLTF_DECIMAL_DIG, vals[i]); + } + } + CGLTF_SPRINTF("]"); + context->needs_comma = 1; +} + +static bool cgltf_check_floatarray(const float* vals, int dim, float val) { + while (dim--) + { + if (vals[dim] != val) + { + return true; + } + } + return false; +} + +static int cgltf_int_from_component_type(cgltf_component_type ctype) +{ + switch (ctype) + { + case cgltf_component_type_r_8: return 5120; + case cgltf_component_type_r_8u: return 5121; + case cgltf_component_type_r_16: return 5122; + case cgltf_component_type_r_16u: return 5123; + case cgltf_component_type_r_32u: return 5125; + case cgltf_component_type_r_32f: return 5126; + default: return 0; + } +} + +static int cgltf_int_from_primitive_type(cgltf_primitive_type ctype) +{ + switch (ctype) + { + case cgltf_primitive_type_points: return 0; + case cgltf_primitive_type_lines: return 1; + case cgltf_primitive_type_line_loop: return 2; + case cgltf_primitive_type_line_strip: return 3; + case cgltf_primitive_type_triangles: return 4; + case cgltf_primitive_type_triangle_strip: return 5; + case cgltf_primitive_type_triangle_fan: return 6; + default: return -1; + } +} + +static const char* cgltf_str_from_alpha_mode(cgltf_alpha_mode alpha_mode) +{ + switch (alpha_mode) + { + case cgltf_alpha_mode_mask: return "MASK"; + case cgltf_alpha_mode_blend: return "BLEND"; + default: return NULL; + } +} + +static const char* cgltf_str_from_type(cgltf_type type) +{ + switch (type) + { + case cgltf_type_scalar: return "SCALAR"; + case cgltf_type_vec2: return "VEC2"; + case cgltf_type_vec3: return "VEC3"; + case cgltf_type_vec4: return "VEC4"; + case cgltf_type_mat2: return "MAT2"; + case cgltf_type_mat3: return "MAT3"; + case cgltf_type_mat4: return "MAT4"; + default: return NULL; + } +} + +static cgltf_size cgltf_dim_from_type(cgltf_type type) +{ + switch (type) + { + case cgltf_type_scalar: return 1; + case cgltf_type_vec2: return 2; + case cgltf_type_vec3: return 3; + case cgltf_type_vec4: return 4; + case cgltf_type_mat2: return 4; + case cgltf_type_mat3: return 9; + case cgltf_type_mat4: return 16; + default: return 0; + } +} + +static const char* cgltf_str_from_camera_type(cgltf_camera_type camera_type) +{ + switch (camera_type) + { + case cgltf_camera_type_perspective: return "perspective"; + case cgltf_camera_type_orthographic: return "orthographic"; + default: return NULL; + } +} + +static const char* cgltf_str_from_light_type(cgltf_light_type light_type) +{ + switch (light_type) + { + case cgltf_light_type_directional: return "directional"; + case cgltf_light_type_point: return "point"; + case cgltf_light_type_spot: return "spot"; + default: return NULL; + } +} + +static void cgltf_write_texture_transform(cgltf_write_context* context, const cgltf_texture_transform* transform) +{ + cgltf_write_line(context, "\"extensions\": {"); + cgltf_write_line(context, "\"KHR_texture_transform\": {"); + if (cgltf_check_floatarray(transform->offset, 2, 0.0f)) + { + cgltf_write_floatarrayprop(context, "offset", transform->offset, 2); + } + cgltf_write_floatprop(context, "rotation", transform->rotation, 0.0f); + if (cgltf_check_floatarray(transform->scale, 2, 1.0f)) + { + cgltf_write_floatarrayprop(context, "scale", transform->scale, 2); + } + if (transform->has_texcoord) + { + cgltf_write_intprop(context, "texCoord", transform->texcoord, -1); + } + cgltf_write_line(context, "}"); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_asset(cgltf_write_context* context, const cgltf_asset* asset) +{ + cgltf_write_line(context, "\"asset\": {"); + cgltf_write_strprop(context, "copyright", asset->copyright); + cgltf_write_strprop(context, "generator", asset->generator); + cgltf_write_strprop(context, "version", asset->version); + cgltf_write_strprop(context, "min_version", asset->min_version); + cgltf_write_extras(context, &asset->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_primitive(cgltf_write_context* context, const cgltf_primitive* prim) +{ + cgltf_write_intprop(context, "mode", cgltf_int_from_primitive_type(prim->type), 4); + CGLTF_WRITE_IDXPROP("indices", prim->indices, context->data->accessors); + CGLTF_WRITE_IDXPROP("material", prim->material, context->data->materials); + cgltf_write_line(context, "\"attributes\": {"); + for (cgltf_size i = 0; i < prim->attributes_count; ++i) + { + const cgltf_attribute* attr = prim->attributes + i; + CGLTF_WRITE_IDXPROP(attr->name, attr->data, context->data->accessors); + } + cgltf_write_line(context, "}"); + + if (prim->targets_count) + { + cgltf_write_line(context, "\"targets\": ["); + for (cgltf_size i = 0; i < prim->targets_count; ++i) + { + cgltf_write_line(context, "{"); + for (cgltf_size j = 0; j < prim->targets[i].attributes_count; ++j) + { + const cgltf_attribute* attr = prim->targets[i].attributes + j; + CGLTF_WRITE_IDXPROP(attr->name, attr->data, context->data->accessors); + } + cgltf_write_line(context, "}"); + } + cgltf_write_line(context, "]"); + } + cgltf_write_extras(context, &prim->extras); + + if (prim->has_draco_mesh_compression || prim->mappings_count > 0) + { + cgltf_write_line(context, "\"extensions\": {"); + + if (prim->has_draco_mesh_compression) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_DRACO_MESH_COMPRESSION; + if (prim->attributes_count == 0 || prim->indices == 0) + { + context->required_extension_flags |= CGLTF_EXTENSION_FLAG_DRACO_MESH_COMPRESSION; + } + + cgltf_write_line(context, "\"KHR_draco_mesh_compression\": {"); + CGLTF_WRITE_IDXPROP("bufferView", prim->draco_mesh_compression.buffer_view, context->data->buffer_views); + cgltf_write_line(context, "\"attributes\": {"); + for (cgltf_size i = 0; i < prim->draco_mesh_compression.attributes_count; ++i) + { + const cgltf_attribute* attr = prim->draco_mesh_compression.attributes + i; + CGLTF_WRITE_IDXPROP(attr->name, attr->data, context->data->accessors); + } + cgltf_write_line(context, "}"); + cgltf_write_line(context, "}"); + } + + if (prim->mappings_count > 0) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_VARIANTS; + cgltf_write_line(context, "\"KHR_materials_variants\": {"); + cgltf_write_line(context, "\"mappings\": ["); + for (cgltf_size i = 0; i < prim->mappings_count; ++i) + { + const cgltf_material_mapping* map = prim->mappings + i; + cgltf_write_line(context, "{"); + CGLTF_WRITE_IDXPROP("material", map->material, context->data->materials); + + cgltf_write_indent(context); + CGLTF_SPRINTF("\"variants\": [%d]", (int)map->variant); + context->needs_comma = 1; + + cgltf_write_extras(context, &map->extras); + cgltf_write_line(context, "}"); + } + cgltf_write_line(context, "]"); + cgltf_write_line(context, "}"); + } + + cgltf_write_line(context, "}"); + } +} + +static void cgltf_write_mesh(cgltf_write_context* context, const cgltf_mesh* mesh) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", mesh->name); + + cgltf_write_line(context, "\"primitives\": ["); + for (cgltf_size i = 0; i < mesh->primitives_count; ++i) + { + cgltf_write_line(context, "{"); + cgltf_write_primitive(context, mesh->primitives + i); + cgltf_write_line(context, "}"); + } + cgltf_write_line(context, "]"); + + if (mesh->weights_count > 0) + { + cgltf_write_floatarrayprop(context, "weights", mesh->weights, mesh->weights_count); + } + + cgltf_write_extras(context, &mesh->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_buffer_view(cgltf_write_context* context, const cgltf_buffer_view* view) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", view->name); + CGLTF_WRITE_IDXPROP("buffer", view->buffer, context->data->buffers); + cgltf_write_sizeprop(context, "byteLength", view->size, (cgltf_size)-1); + cgltf_write_sizeprop(context, "byteOffset", view->offset, 0); + cgltf_write_sizeprop(context, "byteStride", view->stride, 0); + // NOTE: We skip writing "target" because the spec says its usage can be inferred. + cgltf_write_extras(context, &view->extras); + cgltf_write_line(context, "}"); +} + + +static void cgltf_write_buffer(cgltf_write_context* context, const cgltf_buffer* buffer) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", buffer->name); + cgltf_write_strprop(context, "uri", buffer->uri); + cgltf_write_sizeprop(context, "byteLength", buffer->size, (cgltf_size)-1); + cgltf_write_extras(context, &buffer->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_material(cgltf_write_context* context, const cgltf_material* material) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", material->name); + if (material->alpha_mode == cgltf_alpha_mode_mask) + { + cgltf_write_floatprop(context, "alphaCutoff", material->alpha_cutoff, 0.5f); + } + cgltf_write_boolprop_optional(context, "doubleSided", (bool)material->double_sided, false); + // cgltf_write_boolprop_optional(context, "unlit", material->unlit, false); + + if (material->unlit) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_UNLIT; + } + + if (material->has_pbr_specular_glossiness) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_SPECULAR_GLOSSINESS; + } + + if (material->has_clearcoat) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_CLEARCOAT; + } + + if (material->has_transmission) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_TRANSMISSION; + } + + if (material->has_volume) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_VOLUME; + } + + if (material->has_ior) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_IOR; + } + + if (material->has_specular) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_SPECULAR; + } + + if (material->has_sheen) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_SHEEN; + } + + if (material->has_emissive_strength) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_EMISSIVE_STRENGTH; + } + + if (material->has_iridescence) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_IRIDESCENCE; + } + + if (material->has_diffuse_transmission) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_DIFFUSE_TRANSMISSION; + } + + if (material->has_anisotropy) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_ANISOTROPY; + } + + if (material->has_dispersion) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_DISPERSION; + } + + if (material->has_pbr_metallic_roughness) + { + const cgltf_pbr_metallic_roughness* params = &material->pbr_metallic_roughness; + cgltf_write_line(context, "\"pbrMetallicRoughness\": {"); + CGLTF_WRITE_TEXTURE_INFO("baseColorTexture", params->base_color_texture); + CGLTF_WRITE_TEXTURE_INFO("metallicRoughnessTexture", params->metallic_roughness_texture); + cgltf_write_floatprop(context, "metallicFactor", params->metallic_factor, 1.0f); + cgltf_write_floatprop(context, "roughnessFactor", params->roughness_factor, 1.0f); + if (cgltf_check_floatarray(params->base_color_factor, 4, 1.0f)) + { + cgltf_write_floatarrayprop(context, "baseColorFactor", params->base_color_factor, 4); + } + cgltf_write_line(context, "}"); + } + + if (material->unlit || material->has_pbr_specular_glossiness || material->has_clearcoat || material->has_ior || material->has_specular || material->has_transmission || material->has_sheen || material->has_volume || material->has_emissive_strength || material->has_iridescence || material->has_anisotropy || material->has_dispersion || material->has_diffuse_transmission) + { + cgltf_write_line(context, "\"extensions\": {"); + if (material->has_clearcoat) + { + const cgltf_clearcoat* params = &material->clearcoat; + cgltf_write_line(context, "\"KHR_materials_clearcoat\": {"); + CGLTF_WRITE_TEXTURE_INFO("clearcoatTexture", params->clearcoat_texture); + CGLTF_WRITE_TEXTURE_INFO("clearcoatRoughnessTexture", params->clearcoat_roughness_texture); + CGLTF_WRITE_NORMAL_TEXTURE_INFO("clearcoatNormalTexture", params->clearcoat_normal_texture); + cgltf_write_floatprop(context, "clearcoatFactor", params->clearcoat_factor, 0.0f); + cgltf_write_floatprop(context, "clearcoatRoughnessFactor", params->clearcoat_roughness_factor, 0.0f); + cgltf_write_line(context, "}"); + } + if (material->has_ior) + { + const cgltf_ior* params = &material->ior; + cgltf_write_line(context, "\"KHR_materials_ior\": {"); + cgltf_write_floatprop(context, "ior", params->ior, 1.5f); + cgltf_write_line(context, "}"); + } + if (material->has_specular) + { + const cgltf_specular* params = &material->specular; + cgltf_write_line(context, "\"KHR_materials_specular\": {"); + CGLTF_WRITE_TEXTURE_INFO("specularTexture", params->specular_texture); + CGLTF_WRITE_TEXTURE_INFO("specularColorTexture", params->specular_color_texture); + cgltf_write_floatprop(context, "specularFactor", params->specular_factor, 1.0f); + if (cgltf_check_floatarray(params->specular_color_factor, 3, 1.0f)) + { + cgltf_write_floatarrayprop(context, "specularColorFactor", params->specular_color_factor, 3); + } + cgltf_write_line(context, "}"); + } + if (material->has_transmission) + { + const cgltf_transmission* params = &material->transmission; + cgltf_write_line(context, "\"KHR_materials_transmission\": {"); + CGLTF_WRITE_TEXTURE_INFO("transmissionTexture", params->transmission_texture); + cgltf_write_floatprop(context, "transmissionFactor", params->transmission_factor, 0.0f); + cgltf_write_line(context, "}"); + } + if (material->has_volume) + { + const cgltf_volume* params = &material->volume; + cgltf_write_line(context, "\"KHR_materials_volume\": {"); + CGLTF_WRITE_TEXTURE_INFO("thicknessTexture", params->thickness_texture); + cgltf_write_floatprop(context, "thicknessFactor", params->thickness_factor, 0.0f); + if (cgltf_check_floatarray(params->attenuation_color, 3, 1.0f)) + { + cgltf_write_floatarrayprop(context, "attenuationColor", params->attenuation_color, 3); + } + if (params->attenuation_distance < FLT_MAX) + { + cgltf_write_floatprop(context, "attenuationDistance", params->attenuation_distance, FLT_MAX); + } + cgltf_write_line(context, "}"); + } + if (material->has_sheen) + { + const cgltf_sheen* params = &material->sheen; + cgltf_write_line(context, "\"KHR_materials_sheen\": {"); + CGLTF_WRITE_TEXTURE_INFO("sheenColorTexture", params->sheen_color_texture); + CGLTF_WRITE_TEXTURE_INFO("sheenRoughnessTexture", params->sheen_roughness_texture); + if (cgltf_check_floatarray(params->sheen_color_factor, 3, 0.0f)) + { + cgltf_write_floatarrayprop(context, "sheenColorFactor", params->sheen_color_factor, 3); + } + cgltf_write_floatprop(context, "sheenRoughnessFactor", params->sheen_roughness_factor, 0.0f); + cgltf_write_line(context, "}"); + } + if (material->has_pbr_specular_glossiness) + { + const cgltf_pbr_specular_glossiness* params = &material->pbr_specular_glossiness; + cgltf_write_line(context, "\"KHR_materials_pbrSpecularGlossiness\": {"); + CGLTF_WRITE_TEXTURE_INFO("diffuseTexture", params->diffuse_texture); + CGLTF_WRITE_TEXTURE_INFO("specularGlossinessTexture", params->specular_glossiness_texture); + if (cgltf_check_floatarray(params->diffuse_factor, 4, 1.0f)) + { + cgltf_write_floatarrayprop(context, "diffuseFactor", params->diffuse_factor, 4); + } + if (cgltf_check_floatarray(params->specular_factor, 3, 1.0f)) + { + cgltf_write_floatarrayprop(context, "specularFactor", params->specular_factor, 3); + } + cgltf_write_floatprop(context, "glossinessFactor", params->glossiness_factor, 1.0f); + cgltf_write_line(context, "}"); + } + if (material->unlit) + { + cgltf_write_line(context, "\"KHR_materials_unlit\": {}"); + } + if (material->has_emissive_strength) + { + cgltf_write_line(context, "\"KHR_materials_emissive_strength\": {"); + const cgltf_emissive_strength* params = &material->emissive_strength; + cgltf_write_floatprop(context, "emissiveStrength", params->emissive_strength, 1.f); + cgltf_write_line(context, "}"); + } + if (material->has_iridescence) + { + cgltf_write_line(context, "\"KHR_materials_iridescence\": {"); + const cgltf_iridescence* params = &material->iridescence; + cgltf_write_floatprop(context, "iridescenceFactor", params->iridescence_factor, 0.f); + CGLTF_WRITE_TEXTURE_INFO("iridescenceTexture", params->iridescence_texture); + cgltf_write_floatprop(context, "iridescenceIor", params->iridescence_ior, 1.3f); + cgltf_write_floatprop(context, "iridescenceThicknessMinimum", params->iridescence_thickness_min, 100.f); + cgltf_write_floatprop(context, "iridescenceThicknessMaximum", params->iridescence_thickness_max, 400.f); + CGLTF_WRITE_TEXTURE_INFO("iridescenceThicknessTexture", params->iridescence_thickness_texture); + cgltf_write_line(context, "}"); + } + if (material->has_diffuse_transmission) + { + const cgltf_diffuse_transmission* params = &material->diffuse_transmission; + cgltf_write_line(context, "\"KHR_materials_diffuse_transmission\": {"); + CGLTF_WRITE_TEXTURE_INFO("diffuseTransmissionTexture", params->diffuse_transmission_texture); + cgltf_write_floatprop(context, "diffuseTransmissionFactor", params->diffuse_transmission_factor, 0.f); + if (cgltf_check_floatarray(params->diffuse_transmission_color_factor, 3, 1.f)) + { + cgltf_write_floatarrayprop(context, "diffuseTransmissionColorFactor", params->diffuse_transmission_color_factor, 3); + } + CGLTF_WRITE_TEXTURE_INFO("diffuseTransmissionColorTexture", params->diffuse_transmission_color_texture); + cgltf_write_line(context, "}"); + } + if (material->has_anisotropy) + { + cgltf_write_line(context, "\"KHR_materials_anisotropy\": {"); + const cgltf_anisotropy* params = &material->anisotropy; + cgltf_write_floatprop(context, "anisotropyStrength", params->anisotropy_strength, 0.f); + cgltf_write_floatprop(context, "anisotropyRotation", params->anisotropy_rotation, 0.f); + CGLTF_WRITE_TEXTURE_INFO("anisotropyTexture", params->anisotropy_texture); + cgltf_write_line(context, "}"); + } + if (material->has_dispersion) + { + cgltf_write_line(context, "\"KHR_materials_dispersion\": {"); + const cgltf_dispersion* params = &material->dispersion; + cgltf_write_floatprop(context, "dispersion", params->dispersion, 0.f); + cgltf_write_line(context, "}"); + } + cgltf_write_line(context, "}"); + } + + CGLTF_WRITE_NORMAL_TEXTURE_INFO("normalTexture", material->normal_texture); + CGLTF_WRITE_OCCLUSION_TEXTURE_INFO("occlusionTexture", material->occlusion_texture); + CGLTF_WRITE_TEXTURE_INFO("emissiveTexture", material->emissive_texture); + if (cgltf_check_floatarray(material->emissive_factor, 3, 0.0f)) + { + cgltf_write_floatarrayprop(context, "emissiveFactor", material->emissive_factor, 3); + } + cgltf_write_strprop(context, "alphaMode", cgltf_str_from_alpha_mode(material->alpha_mode)); + cgltf_write_extras(context, &material->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_image(cgltf_write_context* context, const cgltf_image* image) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", image->name); + cgltf_write_strprop(context, "uri", image->uri); + CGLTF_WRITE_IDXPROP("bufferView", image->buffer_view, context->data->buffer_views); + cgltf_write_strprop(context, "mimeType", image->mime_type); + cgltf_write_extras(context, &image->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_texture(cgltf_write_context* context, const cgltf_texture* texture) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", texture->name); + CGLTF_WRITE_IDXPROP("source", texture->image, context->data->images); + CGLTF_WRITE_IDXPROP("sampler", texture->sampler, context->data->samplers); + + if (texture->has_basisu || texture->has_webp) + { + cgltf_write_line(context, "\"extensions\": {"); + if (texture->has_basisu) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_BASISU; + cgltf_write_line(context, "\"KHR_texture_basisu\": {"); + CGLTF_WRITE_IDXPROP("source", texture->basisu_image, context->data->images); + cgltf_write_line(context, "}"); + } + if (texture->has_webp) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_WEBP; + cgltf_write_line(context, "\"EXT_texture_webp\": {"); + CGLTF_WRITE_IDXPROP("source", texture->webp_image, context->data->images); + cgltf_write_line(context, "}"); + } + cgltf_write_line(context, "}"); + } + cgltf_write_extras(context, &texture->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_skin(cgltf_write_context* context, const cgltf_skin* skin) +{ + cgltf_write_line(context, "{"); + CGLTF_WRITE_IDXPROP("skeleton", skin->skeleton, context->data->nodes); + CGLTF_WRITE_IDXPROP("inverseBindMatrices", skin->inverse_bind_matrices, context->data->accessors); + CGLTF_WRITE_IDXARRPROP("joints", skin->joints_count, skin->joints, context->data->nodes); + cgltf_write_strprop(context, "name", skin->name); + cgltf_write_extras(context, &skin->extras); + cgltf_write_line(context, "}"); +} + +static const char* cgltf_write_str_path_type(cgltf_animation_path_type path_type) +{ + switch (path_type) + { + case cgltf_animation_path_type_translation: + return "translation"; + case cgltf_animation_path_type_rotation: + return "rotation"; + case cgltf_animation_path_type_scale: + return "scale"; + case cgltf_animation_path_type_weights: + return "weights"; + default: + break; + } + return "invalid"; +} + +static const char* cgltf_write_str_interpolation_type(cgltf_interpolation_type interpolation_type) +{ + switch (interpolation_type) + { + case cgltf_interpolation_type_linear: + return "LINEAR"; + case cgltf_interpolation_type_step: + return "STEP"; + case cgltf_interpolation_type_cubic_spline: + return "CUBICSPLINE"; + default: + break; + } + return "invalid"; +} + +static void cgltf_write_path_type(cgltf_write_context* context, const char *label, cgltf_animation_path_type path_type) +{ + cgltf_write_strprop(context, label, cgltf_write_str_path_type(path_type)); +} + +static void cgltf_write_interpolation_type(cgltf_write_context* context, const char *label, cgltf_interpolation_type interpolation_type) +{ + cgltf_write_strprop(context, label, cgltf_write_str_interpolation_type(interpolation_type)); +} + +static void cgltf_write_animation_sampler(cgltf_write_context* context, const cgltf_animation_sampler* animation_sampler) +{ + cgltf_write_line(context, "{"); + cgltf_write_interpolation_type(context, "interpolation", animation_sampler->interpolation); + CGLTF_WRITE_IDXPROP("input", animation_sampler->input, context->data->accessors); + CGLTF_WRITE_IDXPROP("output", animation_sampler->output, context->data->accessors); + cgltf_write_extras(context, &animation_sampler->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_animation_channel(cgltf_write_context* context, const cgltf_animation* animation, const cgltf_animation_channel* animation_channel) +{ + cgltf_write_line(context, "{"); + CGLTF_WRITE_IDXPROP("sampler", animation_channel->sampler, animation->samplers); + cgltf_write_line(context, "\"target\": {"); + CGLTF_WRITE_IDXPROP("node", animation_channel->target_node, context->data->nodes); + cgltf_write_path_type(context, "path", animation_channel->target_path); + cgltf_write_line(context, "}"); + cgltf_write_extras(context, &animation_channel->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_animation(cgltf_write_context* context, const cgltf_animation* animation) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", animation->name); + + if (animation->samplers_count > 0) + { + cgltf_write_line(context, "\"samplers\": ["); + for (cgltf_size i = 0; i < animation->samplers_count; ++i) + { + cgltf_write_animation_sampler(context, animation->samplers + i); + } + cgltf_write_line(context, "]"); + } + if (animation->channels_count > 0) + { + cgltf_write_line(context, "\"channels\": ["); + for (cgltf_size i = 0; i < animation->channels_count; ++i) + { + cgltf_write_animation_channel(context, animation, animation->channels + i); + } + cgltf_write_line(context, "]"); + } + cgltf_write_extras(context, &animation->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_sampler(cgltf_write_context* context, const cgltf_sampler* sampler) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", sampler->name); + cgltf_write_intprop(context, "magFilter", sampler->mag_filter, 0); + cgltf_write_intprop(context, "minFilter", sampler->min_filter, 0); + cgltf_write_intprop(context, "wrapS", sampler->wrap_s, 10497); + cgltf_write_intprop(context, "wrapT", sampler->wrap_t, 10497); + cgltf_write_extras(context, &sampler->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_node(cgltf_write_context* context, const cgltf_node* node) +{ + cgltf_write_line(context, "{"); + CGLTF_WRITE_IDXARRPROP("children", node->children_count, node->children, context->data->nodes); + CGLTF_WRITE_IDXPROP("mesh", node->mesh, context->data->meshes); + cgltf_write_strprop(context, "name", node->name); + if (node->has_matrix) + { + cgltf_write_floatarrayprop(context, "matrix", node->matrix, 16); + } + if (node->has_translation) + { + cgltf_write_floatarrayprop(context, "translation", node->translation, 3); + } + if (node->has_rotation) + { + cgltf_write_floatarrayprop(context, "rotation", node->rotation, 4); + } + if (node->has_scale) + { + cgltf_write_floatarrayprop(context, "scale", node->scale, 3); + } + if (node->skin) + { + CGLTF_WRITE_IDXPROP("skin", node->skin, context->data->skins); + } + + bool has_extension = node->light || (node->has_mesh_gpu_instancing && node->mesh_gpu_instancing.attributes_count > 0); + if(has_extension) + cgltf_write_line(context, "\"extensions\": {"); + + if (node->light) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_LIGHTS_PUNCTUAL; + cgltf_write_line(context, "\"KHR_lights_punctual\": {"); + CGLTF_WRITE_IDXPROP("light", node->light, context->data->lights); + cgltf_write_line(context, "}"); + } + + if (node->has_mesh_gpu_instancing && node->mesh_gpu_instancing.attributes_count > 0) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MESH_GPU_INSTANCING; + context->required_extension_flags |= CGLTF_EXTENSION_FLAG_MESH_GPU_INSTANCING; + + cgltf_write_line(context, "\"EXT_mesh_gpu_instancing\": {"); + { + cgltf_write_line(context, "\"attributes\": {"); + { + for (cgltf_size i = 0; i < node->mesh_gpu_instancing.attributes_count; ++i) + { + const cgltf_attribute* attr = node->mesh_gpu_instancing.attributes + i; + CGLTF_WRITE_IDXPROP(attr->name, attr->data, context->data->accessors); + } + } + cgltf_write_line(context, "}"); + } + cgltf_write_line(context, "}"); + } + + if (has_extension) + cgltf_write_line(context, "}"); + + if (node->weights_count > 0) + { + cgltf_write_floatarrayprop(context, "weights", node->weights, node->weights_count); + } + + if (node->camera) + { + CGLTF_WRITE_IDXPROP("camera", node->camera, context->data->cameras); + } + + cgltf_write_extras(context, &node->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_scene(cgltf_write_context* context, const cgltf_scene* scene) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", scene->name); + CGLTF_WRITE_IDXARRPROP("nodes", scene->nodes_count, scene->nodes, context->data->nodes); + cgltf_write_extras(context, &scene->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_accessor(cgltf_write_context* context, const cgltf_accessor* accessor) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", accessor->name); + CGLTF_WRITE_IDXPROP("bufferView", accessor->buffer_view, context->data->buffer_views); + cgltf_write_intprop(context, "componentType", cgltf_int_from_component_type(accessor->component_type), 0); + cgltf_write_strprop(context, "type", cgltf_str_from_type(accessor->type)); + cgltf_size dim = cgltf_dim_from_type(accessor->type); + cgltf_write_boolprop_optional(context, "normalized", (bool)accessor->normalized, false); + cgltf_write_sizeprop(context, "byteOffset", (int)accessor->offset, 0); + cgltf_write_intprop(context, "count", (int)accessor->count, -1); + if (accessor->has_min) + { + cgltf_write_floatarrayprop(context, "min", accessor->min, dim); + } + if (accessor->has_max) + { + cgltf_write_floatarrayprop(context, "max", accessor->max, dim); + } + if (accessor->is_sparse) + { + cgltf_write_line(context, "\"sparse\": {"); + cgltf_write_intprop(context, "count", (int)accessor->sparse.count, 0); + cgltf_write_line(context, "\"indices\": {"); + cgltf_write_sizeprop(context, "byteOffset", (int)accessor->sparse.indices_byte_offset, 0); + CGLTF_WRITE_IDXPROP("bufferView", accessor->sparse.indices_buffer_view, context->data->buffer_views); + cgltf_write_intprop(context, "componentType", cgltf_int_from_component_type(accessor->sparse.indices_component_type), 0); + cgltf_write_line(context, "}"); + cgltf_write_line(context, "\"values\": {"); + cgltf_write_sizeprop(context, "byteOffset", (int)accessor->sparse.values_byte_offset, 0); + CGLTF_WRITE_IDXPROP("bufferView", accessor->sparse.values_buffer_view, context->data->buffer_views); + cgltf_write_line(context, "}"); + cgltf_write_line(context, "}"); + } + cgltf_write_extras(context, &accessor->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_camera(cgltf_write_context* context, const cgltf_camera* camera) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "type", cgltf_str_from_camera_type(camera->type)); + if (camera->name) + { + cgltf_write_strprop(context, "name", camera->name); + } + + if (camera->type == cgltf_camera_type_orthographic) + { + cgltf_write_line(context, "\"orthographic\": {"); + cgltf_write_floatprop(context, "xmag", camera->data.orthographic.xmag, -1.0f); + cgltf_write_floatprop(context, "ymag", camera->data.orthographic.ymag, -1.0f); + cgltf_write_floatprop(context, "zfar", camera->data.orthographic.zfar, -1.0f); + cgltf_write_floatprop(context, "znear", camera->data.orthographic.znear, -1.0f); + cgltf_write_extras(context, &camera->data.orthographic.extras); + cgltf_write_line(context, "}"); + } + else if (camera->type == cgltf_camera_type_perspective) + { + cgltf_write_line(context, "\"perspective\": {"); + + if (camera->data.perspective.has_aspect_ratio) { + cgltf_write_floatprop(context, "aspectRatio", camera->data.perspective.aspect_ratio, -1.0f); + } + + cgltf_write_floatprop(context, "yfov", camera->data.perspective.yfov, -1.0f); + + if (camera->data.perspective.has_zfar) { + cgltf_write_floatprop(context, "zfar", camera->data.perspective.zfar, -1.0f); + } + + cgltf_write_floatprop(context, "znear", camera->data.perspective.znear, -1.0f); + cgltf_write_extras(context, &camera->data.perspective.extras); + cgltf_write_line(context, "}"); + } + cgltf_write_extras(context, &camera->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_light(cgltf_write_context* context, const cgltf_light* light) +{ + context->extension_flags |= CGLTF_EXTENSION_FLAG_LIGHTS_PUNCTUAL; + + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "type", cgltf_str_from_light_type(light->type)); + if (light->name) + { + cgltf_write_strprop(context, "name", light->name); + } + if (cgltf_check_floatarray(light->color, 3, 1.0f)) + { + cgltf_write_floatarrayprop(context, "color", light->color, 3); + } + cgltf_write_floatprop(context, "intensity", light->intensity, 1.0f); + cgltf_write_floatprop(context, "range", light->range, 0.0f); + + if (light->type == cgltf_light_type_spot) + { + cgltf_write_line(context, "\"spot\": {"); + cgltf_write_floatprop(context, "innerConeAngle", light->spot_inner_cone_angle, 0.0f); + cgltf_write_floatprop(context, "outerConeAngle", light->spot_outer_cone_angle, 3.14159265358979323846f/4.0f); + cgltf_write_line(context, "}"); + } + cgltf_write_extras( context, &light->extras ); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_variant(cgltf_write_context* context, const cgltf_material_variant* variant) +{ + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_VARIANTS; + + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", variant->name); + cgltf_write_extras(context, &variant->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_glb(FILE* file, const void* json_buf, const cgltf_size json_size, const void* bin_buf, const cgltf_size bin_size) +{ + char header[GlbHeaderSize]; + char chunk_header[GlbChunkHeaderSize]; + char json_pad[3] = { 0x20, 0x20, 0x20 }; + char bin_pad[3] = { 0, 0, 0 }; + + cgltf_size json_padsize = (json_size % 4 != 0) ? 4 - json_size % 4 : 0; + cgltf_size bin_padsize = (bin_size % 4 != 0) ? 4 - bin_size % 4 : 0; + cgltf_size total_size = GlbHeaderSize + GlbChunkHeaderSize + json_size + json_padsize; + if (bin_buf != NULL && bin_size > 0) { + total_size += GlbChunkHeaderSize + bin_size + bin_padsize; + } + + // Write a GLB header + memcpy(header, &GlbMagic, 4); + memcpy(header + 4, &GlbVersion, 4); + memcpy(header + 8, &total_size, 4); + fwrite(header, 1, GlbHeaderSize, file); + + // Write a JSON chunk (header & data) + uint32_t json_chunk_size = (uint32_t)(json_size + json_padsize); + memcpy(chunk_header, &json_chunk_size, 4); + memcpy(chunk_header + 4, &GlbMagicJsonChunk, 4); + fwrite(chunk_header, 1, GlbChunkHeaderSize, file); + + fwrite(json_buf, 1, json_size, file); + fwrite(json_pad, 1, json_padsize, file); + + if (bin_buf != NULL && bin_size > 0) { + // Write a binary chunk (header & data) + uint32_t bin_chunk_size = (uint32_t)(bin_size + bin_padsize); + memcpy(chunk_header, &bin_chunk_size, 4); + memcpy(chunk_header + 4, &GlbMagicBinChunk, 4); + fwrite(chunk_header, 1, GlbChunkHeaderSize, file); + + fwrite(bin_buf, 1, bin_size, file); + fwrite(bin_pad, 1, bin_padsize, file); + } +} + +cgltf_result cgltf_write_file(const cgltf_options* options, const char* path, const cgltf_data* data) +{ + cgltf_size expected = cgltf_write(options, NULL, 0, data); + char* buffer = (char*) malloc(expected); + cgltf_size actual = cgltf_write(options, buffer, expected, data); + if (expected != actual) { + fprintf(stderr, "Error: expected %zu bytes but wrote %zu bytes.\n", expected, actual); + } + FILE* file = fopen(path, "wb"); + if (!file) + { + return cgltf_result_file_not_found; + } + // Note that cgltf_write() includes a null terminator, which we omit from the file content. + if (options->type == cgltf_file_type_glb) { + cgltf_write_glb(file, buffer, actual - 1, data->bin, data->bin_size); + } else { + // Write a plain JSON file. + fwrite(buffer, actual - 1, 1, file); + } + fclose(file); + free(buffer); + return cgltf_result_success; +} + +static void cgltf_write_extensions(cgltf_write_context* context, uint32_t extension_flags) +{ + if (extension_flags & CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM) { + cgltf_write_stritem(context, "KHR_texture_transform"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_UNLIT) { + cgltf_write_stritem(context, "KHR_materials_unlit"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_SPECULAR_GLOSSINESS) { + cgltf_write_stritem(context, "KHR_materials_pbrSpecularGlossiness"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_LIGHTS_PUNCTUAL) { + cgltf_write_stritem(context, "KHR_lights_punctual"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_DRACO_MESH_COMPRESSION) { + cgltf_write_stritem(context, "KHR_draco_mesh_compression"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_CLEARCOAT) { + cgltf_write_stritem(context, "KHR_materials_clearcoat"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_IOR) { + cgltf_write_stritem(context, "KHR_materials_ior"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_SPECULAR) { + cgltf_write_stritem(context, "KHR_materials_specular"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_TRANSMISSION) { + cgltf_write_stritem(context, "KHR_materials_transmission"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_SHEEN) { + cgltf_write_stritem(context, "KHR_materials_sheen"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_VARIANTS) { + cgltf_write_stritem(context, "KHR_materials_variants"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_VOLUME) { + cgltf_write_stritem(context, "KHR_materials_volume"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_TEXTURE_BASISU) { + cgltf_write_stritem(context, "KHR_texture_basisu"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_TEXTURE_WEBP) { + cgltf_write_stritem(context, "EXT_texture_webp"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_EMISSIVE_STRENGTH) { + cgltf_write_stritem(context, "KHR_materials_emissive_strength"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_IRIDESCENCE) { + cgltf_write_stritem(context, "KHR_materials_iridescence"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_DIFFUSE_TRANSMISSION) { + cgltf_write_stritem(context, "KHR_materials_diffuse_transmission"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_ANISOTROPY) { + cgltf_write_stritem(context, "KHR_materials_anisotropy"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MESH_GPU_INSTANCING) { + cgltf_write_stritem(context, "EXT_mesh_gpu_instancing"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_DISPERSION) { + cgltf_write_stritem(context, "KHR_materials_dispersion"); + } +} + +cgltf_size cgltf_write(const cgltf_options* options, char* buffer, cgltf_size size, const cgltf_data* data) +{ + (void)options; + cgltf_write_context ctx; + ctx.buffer = buffer; + ctx.buffer_size = size; + ctx.remaining = size; + ctx.cursor = buffer; + ctx.chars_written = 0; + ctx.data = data; + ctx.depth = 1; + ctx.indent = " "; + ctx.needs_comma = 0; + ctx.extension_flags = 0; + ctx.required_extension_flags = 0; + + cgltf_write_context* context = &ctx; + + CGLTF_SPRINTF("{"); + + if (data->accessors_count > 0) + { + cgltf_write_line(context, "\"accessors\": ["); + for (cgltf_size i = 0; i < data->accessors_count; ++i) + { + cgltf_write_accessor(context, data->accessors + i); + } + cgltf_write_line(context, "]"); + } + + cgltf_write_asset(context, &data->asset); + + if (data->buffer_views_count > 0) + { + cgltf_write_line(context, "\"bufferViews\": ["); + for (cgltf_size i = 0; i < data->buffer_views_count; ++i) + { + cgltf_write_buffer_view(context, data->buffer_views + i); + } + cgltf_write_line(context, "]"); + } + + if (data->buffers_count > 0) + { + cgltf_write_line(context, "\"buffers\": ["); + for (cgltf_size i = 0; i < data->buffers_count; ++i) + { + cgltf_write_buffer(context, data->buffers + i); + } + cgltf_write_line(context, "]"); + } + + if (data->images_count > 0) + { + cgltf_write_line(context, "\"images\": ["); + for (cgltf_size i = 0; i < data->images_count; ++i) + { + cgltf_write_image(context, data->images + i); + } + cgltf_write_line(context, "]"); + } + + if (data->meshes_count > 0) + { + cgltf_write_line(context, "\"meshes\": ["); + for (cgltf_size i = 0; i < data->meshes_count; ++i) + { + cgltf_write_mesh(context, data->meshes + i); + } + cgltf_write_line(context, "]"); + } + + if (data->materials_count > 0) + { + cgltf_write_line(context, "\"materials\": ["); + for (cgltf_size i = 0; i < data->materials_count; ++i) + { + cgltf_write_material(context, data->materials + i); + } + cgltf_write_line(context, "]"); + } + + if (data->nodes_count > 0) + { + cgltf_write_line(context, "\"nodes\": ["); + for (cgltf_size i = 0; i < data->nodes_count; ++i) + { + cgltf_write_node(context, data->nodes + i); + } + cgltf_write_line(context, "]"); + } + + if (data->samplers_count > 0) + { + cgltf_write_line(context, "\"samplers\": ["); + for (cgltf_size i = 0; i < data->samplers_count; ++i) + { + cgltf_write_sampler(context, data->samplers + i); + } + cgltf_write_line(context, "]"); + } + + CGLTF_WRITE_IDXPROP("scene", data->scene, data->scenes); + + if (data->scenes_count > 0) + { + cgltf_write_line(context, "\"scenes\": ["); + for (cgltf_size i = 0; i < data->scenes_count; ++i) + { + cgltf_write_scene(context, data->scenes + i); + } + cgltf_write_line(context, "]"); + } + + if (data->textures_count > 0) + { + cgltf_write_line(context, "\"textures\": ["); + for (cgltf_size i = 0; i < data->textures_count; ++i) + { + cgltf_write_texture(context, data->textures + i); + } + cgltf_write_line(context, "]"); + } + + if (data->skins_count > 0) + { + cgltf_write_line(context, "\"skins\": ["); + for (cgltf_size i = 0; i < data->skins_count; ++i) + { + cgltf_write_skin(context, data->skins + i); + } + cgltf_write_line(context, "]"); + } + + if (data->animations_count > 0) + { + cgltf_write_line(context, "\"animations\": ["); + for (cgltf_size i = 0; i < data->animations_count; ++i) + { + cgltf_write_animation(context, data->animations + i); + } + cgltf_write_line(context, "]"); + } + + if (data->cameras_count > 0) + { + cgltf_write_line(context, "\"cameras\": ["); + for (cgltf_size i = 0; i < data->cameras_count; ++i) + { + cgltf_write_camera(context, data->cameras + i); + } + cgltf_write_line(context, "]"); + } + + if (data->lights_count > 0 || data->variants_count > 0) + { + cgltf_write_line(context, "\"extensions\": {"); + + if (data->lights_count > 0) + { + cgltf_write_line(context, "\"KHR_lights_punctual\": {"); + cgltf_write_line(context, "\"lights\": ["); + for (cgltf_size i = 0; i < data->lights_count; ++i) + { + cgltf_write_light(context, data->lights + i); + } + cgltf_write_line(context, "]"); + cgltf_write_line(context, "}"); + } + + if (data->variants_count) + { + cgltf_write_line(context, "\"KHR_materials_variants\": {"); + cgltf_write_line(context, "\"variants\": ["); + for (cgltf_size i = 0; i < data->variants_count; ++i) + { + cgltf_write_variant(context, data->variants + i); + } + cgltf_write_line(context, "]"); + cgltf_write_line(context, "}"); + } + + cgltf_write_line(context, "}"); + } + + if (context->extension_flags != 0) + { + cgltf_write_line(context, "\"extensionsUsed\": ["); + cgltf_write_extensions(context, context->extension_flags); + cgltf_write_line(context, "]"); + } + + if (context->required_extension_flags != 0) + { + cgltf_write_line(context, "\"extensionsRequired\": ["); + cgltf_write_extensions(context, context->required_extension_flags); + cgltf_write_line(context, "]"); + } + + cgltf_write_extras(context, &data->extras); + + CGLTF_SPRINTF("\n}\n"); + + // snprintf does not include the null terminator in its return value, so be sure to include it + // in the returned byte count. + return 1 + ctx.chars_written; +} + +#endif /* #ifdef CGLTF_WRITE_IMPLEMENTATION */ + +/* cgltf is distributed under MIT license: + * + * Copyright (c) 2019-2021 Philip Rideout + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ From 8d70f1007b5c34be0967ff091067c52962861f1c Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Mar 2026 21:24:33 +0200 Subject: [PATCH 244/319] Update CHANGELOG --- CHANGELOG | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index cf9bf238d..cc44aac2c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -759,6 +759,7 @@ Detailed changes: [rexm] REVIEWED: `Makefile.Web` before trying to rebuild new example for web by @raysan5 [rexm] REVIEWED: Using `Makefile.Web` for specific web versions generation by @raysan5 +[external] ADDED: `cgltf_write` 1.15, to support glTF models export in the future, by @raysan5 [external] RENAMED: `rl_gputex.h` to `rltexgpu.h`, compressed textures loading [external] REVIEWED: `rltexgpu.h`, make it usable standalone by @raysan5 [external] REVIEWED: `rltexgpu.h, fix the swizzling in `rl_load_dds_from_memory()` (#5422) by @msmith-codes @@ -767,16 +768,23 @@ Detailed changes: [external] REVIEWED: `sdefl` and `sinfl` issues (#5367) by @raysan5 [external] REVIEWED: `sinfl_bsr()`, improvements by @RicoP [external] REVIEWED: `stb_truetype`, fix composite glyph scaling logic (#4811) by @ashishbhattarai +[external] UPDATED: `raygui` to 5.0-dev for examples by @raysan5 [external] UPDATED: dr_libs (#5020) by @Emil2010 -[external] UPDATED: miniaudio to v0.11.22 (#4983) by @M374LX -[external] UPDATED: miniaudio to v0.11.23 (#5234) by @pyrokn8 -[external] UPDATED: miniaudio to v0.11.24 (#5506) by @vdemcak -[external] UPDATED: raygui to 5.0-dev for examples by @raysan5 -[external] UPDATED: RGFW to 1.5 (#4688) by @ColleagueRiley -[external] UPDATED: RGFW to 1.6 (#4795) by @colleagueRiley -[external] UPDATED: RGFW to 1.7 (#4965) by @M374LX -[external] UPDATED: RGFW to 1.7.5-dev (#4976) by @M374LX -[external] UPDATED: RGFW to 2.0.0 (#5582) by @CrackedPixel +[external] UPDATED: `miniaudio` to v0.11.22 (#4983) by @M374LX +[external] UPDATED: `miniaudio` to v0.11.23 (#5234) by @pyrokn8 +[external] UPDATED: `miniaudio` to v0.11.24 (#5506) by @vdemcak +[external] UPDATED: `RGFW` to 1.5 (#4688) by @ColleagueRiley +[external] UPDATED: `RGFW` to 1.6 (#4795) by @colleagueRiley +[external] UPDATED: `RGFW` to 1.7 (#4965) by @M374LX +[external] UPDATED: `RGFW` to 1.7.5-dev (#4976) by @M374LX +[external] UPDATED: `RGFW` to 2.0.0 (#5582) by @CrackedPixel +[external] UPDATED: `cgltf` 1.14 to 1.15 by @raysan5 +[external] UPDATED: `dr_flac` v0.13.0 to v0.13.3 by @raysan5 +[external] UPDATED: `dr_mp3` v0.7.0 to v0.7.4 by @raysan5 +[external] UPDATED: `m3d` to latest master by @raysan5 +[external] UPDATED: `qoi` to latest master by @raysan5 +[external] UPDATED: `qoa` to latest master by @raysan5 +[external] UPDATED: `stb_image_resize2` v2.12 to v2.18 by @raysan5 [misc] ADDED: SECURITY.md for security reporting policies by @raysan5 [misc] ADDED: `examples/examples_list`, to be used by `rexm` or other tools by @raysan5 From 04f81538b7d458569d280dc1e10e517494df9fa4 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Mar 2026 21:37:01 +0200 Subject: [PATCH 245/319] ADDED: Some sample code to export gltf/glb meshes -WIP- --- src/rmodels.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/rmodels.c b/src/rmodels.c index 6c1e0482e..3fff05eaa 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -10,6 +10,7 @@ * #define SUPPORT_FILEFORMAT_MTL * #define SUPPORT_FILEFORMAT_IQM * #define SUPPORT_FILEFORMAT_GLTF +* #define SUPPORT_FILEFORMAT_GLTF_WRITE * #define SUPPORT_FILEFORMAT_VOX * #define SUPPORT_FILEFORMAT_M3D * Selected desired fileformats to be supported for model data loading @@ -71,6 +72,11 @@ #define CGLTF_IMPLEMENTATION #include "external/cgltf.h" // glTF file format loading #endif +#if SUPPORT_FILEFORMAT_GLTF_WRITE + // NOTE: No need for custom allocators, memory buffer provided + #define CGLTF_WRITE_IMPLEMENTATION + #include "external/cgltf_write.h" // glTF file format writing +#endif #if SUPPORT_FILEFORMAT_VOX #define VOX_MALLOC RL_MALLOC @@ -2017,6 +2023,19 @@ bool ExportMesh(Mesh mesh, const char *fileName) RL_FREE(txtData); } + else if (IsFileExtension(fileName, ".gltf")) // Or .glb + { + // TODO: Implement gltf/glb support + /* + cgltf_size expected = cgltf_write(options, NULL, 0, data); + char *buffer = (char *)RL_CALLOC(expected, 0); + cgltf_size actual = cgltf_write(options, buffer, expected, data); + + // NOTE: cgltf_write() includes a NULL terminator that should be ommited in case of a .glb + if (options->type == cgltf_file_type_glb) cgltf_write_glb(file, buffer, actual - 1, data->bin, data->bin_size); + else SaveFileText(fileName, buffer); // Write a plain JSON file + */ + } else if (IsFileExtension(fileName, ".raw")) { // TODO: Support additional file formats to export mesh vertex data From 8743e11285c5d1dae3f64d36f799deac01898977 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Mar 2026 21:40:41 +0200 Subject: [PATCH 246/319] Update rmodels.c --- src/rmodels.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index 3fff05eaa..3dd28c6c1 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5284,7 +5284,7 @@ static cgltf_result LoadFileGLTFCallback(const struct cgltf_memory_options *memo } // Release file data callback for cgltf -static void ReleaseFileGLTFCallback(const struct cgltf_memory_options *memoryOptions, const struct cgltf_file_options *fileOptions, void *data) +static void ReleaseFileGLTFCallback(const struct cgltf_memory_options *memoryOptions, const struct cgltf_file_options *fileOptions, void *data, cgltf_size size) { UnloadFileData((unsigned char *)data); } From b3bf537fab25f9395f42281e8e935b77b820f735 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 30 Mar 2026 11:00:26 +0200 Subject: [PATCH 247/319] Update rexm.c --- tools/rexm/rexm.c | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index b160a9be1..d47a207e3 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -68,6 +68,7 @@ #define REXM_MAX_BUFFER_SIZE (2*1024*1024) // 2MB #define REXM_MAX_RESOURCE_PATHS 256 +#define REXM_MAX_RESOURCE_PATH_LENGTH 256 // Create local commit with changes on example renaming //#define RENAME_AUTO_COMMIT_CREATION @@ -2493,25 +2494,21 @@ static void SortExampleByName(rlExampleInfo *items, int count) qsort(items, count, sizeof(rlExampleInfo), rlExampleInfoCompare); } -// 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 -// TODO: WARNING: Some resources could require linked resources: .fnt --> .png, .mtl --> .png, .gltf --> .png, ... -static char **LoadExampleResourcePaths(const char *filePath, int *resPathCount) +// Scan asset paths from a source code file (raylib) +// WARNING: Supported asset file extensions are hardcoded by used file types +// but new examples could require other file extensions to be added +static char **LoadExampleResourcePaths(const char *srcFilePath, int *resPathCount) { - #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(REXM_MAX_RESOURCE_PATH_LEN, sizeof(char)); + for (int i = 0; i < REXM_MAX_RESOURCE_PATHS; i++) paths[i] = (char *)RL_CALLOC(REXM_MAX_RESOURCE_PATH_LENGTH, sizeof(char)); int resCounter = 0; - char *code = LoadFileText(filePath); + char *code = LoadFileText(srcFilePath); 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", ".obj", ".iqm", ".glb", ".m3d", ".vox", ".vs", ".fs", ".txt" }; + const char *exts[] = { ".png", ".bmp", ".jpg", ".qoi", ".gif", ".raw", ".hdr", ".ttf", ".fnt", ".wav", ".ogg", ".mp3", ".flac", ".mod", ".xm", ".qoa", ".obj", ".iqm", ".glb", ".m3d", ".vox", ".vs", ".fs", ".txt" }; const int extCount = sizeof(exts)/sizeof(char *); char *ptr = code; @@ -2537,9 +2534,9 @@ static char **LoadExampleResourcePaths(const char *filePath, int *resPathCount) !((functionIndex05 != -1) && (functionIndex05 < 40))) // Not found SaveFileText() before "" { int len = (int)(end - start); - if ((len > 0) && (len < REXM_MAX_RESOURCE_PATH_LEN)) + if ((len > 0) && (len < REXM_MAX_RESOURCE_PATH_LENGTH)) { - char buffer[REXM_MAX_RESOURCE_PATH_LEN] = { 0 }; + char buffer[REXM_MAX_RESOURCE_PATH_LENGTH] = { 0 }; strncpy(buffer, start, len); buffer[len] = '\0'; @@ -2575,6 +2572,29 @@ static char **LoadExampleResourcePaths(const char *filePath, int *resPathCount) UnloadFileText(code); } + /* + // Some resources could require linked resources: .fnt --> .png, .mtl --> .png, .gltf --> .png + // So doing a recursive pass to scan possible files with second resources + int currentAssetCounter = resCounter; + for (int i = 0; i < currentAssetCounter; i++) + { + if (IsFileExtension(paths[i], ".fnt;.gltf")) + { + int assetCount2 = 0; + // ERROR: srcFilePath changes on rcursive call and TextFormat() static arrays are oveerride + char **assetPaths2 = LoadExampleResourcePaths(TextFormat("%s/%s", GetDirectoryPath(srcFilePath), paths[i]), &assetCount2); + + for (int j = 0; j < assetCount2; j++) + { + strcpy(paths[resCounter], TextFormat("%s/%s", GetDirectoryPath(paths[i]) + 2, assetPaths2[j])); + resCounter++; + } + + UnloadExampleResourcePaths(assetPaths2); + } + } + */ + *resPathCount = resCounter; return paths; } From 63beefd0de4ac59fdd1d20a0314166a0173a7389 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 30 Mar 2026 11:00:29 +0200 Subject: [PATCH 248/319] Update Makefile.Web --- examples/Makefile.Web | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 68c34f31b..709072be1 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -1153,7 +1153,10 @@ 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) + $(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) \ From c7df641aa25d40b1998d28cd38876e09f738b656 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 30 Mar 2026 11:00:39 +0200 Subject: [PATCH 249/319] Update text_font_loading.c --- examples/text/text_font_loading.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/text/text_font_loading.c b/examples/text/text_font_loading.c index 151b63d45..1bd3d26a2 100644 --- a/examples/text/text_font_loading.c +++ b/examples/text/text_font_loading.c @@ -38,12 +38,12 @@ int main(void) // Define characters to draw // NOTE: raylib supports UTF-8 encoding, following list is actually codified as UTF8 internally - const char msg[256] = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHI\nJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmn\nopqrstuvwxyz{|}~¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓ\nÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷\nøùúûüýþÿ"; + const char msg[256] = "!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHI\nJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmn\nopqrstuvwxyz{|}~¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓ\nÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷\nøùúûüýþÿ"; // NOTE: Textures/Fonts MUST be loaded after Window initialization (OpenGL context is required) // BMFont (AngelCode) : Font data and image atlas have been generated using external program - Font fontBm = LoadFont("resources/pixantiqua.fnt"); + Font fontBm = LoadFont("resources/pixantiqua.fnt"); // Requires "resources/pixantiqua.png" // TTF font : Font data and atlas are generated directly from TTF // NOTE: We define a font base size of 32 pixels tall and up-to 250 characters From a6dc9f9e9251818bc15c556986e2f2e8d6a7cb20 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 30 Mar 2026 20:16:40 +0200 Subject: [PATCH 250/319] Update LICENSE.md --- examples/models/resources/LICENSE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/models/resources/LICENSE.md b/examples/models/resources/LICENSE.md index d7f85c0f4..1feb770ab 100644 --- a/examples/models/resources/LICENSE.md +++ b/examples/models/resources/LICENSE.md @@ -13,9 +13,9 @@ | models/vox/chr_knight.vox | ❔ | ❔ | - | | models/vox/chr_sword.vox | ❔ | ❔ | - | | models/vox/monu9.vox | ❔ | ❔ | - | -| billboard.png | [@emegeme](https://github.com/emegeme) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | - | +| billboard.png | [@raysan5](https://github.com/raysan5) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | - | | cubicmap.png | [@raysan5](https://github.com/raysan5) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | - | -| cubicmap_atlas.png | [@emegeme](https://github.com/emegeme) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | - | +| cubicmap_atlas.png | [@raysan5](https://github.com/raysan5) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | - | | heightmap.png | [@raysan5](https://github.com/raysan5) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | - | | dresden_square_1k.hdr | [HDRIHaven](https://hdrihaven.com/hdri/?h=dresden_square) | [CC0](https://hdrihaven.com/p/license.php) | - | | dresden_square_2k.hdr | [HDRIHaven](https://hdrihaven.com/hdri/?h=dresden_square) | [CC0](https://hdrihaven.com/p/license.php) | - | From 78797757f04f6b76f7a340ecbbd75cfa63ab5762 Mon Sep 17 00:00:00 2001 From: Green3492 <57958064+r3g492@users.noreply.github.com> Date: Tue, 31 Mar 2026 23:45:54 +0900 Subject: [PATCH 251/319] store bytes at global data seg (#5708) --- examples/models/models_decals.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/models/models_decals.c b/examples/models/models_decals.c index 6e80283f2..2956d3c7a 100644 --- a/examples/models/models_decals.c +++ b/examples/models/models_decals.c @@ -108,7 +108,7 @@ int main(void) decalMaterial.maps[MATERIAL_MAP_DIFFUSE].color = RAYWHITE; bool showModel = true; - Model decalModels[MAX_DECALS] = { 0 }; + static Model decalModels[MAX_DECALS] = { 0 }; int decalCount = 0; SetTargetFPS(60); // Set our game to run at 60 frames-per-second From 4ca9170f5b03f6fddd1f40494ddbcd8d27646795 Mon Sep 17 00:00:00 2001 From: Krzysztof Szenk Date: Wed, 1 Apr 2026 09:46:14 +0200 Subject: [PATCH 252/319] [rcore_rgfw] Updating render resolution as well on SetWindowSize (#5709) * [rcore_rgfw] Updating render resolution as well on SetWindowSize * Actually, maybe even better call SetupViewport() --- src/platforms/rcore_desktop_rgfw.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 51a73e1ad..11f268d9f 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -821,6 +821,11 @@ void SetWindowSize(int width, int height) CORE.Window.screen.height = height; } + if (!CORE.Window.usingFbo) + { + SetupViewport(CORE.Window.screen.width, CORE.Window.screen.height); + } + RGFW_window_resize(platform.window, CORE.Window.screen.width, CORE.Window.screen.height); } From 449182bb517b7d62e9b726f650cb281df0e4a7c5 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 1 Apr 2026 10:17:34 +0200 Subject: [PATCH 253/319] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0bc503cd6..00929215c 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ features - Written in plain C code (C99) using PascalCase/camelCase notation - Hardware accelerated with OpenGL: **1.1, 2.1, 3.3, 4.3, ES 2.0, ES 3.0** - **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.h) + - **Software Renderer** backend (no OpenGL required!): [rlsw](https://github.com/raysan5/raylib/blob/master/src/external/rlsw.h) - Multiple **Fonts** formats supported (TTF, OTF, FNT, BDF, sprite fonts) - Multiple texture formats supported, including **compressed formats** (DXT, ETC, ASTC) - **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more! @@ -61,7 +62,7 @@ This is a basic raylib example, it creates a window and draws the text `"Congrat int main(void) { - InitWindow(800, 450, "raylib [core] example - basic window"); + InitWindow(800, 450, "raylib example - basic window"); while (!WindowShouldClose()) { From eb4f1a6da0b443db9eb2e2cead3befbbbf762264 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 1 Apr 2026 10:17:37 +0200 Subject: [PATCH 254/319] Update ROADMAP.md --- ROADMAP.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ROADMAP.md b/ROADMAP.md index 91d2299c3..87cbb3fc8 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -6,6 +6,7 @@ Here is a wishlist with features and ideas to improve the library. Note that fea - [GitHub PRs](https://github.com/raysan5/raylib/pulls) open with improvements to be reviewed. - [raylib source code](https://github.com/raysan5/raylib/tree/master/src) has multiple *TODO* comments around code with pending things to review or improve. - raylib wishlists discussions are open to everyone to ask for improvements, feel free to check and comment: + - [raylib 7.0 wishlist](https://github.com/raysan5/raylib/discussions/5710) - [raylib 6.0 wishlist](https://github.com/raysan5/raylib/discussions/4660) - [raylib 5.0 wishlist](https://github.com/raysan5/raylib/discussions/2952) - [raylib wishlist 2022](https://github.com/raysan5/raylib/discussions/2272) @@ -21,7 +22,7 @@ _Current version of raylib is complete and functional but there is always room f - [ ] `rcore_desktop_wayland`: Create additional platform backend: Linux/Wayland - [ ] `rcore`: Investigate alternative embedded platforms and realtime OSs - [ ] `rlsw`: Software renderer optimizations: mipmaps, platform-specific SIMD - - [ ] `rtextures`: Consider moving N-patch system to separate example + - [ ] `rtextures`: Consider removing N-patch system, provide as separate example - [ ] `rtextures`: Review blending modes system, provide more options or better samples - [ ] `rtext`: Investigate the recently opened [`Slug`](https://sluglibrary.com/) font rendering algorithm - [ ] `raudio`: Support microphone input, basic API to read microphone From 4799cab7727cd95796ebc77730d1fb65e52f90bc Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 1 Apr 2026 10:17:41 +0200 Subject: [PATCH 255/319] Update rexm.c --- tools/rexm/rexm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index d47a207e3..841ccbce8 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -2573,7 +2573,7 @@ static char **LoadExampleResourcePaths(const char *srcFilePath, int *resPathCoun } /* - // Some resources could require linked resources: .fnt --> .png, .mtl --> .png, .gltf --> .png + // WARNING: Some resources could require linked resources: .fnt --> .png, .mtl --> .png, .gltf --> .png // So doing a recursive pass to scan possible files with second resources int currentAssetCounter = resCounter; for (int i = 0; i < currentAssetCounter; i++) @@ -2581,7 +2581,7 @@ static char **LoadExampleResourcePaths(const char *srcFilePath, int *resPathCoun if (IsFileExtension(paths[i], ".fnt;.gltf")) { int assetCount2 = 0; - // ERROR: srcFilePath changes on rcursive call and TextFormat() static arrays are oveerride + // ERROR: srcFilePath changes on rcursive call and TextFormat() static arrays are override char **assetPaths2 = LoadExampleResourcePaths(TextFormat("%s/%s", GetDirectoryPath(srcFilePath), paths[i]), &assetCount2); for (int j = 0; j < assetCount2; j++) From 12140cd444d03591c36b0fdd6dec3a7173e5edb3 Mon Sep 17 00:00:00 2001 From: Green3492 <57958064+r3g492@users.noreply.github.com> Date: Wed, 1 Apr 2026 21:48:49 +0900 Subject: [PATCH 256/319] explicitly state TEXTURE_WRAP_REPEAT for web build (#5711) --- examples/shaders/shaders_texture_tiling.c | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/shaders/shaders_texture_tiling.c b/examples/shaders/shaders_texture_tiling.c index 14e15b8b2..4f6b26d71 100644 --- a/examples/shaders/shaders_texture_tiling.c +++ b/examples/shaders/shaders_texture_tiling.c @@ -56,6 +56,7 @@ int main(void) // Set the texture tiling using a shader float tiling[2] = { 3.0f, 3.0f }; Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/tiling.fs", GLSL_VERSION)); + SetTextureWrap(texture, TEXTURE_WRAP_REPEAT); SetShaderValue(shader, GetShaderLocation(shader, "tiling"), tiling, SHADER_UNIFORM_VEC2); model.materials[0].shader = shader; From 5c2dee086282397ecaea9cbe46b1077704f946d2 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 1 Apr 2026 23:23:25 +0200 Subject: [PATCH 257/319] REVIEWED: `LoadDirectoryFiles*()` comments --- src/raylib.h | 4 ++-- src/rcore.c | 26 +++++++++++++++----------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index ac5bc414e..c6aad860a 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1161,8 +1161,8 @@ RLAPI int MakeDirectory(const char *dirPath); // Create di RLAPI bool ChangeDirectory(const char *dirPath); // Change working directory, return true on success RLAPI bool IsPathFile(const char *path); // Check if a given path is a file or a directory RLAPI bool IsFileNameValid(const char *fileName); // Check if fileName is valid for the platform/OS -RLAPI FilePathList LoadDirectoryFiles(const char *dirPath); // Load directory filepaths -RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result +RLAPI FilePathList LoadDirectoryFiles(const char *dirPath); // Load directory filepaths, files and directories, no subdirs scan +RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and subdir scan; some filters available: "*.*", "FILES*", "DIRS*" RLAPI void UnloadDirectoryFiles(FilePathList files); // Unload filepaths RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window RLAPI FilePathList LoadDroppedFiles(void); // Load dropped filepaths diff --git a/src/rcore.c b/src/rcore.c index 1f6699af9..f93e25573 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -267,15 +267,18 @@ #define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record #endif +// File and directory scan filters +// NOTE: Used in ScanDirectoryFiles(), LoadDirectoryFilesEx() and GetDirectoryFileCountEx() +// WARNING: Custom file filters can be specified but following raylib IsFileExtension() convention: ".png;.wav;.glb" #ifndef FILE_FILTER_TAG_ALL - #define FILE_FILTER_TAG_ALL "*.*" // Filter to include all file types and directories on directory scan -#endif // NOTE: Used in ScanDirectoryFiles(), LoadDirectoryFilesEx() and GetDirectoryFileCountEx() + #define FILE_FILTER_TAG_ALL "*.*" // Filter to include all file types and directories on scan +#endif #ifndef FILE_FILTER_TAG_FILE_ONLY - #define FILE_FILTER_TAG_FILE_ONLY "FILES*" // Filter to include all file types on directory scan -#endif // NOTE: Used in ScanDirectoryFiles(), LoadDirectoryFilesEx() and GetDirectoryFileCountEx() + #define FILE_FILTER_TAG_FILE_ONLY "FILES*" // Filter to include all file types on scan (no directories) +#endif #ifndef FILE_FILTER_TAG_DIR_ONLY - #define FILE_FILTER_TAG_DIR_ONLY "DIR*" // Filter to include directories on directory scan -#endif // NOTE: Used in ScanDirectoryFiles(), LoadDirectoryFilesEx() and GetDirectoryFileCountEx() + #define FILE_FILTER_TAG_DIR_ONLY "DIRS*" // Filter to include only directories on scan +#endif // Flags bitwise operation macros #define FLAG_SET(n, f) ((n) |= (f)) @@ -2720,17 +2723,18 @@ const char *GetApplicationDirectory(void) // Load directory filepaths // NOTE: Base path is prepended to the scanned filepaths -// WARNING: Directory is scanned twice, first time to get files count -// No recursive scanning is done! +// WARNING: Directory is scanned twice, first time to get paths count +// Scanneed files and directories, no recursive/subdirs scanning FilePathList LoadDirectoryFiles(const char *dirPath) { return LoadDirectoryFilesEx(dirPath, FILE_FILTER_TAG_ALL, false); } // Load directory filepaths with extension filtering and recursive directory scan -// Use 'DIR*' to include directories on directory scan -// Use '*.*' to include all file types and directories on directory scan -// WARNING: Directory is scanned twice, first time to get files count +// Use "*.*" to include all files and directories on scan +// Use "FILES*" to include only files on scan +// Use "DIRS*" to include only directories on scan +// WARNING: Directory is scanned twice, first time to get paths count FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs) { FilePathList files = { 0 }; From f36533cbd8feead845a2e0b4568ec0484a15cd10 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 21:23:44 +0000 Subject: [PATCH 258/319] rlparser: update raylib_api.* by CI --- tools/rlparser/output/raylib_api.json | 4 ++-- tools/rlparser/output/raylib_api.lua | 4 ++-- tools/rlparser/output/raylib_api.txt | 4 ++-- tools/rlparser/output/raylib_api.xml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/rlparser/output/raylib_api.json b/tools/rlparser/output/raylib_api.json index 1fbc87a9d..ed0e220d7 100644 --- a/tools/rlparser/output/raylib_api.json +++ b/tools/rlparser/output/raylib_api.json @@ -4685,7 +4685,7 @@ }, { "name": "LoadDirectoryFiles", - "description": "Load directory filepaths", + "description": "Load directory filepaths, files and directories, no subdirs scan", "returnType": "FilePathList", "params": [ { @@ -4696,7 +4696,7 @@ }, { "name": "LoadDirectoryFilesEx", - "description": "Load directory filepaths with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result", + "description": "Load directory filepaths with extension filtering and subdir scan; some filters available: "*.*", "FILES*", "DIRS*"", "returnType": "FilePathList", "params": [ { diff --git a/tools/rlparser/output/raylib_api.lua b/tools/rlparser/output/raylib_api.lua index 398e1b177..a412e513f 100644 --- a/tools/rlparser/output/raylib_api.lua +++ b/tools/rlparser/output/raylib_api.lua @@ -4199,7 +4199,7 @@ return { }, { name = "LoadDirectoryFiles", - description = "Load directory filepaths", + description = "Load directory filepaths, files and directories, no subdirs scan", returnType = "FilePathList", params = { {type = "const char *", name = "dirPath"} @@ -4207,7 +4207,7 @@ return { }, { name = "LoadDirectoryFilesEx", - description = "Load directory filepaths with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result", + description = "Load directory filepaths with extension filtering and subdir scan; some filters available: "*.*", "FILES*", "DIRS*"", returnType = "FilePathList", params = { {type = "const char *", name = "basePath"}, diff --git a/tools/rlparser/output/raylib_api.txt b/tools/rlparser/output/raylib_api.txt index d3cd7bcdc..bdad7db98 100644 --- a/tools/rlparser/output/raylib_api.txt +++ b/tools/rlparser/output/raylib_api.txt @@ -1784,12 +1784,12 @@ Function 145: IsFileNameValid() (1 input parameters) Function 146: LoadDirectoryFiles() (1 input parameters) Name: LoadDirectoryFiles Return type: FilePathList - Description: Load directory filepaths + Description: Load directory filepaths, files and directories, no subdirs scan Param[1]: dirPath (type: const char *) Function 147: LoadDirectoryFilesEx() (3 input parameters) Name: LoadDirectoryFilesEx Return type: FilePathList - Description: Load directory filepaths with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result + Description: Load directory filepaths with extension filtering and subdir scan; some filters available: "*.*", "FILES*", "DIRS*" Param[1]: basePath (type: const char *) Param[2]: filter (type: const char *) Param[3]: scanSubdirs (type: bool) diff --git a/tools/rlparser/output/raylib_api.xml b/tools/rlparser/output/raylib_api.xml index 62d8bdfaf..83e2bd548 100644 --- a/tools/rlparser/output/raylib_api.xml +++ b/tools/rlparser/output/raylib_api.xml @@ -1122,10 +1122,10 @@ - + - + From bb7b8d70e90f4d8303817cc0701f2f283ffdf0a9 Mon Sep 17 00:00:00 2001 From: Arbinda Rizki Muhammad <156565433+arbipink@users.noreply.github.com> Date: Sat, 4 Apr 2026 16:04:24 +0700 Subject: [PATCH 259/319] Example/audio adsr (#5713) * add audio_amp_envelope example * Change 1.0f to env->sustainLevel in case the user release the spacebar before attack state done --- examples/audio/audio_amp_envelope.c | 235 + examples/audio/audio_amp_envelope.png | Bin 0 -> 11047 bytes examples/audio/raygui.h | 6051 +++++++++++++++++++++++++ 3 files changed, 6286 insertions(+) create mode 100644 examples/audio/audio_amp_envelope.c create mode 100644 examples/audio/audio_amp_envelope.png create mode 100644 examples/audio/raygui.h diff --git a/examples/audio/audio_amp_envelope.c b/examples/audio/audio_amp_envelope.c new file mode 100644 index 000000000..9d6939e35 --- /dev/null +++ b/examples/audio/audio_amp_envelope.c @@ -0,0 +1,235 @@ +/******************************************************************************************* +* +* raylib [audio] example - amp envelope +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* Example originally created with raylib 6.0, last time updated with raylib 6.0 +* +* Example contributed by Arbinda Rizki Muhammad (@arbipink) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2026 Arbinda Rizki Muhammad (@arbipink) +* +********************************************************************************************/ + +#include "raylib.h" + +#define RAYGUI_IMPLEMENTATION +#include "raygui.h" + +#include + +#define BUFFER_SIZE 4096 +#define SAMPLE_RATE 44100 + +// Wave state +typedef enum { + IDLE, + ATTACK, + DECAY, + SUSTAIN, + RELEASE +} ADSRState; + +// Grouping all ADSR parameters and state into a struct +typedef struct { + float attackTime; + float decayTime; + float sustainLevel; + float releaseTime; + float currentValue; + ADSRState state; +} Envelope; + +//------------------------------------------------------------------------------------ +// Module Functions Declaration +//------------------------------------------------------------------------------------ +static void FillAudioBuffer(int i, float* buffer, float envelopeValue, float* audioTime); +static void UpdateEnvelope(Envelope* env); +static void DrawADSRGraph(const Envelope *env, Rectangle bounds); + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [audio] example - amp envelope"); + + InitAudioDevice(); + + // Set the number of samples the stream will keep in memory at a time to BUFFER_SIZE + SetAudioStreamBufferSizeDefault(BUFFER_SIZE); + float buffer[BUFFER_SIZE] = { 0 }; + + // Init raw audio stream (sample rate: 44100, sample size: 32bit-float, channels: 1-mono) + AudioStream stream = LoadAudioStream(SAMPLE_RATE, 32, 1); + + // Init Phase + float audioTime = 0.0f; + + // Initialize the struct + Envelope env = { + .attackTime = 1.0f, + .decayTime = 1.0f, + .sustainLevel = 0.5f, + .releaseTime = 1.0f, + .currentValue = 0.0f, + .state = IDLE + }; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) + { + // Update + //---------------------------------------------------------------------------------- + + if (IsKeyPressed(KEY_SPACE)) { + env.state = ATTACK; + } + + if (IsKeyReleased(KEY_SPACE)) { + if (env.state != IDLE) env.state = RELEASE; + } + + if (IsAudioStreamProcessed(stream)) { + + if (env.state != IDLE || env.currentValue > 0.0f) { + for (int i = 0; i < BUFFER_SIZE; i++) + { + UpdateEnvelope(&env); + FillAudioBuffer(i, buffer, env.currentValue, &audioTime); + } + } else { + // Clear buffer if silent to avoid looping noise + for (int i = 0; i < BUFFER_SIZE; i++) buffer[i] = 0; + audioTime = 0.0f; + } + + UpdateAudioStream(stream, buffer, BUFFER_SIZE); + } + + if (!IsAudioStreamPlaying(stream)) PlayAudioStream(stream); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + GuiSliderBar((Rectangle){ 100, 60, 400, 30 }, "Attack (s)", TextFormat("%2.2fs", env.attackTime), &env.attackTime, 0.1f, 3.0f); + GuiSliderBar((Rectangle){ 100, 100, 400, 30 }, "Decay (s)", TextFormat("%2.2fs", env.decayTime), &env.decayTime, 0.1f, 3.0f); + GuiSliderBar((Rectangle){ 100, 140, 400, 30 }, "Sustain", TextFormat("%2.2f", env.sustainLevel), &env.sustainLevel, 0.0f, 1.0f); + GuiSliderBar((Rectangle){ 100, 180, 400, 30 }, "Release (s)", TextFormat("%2.2fs", env.releaseTime), &env.releaseTime, 0.1f, 3.0f); + + DrawADSRGraph(&env, (Rectangle){ 100, 250, 400, 100 }); + + DrawCircleV((Vector2){ 520, 350 - (env.currentValue * 100) }, 5, MAROON); + DrawText(TextFormat("Current Gain: %2.2f", env.currentValue), 535, 345 - (env.currentValue * 100), 10, MAROON); + + DrawText("Press SPACE to PLAY the sound!", 200, 400, 20, LIGHTGRAY); + EndDrawing(); + //---------------------------------------------------------------------------------- + } + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadAudioStream(stream); + CloseAudioDevice(); + + CloseWindow(); + //-------------------------------------------------------------------------------------- + + return 0; +} + +//------------------------------------------------------------------------------------ +// Module Functions Definition +//------------------------------------------------------------------------------------ +static void FillAudioBuffer(int i, float* buffer, float envelopeValue, float* audioTime) +{ + + int frequency = 440; + buffer[i] = envelopeValue * sinf(2.0f * PI * frequency * (*audioTime)); + *audioTime += 1.0f / SAMPLE_RATE; +} + +static void UpdateEnvelope(Envelope *env) +{ + // Calculate the time delta for ONE sample (1/44100) + float sampleTime = 1.0f / SAMPLE_RATE; + + switch(env->state) + { + case ATTACK: + env->currentValue += (1.0f / env->attackTime) * sampleTime; + if (env->currentValue >= 1.0f) + { + env->currentValue = 1.0f; + env->state = DECAY; + } + break; + + case DECAY: + env->currentValue -= ((1.0f - env->sustainLevel) / env->decayTime) * sampleTime; + if (env->currentValue <= env->sustainLevel) + { + env->currentValue = env->sustainLevel; + env->state = SUSTAIN; + } + break; + + case SUSTAIN: + env->currentValue = env->sustainLevel; + break; + + case RELEASE: + env->currentValue -= (env->sustainLevel / env->releaseTime) * sampleTime; + if (env->currentValue <= 0.001f) // Use a small threshold to avoid infinite tail + { + env->currentValue = 0.0f; + env->state = IDLE; + } + break; + + default: break; + } +} + +static void DrawADSRGraph(const Envelope *env, Rectangle bounds) +{ + DrawRectangleRec(bounds, Fade(LIGHTGRAY, 0.3f)); + DrawRectangleLinesEx(bounds, 1, GRAY); + + // Fixed visual width for sustain stage since it's an amplitude not a time value + float sustainWidth = 1.0f; + + // Total time to visualize (sum of A, D, R + a padding for Sustain) + float totalTime = env->attackTime + env->decayTime + sustainWidth + env->releaseTime; + + float scaleX = bounds.width / totalTime; + float scaleY = bounds.height; + + Vector2 start = { bounds.x, bounds.y + bounds.height }; + Vector2 peak = { start.x + (env->attackTime * scaleX), bounds.y }; + Vector2 sustain = { peak.x + (env->decayTime * scaleX), bounds.y + (1.0f - env->sustainLevel) * scaleY }; + Vector2 rel = { sustain.x + (sustainWidth * scaleX), sustain.y }; + Vector2 end = { rel.x + (env->releaseTime * scaleX), bounds.y + bounds.height }; + + DrawLineV(start, peak, SKYBLUE); + DrawLineV(peak, sustain, BLUE); + DrawLineV(sustain, rel, DARKBLUE); + DrawLineV(rel, end, ORANGE); + + DrawText("ADSR Visualizer", bounds.x, bounds.y - 20, 10, DARKGRAY); +} \ No newline at end of file diff --git a/examples/audio/audio_amp_envelope.png b/examples/audio/audio_amp_envelope.png new file mode 100644 index 0000000000000000000000000000000000000000..b09dcc9d016d19ad80a92e2ca482ce22b35a67c8 GIT binary patch literal 11047 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 literal 0 HcmV?d00001 diff --git a/examples/audio/raygui.h b/examples/audio/raygui.h new file mode 100644 index 000000000..03e487912 --- /dev/null +++ b/examples/audio/raygui.h @@ -0,0 +1,6051 @@ +/******************************************************************************************* +* +* raygui v5.0-dev - A simple and easy-to-use immediate-mode gui library +* +* DESCRIPTION: +* raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also +* available as a standalone library, as long as input and drawing functions are provided +* +* FEATURES: +* - Immediate-mode gui, minimal retained data +* - +25 controls provided (basic and advanced) +* - Styling system for colors, font and metrics +* - Icons supported, embedded as a 1-bit icons pack +* - Standalone mode option (custom input/graphics backend) +* - Multiple support tools provided for raygui development +* +* POSSIBLE IMPROVEMENTS: +* - Better standalone mode API for easy plug of custom backends +* - Externalize required inputs, allow user easier customization +* +* LIMITATIONS: +* - No editable multi-line word-wraped text box supported +* - No auto-layout mechanism, up to the user to define controls position and size +* - Standalone mode requires library modification and some user work to plug another backend +* +* NOTES: +* - WARNING: GuiLoadStyle() and GuiLoadStyle{Custom}() functions, allocate memory for +* font atlas recs and glyphs, freeing that memory is (usually) up to the user, +* no unload function is explicitly provided... but note that GuiLoadStyleDefault() unloads +* by default any previously loaded font (texture, recs, glyphs) +* - Global UI alpha (guiAlpha) is applied inside GuiDrawRectangle() and GuiDrawText() functions +* +* CONTROLS PROVIDED: +* # Container/separators Controls +* - WindowBox --> StatusBar, Panel +* - GroupBox --> Line +* - Line +* - Panel --> StatusBar +* - ScrollPanel --> StatusBar +* - TabBar --> Button +* +* # Basic Controls +* - Label +* - LabelButton --> Label +* - Button +* - Toggle +* - ToggleGroup --> Toggle +* - ToggleSlider +* - CheckBox +* - ComboBox +* - DropdownBox +* - TextBox +* - ValueBox --> TextBox +* - Spinner --> Button, ValueBox +* - Slider +* - SliderBar --> Slider +* - ProgressBar +* - StatusBar +* - DummyRec +* - Grid +* +* # Advance Controls +* - ListView +* - ColorPicker --> ColorPanel, ColorBarHue +* - MessageBox --> Window, Label, Button +* - TextInputBox --> Window, Label, TextBox, Button +* +* It also provides a set of functions for styling the controls based on its properties (size, color) +* +* +* RAYGUI STYLE (guiStyle): +* raygui uses a global data array for all gui style properties (allocated on data segment by default), +* when a new style is loaded, it is loaded over the global style... but a default gui style could always be +* recovered with GuiLoadStyleDefault() function, that overwrites the current style to the default one +* +* The global style array size is fixed and depends on the number of controls and properties: +* +* static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)]; +* +* guiStyle size is by default: 16*(16 + 8) = 384 int = 384*4 bytes = 1536 bytes = 1.5 KB +* +* Note that the first set of BASE properties (by default guiStyle[0..15]) belong to the generic style +* used for all controls, when any of those base values is set, it is automatically populated to all +* controls, so, specific control values overwriting generic style should be set after base values +* +* After the first BASE properties set, the EXTENDED properties set is defined (by default guiStyle[16..23]), +* those properties are actually common to all controls and can not be overwritten individually (like BASE ones) +* Some of those properties are: TEXT_SIZE, TEXT_SPACING, LINE_COLOR, BACKGROUND_COLOR +* +* Custom control properties can be defined using the EXTENDED properties for each independent control. +* +* TOOL: rGuiStyler is a visual tool to customize raygui style: github.com/raysan5/rguistyler +* +* +* RAYGUI ICONS (guiIcons): +* raygui could use a global array containing icons data (allocated on data segment by default), +* a custom icons set could be loaded over this array using GuiLoadIcons(), but loaded icons set +* must be same RAYGUI_ICON_SIZE and no more than RAYGUI_ICON_MAX_ICONS will be loaded +* +* Every icon is codified in binary form, using 1 bit per pixel, so, every 16x16 icon +* requires 8 integers (16*16/32) to be stored in memory. +* +* When the icon is draw, actually one quad per pixel is drawn if the bit for that pixel is set +* +* The global icons array size is fixed and depends on the number of icons and size: +* +* static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS]; +* +* guiIcons size is by default: 256*(16*16/32) = 2048*4 = 8192 bytes = 8 KB +* +* TOOL: rGuiIcons is a visual tool to customize/create raygui icons: github.com/raysan5/rguiicons +* +* RAYGUI LAYOUT: +* raygui currently does not provide an auto-layout mechanism like other libraries, +* layouts must be defined manually on controls drawing, providing the right bounds Rectangle for it +* +* TOOL: rGuiLayout is a visual tool to create raygui layouts: github.com/raysan5/rguilayout +* +* CONFIGURATION: +* #define RAYGUI_IMPLEMENTATION +* Generates the implementation of the library into the included file +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation +* +* #define RAYGUI_STANDALONE +* Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined +* internally in the library and input management and drawing functions must be provided by +* the user (check library implementation for further details) +* +* #define RAYGUI_NO_ICONS +* Avoid including embedded ricons data (256 icons, 16x16 pixels, 1-bit per pixel, 2KB) +* +* #define RAYGUI_CUSTOM_ICONS +* Includes custom ricons.h header defining a set of custom icons, +* this file can be generated using rGuiIcons tool +* +* #define RAYGUI_DEBUG_RECS_BOUNDS +* Draw control bounds rectangles for debug +* +* #define RAYGUI_DEBUG_TEXT_BOUNDS +* Draw text bounds rectangles for debug +* +* VERSIONS HISTORY: +* 5.0 (xx-Mar-2026) ADDED: Support up to 32 controls (v500) +* ADDED: guiControlExclusiveMode and guiControlExclusiveRec for exclusive modes +* ADDED: GuiValueBoxFloat() +* ADDED: GuiDropdonwBox() properties: DROPDOWN_ARROW_HIDDEN, DROPDOWN_ROLL_UP +* ADDED: GuiListView() property: LIST_ITEMS_BORDER_WIDTH +* ADDED: GuiLoadIconsFromMemory() +* ADDED: Multiple new icons +* ADDED: Macros for inputs customization, raylib decoupling +* REMOVED: GuiSpinner() from controls list, using BUTTON + VALUEBOX properties +* REMOVED: GuiSliderPro(), functionality was redundant +* REVIEWED: Controls using text labels to use LABEL properties +* REVIEWED: Replaced sprintf() by snprintf() for more safety +* REVIEWED: GuiTabBar(), close tab with mouse middle button +* REVIEWED: GuiScrollPanel(), scroll speed proportional to content +* REVIEWED: GuiDropdownBox(), support roll up and hidden arrow +* REVIEWED: GuiTextBox(), cursor position initialization +* REVIEWED: GuiSliderPro(), control value change check +* REVIEWED: GuiGrid(), simplified implementation +* REVIEWED: GuiIconText(), increase buffer size and reviewed padding +* REVIEWED: GuiDrawText(), improved wrap mode drawing +* REVIEWED: GuiScrollBar(), minor tweaks +* REVIEWED: GuiProgressBar(), improved borders computing +* REVIEWED: GuiTextBox(), multiple improvements: autocursor and more +* REVIEWED: Functions descriptions, removed wrong return value reference +* REDESIGNED: GuiColorPanel(), improved HSV <-> RGBA convertion +* REDESIGNED: WARNING: TEXT_LINE_SPACING does not consider text height, only lines spacing +* +* 4.0 (12-Sep-2023) ADDED: GuiToggleSlider() +* ADDED: GuiColorPickerHSV() and GuiColorPanelHSV() +* ADDED: Multiple new icons, mostly compiler related +* ADDED: New DEFAULT properties: TEXT_LINE_SPACING, TEXT_ALIGNMENT_VERTICAL, TEXT_WRAP_MODE +* ADDED: New enum values: GuiTextAlignment, GuiTextAlignmentVertical, GuiTextWrapMode +* ADDED: Support loading styles with custom font charset from external file +* REDESIGNED: GuiTextBox(), support mouse cursor positioning +* REDESIGNED: GuiDrawText(), support multiline and word-wrap modes (read only) +* REDESIGNED: GuiProgressBar() to be more visual, progress affects border color +* REDESIGNED: Global alpha consideration moved to GuiDrawRectangle() and GuiDrawText() +* REDESIGNED: GuiScrollPanel(), get parameters by reference and return result value +* REDESIGNED: GuiToggleGroup(), get parameters by reference and return result value +* REDESIGNED: GuiComboBox(), get parameters by reference and return result value +* REDESIGNED: GuiCheckBox(), get parameters by reference and return result value +* REDESIGNED: GuiSlider(), get parameters by reference and return result value +* REDESIGNED: GuiSliderBar(), get parameters by reference and return result value +* REDESIGNED: GuiProgressBar(), get parameters by reference and return result value +* REDESIGNED: GuiListView(), get parameters by reference and return result value +* REDESIGNED: GuiColorPicker(), get parameters by reference and return result value +* REDESIGNED: GuiColorPanel(), get parameters by reference and return result value +* REDESIGNED: GuiColorBarAlpha(), get parameters by reference and return result value +* REDESIGNED: GuiColorBarHue(), get parameters by reference and return result value +* REDESIGNED: GuiGrid(), get parameters by reference and return result value +* REDESIGNED: GuiGrid(), added extra parameter +* REDESIGNED: GuiListViewEx(), change parameters order +* REDESIGNED: All controls return result as int value +* REVIEWED: GuiScrollPanel() to avoid smallish scroll-bars +* REVIEWED: All examples and specially controls_test_suite +* RENAMED: gui_file_dialog module to gui_window_file_dialog +* UPDATED: All styles to include ISO-8859-15 charset (as much as possible) +* +* 3.6 (10-May-2023) ADDED: New icon: SAND_TIMER +* ADDED: GuiLoadStyleFromMemory() (binary only) +* REVIEWED: GuiScrollBar() horizontal movement key +* REVIEWED: GuiTextBox() crash on cursor movement +* REVIEWED: GuiTextBox(), additional inputs support +* REVIEWED: GuiLabelButton(), avoid text cut +* REVIEWED: GuiTextInputBox(), password input +* REVIEWED: Local GetCodepointNext(), aligned with raylib +* REDESIGNED: GuiSlider*()/GuiScrollBar() to support out-of-bounds +* +* 3.5 (20-Apr-2023) ADDED: GuiTabBar(), based on GuiToggle() +* ADDED: Helper functions to split text in separate lines +* ADDED: Multiple new icons, useful for code editing tools +* REMOVED: Unneeded icon editing functions +* REMOVED: GuiTextBoxMulti(), very limited and broken +* REMOVED: MeasureTextEx() dependency, logic directly implemented +* REMOVED: DrawTextEx() dependency, logic directly implemented +* REVIEWED: GuiScrollBar(), improve mouse-click behaviour +* REVIEWED: Library header info, more info, better organized +* REDESIGNED: GuiTextBox() to support cursor movement +* REDESIGNED: GuiDrawText() to divide drawing by lines +* +* 3.2 (22-May-2022) RENAMED: Some enum values, for unification, avoiding prefixes +* REMOVED: GuiScrollBar(), only internal +* REDESIGNED: GuiPanel() to support text parameter +* REDESIGNED: GuiScrollPanel() to support text parameter +* REDESIGNED: GuiColorPicker() to support text parameter +* REDESIGNED: GuiColorPanel() to support text parameter +* REDESIGNED: GuiColorBarAlpha() to support text parameter +* REDESIGNED: GuiColorBarHue() to support text parameter +* REDESIGNED: GuiTextInputBox() to support password +* +* 3.1 (12-Jan-2022) REVIEWED: Default style for consistency (aligned with rGuiLayout v2.5 tool) +* REVIEWED: GuiLoadStyle() to support compressed font atlas image data and unload previous textures +* REVIEWED: External icons usage logic +* REVIEWED: GuiLine() for centered alignment when including text +* RENAMED: Multiple controls properties definitions to prepend RAYGUI_ +* RENAMED: RICON_ references to RAYGUI_ICON_ for library consistency +* Projects updated and multiple tweaks +* +* 3.0 (04-Nov-2021) Integrated ricons data to avoid external file +* REDESIGNED: GuiTextBoxMulti() +* REMOVED: GuiImageButton*() +* Multiple minor tweaks and bugs corrected +* +* 2.9 (17-Mar-2021) REMOVED: Tooltip API +* 2.8 (03-May-2020) Centralized rectangles drawing to GuiDrawRectangle() +* 2.7 (20-Feb-2020) ADDED: Possible tooltips API +* 2.6 (09-Sep-2019) ADDED: GuiTextInputBox() +* REDESIGNED: GuiListView*(), GuiDropdownBox(), GuiSlider*(), GuiProgressBar(), GuiMessageBox() +* REVIEWED: GuiTextBox(), GuiSpinner(), GuiValueBox(), GuiLoadStyle() +* Replaced property INNER_PADDING by TEXT_PADDING, renamed some properties +* ADDED: 8 new custom styles ready to use +* Multiple minor tweaks and bugs corrected +* +* 2.5 (28-May-2019) Implemented extended GuiTextBox(), GuiValueBox(), GuiSpinner() +* 2.3 (29-Apr-2019) ADDED: rIcons auxiliar library and support for it, multiple controls reviewed +* Refactor all controls drawing mechanism to use control state +* 2.2 (05-Feb-2019) ADDED: GuiScrollBar(), GuiScrollPanel(), reviewed GuiListView(), removed Gui*Ex() controls +* 2.1 (26-Dec-2018) REDESIGNED: GuiCheckBox(), GuiComboBox(), GuiDropdownBox(), GuiToggleGroup() > Use combined text string +* REDESIGNED: Style system (breaking change) +* 2.0 (08-Nov-2018) ADDED: Support controls guiLock and custom fonts +* REVIEWED: GuiComboBox(), GuiListView()... +* 1.9 (09-Oct-2018) REVIEWED: GuiGrid(), GuiTextBox(), GuiTextBoxMulti(), GuiValueBox()... +* 1.8 (01-May-2018) Lot of rework and redesign to align with rGuiStyler and rGuiLayout +* 1.5 (21-Jun-2017) Working in an improved styles system +* 1.4 (15-Jun-2017) Rewritten all GUI functions (removed useless ones) +* 1.3 (12-Jun-2017) Complete redesign of style system +* 1.1 (01-Jun-2017) Complete review of the library +* 1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria +* 0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria +* 0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria +* +* DEPENDENCIES: +* raylib 5.6-dev - Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing +* +* STANDALONE MODE: +* By default raygui depends on raylib mostly for the inputs and the drawing functionality but that dependency can be disabled +* with the config flag RAYGUI_STANDALONE. In that case is up to the user to provide another backend to cover library needs +* +* The following functions should be redefined for a custom backend: +* +* - Vector2 GetMousePosition(void); +* - float GetMouseWheelMove(void); +* - bool IsMouseButtonDown(int button); +* - bool IsMouseButtonPressed(int button); +* - bool IsMouseButtonReleased(int button); +* - bool IsKeyDown(int key); +* - bool IsKeyPressed(int key); +* - int GetCharPressed(void); // -- GuiTextBox(), GuiValueBox() +* +* - void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle() +* - void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker() +* +* - Font GetFontDefault(void); // -- GuiLoadStyleDefault() +* - Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // -- GuiLoadStyle() +* - Texture2D LoadTextureFromImage(Image image); // -- GuiLoadStyle(), required to load texture from embedded font atlas image +* - void SetShapesTexture(Texture2D tex, Rectangle rec); // -- GuiLoadStyle(), required to set shapes rec to font white rec (optimization) +* - char *LoadFileText(const char *fileName); // -- GuiLoadStyle(), required to load charset data +* - void UnloadFileText(char *text); // -- GuiLoadStyle(), required to unload charset data +* - const char *GetDirectoryPath(const char *filePath); // -- GuiLoadStyle(), required to find charset/font file from text .rgs +* - int *LoadCodepoints(const char *text, int *count); // -- GuiLoadStyle(), required to load required font codepoints list +* - void UnloadCodepoints(int *codepoints); // -- GuiLoadStyle(), required to unload codepoints list +* - unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // -- GuiLoadStyle() +* +* CONTRIBUTORS: +* Ramon Santamaria: Supervision, review, redesign, update and maintenance +* Vlad Adrian: Complete rewrite of GuiTextBox() to support extended features (2019) +* Sergio Martinez: Review, testing (2015) and redesign of multiple controls (2018) +* Adria Arranz: Testing and implementation of additional controls (2018) +* Jordi Jorba: Testing and implementation of additional controls (2018) +* Albert Martos: Review and testing of the library (2015) +* Ian Eito: Review and testing of the library (2015) +* Kevin Gato: Initial implementation of basic components (2014) +* Daniel Nicolas: Initial implementation of basic components (2014) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2014-2026 Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef RAYGUI_H +#define RAYGUI_H + +#define RAYGUI_VERSION_MAJOR 4 +#define RAYGUI_VERSION_MINOR 5 +#define RAYGUI_VERSION_PATCH 0 +#define RAYGUI_VERSION "5.0-dev" + +#if !defined(RAYGUI_STANDALONE) + #include "raylib.h" +#endif + +// Function specifiers in case library is build/used as a shared library (Windows) +// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll +#if defined(_WIN32) + #if defined(BUILD_LIBTYPE_SHARED) + #define RAYGUIAPI __declspec(dllexport) // Building the library as a Win32 shared library (.dll) + #elif defined(USE_LIBTYPE_SHARED) + #define RAYGUIAPI __declspec(dllimport) // Using the library as a Win32 shared library (.dll) + #endif + #define _CRT_SECURE_NO_WARNINGS // Disable unsafe warnings on scanf() functions in MSVC +#endif + +// Function specifiers definition +#ifndef RAYGUIAPI + #define RAYGUIAPI // Functions defined as 'extern' by default (implicit specifiers) +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// Simple log system to avoid printf() calls if required +// NOTE: Avoiding those calls, also avoids const strings memory usage +#define RAYGUI_SUPPORT_LOG_INFO +#if defined(RAYGUI_SUPPORT_LOG_INFO) + #define RAYGUI_LOG(...) printf(__VA_ARGS__) +#else + #define RAYGUI_LOG(...) +#endif + +// Macros to define required UI inputs, including mapping to gamepad controls +// TODO: Define additionally required macros for missing inputs +#if !defined(GUI_BUTTON_DOWN) + #define GUI_BUTTON_DOWN (IsMouseButtonDown(MOUSE_LEFT_BUTTON) || IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) +#endif +#if !defined(GUI_BUTTON_DOWN_ALT) + // Mapping to alternative button down pressed + #define GUI_BUTTON_DOWN_ALT (IsMouseButtonDown(MOUSE_RIGHT_BUTTON) || IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT)) +#endif +#if !defined(GUI_BUTTON_PRESSED) + #define GUI_BUTTON_PRESSED (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsGamepadButtonPressed(0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) +#endif +// TODO: WARNING: GuiTabBar() still requires IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON) +#if !defined(GUI_BUTTON_RELEASED) + #define GUI_BUTTON_RELEASED (IsMouseButtonReleased(MOUSE_LEFT_BUTTON) || IsGamepadButtonReleased(0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) +#endif +#if !defined(GUI_SCROLL_DELTA) + // Mapping to scroll delta changes + // TODO: Review inconsistencies between platforms + #if defined(PLATFORM_WEB) + // NOTE: Gamepad axis triggers not detected on web platform + #define GUI_SCROLL_DELTA ((float)IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_TRIGGER_2) - (float)IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_TRIGGER_2)) + #else + #define GUI_SCROLL_DELTA (GetMouseWheelMove() + (GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_TRIGGER) + 1) - (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_TRIGGER) + 1)) + #endif +#endif +#if !defined(GUI_POINTER_POSITION) + #define GUI_POINTER_POSITION GetMousePosition() +#endif +#if !defined(GUI_KEY_DOWN) + #define GUI_KEY_DOWN(key) IsKeyDown(key) +#endif +#if !defined(GUI_KEY_PRESSED) + #define GUI_KEY_PRESSED(key) IsKeyPressed(key) +#endif +#if !defined(GUI_INPUT_KEY) + #define GUI_INPUT_KEY GetCharPressed() +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +// NOTE: Some types are required for RAYGUI_STANDALONE usage +//---------------------------------------------------------------------------------- +#if defined(RAYGUI_STANDALONE) + #ifndef __cplusplus + // Boolean type + #ifndef true + typedef enum { false, true } bool; + #endif + #endif + + // Vector2 type + typedef struct Vector2 { + float x; + float y; + } Vector2; + + // Vector3 type // -- ConvertHSVtoRGB(), ConvertRGBtoHSV() + typedef struct Vector3 { + float x; + float y; + float z; + } Vector3; + + // Color type, RGBA (32bit) + typedef struct Color { + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; + } Color; + + // Rectangle type + typedef struct Rectangle { + float x; + float y; + float width; + float height; + } Rectangle; + + // TODO: Texture2D type is very coupled to raylib, required by Font type + // It should be redesigned to be provided by user + typedef struct Texture { + unsigned int id; // OpenGL texture id + int width; // Texture base width + int height; // Texture base height + int mipmaps; // Mipmap levels, 1 by default + int format; // Data format (PixelFormat type) + } Texture; + + // Texture2D, same as Texture + typedef Texture Texture2D; + + // Image, pixel data stored in CPU memory (RAM) + typedef struct Image { + void *data; // Image raw data + int width; // Image base width + int height; // Image base height + int mipmaps; // Mipmap levels, 1 by default + int format; // Data format (PixelFormat type) + } Image; + + // GlyphInfo, font characters glyphs info + typedef struct GlyphInfo { + int value; // Character value (Unicode) + int offsetX; // Character offset X when drawing + int offsetY; // Character offset Y when drawing + int advanceX; // Character advance position X + Image image; // Character image data + } GlyphInfo; + + // TODO: Font type is very coupled to raylib, mostly required by GuiLoadStyle() + // It should be redesigned to be provided by user + typedef struct Font { + int baseSize; // Base size (default chars height) + int glyphCount; // Number of glyph characters + int glyphPadding; // Padding around the glyph characters + Texture2D texture; // Texture atlas containing the glyphs + Rectangle *recs; // Rectangles in texture for the glyphs + GlyphInfo *glyphs; // Glyphs info data + } Font; +#endif + +// Style property +// NOTE: Used when exporting style as code for convenience +typedef struct GuiStyleProp { + unsigned short controlId; // Control identifier + unsigned short propertyId; // Property identifier + int propertyValue; // Property value +} GuiStyleProp; + +/* +// Controls text style -NOT USED- +// NOTE: Text style is defined by control +typedef struct GuiTextStyle { + unsigned int size; + int charSpacing; + int lineSpacing; + int alignmentH; + int alignmentV; + int padding; +} GuiTextStyle; +*/ + +// Gui control state +typedef enum { + STATE_NORMAL = 0, + STATE_FOCUSED, + STATE_PRESSED, + STATE_DISABLED +} GuiState; + +// Gui control text alignment +typedef enum { + TEXT_ALIGN_LEFT = 0, + TEXT_ALIGN_CENTER, + TEXT_ALIGN_RIGHT +} GuiTextAlignment; + +// Gui control text alignment vertical +// NOTE: Text vertical position inside the text bounds +typedef enum { + TEXT_ALIGN_TOP = 0, + TEXT_ALIGN_MIDDLE, + TEXT_ALIGN_BOTTOM +} GuiTextAlignmentVertical; + +// Gui control text wrap mode +// NOTE: Useful for multiline text +typedef enum { + TEXT_WRAP_NONE = 0, + TEXT_WRAP_CHAR, + TEXT_WRAP_WORD +} GuiTextWrapMode; + +// Gui controls +typedef enum { + // Default -> populates to all controls when set + DEFAULT = 0, + + // Basic controls + LABEL, // Used also for: LABELBUTTON + BUTTON, + TOGGLE, // Used also for: TOGGLEGROUP + SLIDER, // Used also for: SLIDERBAR, TOGGLESLIDER + PROGRESSBAR, + CHECKBOX, + COMBOBOX, + DROPDOWNBOX, + TEXTBOX, // Used also for: TEXTBOXMULTI + VALUEBOX, + CONTROL11, + LISTVIEW, + COLORPICKER, + SCROLLBAR, + STATUSBAR +} GuiControl; + +// Gui base properties for every control +// NOTE: RAYGUI_MAX_PROPS_BASE properties (by default 16 properties) +typedef enum { + BORDER_COLOR_NORMAL = 0, // Control border color in STATE_NORMAL + BASE_COLOR_NORMAL, // Control base color in STATE_NORMAL + TEXT_COLOR_NORMAL, // Control text color in STATE_NORMAL + BORDER_COLOR_FOCUSED, // Control border color in STATE_FOCUSED + BASE_COLOR_FOCUSED, // Control base color in STATE_FOCUSED + TEXT_COLOR_FOCUSED, // Control text color in STATE_FOCUSED + BORDER_COLOR_PRESSED, // Control border color in STATE_PRESSED + BASE_COLOR_PRESSED, // Control base color in STATE_PRESSED + TEXT_COLOR_PRESSED, // Control text color in STATE_PRESSED + BORDER_COLOR_DISABLED, // Control border color in STATE_DISABLED + BASE_COLOR_DISABLED, // Control base color in STATE_DISABLED + TEXT_COLOR_DISABLED, // Control text color in STATE_DISABLED + BORDER_WIDTH = 12, // Control border size, 0 for no border + //TEXT_SIZE, // Control text size (glyphs max height) -> GLOBAL for all controls + //TEXT_SPACING, // Control text spacing between glyphs -> GLOBAL for all controls + //TEXT_LINE_SPACING, // Control text spacing between lines -> GLOBAL for all controls + TEXT_PADDING = 13, // Control text padding, not considering border + TEXT_ALIGNMENT = 14, // Control text horizontal alignment inside control text bound (after border and padding) + //TEXT_WRAP_MODE // Control text wrap-mode inside text bounds -> GLOBAL for all controls +} GuiControlProperty; + +// TODO: Which text styling properties should be global or per-control? +// At this moment TEXT_PADDING and TEXT_ALIGNMENT is configured and saved per control while +// TEXT_SIZE, TEXT_SPACING, TEXT_LINE_SPACING, TEXT_ALIGNMENT_VERTICAL, TEXT_WRAP_MODE are global and +// should be configured by user as needed while defining the UI layout + +// Gui extended properties depend on control +// NOTE: RAYGUI_MAX_PROPS_EXTENDED properties (by default, max 8 properties) +//---------------------------------------------------------------------------------- +// DEFAULT extended properties +// NOTE: Those properties are common to all controls or global +// WARNING: Only 8 slots vailable for those properties by default +typedef enum { + TEXT_SIZE = 16, // Text size (glyphs max height) + TEXT_SPACING, // Text spacing between glyphs + LINE_COLOR, // Line control color + BACKGROUND_COLOR, // Background color + TEXT_LINE_SPACING, // Text spacing between lines + TEXT_ALIGNMENT_VERTICAL, // Text vertical alignment inside text bounds (after border and padding) + TEXT_WRAP_MODE // Text wrap-mode inside text bounds + //TEXT_DECORATION // Text decoration: 0-None, 1-Underline, 2-Line-through, 3-Overline + //TEXT_DECORATION_THICK // Text decoration line thickness +} GuiDefaultProperty; + +// Other possible text properties: +// TEXT_WEIGHT // Normal, Italic, Bold -> Requires specific font change +// TEXT_INDENT // Text indentation -> Now using TEXT_PADDING... + +// Label +//typedef enum { } GuiLabelProperty; + +// Button/Spinner +//typedef enum { } GuiButtonProperty; + +// Toggle/ToggleGroup +typedef enum { + GROUP_PADDING = 16, // ToggleGroup separation between toggles +} GuiToggleProperty; + +// Slider/SliderBar +typedef enum { + SLIDER_WIDTH = 16, // Slider size of internal bar + SLIDER_PADDING // Slider/SliderBar internal bar padding +} GuiSliderProperty; + +// ProgressBar +typedef enum { + PROGRESS_PADDING = 16, // ProgressBar internal padding + PROGRESS_SIDE, // ProgressBar increment side: 0-left->right, 1-right-left +} GuiProgressBarProperty; + +// ScrollBar +typedef enum { + ARROWS_SIZE = 16, // ScrollBar arrows size + ARROWS_VISIBLE, // ScrollBar arrows visible + SCROLL_SLIDER_PADDING, // ScrollBar slider internal padding + SCROLL_SLIDER_SIZE, // ScrollBar slider size + SCROLL_PADDING, // ScrollBar scroll padding from arrows + SCROLL_SPEED, // ScrollBar scrolling speed +} GuiScrollBarProperty; + +// CheckBox +typedef enum { + CHECK_PADDING = 16 // CheckBox internal check padding +} GuiCheckBoxProperty; + +// ComboBox +typedef enum { + COMBO_BUTTON_WIDTH = 16, // ComboBox right button width + COMBO_BUTTON_SPACING // ComboBox button separation +} GuiComboBoxProperty; + +// DropdownBox +typedef enum { + ARROW_PADDING = 16, // DropdownBox arrow separation from border and items + DROPDOWN_ITEMS_SPACING, // DropdownBox items separation + DROPDOWN_ARROW_HIDDEN, // DropdownBox arrow hidden + DROPDOWN_ROLL_UP // DropdownBox roll up flag (default rolls down) +} GuiDropdownBoxProperty; + +// TextBox/TextBoxMulti/ValueBox/Spinner +typedef enum { + TEXT_READONLY = 16, // TextBox in read-only mode: 0-text editable, 1-text no-editable +} GuiTextBoxProperty; + +// ValueBox/Spinner +typedef enum { + SPINNER_BUTTON_WIDTH = 16, // Spinner left/right buttons width + SPINNER_BUTTON_SPACING, // Spinner buttons separation +} GuiValueBoxProperty; + +// Control11 +//typedef enum { } GuiControl11Property; + +// ListView +typedef enum { + LIST_ITEMS_HEIGHT = 16, // ListView items height + LIST_ITEMS_SPACING, // ListView items separation + SCROLLBAR_WIDTH, // ListView scrollbar size (usually width) + SCROLLBAR_SIDE, // ListView scrollbar side (0-SCROLLBAR_LEFT_SIDE, 1-SCROLLBAR_RIGHT_SIDE) + LIST_ITEMS_BORDER_NORMAL, // ListView items border enabled in normal state + LIST_ITEMS_BORDER_WIDTH // ListView items border width +} GuiListViewProperty; + +// ColorPicker +typedef enum { + COLOR_SELECTOR_SIZE = 16, + HUEBAR_WIDTH, // ColorPicker right hue bar width + HUEBAR_PADDING, // ColorPicker right hue bar separation from panel + HUEBAR_SELECTOR_HEIGHT, // ColorPicker right hue bar selector height + HUEBAR_SELECTOR_OVERFLOW // ColorPicker right hue bar selector overflow +} GuiColorPickerProperty; + +#define SCROLLBAR_LEFT_SIDE 0 +#define SCROLLBAR_RIGHT_SIDE 1 + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +// ... + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- + +#if defined(__cplusplus) +extern "C" { // Prevents name mangling of functions +#endif + +// Global gui state control functions +RAYGUIAPI void GuiEnable(void); // Enable gui controls (global state) +RAYGUIAPI void GuiDisable(void); // Disable gui controls (global state) +RAYGUIAPI void GuiLock(void); // Lock gui controls (global state) +RAYGUIAPI void GuiUnlock(void); // Unlock gui controls (global state) +RAYGUIAPI bool GuiIsLocked(void); // Check if gui is locked (global state) +RAYGUIAPI void GuiSetAlpha(float alpha); // Set gui controls alpha (global state), alpha goes from 0.0f to 1.0f +RAYGUIAPI void GuiSetState(int state); // Set gui state (global state) +RAYGUIAPI int GuiGetState(void); // Get gui state (global state) + +// Font set/get functions +RAYGUIAPI void GuiSetFont(Font font); // Set gui custom font (global state) +RAYGUIAPI Font GuiGetFont(void); // Get gui custom font (global state) + +// Style set/get functions +RAYGUIAPI void GuiSetStyle(int control, int property, int value); // Set one style property +RAYGUIAPI int GuiGetStyle(int control, int property); // Get one style property + +// Styles loading functions +RAYGUIAPI void GuiLoadStyle(const char *fileName); // Load style file over global style variable (.rgs) +RAYGUIAPI void GuiLoadStyleDefault(void); // Load style default over global style + +// Tooltips management functions +RAYGUIAPI void GuiEnableTooltip(void); // Enable gui tooltips (global state) +RAYGUIAPI void GuiDisableTooltip(void); // Disable gui tooltips (global state) +RAYGUIAPI void GuiSetTooltip(const char *tooltip); // Set tooltip string + +// Icons functionality +RAYGUIAPI const char *GuiIconText(int iconId, const char *text); // Get text with icon id prepended (if supported) +#if !defined(RAYGUI_NO_ICONS) +RAYGUIAPI void GuiSetIconScale(int scale); // Set default icon drawing size +RAYGUIAPI unsigned int *GuiGetIcons(void); // Get raygui icons data pointer +RAYGUIAPI char **GuiLoadIcons(const char *fileName, bool loadIconsName); // Load raygui icons file (.rgi) into internal icons data +RAYGUIAPI void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color); // Draw icon using pixel size at specified position +#endif + +// Utility functions +RAYGUIAPI int GuiGetTextWidth(const char *text); // Get text width considering gui style and icon size (if required) + +// Controls +//---------------------------------------------------------------------------------------------------------- +// Container/separator controls, useful for controls organization +RAYGUIAPI int GuiWindowBox(Rectangle bounds, const char *title); // Window Box control, shows a window that can be closed +RAYGUIAPI int GuiGroupBox(Rectangle bounds, const char *text); // Group Box control with text name +RAYGUIAPI int GuiLine(Rectangle bounds, const char *text); // Line separator control, could contain text +RAYGUIAPI int GuiPanel(Rectangle bounds, const char *text); // Panel control, useful to group controls +RAYGUIAPI int GuiTabBar(Rectangle bounds, char **text, int count, int *active); // Tab Bar control, returns TAB to be closed or -1 +RAYGUIAPI int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view); // Scroll Panel control + +// Basic controls set +RAYGUIAPI int GuiLabel(Rectangle bounds, const char *text); // Label control +RAYGUIAPI int GuiButton(Rectangle bounds, const char *text); // Button control, returns true when clicked +RAYGUIAPI int GuiLabelButton(Rectangle bounds, const char *text); // Label button control, returns true when clicked +RAYGUIAPI int GuiToggle(Rectangle bounds, const char *text, bool *active); // Toggle Button control +RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int *active); // Toggle Group control +RAYGUIAPI int GuiToggleSlider(Rectangle bounds, const char *text, int *active); // Toggle Slider control +RAYGUIAPI int GuiCheckBox(Rectangle bounds, const char *text, bool *checked); // Check Box control, returns true when active +RAYGUIAPI int GuiComboBox(Rectangle bounds, const char *text, int *active); // Combo Box control + +RAYGUIAPI int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode); // Dropdown Box control +RAYGUIAPI int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Spinner control +RAYGUIAPI int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Value Box control, updates input text with numbers +RAYGUIAPI int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode); // Value box control for float values +RAYGUIAPI int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control, updates input text + +RAYGUIAPI int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider control +RAYGUIAPI int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider Bar control +RAYGUIAPI int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Progress Bar control +RAYGUIAPI int GuiStatusBar(Rectangle bounds, const char *text); // Status Bar control, shows info text +RAYGUIAPI int GuiDummyRec(Rectangle bounds, const char *text); // Dummy control for placeholders +RAYGUIAPI int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell); // Grid control + +// Advance controls set +RAYGUIAPI int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active); // List View control +RAYGUIAPI int GuiListViewEx(Rectangle bounds, char **text, int count, int *scrollIndex, int *active, int *focus); // List View with extended parameters +RAYGUIAPI int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons); // Message Box control, displays a message +RAYGUIAPI int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, bool *secretViewActive); // Text Input Box control, ask for text, supports secret +RAYGUIAPI int GuiColorPicker(Rectangle bounds, const char *text, Color *color); // Color Picker control (multiple color controls) +RAYGUIAPI int GuiColorPanel(Rectangle bounds, const char *text, Color *color); // Color Panel control +RAYGUIAPI int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha); // Color Bar Alpha control +RAYGUIAPI int GuiColorBarHue(Rectangle bounds, const char *text, float *value); // Color Bar Hue control +RAYGUIAPI int GuiColorPickerHSV(Rectangle bounds, const char *text, Vector3 *colorHsv); // Color Picker control that avoids conversion to RGB on each call (multiple color controls) +RAYGUIAPI int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv); // Color Panel control that updates Hue-Saturation-Value color value, used by GuiColorPickerHSV() +//---------------------------------------------------------------------------------------------------------- + +#if !defined(RAYGUI_NO_ICONS) + +#if !defined(RAYGUI_CUSTOM_ICONS) +//---------------------------------------------------------------------------------- +// Icons enumeration +//---------------------------------------------------------------------------------- +typedef enum { + ICON_NONE = 0, + ICON_FOLDER_FILE_OPEN = 1, + ICON_FILE_SAVE_CLASSIC = 2, + ICON_FOLDER_OPEN = 3, + ICON_FOLDER_SAVE = 4, + ICON_FILE_OPEN = 5, + ICON_FILE_SAVE = 6, + ICON_FILE_EXPORT = 7, + ICON_FILE_ADD = 8, + ICON_FILE_DELETE = 9, + ICON_FILETYPE_TEXT = 10, + ICON_FILETYPE_AUDIO = 11, + ICON_FILETYPE_IMAGE = 12, + ICON_FILETYPE_PLAY = 13, + ICON_FILETYPE_VIDEO = 14, + ICON_FILETYPE_INFO = 15, + ICON_FILE_COPY = 16, + ICON_FILE_CUT = 17, + ICON_FILE_PASTE = 18, + ICON_CURSOR_HAND = 19, + ICON_CURSOR_POINTER = 20, + ICON_CURSOR_CLASSIC = 21, + ICON_PENCIL = 22, + ICON_PENCIL_BIG = 23, + ICON_BRUSH_CLASSIC = 24, + ICON_BRUSH_PAINTER = 25, + ICON_WATER_DROP = 26, + ICON_COLOR_PICKER = 27, + ICON_RUBBER = 28, + ICON_COLOR_BUCKET = 29, + ICON_TEXT_T = 30, + ICON_TEXT_A = 31, + ICON_SCALE = 32, + ICON_RESIZE = 33, + ICON_FILTER_POINT = 34, + ICON_FILTER_BILINEAR = 35, + ICON_CROP = 36, + ICON_CROP_ALPHA = 37, + ICON_SQUARE_TOGGLE = 38, + ICON_SYMMETRY = 39, + ICON_SYMMETRY_HORIZONTAL = 40, + ICON_SYMMETRY_VERTICAL = 41, + ICON_LENS = 42, + ICON_LENS_BIG = 43, + ICON_EYE_ON = 44, + ICON_EYE_OFF = 45, + ICON_FILTER_TOP = 46, + ICON_FILTER = 47, + ICON_TARGET_POINT = 48, + ICON_TARGET_SMALL = 49, + ICON_TARGET_BIG = 50, + ICON_TARGET_MOVE = 51, + ICON_CURSOR_MOVE = 52, + ICON_CURSOR_SCALE = 53, + ICON_CURSOR_SCALE_RIGHT = 54, + ICON_CURSOR_SCALE_LEFT = 55, + ICON_UNDO = 56, + ICON_REDO = 57, + ICON_REREDO = 58, + ICON_MUTATE = 59, + ICON_ROTATE = 60, + ICON_REPEAT = 61, + ICON_SHUFFLE = 62, + ICON_EMPTYBOX = 63, + ICON_TARGET = 64, + ICON_TARGET_SMALL_FILL = 65, + ICON_TARGET_BIG_FILL = 66, + ICON_TARGET_MOVE_FILL = 67, + ICON_CURSOR_MOVE_FILL = 68, + ICON_CURSOR_SCALE_FILL = 69, + ICON_CURSOR_SCALE_RIGHT_FILL = 70, + ICON_CURSOR_SCALE_LEFT_FILL = 71, + ICON_UNDO_FILL = 72, + ICON_REDO_FILL = 73, + ICON_REREDO_FILL = 74, + ICON_MUTATE_FILL = 75, + ICON_ROTATE_FILL = 76, + ICON_REPEAT_FILL = 77, + ICON_SHUFFLE_FILL = 78, + ICON_EMPTYBOX_SMALL = 79, + ICON_BOX = 80, + ICON_BOX_TOP = 81, + ICON_BOX_TOP_RIGHT = 82, + ICON_BOX_RIGHT = 83, + ICON_BOX_BOTTOM_RIGHT = 84, + ICON_BOX_BOTTOM = 85, + ICON_BOX_BOTTOM_LEFT = 86, + ICON_BOX_LEFT = 87, + ICON_BOX_TOP_LEFT = 88, + ICON_BOX_CENTER = 89, + ICON_BOX_CIRCLE_MASK = 90, + ICON_POT = 91, + ICON_ALPHA_MULTIPLY = 92, + ICON_ALPHA_CLEAR = 93, + ICON_DITHERING = 94, + ICON_MIPMAPS = 95, + ICON_BOX_GRID = 96, + ICON_GRID = 97, + ICON_BOX_CORNERS_SMALL = 98, + ICON_BOX_CORNERS_BIG = 99, + ICON_FOUR_BOXES = 100, + ICON_GRID_FILL = 101, + ICON_BOX_MULTISIZE = 102, + ICON_ZOOM_SMALL = 103, + ICON_ZOOM_MEDIUM = 104, + ICON_ZOOM_BIG = 105, + ICON_ZOOM_ALL = 106, + ICON_ZOOM_CENTER = 107, + ICON_BOX_DOTS_SMALL = 108, + ICON_BOX_DOTS_BIG = 109, + ICON_BOX_CONCENTRIC = 110, + ICON_BOX_GRID_BIG = 111, + ICON_OK_TICK = 112, + ICON_CROSS = 113, + ICON_ARROW_LEFT = 114, + ICON_ARROW_RIGHT = 115, + ICON_ARROW_DOWN = 116, + ICON_ARROW_UP = 117, + ICON_ARROW_LEFT_FILL = 118, + ICON_ARROW_RIGHT_FILL = 119, + ICON_ARROW_DOWN_FILL = 120, + ICON_ARROW_UP_FILL = 121, + ICON_AUDIO = 122, + ICON_FX = 123, + ICON_WAVE = 124, + ICON_WAVE_SINUS = 125, + ICON_WAVE_SQUARE = 126, + ICON_WAVE_TRIANGULAR = 127, + ICON_CROSS_SMALL = 128, + ICON_PLAYER_PREVIOUS = 129, + ICON_PLAYER_PLAY_BACK = 130, + ICON_PLAYER_PLAY = 131, + ICON_PLAYER_PAUSE = 132, + ICON_PLAYER_STOP = 133, + ICON_PLAYER_NEXT = 134, + ICON_PLAYER_RECORD = 135, + ICON_MAGNET = 136, + ICON_LOCK_CLOSE = 137, + ICON_LOCK_OPEN = 138, + ICON_CLOCK = 139, + ICON_TOOLS = 140, + ICON_GEAR = 141, + ICON_GEAR_BIG = 142, + ICON_BIN = 143, + ICON_HAND_POINTER = 144, + ICON_LASER = 145, + ICON_COIN = 146, + ICON_EXPLOSION = 147, + ICON_1UP = 148, + ICON_PLAYER = 149, + ICON_PLAYER_JUMP = 150, + ICON_KEY = 151, + ICON_DEMON = 152, + ICON_TEXT_POPUP = 153, + ICON_GEAR_EX = 154, + ICON_CRACK = 155, + ICON_CRACK_POINTS = 156, + ICON_STAR = 157, + ICON_DOOR = 158, + ICON_EXIT = 159, + ICON_MODE_2D = 160, + ICON_MODE_3D = 161, + ICON_CUBE = 162, + ICON_CUBE_FACE_TOP = 163, + ICON_CUBE_FACE_LEFT = 164, + ICON_CUBE_FACE_FRONT = 165, + ICON_CUBE_FACE_BOTTOM = 166, + ICON_CUBE_FACE_RIGHT = 167, + ICON_CUBE_FACE_BACK = 168, + ICON_CAMERA = 169, + ICON_SPECIAL = 170, + ICON_LINK_NET = 171, + ICON_LINK_BOXES = 172, + ICON_LINK_MULTI = 173, + ICON_LINK = 174, + ICON_LINK_BROKE = 175, + ICON_TEXT_NOTES = 176, + ICON_NOTEBOOK = 177, + ICON_SUITCASE = 178, + ICON_SUITCASE_ZIP = 179, + ICON_MAILBOX = 180, + ICON_MONITOR = 181, + ICON_PRINTER = 182, + ICON_PHOTO_CAMERA = 183, + ICON_PHOTO_CAMERA_FLASH = 184, + ICON_HOUSE = 185, + ICON_HEART = 186, + ICON_CORNER = 187, + ICON_VERTICAL_BARS = 188, + ICON_VERTICAL_BARS_FILL = 189, + ICON_LIFE_BARS = 190, + ICON_INFO = 191, + ICON_CROSSLINE = 192, + ICON_HELP = 193, + ICON_FILETYPE_ALPHA = 194, + ICON_FILETYPE_HOME = 195, + ICON_LAYERS_VISIBLE = 196, + ICON_LAYERS = 197, + ICON_WINDOW = 198, + ICON_HIDPI = 199, + ICON_FILETYPE_BINARY = 200, + ICON_HEX = 201, + ICON_SHIELD = 202, + ICON_FILE_NEW = 203, + ICON_FOLDER_ADD = 204, + ICON_ALARM = 205, + ICON_CPU = 206, + ICON_ROM = 207, + ICON_STEP_OVER = 208, + ICON_STEP_INTO = 209, + ICON_STEP_OUT = 210, + ICON_RESTART = 211, + ICON_BREAKPOINT_ON = 212, + ICON_BREAKPOINT_OFF = 213, + ICON_BURGER_MENU = 214, + ICON_CASE_SENSITIVE = 215, + ICON_REG_EXP = 216, + ICON_FOLDER = 217, + ICON_FILE = 218, + ICON_SAND_TIMER = 219, + ICON_WARNING = 220, + ICON_HELP_BOX = 221, + ICON_INFO_BOX = 222, + ICON_PRIORITY = 223, + ICON_LAYERS_ISO = 224, + ICON_LAYERS2 = 225, + ICON_MLAYERS = 226, + ICON_MAPS = 227, + ICON_HOT = 228, + ICON_LABEL = 229, + ICON_NAME_ID = 230, + ICON_SLICING = 231, + ICON_MANUAL_CONTROL = 232, + ICON_COLLISION = 233, + ICON_CIRCLE_ADD = 234, + ICON_CIRCLE_ADD_FILL = 235, + ICON_CIRCLE_WARNING = 236, + ICON_CIRCLE_WARNING_FILL = 237, + ICON_BOX_MORE = 238, + ICON_BOX_MORE_FILL = 239, + ICON_BOX_MINUS = 240, + ICON_BOX_MINUS_FILL = 241, + ICON_UNION = 242, + ICON_INTERSECTION = 243, + ICON_DIFFERENCE = 244, + ICON_SPHERE = 245, + ICON_CYLINDER = 246, + ICON_CONE = 247, + ICON_ELLIPSOID = 248, + ICON_CAPSULE = 249, + ICON_250 = 250, + ICON_251 = 251, + ICON_252 = 252, + ICON_253 = 253, + ICON_254 = 254, + ICON_255 = 255 +} GuiIconName; +#endif + +#endif + +#if defined(__cplusplus) +} // Prevents name mangling of functions +#endif + +#endif // RAYGUI_H + +/*********************************************************************************** +* +* RAYGUI IMPLEMENTATION +* +************************************************************************************/ + +#if defined(RAYGUI_IMPLEMENTATION) + +#include // required for: isspace() [GuiTextBox()] +#include // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), snprintf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()] +#include // Required for: strlen() [GuiTextBox(), GuiValueBox()], memset(), memcpy() +#include // Required for: va_list, va_start(), vfprintf(), va_end() [TextFormat()] +#include // Required for: roundf() [GuiColorPicker()] + +// Allow custom memory allocators +#if defined(RAYGUI_MALLOC) || defined(RAYGUI_CALLOC) || defined(RAYGUI_FREE) + #if !defined(RAYGUI_MALLOC) || !defined(RAYGUI_CALLOC) || !defined(RAYGUI_FREE) + #error "RAYGUI: if RAYGUI_MALLOC, RAYGUI_CALLOC, or RAYGUI_FREE is customized, all three must be customized" + #endif +#else + #include // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()] + + #define RAYGUI_MALLOC(sz) malloc(sz) + #define RAYGUI_CALLOC(n,sz) calloc(n,sz) + #define RAYGUI_FREE(p) free(p) +#endif + +#ifdef __cplusplus + #define RAYGUI_CLITERAL(name) name +#else + #define RAYGUI_CLITERAL(name) (name) +#endif + +// Check if two rectangles are equal, used to validate a slider bounds as an id +#ifndef CHECK_BOUNDS_ID + #define CHECK_BOUNDS_ID(src, dst) (((int)src.x == (int)dst.x) && ((int)src.y == (int)dst.y) && ((int)src.width == (int)dst.width) && ((int)src.height == (int)dst.height)) +#endif + +#if !defined(RAYGUI_NO_ICONS) && !defined(RAYGUI_CUSTOM_ICONS) + +// Embedded icons, no external file provided +#define RAYGUI_ICON_SIZE 16 // Size of icons in pixels (squared) +#define RAYGUI_ICON_MAX_ICONS 256 // Maximum number of icons +#define RAYGUI_ICON_MAX_NAME_LENGTH 32 // Maximum length of icon name id + +// Icons data is defined by bit array (every bit represents one pixel) +// Those arrays are stored as unsigned int data arrays, so, +// every array element defines 32 pixels (bits) of information +// One icon is defined by 8 int, (8 int*32 bit = 256 bit = 16*16 pixels) +// NOTE: Number of elemens depend on RAYGUI_ICON_SIZE (by default 16x16 pixels) +#define RAYGUI_ICON_DATA_ELEMENTS (RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32) + +//---------------------------------------------------------------------------------- +// Icons data for all gui possible icons (allocated on data segment by default) +// +// NOTE 1: Every icon is codified in binary form, using 1 bit per pixel, so, +// every 16x16 icon requires 8 integers (16*16/32) to be stored +// +// NOTE 2: A different icon set could be loaded over this array using GuiLoadIcons(), +// but loaded icons set must be same RAYGUI_ICON_SIZE and no more than RAYGUI_ICON_MAX_ICONS +// +// guiIcons size is by default: 256*(16*16/32) = 2048*4 = 8192 bytes = 8 KB +//---------------------------------------------------------------------------------- +static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_NONE + 0x3ff80000, 0x2f082008, 0x2042207e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x00007ffe, // ICON_FOLDER_FILE_OPEN + 0x3ffe0000, 0x44226422, 0x400247e2, 0x5ffa4002, 0x57ea500a, 0x500a500a, 0x40025ffa, 0x00007ffe, // ICON_FILE_SAVE_CLASSIC + 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x41024002, 0x44424282, 0x793e4102, 0x00000100, // ICON_FOLDER_OPEN + 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x41024102, 0x44424102, 0x793e4282, 0x00000000, // ICON_FOLDER_SAVE + 0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, 0x24442284, 0x21042104, 0x20042104, 0x00003ffc, // ICON_FILE_OPEN + 0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, 0x21042104, 0x22842444, 0x20042104, 0x00003ffc, // ICON_FILE_SAVE + 0x3ff00000, 0x201c2010, 0x00042004, 0x20041004, 0x20844784, 0x00841384, 0x20042784, 0x00003ffc, // ICON_FILE_EXPORT + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x22042204, 0x22042f84, 0x20042204, 0x00003ffc, // ICON_FILE_ADD + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x25042884, 0x25042204, 0x20042884, 0x00003ffc, // ICON_FILE_DELETE + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042ff4, 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc, // ICON_FILETYPE_TEXT + 0x3ff00000, 0x201c2010, 0x27042004, 0x244424c4, 0x26442444, 0x20642664, 0x20042004, 0x00003ffc, // ICON_FILETYPE_AUDIO + 0x3ff00000, 0x201c2010, 0x26042604, 0x20042004, 0x35442884, 0x2414222c, 0x20042004, 0x00003ffc, // ICON_FILETYPE_IMAGE + 0x3ff00000, 0x201c2010, 0x20c42004, 0x22442144, 0x22442444, 0x20c42144, 0x20042004, 0x00003ffc, // ICON_FILETYPE_PLAY + 0x3ff00000, 0x3ffc2ff0, 0x3f3c2ff4, 0x3dbc2eb4, 0x3dbc2bb4, 0x3f3c2eb4, 0x3ffc2ff4, 0x00002ff4, // ICON_FILETYPE_VIDEO + 0x3ff00000, 0x201c2010, 0x21842184, 0x21842004, 0x21842184, 0x21842184, 0x20042184, 0x00003ffc, // ICON_FILETYPE_INFO + 0x0ff00000, 0x381c0810, 0x28042804, 0x28042804, 0x28042804, 0x28042804, 0x20102ffc, 0x00003ff0, // ICON_FILE_COPY + 0x00000000, 0x701c0000, 0x079c1e14, 0x55a000f0, 0x079c00f0, 0x701c1e14, 0x00000000, 0x00000000, // ICON_FILE_CUT + 0x01c00000, 0x13e41bec, 0x3f841004, 0x204420c4, 0x20442044, 0x20442044, 0x207c2044, 0x00003fc0, // ICON_FILE_PASTE + 0x00000000, 0x3aa00fe0, 0x2abc2aa0, 0x2aa42aa4, 0x20042aa4, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_CURSOR_HAND + 0x00000000, 0x003c000c, 0x030800c8, 0x30100c10, 0x10202020, 0x04400840, 0x01800280, 0x00000000, // ICON_CURSOR_POINTER + 0x00000000, 0x00180000, 0x01f00078, 0x03e007f0, 0x07c003e0, 0x04000e40, 0x00000000, 0x00000000, // ICON_CURSOR_CLASSIC + 0x00000000, 0x04000000, 0x11000a00, 0x04400a80, 0x01100220, 0x00580088, 0x00000038, 0x00000000, // ICON_PENCIL + 0x04000000, 0x15000a00, 0x50402880, 0x14102820, 0x05040a08, 0x015c028c, 0x007c00bc, 0x00000000, // ICON_PENCIL_BIG + 0x01c00000, 0x01400140, 0x01400140, 0x0ff80140, 0x0ff80808, 0x0aa80808, 0x0aa80aa8, 0x00000ff8, // ICON_BRUSH_CLASSIC + 0x1ffc0000, 0x5ffc7ffe, 0x40004000, 0x00807f80, 0x01c001c0, 0x01c001c0, 0x01c001c0, 0x00000080, // ICON_BRUSH_PAINTER + 0x00000000, 0x00800000, 0x01c00080, 0x03e001c0, 0x07f003e0, 0x036006f0, 0x000001c0, 0x00000000, // ICON_WATER_DROP + 0x00000000, 0x3e003800, 0x1f803f80, 0x0c201e40, 0x02080c10, 0x00840104, 0x00380044, 0x00000000, // ICON_COLOR_PICKER + 0x00000000, 0x07800300, 0x1fe00fc0, 0x3f883fd0, 0x0e021f04, 0x02040402, 0x00f00108, 0x00000000, // ICON_RUBBER + 0x00c00000, 0x02800140, 0x08200440, 0x20081010, 0x2ffe3004, 0x03f807fc, 0x00e001f0, 0x00000040, // ICON_COLOR_BUCKET + 0x00000000, 0x21843ffc, 0x01800180, 0x01800180, 0x01800180, 0x01800180, 0x03c00180, 0x00000000, // ICON_TEXT_T + 0x00800000, 0x01400180, 0x06200340, 0x0c100620, 0x1ff80c10, 0x380c1808, 0x70067004, 0x0000f80f, // ICON_TEXT_A + 0x78000000, 0x50004000, 0x00004800, 0x03c003c0, 0x03c003c0, 0x00100000, 0x0002000a, 0x0000000e, // ICON_SCALE + 0x75560000, 0x5e004002, 0x54001002, 0x41001202, 0x408200fe, 0x40820082, 0x40820082, 0x00006afe, // ICON_RESIZE + 0x00000000, 0x3f003f00, 0x3f003f00, 0x3f003f00, 0x00400080, 0x001c0020, 0x001c001c, 0x00000000, // ICON_FILTER_POINT + 0x6d800000, 0x00004080, 0x40804080, 0x40800000, 0x00406d80, 0x001c0020, 0x001c001c, 0x00000000, // ICON_FILTER_BILINEAR + 0x40080000, 0x1ffe2008, 0x14081008, 0x11081208, 0x10481088, 0x10081028, 0x10047ff8, 0x00001002, // ICON_CROP + 0x00100000, 0x3ffc0010, 0x2ab03550, 0x22b02550, 0x20b02150, 0x20302050, 0x2000fff0, 0x00002000, // ICON_CROP_ALPHA + 0x40000000, 0x1ff82000, 0x04082808, 0x01082208, 0x00482088, 0x00182028, 0x35542008, 0x00000002, // ICON_SQUARE_TOGGLE + 0x00000000, 0x02800280, 0x06c006c0, 0x0ea00ee0, 0x1e901eb0, 0x3e883e98, 0x7efc7e8c, 0x00000000, // ICON_SYMMETRY + 0x01000000, 0x05600100, 0x1d480d50, 0x7d423d44, 0x3d447d42, 0x0d501d48, 0x01000560, 0x00000100, // ICON_SYMMETRY_HORIZONTAL + 0x01800000, 0x04200240, 0x10080810, 0x00001ff8, 0x00007ffe, 0x0ff01ff8, 0x03c007e0, 0x00000180, // ICON_SYMMETRY_VERTICAL + 0x00000000, 0x010800f0, 0x02040204, 0x02040204, 0x07f00308, 0x1c000e00, 0x30003800, 0x00000000, // ICON_LENS + 0x00000000, 0x061803f0, 0x08240c0c, 0x08040814, 0x0c0c0804, 0x23f01618, 0x18002400, 0x00000000, // ICON_LENS_BIG + 0x00000000, 0x00000000, 0x1c7007c0, 0x638e3398, 0x1c703398, 0x000007c0, 0x00000000, 0x00000000, // ICON_EYE_ON + 0x00000000, 0x10002000, 0x04700fc0, 0x610e3218, 0x1c703098, 0x001007a0, 0x00000008, 0x00000000, // ICON_EYE_OFF + 0x00000000, 0x00007ffc, 0x40047ffc, 0x10102008, 0x04400820, 0x02800280, 0x02800280, 0x00000100, // ICON_FILTER_TOP + 0x00000000, 0x40027ffe, 0x10082004, 0x04200810, 0x02400240, 0x02400240, 0x01400240, 0x000000c0, // ICON_FILTER + 0x00800000, 0x00800080, 0x00000080, 0x3c9e0000, 0x00000000, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_POINT + 0x00800000, 0x00800080, 0x00800080, 0x3f7e01c0, 0x008001c0, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_SMALL + 0x00800000, 0x00800080, 0x03e00080, 0x3e3e0220, 0x03e00220, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_BIG + 0x01000000, 0x04400280, 0x01000100, 0x43842008, 0x43849ab2, 0x01002008, 0x04400100, 0x01000280, // ICON_TARGET_MOVE + 0x01000000, 0x04400280, 0x01000100, 0x41042108, 0x41049ff2, 0x01002108, 0x04400100, 0x01000280, // ICON_CURSOR_MOVE + 0x781e0000, 0x500a4002, 0x04204812, 0x00000240, 0x02400000, 0x48120420, 0x4002500a, 0x0000781e, // ICON_CURSOR_SCALE + 0x00000000, 0x20003c00, 0x24002800, 0x01000200, 0x00400080, 0x00140024, 0x003c0004, 0x00000000, // ICON_CURSOR_SCALE_RIGHT + 0x00000000, 0x0004003c, 0x00240014, 0x00800040, 0x02000100, 0x28002400, 0x3c002000, 0x00000000, // ICON_CURSOR_SCALE_LEFT + 0x00000000, 0x00100020, 0x10101fc8, 0x10001020, 0x10001000, 0x10001000, 0x00001fc0, 0x00000000, // ICON_UNDO + 0x00000000, 0x08000400, 0x080813f8, 0x00080408, 0x00080008, 0x00080008, 0x000003f8, 0x00000000, // ICON_REDO + 0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, 0x20402000, 0x3f902020, 0x00400020, 0x00000000, // ICON_REREDO + 0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, 0x20202000, 0x3fc82010, 0x00200010, 0x00000000, // ICON_MUTATE + 0x00000000, 0x0ff00000, 0x10081818, 0x11801008, 0x10001180, 0x18101020, 0x00100fc8, 0x00000020, // ICON_ROTATE + 0x00000000, 0x04000200, 0x240429fc, 0x20042204, 0x20442004, 0x3f942024, 0x00400020, 0x00000000, // ICON_REPEAT + 0x00000000, 0x20001000, 0x22104c0e, 0x00801120, 0x11200040, 0x4c0e2210, 0x10002000, 0x00000000, // ICON_SHUFFLE + 0x7ffe0000, 0x50024002, 0x44024802, 0x41024202, 0x40424082, 0x40124022, 0x4002400a, 0x00007ffe, // ICON_EMPTYBOX + 0x00800000, 0x03e00080, 0x08080490, 0x3c9e0808, 0x08080808, 0x03e00490, 0x00800080, 0x00000000, // ICON_TARGET + 0x00800000, 0x00800080, 0x00800080, 0x3ffe01c0, 0x008001c0, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_SMALL_FILL + 0x00800000, 0x00800080, 0x03e00080, 0x3ffe03e0, 0x03e003e0, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_BIG_FILL + 0x01000000, 0x07c00380, 0x01000100, 0x638c2008, 0x638cfbbe, 0x01002008, 0x07c00100, 0x01000380, // ICON_TARGET_MOVE_FILL + 0x01000000, 0x07c00380, 0x01000100, 0x610c2108, 0x610cfffe, 0x01002108, 0x07c00100, 0x01000380, // ICON_CURSOR_MOVE_FILL + 0x781e0000, 0x6006700e, 0x04204812, 0x00000240, 0x02400000, 0x48120420, 0x700e6006, 0x0000781e, // ICON_CURSOR_SCALE_FILL + 0x00000000, 0x38003c00, 0x24003000, 0x01000200, 0x00400080, 0x000c0024, 0x003c001c, 0x00000000, // ICON_CURSOR_SCALE_RIGHT_FILL + 0x00000000, 0x001c003c, 0x0024000c, 0x00800040, 0x02000100, 0x30002400, 0x3c003800, 0x00000000, // ICON_CURSOR_SCALE_LEFT_FILL + 0x00000000, 0x00300020, 0x10301ff8, 0x10001020, 0x10001000, 0x10001000, 0x00001fc0, 0x00000000, // ICON_UNDO_FILL + 0x00000000, 0x0c000400, 0x0c081ff8, 0x00080408, 0x00080008, 0x00080008, 0x000003f8, 0x00000000, // ICON_REDO_FILL + 0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, 0x20402000, 0x3ff02060, 0x00400060, 0x00000000, // ICON_REREDO_FILL + 0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, 0x20202000, 0x3ff82030, 0x00200030, 0x00000000, // ICON_MUTATE_FILL + 0x00000000, 0x0ff00000, 0x10081818, 0x11801008, 0x10001180, 0x18301020, 0x00300ff8, 0x00000020, // ICON_ROTATE_FILL + 0x00000000, 0x06000200, 0x26042ffc, 0x20042204, 0x20442004, 0x3ff42064, 0x00400060, 0x00000000, // ICON_REPEAT_FILL + 0x00000000, 0x30001000, 0x32107c0e, 0x00801120, 0x11200040, 0x7c0e3210, 0x10003000, 0x00000000, // ICON_SHUFFLE_FILL + 0x00000000, 0x30043ffc, 0x24042804, 0x21042204, 0x20442084, 0x20142024, 0x3ffc200c, 0x00000000, // ICON_EMPTYBOX_SMALL + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX + 0x00000000, 0x23c43ffc, 0x23c423c4, 0x200423c4, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_TOP + 0x00000000, 0x3e043ffc, 0x3e043e04, 0x20043e04, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_TOP_RIGHT + 0x00000000, 0x20043ffc, 0x20042004, 0x3e043e04, 0x3e043e04, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_RIGHT + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x3e042004, 0x3e043e04, 0x3ffc3e04, 0x00000000, // ICON_BOX_BOTTOM_RIGHT + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x23c42004, 0x23c423c4, 0x3ffc23c4, 0x00000000, // ICON_BOX_BOTTOM + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x207c2004, 0x207c207c, 0x3ffc207c, 0x00000000, // ICON_BOX_BOTTOM_LEFT + 0x00000000, 0x20043ffc, 0x20042004, 0x207c207c, 0x207c207c, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_LEFT + 0x00000000, 0x207c3ffc, 0x207c207c, 0x2004207c, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_TOP_LEFT + 0x00000000, 0x20043ffc, 0x20042004, 0x23c423c4, 0x23c423c4, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_CENTER + 0x7ffe0000, 0x40024002, 0x47e24182, 0x4ff247e2, 0x47e24ff2, 0x418247e2, 0x40024002, 0x00007ffe, // ICON_BOX_CIRCLE_MASK + 0x7fff0000, 0x40014001, 0x40014001, 0x49555ddd, 0x4945495d, 0x400149c5, 0x40014001, 0x00007fff, // ICON_POT + 0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, 0x404e40ce, 0x48125432, 0x4006540e, 0x00007ffe, // ICON_ALPHA_MULTIPLY + 0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, 0x5c4e40ce, 0x44124432, 0x40065c0e, 0x00007ffe, // ICON_ALPHA_CLEAR + 0x7ffe0000, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x00007ffe, // ICON_DITHERING + 0x07fe0000, 0x1ffa0002, 0x7fea000a, 0x402a402a, 0x5b2a512a, 0x5128552a, 0x40205128, 0x00007fe0, // ICON_MIPMAPS + 0x00000000, 0x1ff80000, 0x12481248, 0x12481ff8, 0x1ff81248, 0x12481248, 0x00001ff8, 0x00000000, // ICON_BOX_GRID + 0x12480000, 0x7ffe1248, 0x12481248, 0x12487ffe, 0x7ffe1248, 0x12481248, 0x12487ffe, 0x00001248, // ICON_GRID + 0x00000000, 0x1c380000, 0x1c3817e8, 0x08100810, 0x08100810, 0x17e81c38, 0x00001c38, 0x00000000, // ICON_BOX_CORNERS_SMALL + 0x700e0000, 0x700e5ffa, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x5ffa700e, 0x0000700e, // ICON_BOX_CORNERS_BIG + 0x3f7e0000, 0x21422142, 0x21422142, 0x00003f7e, 0x21423f7e, 0x21422142, 0x3f7e2142, 0x00000000, // ICON_FOUR_BOXES + 0x00000000, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x00000000, // ICON_GRID_FILL + 0x7ffe0000, 0x7ffe7ffe, 0x77fe7000, 0x77fe77fe, 0x777e7700, 0x777e777e, 0x777e777e, 0x0000777e, // ICON_BOX_MULTISIZE + 0x781e0000, 0x40024002, 0x00004002, 0x01800000, 0x00000180, 0x40020000, 0x40024002, 0x0000781e, // ICON_ZOOM_SMALL + 0x781e0000, 0x40024002, 0x00004002, 0x03c003c0, 0x03c003c0, 0x40020000, 0x40024002, 0x0000781e, // ICON_ZOOM_MEDIUM + 0x781e0000, 0x40024002, 0x07e04002, 0x07e007e0, 0x07e007e0, 0x400207e0, 0x40024002, 0x0000781e, // ICON_ZOOM_BIG + 0x781e0000, 0x5ffa4002, 0x1ff85ffa, 0x1ff81ff8, 0x1ff81ff8, 0x5ffa1ff8, 0x40025ffa, 0x0000781e, // ICON_ZOOM_ALL + 0x00000000, 0x2004381c, 0x00002004, 0x00000000, 0x00000000, 0x20040000, 0x381c2004, 0x00000000, // ICON_ZOOM_CENTER + 0x00000000, 0x1db80000, 0x10081008, 0x10080000, 0x00001008, 0x10081008, 0x00001db8, 0x00000000, // ICON_BOX_DOTS_SMALL + 0x35560000, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x35562002, 0x00000000, // ICON_BOX_DOTS_BIG + 0x7ffe0000, 0x40024002, 0x48124ff2, 0x49924812, 0x48124992, 0x4ff24812, 0x40024002, 0x00007ffe, // ICON_BOX_CONCENTRIC + 0x00000000, 0x10841ffc, 0x10841084, 0x1ffc1084, 0x10841084, 0x10841084, 0x00001ffc, 0x00000000, // ICON_BOX_GRID_BIG + 0x00000000, 0x00000000, 0x10000000, 0x04000800, 0x01040200, 0x00500088, 0x00000020, 0x00000000, // ICON_OK_TICK + 0x00000000, 0x10080000, 0x04200810, 0x01800240, 0x02400180, 0x08100420, 0x00001008, 0x00000000, // ICON_CROSS + 0x00000000, 0x02000000, 0x00800100, 0x00200040, 0x00200010, 0x00800040, 0x02000100, 0x00000000, // ICON_ARROW_LEFT + 0x00000000, 0x00400000, 0x01000080, 0x04000200, 0x04000800, 0x01000200, 0x00400080, 0x00000000, // ICON_ARROW_RIGHT + 0x00000000, 0x00000000, 0x00000000, 0x08081004, 0x02200410, 0x00800140, 0x00000000, 0x00000000, // ICON_ARROW_DOWN + 0x00000000, 0x00000000, 0x01400080, 0x04100220, 0x10040808, 0x00000000, 0x00000000, 0x00000000, // ICON_ARROW_UP + 0x00000000, 0x02000000, 0x03800300, 0x03e003c0, 0x03e003f0, 0x038003c0, 0x02000300, 0x00000000, // ICON_ARROW_LEFT_FILL + 0x00000000, 0x00400000, 0x01c000c0, 0x07c003c0, 0x07c00fc0, 0x01c003c0, 0x004000c0, 0x00000000, // ICON_ARROW_RIGHT_FILL + 0x00000000, 0x00000000, 0x00000000, 0x0ff81ffc, 0x03e007f0, 0x008001c0, 0x00000000, 0x00000000, // ICON_ARROW_DOWN_FILL + 0x00000000, 0x00000000, 0x01c00080, 0x07f003e0, 0x1ffc0ff8, 0x00000000, 0x00000000, 0x00000000, // ICON_ARROW_UP_FILL + 0x00000000, 0x18a008c0, 0x32881290, 0x24822686, 0x26862482, 0x12903288, 0x08c018a0, 0x00000000, // ICON_AUDIO + 0x00000000, 0x04800780, 0x004000c0, 0x662000f0, 0x08103c30, 0x130a0e18, 0x0000318e, 0x00000000, // ICON_FX + 0x00000000, 0x00800000, 0x08880888, 0x2aaa0a8a, 0x0a8a2aaa, 0x08880888, 0x00000080, 0x00000000, // ICON_WAVE + 0x00000000, 0x00600000, 0x01080090, 0x02040108, 0x42044204, 0x24022402, 0x00001800, 0x00000000, // ICON_WAVE_SINUS + 0x00000000, 0x07f80000, 0x04080408, 0x04080408, 0x04080408, 0x7c0e0408, 0x00000000, 0x00000000, // ICON_WAVE_SQUARE + 0x00000000, 0x00000000, 0x00a00040, 0x22084110, 0x08021404, 0x00000000, 0x00000000, 0x00000000, // ICON_WAVE_TRIANGULAR + 0x00000000, 0x00000000, 0x04200000, 0x01800240, 0x02400180, 0x00000420, 0x00000000, 0x00000000, // ICON_CROSS_SMALL + 0x00000000, 0x18380000, 0x12281428, 0x10a81128, 0x112810a8, 0x14281228, 0x00001838, 0x00000000, // ICON_PLAYER_PREVIOUS + 0x00000000, 0x18000000, 0x11801600, 0x10181060, 0x10601018, 0x16001180, 0x00001800, 0x00000000, // ICON_PLAYER_PLAY_BACK + 0x00000000, 0x00180000, 0x01880068, 0x18080608, 0x06081808, 0x00680188, 0x00000018, 0x00000000, // ICON_PLAYER_PLAY + 0x00000000, 0x1e780000, 0x12481248, 0x12481248, 0x12481248, 0x12481248, 0x00001e78, 0x00000000, // ICON_PLAYER_PAUSE + 0x00000000, 0x1ff80000, 0x10081008, 0x10081008, 0x10081008, 0x10081008, 0x00001ff8, 0x00000000, // ICON_PLAYER_STOP + 0x00000000, 0x1c180000, 0x14481428, 0x15081488, 0x14881508, 0x14281448, 0x00001c18, 0x00000000, // ICON_PLAYER_NEXT + 0x00000000, 0x03c00000, 0x08100420, 0x10081008, 0x10081008, 0x04200810, 0x000003c0, 0x00000000, // ICON_PLAYER_RECORD + 0x00000000, 0x0c3007e0, 0x13c81818, 0x14281668, 0x14281428, 0x1c381c38, 0x08102244, 0x00000000, // ICON_MAGNET + 0x07c00000, 0x08200820, 0x3ff80820, 0x23882008, 0x21082388, 0x20082108, 0x1ff02008, 0x00000000, // ICON_LOCK_CLOSE + 0x07c00000, 0x08000800, 0x3ff80800, 0x23882008, 0x21082388, 0x20082108, 0x1ff02008, 0x00000000, // ICON_LOCK_OPEN + 0x01c00000, 0x0c180770, 0x3086188c, 0x60832082, 0x60034781, 0x30062002, 0x0c18180c, 0x01c00770, // ICON_CLOCK + 0x0a200000, 0x1b201b20, 0x04200e20, 0x04200420, 0x04700420, 0x0e700e70, 0x0e700e70, 0x04200e70, // ICON_TOOLS + 0x01800000, 0x3bdc318c, 0x0ff01ff8, 0x7c3e1e78, 0x1e787c3e, 0x1ff80ff0, 0x318c3bdc, 0x00000180, // ICON_GEAR + 0x01800000, 0x3ffc318c, 0x1c381ff8, 0x781e1818, 0x1818781e, 0x1ff81c38, 0x318c3ffc, 0x00000180, // ICON_GEAR_BIG + 0x00000000, 0x08080ff8, 0x08081ffc, 0x0aa80aa8, 0x0aa80aa8, 0x0aa80aa8, 0x08080aa8, 0x00000ff8, // ICON_BIN + 0x00000000, 0x00000000, 0x20043ffc, 0x08043f84, 0x04040f84, 0x04040784, 0x000007fc, 0x00000000, // ICON_HAND_POINTER + 0x00000000, 0x24400400, 0x00001480, 0x6efe0e00, 0x00000e00, 0x24401480, 0x00000400, 0x00000000, // ICON_LASER + 0x00000000, 0x03c00000, 0x08300460, 0x11181118, 0x11181118, 0x04600830, 0x000003c0, 0x00000000, // ICON_COIN + 0x00000000, 0x10880080, 0x06c00810, 0x366c07e0, 0x07e00240, 0x00001768, 0x04200240, 0x00000000, // ICON_EXPLOSION + 0x00000000, 0x3d280000, 0x2528252c, 0x3d282528, 0x05280528, 0x05e80528, 0x00000000, 0x00000000, // ICON_1UP + 0x01800000, 0x03c003c0, 0x018003c0, 0x0ff007e0, 0x0bd00bd0, 0x0a500bd0, 0x02400240, 0x02400240, // ICON_PLAYER + 0x01800000, 0x03c003c0, 0x118013c0, 0x03c81ff8, 0x07c003c8, 0x04400440, 0x0c080478, 0x00000000, // ICON_PLAYER_JUMP + 0x3ff80000, 0x30183ff8, 0x30183018, 0x3ff83ff8, 0x03000300, 0x03c003c0, 0x03e00300, 0x000003e0, // ICON_KEY + 0x3ff80000, 0x3ff83ff8, 0x33983ff8, 0x3ff83398, 0x3ff83ff8, 0x00000540, 0x0fe00aa0, 0x00000fe0, // ICON_DEMON + 0x00000000, 0x0ff00000, 0x20041008, 0x25442004, 0x10082004, 0x06000bf0, 0x00000300, 0x00000000, // ICON_TEXT_POPUP + 0x00000000, 0x11440000, 0x07f00be8, 0x1c1c0e38, 0x1c1c0c18, 0x07f00e38, 0x11440be8, 0x00000000, // ICON_GEAR_EX + 0x00000000, 0x20080000, 0x0c601010, 0x07c00fe0, 0x07c007c0, 0x0c600fe0, 0x20081010, 0x00000000, // ICON_CRACK + 0x00000000, 0x20080000, 0x0c601010, 0x04400fe0, 0x04405554, 0x0c600fe0, 0x20081010, 0x00000000, // ICON_CRACK_POINTS + 0x00000000, 0x00800080, 0x01c001c0, 0x1ffc3ffe, 0x03e007f0, 0x07f003e0, 0x0c180770, 0x00000808, // ICON_STAR + 0x0ff00000, 0x08180810, 0x08100818, 0x0a100810, 0x08180810, 0x08100818, 0x08100810, 0x00001ff8, // ICON_DOOR + 0x0ff00000, 0x08100810, 0x08100810, 0x10100010, 0x4f902010, 0x10102010, 0x08100010, 0x00000ff0, // ICON_EXIT + 0x00040000, 0x001f000e, 0x0ef40004, 0x12f41284, 0x0ef41214, 0x10040004, 0x7ffc3004, 0x10003000, // ICON_MODE_2D + 0x78040000, 0x501f600e, 0x0ef44004, 0x12f41284, 0x0ef41284, 0x10140004, 0x7ffc300c, 0x10003000, // ICON_MODE_3D + 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe, // ICON_CUBE + 0x7fe00000, 0x5ff87ff0, 0x47fe4ffc, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe, // ICON_CUBE_FACE_TOP + 0x7fe00000, 0x50386030, 0x47c2483c, 0x443e443e, 0x443e443e, 0x241e75fe, 0x0c06140e, 0x000007fe, // ICON_CUBE_FACE_LEFT + 0x7fe00000, 0x50286030, 0x47fe4804, 0x47fe47fe, 0x47fe47fe, 0x27fe77fe, 0x0ffe17fe, 0x000007fe, // ICON_CUBE_FACE_FRONT + 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x3bf27be2, 0x0bfe1bfa, 0x000007fe, // ICON_CUBE_FACE_BOTTOM + 0x7fe00000, 0x70286030, 0x7ffe7804, 0x7c227c02, 0x7c227c22, 0x3c127de2, 0x0c061c0a, 0x000007fe, // ICON_CUBE_FACE_RIGHT + 0x7fe00000, 0x6fe85ff0, 0x781e77e4, 0x7be27be2, 0x7be27be2, 0x24127be2, 0x0c06140a, 0x000007fe, // ICON_CUBE_FACE_BACK + 0x00000000, 0x2a0233fe, 0x22022602, 0x22022202, 0x2a022602, 0x00a033fe, 0x02080110, 0x00000000, // ICON_CAMERA + 0x00000000, 0x200c3ffc, 0x000c000c, 0x3ffc000c, 0x30003000, 0x30003000, 0x3ffc3004, 0x00000000, // ICON_SPECIAL + 0x00000000, 0x0022003e, 0x012201e2, 0x0100013e, 0x01000100, 0x79000100, 0x4f004900, 0x00007800, // ICON_LINK_NET + 0x00000000, 0x44007c00, 0x45004600, 0x00627cbe, 0x00620022, 0x45007cbe, 0x44004600, 0x00007c00, // ICON_LINK_BOXES + 0x00000000, 0x0044007c, 0x0010007c, 0x3f100010, 0x3f1021f0, 0x3f100010, 0x3f0021f0, 0x00000000, // ICON_LINK_MULTI + 0x00000000, 0x0044007c, 0x00440044, 0x0010007c, 0x00100010, 0x44107c10, 0x440047f0, 0x00007c00, // ICON_LINK + 0x00000000, 0x0044007c, 0x00440044, 0x0000007c, 0x00000010, 0x44007c10, 0x44004550, 0x00007c00, // ICON_LINK_BROKE + 0x02a00000, 0x22a43ffc, 0x20042004, 0x20042ff4, 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc, // ICON_TEXT_NOTES + 0x3ffc0000, 0x20042004, 0x245e27c4, 0x27c42444, 0x2004201e, 0x201e2004, 0x20042004, 0x00003ffc, // ICON_NOTEBOOK + 0x00000000, 0x07e00000, 0x04200420, 0x24243ffc, 0x24242424, 0x24242424, 0x3ffc2424, 0x00000000, // ICON_SUITCASE + 0x00000000, 0x0fe00000, 0x08200820, 0x40047ffc, 0x7ffc5554, 0x40045554, 0x7ffc4004, 0x00000000, // ICON_SUITCASE_ZIP + 0x00000000, 0x20043ffc, 0x3ffc2004, 0x13c81008, 0x100813c8, 0x10081008, 0x1ff81008, 0x00000000, // ICON_MAILBOX + 0x00000000, 0x40027ffe, 0x5ffa5ffa, 0x5ffa5ffa, 0x40025ffa, 0x03c07ffe, 0x1ff81ff8, 0x00000000, // ICON_MONITOR + 0x0ff00000, 0x6bfe7ffe, 0x7ffe7ffe, 0x68167ffe, 0x08106816, 0x08100810, 0x0ff00810, 0x00000000, // ICON_PRINTER + 0x3ff80000, 0xfffe2008, 0x870a8002, 0x904a888a, 0x904a904a, 0x870a888a, 0xfffe8002, 0x00000000, // ICON_PHOTO_CAMERA + 0x0fc00000, 0xfcfe0cd8, 0x8002fffe, 0x84428382, 0x84428442, 0x80028382, 0xfffe8002, 0x00000000, // ICON_PHOTO_CAMERA_FLASH + 0x00000000, 0x02400180, 0x08100420, 0x20041008, 0x23c42004, 0x22442244, 0x3ffc2244, 0x00000000, // ICON_HOUSE + 0x00000000, 0x1c700000, 0x3ff83ef8, 0x3ff83ff8, 0x0fe01ff0, 0x038007c0, 0x00000100, 0x00000000, // ICON_HEART + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x80000000, 0xe000c000, // ICON_CORNER + 0x00000000, 0x14001c00, 0x15c01400, 0x15401540, 0x155c1540, 0x15541554, 0x1ddc1554, 0x00000000, // ICON_VERTICAL_BARS + 0x00000000, 0x03000300, 0x1b001b00, 0x1b601b60, 0x1b6c1b60, 0x1b6c1b6c, 0x1b6c1b6c, 0x00000000, // ICON_VERTICAL_BARS_FILL + 0x00000000, 0x00000000, 0x403e7ffe, 0x7ffe403e, 0x7ffe0000, 0x43fe43fe, 0x00007ffe, 0x00000000, // ICON_LIFE_BARS + 0x7ffc0000, 0x43844004, 0x43844284, 0x43844004, 0x42844284, 0x42844284, 0x40044384, 0x00007ffc, // ICON_INFO + 0x40008000, 0x10002000, 0x04000800, 0x01000200, 0x00400080, 0x00100020, 0x00040008, 0x00010002, // ICON_CROSSLINE + 0x00000000, 0x1ff01ff0, 0x18301830, 0x1f001830, 0x03001f00, 0x00000300, 0x03000300, 0x00000000, // ICON_HELP + 0x3ff00000, 0x2abc3550, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x00003ffc, // ICON_FILETYPE_ALPHA + 0x3ff00000, 0x201c2010, 0x22442184, 0x28142424, 0x29942814, 0x2ff42994, 0x20042004, 0x00003ffc, // ICON_FILETYPE_HOME + 0x07fe0000, 0x04020402, 0x7fe20402, 0x44224422, 0x44224422, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_LAYERS_VISIBLE + 0x07fe0000, 0x04020402, 0x7c020402, 0x44024402, 0x44024402, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_LAYERS + 0x00000000, 0x40027ffe, 0x7ffe4002, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000, // ICON_WINDOW + 0x09100000, 0x09f00910, 0x09100910, 0x00000910, 0x24a2779e, 0x27a224a2, 0x709e20a2, 0x00000000, // ICON_HIDPI + 0x3ff00000, 0x201c2010, 0x2a842e84, 0x2e842a84, 0x2ba42004, 0x2aa42aa4, 0x20042ba4, 0x00003ffc, // ICON_FILETYPE_BINARY + 0x00000000, 0x00000000, 0x00120012, 0x4a5e4bd2, 0x485233d2, 0x00004bd2, 0x00000000, 0x00000000, // ICON_HEX + 0x01800000, 0x381c0660, 0x23c42004, 0x23c42044, 0x13c82204, 0x08101008, 0x02400420, 0x00000180, // ICON_SHIELD + 0x007e0000, 0x20023fc2, 0x40227fe2, 0x400a403a, 0x400a400a, 0x400a400a, 0x4008400e, 0x00007ff8, // ICON_FILE_NEW + 0x00000000, 0x0042007e, 0x40027fc2, 0x44024002, 0x5f024402, 0x44024402, 0x7ffe4002, 0x00000000, // ICON_FOLDER_ADD + 0x44220000, 0x12482244, 0xf3cf0000, 0x14280420, 0x48122424, 0x08100810, 0x1ff81008, 0x03c00420, // ICON_ALARM + 0x0aa00000, 0x1ff80aa0, 0x1068700e, 0x1008706e, 0x1008700e, 0x1008700e, 0x0aa01ff8, 0x00000aa0, // ICON_CPU + 0x07e00000, 0x04201db8, 0x04a01c38, 0x04a01d38, 0x04a01d38, 0x04a01d38, 0x04201d38, 0x000007e0, // ICON_ROM + 0x00000000, 0x03c00000, 0x3c382ff0, 0x3c04380c, 0x01800000, 0x03c003c0, 0x00000180, 0x00000000, // ICON_STEP_OVER + 0x01800000, 0x01800180, 0x01800180, 0x03c007e0, 0x00000180, 0x01800000, 0x03c003c0, 0x00000180, // ICON_STEP_INTO + 0x01800000, 0x07e003c0, 0x01800180, 0x01800180, 0x00000180, 0x01800000, 0x03c003c0, 0x00000180, // ICON_STEP_OUT + 0x00000000, 0x0ff003c0, 0x181c1c34, 0x303c301c, 0x30003000, 0x1c301800, 0x03c00ff0, 0x00000000, // ICON_RESTART + 0x00000000, 0x00000000, 0x07e003c0, 0x0ff00ff0, 0x0ff00ff0, 0x03c007e0, 0x00000000, 0x00000000, // ICON_BREAKPOINT_ON + 0x00000000, 0x00000000, 0x042003c0, 0x08100810, 0x08100810, 0x03c00420, 0x00000000, 0x00000000, // ICON_BREAKPOINT_OFF + 0x00000000, 0x00000000, 0x1ff81ff8, 0x1ff80000, 0x00001ff8, 0x1ff81ff8, 0x00000000, 0x00000000, // ICON_BURGER_MENU + 0x00000000, 0x00000000, 0x00880070, 0x0c880088, 0x1e8810f8, 0x3e881288, 0x00000000, 0x00000000, // ICON_CASE_SENSITIVE + 0x00000000, 0x02000000, 0x07000a80, 0x07001fc0, 0x02000a80, 0x00300030, 0x00000000, 0x00000000, // ICON_REG_EXP + 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000, // ICON_FOLDER + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x00003ffc, // ICON_FILE + 0x1ff00000, 0x20082008, 0x17d02fe8, 0x05400ba0, 0x09200540, 0x23881010, 0x2fe827c8, 0x00001ff0, // ICON_SAND_TIMER + 0x01800000, 0x02400240, 0x05a00420, 0x09900990, 0x11881188, 0x21842004, 0x40024182, 0x00003ffc, // ICON_WARNING + 0x7ffe0000, 0x4ff24002, 0x4c324ff2, 0x4f824c02, 0x41824f82, 0x41824002, 0x40024182, 0x00007ffe, // ICON_HELP_BOX + 0x7ffe0000, 0x41824002, 0x40024182, 0x41824182, 0x41824182, 0x41824182, 0x40024182, 0x00007ffe, // ICON_INFO_BOX + 0x01800000, 0x04200240, 0x10080810, 0x7bde2004, 0x0a500a50, 0x08500bd0, 0x08100850, 0x00000ff0, // ICON_PRIORITY + 0x01800000, 0x18180660, 0x80016006, 0x98196006, 0x99996666, 0x19986666, 0x01800660, 0x00000000, // ICON_LAYERS_ISO + 0x07fe0000, 0x1c020402, 0x74021402, 0x54025402, 0x54025402, 0x500857fe, 0x40205ff8, 0x00007fe0, // ICON_LAYERS2 + 0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x422a422a, 0x422e422a, 0x40384e28, 0x00007fe0, // ICON_MLAYERS + 0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x5b2a512a, 0x512e552a, 0x40385128, 0x00007fe0, // ICON_MAPS + 0x04200000, 0x1cf00c60, 0x11f019f0, 0x0f3807b8, 0x1e3c0f3c, 0x1c1c1e1c, 0x1e3c1c1c, 0x00000f70, // ICON_HOT + 0x00000000, 0x20803f00, 0x2a202e40, 0x20082e10, 0x08021004, 0x02040402, 0x00900108, 0x00000060, // ICON_LABEL + 0x00000000, 0x042007e0, 0x47e27c3e, 0x4ffa4002, 0x47fa4002, 0x4ffa4002, 0x7ffe4002, 0x00000000, // ICON_NAME_ID + 0x7fe00000, 0x402e4020, 0x43ce5e0a, 0x40504078, 0x438e4078, 0x402e5e0a, 0x7fe04020, 0x00000000, // ICON_SLICING + 0x00000000, 0x40027ffe, 0x47c24002, 0x55425d42, 0x55725542, 0x50125552, 0x10105016, 0x00001ff0, // ICON_MANUAL_CONTROL + 0x7ffe0000, 0x43c24002, 0x48124422, 0x500a500a, 0x500a500a, 0x44224812, 0x400243c2, 0x00007ffe, // ICON_COLLISION + 0x03c00000, 0x10080c30, 0x21842184, 0x4ff24182, 0x41824ff2, 0x21842184, 0x0c301008, 0x000003c0, // ICON_CIRCLE_ADD + 0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x700e7e7e, 0x7e7e700e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0, // ICON_CIRCLE_ADD_FILL + 0x03c00000, 0x10080c30, 0x21842184, 0x41824182, 0x40024182, 0x21842184, 0x0c301008, 0x000003c0, // ICON_CIRCLE_WARNING + 0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x7e7e7e7e, 0x7ffe7e7e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0, // ICON_CIRCLE_WARNING_FILL + 0x00000000, 0x10041ffc, 0x10841004, 0x13e41084, 0x10841084, 0x10041004, 0x00001ffc, 0x00000000, // ICON_BOX_MORE + 0x00000000, 0x1ffc1ffc, 0x1f7c1ffc, 0x1c1c1f7c, 0x1f7c1f7c, 0x1ffc1ffc, 0x00001ffc, 0x00000000, // ICON_BOX_MORE_FILL + 0x00000000, 0x1ffc1ffc, 0x1ffc1ffc, 0x1c1c1ffc, 0x1ffc1ffc, 0x1ffc1ffc, 0x00001ffc, 0x00000000, // ICON_BOX_MINUS + 0x00000000, 0x10041ffc, 0x10041004, 0x13e41004, 0x10041004, 0x10041004, 0x00001ffc, 0x00000000, // ICON_BOX_MINUS_FILL + 0x07fe0000, 0x055606aa, 0x7ff606aa, 0x55766eba, 0x55766eaa, 0x55606ffe, 0x55606aa0, 0x00007fe0, // ICON_UNION + 0x07fe0000, 0x04020402, 0x7fe20402, 0x456246a2, 0x456246a2, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_INTERSECTION + 0x07fe0000, 0x055606aa, 0x7ff606aa, 0x4436442a, 0x4436442a, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_DIFFERENCE + 0x03c00000, 0x10080c30, 0x20042004, 0x60064002, 0x47e2581a, 0x20042004, 0x0c301008, 0x000003c0, // ICON_SPHERE + 0x03e00000, 0x08080410, 0x0c180808, 0x08080be8, 0x08080808, 0x08080808, 0x04100808, 0x000003e0, // ICON_CYLINDER + 0x00800000, 0x01400140, 0x02200220, 0x04100410, 0x08080808, 0x1c1c13e4, 0x08081004, 0x000007f0, // ICON_CONE + 0x00000000, 0x07e00000, 0x20841918, 0x40824082, 0x40824082, 0x19182084, 0x000007e0, 0x00000000, // ICON_ELLIPSOID + 0x00000000, 0x00000000, 0x20041ff8, 0x40024002, 0x40024002, 0x1ff82004, 0x00000000, 0x00000000, // ICON_CAPSULE + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_250 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_251 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_252 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_253 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_254 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_255 +}; + +// NOTE: A pointer to current icons array should be defined +static unsigned int *guiIconsPtr = guiIcons; + +#endif // !RAYGUI_NO_ICONS && !RAYGUI_CUSTOM_ICONS + +#ifndef RAYGUI_ICON_SIZE + #define RAYGUI_ICON_SIZE 0 +#endif + +// WARNING: Those values define the total size of the style data array, +// if changed, previous saved styles could become incompatible +#define RAYGUI_MAX_CONTROLS 16 // Maximum number of controls +#define RAYGUI_MAX_PROPS_BASE 16 // Maximum number of base properties +#define RAYGUI_MAX_PROPS_EXTENDED 8 // Maximum number of extended properties + +//---------------------------------------------------------------------------------- +// Module Types and Structures Definition +//---------------------------------------------------------------------------------- +// Gui control property style color element +typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static GuiState guiState = STATE_NORMAL; // Gui global state, if !STATE_NORMAL, forces defined state + +static Font guiFont = { 0 }; // Gui current font (WARNING: highly coupled to raylib) +static bool guiLocked = false; // Gui lock state (no inputs processed) +static float guiAlpha = 1.0f; // Gui controls transparency + +static unsigned int guiIconScale = 1; // Gui icon default scale (if icons enabled) + +static bool guiTooltip = false; // Tooltip enabled/disabled +static const char *guiTooltipPtr = NULL; // Tooltip string pointer (string provided by user) + +static bool guiControlExclusiveMode = false; // Gui control exclusive mode (no inputs processed except current control) +static Rectangle guiControlExclusiveRec = { 0 }; // Gui control exclusive bounds rectangle, used as an unique identifier + +static int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*() +//static int blinkCursorFrameCounter = 0; // Frame counter for cursor blinking +static int autoCursorCounter = 0; // Frame counter for automatic repeated cursor movement on key-down (cooldown and delay) + +//---------------------------------------------------------------------------------- +// Style data array for all gui style properties (allocated on data segment by default) +// +// NOTE 1: First set of BASE properties are generic to all controls but could be individually +// overwritten per control, first set of EXTENDED properties are generic to all controls and +// can not be overwritten individually but custom EXTENDED properties can be used by control +// +// NOTE 2: A new style set could be loaded over this array using GuiLoadStyle(), +// but default gui style could always be recovered with GuiLoadStyleDefault() +// +// guiStyle size is by default: 16*(16 + 8) = 384*4 = 1536 bytes = 1.5 KB +//---------------------------------------------------------------------------------- +static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)] = { 0 }; + +static bool guiStyleLoaded = false; // Style loaded flag for lazy style initialization + +//---------------------------------------------------------------------------------- +// Standalone Mode Functions Declaration +// +// NOTE: raygui depend on some raylib input and drawing functions +// To use raygui as standalone library, below functions must be defined by the user +//---------------------------------------------------------------------------------- +#if defined(RAYGUI_STANDALONE) + +#define KEY_RIGHT 262 +#define KEY_LEFT 263 +#define KEY_DOWN 264 +#define KEY_UP 265 +#define KEY_BACKSPACE 259 +#define KEY_ENTER 257 + +#define MOUSE_LEFT_BUTTON 0 + +// Input required functions +//------------------------------------------------------------------------------- +static Vector2 GetMousePosition(void); +static float GetMouseWheelMove(void); +static bool IsMouseButtonDown(int button); +static bool IsMouseButtonPressed(int button); +static bool IsMouseButtonReleased(int button); + +static bool IsKeyDown(int key); +static bool IsKeyPressed(int key); +static int GetCharPressed(void); // -- GuiTextBox(), GuiValueBox() +//------------------------------------------------------------------------------- + +// Drawing required functions +//------------------------------------------------------------------------------- +static void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle() +static void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker() +//------------------------------------------------------------------------------- + +// Text required functions +//------------------------------------------------------------------------------- +static Font GetFontDefault(void); // -- GuiLoadStyleDefault() +static Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // -- GuiLoadStyle(), load font + +static Texture2D LoadTextureFromImage(Image image); // -- GuiLoadStyle(), required to load texture from embedded font atlas image +static void SetShapesTexture(Texture2D tex, Rectangle rec); // -- GuiLoadStyle(), required to set shapes rec to font white rec (optimization) + +static char *LoadFileText(const char *fileName); // -- GuiLoadStyle(), required to load charset data +static void UnloadFileText(char *text); // -- GuiLoadStyle(), required to unload charset data + +static const char *GetDirectoryPath(const char *filePath); // -- GuiLoadStyle(), required to find charset/font file from text .rgs + +static int *LoadCodepoints(const char *text, int *count); // -- GuiLoadStyle(), required to load required font codepoints list +static void UnloadCodepoints(int *codepoints); // -- GuiLoadStyle(), required to unload codepoints list + +static unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // -- GuiLoadStyle() +//------------------------------------------------------------------------------- + +// raylib functions already implemented in raygui +//------------------------------------------------------------------------------- +static Color GetColor(int hexValue); // Returns a Color struct from hexadecimal value +static int ColorToInt(Color color); // Returns hexadecimal value for a Color +static bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle +static const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' +static char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings +static int TextToInteger(const char *text); // Get integer value from text +static float TextToFloat(const char *text); // Get float value from text + +static int GetCodepointNext(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded text +static const char *CodepointToUTF8(int codepoint, int *byteSize); // Encode codepoint into UTF-8 text (char array size returned as parameter) + +static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2); // Draw rectangle vertical gradient +//------------------------------------------------------------------------------- + +#endif // RAYGUI_STANDALONE + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize); // Load style from memory (binary only) + +static Rectangle GetTextBounds(int control, Rectangle bounds); // Get text bounds considering control bounds +static const char *GetTextIcon(const char *text, int *iconId); // Get text icon if provided and move text cursor + +static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, Color tint); // Gui draw text using default font +static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color); // Gui draw rectangle using default raygui style + +static char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow); // Split controls text into multiple strings +static Vector3 ConvertHSVtoRGB(Vector3 hsv); // Convert color data from HSV to RGB +static Vector3 ConvertRGBtoHSV(Vector3 rgb); // Convert color data from RGB to HSV + +static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue); // Scroll bar control, used by GuiScrollPanel() +static void GuiTooltip(Rectangle controlRec); // Draw tooltip using control rec position + +static Color GuiFade(Color color, float alpha); // Fade color by an alpha factor + +//---------------------------------------------------------------------------------- +// Gui Setup Functions Definition +//---------------------------------------------------------------------------------- +// Enable gui global state +// NOTE: Checking for STATE_DISABLED to avoid messing custom global state setups +void GuiEnable(void) { if (guiState == STATE_DISABLED) guiState = STATE_NORMAL; } + +// Disable gui global state +// NOTE: Checking for STATE_NORMAL to avoid messing custom global state setups +void GuiDisable(void) { if (guiState == STATE_NORMAL) guiState = STATE_DISABLED; } + +// Lock gui global state +void GuiLock(void) { guiLocked = true; } + +// Unlock gui global state +void GuiUnlock(void) { guiLocked = false; } + +// Check if gui is locked (global state) +bool GuiIsLocked(void) { return guiLocked; } + +// Set gui controls alpha global state +void GuiSetAlpha(float alpha) +{ + if (alpha < 0.0f) alpha = 0.0f; + else if (alpha > 1.0f) alpha = 1.0f; + + guiAlpha = alpha; +} + +// Set gui state (global state) +void GuiSetState(int state) { guiState = (GuiState)state; } + +// Get gui state (global state) +int GuiGetState(void) { return guiState; } + +// Set custom gui font +// NOTE: Font loading/unloading is external to raygui +void GuiSetFont(Font font) +{ + if (font.texture.id > 0) + { + // NOTE: If a font is tried to be set but default style has not been lazily loaded first, + // it will be overwritten, so default style loading needs to be forced first + if (!guiStyleLoaded) GuiLoadStyleDefault(); + + guiFont = font; + } +} + +// Get custom gui font +Font GuiGetFont(void) +{ + return guiFont; +} + +// Set control style property value +void GuiSetStyle(int control, int property, int value) +{ + if (!guiStyleLoaded) GuiLoadStyleDefault(); + guiStyle[control*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property] = value; + + // Default properties are propagated to all controls + if ((control == 0) && (property < RAYGUI_MAX_PROPS_BASE)) + { + for (int i = 1; i < RAYGUI_MAX_CONTROLS; i++) guiStyle[i*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property] = value; + } +} + +// Get control style property value +int GuiGetStyle(int control, int property) +{ + if (!guiStyleLoaded) GuiLoadStyleDefault(); + return guiStyle[control*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property]; +} + +//---------------------------------------------------------------------------------- +// Gui Controls Functions Definition +//---------------------------------------------------------------------------------- + +// Window Box control +int GuiWindowBox(Rectangle bounds, const char *title) +{ + // Window title bar height (including borders) + // NOTE: This define is also used by GuiMessageBox() and GuiTextInputBox() + #if !defined(RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT) + #define RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT 24 + #endif + + #if !defined(RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT) + #define RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT 18 + #endif + + int result = 0; + //GuiState state = guiState; + + int statusBarHeight = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT; + int statusBorderWidth = GuiGetStyle(STATUSBAR, BORDER_WIDTH); + + Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)statusBarHeight }; + if (bounds.height < statusBarHeight*2.0f) bounds.height = statusBarHeight*2.0f; + + const float vPadding = statusBarHeight/2.0f - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT/2.0f; + Rectangle windowPanel = { bounds.x, bounds.y + (float)statusBarHeight - (float)statusBorderWidth, bounds.width, bounds.height - (float)statusBarHeight + (float)statusBorderWidth }; + Rectangle closeButtonRec = { statusBar.x + statusBar.width - (float)statusBorderWidth - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT - vPadding, + statusBar.y + vPadding, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT }; + + // Update control + //-------------------------------------------------------------------- + // NOTE: Logic is directly managed by button + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiPanel(windowPanel, NULL); // Draw window base + GuiStatusBar(statusBar, title); // Draw window header as status bar + + // Draw window close button + int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); + int tempTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, BORDER_WIDTH, 1); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); +#if defined(RAYGUI_NO_ICONS) + result = GuiButton(closeButtonRec, "x"); +#else + result = GuiButton(closeButtonRec, GuiIconText(ICON_CROSS_SMALL, NULL)); +#endif + GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment); + //-------------------------------------------------------------------- + + return result; // Window close button clicked: result = 1 +} + +// Group Box control with text name +int GuiGroupBox(Rectangle bounds, const char *text) +{ + #if !defined(RAYGUI_GROUPBOX_LINE_THICK) + #define RAYGUI_GROUPBOX_LINE_THICK 1 + #endif + + int result = 0; + GuiState state = guiState; + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, RAYGUI_GROUPBOX_LINE_THICK }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR))); + + GuiLine(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2, bounds.width, (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }, text); + //-------------------------------------------------------------------- + + return result; +} + +// Line control +int GuiLine(Rectangle bounds, const char *text) +{ + #if !defined(RAYGUI_LINE_MARGIN_TEXT) + #define RAYGUI_LINE_MARGIN_TEXT 12 + #endif + #if !defined(RAYGUI_LINE_TEXT_PADDING) + #define RAYGUI_LINE_TEXT_PADDING 4 + #endif + + int result = 0; + GuiState state = guiState; + + Color color = GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)); + + // Draw control + //-------------------------------------------------------------------- + if (text == NULL) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height/2, bounds.width, 1 }, 0, BLANK, color); + else + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GuiGetTextWidth(text) + 2; + textBounds.height = bounds.height; + textBounds.x = bounds.x + RAYGUI_LINE_MARGIN_TEXT; + textBounds.y = bounds.y; + + // Draw line with embedded text label: "--- text --------------" + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height/2, RAYGUI_LINE_MARGIN_TEXT - RAYGUI_LINE_TEXT_PADDING, 1 }, 0, BLANK, color); + GuiDrawText(text, textBounds, TEXT_ALIGN_LEFT, color); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + 12 + textBounds.width + 4, bounds.y + bounds.height/2, bounds.width - textBounds.width - RAYGUI_LINE_MARGIN_TEXT - RAYGUI_LINE_TEXT_PADDING, 1 }, 0, BLANK, color); + } + //-------------------------------------------------------------------- + + return result; +} + +// Panel control +int GuiPanel(Rectangle bounds, const char *text) +{ + #if !defined(RAYGUI_PANEL_BORDER_WIDTH) + #define RAYGUI_PANEL_BORDER_WIDTH 1 + #endif + + int result = 0; + GuiState state = guiState; + + // Text will be drawn as a header bar (if provided) + Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT }; + if ((text != NULL) && (bounds.height < RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f)) bounds.height = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f; + + if (text != NULL) + { + // Move panel bounds after the header bar + bounds.y += (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1; + bounds.height -= (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1; + } + + // Draw control + //-------------------------------------------------------------------- + if (text != NULL) GuiStatusBar(statusBar, text); // Draw panel header as status bar + + GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)), + GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BASE_COLOR_DISABLED : (int)BACKGROUND_COLOR))); + //-------------------------------------------------------------------- + + return result; +} + +// Tab Bar control +// NOTE: Using GuiToggle() for the TABS +int GuiTabBar(Rectangle bounds, char **text, int count, int *active) +{ + #define RAYGUI_TABBAR_ITEM_WIDTH 148 + + int result = -1; + //GuiState state = guiState; + + Rectangle tabBounds = { bounds.x, bounds.y, RAYGUI_TABBAR_ITEM_WIDTH, bounds.height }; + + if (*active < 0) *active = 0; + else if (*active > count - 1) *active = count - 1; + + int offsetX = 0; // Required in case tabs go out of screen + offsetX = (*active + 2)*RAYGUI_TABBAR_ITEM_WIDTH - GetScreenWidth(); + if (offsetX < 0) offsetX = 0; + + bool toggle = false; // Required for individual toggles + + // Draw control + //-------------------------------------------------------------------- + for (int i = 0; i < count; i++) + { + tabBounds.x = bounds.x + (RAYGUI_TABBAR_ITEM_WIDTH + 4)*i - offsetX; + + if (tabBounds.x < GetScreenWidth()) + { + // Draw tabs as toggle controls + int textAlignment = GuiGetStyle(TOGGLE, TEXT_ALIGNMENT); + int textPadding = GuiGetStyle(TOGGLE, TEXT_PADDING); + GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + GuiSetStyle(TOGGLE, TEXT_PADDING, 8); + + if (i == (*active)) + { + toggle = true; + GuiToggle(tabBounds, text[i], &toggle); + } + else + { + toggle = false; + GuiToggle(tabBounds, text[i], &toggle); + if (toggle) *active = i; + } + + // Close tab with middle mouse button pressed + if (CheckCollisionPointRec(GUI_POINTER_POSITION, tabBounds) && IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) result = i; + + GuiSetStyle(TOGGLE, TEXT_PADDING, textPadding); + GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, textAlignment); + + // Draw tab close button + // NOTE: Only draw close button for current tab: if (CheckCollisionPointRec(mousePosition, tabBounds)) + int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); + int tempTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, BORDER_WIDTH, 1); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); +#if defined(RAYGUI_NO_ICONS) + if (GuiButton(RAYGUI_CLITERAL(Rectangle){ tabBounds.x + tabBounds.width - 14 - 5, tabBounds.y + 5, 14, 14 }, "x")) result = i; +#else + if (GuiButton(RAYGUI_CLITERAL(Rectangle){ tabBounds.x + tabBounds.width - 14 - 5, tabBounds.y + 5, 14, 14 }, GuiIconText(ICON_CROSS_SMALL, NULL))) result = i; +#endif + GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment); + } + } + + // Draw tab-bar bottom line + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, 1 }, 0, BLANK, GetColor(GuiGetStyle(TOGGLE, BORDER_COLOR_NORMAL))); + //-------------------------------------------------------------------- + + return result; // Return as result the current TAB closing requested +} + +// Scroll Panel control +int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view) +{ + #define RAYGUI_MIN_SCROLLBAR_WIDTH 40 + #define RAYGUI_MIN_SCROLLBAR_HEIGHT 40 + #define RAYGUI_MIN_MOUSE_WHEEL_SPEED 20 + + int result = 0; + GuiState state = guiState; + + Rectangle temp = { 0 }; + if (view == NULL) view = &temp; + + Vector2 scrollPos = { 0.0f, 0.0f }; + if (scroll != NULL) scrollPos = *scroll; + + // Text will be drawn as a header bar (if provided) + Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT }; + if (bounds.height < RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f) bounds.height = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f; + + if (text != NULL) + { + // Move panel bounds after the header bar + bounds.y += (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1; + bounds.height -= (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + 1; + } + + bool hasHorizontalScrollBar = (content.width > bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH))? true : false; + bool hasVerticalScrollBar = (content.height > bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH))? true : false; + + // Recheck to account for the other scrollbar being visible + if (!hasHorizontalScrollBar) hasHorizontalScrollBar = (hasVerticalScrollBar && (content.width > (bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH))))? true : false; + if (!hasVerticalScrollBar) hasVerticalScrollBar = (hasHorizontalScrollBar && (content.height > (bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH))))? true : false; + + int horizontalScrollBarWidth = hasHorizontalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0; + int verticalScrollBarWidth = hasVerticalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0; + Rectangle horizontalScrollBar = { + (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + verticalScrollBarWidth : (float)bounds.x) + GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)bounds.y + bounds.height - horizontalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)bounds.width - verticalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)horizontalScrollBarWidth + }; + Rectangle verticalScrollBar = { + (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)bounds.x + bounds.width - verticalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH)), + (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)verticalScrollBarWidth, + (float)bounds.height - horizontalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) + }; + + // Make sure scroll bars have a minimum width/height + if (horizontalScrollBar.width < RAYGUI_MIN_SCROLLBAR_WIDTH) horizontalScrollBar.width = RAYGUI_MIN_SCROLLBAR_WIDTH; + if (verticalScrollBar.height < RAYGUI_MIN_SCROLLBAR_HEIGHT) verticalScrollBar.height = RAYGUI_MIN_SCROLLBAR_HEIGHT; + + // Calculate view area (area without the scrollbars) + *view = (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? + RAYGUI_CLITERAL(Rectangle){ bounds.x + verticalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth } : + RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth }; + + // Clip view area to the actual content size + if (view->width > content.width) view->width = content.width; + if (view->height > content.height) view->height = content.height; + + float horizontalMin = hasHorizontalScrollBar? ((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)-verticalScrollBarWidth : 0) - (float)GuiGetStyle(DEFAULT, BORDER_WIDTH) : (((float)GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)-verticalScrollBarWidth : 0) - (float)GuiGetStyle(DEFAULT, BORDER_WIDTH); + float horizontalMax = hasHorizontalScrollBar? content.width - bounds.width + (float)verticalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH) - (((float)GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)verticalScrollBarWidth : 0) : (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH); + float verticalMin = hasVerticalScrollBar? 0.0f : -1.0f; + float verticalMax = hasVerticalScrollBar? content.height - bounds.height + (float)horizontalScrollBarWidth + (float)GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH); + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + // Check button state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else state = STATE_FOCUSED; + +#if defined(SUPPORT_SCROLLBAR_KEY_INPUT) + if (hasHorizontalScrollBar) + { + if (GUI_KEY_DOWN(KEY_RIGHT)) scrollPos.x -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (GUI_KEY_DOWN(KEY_LEFT)) scrollPos.x += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + } + + if (hasVerticalScrollBar) + { + if (GUI_KEY_DOWN(KEY_DOWN)) scrollPos.y -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (GUI_KEY_DOWN(KEY_UP)) scrollPos.y += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + } +#endif + float scrollDelta = GUI_SCROLL_DELTA; + + // Set scrolling speed with mouse wheel based on ratio between bounds and content + Vector2 scrollSpeed = { content.width/bounds.width, content.height/bounds.height }; + if (scrollSpeed.x < RAYGUI_MIN_MOUSE_WHEEL_SPEED) scrollSpeed.x = RAYGUI_MIN_MOUSE_WHEEL_SPEED; + if (scrollSpeed.y < RAYGUI_MIN_MOUSE_WHEEL_SPEED) scrollSpeed.y = RAYGUI_MIN_MOUSE_WHEEL_SPEED; + + // Horizontal and vertical scrolling with mouse wheel + if (hasHorizontalScrollBar && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_LEFT_SHIFT))) scrollPos.x += scrollDelta*scrollSpeed.x; + else scrollPos.y += scrollDelta*scrollSpeed.y; // Vertical scroll + } + } + + // Normalize scroll values + if (scrollPos.x > -horizontalMin) scrollPos.x = -horizontalMin; + if (scrollPos.x < -horizontalMax) scrollPos.x = -horizontalMax; + if (scrollPos.y > -verticalMin) scrollPos.y = -verticalMin; + if (scrollPos.y < -verticalMax) scrollPos.y = -verticalMax; + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (text != NULL) GuiStatusBar(statusBar, text); // Draw panel header as status bar + + GuiDrawRectangle(bounds, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background + + // Save size of the scrollbar slider + const int slider = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE); + + // Draw horizontal scrollbar if visible + if (hasHorizontalScrollBar) + { + // Change scrollbar slider size to show the diff in size between the content width and the widget width + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)(((bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth)/(int)content.width)*((int)bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth))); + scrollPos.x = (float)-GuiScrollBar(horizontalScrollBar, (int)-scrollPos.x, (int)horizontalMin, (int)horizontalMax); + } + else scrollPos.x = 0.0f; + + // Draw vertical scrollbar if visible + if (hasVerticalScrollBar) + { + // Change scrollbar slider size to show the diff in size between the content height and the widget height + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)(((bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)/(int)content.height)*((int)bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth))); + scrollPos.y = (float)-GuiScrollBar(verticalScrollBar, (int)-scrollPos.y, (int)verticalMin, (int)verticalMax); + } + else scrollPos.y = 0.0f; + + // Draw detail corner rectangle if both scroll bars are visible + if (hasHorizontalScrollBar && hasVerticalScrollBar) + { + Rectangle corner = { (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) + 2) : (horizontalScrollBar.x + horizontalScrollBar.width + 2), verticalScrollBar.y + verticalScrollBar.height + 2, (float)horizontalScrollBarWidth - 4, (float)verticalScrollBarWidth - 4 }; + GuiDrawRectangle(corner, 0, BLANK, GetColor(GuiGetStyle(LISTVIEW, TEXT + (state*3)))); + } + + // Draw scrollbar lines depending on current state + GuiDrawRectangle(bounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + (state*3))), BLANK); + + // Set scrollbar slider size back to the way it was before + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, slider); + //-------------------------------------------------------------------- + + if (scroll != NULL) *scroll = scrollPos; + + return result; +} + +// Label control +int GuiLabel(Rectangle bounds, const char *text) +{ + int result = 0; + GuiState state = guiState; + + // Update control + //-------------------------------------------------------------------- + //... + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + return result; +} + +// Button control, returns true when clicked +int GuiButton(Rectangle bounds, const char *text) +{ + int result = 0; + GuiState state = guiState; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + // Check button state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else state = STATE_FOCUSED; + + if (GUI_BUTTON_RELEASED) result = 1; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), GetColor(GuiGetStyle(BUTTON, BASE + (state*3)))); + GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), GetColor(GuiGetStyle(BUTTON, TEXT + (state*3)))); + + if (state == STATE_FOCUSED) GuiTooltip(bounds); + //------------------------------------------------------------------ + + return result; // Button pressed: result = 1 +} + +// Label button control +int GuiLabelButton(Rectangle bounds, const char *text) +{ + GuiState state = guiState; + bool pressed = false; + + // NOTE: Force bounds.width to be all text + float textWidth = (float)GuiGetTextWidth(text); + if ((bounds.width - 2*GuiGetStyle(LABEL, BORDER_WIDTH) - 2*GuiGetStyle(LABEL, TEXT_PADDING)) < textWidth) bounds.width = textWidth + 2*GuiGetStyle(LABEL, BORDER_WIDTH) + 2*GuiGetStyle(LABEL, TEXT_PADDING) + 2; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + // Check checkbox state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else state = STATE_FOCUSED; + + if (GUI_BUTTON_RELEASED) pressed = true; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + return pressed; +} + +// Toggle Button control +int GuiToggle(Rectangle bounds, const char *text, bool *active) +{ + int result = 0; + GuiState state = guiState; + + bool temp = false; + if (active == NULL) active = &temp; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + // Check toggle button state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else if (GUI_BUTTON_RELEASED) + { + state = STATE_NORMAL; + *active = !(*active); + } + else state = STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state == STATE_NORMAL) + { + GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, ((*active)? BORDER_COLOR_PRESSED : (BORDER + state*3)))), GetColor(GuiGetStyle(TOGGLE, ((*active)? BASE_COLOR_PRESSED : (BASE + state*3))))); + GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TOGGLE, ((*active)? TEXT_COLOR_PRESSED : (TEXT + state*3))))); + } + else + { + GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, BORDER + state*3)), GetColor(GuiGetStyle(TOGGLE, BASE + state*3))); + GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TOGGLE, TEXT + state*3))); + } + + if (state == STATE_FOCUSED) GuiTooltip(bounds); + //-------------------------------------------------------------------- + + return result; +} + +// Toggle Group control +int GuiToggleGroup(Rectangle bounds, const char *text, int *active) +{ + #if !defined(RAYGUI_TOGGLEGROUP_MAX_ITEMS) + #define RAYGUI_TOGGLEGROUP_MAX_ITEMS 32 + #endif + + int result = 0; + float initBoundsX = bounds.x; + + int temp = 0; + if (active == NULL) active = &temp; + + bool toggle = false; // Required for individual toggles + + // Get substrings items from text (items pointers) + int rows[RAYGUI_TOGGLEGROUP_MAX_ITEMS] = { 0 }; + int itemCount = 0; + char **items = GuiTextSplit(text, ';', &itemCount, rows); + + int prevRow = rows[0]; + + for (int i = 0; i < itemCount; i++) + { + if (prevRow != rows[i]) + { + bounds.x = initBoundsX; + bounds.y += (bounds.height + GuiGetStyle(TOGGLE, GROUP_PADDING)); + prevRow = rows[i]; + } + + if (i == (*active)) + { + toggle = true; + GuiToggle(bounds, items[i], &toggle); + } + else + { + toggle = false; + GuiToggle(bounds, items[i], &toggle); + if (toggle) *active = i; + } + + bounds.x += (bounds.width + GuiGetStyle(TOGGLE, GROUP_PADDING)); + } + + return result; +} + +// Toggle Slider control extended +int GuiToggleSlider(Rectangle bounds, const char *text, int *active) +{ + int result = 0; + GuiState state = guiState; + + int temp = 0; + if (active == NULL) active = &temp; + + //bool toggle = false; // Required for individual toggles + + // Get substrings items from text (items pointers) + int itemCount = 0; + char **items = NULL; + + if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL); + + Rectangle slider = { + 0, // Calculated later depending on the active toggle + bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING), + (bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - (itemCount + 1)*GuiGetStyle(SLIDER, SLIDER_PADDING))/itemCount, + bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) }; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else if (GUI_BUTTON_RELEASED) + { + state = STATE_PRESSED; + (*active)++; + result = 1; + } + else state = STATE_FOCUSED; + } + + if ((*active) && (state != STATE_FOCUSED)) state = STATE_PRESSED; + } + + if (*active >= itemCount) *active = 0; + slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH) + (*active + 1)*GuiGetStyle(SLIDER, SLIDER_PADDING) + (*active)*slider.width; + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, BORDER + (state*3))), + GetColor(GuiGetStyle(TOGGLE, BASE_COLOR_NORMAL))); + + // Draw internal slider + if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED))); + else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_FOCUSED))); + else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED))); + + // Draw text in slider + if (text != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GuiGetTextWidth(text); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = slider.x + slider.width/2 - textBounds.width/2; + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + GuiDrawText(items[*active], textBounds, GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, TEXT + (state*3))), guiAlpha)); + } + //-------------------------------------------------------------------- + + return result; +} + +// Check Box control, returns 1 when state changed +int GuiCheckBox(Rectangle bounds, const char *text, bool *checked) +{ + int result = 0; + GuiState state = guiState; + + bool temp = false; + if (checked == NULL) checked = &temp; + + Rectangle textBounds = { 0 }; + + if (text != NULL) + { + textBounds.width = (float)GuiGetTextWidth(text) + 2; + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + if (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(CHECKBOX, TEXT_PADDING); + } + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + Rectangle totalBounds = { + (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT)? textBounds.x : bounds.x, + bounds.y, + bounds.width + textBounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING), + bounds.height, + }; + + // Check checkbox state + if (CheckCollisionPointRec(mousePoint, totalBounds)) + { + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else state = STATE_FOCUSED; + + if (GUI_BUTTON_RELEASED) + { + *checked = !(*checked); + result = 1; + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(CHECKBOX, BORDER_WIDTH), GetColor(GuiGetStyle(CHECKBOX, BORDER + (state*3))), BLANK); + + if (*checked) + { + Rectangle check = { bounds.x + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING), + bounds.y + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING), + bounds.width - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING)), + bounds.height - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING)) }; + GuiDrawRectangle(check, 0, BLANK, GetColor(GuiGetStyle(CHECKBOX, TEXT + state*3))); + } + + GuiDrawText(text, textBounds, (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + return result; +} + +// Combo Box control +int GuiComboBox(Rectangle bounds, const char *text, int *active) +{ + int result = 0; + GuiState state = guiState; + + int temp = 0; + if (active == NULL) active = &temp; + + bounds.width -= (GuiGetStyle(COMBOBOX, COMBO_BUTTON_WIDTH) + GuiGetStyle(COMBOBOX, COMBO_BUTTON_SPACING)); + + Rectangle selector = { (float)bounds.x + bounds.width + GuiGetStyle(COMBOBOX, COMBO_BUTTON_SPACING), + (float)bounds.y, (float)GuiGetStyle(COMBOBOX, COMBO_BUTTON_WIDTH), (float)bounds.height }; + + // Get substrings items from text (items pointers, lengths and count) + int itemCount = 0; + char **items = GuiTextSplit(text, ';', &itemCount, NULL); + + if (*active < 0) *active = 0; + else if (*active > (itemCount - 1)) *active = itemCount - 1; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1) && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + if (CheckCollisionPointRec(mousePoint, bounds) || + CheckCollisionPointRec(mousePoint, selector)) + { + if (GUI_BUTTON_PRESSED) + { + *active += 1; + if (*active >= itemCount) *active = 0; // Cyclic combobox + } + + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else state = STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + // Draw combo box main + GuiDrawRectangle(bounds, GuiGetStyle(COMBOBOX, BORDER_WIDTH), GetColor(GuiGetStyle(COMBOBOX, BORDER + (state*3))), GetColor(GuiGetStyle(COMBOBOX, BASE + (state*3)))); + GuiDrawText(items[*active], GetTextBounds(COMBOBOX, bounds), GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(COMBOBOX, TEXT + (state*3)))); + + // Draw selector using a custom button + // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values + int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); + int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, BORDER_WIDTH, 1); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + + GuiButton(selector, TextFormat("%i/%i", *active + 1, itemCount)); + + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign); + GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); + //-------------------------------------------------------------------- + + return result; +} + +// Dropdown Box control +// NOTE: Returns mouse click +int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode) +{ + int result = 0; + GuiState state = guiState; + + int temp = 0; + if (active == NULL) active = &temp; + + int itemSelected = *active; + int itemFocused = -1; + + int direction = 0; // Dropdown box open direction: down (default) + if (GuiGetStyle(DROPDOWNBOX, DROPDOWN_ROLL_UP) == 1) direction = 1; // Up + + // Get substrings items from text (items pointers, lengths and count) + int itemCount = 0; + char **items = GuiTextSplit(text, ';', &itemCount, NULL); + + Rectangle boundsOpen = bounds; + boundsOpen.height = (itemCount + 1)*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); + if (direction == 1) boundsOpen.y -= itemCount*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)) + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING); + + Rectangle itemBounds = bounds; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1) && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + if (editMode) + { + state = STATE_PRESSED; + + // Check if mouse has been pressed or released outside limits + if (!CheckCollisionPointRec(mousePoint, boundsOpen)) + { + if (GUI_BUTTON_PRESSED || GUI_BUTTON_RELEASED) result = 1; + } + + // Check if already selected item has been pressed again + if (CheckCollisionPointRec(mousePoint, bounds) && GUI_BUTTON_PRESSED) result = 1; + + // Check focused and selected item + for (int i = 0; i < itemCount; i++) + { + // Update item rectangle y position for next item + if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); + else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); + + if (CheckCollisionPointRec(mousePoint, itemBounds)) + { + itemFocused = i; + if (GUI_BUTTON_RELEASED) + { + itemSelected = i; + result = 1; // Item selected + } + break; + } + } + + itemBounds = bounds; + } + else + { + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (GUI_BUTTON_PRESSED) + { + result = 1; + state = STATE_PRESSED; + } + else state = STATE_FOCUSED; + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (editMode) GuiPanel(boundsOpen, NULL); + + GuiDrawRectangle(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER + state*3)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE + state*3))); + GuiDrawText(items[itemSelected], GetTextBounds(DROPDOWNBOX, bounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + state*3))); + + if (editMode) + { + // Draw visible items + for (int i = 0; i < itemCount; i++) + { + // Update item rectangle y position for next item + if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); + else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); + + if (i == itemSelected) + { + GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_PRESSED))); + GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_PRESSED))); + } + else if (i == itemFocused) + { + GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_FOCUSED))); + GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_FOCUSED))); + } + else GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_NORMAL))); + } + } + + if (!GuiGetStyle(DROPDOWNBOX, DROPDOWN_ARROW_HIDDEN)) + { + // Draw arrows (using icon if available) +#if defined(RAYGUI_NO_ICONS) + GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 }, + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); +#else + GuiDrawText(direction? "#121#" : "#120#", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 6, 10, 10 }, + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); // ICON_ARROW_DOWN_FILL +#endif + } + //-------------------------------------------------------------------- + + *active = itemSelected; + + // TODO: Use result to return more internal states: mouse-press out-of-bounds, mouse-press over selected-item... + return result; // Mouse click: result = 1 +} + +// Text Box control +// NOTE: Returns true on ENTER pressed (useful for data validation) +int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) +{ + #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) + #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN 20 // Frames to wait for autocursor movement + #endif + #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) + #define RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY 1 // Frames delay for autocursor movement + #endif + + int result = 0; + GuiState state = guiState; + + bool multiline = false; // TODO: Consider multiline text input + int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); + + Rectangle textBounds = GetTextBounds(TEXTBOX, bounds); + int textLength = (text != NULL)? (int)strlen(text) : 0; // Get current text length + int thisCursorIndex = textBoxCursorIndex; + if (thisCursorIndex > textLength) thisCursorIndex = textLength; + int textWidth = GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex); + int textIndexOffset = 0; // Text index offset to start drawing in the box + + // Cursor rectangle + // NOTE: Position X value should be updated + Rectangle cursor = { + textBounds.x + textWidth + GuiGetStyle(DEFAULT, TEXT_SPACING), + textBounds.y + textBounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE), + 2, + (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*2 + }; + + if (cursor.height >= bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2; + if (cursor.y < (bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH))) cursor.y = bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH); + + // Mouse cursor rectangle + // NOTE: Initialized outside of screen + Rectangle mouseCursor = cursor; + mouseCursor.x = -1; + mouseCursor.width = 1; + + // Blink-cursor frame counter + //if (!autoCursorMode) blinkCursorFrameCounter++; + //else blinkCursorFrameCounter = 0; + + // Update control + //-------------------------------------------------------------------- + // WARNING: Text editing is only supported under certain conditions: + if ((state != STATE_DISABLED) && // Control not disabled + !GuiGetStyle(TEXTBOX, TEXT_READONLY) && // TextBox not on read-only mode + !guiLocked && // Gui not locked + !guiControlExclusiveMode && // No gui slider on dragging + (wrapMode == TEXT_WRAP_NONE)) // No wrap mode + { + Vector2 mousePosition = GUI_POINTER_POSITION; + + if (editMode) + { + // GLOBAL: Auto-cursor movement logic + // NOTE: Keystrokes are handled repeatedly when button is held down for some time + if (GUI_KEY_DOWN(KEY_LEFT) || GUI_KEY_DOWN(KEY_RIGHT) || GUI_KEY_DOWN(KEY_UP) || GUI_KEY_DOWN(KEY_DOWN) || GUI_KEY_DOWN(KEY_BACKSPACE) || GUI_KEY_DOWN(KEY_DELETE)) autoCursorCounter++; + else autoCursorCounter = 0; + + bool autoCursorShouldTrigger = (autoCursorCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) && ((autoCursorCounter % RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0); + + state = STATE_PRESSED; + + if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength; + + // If text does not fit in the textbox and current cursor position is out of bounds, + // adding an index offset to text for drawing only what requires depending on cursor + while (textWidth >= textBounds.width) + { + int nextCodepointSize = 0; + GetCodepointNext(text + textIndexOffset, &nextCodepointSize); + + textIndexOffset += nextCodepointSize; + + textWidth = GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex); + } + + int codepoint = GUI_INPUT_KEY; // Get Unicode codepoint + if (multiline && GUI_KEY_PRESSED(KEY_ENTER)) codepoint = (int)'\n'; + + // Encode codepoint as UTF-8 + int codepointSize = 0; + const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize); + + // Handle text paste action + if (GUI_KEY_PRESSED(KEY_V) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) + { + const char *pasteText = GetClipboardText(); + if (pasteText != NULL) + { + int pasteLength = 0; + int pasteCodepoint; + int pasteCodepointSize; + + // Count how many codepoints to copy, stopping at the first unwanted control character + while (true) + { + pasteCodepoint = GetCodepointNext(pasteText + pasteLength, &pasteCodepointSize); + if (textLength + pasteLength + pasteCodepointSize >= textSize) break; + if (!(multiline && (pasteCodepoint == (int)'\n')) && !(pasteCodepoint >= 32)) break; + pasteLength += pasteCodepointSize; + } + + if (pasteLength > 0) + { + // Move forward data from cursor position + for (int i = textLength + pasteLength; i > textBoxCursorIndex; i--) text[i] = text[i - pasteLength]; + + // Paste data in at cursor + for (int i = 0; i < pasteLength; i++) text[textBoxCursorIndex + i] = pasteText[i]; + + textBoxCursorIndex += pasteLength; + textLength += pasteLength; + text[textLength] = '\0'; + } + } + } + else if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize)) + { + // Adding codepoint to text, at current cursor position + + // Move forward data from cursor position + for (int i = (textLength + codepointSize); i > textBoxCursorIndex; i--) text[i] = text[i - codepointSize]; + + // Add new codepoint in current cursor position + for (int i = 0; i < codepointSize; i++) text[textBoxCursorIndex + i] = charEncoded[i]; + + textBoxCursorIndex += codepointSize; + textLength += codepointSize; + + // Make sure text last character is EOL + text[textLength] = '\0'; + } + + // Move cursor to start + if ((textLength > 0) && GUI_KEY_PRESSED(KEY_HOME)) textBoxCursorIndex = 0; + + // Move cursor to end + if ((textLength > textBoxCursorIndex) && GUI_KEY_PRESSED(KEY_END)) textBoxCursorIndex = textLength; + + // Delete related codepoints from text, after current cursor position + if ((textLength > textBoxCursorIndex) && GUI_KEY_PRESSED(KEY_DELETE) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) + { + int offset = textBoxCursorIndex; + int accCodepointSize = 0; + int nextCodepointSize; + int nextCodepoint; + + // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace) + // Not using isalnum() since it only works on ASCII characters + nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); + bool puctuation = ispunct(nextCodepoint & 0xff); + while (offset < textLength) + { + if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) + break; + offset += nextCodepointSize; + accCodepointSize += nextCodepointSize; + nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); + } + + // Check whitespace to delete (ASCII only) + while (offset < textLength) + { + if (!isspace(nextCodepoint & 0xff)) break; + + offset += nextCodepointSize; + accCodepointSize += nextCodepointSize; + nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); + } + + // Move text after cursor forward (including final null terminator) + for (int i = offset; i <= textLength; i++) text[i - accCodepointSize] = text[i]; + + textLength -= accCodepointSize; + } + + else if ((textLength > textBoxCursorIndex) && (GUI_KEY_PRESSED(KEY_DELETE) || (GUI_KEY_DOWN(KEY_DELETE) && autoCursorShouldTrigger))) + { + // Delete single codepoint from text, after current cursor position + + int nextCodepointSize = 0; + GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize); + + // Move text after cursor forward (including final null terminator) + for (int i = textBoxCursorIndex + nextCodepointSize; i <= textLength; i++) text[i - nextCodepointSize] = text[i]; + + textLength -= nextCodepointSize; + } + + // Delete related codepoints from text, before current cursor position + if ((textBoxCursorIndex > 0) && GUI_KEY_PRESSED(KEY_BACKSPACE) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) + { + int offset = textBoxCursorIndex; + int accCodepointSize = 0; + int prevCodepointSize = 0; + int prevCodepoint = 0; + + // Check whitespace to delete (ASCII only) + while (offset > 0) + { + prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); + if (!isspace(prevCodepoint & 0xff)) break; + + offset -= prevCodepointSize; + accCodepointSize += prevCodepointSize; + } + + // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace) + // Not using isalnum() since it only works on ASCII characters + bool puctuation = ispunct(prevCodepoint & 0xff); + while (offset > 0) + { + prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); + if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break; + + offset -= prevCodepointSize; + accCodepointSize += prevCodepointSize; + } + + // Move text after cursor forward (including final null terminator) + for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - accCodepointSize] = text[i]; + + textLength -= accCodepointSize; + textBoxCursorIndex -= accCodepointSize; + } + + else if ((textBoxCursorIndex > 0) && (GUI_KEY_PRESSED(KEY_BACKSPACE) || (GUI_KEY_DOWN(KEY_BACKSPACE) && autoCursorShouldTrigger))) + { + // Delete single codepoint from text, before current cursor position + + int prevCodepointSize = 0; + + GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize); + + // Move text after cursor forward (including final null terminator) + for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - prevCodepointSize] = text[i]; + + textLength -= prevCodepointSize; + textBoxCursorIndex -= prevCodepointSize; + } + + // Move cursor position with keys + if ((textBoxCursorIndex > 0) && GUI_KEY_PRESSED(KEY_LEFT) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) + { + int offset = textBoxCursorIndex; + //int accCodepointSize = 0; + int prevCodepointSize = 0; + int prevCodepoint = 0; + + // Check whitespace to skip (ASCII only) + while (offset > 0) + { + prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); + if (!isspace(prevCodepoint & 0xff)) break; + + offset -= prevCodepointSize; + //accCodepointSize += prevCodepointSize; + } + + // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace) + // Not using isalnum() since it only works on ASCII characters + bool puctuation = ispunct(prevCodepoint & 0xff); + while (offset > 0) + { + prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); + if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break; + + offset -= prevCodepointSize; + //accCodepointSize += prevCodepointSize; + } + + textBoxCursorIndex = offset; + } + else if ((textBoxCursorIndex > 0) && (GUI_KEY_PRESSED(KEY_LEFT) || (GUI_KEY_DOWN(KEY_LEFT) && autoCursorShouldTrigger))) + { + int prevCodepointSize = 0; + GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize); + + textBoxCursorIndex -= prevCodepointSize; + } + else if ((textLength > textBoxCursorIndex) && GUI_KEY_PRESSED(KEY_RIGHT) && (GUI_KEY_DOWN(KEY_LEFT_CONTROL) || GUI_KEY_DOWN(KEY_RIGHT_CONTROL))) + { + int offset = textBoxCursorIndex; + //int accCodepointSize = 0; + int nextCodepointSize; + int nextCodepoint; + + // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace) + // Not using isalnum() since it only works on ASCII characters + nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); + bool puctuation = ispunct(nextCodepoint & 0xff); + while (offset < textLength) + { + if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) break; + + offset += nextCodepointSize; + //accCodepointSize += nextCodepointSize; + nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); + } + + // Check whitespace to skip (ASCII only) + while (offset < textLength) + { + if (!isspace(nextCodepoint & 0xff)) break; + + offset += nextCodepointSize; + //accCodepointSize += nextCodepointSize; + nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); + } + + textBoxCursorIndex = offset; + } + else if ((textLength > textBoxCursorIndex) && (GUI_KEY_PRESSED(KEY_RIGHT) || (GUI_KEY_DOWN(KEY_RIGHT) && autoCursorShouldTrigger))) + { + int nextCodepointSize = 0; + GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize); + + textBoxCursorIndex += nextCodepointSize; + } + + // Move cursor position with mouse + if (CheckCollisionPointRec(mousePosition, textBounds)) // Mouse hover text + { + float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/(float)guiFont.baseSize; + int codepointIndex = 0; + float glyphWidth = 0.0f; + float widthToMouseX = 0; + int mouseCursorIndex = 0; + + for (int i = textIndexOffset; i < textLength; i += codepointSize) + { + codepoint = GetCodepointNext(&text[i], &codepointSize); + codepointIndex = GetGlyphIndex(guiFont, codepoint); + + if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor); + else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor); + + if (mousePosition.x <= (textBounds.x + (widthToMouseX + glyphWidth/2))) + { + mouseCursor.x = textBounds.x + widthToMouseX; + mouseCursorIndex = i; + break; + } + + widthToMouseX += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + } + + // Check if mouse cursor is at the last position + int textEndWidth = GuiGetTextWidth(text + textIndexOffset); + if (GUI_POINTER_POSITION.x >= (textBounds.x + textEndWidth - glyphWidth/2)) + { + mouseCursor.x = textBounds.x + textEndWidth; + mouseCursorIndex = textLength; + } + + // Place cursor at required index on mouse click + if ((mouseCursor.x >= 0) && GUI_BUTTON_PRESSED) + { + cursor.x = mouseCursor.x; + textBoxCursorIndex = mouseCursorIndex; + } + } + else mouseCursor.x = -1; + + // Recalculate cursor position.y depending on textBoxCursorIndex + cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING); + //if (multiline) cursor.y = GetTextLines() + + // Finish text editing on ENTER or mouse click outside bounds + if ((!multiline && GUI_KEY_PRESSED(KEY_ENTER)) || + (!CheckCollisionPointRec(mousePosition, bounds) && GUI_BUTTON_PRESSED)) + { + textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index + autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes + result = 1; + } + } + else + { + if (CheckCollisionPointRec(mousePosition, bounds)) + { + state = STATE_FOCUSED; + + if (GUI_BUTTON_PRESSED) + { + textBoxCursorIndex = textLength; // GLOBAL: Place cursor index to the end of current text + autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes + result = 1; + } + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state == STATE_PRESSED) + { + GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED))); + } + else if (state == STATE_DISABLED) + { + GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED))); + } + else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), BLANK); + + // Draw text considering index offset if required + // NOTE: Text index offset depends on cursor position + GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3)))); + + // Draw cursor + if (editMode && !GuiGetStyle(TEXTBOX, TEXT_READONLY)) + { + //if (autoCursorMode || ((blinkCursorFrameCounter/40)%2 == 0)) + GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED))); + + // Draw mouse position cursor (if required) + if (mouseCursor.x >= 0) GuiDrawRectangle(mouseCursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED))); + } + else if (state == STATE_FOCUSED) GuiTooltip(bounds); + //-------------------------------------------------------------------- + + return result; // Mouse button pressed: result = 1 +} + +/* +// Text Box control with multiple lines and word-wrap +// NOTE: This text-box is readonly, no editing supported by default +bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode) +{ + bool pressed = false; + + GuiSetStyle(TEXTBOX, TEXT_READONLY, 1); + GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_WORD); // WARNING: If wrap mode enabled, text editing is not supported + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_TOP); + + // TODO: Implement methods to calculate cursor position properly + pressed = GuiTextBox(bounds, text, textSize, editMode); + + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE); + GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_NONE); + GuiSetStyle(TEXTBOX, TEXT_READONLY, 0); + + return pressed; +} +*/ + +// Spinner control, returns selected value +int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode) +{ + int result = 1; + GuiState state = guiState; + + int tempValue = *value; + + Rectangle valueBoxBounds = { + bounds.x + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING), + bounds.y, + bounds.width - 2*(GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING)), bounds.height }; + Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height }; + Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.y, + (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height }; + + Rectangle textBounds = { 0 }; + if (text != NULL) + { + textBounds.width = (float)GuiGetTextWidth(text) + 2; + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING); + } + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + // Check spinner state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else state = STATE_FOCUSED; + } + } + +#if defined(RAYGUI_NO_ICONS) + if (GuiButton(leftButtonBound, "<")) tempValue--; + if (GuiButton(rightButtonBound, ">")) tempValue++; +#else + if (GuiButton(leftButtonBound, GuiIconText(ICON_ARROW_LEFT_FILL, NULL))) tempValue--; + if (GuiButton(rightButtonBound, GuiIconText(ICON_ARROW_RIGHT_FILL, NULL))) tempValue++; +#endif + + if (!editMode) + { + if (tempValue < minValue) tempValue = minValue; + if (tempValue > maxValue) tempValue = maxValue; + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + result = GuiValueBox(valueBoxBounds, NULL, &tempValue, minValue, maxValue, editMode); + + // Draw value selector custom buttons + // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values + int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); + int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(VALUEBOX, BORDER_WIDTH)); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign); + GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); + + // Draw text label if provided + GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + *value = tempValue; + return result; +} + +// Value Box control, updates input text with numbers +// NOTE: Requires static variables: frameCounter +int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode) +{ + #if !defined(RAYGUI_VALUEBOX_MAX_CHARS) + #define RAYGUI_VALUEBOX_MAX_CHARS 32 + #endif + + int result = 0; + GuiState state = guiState; + + char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = { 0 }; + snprintf(textValue, RAYGUI_VALUEBOX_MAX_CHARS + 1, "%i", *value); + + Rectangle textBounds = { 0 }; + if (text != NULL) + { + textBounds.width = (float)GuiGetTextWidth(text) + 2; + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING); + } + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + bool valueHasChanged = false; + + if (editMode) + { + state = STATE_PRESSED; + + int keyCount = (int)strlen(textValue); + + // Add or remove minus symbol + if (GUI_KEY_PRESSED(KEY_MINUS)) + { + if (textValue[0] == '-') + { + for (int i = 0 ; i < keyCount; i++) textValue[i] = textValue[i + 1]; + + keyCount--; + valueHasChanged = true; + } + else if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) + { + if (keyCount == 0) + { + textValue[0] = '0'; + textValue[1] = '\0'; + keyCount++; + } + + for (int i = keyCount ; i > -1; i--) textValue[i + 1] = textValue[i]; + + textValue[0] = '-'; + keyCount++; + valueHasChanged = true; + } + } + + // Add new digit to text value + if ((keyCount >= 0) && (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) && (GuiGetTextWidth(textValue) < bounds.width)) + { + int key = GUI_INPUT_KEY; + + // Only allow keys in range [48..57] + if ((key >= 48) && (key <= 57)) + { + textValue[keyCount] = (char)key; + keyCount++; + valueHasChanged = true; + } + } + + // Delete text + if ((keyCount > 0) && GUI_KEY_PRESSED(KEY_BACKSPACE)) + { + keyCount--; + textValue[keyCount] = '\0'; + valueHasChanged = true; + } + + if (valueHasChanged) *value = TextToInteger(textValue); + + // NOTE: Values are not clamped until user input finishes + //if (*value > maxValue) *value = maxValue; + //else if (*value < minValue) *value = minValue; + + if ((GUI_KEY_PRESSED(KEY_ENTER) || GUI_KEY_PRESSED(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && GUI_BUTTON_PRESSED)) + { + if (*value > maxValue) *value = maxValue; + else if (*value < minValue) *value = minValue; + + result = 1; + } + } + else + { + if (*value > maxValue) *value = maxValue; + else if (*value < minValue) *value = minValue; + + if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = STATE_FOCUSED; + if (GUI_BUTTON_PRESSED) result = 1; + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + Color baseColor = BLANK; + if (state == STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED)); + else if (state == STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED)); + + GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), baseColor); + GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3)))); + + // Draw cursor rectangle + if (editMode) + { + // NOTE: ValueBox internal text is always centered + Rectangle cursor = { bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1, + bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + 2, + 2, bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2 - 4 }; + if (cursor.height > bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2; + GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED))); + } + + // Draw text label if provided + GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + return result; +} + +// Floating point Value Box control, updates input val_str with numbers +// NOTE: Requires static variables: frameCounter +int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode) +{ + #if !defined(RAYGUI_VALUEBOX_MAX_CHARS) + #define RAYGUI_VALUEBOX_MAX_CHARS 32 + #endif + + int result = 0; + GuiState state = guiState; + + //char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0"; + //snprintf(textValue, sizeof(textValue), "%2.2f", *value); + + Rectangle textBounds = { 0 }; + if (text != NULL) + { + textBounds.width = (float)GuiGetTextWidth(text) + 2; + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING); + } + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + bool valueHasChanged = false; + + if (editMode) + { + state = STATE_PRESSED; + + int keyCount = (int)strlen(textValue); + + // Add or remove minus symbol + if (GUI_KEY_PRESSED(KEY_MINUS)) + { + if (textValue[0] == '-') + { + for (int i = 0; i < keyCount; i++) textValue[i] = textValue[i + 1]; + + keyCount--; + valueHasChanged = true; + } + else if (keyCount < (RAYGUI_VALUEBOX_MAX_CHARS - 1)) + { + if (keyCount == 0) + { + textValue[0] = '0'; + textValue[1] = '\0'; + keyCount++; + } + + for (int i = keyCount; i > -1; i--) textValue[i + 1] = textValue[i]; + + textValue[0] = '-'; + keyCount++; + valueHasChanged = true; + } + } + + // Only allow keys in range [48..57] + if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) + { + if (GuiGetTextWidth(textValue) < bounds.width) + { + int key = GUI_INPUT_KEY; + if (((key >= 48) && (key <= 57)) || + (key == '.') || + ((keyCount == 0) && (key == '+')) || // NOTE: Sign can only be in first position + ((keyCount == 0) && (key == '-'))) + { + textValue[keyCount] = (char)key; + keyCount++; + + valueHasChanged = true; + } + } + } + + // Pressed backspace + if (GUI_KEY_PRESSED(KEY_BACKSPACE)) + { + if (keyCount > 0) + { + keyCount--; + textValue[keyCount] = '\0'; + valueHasChanged = true; + } + } + + if (valueHasChanged) *value = TextToFloat(textValue); + + if ((GUI_KEY_PRESSED(KEY_ENTER) || GUI_KEY_PRESSED(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && GUI_BUTTON_PRESSED)) result = 1; + } + else + { + if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = STATE_FOCUSED; + if (GUI_BUTTON_PRESSED) result = 1; + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + Color baseColor = BLANK; + if (state == STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED)); + else if (state == STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED)); + + GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), baseColor); + GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3)))); + + // Draw cursor + if (editMode) + { + // NOTE: ValueBox internal text is always centered + Rectangle cursor = {bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1, + bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4, + bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH)}; + GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED))); + } + + // Draw text label if provided + GuiDrawText(text, textBounds, + (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, + GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + return result; +} + +// Slider control with pro parameters +// NOTE: Other GuiSlider*() controls use this one +int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue) +{ + int result = 0; + GuiState state = guiState; + + float temp = (maxValue - minValue)/2.0f; + if (value == NULL) value = &temp; + float oldValue = *value; + + int sliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH); + + Rectangle slider = { bounds.x, bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING), + 0, bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) }; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds + { + if (GUI_BUTTON_DOWN) + { + if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) + { + state = STATE_PRESSED; + // Get equivalent value and slider position from mousePosition.x + *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue; + } + } + else + { + guiControlExclusiveMode = false; + guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (GUI_BUTTON_DOWN) + { + state = STATE_PRESSED; + guiControlExclusiveMode = true; + guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts + + if (!CheckCollisionPointRec(mousePoint, slider)) + { + // Get equivalent value and slider position from mousePosition.x + *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue; + } + } + else state = STATE_FOCUSED; + } + + if (*value > maxValue) *value = maxValue; + else if (*value < minValue) *value = minValue; + } + + // Control value change check + if (oldValue == *value) result = 0; + else result = 1; + + // Slider bar limits check + float sliderValue = (((*value - minValue)/(maxValue - minValue))*(bounds.width - sliderWidth - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))); + if (sliderWidth > 0) // Slider + { + slider.x += sliderValue; + slider.width = (float)sliderWidth; + if (slider.x <= (bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH))) slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH); + else if ((slider.x + slider.width) >= (bounds.x + bounds.width)) slider.x = bounds.x + bounds.width - slider.width - GuiGetStyle(SLIDER, BORDER_WIDTH); + } + else if (sliderWidth == 0) // SliderBar + { + slider.x += GuiGetStyle(SLIDER, BORDER_WIDTH); + slider.width = sliderValue; + if (slider.width > bounds.width) slider.width = bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH); + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED))); + + // Draw slider internal bar (depends on state) + if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED))); + else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED))); + else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_PRESSED))); + else if (state == STATE_DISABLED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_DISABLED))); + + // Draw left/right text if provided + if (textLeft != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GuiGetTextWidth(textLeft); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + } + + if (textRight != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GuiGetTextWidth(textRight); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + } + //-------------------------------------------------------------------- + + return result; +} + +// Slider Bar control extended, returns selected value +int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue) +{ + int result = 0; + int preSliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH); + GuiSetStyle(SLIDER, SLIDER_WIDTH, 0); + result = GuiSlider(bounds, textLeft, textRight, value, minValue, maxValue); + GuiSetStyle(SLIDER, SLIDER_WIDTH, preSliderWidth); + + return result; +} + +// Progress Bar control extended, shows current progress value +int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue) +{ + int result = 0; + GuiState state = guiState; + + float temp = (maxValue - minValue)/2.0f; + if (value == NULL) value = &temp; + + // Progress bar + Rectangle progress = { bounds.x + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), + bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) + GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING), 0, + bounds.height - GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) -1 }; + + // Update control + //-------------------------------------------------------------------- + if (*value > maxValue) *value = maxValue; + + // WARNING: Working with floats could lead to rounding issues + if ((state != STATE_DISABLED)) progress.width = ((float)*value/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)); + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state == STATE_DISABLED) + { + GuiDrawRectangle(bounds, GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), GetColor(GuiGetStyle(PROGRESSBAR, BORDER + (state*3))), BLANK); + } + else + { + if (*value > minValue) + { + // Draw progress bar with colored border, more visual + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED))); + } + else GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); + + if (*value >= maxValue) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1}, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED))); + else + { + // Draw borders not yet reached by value + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y + bounds.height - 1, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); + } + + // Draw slider internal progress bar (depends on state) + if (GuiGetStyle(PROGRESSBAR, PROGRESS_SIDE) == 0) // Left-->Right + { + GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED))); + } + else // Right-->Left + { + progress.x = bounds.x + bounds.width - progress.width - GuiGetStyle(PROGRESSBAR, BORDER_WIDTH); + GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED))); + } + } + + // Draw left/right text if provided + if (textLeft != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GuiGetTextWidth(textLeft); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x - textBounds.width - GuiGetStyle(PROGRESSBAR, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + } + + if (textRight != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GuiGetTextWidth(textRight); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(PROGRESSBAR, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + } + //-------------------------------------------------------------------- + + return result; +} + +// Status Bar control +int GuiStatusBar(Rectangle bounds, const char *text) +{ + int result = 0; + GuiState state = guiState; + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(STATUSBAR, BORDER_WIDTH), GetColor(GuiGetStyle(STATUSBAR, BORDER + (state*3))), GetColor(GuiGetStyle(STATUSBAR, BASE + (state*3)))); + GuiDrawText(text, GetTextBounds(STATUSBAR, bounds), GuiGetStyle(STATUSBAR, TEXT_ALIGNMENT), GetColor(GuiGetStyle(STATUSBAR, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + return result; +} + +// Dummy rectangle control, intended for placeholding +int GuiDummyRec(Rectangle bounds, const char *text) +{ + int result = 0; + GuiState state = guiState; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + // Check button state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (GUI_BUTTON_DOWN) state = STATE_PRESSED; + else state = STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED))); + GuiDrawText(text, GetTextBounds(DEFAULT, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(BUTTON, (state != STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED))); + //------------------------------------------------------------------ + + return result; +} + +// List View control +int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active) +{ + int result = 0; + int itemCount = 0; + char **items = NULL; + + if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL); + + result = GuiListViewEx(bounds, items, itemCount, scrollIndex, active, NULL); + + return result; +} + +// List View control with extended parameters +int GuiListViewEx(Rectangle bounds, char **text, int count, int *scrollIndex, int *active, int *focus) +{ + int result = 0; + GuiState state = guiState; + + int itemFocused = (focus == NULL)? -1 : *focus; + int itemSelected = (active == NULL)? -1 : *active; + + // Check if scroll bar is needed + bool useScrollBar = false; + if ((GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING))*count > bounds.height) useScrollBar = true; + + // Define base item rectangle [0] + Rectangle itemBounds = { 0 }; + itemBounds.x = bounds.x + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING); + itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) + GuiGetStyle(DEFAULT, BORDER_WIDTH); + itemBounds.width = bounds.width - 2*GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) - GuiGetStyle(DEFAULT, BORDER_WIDTH); + itemBounds.height = (float)GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT); + if (useScrollBar) itemBounds.width -= GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH); + + // Get items on the list + int visibleItems = (int)bounds.height/(GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING)); + if (visibleItems > count) visibleItems = count; + + int startIndex = (scrollIndex == NULL)? 0 : *scrollIndex; + if ((startIndex < 0) || (startIndex > (count - visibleItems))) startIndex = 0; + int endIndex = startIndex + visibleItems; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + // Check mouse inside list view + if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = STATE_FOCUSED; + + // Check focused and selected item + for (int i = 0; i < visibleItems; i++) + { + if (CheckCollisionPointRec(mousePoint, itemBounds)) + { + itemFocused = startIndex + i; + if (GUI_BUTTON_PRESSED) + { + if (itemSelected == (startIndex + i)) itemSelected = -1; + else itemSelected = startIndex + i; + } + break; + } + + // Update item rectangle y position for next item + itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING)); + } + + if (useScrollBar) + { + float scrollDelta = GUI_SCROLL_DELTA; + startIndex -= (int)scrollDelta; + + if (startIndex < 0) startIndex = 0; + else if (startIndex > (count - visibleItems)) startIndex = count - visibleItems; + + endIndex = startIndex + visibleItems; + if (endIndex > count) endIndex = count; + } + } + else itemFocused = -1; + + // Reset item rectangle y to [0] + itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) + GuiGetStyle(DEFAULT, BORDER_WIDTH); + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background + + // Draw visible items + for (int i = 0; ((i < visibleItems) && (text != NULL)); i++) + { + if (GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_NORMAL)) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), BLANK); + + if (state == STATE_DISABLED) + { + if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED))); + + GuiDrawText(text[startIndex + i], GetTextBounds(LISTVIEW, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED))); + } + else + { + if (((startIndex + i) == itemSelected) && (active != NULL)) + { + // Draw item selected + GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED))); + GuiDrawText(text[startIndex + i], GetTextBounds(LISTVIEW, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED))); + } + else if (((startIndex + i) == itemFocused)) // && (focus != NULL)) // NOTE: Items focused, despite not returned + { + // Draw item focused + GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED))); + GuiDrawText(text[startIndex + i], GetTextBounds(LISTVIEW, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED))); + } + else + { + // Draw item normal (no rectangle) + GuiDrawText(text[startIndex + i], GetTextBounds(LISTVIEW, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL))); + } + } + + // Update item rectangle y position for next item + itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING)); + } + + if (useScrollBar) + { + Rectangle scrollBarBounds = { + bounds.x + bounds.width - GuiGetStyle(LISTVIEW, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH), + bounds.y + GuiGetStyle(LISTVIEW, BORDER_WIDTH), (float)GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH), + bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) + }; + + // Calculate percentage of visible items and apply same percentage to scrollbar + float percentVisible = (float)(endIndex - startIndex)/count; + float sliderSize = bounds.height*percentVisible; + + int prevSliderSize = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE); // Save default slider size + int prevScrollSpeed = GuiGetStyle(SCROLLBAR, SCROLL_SPEED); // Save default scroll speed + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)sliderSize); // Change slider size + GuiSetStyle(SCROLLBAR, SCROLL_SPEED, count - visibleItems); // Change scroll speed + + startIndex = GuiScrollBar(scrollBarBounds, startIndex, 0, count - visibleItems); + + GuiSetStyle(SCROLLBAR, SCROLL_SPEED, prevScrollSpeed); // Reset scroll speed to default + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, prevSliderSize); // Reset slider size to default + } + //-------------------------------------------------------------------- + + if (active != NULL) *active = itemSelected; + if (focus != NULL) *focus = itemFocused; + if (scrollIndex != NULL) *scrollIndex = startIndex; + + return result; +} + +// Color Panel control - Color (RGBA) variant +int GuiColorPanel(Rectangle bounds, const char *text, Color *color) +{ + int result = 0; + + Vector3 vcolor = { (float)color->r/255.0f, (float)color->g/255.0f, (float)color->b/255.0f }; + Vector3 hsv = ConvertRGBtoHSV(vcolor); + Vector3 prevHsv = hsv; // workaround to see if GuiColorPanelHSV modifies the hsv + + GuiColorPanelHSV(bounds, text, &hsv); + + // Check if the hsv was changed, only then change the color + // This is required, because the Color->HSV->Color conversion has precision errors + // Thus the assignment from HSV to Color should only be made, if the HSV has a new user-entered value + // Otherwise GuiColorPanel would often modify it's color without user input + // TODO: GuiColorPanelHSV could return 1 if the slider was dragged, to simplify this check + if (hsv.x != prevHsv.x || hsv.y != prevHsv.y || hsv.z != prevHsv.z) + { + Vector3 rgb = ConvertHSVtoRGB(hsv); + + // NOTE: Vector3ToColor() only available on raylib 1.8.1 + *color = RAYGUI_CLITERAL(Color){ (unsigned char)(255.0f*rgb.x), + (unsigned char)(255.0f*rgb.y), + (unsigned char)(255.0f*rgb.z), + color->a }; + } + return result; +} + +// Color Bar Alpha control +// NOTE: Returns alpha value normalized [0..1] +int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) +{ + #if !defined(RAYGUI_COLORBARALPHA_CHECKED_SIZE) + #define RAYGUI_COLORBARALPHA_CHECKED_SIZE 10 + #endif + + int result = 0; + GuiState state = guiState; + Rectangle selector = { (float)bounds.x + (*alpha)*bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2, + (float)bounds.y - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), + (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT), + (float)bounds.height + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2 }; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds + { + if (GUI_BUTTON_DOWN) + { + if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) + { + state = STATE_PRESSED; + + *alpha = (mousePoint.x - bounds.x)/bounds.width; + if (*alpha <= 0.0f) *alpha = 0.0f; + if (*alpha >= 1.0f) *alpha = 1.0f; + } + } + else + { + guiControlExclusiveMode = false; + guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) + { + if (GUI_BUTTON_DOWN) + { + state = STATE_PRESSED; + guiControlExclusiveMode = true; + guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts + + *alpha = (mousePoint.x - bounds.x)/bounds.width; + if (*alpha <= 0.0f) *alpha = 0.0f; + if (*alpha >= 1.0f) *alpha = 1.0f; + //selector.x = bounds.x + (int)(((alpha - 0)/(100 - 0))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))) - selector.width/2; + } + else state = STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + // Draw alpha bar: checked background + if (state != STATE_DISABLED) + { + int checksX = (int)bounds.width/RAYGUI_COLORBARALPHA_CHECKED_SIZE; + int checksY = (int)bounds.height/RAYGUI_COLORBARALPHA_CHECKED_SIZE; + + for (int x = 0; x < checksX; x++) + { + for (int y = 0; y < checksY; y++) + { + Rectangle check = { bounds.x + x*RAYGUI_COLORBARALPHA_CHECKED_SIZE, bounds.y + y*RAYGUI_COLORBARALPHA_CHECKED_SIZE, RAYGUI_COLORBARALPHA_CHECKED_SIZE, RAYGUI_COLORBARALPHA_CHECKED_SIZE }; + GuiDrawRectangle(check, 0, BLANK, ((x + y)%2)? Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.4f) : Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.4f)); + } + } + + DrawRectangleGradientEx(bounds, RAYGUI_CLITERAL(Color){ 255, 255, 255, 0 }, RAYGUI_CLITERAL(Color){ 255, 255, 255, 0 }, Fade(RAYGUI_CLITERAL(Color){ 0, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 0, 255 }, guiAlpha)); + } + else DrawRectangleGradientEx(bounds, Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha)); + + GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); + + // Draw alpha bar: selector + GuiDrawRectangle(selector, 0, BLANK, GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3))); + //-------------------------------------------------------------------- + + return result; +} + +// Color Bar Hue control +// Returns hue value normalized [0..1] +// NOTE: Other similar bars (for reference): +// Color GuiColorBarSat() [WHITE->color] +// Color GuiColorBarValue() [BLACK->color], HSV/HSL +// float GuiColorBarLuminance() [BLACK->WHITE] +int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) +{ + int result = 0; + GuiState state = guiState; + Rectangle selector = { (float)bounds.x - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (float)bounds.y + (*hue)/360.0f*bounds.height - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2, (float)bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2, (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT) }; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds + { + if (GUI_BUTTON_DOWN) + { + if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) + { + state = STATE_PRESSED; + + *hue = (mousePoint.y - bounds.y)*360/bounds.height; + if (*hue <= 0.0f) *hue = 0.0f; + if (*hue >= 359.0f) *hue = 359.0f; + } + } + else + { + guiControlExclusiveMode = false; + guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) + { + if (GUI_BUTTON_DOWN) + { + state = STATE_PRESSED; + guiControlExclusiveMode = true; + guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts + + *hue = (mousePoint.y - bounds.y)*360/bounds.height; + if (*hue <= 0.0f) *hue = 0.0f; + if (*hue >= 359.0f) *hue = 359.0f; + + } + else state = STATE_FOCUSED; + + /*if (GUI_KEY_DOWN(KEY_UP)) + { + hue -= 2.0f; + if (hue <= 0.0f) hue = 0.0f; + } + else if (GUI_KEY_DOWN(KEY_DOWN)) + { + hue += 2.0f; + if (hue >= 360.0f) hue = 360.0f; + }*/ + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state != STATE_DISABLED) + { + // Draw hue bar:color bars + // TODO: Use directly DrawRectangleGradientEx(bounds, color1, color2, color2, color1); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + bounds.height/6), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 0, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 2*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 255, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 3*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 255, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 4*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 255, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 5*(bounds.height/6)), (int)bounds.width, (int)(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 0, 255 }, guiAlpha)); + } + else DrawRectangleGradientV((int)bounds.x, (int)bounds.y, (int)bounds.width, (int)bounds.height, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha)); + + GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); + + // Draw hue bar: selector + GuiDrawRectangle(selector, 0, BLANK, GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3))); + //-------------------------------------------------------------------- + + return result; +} + +// Color Picker control +// NOTE: It's divided in multiple controls: +// Color GuiColorPanel(Rectangle bounds, Color color) +// float GuiColorBarAlpha(Rectangle bounds, float alpha) +// float GuiColorBarHue(Rectangle bounds, float value) +// NOTE: bounds define GuiColorPanel() size +// NOTE: this picker converts RGB to HSV, which can cause the Hue control to jump. If you have this problem, consider using the HSV variant instead +int GuiColorPicker(Rectangle bounds, const char *text, Color *color) +{ + int result = 0; + + Color temp = { 200, 0, 0, 255 }; + if (color == NULL) color = &temp; + + GuiColorPanel(bounds, NULL, color); + + Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height }; + //Rectangle boundsAlpha = { bounds.x, bounds.y + bounds.height + GuiGetStyle(COLORPICKER, BARS_PADDING), bounds.width, GuiGetStyle(COLORPICKER, BARS_THICK) }; + + // NOTE: this conversion can cause low hue-resolution, if the r, g and b value are very similar, which causes the hue bar to shift around when only the GuiColorPanel is used + Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ (*color).r/255.0f, (*color).g/255.0f, (*color).b/255.0f }); + + GuiColorBarHue(boundsHue, NULL, &hsv.x); + + //color.a = (unsigned char)(GuiColorBarAlpha(boundsAlpha, (float)color.a/255.0f)*255.0f); + Vector3 rgb = ConvertHSVtoRGB(hsv); + + *color = RAYGUI_CLITERAL(Color){ (unsigned char)roundf(rgb.x*255.0f), (unsigned char)roundf(rgb.y*255.0f), (unsigned char)roundf(rgb.z*255.0f), (*color).a }; + + return result; +} + +// Color Picker control that avoids conversion to RGB and back to HSV on each call, thus avoiding jittering +// The user can call ConvertHSVtoRGB() to convert *colorHsv value to RGB +// NOTE: It's divided in multiple controls: +// int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) +// int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) +// float GuiColorBarHue(Rectangle bounds, float value) +// NOTE: bounds define GuiColorPanelHSV() size +int GuiColorPickerHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) +{ + int result = 0; + + Vector3 tempHsv = { 0 }; + + if (colorHsv == NULL) + { + const Vector3 tempColor = { 200.0f/255.0f, 0.0f, 0.0f }; + tempHsv = ConvertRGBtoHSV(tempColor); + colorHsv = &tempHsv; + } + + GuiColorPanelHSV(bounds, NULL, colorHsv); + + const Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height }; + + GuiColorBarHue(boundsHue, NULL, &colorHsv->x); + + return result; +} + +// Color Panel control - HSV variant +int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) +{ + int result = 0; + GuiState state = guiState; + Vector2 pickerSelector = { 0 }; + + const Color colWhite = { 255, 255, 255, 255 }; + const Color colBlack = { 0, 0, 0, 255 }; + + pickerSelector.x = bounds.x + (float)colorHsv->y*bounds.width; // HSV: Saturation + pickerSelector.y = bounds.y + (1.0f - (float)colorHsv->z)*bounds.height; // HSV: Value + + Vector3 maxHue = { colorHsv->x, 1.0f, 1.0f }; + Vector3 rgbHue = ConvertHSVtoRGB(maxHue); + Color maxHueCol = { (unsigned char)(255.0f*rgbHue.x), + (unsigned char)(255.0f*rgbHue.y), + (unsigned char)(255.0f*rgbHue.z), 255 }; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds + { + if (GUI_BUTTON_DOWN) + { + if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) + { + pickerSelector = mousePoint; + + if (pickerSelector.x < bounds.x) pickerSelector.x = bounds.x; + if (pickerSelector.x > bounds.x + bounds.width) pickerSelector.x = bounds.x + bounds.width; + if (pickerSelector.y < bounds.y) pickerSelector.y = bounds.y; + if (pickerSelector.y > bounds.y + bounds.height) pickerSelector.y = bounds.y + bounds.height; + + // Calculate color from picker + Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y }; + + colorPick.x /= (float)bounds.width; // Get normalized value on x + colorPick.y /= (float)bounds.height; // Get normalized value on y + + colorHsv->y = colorPick.x; + colorHsv->z = 1.0f - colorPick.y; + + } + } + else + { + guiControlExclusiveMode = false; + guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (GUI_BUTTON_DOWN) + { + state = STATE_PRESSED; + guiControlExclusiveMode = true; + guiControlExclusiveRec = bounds; + pickerSelector = mousePoint; + + // Calculate color from picker + Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y }; + + colorPick.x /= (float)bounds.width; // Get normalized value on x + colorPick.y /= (float)bounds.height; // Get normalized value on y + + colorHsv->y = colorPick.x; + colorHsv->z = 1.0f - colorPick.y; + } + else state = STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state != STATE_DISABLED) + { + DrawRectangleGradientEx(bounds, Fade(colWhite, guiAlpha), Fade(colWhite, guiAlpha), Fade(maxHueCol, guiAlpha), Fade(maxHueCol, guiAlpha)); + DrawRectangleGradientEx(bounds, Fade(colBlack, 0), Fade(colBlack, guiAlpha), Fade(colBlack, guiAlpha), Fade(colBlack, 0)); + + // Draw color picker: selector + Rectangle selector = { pickerSelector.x - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, pickerSelector.y - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE) }; + GuiDrawRectangle(selector, 0, BLANK, colWhite); + } + else + { + DrawRectangleGradientEx(bounds, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.6f), guiAlpha)); + } + + GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); + //-------------------------------------------------------------------- + + return result; +} + +// Message Box control +int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons) +{ + #if !defined(RAYGUI_MESSAGEBOX_BUTTON_HEIGHT) + #define RAYGUI_MESSAGEBOX_BUTTON_HEIGHT 24 + #endif + #if !defined(RAYGUI_MESSAGEBOX_BUTTON_PADDING) + #define RAYGUI_MESSAGEBOX_BUTTON_PADDING 12 + #endif + + int result = -1; // Returns clicked button from buttons list, 0 refers to closed window button + + int buttonCount = 0; + char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); + Rectangle buttonBounds = { 0 }; + buttonBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING; + buttonBounds.y = bounds.y + bounds.height - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT - RAYGUI_MESSAGEBOX_BUTTON_PADDING; + buttonBounds.width = (bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount; + buttonBounds.height = RAYGUI_MESSAGEBOX_BUTTON_HEIGHT; + + //int textWidth = GuiGetTextWidth(message) + 2; + + Rectangle textBounds = { 0 }; + textBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING; + textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + RAYGUI_MESSAGEBOX_BUTTON_PADDING; + textBounds.width = bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*2; + textBounds.height = bounds.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 3*RAYGUI_MESSAGEBOX_BUTTON_PADDING - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT; + + // Draw control + //-------------------------------------------------------------------- + if (GuiWindowBox(bounds, title)) result = 0; + + int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + GuiLabel(textBounds, message); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment); + + prevTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + + for (int i = 0; i < buttonCount; i++) + { + if (GuiButton(buttonBounds, buttonsText[i])) result = i + 1; + buttonBounds.x += (buttonBounds.width + RAYGUI_MESSAGEBOX_BUTTON_PADDING); + } + + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevTextAlignment); + //-------------------------------------------------------------------- + + return result; +} + +// Text Input Box control, ask for text +int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, bool *secretViewActive) +{ + #if !defined(RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT) + #define RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT 24 + #endif + #if !defined(RAYGUI_TEXTINPUTBOX_BUTTON_PADDING) + #define RAYGUI_TEXTINPUTBOX_BUTTON_PADDING 12 + #endif + #if !defined(RAYGUI_TEXTINPUTBOX_HEIGHT) + #define RAYGUI_TEXTINPUTBOX_HEIGHT 26 + #endif + + // Used to enable text edit mode + // WARNING: No more than one GuiTextInputBox() should be open at the same time + static bool textEditMode = false; + + int result = -1; + + int buttonCount = 0; + char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); + Rectangle buttonBounds = { 0 }; + buttonBounds.x = bounds.x + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; + buttonBounds.y = bounds.y + bounds.height - RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; + buttonBounds.width = (bounds.width - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount; + buttonBounds.height = RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT; + + int messageInputHeight = (int)bounds.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT - 2*RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; + + Rectangle textBounds = { 0 }; + if (message != NULL) + { + int textSize = GuiGetTextWidth(message) + 2; + + textBounds.x = bounds.x + bounds.width/2 - textSize/2; + textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + messageInputHeight/4 - (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + textBounds.width = (float)textSize; + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + } + + Rectangle textBoxBounds = { 0 }; + textBoxBounds.x = bounds.x + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; + textBoxBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - RAYGUI_TEXTINPUTBOX_HEIGHT/2; + if (message == NULL) textBoxBounds.y = bounds.y + 24 + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; + else textBoxBounds.y += (messageInputHeight/2 + messageInputHeight/4); + textBoxBounds.width = bounds.width - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING*2; + textBoxBounds.height = RAYGUI_TEXTINPUTBOX_HEIGHT; + + // Draw control + //-------------------------------------------------------------------- + if (GuiWindowBox(bounds, title)) result = 0; + + // Draw message if available + if (message != NULL) + { + int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + GuiLabel(textBounds, message); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment); + } + + int prevTextBoxAlignment = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT); + GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + + if (secretViewActive != NULL) + { + static char stars[] = "****************"; + if (GuiTextBox(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x, textBoxBounds.y, textBoxBounds.width - 4 - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.height }, + ((*secretViewActive == 1) || textEditMode)? text : stars, textMaxSize, textEditMode)) textEditMode = !textEditMode; + + GuiToggle(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x + textBoxBounds.width - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.y, RAYGUI_TEXTINPUTBOX_HEIGHT, RAYGUI_TEXTINPUTBOX_HEIGHT }, (*secretViewActive == 1)? "#44#" : "#45#", secretViewActive); + } + else + { + if (GuiTextBox(textBoxBounds, text, textMaxSize, textEditMode)) textEditMode = !textEditMode; + } + + GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, prevTextBoxAlignment); + + int prevBtnTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + + for (int i = 0; i < buttonCount; i++) + { + if (GuiButton(buttonBounds, buttonsText[i])) result = i + 1; + buttonBounds.x += (buttonBounds.width + RAYGUI_MESSAGEBOX_BUTTON_PADDING); + } + + if (result >= 0) textEditMode = false; + + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevBtnTextAlignment); + //-------------------------------------------------------------------- + + return result; // Result is the pressed button index +} + +// Grid control +// NOTE: Returns grid mouse-hover selected cell +// About drawing lines at subpixel spacing, simple put, not easy solution: +// REF: https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster +int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell) +{ + // Grid lines alpha amount + #if !defined(RAYGUI_GRID_ALPHA) + #define RAYGUI_GRID_ALPHA 0.15f + #endif + + int result = 0; + GuiState state = guiState; + + Vector2 mousePoint = GUI_POINTER_POSITION; + Vector2 currentMouseCell = { -1, -1 }; + + float spaceWidth = spacing/(float)subdivs; + int linesV = (int)(bounds.width/spaceWidth) + 1; + int linesH = (int)(bounds.height/spaceWidth) + 1; + + int color = GuiGetStyle(DEFAULT, LINE_COLOR); + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + if (CheckCollisionPointRec(mousePoint, bounds)) + { + // NOTE: Cell values must be the upper left of the cell the mouse is in + currentMouseCell.x = floorf((mousePoint.x - bounds.x)/spacing); + currentMouseCell.y = floorf((mousePoint.y - bounds.y)/spacing); + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state == STATE_DISABLED) color = GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED); + + if (subdivs > 0) + { + // Draw vertical grid lines + for (int i = 0; i < linesV; i++) + { + Rectangle lineV = { bounds.x + spacing*i/subdivs, bounds.y, 1, bounds.height + 1 }; + GuiDrawRectangle(lineV, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(color), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(color), RAYGUI_GRID_ALPHA)); + } + + // Draw horizontal grid lines + for (int i = 0; i < linesH; i++) + { + Rectangle lineH = { bounds.x, bounds.y + spacing*i/subdivs, bounds.width + 1, 1 }; + GuiDrawRectangle(lineH, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(color), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(color), RAYGUI_GRID_ALPHA)); + } + } + + if (mouseCell != NULL) *mouseCell = currentMouseCell; + return result; +} + +//---------------------------------------------------------------------------------- +// Tooltip management functions +// NOTE: Tooltips requires some global variables: tooltipPtr +//---------------------------------------------------------------------------------- +// Enable gui tooltips (global state) +void GuiEnableTooltip(void) { guiTooltip = true; } + +// Disable gui tooltips (global state) +void GuiDisableTooltip(void) { guiTooltip = false; } + +// Set tooltip string +void GuiSetTooltip(const char *tooltip) { guiTooltipPtr = tooltip; } + +//---------------------------------------------------------------------------------- +// Styles loading functions +//---------------------------------------------------------------------------------- + +// Load raygui style file (.rgs) +// NOTE: By default a binary file is expected, that file could contain a custom font, +// in that case, custom font image atlas is GRAY+ALPHA and pixel data can be compressed (DEFLATE) +void GuiLoadStyle(const char *fileName) +{ + #define MAX_LINE_BUFFER_SIZE 256 + + bool tryBinary = false; + if (!guiStyleLoaded) GuiLoadStyleDefault(); + + // Try reading the files as text file first + FILE *rgsFile = fopen(fileName, "rt"); + + if (rgsFile != NULL) + { + char buffer[MAX_LINE_BUFFER_SIZE] = { 0 }; + fgets(buffer, MAX_LINE_BUFFER_SIZE, rgsFile); + + if (buffer[0] == '#') + { + int controlId = 0; + int propertyId = 0; + unsigned int propertyValue = 0; + + while (!feof(rgsFile)) + { + switch (buffer[0]) + { + case 'p': + { + // Style property: p + + sscanf(buffer, "p %d %d 0x%x", &controlId, &propertyId, &propertyValue); + GuiSetStyle(controlId, propertyId, (int)propertyValue); + + } break; + case 'f': + { + // Style font: f + + int fontSize = 0; + char charmapFileName[256] = { 0 }; + char fontFileName[256] = { 0 }; + sscanf(buffer, "f %d %s %[^\r\n]s", &fontSize, charmapFileName, fontFileName); + + Font font = { 0 }; + int *codepoints = NULL; + int codepointCount = 0; + + if (charmapFileName[0] != '0') + { + // Load text data from file + // NOTE: Expected an UTF-8 array of codepoints, no separation + char *textData = LoadFileText(TextFormat("%s/%s", GetDirectoryPath(fileName), charmapFileName)); + codepoints = LoadCodepoints(textData, &codepointCount); + UnloadFileText(textData); + } + + if (fontFileName[0] != '\0') + { + // In case a font is already loaded and it is not default internal font, unload it + if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture); + + if (codepointCount > 0) font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, codepoints, codepointCount); + else font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, NULL, 0); // Default to 95 standard codepoints + } + + // If font texture not properly loaded, revert to default font and size/spacing + if (font.texture.id == 0) + { + font = GetFontDefault(); + GuiSetStyle(DEFAULT, TEXT_SIZE, 10); + GuiSetStyle(DEFAULT, TEXT_SPACING, 1); + } + + UnloadCodepoints(codepoints); + + if ((font.texture.id > 0) && (font.glyphCount > 0)) GuiSetFont(font); + + } break; + default: break; + } + + fgets(buffer, MAX_LINE_BUFFER_SIZE, rgsFile); + } + } + else tryBinary = true; + + fclose(rgsFile); + } + + if (tryBinary) + { + rgsFile = fopen(fileName, "rb"); + + if (rgsFile != NULL) + { + fseek(rgsFile, 0, SEEK_END); + int fileDataSize = ftell(rgsFile); + fseek(rgsFile, 0, SEEK_SET); + + if (fileDataSize > 0) + { + unsigned char *fileData = (unsigned char *)RAYGUI_CALLOC(fileDataSize, sizeof(unsigned char)); + if (fileData != NULL) + { + fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile); + + GuiLoadStyleFromMemory(fileData, fileDataSize); + + RAYGUI_FREE(fileData); + } + } + + fclose(rgsFile); + } + } +} + +// Load style default over global style +void GuiLoadStyleDefault(void) +{ + // Setting this flag first to avoid cyclic function calls + // when calling GuiSetStyle() and GuiGetStyle() + guiStyleLoaded = true; + + // Initialize default LIGHT style property values + // WARNING: Default value are applied to all controls on set but + // they can be overwritten later on for every custom control + GuiSetStyle(DEFAULT, BORDER_COLOR_NORMAL, 0x838383ff); + GuiSetStyle(DEFAULT, BASE_COLOR_NORMAL, 0xc9c9c9ff); + GuiSetStyle(DEFAULT, TEXT_COLOR_NORMAL, 0x686868ff); + GuiSetStyle(DEFAULT, BORDER_COLOR_FOCUSED, 0x5bb2d9ff); + GuiSetStyle(DEFAULT, BASE_COLOR_FOCUSED, 0xc9effeff); + GuiSetStyle(DEFAULT, TEXT_COLOR_FOCUSED, 0x6c9bbcff); + GuiSetStyle(DEFAULT, BORDER_COLOR_PRESSED, 0x0492c7ff); + GuiSetStyle(DEFAULT, BASE_COLOR_PRESSED, 0x97e8ffff); + GuiSetStyle(DEFAULT, TEXT_COLOR_PRESSED, 0x368bafff); + GuiSetStyle(DEFAULT, BORDER_COLOR_DISABLED, 0xb5c1c2ff); + GuiSetStyle(DEFAULT, BASE_COLOR_DISABLED, 0xe6e9e9ff); + GuiSetStyle(DEFAULT, TEXT_COLOR_DISABLED, 0xaeb7b8ff); + GuiSetStyle(DEFAULT, BORDER_WIDTH, 1); + GuiSetStyle(DEFAULT, TEXT_PADDING, 0); + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + + // Initialize default extended property values + // NOTE: By default, extended property values are initialized to 0 + GuiSetStyle(DEFAULT, TEXT_SIZE, 10); // DEFAULT, shared by all controls + GuiSetStyle(DEFAULT, TEXT_SPACING, 1); // DEFAULT, shared by all controls + GuiSetStyle(DEFAULT, LINE_COLOR, 0x90abb5ff); // DEFAULT specific property + GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0xf5f5f5ff); // DEFAULT specific property + GuiSetStyle(DEFAULT, TEXT_LINE_SPACING, 5); // DEFAULT, pixels between lines, from bottom of first line to top of second + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE); // DEFAULT, text aligned vertically to middle of text-bounds + + // Initialize control-specific property values + // NOTE: Those properties are in default list but require specific values by control type + GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + GuiSetStyle(BUTTON, BORDER_WIDTH, 2); + GuiSetStyle(SLIDER, TEXT_PADDING, 4); + GuiSetStyle(PROGRESSBAR, TEXT_PADDING, 4); + GuiSetStyle(CHECKBOX, TEXT_PADDING, 4); + GuiSetStyle(CHECKBOX, TEXT_ALIGNMENT, TEXT_ALIGN_RIGHT); + GuiSetStyle(DROPDOWNBOX, TEXT_PADDING, 0); + GuiSetStyle(DROPDOWNBOX, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + GuiSetStyle(TEXTBOX, TEXT_PADDING, 4); + GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + GuiSetStyle(VALUEBOX, TEXT_PADDING, 0); + GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + GuiSetStyle(STATUSBAR, TEXT_PADDING, 8); + GuiSetStyle(STATUSBAR, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + + // Initialize extended property values + // NOTE: By default, extended property values are initialized to 0 + GuiSetStyle(TOGGLE, GROUP_PADDING, 2); + GuiSetStyle(SLIDER, SLIDER_WIDTH, 16); + GuiSetStyle(SLIDER, SLIDER_PADDING, 1); + GuiSetStyle(PROGRESSBAR, PROGRESS_PADDING, 1); + GuiSetStyle(CHECKBOX, CHECK_PADDING, 1); + GuiSetStyle(COMBOBOX, COMBO_BUTTON_WIDTH, 32); + GuiSetStyle(COMBOBOX, COMBO_BUTTON_SPACING, 2); + GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16); + GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 2); + GuiSetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH, 24); + GuiSetStyle(VALUEBOX, SPINNER_BUTTON_SPACING, 2); + GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0); + GuiSetStyle(SCROLLBAR, ARROWS_VISIBLE, 0); + GuiSetStyle(SCROLLBAR, ARROWS_SIZE, 6); + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING, 0); + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, 16); + GuiSetStyle(SCROLLBAR, SCROLL_PADDING, 0); + GuiSetStyle(SCROLLBAR, SCROLL_SPEED, 12); + GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 28); + GuiSetStyle(LISTVIEW, LIST_ITEMS_SPACING, 2); + GuiSetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH, 1); + GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 12); + GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE); + GuiSetStyle(COLORPICKER, COLOR_SELECTOR_SIZE, 8); + GuiSetStyle(COLORPICKER, HUEBAR_WIDTH, 16); + GuiSetStyle(COLORPICKER, HUEBAR_PADDING, 8); + GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT, 8); + GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW, 2); + + if (guiFont.texture.id != GetFontDefault().texture.id) + { + // Unload previous font texture + UnloadTexture(guiFont.texture); + RAYGUI_FREE(guiFont.recs); + RAYGUI_FREE(guiFont.glyphs); + guiFont.recs = NULL; + guiFont.glyphs = NULL; + + // Setup default raylib font + guiFont = GetFontDefault(); + + // NOTE: Default raylib font character 95 is a white square + Rectangle whiteChar = guiFont.recs[95]; + + // NOTE: Setting up a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering + SetShapesTexture(guiFont.texture, RAYGUI_CLITERAL(Rectangle){ whiteChar.x + 1, whiteChar.y + 1, whiteChar.width - 2, whiteChar.height - 2 }); + } +} + +// Get text with icon id prepended +// NOTE: Useful to add icons by name id (enum) instead of +// a number that can change between ricon versions +const char *GuiIconText(int iconId, const char *text) +{ +#if defined(RAYGUI_NO_ICONS) + return NULL; +#else + static char buffer[1024] = { 0 }; + static char iconBuffer[16] = { 0 }; + + if (text != NULL) + { + memset(buffer, 0, 1024); + snprintf(buffer, 1024, "#%03i#", iconId); + + for (int i = 5; i < 1024; i++) + { + buffer[i] = text[i - 5]; + if (text[i - 5] == '\0') break; + } + + return buffer; + } + else + { + snprintf(iconBuffer, 16, "#%03i#", iconId); + + return iconBuffer; + } +#endif +} + +#if !defined(RAYGUI_NO_ICONS) +// Get full icons data pointer +unsigned int *GuiGetIcons(void) { return guiIconsPtr; } + +// Load raygui icons file (.rgi) +// NOTE: In case nameIds are required, they can be requested with loadIconsName, +// they are returned as a guiIconsName[iconCount][RAYGUI_ICON_MAX_NAME_LENGTH], +// WARNING: guiIconsName[]][] memory should be manually freed! +char **GuiLoadIcons(const char *fileName, bool loadIconsName) +{ + // Style File Structure (.rgi) + // ------------------------------------------------------ + // Offset | Size | Type | Description + // ------------------------------------------------------ + // 0 | 4 | char | Signature: "rGI " + // 4 | 2 | short | Version: 100 + // 6 | 2 | short | reserved + + // 8 | 2 | short | Num icons (N) + // 10 | 2 | short | Icons size (Options: 16, 32, 64) (S) + + // Icons name id (32 bytes per name id) + // foreach (icon) + // { + // 12+32*i | 32 | char | Icon NameId + // } + + // Icons data: One bit per pixel, stored as unsigned int array (depends on icon size) + // S*S pixels/32bit per unsigned int = K unsigned int per icon + // foreach (icon) + // { + // ... | K | unsigned int | Icon Data + // } + + FILE *rgiFile = fopen(fileName, "rb"); + + char **guiIconsName = NULL; + + if (rgiFile != NULL) + { + char signature[5] = { 0 }; + short version = 0; + short reserved = 0; + short iconCount = 0; + short iconSize = 0; + + fread(signature, 1, 4, rgiFile); + fread(&version, sizeof(short), 1, rgiFile); + fread(&reserved, sizeof(short), 1, rgiFile); + fread(&iconCount, sizeof(short), 1, rgiFile); + fread(&iconSize, sizeof(short), 1, rgiFile); + + if ((signature[0] == 'r') && + (signature[1] == 'G') && + (signature[2] == 'I') && + (signature[3] == ' ')) + { + if (loadIconsName) + { + guiIconsName = (char **)RAYGUI_CALLOC(iconCount, sizeof(char *)); + for (int i = 0; i < iconCount; i++) + { + guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char)); + fread(guiIconsName[i], 1, RAYGUI_ICON_MAX_NAME_LENGTH, rgiFile); + } + } + else fseek(rgiFile, iconCount*RAYGUI_ICON_MAX_NAME_LENGTH, SEEK_CUR); + + // Read icons data directly over internal icons array + fread(guiIconsPtr, sizeof(unsigned int), (int)iconCount*((int)iconSize*(int)iconSize/32), rgiFile); + } + + fclose(rgiFile); + } + + return guiIconsName; +} + +// Load icons from memory +// WARNING: Binary files only +char **GuiLoadIconsFromMemory(const unsigned char *fileData, int dataSize, bool loadIconsName) +{ + unsigned char *fileDataPtr = (unsigned char *)fileData; + char **guiIconsName = NULL; + + char signature[5] = { 0 }; + short version = 0; + short reserved = 0; + short iconCount = 0; + short iconSize = 0; + + memcpy(signature, fileDataPtr, 4); + memcpy(&version, fileDataPtr + 4, sizeof(short)); + memcpy(&reserved, fileDataPtr + 4 + 2, sizeof(short)); + memcpy(&iconCount, fileDataPtr + 4 + 2 + 2, sizeof(short)); + memcpy(&iconSize, fileDataPtr + 4 + 2 + 2 + 2, sizeof(short)); + fileDataPtr += 12; + + if ((signature[0] == 'r') && + (signature[1] == 'G') && + (signature[2] == 'I') && + (signature[3] == ' ')) + { + if (loadIconsName) + { + guiIconsName = (char **)RAYGUI_CALLOC(iconCount, sizeof(char *)); + for (int i = 0; i < iconCount; i++) + { + guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char)); + memcpy(guiIconsName[i], fileDataPtr, RAYGUI_ICON_MAX_NAME_LENGTH); + fileDataPtr += RAYGUI_ICON_MAX_NAME_LENGTH; + } + } + else + { + // Skip icon name data if not required + fileDataPtr += iconCount*RAYGUI_ICON_MAX_NAME_LENGTH; + } + + int iconDataSize = iconCount*((int)iconSize*(int)iconSize/32)*(int)sizeof(unsigned int); + guiIconsPtr = (unsigned int *)RAYGUI_CALLOC(iconDataSize, 1); + + memcpy(guiIconsPtr, fileDataPtr, iconDataSize); + } + + return guiIconsName; +} + +// Draw selected icon using rectangles pixel-by-pixel +void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color) +{ + #define BIT_CHECK(a,b) ((a) & (1u<<(b))) + + for (int i = 0, y = 0; i < RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32; i++) + { + for (int k = 0; k < 32; k++) + { + if (BIT_CHECK(guiIconsPtr[iconId*RAYGUI_ICON_DATA_ELEMENTS + i], k)) + { + #if !defined(RAYGUI_STANDALONE) + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ (float)posX + (k%RAYGUI_ICON_SIZE)*pixelSize, (float)posY + y*pixelSize, (float)pixelSize, (float)pixelSize }, 0, BLANK, color); + #endif + } + + if ((k == 15) || (k == 31)) y++; + } + } +} + +// Set icon drawing size +void GuiSetIconScale(int scale) +{ + if (scale >= 1) guiIconScale = scale; +} + +// Get text width considering gui style and icon size (if required) +int GuiGetTextWidth(const char *text) +{ + #if !defined(ICON_TEXT_PADDING) + #define ICON_TEXT_PADDING 4 + #endif + + Vector2 textSize = { 0 }; + int textIconOffset = 0; + + if ((text != NULL) && (text[0] != '\0')) + { + if (text[0] == '#') + { + for (int i = 1; (i < 5) && (text[i] != '\0'); i++) + { + if (text[i] == '#') + { + textIconOffset = i; + break; + } + } + } + + text += textIconOffset; + + // Make sure guiFont is set, GuiGetStyle() initializes it lazynessly + float fontSize = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + + // Custom MeasureText() implementation + if ((guiFont.texture.id > 0) && (text != NULL)) + { + // Get size in bytes of text, considering end of line and line break + int size = 0; + for (int i = 0; i < MAX_LINE_BUFFER_SIZE; i++) + { + if ((text[i] != '\0') && (text[i] != '\n')) size++; + else break; + } + + float scaleFactor = fontSize/(float)guiFont.baseSize; + textSize.y = (float)guiFont.baseSize*scaleFactor; + float glyphWidth = 0.0f; + + for (int i = 0, codepointSize = 0; i < size; i += codepointSize) + { + int codepoint = GetCodepointNext(&text[i], &codepointSize); + int codepointIndex = GetGlyphIndex(guiFont, codepoint); + + if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor); + else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor); + + textSize.x += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + } + } + + if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE + ICON_TEXT_PADDING); + } + + return (int)textSize.x; +} + +#endif // !RAYGUI_NO_ICONS + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- +// Load style from memory +// WARNING: Binary files only +static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize) +{ + unsigned char *fileDataPtr = (unsigned char *)fileData; + + char signature[5] = { 0 }; + short version = 0; + short reserved = 0; + int propertyCount = 0; + + memcpy(signature, fileDataPtr, 4); + memcpy(&version, fileDataPtr + 4, sizeof(short)); + memcpy(&reserved, fileDataPtr + 4 + 2, sizeof(short)); + memcpy(&propertyCount, fileDataPtr + 4 + 2 + 2, sizeof(int)); + fileDataPtr += 12; + + if ((signature[0] == 'r') && + (signature[1] == 'G') && + (signature[2] == 'S') && + (signature[3] == ' ')) + { + short controlId = 0; + short propertyId = 0; + unsigned int propertyValue = 0; + + for (int i = 0; i < propertyCount; i++) + { + memcpy(&controlId, fileDataPtr, sizeof(short)); + memcpy(&propertyId, fileDataPtr + 2, sizeof(short)); + memcpy(&propertyValue, fileDataPtr + 2 + 2, sizeof(unsigned int)); + fileDataPtr += 8; + + if (controlId == 0) // DEFAULT control + { + // If a DEFAULT property is loaded, it is propagated to all controls + // NOTE: All DEFAULT properties should be defined first in the file + GuiSetStyle(0, (int)propertyId, propertyValue); + + if (propertyId < RAYGUI_MAX_PROPS_BASE) for (int j = 1; j < RAYGUI_MAX_CONTROLS; j++) GuiSetStyle(j, (int)propertyId, propertyValue); + } + else GuiSetStyle((int)controlId, (int)propertyId, propertyValue); + } + + // Font loading is highly dependant on raylib API to load font data and image + +#if !defined(RAYGUI_STANDALONE) + // Load custom font if available + int fontDataSize = 0; + memcpy(&fontDataSize, fileDataPtr, sizeof(int)); + fileDataPtr += 4; + + if (fontDataSize > 0) + { + Font font = { 0 }; + int fontType = 0; // 0-Normal, 1-SDF + + memcpy(&font.baseSize, fileDataPtr, sizeof(int)); + memcpy(&font.glyphCount, fileDataPtr + 4, sizeof(int)); + memcpy(&fontType, fileDataPtr + 4 + 4, sizeof(int)); + fileDataPtr += 12; + + // Load font white rectangle + Rectangle fontWhiteRec = { 0 }; + memcpy(&fontWhiteRec, fileDataPtr, sizeof(Rectangle)); + fileDataPtr += 16; + + // Load font image parameters + int fontImageUncompSize = 0; + int fontImageCompSize = 0; + memcpy(&fontImageUncompSize, fileDataPtr, sizeof(int)); + memcpy(&fontImageCompSize, fileDataPtr + 4, sizeof(int)); + fileDataPtr += 8; + + Image imFont = { 0 }; + imFont.mipmaps = 1; + memcpy(&imFont.width, fileDataPtr, sizeof(int)); + memcpy(&imFont.height, fileDataPtr + 4, sizeof(int)); + memcpy(&imFont.format, fileDataPtr + 4 + 4, sizeof(int)); + fileDataPtr += 12; + + if ((fontImageCompSize > 0) && (fontImageCompSize != fontImageUncompSize)) + { + // Compressed font atlas image data (DEFLATE), it requires DecompressData() + int dataUncompSize = 0; + unsigned char *compData = (unsigned char *)RAYGUI_CALLOC(fontImageCompSize, sizeof(unsigned char)); + memcpy(compData, fileDataPtr, fontImageCompSize); + fileDataPtr += fontImageCompSize; + + imFont.data = DecompressData(compData, fontImageCompSize, &dataUncompSize); + + // Security check, dataUncompSize must match the provided fontImageUncompSize + if (dataUncompSize != fontImageUncompSize) RAYGUI_LOG("WARNING: Uncompressed font atlas image data could be corrupted"); + + RAYGUI_FREE(compData); + } + else + { + // Font atlas image data is not compressed + imFont.data = (unsigned char *)RAYGUI_CALLOC(fontImageUncompSize, sizeof(unsigned char)); + memcpy(imFont.data, fileDataPtr, fontImageUncompSize); + fileDataPtr += fontImageUncompSize; + } + + if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture); + font.texture = LoadTextureFromImage(imFont); + + RAYGUI_FREE(imFont.data); + + // Validate font atlas texture was loaded correctly + if (font.texture.id != 0) + { + // Load font recs data + int recsDataSize = font.glyphCount*sizeof(Rectangle); + int recsDataCompressedSize = 0; + + // WARNING: Version 400 adds the compression size parameter + if (version >= 400) + { + // RGS files version 400 support compressed recs data + memcpy(&recsDataCompressedSize, fileDataPtr, sizeof(int)); + fileDataPtr += sizeof(int); + } + + if ((recsDataCompressedSize > 0) && (recsDataCompressedSize != recsDataSize)) + { + // Recs data is compressed, uncompress it + unsigned char *recsDataCompressed = (unsigned char *)RAYGUI_CALLOC(recsDataCompressedSize, sizeof(unsigned char)); + + memcpy(recsDataCompressed, fileDataPtr, recsDataCompressedSize); + fileDataPtr += recsDataCompressedSize; + + int recsDataUncompSize = 0; + font.recs = (Rectangle *)DecompressData(recsDataCompressed, recsDataCompressedSize, &recsDataUncompSize); + + // Security check, data uncompressed size must match the expected original data size + if (recsDataUncompSize != recsDataSize) RAYGUI_LOG("WARNING: Uncompressed font recs data could be corrupted"); + + RAYGUI_FREE(recsDataCompressed); + } + else + { + // Recs data is uncompressed + font.recs = (Rectangle *)RAYGUI_CALLOC(font.glyphCount, sizeof(Rectangle)); + for (int i = 0; i < font.glyphCount; i++) + { + memcpy(&font.recs[i], fileDataPtr, sizeof(Rectangle)); + fileDataPtr += sizeof(Rectangle); + } + } + + // Load font glyphs info data + int glyphsDataSize = font.glyphCount*16; // 16 bytes data per glyph + int glyphsDataCompressedSize = 0; + + // WARNING: Version 400 adds the compression size parameter + if (version >= 400) + { + // RGS files version 400 support compressed glyphs data + memcpy(&glyphsDataCompressedSize, fileDataPtr, sizeof(int)); + fileDataPtr += sizeof(int); + } + + // Allocate required glyphs space to fill with data + font.glyphs = (GlyphInfo *)RAYGUI_CALLOC(font.glyphCount, sizeof(GlyphInfo)); + + if ((glyphsDataCompressedSize > 0) && (glyphsDataCompressedSize != glyphsDataSize)) + { + // Glyphs data is compressed, uncompress it + unsigned char *glypsDataCompressed = (unsigned char *)RAYGUI_CALLOC(glyphsDataCompressedSize, sizeof(unsigned char)); + + memcpy(glypsDataCompressed, fileDataPtr, glyphsDataCompressedSize); + fileDataPtr += glyphsDataCompressedSize; + + int glyphsDataUncompSize = 0; + unsigned char *glyphsDataUncomp = DecompressData(glypsDataCompressed, glyphsDataCompressedSize, &glyphsDataUncompSize); + + // Security check, data uncompressed size must match the expected original data size + if (glyphsDataUncompSize != glyphsDataSize) RAYGUI_LOG("WARNING: Uncompressed font glyphs data could be corrupted"); + + unsigned char *glyphsDataUncompPtr = glyphsDataUncomp; + + for (int i = 0; i < font.glyphCount; i++) + { + memcpy(&font.glyphs[i].value, glyphsDataUncompPtr, sizeof(int)); + memcpy(&font.glyphs[i].offsetX, glyphsDataUncompPtr + 4, sizeof(int)); + memcpy(&font.glyphs[i].offsetY, glyphsDataUncompPtr + 8, sizeof(int)); + memcpy(&font.glyphs[i].advanceX, glyphsDataUncompPtr + 12, sizeof(int)); + glyphsDataUncompPtr += 16; + } + + RAYGUI_FREE(glypsDataCompressed); + RAYGUI_FREE(glyphsDataUncomp); + } + else + { + // Glyphs data is uncompressed + for (int i = 0; i < font.glyphCount; i++) + { + memcpy(&font.glyphs[i].value, fileDataPtr, sizeof(int)); + memcpy(&font.glyphs[i].offsetX, fileDataPtr + 4, sizeof(int)); + memcpy(&font.glyphs[i].offsetY, fileDataPtr + 8, sizeof(int)); + memcpy(&font.glyphs[i].advanceX, fileDataPtr + 12, sizeof(int)); + fileDataPtr += 16; + } + } + } + else font = GetFontDefault(); // Fallback in case of errors loading font atlas texture + + GuiSetFont(font); + + // Set font texture source rectangle to be used as white texture to draw shapes + // NOTE: It makes possible to draw shapes and text (full UI) in a single draw call + if ((fontWhiteRec.x > 0) && + (fontWhiteRec.y > 0) && + (fontWhiteRec.width > 0) && + (fontWhiteRec.height > 0)) SetShapesTexture(font.texture, fontWhiteRec); + } +#endif + } +} + +// Get text bounds considering control bounds +static Rectangle GetTextBounds(int control, Rectangle bounds) +{ + Rectangle textBounds = bounds; + + textBounds.x = bounds.x + GuiGetStyle(control, BORDER_WIDTH); + textBounds.y = bounds.y + GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, TEXT_PADDING); + textBounds.width = bounds.width - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING); + textBounds.height = bounds.height - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING); // NOTE: Text is processed line per line! + + // Depending on control, TEXT_PADDING and TEXT_ALIGNMENT properties could affect the text-bounds + switch (control) + { + case COMBOBOX: + case DROPDOWNBOX: + case LISTVIEW: + // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW + case SLIDER: + case CHECKBOX: + case VALUEBOX: + case CONTROL11: + // TODO: More special cases (label on side): SLIDER, CHECKBOX, VALUEBOX, SPINNER + default: + { + // TODO: WARNING: TEXT_ALIGNMENT is already considered in GuiDrawText() + if (GuiGetStyle(control, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT) textBounds.x -= GuiGetStyle(control, TEXT_PADDING); + else textBounds.x += GuiGetStyle(control, TEXT_PADDING); + } + break; + } + + return textBounds; +} + +// Get text icon if provided and move text cursor +// NOTE: Up to #999# values supported for iconId +static const char *GetTextIcon(const char *text, int *iconId) +{ +#if !defined(RAYGUI_NO_ICONS) + *iconId = -1; + if (text[0] == '#') // Maybe it is stars with an icon, ending # must be found + { + char iconValue[4] = { 0 }; // Maximum length for icon value: 3 digits + '\0' + + int pos = 1; + while ((pos < 4) && (text[pos] >= '0') && (text[pos] <= '9')) + { + iconValue[pos - 1] = text[pos]; + pos++; + } + + if (text[pos] == '#') + { + *iconId = TextToInteger(iconValue); + + // Move text pointer after icon + // WARNING: If only icon provided, it could point to EOL character: '\0' + if (*iconId >= 0) text += (pos + 1); + } + } +#endif + + return text; +} + +// Get text divided into lines (by line-breaks '\n') +// WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator! +static const char **GetTextLines(const char *text, int *count) +{ + #define RAYGUI_MAX_TEXT_LINES 128 + + static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; + for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings + + int textLength = (int)strlen(text); + + lines[0] = text; + *count = 1; + + for (int i = 0; (i < textLength) && (*count < RAYGUI_MAX_TEXT_LINES); i++) + { + if ((text[i] == '\n') && ((i + 1) < textLength)) + { + lines[*count] = &text[i + 1]; + *count += 1; + } + } + + return lines; +} + +// Get text width to next space for provided string +static float GetNextSpaceWidth(const char *text, int *nextSpaceIndex) +{ + float width = 0; + int codepointByteCount = 0; + int codepoint = 0; + int index = 0; + float glyphWidth = 0; + float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize; + + for (int i = 0; text[i] != '\0'; i++) + { + if (text[i] != ' ') + { + codepoint = GetCodepoint(&text[i], &codepointByteCount); + index = GetGlyphIndex(guiFont, codepoint); + glyphWidth = (guiFont.glyphs[index].advanceX == 0)? guiFont.recs[index].width*scaleFactor : guiFont.glyphs[index].advanceX*scaleFactor; + width += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + } + else + { + *nextSpaceIndex = i; + break; + } + } + + return width; +} + +// Gui draw text using default font +static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, Color tint) +{ + #define TEXT_VALIGN_PIXEL_OFFSET(h) ((int)h%2) // Vertical alignment for pixel perfect + + #if !defined(ICON_TEXT_PADDING) + #define ICON_TEXT_PADDING 4 + #endif + + if ((text == NULL) || (text[0] == '\0')) return; // Security check + + // PROCEDURE: + // - Text is processed line per line + // - For every line, horizontal alignment is defined + // - For all text, vertical alignment is defined (multiline text only) + // - For every line, wordwrap mode is checked (useful for GuitextBox(), read-only) + + // Get text lines (using '\n' as delimiter) to be processed individually + // WARNING: GuiTextSplit() function can't be used now because it can have already been used + // before the GuiDrawText() call and its buffer is static, it would be overriden :( + int lineCount = 0; + const char **lines = GetTextLines(text, &lineCount); + + // Text style variables + //int alignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT); + int alignmentVertical = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL); + int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); // Wrap-mode only available in read-only mode, no for text editing + + // TODO: WARNING: This totalHeight is not valid for vertical alignment in case of word-wrap + float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); + float posOffsetY = 0.0f; + + for (int i = 0; i < lineCount; i++) + { + int iconId = 0; + lines[i] = GetTextIcon(lines[i], &iconId); // Check text for icon and move cursor + + // Get text position depending on alignment and iconId + //--------------------------------------------------------------------------------- + Vector2 textBoundsPosition = { textBounds.x, textBounds.y }; + float textBoundsWidthOffset = 0.0f; + + // NOTE: Get text size after icon has been processed + // WARNING: GuiGetTextWidth() also processes text icon to get width! -> Really needed? + int textSizeX = GuiGetTextWidth(lines[i]); + + // If text requires an icon, add size to measure + if (iconId >= 0) + { + textSizeX += RAYGUI_ICON_SIZE*guiIconScale; + + // WARNING: If only icon provided, text could be pointing to EOF character: '\0' +#if !defined(RAYGUI_NO_ICONS) + if ((lines[i] != NULL) && (lines[i][0] != '\0')) textSizeX += ICON_TEXT_PADDING; +#endif + } + + // Check guiTextAlign global variables + switch (alignment) + { + case TEXT_ALIGN_LEFT: textBoundsPosition.x = textBounds.x; break; + case TEXT_ALIGN_CENTER: textBoundsPosition.x = textBounds.x + textBounds.width/2 - textSizeX/2; break; + case TEXT_ALIGN_RIGHT: textBoundsPosition.x = textBounds.x + textBounds.width - textSizeX; break; + default: break; + } + + if (textSizeX > textBounds.width && (lines[i] != NULL) && (lines[i][0] != '\0')) textBoundsPosition.x = textBounds.x; + + switch (alignmentVertical) + { + // Only valid in case of wordWrap = 0; + case TEXT_ALIGN_TOP: textBoundsPosition.y = textBounds.y + posOffsetY; break; + case TEXT_ALIGN_MIDDLE: textBoundsPosition.y = textBounds.y + posOffsetY + textBounds.height/2 - totalHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height); break; + case TEXT_ALIGN_BOTTOM: textBoundsPosition.y = textBounds.y + posOffsetY + textBounds.height - totalHeight + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height); break; + default: break; + } + + // NOTE: Make sure getting pixel-perfect coordinates, + // In case of decimals, it could result in text positioning artifacts + textBoundsPosition.x = (float)((int)textBoundsPosition.x); + textBoundsPosition.y = (float)((int)textBoundsPosition.y); + //--------------------------------------------------------------------------------- + + // Draw text (with icon if available) + //--------------------------------------------------------------------------------- +#if !defined(RAYGUI_NO_ICONS) + if (iconId >= 0) + { + // NOTE: Considering icon height, probably different than text size + GuiDrawIcon(iconId, (int)textBoundsPosition.x, (int)(textBounds.y + textBounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height)), guiIconScale, tint); + textBoundsPosition.x += (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); + textBoundsWidthOffset = (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); + } +#endif + // Get size in bytes of text, + // considering end of line and line break + int lineSize = 0; + for (int c = 0; (lines[i][c] != '\0') && (lines[i][c] != '\n') && (lines[i][c] != '\r'); c++, lineSize++){ } + float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize; + + int lastSpaceIndex = 0; + bool tempWrapCharMode = false; + + int textOffsetY = 0; + float textOffsetX = 0.0f; + float glyphWidth = 0; + + int ellipsisWidth = GuiGetTextWidth("..."); + bool textOverflow = false; + for (int c = 0, codepointSize = 0; c < lineSize; c += codepointSize) + { + int codepoint = GetCodepointNext(&lines[i][c], &codepointSize); + int index = GetGlyphIndex(guiFont, codepoint); + + // NOTE: Normally, exiting the decoding sequence as soon as a bad byte is found (and return 0x3f) + // but all of the bad bytes need to be drawn using the '?' symbol, moving one byte + if (codepoint == 0x3f) codepointSize = 1; // TODO: Review not recognized codepoints size + + // Get glyph width to check if it goes out of bounds + if (guiFont.glyphs[index].advanceX == 0) glyphWidth = ((float)guiFont.recs[index].width*scaleFactor); + else glyphWidth = (float)guiFont.glyphs[index].advanceX*scaleFactor; + + // Wrap mode text measuring, to validate if + // it can be drawn or a new line is required + if (wrapMode == TEXT_WRAP_CHAR) + { + // Jump to next line if current character reach end of the box limits + if ((textOffsetX + glyphWidth) > textBounds.width - textBoundsWidthOffset) + { + textOffsetX = 0.0f; + textOffsetY += (GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); + + if (tempWrapCharMode) // Wrap at char level when too long words + { + wrapMode = TEXT_WRAP_WORD; + tempWrapCharMode = false; + } + } + } + else if (wrapMode == TEXT_WRAP_WORD) + { + if (codepoint == 32) lastSpaceIndex = c; + + // Get width to next space in line + int nextSpaceIndex = 0; + float nextSpaceWidth = GetNextSpaceWidth(lines[i] + c, &nextSpaceIndex); + + int nextSpaceIndex2 = 0; + float nextWordSize = GetNextSpaceWidth(lines[i] + lastSpaceIndex + 1, &nextSpaceIndex2); + + if (nextWordSize > textBounds.width - textBoundsWidthOffset) + { + // Considering the case the next word is longer than bounds + tempWrapCharMode = true; + wrapMode = TEXT_WRAP_CHAR; + } + else if ((textOffsetX + nextSpaceWidth) > textBounds.width - textBoundsWidthOffset) + { + textOffsetX = 0.0f; + textOffsetY += (GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); + } + } + + if (codepoint == '\n') break; // WARNING: Lines are already processed manually, no need to keep drawing after this codepoint + else + { + // TODO: There are multiple types of spaces in Unicode, + // maybe it's a good idea to add support for more: http://jkorpela.fi/chars/spaces.html + if ((codepoint != ' ') && (codepoint != '\t')) // Do not draw codepoints with no glyph + { + if (wrapMode == TEXT_WRAP_NONE) + { + // Draw only required text glyphs fitting the textBounds.width + if (textSizeX > textBounds.width) + { + if (textOffsetX <= (textBounds.width - glyphWidth - textBoundsWidthOffset - ellipsisWidth)) + { + DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha)); + } + else if (!textOverflow) + { + textOverflow = true; + + for (int j = 0; j < ellipsisWidth; j += ellipsisWidth/3) + { + DrawTextCodepoint(guiFont, '.', RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX + j, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha)); + } + } + } + else + { + DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha)); + } + } + else if ((wrapMode == TEXT_WRAP_CHAR) || (wrapMode == TEXT_WRAP_WORD)) + { + // Draw only glyphs inside the bounds + if ((textBoundsPosition.y + textOffsetY) <= (textBounds.y + textBounds.height - GuiGetStyle(DEFAULT, TEXT_SIZE))) + { + DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha)); + } + } + } + + if (guiFont.glyphs[index].advanceX == 0) textOffsetX += ((float)guiFont.recs[index].width*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + else textOffsetX += ((float)guiFont.glyphs[index].advanceX*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + } + } + + if (wrapMode == TEXT_WRAP_NONE) posOffsetY += (float)(GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); + else if ((wrapMode == TEXT_WRAP_CHAR) || (wrapMode == TEXT_WRAP_WORD)) posOffsetY += (textOffsetY + (float)GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); + //--------------------------------------------------------------------------------- + } + +#if defined(RAYGUI_DEBUG_TEXT_BOUNDS) + GuiDrawRectangle(textBounds, 0, WHITE, Fade(BLUE, 0.4f)); +#endif +} + +// Gui draw rectangle using default raygui plain style with borders +static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color) +{ + if (color.a > 0) + { + // Draw rectangle filled with color + DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, GuiFade(color, guiAlpha)); + } + + if (borderWidth > 0) + { + // Draw rectangle border lines with color + DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, borderWidth, GuiFade(borderColor, guiAlpha)); + DrawRectangle((int)rec.x, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, GuiFade(borderColor, guiAlpha)); + DrawRectangle((int)rec.x + (int)rec.width - borderWidth, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, GuiFade(borderColor, guiAlpha)); + DrawRectangle((int)rec.x, (int)rec.y + (int)rec.height - borderWidth, (int)rec.width, borderWidth, GuiFade(borderColor, guiAlpha)); + } + +#if defined(RAYGUI_DEBUG_RECS_BOUNDS) + DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, Fade(RED, 0.4f)); +#endif +} + +// Draw tooltip using control bounds +static void GuiTooltip(Rectangle controlRec) +{ + if (!guiLocked && guiTooltip && (guiTooltipPtr != NULL) && !guiControlExclusiveMode) + { + Vector2 textSize = MeasureTextEx(GuiGetFont(), guiTooltipPtr, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + + if ((controlRec.x + textSize.x + 16) > GetScreenWidth()) controlRec.x -= (textSize.x + 16 - controlRec.width); + + int lineCount = 0; + GetTextLines(guiTooltipPtr, &lineCount); // Only using the line count + if ((controlRec.y + controlRec.height + textSize.y + 4 + 8*lineCount) > GetScreenHeight()) + controlRec.y -= (controlRec.height + textSize.y + 4 + 8*lineCount); + + // TODO: Probably TEXT_LINE_SPACING should be considered on panel size instead of hardcoding 8.0f + GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, textSize.y + 8.0f*lineCount }, NULL); + + int textPadding = GuiGetStyle(LABEL, TEXT_PADDING); + int textAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); + GuiSetStyle(LABEL, TEXT_PADDING, 0); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, textSize.y + 8.0f*lineCount }, guiTooltipPtr); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, textAlignment); + GuiSetStyle(LABEL, TEXT_PADDING, textPadding); + } +} + +// Split controls text into multiple strings +// Also check for multiple columns (required by GuiToggleGroup()) +static char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow) +{ + // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) + // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, + // all used memory is static... it has some limitations: + // 1. Maximum number of possible split strings is set by RAYGUI_TEXTSPLIT_MAX_ITEMS + // 2. Maximum size of text to split is RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE + // NOTE: Those definitions could be externally provided if required + + // TODO: HACK: GuiTextSplit() - Review how textRows are returned to user + // textRow is an externally provided array of integers that stores row number for every splitted string + + #if !defined(RAYGUI_TEXTSPLIT_MAX_ITEMS) + #define RAYGUI_TEXTSPLIT_MAX_ITEMS 128 + #endif + #if !defined(RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE) + #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE 1024 + #endif + + static char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL }; // String pointers array (points to buffer data) + static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 }; // Buffer data (text input copy with '\0' added) + memset(buffer, 0, RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE); + + result[0] = buffer; + int counter = 1; + + if (textRow != NULL) textRow[0] = 0; + + // Count how many substrings text contains and point to every one of them + for (int i = 0; i < RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE; i++) + { + buffer[i] = text[i]; + if (buffer[i] == '\0') break; + else if ((buffer[i] == delimiter) || (buffer[i] == '\n')) + { + result[counter] = buffer + i + 1; + + if (textRow != NULL) + { + if (buffer[i] == '\n') textRow[counter] = textRow[counter - 1] + 1; + else textRow[counter] = textRow[counter - 1]; + } + + buffer[i] = '\0'; // Set an end of string at this point + + counter++; + if (counter >= RAYGUI_TEXTSPLIT_MAX_ITEMS) break; + } + } + + *count = counter; + + return result; +} + +// Convert color data from RGB to HSV +// NOTE: Color data should be passed normalized +static Vector3 ConvertRGBtoHSV(Vector3 rgb) +{ + Vector3 hsv = { 0 }; + float min = 0.0f; + float max = 0.0f; + float delta = 0.0f; + + min = (rgb.x < rgb.y)? rgb.x : rgb.y; + min = (min < rgb.z)? min : rgb.z; + + max = (rgb.x > rgb.y)? rgb.x : rgb.y; + max = (max > rgb.z)? max : rgb.z; + + hsv.z = max; // Value + delta = max - min; + + if (delta < 0.00001f) + { + hsv.y = 0.0f; + hsv.x = 0.0f; // Undefined, maybe NAN? + return hsv; + } + + if (max > 0.0f) + { + // NOTE: If max is 0, this divide would cause a crash + hsv.y = (delta/max); // Saturation + } + else + { + // NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined + hsv.y = 0.0f; + hsv.x = 0.0f; // Undefined, maybe NAN? + return hsv; + } + + // NOTE: Comparing float values could not work properly + if (rgb.x >= max) hsv.x = (rgb.y - rgb.z)/delta; // Between yellow & magenta + else + { + if (rgb.y >= max) hsv.x = 2.0f + (rgb.z - rgb.x)/delta; // Between cyan & yellow + else hsv.x = 4.0f + (rgb.x - rgb.y)/delta; // Between magenta & cyan + } + + hsv.x *= 60.0f; // Convert to degrees + + if (hsv.x < 0.0f) hsv.x += 360.0f; + + return hsv; +} + +// Convert color data from HSV to RGB +// NOTE: Color data should be passed normalized +static Vector3 ConvertHSVtoRGB(Vector3 hsv) +{ + Vector3 rgb = { 0 }; + float hh = 0.0f, p = 0.0f, q = 0.0f, t = 0.0f, ff = 0.0f; + long i = 0; + + // NOTE: Comparing float values could not work properly + if (hsv.y <= 0.0f) + { + rgb.x = hsv.z; + rgb.y = hsv.z; + rgb.z = hsv.z; + return rgb; + } + + hh = hsv.x; + if (hh >= 360.0f) hh = 0.0f; + hh /= 60.0f; + + i = (long)hh; + ff = hh - i; + p = hsv.z*(1.0f - hsv.y); + q = hsv.z*(1.0f - (hsv.y*ff)); + t = hsv.z*(1.0f - (hsv.y*(1.0f - ff))); + + switch (i) + { + case 0: + { + rgb.x = hsv.z; + rgb.y = t; + rgb.z = p; + } break; + case 1: + { + rgb.x = q; + rgb.y = hsv.z; + rgb.z = p; + } break; + case 2: + { + rgb.x = p; + rgb.y = hsv.z; + rgb.z = t; + } break; + case 3: + { + rgb.x = p; + rgb.y = q; + rgb.z = hsv.z; + } break; + case 4: + { + rgb.x = t; + rgb.y = p; + rgb.z = hsv.z; + } break; + case 5: + default: + { + rgb.x = hsv.z; + rgb.y = p; + rgb.z = q; + } break; + } + + return rgb; +} + +// Scroll bar control (used by GuiScrollPanel()) +static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) +{ + GuiState state = guiState; + + // Is the scrollbar horizontal or vertical? + bool isVertical = (bounds.width > bounds.height)? false : true; + + // The size (width or height depending on scrollbar type) of the spinner buttons + const int spinnerSize = GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE)? + (isVertical? (int)bounds.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) : + (int)bounds.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH)) : 0; + + // Arrow buttons [<] [>] [∧] [∨] + Rectangle arrowUpLeft = { 0 }; + Rectangle arrowDownRight = { 0 }; + + // Actual area of the scrollbar excluding the arrow buttons + Rectangle scrollbar = { 0 }; + + // Slider bar that moves --[///]----- + Rectangle slider = { 0 }; + + // Normalize value + if (value > maxValue) value = maxValue; + if (value < minValue) value = minValue; + + int valueRange = maxValue - minValue; + if (valueRange <= 0) valueRange = 1; + + int sliderSize = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE); + if (sliderSize < 1) sliderSize = 1; // TODO: Consider a minimum slider size + + // Calculate rectangles for all of the components + arrowUpLeft = RAYGUI_CLITERAL(Rectangle){ + (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), + (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), + (float)spinnerSize, (float)spinnerSize }; + + if (isVertical) + { + arrowDownRight = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + bounds.height - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize }; + scrollbar = RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING), arrowUpLeft.y + arrowUpLeft.height, bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING)), bounds.height - arrowUpLeft.height - arrowDownRight.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) }; + + // Make sure the slider won't get outside of the scrollbar + sliderSize = (sliderSize >= scrollbar.height)? ((int)scrollbar.height - 2) : sliderSize; + slider = RAYGUI_CLITERAL(Rectangle){ + bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING), + scrollbar.y + (int)(((float)(value - minValue)/valueRange)*(scrollbar.height - sliderSize)), + bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)), + (float)sliderSize }; + } + else // horizontal + { + arrowDownRight = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + bounds.width - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize }; + scrollbar = RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x + arrowUpLeft.width, bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING), bounds.width - arrowUpLeft.width - arrowDownRight.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH), bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING)) }; + + // Make sure the slider won't get outside of the scrollbar + sliderSize = (sliderSize >= scrollbar.width)? ((int)scrollbar.width - 2) : sliderSize; + slider = RAYGUI_CLITERAL(Rectangle){ + scrollbar.x + (int)(((float)(value - minValue)/valueRange)*(scrollbar.width - sliderSize)), + bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING), + (float)sliderSize, + bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)) }; + } + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GUI_POINTER_POSITION; + + if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds + { + if (GUI_BUTTON_DOWN && + !CheckCollisionPointRec(mousePoint, arrowUpLeft) && + !CheckCollisionPointRec(mousePoint, arrowDownRight)) + { + if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) + { + state = STATE_PRESSED; + + if (isVertical) value = (int)(((float)(mousePoint.y - scrollbar.y - slider.height/2)*valueRange)/(scrollbar.height - slider.height) + minValue); + else value = (int)(((float)(mousePoint.x - scrollbar.x - slider.width/2)*valueRange)/(scrollbar.width - slider.width) + minValue); + } + } + else + { + guiControlExclusiveMode = false; + guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = STATE_FOCUSED; + + // Handle mouse wheel + float scrollDelta = GUI_SCROLL_DELTA; + if (scrollDelta != 0) value += (int)scrollDelta; + + // Handle mouse button down + if (GUI_BUTTON_PRESSED) + { + guiControlExclusiveMode = true; + guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts + + // Check arrows click + if (CheckCollisionPointRec(mousePoint, arrowUpLeft)) value -= valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + else if (CheckCollisionPointRec(mousePoint, arrowDownRight)) value += valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + else if (!CheckCollisionPointRec(mousePoint, slider)) + { + // If click on scrollbar position but not on slider, place slider directly on that position + if (isVertical) value = (int)(((float)(mousePoint.y - scrollbar.y - slider.height/2)*valueRange)/(scrollbar.height - slider.height) + minValue); + else value = (int)(((float)(mousePoint.x - scrollbar.x - slider.width/2)*valueRange)/(scrollbar.width - slider.width) + minValue); + } + + state = STATE_PRESSED; + } + + // Keyboard control on mouse hover scrollbar + /* + if (isVertical) + { + if (GUI_KEY_DOWN(KEY_DOWN)) value += 5; + else if (GUI_KEY_DOWN(KEY_UP)) value -= 5; + } + else + { + if (GUI_KEY_DOWN(KEY_RIGHT)) value += 5; + else if (GUI_KEY_DOWN(KEY_LEFT)) value -= 5; + } + */ + } + + // Normalize value + if (value > maxValue) value = maxValue; + if (value < minValue) value = minValue; + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(SCROLLBAR, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED))); // Draw the background + + GuiDrawRectangle(scrollbar, 0, BLANK, GetColor(GuiGetStyle(BUTTON, BASE_COLOR_NORMAL))); // Draw the scrollbar active area background + GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BORDER + state*3))); // Draw the slider bar + + // Draw arrows (using icon if available) + if (GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE)) + { +#if defined(RAYGUI_NO_ICONS) + GuiDrawText(isVertical? "^" : "<", + RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); + GuiDrawText(isVertical? "v" : ">", + RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); +#else + GuiDrawText(isVertical? "#121#" : "#118#", + RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3))); // ICON_ARROW_UP_FILL / ICON_ARROW_LEFT_FILL + GuiDrawText(isVertical? "#120#" : "#119#", + RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3))); // ICON_ARROW_DOWN_FILL / ICON_ARROW_RIGHT_FILL +#endif + } + //-------------------------------------------------------------------- + + return value; +} + +// Color fade-in or fade-out, alpha goes from 0.0f to 1.0f +// WARNING: It multiplies current alpha by alpha scale factor +static Color GuiFade(Color color, float alpha) +{ + if (alpha < 0.0f) alpha = 0.0f; + else if (alpha > 1.0f) alpha = 1.0f; + + Color result = { color.r, color.g, color.b, (unsigned char)(color.a*alpha) }; + + return result; +} + +#if defined(RAYGUI_STANDALONE) +// Returns a Color struct from hexadecimal value +static Color GetColor(int hexValue) +{ + Color color; + + color.r = (unsigned char)(hexValue >> 24) & 0xff; + color.g = (unsigned char)(hexValue >> 16) & 0xff; + color.b = (unsigned char)(hexValue >> 8) & 0xff; + color.a = (unsigned char)hexValue & 0xff; + + return color; +} + +// Returns hexadecimal value for a Color +static int ColorToInt(Color color) +{ + return (((int)color.r << 24) | ((int)color.g << 16) | ((int)color.b << 8) | (int)color.a); +} + +// Check if point is inside rectangle +static bool CheckCollisionPointRec(Vector2 point, Rectangle rec) +{ + bool collision = false; + + if ((point.x >= rec.x) && (point.x <= (rec.x + rec.width)) && + (point.y >= rec.y) && (point.y <= (rec.y + rec.height))) collision = true; + + return collision; +} + +// Formatting of text with variables to 'embed' +static const char *TextFormat(const char *text, ...) +{ + #if !defined(RAYGUI_TEXTFORMAT_MAX_SIZE) + #define RAYGUI_TEXTFORMAT_MAX_SIZE 256 + #endif + + static char buffer[RAYGUI_TEXTFORMAT_MAX_SIZE]; + + va_list args; + va_start(args, text); + vsnprintf(buffer, RAYGUI_TEXTFORMAT_MAX_SIZE, text, args); + va_end(args); + + return buffer; +} + +// Draw rectangle with vertical gradient fill color +// NOTE: This function is only used by GuiColorPicker() +static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2) +{ + Rectangle bounds = { (float)posX, (float)posY, (float)width, (float)height }; + DrawRectangleGradientEx(bounds, color1, color2, color2, color1); +} + +// Split string into multiple strings +char **TextSplit(const char *text, char delimiter, int *count) +{ + // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) + // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, + // all used memory is static... it has some limitations: + // 1. Maximum number of possible split strings is set by RAYGUI_TEXTSPLIT_MAX_ITEMS + // 2. Maximum size of text to split is RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE + + #if !defined(RAYGUI_TEXTSPLIT_MAX_ITEMS) + #define RAYGUI_TEXTSPLIT_MAX_ITEMS 128 + #endif + #if !defined(RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE) + #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE 1024 + #endif + + static const char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL }; + static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 }; + memset(buffer, 0, RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE); + + result[0] = buffer; + int counter = 0; + + if (text != NULL) + { + counter = 1; + + // Count how many substrings text contains and point to every one of them + for (int i = 0; i < RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE; i++) + { + buffer[i] = text[i]; + if (buffer[i] == '\0') break; + else if (buffer[i] == delimiter) + { + buffer[i] = '\0'; // Set an end of string at this point + result[counter] = buffer + i + 1; + counter++; + + if (counter == RAYGUI_TEXTSPLIT_MAX_ITEMS) break; + } + } + } + + *count = counter; + return result; +} + +// Get integer value from text +// NOTE: This function replaces atoi() [stdlib.h] +static int TextToInteger(const char *text) +{ + int value = 0; + int sign = 1; + + if ((text[0] == '+') || (text[0] == '-')) + { + if (text[0] == '-') sign = -1; + text++; + } + + for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0'); + + return value*sign; +} + +// Get float value from text +// NOTE: This function replaces atof() [stdlib.h] +// WARNING: Only '.' character is understood as decimal point +static float TextToFloat(const char *text) +{ + float value = 0.0f; + float sign = 1.0f; + + if ((text[0] == '+') || (text[0] == '-')) + { + if (text[0] == '-') sign = -1.0f; + text++; + } + + int i = 0; + for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0'); + + if (text[i++] != '.') value *= sign; + else + { + float divisor = 10.0f; + for (; ((text[i] >= '0') && (text[i] <= '9')); i++) + { + value += ((float)(text[i] - '0'))/divisor; + divisor = divisor*10.0f; + } + } + + return value; +} + +// Encode codepoint into UTF-8 text (char array size returned as parameter) +static const char *CodepointToUTF8(int codepoint, int *byteSize) +{ + static char utf8[6] = { 0 }; + int size = 0; + + if (codepoint <= 0x7f) + { + utf8[0] = (char)codepoint; + size = 1; + } + else if (codepoint <= 0x7ff) + { + utf8[0] = (char)(((codepoint >> 6) & 0x1f) | 0xc0); + utf8[1] = (char)((codepoint & 0x3f) | 0x80); + size = 2; + } + else if (codepoint <= 0xffff) + { + utf8[0] = (char)(((codepoint >> 12) & 0x0f) | 0xe0); + utf8[1] = (char)(((codepoint >> 6) & 0x3f) | 0x80); + utf8[2] = (char)((codepoint & 0x3f) | 0x80); + size = 3; + } + else if (codepoint <= 0x10ffff) + { + utf8[0] = (char)(((codepoint >> 18) & 0x07) | 0xf0); + utf8[1] = (char)(((codepoint >> 12) & 0x3f) | 0x80); + utf8[2] = (char)(((codepoint >> 6) & 0x3f) | 0x80); + utf8[3] = (char)((codepoint & 0x3f) | 0x80); + size = 4; + } + + *byteSize = size; + + return utf8; +} + +// Get next codepoint in a UTF-8 encoded text, scanning until '\0' is found +// When a invalid UTF-8 byte is encountered, exiting as soon as possible and returning a '?'(0x3f) codepoint +// Total number of bytes processed are returned as a parameter +// NOTE: The standard says U+FFFD should be returned in case of errors +// but that character is not supported by the default font in raylib +static int GetCodepointNext(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; +} +#endif // RAYGUI_STANDALONE + +#endif // RAYGUI_IMPLEMENTATION From ea6292e27a4f4fceaa203fb0d381688288c665a6 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 4 Apr 2026 11:12:30 +0200 Subject: [PATCH 260/319] REVIEWED: example: `audio_amp_envelope`, code formating to follow raylib code conventions #5713 --- examples/audio/audio_amp_envelope.c | 83 +++++++++++++++-------------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/examples/audio/audio_amp_envelope.c b/examples/audio/audio_amp_envelope.c index 9d6939e35..2e51306c9 100644 --- a/examples/audio/audio_amp_envelope.c +++ b/examples/audio/audio_amp_envelope.c @@ -20,10 +20,10 @@ #define RAYGUI_IMPLEMENTATION #include "raygui.h" -#include +#include // Required for: sinf() -#define BUFFER_SIZE 4096 -#define SAMPLE_RATE 44100 +#define BUFFER_SIZE 4096 +#define SAMPLE_RATE 44100 // Wave state typedef enum { @@ -47,9 +47,9 @@ typedef struct { //------------------------------------------------------------------------------------ // Module Functions Declaration //------------------------------------------------------------------------------------ -static void FillAudioBuffer(int i, float* buffer, float envelopeValue, float* audioTime); -static void UpdateEnvelope(Envelope* env); -static void DrawADSRGraph(const Envelope *env, Rectangle bounds); +static void FillAudioBuffer(int i, float *buffer, float envelopeValue, float *audioTime); +static void UpdateEnvelope(Envelope *env); +static void DrawADSRGraph(Envelope *env, Rectangle bounds); //------------------------------------------------------------------------------------ // Program main entry point @@ -93,24 +93,22 @@ int main(void) { // Update //---------------------------------------------------------------------------------- + if (IsKeyPressed(KEY_SPACE)) env.state = ATTACK; - if (IsKeyPressed(KEY_SPACE)) { - env.state = ATTACK; - } + if (IsKeyReleased(KEY_SPACE) && (env.state != IDLE)) env.state = RELEASE; - if (IsKeyReleased(KEY_SPACE)) { - if (env.state != IDLE) env.state = RELEASE; - } - - if (IsAudioStreamProcessed(stream)) { - - if (env.state != IDLE || env.currentValue > 0.0f) { + if (IsAudioStreamProcessed(stream)) + { + if ((env.state != IDLE) || (env.currentValue > 0.0f)) + { for (int i = 0; i < BUFFER_SIZE; i++) { UpdateEnvelope(&env); FillAudioBuffer(i, buffer, env.currentValue, &audioTime); } - } else { + } + else + { // Clear buffer if silent to avoid looping noise for (int i = 0; i < BUFFER_SIZE; i++) buffer[i] = 0; audioTime = 0.0f; @@ -139,9 +137,11 @@ int main(void) DrawText(TextFormat("Current Gain: %2.2f", env.currentValue), 535, 345 - (env.currentValue * 100), 10, MAROON); DrawText("Press SPACE to PLAY the sound!", 200, 400, 20, LIGHTGRAY); + EndDrawing(); //---------------------------------------------------------------------------------- } + // De-Initialization //-------------------------------------------------------------------------------------- UnloadAudioStream(stream); @@ -156,57 +156,60 @@ int main(void) //------------------------------------------------------------------------------------ // Module Functions Definition //------------------------------------------------------------------------------------ -static void FillAudioBuffer(int i, float* buffer, float envelopeValue, float* audioTime) +static void FillAudioBuffer(int i, float *buffer, float envelopeValue, float *audioTime) { - int frequency = 440; - buffer[i] = envelopeValue * sinf(2.0f * PI * frequency * (*audioTime)); - *audioTime += 1.0f / SAMPLE_RATE; + buffer[i] = envelopeValue*sinf(2.0f*PI*frequency*(*audioTime)); + *audioTime += (1.0f/SAMPLE_RATE); } static void UpdateEnvelope(Envelope *env) { // Calculate the time delta for ONE sample (1/44100) - float sampleTime = 1.0f / SAMPLE_RATE; + float sampleTime = 1.0f/SAMPLE_RATE; switch(env->state) { case ATTACK: - env->currentValue += (1.0f / env->attackTime) * sampleTime; + { + env->currentValue += (1.0f/env->attackTime)*sampleTime; if (env->currentValue >= 1.0f) { env->currentValue = 1.0f; env->state = DECAY; } - break; - + + } break; case DECAY: - env->currentValue -= ((1.0f - env->sustainLevel) / env->decayTime) * sampleTime; + { + env->currentValue -= ((1.0f - env->sustainLevel)/env->decayTime)*sampleTime; if (env->currentValue <= env->sustainLevel) { env->currentValue = env->sustainLevel; env->state = SUSTAIN; } - break; - + + } break; case SUSTAIN: + { env->currentValue = env->sustainLevel; - break; - + + } break; case RELEASE: - env->currentValue -= (env->sustainLevel / env->releaseTime) * sampleTime; + { + env->currentValue -= (env->sustainLevel/env->releaseTime)*sampleTime; if (env->currentValue <= 0.001f) // Use a small threshold to avoid infinite tail { env->currentValue = 0.0f; env->state = IDLE; } - break; + } break; default: break; } } -static void DrawADSRGraph(const Envelope *env, Rectangle bounds) +static void DrawADSRGraph(Envelope *env, Rectangle bounds) { DrawRectangleRec(bounds, Fade(LIGHTGRAY, 0.3f)); DrawRectangleLinesEx(bounds, 1, GRAY); @@ -217,14 +220,14 @@ static void DrawADSRGraph(const Envelope *env, Rectangle bounds) // Total time to visualize (sum of A, D, R + a padding for Sustain) float totalTime = env->attackTime + env->decayTime + sustainWidth + env->releaseTime; - float scaleX = bounds.width / totalTime; + float scaleX = bounds.width/totalTime; float scaleY = bounds.height; - Vector2 start = { bounds.x, bounds.y + bounds.height }; - Vector2 peak = { start.x + (env->attackTime * scaleX), bounds.y }; - Vector2 sustain = { peak.x + (env->decayTime * scaleX), bounds.y + (1.0f - env->sustainLevel) * scaleY }; - Vector2 rel = { sustain.x + (sustainWidth * scaleX), sustain.y }; - Vector2 end = { rel.x + (env->releaseTime * scaleX), bounds.y + bounds.height }; + Vector2 start = { bounds.x, bounds.y + bounds.height }; + Vector2 peak = { start.x + (env->attackTime*scaleX), bounds.y }; + Vector2 sustain = { peak.x + (env->decayTime*scaleX), bounds.y + (1.0f - env->sustainLevel)*scaleY }; + Vector2 rel = { sustain.x + (sustainWidth*scaleX), sustain.y }; + Vector2 end = { rel.x + (env->releaseTime*scaleX), bounds.y + bounds.height }; DrawLineV(start, peak, SKYBLUE); DrawLineV(peak, sustain, BLUE); @@ -232,4 +235,4 @@ static void DrawADSRGraph(const Envelope *env, Rectangle bounds) DrawLineV(rel, end, ORANGE); DrawText("ADSR Visualizer", bounds.x, bounds.y - 20, 10, DARKGRAY); -} \ No newline at end of file +} From 6ff51d3a2aa3866d81f3a4ca7f4088c51325d02a Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 4 Apr 2026 11:13:29 +0200 Subject: [PATCH 261/319] Update audio_amp_envelope.c --- examples/audio/audio_amp_envelope.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/audio/audio_amp_envelope.c b/examples/audio/audio_amp_envelope.c index 2e51306c9..fcc27bf56 100644 --- a/examples/audio/audio_amp_envelope.c +++ b/examples/audio/audio_amp_envelope.c @@ -93,7 +93,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - if (IsKeyPressed(KEY_SPACE)) env.state = ATTACK; + if (IsKeyPressed(KEY_SPACE)) env.state = ATTACK; if (IsKeyReleased(KEY_SPACE) && (env.state != IDLE)) env.state = RELEASE; @@ -106,14 +106,14 @@ int main(void) UpdateEnvelope(&env); FillAudioBuffer(i, buffer, env.currentValue, &audioTime); } - } + } else { // Clear buffer if silent to avoid looping noise for (int i = 0; i < BUFFER_SIZE; i++) buffer[i] = 0; audioTime = 0.0f; } - + UpdateAudioStream(stream, buffer, BUFFER_SIZE); } @@ -166,7 +166,7 @@ static void FillAudioBuffer(int i, float *buffer, float envelopeValue, float *au static void UpdateEnvelope(Envelope *env) { // Calculate the time delta for ONE sample (1/44100) - float sampleTime = 1.0f/SAMPLE_RATE; + float sampleTime = 1.0f/SAMPLE_RATE; switch(env->state) { @@ -178,7 +178,7 @@ static void UpdateEnvelope(Envelope *env) env->currentValue = 1.0f; env->state = DECAY; } - + } break; case DECAY: { @@ -188,12 +188,12 @@ static void UpdateEnvelope(Envelope *env) env->currentValue = env->sustainLevel; env->state = SUSTAIN; } - + } break; case SUSTAIN: { env->currentValue = env->sustainLevel; - + } break; case RELEASE: { @@ -203,8 +203,8 @@ static void UpdateEnvelope(Envelope *env) env->currentValue = 0.0f; env->state = IDLE; } - - } break; + + } break; default: break; } } @@ -219,7 +219,7 @@ static void DrawADSRGraph(Envelope *env, Rectangle bounds) // Total time to visualize (sum of A, D, R + a padding for Sustain) float totalTime = env->attackTime + env->decayTime + sustainWidth + env->releaseTime; - + float scaleX = bounds.width/totalTime; float scaleY = bounds.height; From 138ef838d91b567a31fe782f2221a77d9076eeb6 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Sat, 4 Apr 2026 16:47:33 +0200 Subject: [PATCH 262/319] [rlsw] Some platform fixes (#5720) * fix drm with rlsw * fix window resizing with sdl * fix window resizing with win32 --- src/platforms/rcore_desktop_sdl.c | 4 ++++ src/platforms/rcore_desktop_win32.c | 4 ++++ src/platforms/rcore_drm.c | 8 +------- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 92c1979b3..dd845131e 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1528,6 +1528,10 @@ void PollInputEvents(void) if ((width + borderLeft + borderRight != usableBounds.w) && (height + borderTop + borderBottom != usableBounds.h)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED); } #endif + + #if defined(GRAPHICS_API_OPENGL_SOFTWARE) + swResize(width, height); + #endif } break; case SDL_WINDOWEVENT_ENTER: CORE.Input.Mouse.cursorOnScreen = true; break; diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index afbbedffc..5b19cd41a 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -2091,6 +2091,10 @@ static void HandleWindowResize(HWND hwnd, int *width, int *height) CORE.Window.screenScale = MatrixScale( (float)CORE.Window.render.width/CORE.Window.screen.width, (float)CORE.Window.render.height/CORE.Window.screen.height, 1.0f); + + #if defined(GRAPHICS_API_OPENGL_SOFTWARE) + swResize(clientSize.cx, clientSize.cy); + #endif } // Update window style diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index faba8956a..e742cb625 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -837,14 +837,8 @@ void SwapScreenBuffer(void) uint32_t height = mode->vdisplay; // Dumb buffers use a fixed format based on bpp -#if SW_COLOR_BUFFER_BITS == 24 const uint32_t bpp = 32; // 32 bits per pixel (XRGB8888 format) const uint32_t depth = 24; // Color depth, here only 24 bits, alpha is not used -#else - // REVIEW: Not sure how it will be interpreted (RGB or RGBA?) - const uint32_t bpp = SW_COLOR_BUFFER_BITS; - const uint32_t depth = SW_COLOR_BUFFER_BITS; -#endif // Create a dumb buffer for software rendering struct drm_mode_create_dumb creq = { 0 }; @@ -899,7 +893,7 @@ void SwapScreenBuffer(void) // Copy the software rendered buffer to the dumb buffer with scaling if needed // NOTE: RLSW will make a simple copy if the dimensions match - swBlitFramebuffer(0, 0, width, height, 0, 0, width, height, SW_RGBA, SW_UNSIGNED_BYTE, dumbBuffer); + swBlitPixels(0, 0, width, height, 0, 0, width, height, SW_RGBA, SW_UNSIGNED_BYTE, dumbBuffer); // Unmap the buffer munmap(dumbBuffer, creq.size); From cdff3d7bea31374af661910014ac07cbe2e87fa9 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 4 Apr 2026 16:50:10 +0200 Subject: [PATCH 263/319] Update CHANGELOG --- CHANGELOG | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index cc44aac2c..92e4c6bbd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,7 +4,7 @@ changelog Current Release: raylib 5.5 (18 November 2024) ------------------------------------------------------------------------- -Release: raylib 6.0 (?? March 2026) +Release: raylib 6.0 (23 April 2026) ------------------------------------------------------------------------- KEY CHANGES: - New Software Renderer backend [rlsw] @@ -285,6 +285,7 @@ Detailed changes: [rtext] REVIEWED: `TextToFloat()`, remove removed inaccurate comment (#4596) by @hexmaster111 [rtext] REDESIGNED: `LoadFontData()`, added input parameter by @raysan5 -WARNING- [rmodels] ADDED: Support CPU animation in OpenGL 1.1 (#4925) by @JeffM2501 +[rmodels] REMOVED: `DrawModelPoints()` and `DrawModelPointsEx()`, moved to an example (#5697) by @maiconpintoabreu -WARNING- [rmodels] RENAMED: Skinning shader variables (new default naming) by @raysan5 [rmodels] REVIEWED: glTF animation framerate calculation (#4472, #5445) by @TheLazyIndianTechie [rmodels] REVIEWED: Assign meshes without bone weights to the bone they are attached to so they animate by @Jeffm2501 @@ -311,6 +312,7 @@ Detailed changes: [rmodels] REVIEWED: `LoadModelAnimationsGLTF()`, properly load 1 frame animations (#5561) by @arlez80 [rmodels] REVIEWED: `LoadModelAnimationsGLTF()`, anim correctly inherits world transform (#5206) by @ArmanOmmid [rmodels] REVIEWED: `UploadMesh()`, improve default normal and tangent values (#4763) by @Bigfoot71 +[rmodels] REVIEWED: `UplaodMesh()`, fix for devices without VAO support (#5692) by @psxdev [rmodels] REVIEWED: `GenMeshTangents()`, improvements (#4937) by @Bigfoot71 [rmodels] REVIEWED: `UpdateModelAnimation()` optimization (#5244) by @Arrangemonk [rmodels] REVIEWED: `UpdateModelAnimationBones()`, break on first mesh found and formating by @raysan5 @@ -322,6 +324,7 @@ Detailed changes: [rmodels] REVIEWED: `DrawMeshInstanced()`, breaking if instanceTransform was unused (#5469) by @al13n321 [rmodels] REVIEWED: `DrawSphereEx()`, normals support (#4926) by @karl-zylinski [rmodels] REVIEWED: `ExportMesh()`, improve OBJ vertex data precision and lower memory usage (#4496) by @mikeemm +[rmodels] REVIEWED: `CheckCollisionSpheres()`, simplified using `Vector3DistanceSqr()` (#5695) by @Bigfoot71 [raudio] REVIEWED: Fix a glitch at the end of a sound (#5578) by @mackron [raudio] REVIEWED: Improvements to device configuration (#5577) by @mackron [raudio] REVIEWED: Initialize sound alias properties as if it was a new sound (#5123) by @JeffM2501 From 92f82b4add6ac57b871dd7c9b749ce6874d3bef1 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 4 Apr 2026 16:50:15 +0200 Subject: [PATCH 264/319] Update raylib.h --- src/raylib.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index c6aad860a..6849e7176 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1539,11 +1539,11 @@ RLAPI const char *TextSubtext(const char *text, int position, int length); RLAPI const char *TextRemoveSpaces(const char *text); // Remove text spaces, concat words RLAPI char *GetTextBetween(const char *text, const char *begin, const char *end); // Get text between two strings RLAPI char *TextReplace(const char *text, const char *search, const char *replacement); // Replace text string with new string -RLAPI char *TextReplaceAlloc(const char *text, const char *search, const char *replacement); // Replace text string with new string, memory must be MemFree() +RLAPI char *TextReplaceAlloc(const char *text, const char *search, const char *replacement); // Replace text string with new string, memory must be MemFree() RLAPI char *TextReplaceBetween(const char *text, const char *begin, const char *end, const char *replacement); // Replace text between two specific strings RLAPI char *TextReplaceBetweenAlloc(const char *text, const char *begin, const char *end, const char *replacement); // Replace text between two specific strings, memory must be MemFree() RLAPI char *TextInsert(const char *text, const char *insert, int position); // Insert text in a defined byte position -RLAPI char *TextInsertAlloc(const char *text, const char *insert, int position); // Insert text in a defined byte position, memory must be MemFree() +RLAPI char *TextInsertAlloc(const char *text, const char *insert, int position); // Insert text in a defined byte position, memory must be MemFree() 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 From 1122add3eeaeeac6f63406ed010db5e5e957f96b Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 4 Apr 2026 17:36:18 +0200 Subject: [PATCH 265/319] Minor format tweak --- src/external/rlsw.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/external/rlsw.h b/src/external/rlsw.h index 0342211d2..c9ace86cd 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -2357,7 +2357,8 @@ static inline bool sw_texture_alloc(sw_texture_t *texture, const void *data, int sw_pixel_read_color8_f readColor8 = NULL; sw_pixel_read_color_f readColor = NULL; - if (!isDepth) { + if (!isDepth) + { readColor8 = sw_pixel_get_read_color8_func(format); readColor = sw_pixel_get_read_color_func(format); } @@ -2803,7 +2804,8 @@ static const sw_blend_f SW_BLEND_TABLE[SW_BLEND_FACTOR_COUNT][SW_BLEND_FACTOR_CO // Maps a GL blend factor enum to its compact table index static inline int sw_blend_factor_index(SWfactor f) { - switch (f) { + switch (f) + { case SW_ZERO: return 0; case SW_ONE: return 1; case SW_SRC_COLOR: return 2; @@ -2821,7 +2823,8 @@ static inline int sw_blend_factor_index(SWfactor f) static bool sw_blend_factor_needs_alpha(SWfactor f) { - switch (f) { + switch (f) + { case SW_SRC_ALPHA: case SW_ONE_MINUS_SRC_ALPHA: case SW_DST_ALPHA: @@ -2829,6 +2832,7 @@ static bool sw_blend_factor_needs_alpha(SWfactor f) case SW_SRC_ALPHA_SATURATE: return true; default: break; } + return false; } From 6ef0044381c71d76ba0784e8c98aab28e26b817a Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 4 Apr 2026 17:37:25 +0200 Subject: [PATCH 266/319] REVIEWED: Window messages `WM_SIZING`, `WM_SIZE`, `WM_WINDOWPOSCHANGED` #5720 --- src/platforms/rcore_desktop_win32.c | 46 ++++++++++++++++++----------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index 5b19cd41a..07262aaea 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -1063,14 +1063,14 @@ Vector2 GetMonitorPosition(int monitor) // Get selected monitor width (currently used by monitor) int GetMonitorWidth(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorWidth not implemented"); + //TRACELOG(LOG_WARNING, "GetMonitorWidth not implemented"); return 0; } // Get selected monitor height (currently used by monitor) int GetMonitorHeight(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorHeight not implemented"); + //TRACELOG(LOG_WARNING, "GetMonitorHeight not implemented"); return 0; } @@ -1105,7 +1105,7 @@ const char *GetMonitorName(int monitor) // Get window position XY on monitor Vector2 GetWindowPosition(void) { - TRACELOG(LOG_WARNING, "GetWindowPosition not implemented"); + //TRACELOG(LOG_WARNING, "GetWindowPosition not implemented"); return (Vector2){ 0, 0 }; } @@ -1760,13 +1760,31 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara memset(CORE.Input.Keyboard.previousKeyState, 0, sizeof(CORE.Input.Keyboard.previousKeyState)); memset(CORE.Input.Keyboard.currentKeyState, 0, sizeof(CORE.Input.Keyboard.currentKeyState)); } break; - case WM_SIZING: + case WM_SIZING: // Sent to a window that the user is resizing { - if (!(CORE.Window.flags & FLAG_WINDOW_RESIZABLE)) - TRACELOG(LOG_WARNING, "WIN32: WINDOW: Trying to resize a non-resizable window"); + if (CORE.Window.flags & FLAG_WINDOW_RESIZABLE) + { + //HandleWindowResize(hwnd, &platform.appScreenWidth, &platform.appScreenHeight); + } result = TRUE; } break; + case WM_SIZE: + { + // WARNING: Don't trust the docs, they say this message can not be obtained if not calling DefWindowProc() + // in response to WM_WINDOWPOSCHANGED but looks like when a window is created, + // this message can be obtained without getting WM_WINDOWPOSCHANGED + +#if defined(GRAPHICS_API_OPENGL_SOFTWARE) + // WARNING: Waiting two frames before resizing because software-renderer backend is initilized with swInit() later + // than InitPlatform(), that triggers WM_SIZE, so avoid crashing + if (CORE.Time.frameCounter > 2) HandleWindowResize(hwnd, &platform.appScreenWidth, &platform.appScreenHeight); +#else + // NOTE: This message is only triggered on window creation + HandleWindowResize(hwnd, &platform.appScreenWidth, &platform.appScreenHeight); +#endif + result = 0; // If an application processes WM_SIZE message, it should return zero + } break; case WM_GETMINMAXINFO: { DWORD style = MakeWindowStyle(platform.desiredFlags); @@ -1865,19 +1883,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara default: break; } } break; - case WM_SIZE: - { - // WARNING: Don't trust the docs, they say this message can not be obtained if not calling DefWindowProc() - // in response to WM_WINDOWPOSCHANGED but looks like when a window is created, - // this message can be obtained without getting WM_WINDOWPOSCHANGED - // WARNING: This call fails for Software-Renderer backend - //HandleWindowResize(hwnd, &platform.appScreenWidth, &platform.appScreenHeight); - } break; - //case WM_MOVE + //case WM_MOVE: break; case WM_WINDOWPOSCHANGED: { WINDOWPOS *pos = (WINDOWPOS*)lparam; if (!(pos->flags & SWP_NOSIZE)) HandleWindowResize(hwnd, &platform.appScreenWidth, &platform.appScreenHeight); + + DefWindowProc(hwnd, msg, wparam, lparam); } break; case WM_GETDPISCALEDSIZE: { @@ -2092,9 +2104,9 @@ static void HandleWindowResize(HWND hwnd, int *width, int *height) CORE.Window.screenScale = MatrixScale( (float)CORE.Window.render.width/CORE.Window.screen.width, (float)CORE.Window.render.height/CORE.Window.screen.height, 1.0f); - #if defined(GRAPHICS_API_OPENGL_SOFTWARE) +#if defined(GRAPHICS_API_OPENGL_SOFTWARE) swResize(clientSize.cx, clientSize.cy); - #endif +#endif } // Update window style From 4a6ceb9c76125c5b0c0f2ba2cea63b4b6db931fa Mon Sep 17 00:00:00 2001 From: Krzysztof Szenk Date: Sun, 5 Apr 2026 10:50:31 +0200 Subject: [PATCH 267/319] [rcore_rgfw] Icon color format fix (#5724) After RGFW update to 2.0.0-dev in fbd83cafc7c51ecc38be7f7b19c185a42f333cb0, RGFW_window_setIcon api has changed -- not it takes pixel format enum instead of number of channels. On raylib side it was still passing 4 (number of channels in rgba) which is enum value for BGRA8. Instead we have to pass 2 now (RGFW_formatRGBA8 = 2). --- src/platforms/rcore_desktop_rgfw.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 11f268d9f..28a617af9 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -723,7 +723,7 @@ void SetWindowIcon(Image image) TRACELOG(LOG_WARNING, "RGFW: Window icon image must be in R8G8B8A8 pixel format"); return; } - RGFW_window_setIcon(platform.window, (u8 *)image.data, image.width, image.height, 4); + RGFW_window_setIcon(platform.window, (u8 *)image.data, image.width, image.height, RGFW_formatRGBA8); } // Set icon for window @@ -749,8 +749,8 @@ void SetWindowIcons(Image *images, int count) if ((smallIcon == NULL) || ((images[i].width < smallIcon->width) && (images[i].height > smallIcon->height))) smallIcon = &images[i]; } - if (smallIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)smallIcon->data, smallIcon->width, smallIcon->height, 4, RGFW_iconWindow); - if (bigIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)bigIcon->data, bigIcon->width, bigIcon->height, 4, RGFW_iconTaskbar); + if (smallIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)smallIcon->data, smallIcon->width, smallIcon->height, RGFW_formatRGBA8, RGFW_iconWindow); + if (bigIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)bigIcon->data, bigIcon->width, bigIcon->height, RGFW_formatRGBA8, RGFW_iconTaskbar); } } From c4bd4faab561efb4ecb9e79c9495ce4bd907cc90 Mon Sep 17 00:00:00 2001 From: Peter0x44 Date: Sun, 5 Apr 2026 10:01:44 +0100 Subject: [PATCH 268/319] [build][CMake] REVIEWED: DRM software renderer configuration (#5721) https://github.com/raysan5/raylib/pull/5720#issuecomment-4187113099 In this comment, it was pointed out that LibraryConfigurations.cmake still tries to find and link egl, gbm, and glesv2, even when using the software renderer is it not necessary. This patch addresses that. --- cmake/LibraryConfigurations.cmake | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index 7af078de0..4a8eb7d55 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -96,21 +96,30 @@ elseif (${PLATFORM} STREQUAL "Android") elseif ("${PLATFORM}" STREQUAL "DRM") set(PLATFORM_CPP "PLATFORM_DRM") - set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") add_definitions(-D_DEFAULT_SOURCE) - add_definitions(-DEGL_NO_X11) add_definitions(-DPLATFORM_DRM) - find_library(GLESV2 GLESv2) - find_library(EGL EGL) find_library(DRM drm) - find_library(GBM gbm) if (NOT CMAKE_CROSSCOMPILING OR NOT CMAKE_SYSROOT) include_directories(/usr/include/libdrm) endif () - set(LIBS_PRIVATE ${GLESV2} ${EGL} ${DRM} ${GBM} atomic pthread dl) + + if ("${OPENGL_VERSION}" STREQUAL "Software") + # software rendering does not require EGL/GBM. + set(GRAPHICS "GRAPHICS_API_OPENGL_SOFTWARE") + set(LIBS_PRIVATE ${DRM} atomic pthread dl) + else () + set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") + add_definitions(-DEGL_NO_X11) + + find_library(GLESV2 GLESv2) + find_library(EGL EGL) + find_library(GBM gbm) + + set(LIBS_PRIVATE ${GLESV2} ${EGL} ${DRM} ${GBM} atomic pthread dl) + endif () set(LIBS_PUBLIC m) elseif ("${PLATFORM}" STREQUAL "SDL") From d3c6f426b4c9e22fd51418082ca321ed7f523321 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Apr 2026 11:02:26 +0200 Subject: [PATCH 269/319] Update for software renderer on DRM #5721 --- build.zig | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/build.zig b/build.zig index ec254debf..1aedc58fe 100644 --- a/build.zig +++ b/build.zig @@ -202,8 +202,10 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. raylib.root_module.addCMacro("GRAPHICS_API_OPENGL_ES2", ""); } - raylib.root_module.linkSystemLibrary("EGL", .{}); - raylib.root_module.linkSystemLibrary("gbm", .{}); + if (options.opengl_version != .gl_soft) { + raylib.root_module.linkSystemLibrary("EGL", .{}); + raylib.root_module.linkSystemLibrary("gbm", .{}); + } raylib.root_module.linkSystemLibrary("libdrm", .{ .use_pkg_config = .force }); raylib.root_module.addCMacro("PLATFORM_DRM", ""); @@ -416,6 +418,7 @@ pub const Options = struct { pub const OpenglVersion = enum { auto, + gl_soft, gl_1_1, gl_2_1, gl_3_3, @@ -426,6 +429,7 @@ pub const OpenglVersion = enum { pub fn toCMacroStr(self: @This()) []const u8 { switch (self) { .auto => @panic("OpenglVersion.auto cannot be turned into a C macro string"), + .gl_soft => return "GRAPHICS_API_OPENGL_SOFTWARE", .gl_1_1 => return "GRAPHICS_API_OPENGL_11", .gl_2_1 => return "GRAPHICS_API_OPENGL_21", .gl_3_3 => return "GRAPHICS_API_OPENGL_33", From c5fc7716229cef1727e7baf325a695a0ac00cf27 Mon Sep 17 00:00:00 2001 From: Angshuman Kishore Mahato <148174774+angshumankishore@users.noreply.github.com> Date: Mon, 6 Apr 2026 04:08:04 +0530 Subject: [PATCH 270/319] removed the first RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEMATRICES (#5727) --- src/rlgl.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index e97bad36a..923f84dfd 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1015,9 +1015,7 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS #endif -#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES - #define RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES "boneMatrices" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEMATRICES -#endif + #ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORM #define RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORM "instanceTransform" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORM #endif From 05a14456ae090378d8b38505008af3de45e8074d Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 9 Apr 2026 11:16:31 +0200 Subject: [PATCH 271/319] REDESIGNED: `DrawCircleGradieent()` to use Vector2 as center parameter #5738 --- CHANGELOG | 1 + src/raylib.h | 4 ++-- src/rshapes.c | 32 ++++++++++++++++---------------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 92e4c6bbd..e049512bd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -221,6 +221,7 @@ Detailed changes: [rlsw] REVIEWED: C++ support (#5291) by @alexgb0 [rshapes] ADDED: `DrawLineDashed()` (#5222) by @luis605 [rshapes] ADDED: `DrawEllipseV()` and `DrawEllipseLinesV()` (#4963) by @meowstr +[rshapes] REDESIGNED: `DrawCircleGradieent()` to use Vector2 as center parameter by @raysan5 -WARNING- [rshapes] REVIEWED: `DrawLine()`, fix pixel offset issue on drawing (#4666) by @Bigfoot71 [rshapes] REVIEWED: `DrawRectangleGradientEx()`, incorrect parameter names (#4980) by @vincent [rshapes] REVIEWED: `DrawRectangleLines`, fix pixel offset issue (#4669) by @Bigfoot71 diff --git a/src/raylib.h b/src/raylib.h index 6849e7176..c30fa9e45 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1279,10 +1279,10 @@ RLAPI void DrawLineStrip(const Vector2 *points, int pointCount, Color color); RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw line segment cubic-bezier in-out interpolation RLAPI void DrawLineDashed(Vector2 startPos, Vector2 endPos, int dashSize, int spaceSize, Color color); // Draw a dashed line RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle +RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) +RLAPI void DrawCircleGradient(Vector2 center, float radius, Color inner, Color outer); // Draw a gradient-filled circle RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw a piece of a circle RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw circle sector outline -RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color inner, Color outer); // Draw a gradient-filled circle -RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline RLAPI void DrawCircleLinesV(Vector2 center, float radius, Color color); // Draw circle outline (Vector version) RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse diff --git a/src/rshapes.c b/src/rshapes.c index 0a927a1d7..b39ecb743 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -324,6 +324,22 @@ void DrawCircleV(Vector2 center, float radius, Color color) DrawCircleSector(center, radius, 0, 360, 36, color); } +// Draw a gradient-filled circle +void DrawCircleGradient(Vector2 center, float radius, Color inner, Color outer) +{ + rlBegin(RL_TRIANGLES); + for (int i = 0; i < 360; i += 10) + { + rlColor4ub(inner.r, inner.g, inner.b, inner.a); + rlVertex2f(center.x, center.y); + rlColor4ub(outer.r, outer.g, outer.b, outer.a); + rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radius, center.y + sinf(DEG2RAD*(i + 10))*radius); + rlColor4ub(outer.r, outer.g, outer.b, outer.a); + rlVertex2f(center.x + cosf(DEG2RAD*i)*radius, center.y + sinf(DEG2RAD*i)*radius); + } + rlEnd(); +} + // Draw a piece of a circle void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color) { @@ -473,22 +489,6 @@ void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float rlEnd(); } -// Draw a gradient-filled circle -void DrawCircleGradient(int centerX, int centerY, float radius, Color inner, Color outer) -{ - rlBegin(RL_TRIANGLES); - for (int i = 0; i < 360; i += 10) - { - rlColor4ub(inner.r, inner.g, inner.b, inner.a); - rlVertex2f((float)centerX, (float)centerY); - rlColor4ub(outer.r, outer.g, outer.b, outer.a); - rlVertex2f((float)centerX + cosf(DEG2RAD*(i + 10))*radius, (float)centerY + sinf(DEG2RAD*(i + 10))*radius); - rlColor4ub(outer.r, outer.g, outer.b, outer.a); - rlVertex2f((float)centerX + cosf(DEG2RAD*i)*radius, (float)centerY + sinf(DEG2RAD*i)*radius); - } - rlEnd(); -} - // Draw circle outline void DrawCircleLines(int centerX, int centerY, float radius, Color color) { From b202421e083bb1592cca9af50fd1123b07b948d6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 09:16:49 +0000 Subject: [PATCH 272/319] rlparser: update raylib_api.* by CI --- tools/rlparser/output/raylib_api.json | 88 +++++++++++++-------------- tools/rlparser/output/raylib_api.lua | 43 +++++++------ tools/rlparser/output/raylib_api.txt | 35 ++++++----- tools/rlparser/output/raylib_api.xml | 23 ++++--- 4 files changed, 91 insertions(+), 98 deletions(-) diff --git a/tools/rlparser/output/raylib_api.json b/tools/rlparser/output/raylib_api.json index ed0e220d7..06abbb7f7 100644 --- a/tools/rlparser/output/raylib_api.json +++ b/tools/rlparser/output/raylib_api.json @@ -5698,6 +5698,48 @@ } ] }, + { + "name": "DrawCircleV", + "description": "Draw a color-filled circle (Vector version)", + "returnType": "void", + "params": [ + { + "type": "Vector2", + "name": "center" + }, + { + "type": "float", + "name": "radius" + }, + { + "type": "Color", + "name": "color" + } + ] + }, + { + "name": "DrawCircleGradient", + "description": "Draw a gradient-filled circle", + "returnType": "void", + "params": [ + { + "type": "Vector2", + "name": "center" + }, + { + "type": "float", + "name": "radius" + }, + { + "type": "Color", + "name": "inner" + }, + { + "type": "Color", + "name": "outer" + } + ] + }, { "name": "DrawCircleSector", "description": "Draw a piece of a circle", @@ -5760,52 +5802,6 @@ } ] }, - { - "name": "DrawCircleGradient", - "description": "Draw a gradient-filled circle", - "returnType": "void", - "params": [ - { - "type": "int", - "name": "centerX" - }, - { - "type": "int", - "name": "centerY" - }, - { - "type": "float", - "name": "radius" - }, - { - "type": "Color", - "name": "inner" - }, - { - "type": "Color", - "name": "outer" - } - ] - }, - { - "name": "DrawCircleV", - "description": "Draw a color-filled circle (Vector version)", - "returnType": "void", - "params": [ - { - "type": "Vector2", - "name": "center" - }, - { - "type": "float", - "name": "radius" - }, - { - "type": "Color", - "name": "color" - } - ] - }, { "name": "DrawCircleLines", "description": "Draw circle outline", diff --git a/tools/rlparser/output/raylib_api.lua b/tools/rlparser/output/raylib_api.lua index a412e513f..1fe26250c 100644 --- a/tools/rlparser/output/raylib_api.lua +++ b/tools/rlparser/output/raylib_api.lua @@ -4861,6 +4861,27 @@ return { {type = "Color", name = "color"} } }, + { + name = "DrawCircleV", + description = "Draw a color-filled circle (Vector version)", + returnType = "void", + params = { + {type = "Vector2", name = "center"}, + {type = "float", name = "radius"}, + {type = "Color", name = "color"} + } + }, + { + name = "DrawCircleGradient", + description = "Draw a gradient-filled circle", + returnType = "void", + params = { + {type = "Vector2", name = "center"}, + {type = "float", name = "radius"}, + {type = "Color", name = "inner"}, + {type = "Color", name = "outer"} + } + }, { name = "DrawCircleSector", description = "Draw a piece of a circle", @@ -4887,28 +4908,6 @@ return { {type = "Color", name = "color"} } }, - { - name = "DrawCircleGradient", - description = "Draw a gradient-filled circle", - returnType = "void", - params = { - {type = "int", name = "centerX"}, - {type = "int", name = "centerY"}, - {type = "float", name = "radius"}, - {type = "Color", name = "inner"}, - {type = "Color", name = "outer"} - } - }, - { - name = "DrawCircleV", - description = "Draw a color-filled circle (Vector version)", - returnType = "void", - params = { - {type = "Vector2", name = "center"}, - {type = "float", name = "radius"}, - {type = "Color", name = "color"} - } - }, { name = "DrawCircleLines", description = "Draw circle outline", diff --git a/tools/rlparser/output/raylib_api.txt b/tools/rlparser/output/raylib_api.txt index bdad7db98..1d82497b5 100644 --- a/tools/rlparser/output/raylib_api.txt +++ b/tools/rlparser/output/raylib_api.txt @@ -2262,7 +2262,22 @@ Function 230: DrawCircle() (4 input parameters) Param[2]: centerY (type: int) Param[3]: radius (type: float) Param[4]: color (type: Color) -Function 231: DrawCircleSector() (6 input parameters) +Function 231: DrawCircleV() (3 input parameters) + Name: DrawCircleV + Return type: void + Description: Draw a color-filled circle (Vector version) + Param[1]: center (type: Vector2) + Param[2]: radius (type: float) + Param[3]: color (type: Color) +Function 232: DrawCircleGradient() (4 input parameters) + Name: DrawCircleGradient + Return type: void + Description: Draw a gradient-filled circle + Param[1]: center (type: Vector2) + Param[2]: radius (type: float) + Param[3]: inner (type: Color) + Param[4]: outer (type: Color) +Function 233: DrawCircleSector() (6 input parameters) Name: DrawCircleSector Return type: void Description: Draw a piece of a circle @@ -2272,7 +2287,7 @@ Function 231: DrawCircleSector() (6 input parameters) Param[4]: endAngle (type: float) Param[5]: segments (type: int) Param[6]: color (type: Color) -Function 232: DrawCircleSectorLines() (6 input parameters) +Function 234: DrawCircleSectorLines() (6 input parameters) Name: DrawCircleSectorLines Return type: void Description: Draw circle sector outline @@ -2282,22 +2297,6 @@ Function 232: DrawCircleSectorLines() (6 input parameters) Param[4]: endAngle (type: float) Param[5]: segments (type: int) Param[6]: color (type: Color) -Function 233: DrawCircleGradient() (5 input parameters) - Name: DrawCircleGradient - Return type: void - Description: Draw a gradient-filled circle - Param[1]: centerX (type: int) - Param[2]: centerY (type: int) - Param[3]: radius (type: float) - Param[4]: inner (type: Color) - Param[5]: outer (type: Color) -Function 234: DrawCircleV() (3 input parameters) - Name: DrawCircleV - Return type: void - Description: Draw a color-filled circle (Vector version) - Param[1]: center (type: Vector2) - Param[2]: radius (type: float) - Param[3]: color (type: Color) Function 235: DrawCircleLines() (4 input parameters) Name: DrawCircleLines Return type: void diff --git a/tools/rlparser/output/raylib_api.xml b/tools/rlparser/output/raylib_api.xml index 83e2bd548..37d828049 100644 --- a/tools/rlparser/output/raylib_api.xml +++ b/tools/rlparser/output/raylib_api.xml @@ -1409,6 +1409,17 @@ + + + + + + + + + + + @@ -1425,18 +1436,6 @@ - - - - - - - - - - - - From 53915d77e65fd55c82cb2e9ea7d159abffcc5ffe Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 9 Apr 2026 11:22:13 +0200 Subject: [PATCH 273/319] Update CHANGELOG --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index e049512bd..ec1957103 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -221,7 +221,7 @@ Detailed changes: [rlsw] REVIEWED: C++ support (#5291) by @alexgb0 [rshapes] ADDED: `DrawLineDashed()` (#5222) by @luis605 [rshapes] ADDED: `DrawEllipseV()` and `DrawEllipseLinesV()` (#4963) by @meowstr -[rshapes] REDESIGNED: `DrawCircleGradieent()` to use Vector2 as center parameter by @raysan5 -WARNING- +[rshapes] REDESIGNED: `DrawCircleGradient()` to use Vector2 as center parameter by @raysan5 -WARNING- [rshapes] REVIEWED: `DrawLine()`, fix pixel offset issue on drawing (#4666) by @Bigfoot71 [rshapes] REVIEWED: `DrawRectangleGradientEx()`, incorrect parameter names (#4980) by @vincent [rshapes] REVIEWED: `DrawRectangleLines`, fix pixel offset issue (#4669) by @Bigfoot71 From e24b01bb288a317e39c79ebf9071ec18e4e75447 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 9 Apr 2026 11:36:26 +0200 Subject: [PATCH 274/319] Update HISTORY.md --- HISTORY.md | 85 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 69 insertions(+), 16 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index b68ee44f4..a6ff00468 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -526,26 +526,79 @@ Last but not least, I want to thank **raylib sponsors and all the raylib communi notes on raylib 6.0 ------------------- -A new raylib release is finally ready, after almost 1.5 years since last release. This is the biggest release ever with many improvements to the library, thanks to the support of many amazing contributors and also thanks to the financial support of NLnet/NGI0 funds. +A new `raylib` release is finally ready and, again, this is the **biggest `raylib` release ever**! Thanks to the support of many amazing contributors this release comes packed with many new features and improvements, also thanks to the financial support of [NLnet](https://nlnet.nl/) and the [NGI Zero Commond Fund](https://nlnet.nl/NGI0/) that allow me to work on this project mostly fulltime for the past few months. Some astonishing numbers for this release: - - **+320** closed issues (for a TOTAL of **+2130**!) - - **+1800** commits since previous RELEASE (for a TOTAL of **+9600**!) - - **18** functions ADDED to raylib API (for a TOTAL of **599**!) - - **+50** new examples to learn from (for a TOTAL of **212**!) - - **+200** new contributors (for a TOTAL of **+840**!) + - **+320** closed issues (for a TOTAL of **+2140**!) + - **+1950** commits since previous RELEASE (for a TOTAL of **+9700**!) + - **+20** new functions ADDED to raylib API (for a TOTAL of **600**!) + - **+50** new examples to learn from (for a TOTAL of **+210**!) + - **+210** new contributors (for a TOTAL of **+850**!) Highlights for `raylib 6.0`: - - New Software Renderer backend [rlsw] - - New rcore platform backend: Memory (memory buffer) - - New rcore platform backend: Win32 (experimental) - - New rcore platform backend: Emscripten (experimental) - - Redesigned Skeletal Animation System - - Redesigned fullscreen modes and high-dpi content scaling - - Redesigned Build Config System [config.h] - - New File System API - - New Text Management API - - New tool: [rexm] raylib examples manager + - **`NEW` Software Renderer - [`rlsw`](https://github.com/raysan5/raylib/blob/master/src/external/rlsw.h)**: The biggest addition of this new release. A new software renderer backend, that allows raylib to run purely on CPU, with no neeed for a GPU. It finally closes the circle of my search for a portable self-contained, with **no-external-dependencies**, graphics library, able to run on any device providing some CPU-power and some RAM memory. It has been possible thanks to the amazing work of **Le Juez Victor** ([@Bigfoot71](https://github.com/Bigfoot71)), who created [`rlsw`](https://github.com/raysan5/raylib/blob/master/src/external/rlsw.h), a single-file header-only library implementing OpenGL 1.1+ specification, tailored to fit into raylib [`rlgl`](https://github.com/raysan5/raylib/blob/master/src/rlgl.h) OpenGL wrapper, and allowing to run raylib seamlessly over CPU with **no code changes required on user side**. As expected, software rendering is slower than hardware-accelerated rendering but it is still fast enough to run basic application at 30-60 fps. Actually, it already proved it usefulness on a new [raylib port for ESP32](https://components.espressif.com/components/georgik/raylib/versions/6.0.0/readme) microcontroller by Espressif, useful for industrial applications, and opens the door to the upcoming RISC-V powered devices that start arriving to the marked, and many times come with no GPU. Along the new software renderer, some of the existing platform backends have been adapted to support it (SDL, RGFW, DRM) and also **new platforms backends have been created** to accomodate it (Win32, Emscripten), incluing a new `PLATFORM_MEMORY`, that allows direct rendering to a memory framebuffer. + - **`NEW` Platform backend: Memory - [`rcore_memory`](https://github.com/raysan5/raylib/blob/master/src/platforms/rcore_memory.c)**: This new platform has been added along the **software renderer** backend, allowing 2d and 3d rendering over a **platform-agnostic memory framebuffer**, it can run headless and output frames can be directly exported to images. This new backend could also be useful for graphics rendering on servers or process images directly using the memory buffer. + + - **`NEW` Platform backend: Win32 - [`rcore_desktop_win32`](https://github.com/raysan5/raylib/blob/master/src/platforms/rcore_desktop_win32.c)**: A new **Windows platform backend** and the first step towards a potential replacement/alternative to the platform libraries currently used by raylib (GLFW/SDL/RGFW). This backend follows same API template structure than the other raylib backends, but directly implementing Win32 API calls. It allows initializing OpenGL GPU-accelerated windows and also GDI based windows, useful for the software renderer backend. This new backend approach, following a common template-structure and separating the platform logic by specific OS/Windowing system, will simplify code, improve maintenance, readability and portability for raylib, setting some bases for the future. *NOTE: This backend is new and it could require further testing, use it as an experimental backend for now.* + + - **`NEW` Platform backend: Emscripten - [`rcore_web_emscripten`](https://github.com/raysan5/raylib/blob/master/src/platforms/rcore_web_emscripten.c)**: In the same line as Win32 backend, this new web backend moves away from `libglfw.js` and **directly implements Emscripten/JS functionality**, with **no other dependencies**, adding support for the new software renderer to draw directly on a **non-accelerated 2d canvas** but also supporting a WebGL-hardware-accelerated canvas when required. *NOTE: This backend is new and it could require further testing, use it as an experimental backend for now.* + + - **`REDESIGNED` Fullscreen modes and High-DPI content scaling**: After many years and many related issues, the full-screen and high-dpi content scaling support has been **completely redesigned** from scratch. New design prioritizes **borderless fullscreen modes** and automatically detects current monitor content scaling configuration to scale window and framebuffer accordingly when required. Still, High-DPI support must be requested by user if desired enabling `FLAG_WINDOW_HIGHDPI` on window creation. This new system has been carefully tested on Windows, Linux (X11, Wayland), macOS with multiple monitors and multiple resolutions, including 4K monitors. + + - **`REDESIGNED` Skeletal Animation System**: A new animation system for 3d models has been created to support animation blending, between single frames but also between differents frames on different animations, to allow easy **timed transitions** between animations. This redesign implied reviewing several raylib structures to better accomodate animation data: `Model`, `ModelSkeleton`, `ModelAnimation`, but the API was simplified and support for GPU-skinning was improved with multiple optimizations. + + - **`REDESIGNED` Build Config System - [`config.h`](https://github.com/raysan5/raylib/blob/master/src/config.h)**: raylib allows lot of customization for specific needs (i.e. disabling modules not needed for specific applications like rmodels or raudio) but previous implementation did not allow easely disabling some features from **custom build systems**. New design not only allows disabling features with simple `-DSUPPORT_FILEFORMAT_OBJ=0` on building command-line but also the full system has been reviewed, removing useless flags and exposing new ones. + + - **`NEW` File System API**: Along the years, multiple filesystem functions have been added to raylib API as required but it felt somewhat inconsistent with some pieces missing. In this new release, the full filesystem API has beeen reviewed and reorganized, compiling all the functionality single module: [rcore](https://github.com/raysan5/raylib/blob/master/src/raylib.h#L1126), consequently `utils` module has been removed and build system has been simplified even more; **only 6-7 modules (.c) need to be compiled containing the full raylib library**. This new filesystem API will allow raylib to be used on the creation of custom build systems, as already demostrated with the new `rexm` tool for examples management. At the moment raylib includes **+40 file system management functions**, here a list with the new functions added: +```c + int FileRename(const char *fileName, const char *fileRename); // Rename file (if exists) + int FileRemove(const char *fileName); // Remove file (if exists) + int FileCopy(const char *srcPath, const char *dstPath); // Copy file from one path to another, dstPath created if it doesn't exist + int FileMove(const char *srcPath, const char *dstPath); // Move file from one directory to another, dstPath created if it doesn't exist + int FileTextReplace(const char *fileName, const char *search, const char *replacement); // Replace text in an existing file + int FileTextFindIndex(const char *fileName, const char *search); // Find text in existing file +``` + + - **`NEW` Text Management API**: Along with the new file system functionality, a new set of text management functions has been added, also very useful for text procesing and also used in custom build systems creation using raylib. At the moment raylib includes **+30 text management functions**, here a list with the new functions added: +```c + char **LoadTextLines(const char *text, int *count); // Load text as separate lines ('\n') + void UnloadTextLines(char **text, int lineCount); // Unload text lines + const char *TextRemoveSpaces(const char *text); // Remove text spaces, concat words + char *GetTextBetween(const char *text, const char *begin, const char *end); // Get text between two strings + char *TextReplace(const char *text, const char *search, const char *replacement); // Replace text string with new string + char *TextReplaceAlloc(const char *text, const char *search, const char *replacement); // Replace text string with new string, memory must be MemFree() + char *TextReplaceBetween(const char *text, const char *begin, const char *end, const char *replacement); // Replace text between two specific strings + char *TextReplaceBetweenAlloc(const char *text, const char *begin, const char *end, const char *replacement); // Replace text between two specific strings, memory must be MemFree() + char *TextInsertAlloc(const char *text, const char *insert, int position); // Insert text in a defined byte position, memory must be MemFree() +``` + + - **`NEW` tool: raylib examples manager - [rexm](https://github.com/raysan5/raylib/tree/master/tools/rexm)**: raylib examples collection is huge, with **more than 200 examples** it was quite difficult to manage: adding, removing, renaming examples was a very costly process involving many files to be modified (including build systems), also the examples did not follow a common header convention neither a structure conventions. For that reason, a new support tool has been created: **rexm**, a raylib examples manager that allows to easely add/remove/rename examples, automatically fix inconsistencies and even **building and automated testing** on multiple platforms. +``` +USAGE: + > rexm [] + +COMMANDS: + create : Creates an empty example, from internal template + add : Add existing example to collection + rename : Rename an existing example + remove : Remove an existing example from collection + build : Build example for Desktop and Web platforms + test : Build and Test example for Desktop and Web platforms + validate : Validate examples collection, generates report + update : Validate and update examples collection, generates report +``` + + - **`NEW` +50 new examples**: Thanks to `rexm` and the simplification on examples management, this new raylib release includes +50 new examples to leearn from, most of them contributed by community. + +Make sure to check raylib [CHANGELOG](https://github.com/raysan5/raylib/blob/master/CHANGELOG) for a detailed list of changes! + +I want to **thank all the contributors (+850!**) that along the years have **greatly improved raylib** and pushed it further and better day after day. And **many thanks to raylib community and all raylib users** for supporting the library along those many years. + +Finally, I want to thank [puffer.ai](https://puffer.ai/) and [comma.ai](https://comma.ai/) for **supporting and sponsoring the project** as platinum sponsors, along many others individuals that have been sponsoring raylib along the years. Thanks to all of you for allowing me to keep working on this library! + +**After +12 years of development, `raylib 6.0` is today one of the bests libraries to enjoy games/tools/graphic programming!** + +**Enjoy graphics programming with raylib!** :) From 8e54b19d9f64f950ae22af8b91c077f38f3bf663 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 9 Apr 2026 11:46:00 +0200 Subject: [PATCH 275/319] Update shapes_basic_shapes.c --- examples/shapes/shapes_basic_shapes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shapes/shapes_basic_shapes.c b/examples/shapes/shapes_basic_shapes.c index 77f2246f0..d14e45fe0 100644 --- a/examples/shapes/shapes_basic_shapes.c +++ b/examples/shapes/shapes_basic_shapes.c @@ -50,7 +50,7 @@ int main(void) // Circle shapes and lines DrawCircle(screenWidth/5, 120, 35, DARKBLUE); - DrawCircleGradient(screenWidth/5, 220, 60, GREEN, SKYBLUE); + DrawCircleGradient((Vector2){ screenWidth/5.0f, 220.0f }, 60, GREEN, SKYBLUE); DrawCircleLines(screenWidth/5, 340, 80, DARKBLUE); DrawEllipse(screenWidth/5, 120, 25, 20, YELLOW); DrawEllipseLines(screenWidth/5, 120, 30, 25, YELLOW); From ca6f188233a46e396bb11629868e920ec583eb92 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 9 Apr 2026 11:52:04 +0200 Subject: [PATCH 276/319] Update shaders_shapes_textures.c --- examples/shaders/shaders_shapes_textures.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shaders/shaders_shapes_textures.c b/examples/shaders/shaders_shapes_textures.c index 1517f0de8..98a7c1966 100644 --- a/examples/shaders/shaders_shapes_textures.c +++ b/examples/shaders/shaders_shapes_textures.c @@ -69,7 +69,7 @@ int main(void) DrawText("USING DEFAULT SHADER", 20, 40, 10, RED); DrawCircle(80, 120, 35, DARKBLUE); - DrawCircleGradient(80, 220, 60, GREEN, SKYBLUE); + DrawCircleGradient((Vector2){ 80.0f, 220.0f }, 60, GREEN, SKYBLUE); DrawCircleLines(80, 340, 80, DARKBLUE); From fbcc8cdb554574aa2abbafecb348c32928fd713e Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 9 Apr 2026 11:52:06 +0200 Subject: [PATCH 277/319] Update shapes_top_down_lights.c --- examples/shapes/shapes_top_down_lights.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shapes/shapes_top_down_lights.c b/examples/shapes/shapes_top_down_lights.c index 4a2ea0dcd..0f76d2e40 100644 --- a/examples/shapes/shapes_top_down_lights.c +++ b/examples/shapes/shapes_top_down_lights.c @@ -341,7 +341,7 @@ static void DrawLightMask(int slot) rlSetBlendMode(BLEND_CUSTOM); // 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); + if (lights[slot].valid) DrawCircleGradient(lights[slot].position, lights[slot].outerRadius, ColorAlpha(WHITE, 0), WHITE); rlDrawRenderBatchActive(); From de95b3aa70545eb59b44aa6fe7725e25da0f48da Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Fri, 10 Apr 2026 08:42:06 -0500 Subject: [PATCH 278/319] added gpuready check to gentexturemipmaps (#5747) --- src/rlgl.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rlgl.h b/src/rlgl.h index 923f84dfd..073cecd68 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3685,6 +3685,8 @@ void rlUnloadTexture(unsigned int id) // NOTE: Only supports GPU mipmap generation void rlGenTextureMipmaps(unsigned int id, int width, int height, int format, int *mipmaps) { + if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return; } + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glBindTexture(GL_TEXTURE_2D, id); From fe5271c5688a27e3b3d75614d45d76bf739699cc Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Sun, 12 Apr 2026 03:23:09 -0500 Subject: [PATCH 279/319] update raylib.rc to 6.0 (2026) (#5748) --- src/raylib.rc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/raylib.rc b/src/raylib.rc index 7de8382bd..bd9fceaf7 100644 --- a/src/raylib.rc +++ b/src/raylib.rc @@ -1,8 +1,8 @@ GLFW_ICON ICON "raylib.ico" 1 VERSIONINFO -FILEVERSION 5,5,0,0 -PRODUCTVERSION 5,5,0,0 +FILEVERSION 6,0,0,0 +PRODUCTVERSION 6,0,0,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -11,12 +11,12 @@ BEGIN BEGIN VALUE "CompanyName", "raylib technologies" VALUE "FileDescription", "raylib application (www.raylib.com)" - VALUE "FileVersion", "5.5.0" + VALUE "FileVersion", "6.0.0" VALUE "InternalName", "raylib" - VALUE "LegalCopyright", "(c) 2025 Ramon Santamaria (@raysan5)" + VALUE "LegalCopyright", "(c) 2026 Ramon Santamaria (@raysan5)" VALUE "OriginalFilename", "raylib" VALUE "ProductName", "raylib app" - VALUE "ProductVersion", "5.5.0" + VALUE "ProductVersion", "6.0.0" END END BLOCK "VarFileInfo" From ddfbe973d4b49f725562a81860de7b46d0814e3f Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Apr 2026 10:24:35 +0200 Subject: [PATCH 280/319] Updated Windows resource file --- src/raylib.dll.rc | 10 +++++----- src/raylib.dll.rc.data | Bin 7742 -> 7742 bytes src/raylib.rc.data | Bin 7726 -> 7726 bytes 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/raylib.dll.rc b/src/raylib.dll.rc index 7ad39c76f..e8d24a037 100644 --- a/src/raylib.dll.rc +++ b/src/raylib.dll.rc @@ -1,8 +1,8 @@ GLFW_ICON ICON "raylib.ico" 1 VERSIONINFO -FILEVERSION 5,5,0,0 -PRODUCTVERSION 5,5,0,0 +FILEVERSION 6,0,0,0 +PRODUCTVERSION 6,0,0,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -11,12 +11,12 @@ BEGIN BEGIN VALUE "CompanyName", "raylib technologies" VALUE "FileDescription", "raylib dynamic library (www.raylib.com)" - VALUE "FileVersion", "5.5.0" + VALUE "FileVersion", "6.0.0" VALUE "InternalName", "raylib.dll" - VALUE "LegalCopyright", "(c) 2025 Ramon Santamaria (@raysan5)" + VALUE "LegalCopyright", "(c) 2026 Ramon Santamaria (@raysan5)" VALUE "OriginalFilename", "raylib.dll" VALUE "ProductName", "raylib" - VALUE "ProductVersion", "5.5.0" + VALUE "ProductVersion", "6.0.0" END END BLOCK "VarFileInfo" diff --git a/src/raylib.dll.rc.data b/src/raylib.dll.rc.data index db6b924a70a57a674f6ece692b44bac7e11eac6d..4a8a6a19d8f8e07a34fe54f6bf946d2280dc6fdd 100644 GIT binary patch delta 54 zcmdmIv(IM36De*61~vu=V4M6=N`5n+v>YR=8G{~!!DK_3LPoR6i)AiC`IWLq0c3g& AJpcdz delta 54 zcmdmIv(IM36De+1237_T0Me5`O382Lla^y-HD%CaFr92DQ^;sKd9lnzD8EwnC;(<3 B4NL$4 diff --git a/src/raylib.rc.data b/src/raylib.rc.data index 8727d21308057ce88f30f52cc39578fe8f733620..64ed4e3c7ff728b0c7fed3bb2680d95e6a623615 100644 GIT binary patch delta 54 zcmZ2yv(9G26De*61~vu=V4M6=N`5n+v=}3+8G{~!!DLODOh&WGlVvVK`I)ju0b1G& A Date: Sun, 12 Apr 2026 03:25:59 -0500 Subject: [PATCH 281/319] update makefile for PLATFORM_MEMORY (#5750) --- src/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Makefile b/src/Makefile index 8f13b4355..247978fd4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -284,6 +284,10 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) # By default use OpenGL ES 2.0 on Android GRAPHICS ?= GRAPHICS_API_OPENGL_ES2 endif +ifeq ($(TARGET_PLATFORM),PLATFORM_MEMORY) + # By default use OpenGL Software + GRAPHICS ?= GRAPHICS_API_OPENGL_SOFTWARE +endif # Define default C compiler and archiver to pack library: CC, AR #------------------------------------------------------------------------------------------------ From 605303f62b0a0554105fd1bcdf240b6a3cd75318 Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Sun, 12 Apr 2026 09:32:05 +0100 Subject: [PATCH 282/319] fix: pthread_mutex_lock called on a destroyed mutex (#5752) --- src/raudio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/raudio.c b/src/raudio.c index cd37ca305..8107886cb 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1763,6 +1763,8 @@ bool IsMusicValid(Music music) // Unload music stream void UnloadMusicStream(Music music) { + if (IsMusicStreamPlaying(music)) StopMusicStream(music); + UnloadAudioStream(music.stream); if (music.ctxData != NULL) From a5427bc38fe2dd9018bf657525406287bec0cbfa Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Apr 2026 11:07:09 +0200 Subject: [PATCH 283/319] Update rlsw.h --- src/external/rlsw.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/external/rlsw.h b/src/external/rlsw.h index c9ace86cd..8f7ab4405 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -2417,7 +2417,7 @@ static inline void sw_texture_sample_nearest(float *SW_RESTRICT color, const sw_ static inline void sw_texture_sample_linear(float *SW_RESTRICT color, const sw_texture_t *SW_RESTRICT tex, float u, float v) { - // TODO: With a bit more cleverness thee number of operations can + // TODO: With a bit more cleverness the number of operations can // be clearly reduced, but for now it works fine float xf = (u*tex->width) - 0.5f; From 51d693607ece4d66c09157821efd9b9c2de35019 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Apr 2026 11:07:19 +0200 Subject: [PATCH 284/319] Update Makefile --- src/Makefile | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/src/Makefile b/src/Makefile index 247978fd4..997165b4a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -286,7 +286,7 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) endif ifeq ($(TARGET_PLATFORM),PLATFORM_MEMORY) # By default use OpenGL Software - GRAPHICS ?= GRAPHICS_API_OPENGL_SOFTWARE + GRAPHICS = GRAPHICS_API_OPENGL_SOFTWARE endif # Define default C compiler and archiver to pack library: CC, AR @@ -324,7 +324,12 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) AR = $(RPI_TOOLCHAIN)/bin/$(RPI_TOOLCHAIN_NAME)-ar endif endif -ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) + # HTML5 emscripten compiler + CC = emcc + AR = emar +endif +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB_RGFW) # HTML5 emscripten compiler CC = emcc AR = emar @@ -368,7 +373,6 @@ ifneq ($(RAYLIB_CONFIG_FLAGS), NONE) endif ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) - # NOTE: When using multi-threading in the user code, it requires -pthread enabled CFLAGS += -std=gnu99 else CFLAGS += -std=c99 @@ -387,12 +391,15 @@ ifeq ($(RAYLIB_BUILD_MODE),DEBUG) endif ifeq ($(RAYLIB_BUILD_MODE),RELEASE) - ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) - CFLAGS += -Os - endif ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) CFLAGS += -O1 endif + ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) + CFLAGS += -Os + endif + ifeq ($(TARGET_PLATFORM),PLATFORM_WEB_RGFW) + CFLAGS += -Os + endif ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) CFLAGS += -O2 endif @@ -406,23 +413,12 @@ endif ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) CFLAGS += -Werror=implicit-function-declaration 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) - # -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 +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) + ifeq ($(RAYLIB_BUILD_MODE),DEBUG) + CFLAGS += --profiling + endif +endif +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB_RGFW) ifeq ($(RAYLIB_BUILD_MODE),DEBUG) CFLAGS += --profiling endif @@ -772,7 +768,6 @@ all: raylib raylib: $(OBJS) ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) # Compile raylib libray for web - #$(CC) $(OBJS) -r -o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).bc ifeq ($(RAYLIB_LIBTYPE),SHARED) @echo "WARNING: $(TARGET_PLATFORM) does not support SHARED libraries. Generating STATIC library." endif From 44e5126d089cf86a7466d98ac0457837362fcf18 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Sun, 12 Apr 2026 12:21:09 -0500 Subject: [PATCH 285/319] update 2025->2026, 5.5->6.0 (#5754) --- BINDINGS.md | 2 +- CHANGELOG | 2 +- examples/Makefile.Android | 2 +- examples/examples.rc | 10 +++++----- projects/CMake/CMakeLists.txt | 2 +- tools/rexm/LICENSE | 2 +- tools/rexm/Makefile | 2 +- tools/rexm/rexm.c | 6 +++--- tools/rexm/rexm.rc | 2 +- tools/rlparser/rlparser.rc | 6 +++--- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/BINDINGS.md b/BINDINGS.md index 4a7680964..66467d9fa 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -6,7 +6,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers | Name | raylib Version | Language | License | | :--------------------------------------------------------------------------------------- | :--------------: | :------------------------------------------------------------------: | :------------------: | -| [raylib](https://github.com/raysan5/raylib) | **5.5** | [C/C++](https://en.wikipedia.org/wiki/C_(programming_language)) | Zlib | +| [raylib](https://github.com/raysan5/raylib) | **6.0** | [C/C++](https://en.wikipedia.org/wiki/C_(programming_language)) | Zlib | | [raylib-ada](https://github.com/Fabien-Chouteau/raylib-ada) | **5.5** | [Ada](https://en.wikipedia.org/wiki/Ada_(programming_language)) | MIT | | [raylib-beef](https://github.com/Starpelly/raylib-beef) | **5.5** | [Beef](https://www.beeflang.org) | MIT | | [raybit](https://github.com/Alex-Velez/raybit) | **5.0** | [Brainfuck](https://en.wikipedia.org/wiki/Brainfuck) | MIT | diff --git a/CHANGELOG b/CHANGELOG index ec1957103..e4e06ce47 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,7 @@ changelog --------- -Current Release: raylib 5.5 (18 November 2024) +Current Release: raylib 6.0 (23 April 2026) ------------------------------------------------------------------------- Release: raylib 6.0 (23 April 2026) diff --git a/examples/Makefile.Android b/examples/Makefile.Android index cf4ad1257..0d8da9701 100644 --- a/examples/Makefile.Android +++ b/examples/Makefile.Android @@ -2,7 +2,7 @@ # # raylib makefile for Android project (APK building) # -# Copyright (c) 2017-2025 Ramon Santamaria (@raysan5) +# Copyright (c) 2017-2026 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. In no event # will the authors be held liable for any damages arising from the use of this software. diff --git a/examples/examples.rc b/examples/examples.rc index 14938ae9d..4b53143fb 100644 --- a/examples/examples.rc +++ b/examples/examples.rc @@ -1,8 +1,8 @@ GLFW_ICON ICON "raylib.ico" 1 VERSIONINFO -FILEVERSION 5,5,0,0 -PRODUCTVERSION 5,5,0,0 +FILEVERSION 6,0,0,0 +PRODUCTVERSION 6,0,0,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -11,12 +11,12 @@ BEGIN BEGIN VALUE "CompanyName", "raylib technologies" VALUE "FileDescription", "raylib application (www.raylib.com)" - VALUE "FileVersion", "5.5.0" + VALUE "FileVersion", "6.0.0" VALUE "InternalName", "raylib-example" - VALUE "LegalCopyright", "(c) 2025 Ramon Santamaria (@raysan5)" + VALUE "LegalCopyright", "(c) 2026 Ramon Santamaria (@raysan5)" VALUE "OriginalFilename", "raylib-example" VALUE "ProductName", "raylib-example" - VALUE "ProductVersion", "5.5.0" + VALUE "ProductVersion", "6.0.0" END END BLOCK "VarFileInfo" diff --git a/projects/CMake/CMakeLists.txt b/projects/CMake/CMakeLists.txt index 56a5a5f51..b1d307489 100644 --- a/projects/CMake/CMakeLists.txt +++ b/projects/CMake/CMakeLists.txt @@ -5,7 +5,7 @@ project(example) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Dependencies -set(RAYLIB_VERSION 5.5) +set(RAYLIB_VERSION 6.0) find_package(raylib ${RAYLIB_VERSION} QUIET) # QUIET or REQUIRED if (NOT raylib_FOUND) # If there's none, fetch and build raylib include(FetchContent) diff --git a/tools/rexm/LICENSE b/tools/rexm/LICENSE index 7d8c7bfc5..9bcc4ed0d 100644 --- a/tools/rexm/LICENSE +++ b/tools/rexm/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2025 Ramon Santamaria (@raysan5) +Copyright (c) 2026 Ramon Santamaria (@raysan5) This software is provided "as-is", without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. diff --git a/tools/rexm/Makefile b/tools/rexm/Makefile index c8f2490c0..6d061a6f6 100644 --- a/tools/rexm/Makefile +++ b/tools/rexm/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Web (Wasm), Raspberry Pi (DRM mode) and Android # -# Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. In no event # will the authors be held liable for any damages arising from the use of this software. diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 841ccbce8..84780988f 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -474,8 +474,8 @@ int main(int argc, char *argv[]) exTextUpdated[1] = TextReplaceAlloc(exTextUpdated[0], "", exName + strlen(exCategory) + 1); //TextReplaceAlloc(newExample, "", "Ray"); //TextReplaceAlloc(newExample, "@", "@raysan5"); - //TextReplaceAlloc(newExample, "", 2025); - //TextReplaceAlloc(newExample, "", 2025); + //TextReplaceAlloc(newExample, "", 2026); + //TextReplaceAlloc(newExample, "", 2026); SaveFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), exTextUpdated[1]); for (int i = 0; i < 6; i++) { MemFree(exTextUpdated[i]); exTextUpdated[i] = NULL; } @@ -1875,7 +1875,7 @@ int main(int argc, char *argv[]) printf("\n////////////////////////////////////////////////////////////////////////////////////////////\n"); printf("// //\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("// powered by raylib v6.0 //\n"); printf("// //\n"); printf("// Copyright (c) 2025-2026 Ramon Santamaria (@raysan5) //\n"); printf("// //\n"); diff --git a/tools/rexm/rexm.rc b/tools/rexm/rexm.rc index 4d3785a6a..83f4e1746 100644 --- a/tools/rexm/rexm.rc +++ b/tools/rexm/rexm.rc @@ -13,7 +13,7 @@ BEGIN VALUE "FileDescription", "rexm | raylib examples manager" VALUE "FileVersion", "1.0" VALUE "InternalName", "rexm" - VALUE "LegalCopyright", "(c) 2025 Ramon Santamaria" + VALUE "LegalCopyright", "(c) 2026 Ramon Santamaria" //VALUE "OriginalFilename", "rexm.exe" VALUE "rexm", "rexm" VALUE "ProductVersion", "1.0" diff --git a/tools/rlparser/rlparser.rc b/tools/rlparser/rlparser.rc index a5bb5b7ad..e487b7a09 100644 --- a/tools/rlparser/rlparser.rc +++ b/tools/rlparser/rlparser.rc @@ -1,7 +1,7 @@ IDI_APP_ICON ICON "rlparser.ico" 1 VERSIONINFO -FILEVERSION 5,5,0,0 -PRODUCTVERSION 5,5,0,0 +FILEVERSION 6,0,0,0 +PRODUCTVERSION 6,0,0,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -11,7 +11,7 @@ BEGIN VALUE "FileDescription", "rlparser | raylib header API parser" VALUE "FileVersion", "1.0" VALUE "InternalName", "rlparser" - VALUE "LegalCopyright", "(c) 2025 Ramon Santamaria (@raysan5)" + VALUE "LegalCopyright", "(c) 2026 Ramon Santamaria (@raysan5)" VALUE "OriginalFilename", "rlparser" VALUE "ProductName", "rlparser" VALUE "ProductVersion", "1.0" From 1339ec637e49f71bb9d723d54abe7db6787576f9 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Apr 2026 19:49:45 +0200 Subject: [PATCH 286/319] Update raylib.h --- src/raylib.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index c30fa9e45..34c5a6488 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1071,9 +1071,9 @@ RLAPI Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode); // Lo RLAPI bool IsShaderValid(Shader shader); // Check if a shader is valid (loaded on GPU) RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location RLAPI int GetShaderLocationAttrib(Shader shader, const char *attribName); // Get shader attribute location -RLAPI void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType); // Set shader uniform value -RLAPI void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count); // Set shader uniform value vector -RLAPI void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat); // Set shader uniform value (matrix 4x4) +RLAPI void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType); // Set shader uniform value +RLAPI void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count); // Set shader uniform value vector +RLAPI void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat); // Set shader uniform value (matrix 4x4) RLAPI void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture); // Set shader uniform value and bind the texture (sampler2d) RLAPI void UnloadShader(Shader shader); // Unload shader from GPU memory (VRAM) @@ -1175,10 +1175,10 @@ RLAPI unsigned char *CompressData(const unsigned char *data, int dataSize, int * 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 *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) -RLAPI unsigned int *ComputeSHA256(unsigned char *data, int dataSize); // Compute SHA256 hash code, returns static int[8] (32 bytes) +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) +RLAPI unsigned int *ComputeSHA256(unsigned char *data, int dataSize); // Compute SHA256 hash code, returns static int[8] (32 bytes) // Automation events functionality RLAPI AutomationEventList LoadAutomationEventList(const char *fileName); // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS @@ -1281,7 +1281,7 @@ RLAPI void DrawLineDashed(Vector2 startPos, Vector2 endPos, int dashSize, int sp RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) RLAPI void DrawCircleGradient(Vector2 center, float radius, Color inner, Color outer); // Draw a gradient-filled circle -RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw a piece of a circle +RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw a piece of a circle RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw circle sector outline RLAPI void 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) @@ -1290,7 +1290,7 @@ RLAPI void DrawEllipseV(Vector2 center, float radiusH, float radiusV, Color colo 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 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 RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color); // Draw a color-filled rectangle (Vector version) RLAPI void DrawRectangleRec(Rectangle rec, Color color); // Draw a color-filled rectangle @@ -1600,7 +1600,7 @@ RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, floa RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set) RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters RLAPI void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires) -RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint); // Draw a billboard texture +RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint); // Draw a billboard texture RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source RLAPI void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint); // Draw a billboard texture defined by source and rotation From 970531d112fd535c13b45442468dded784b9779e Mon Sep 17 00:00:00 2001 From: Ebben Feagan Date: Sun, 12 Apr 2026 16:49:38 -0500 Subject: [PATCH 287/319] Update BINDINGS.md to add new FreeBASIC port (#5755) Added the raylib4fb port --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index 66467d9fa..7169fbc05 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -33,6 +33,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers | [rayex](https://github.com/shiryel/rayex) | 3.7 | [elixir](https://elixir-lang.org) | Apache-2.0 | | [raylib-elle](https://github.com/acquitelol/elle/blob/rewrite/std/raylib.le) | **5.5** | [Elle](https://github.com/acquitelol/elle) | GPL-3.0 | | [raylib-factor](https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor) | 5.5 | [Factor](https://factorcode.org) | BSD | +| [raylib4fb](https://github.com/mudhairless/raylib4fb) | **5.5** | [FreeBASIC](https://www.freebasic.net) | Zlib | | [raylib-freebasic](https://github.com/WIITD/raylib-freebasic) | **5.0** | [FreeBASIC](https://www.freebasic.net) | MIT | | [raylib.f](https://github.com/cthulhuology/raylib.f) | **5.5** | [Forth](https://forth.com) | Zlib | | [fortran-raylib](https://github.com/interkosmos/fortran-raylib) | **5.5** | [Fortran](https://fortran-lang.org) | ISC | From 034ffda88738855538c5369b0d531d94c1b647b0 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Mon, 13 Apr 2026 11:55:45 -0500 Subject: [PATCH 288/319] [cmake] update cmake for PLATFORM_MEMORY (#5749) * update cmake for PLATFORM_MEMORY * remove opengl example for memory platform * update for winmm and cross compile --- CMakeOptions.txt | 2 +- cmake/LibraryConfigurations.cmake | 10 ++++++++++ examples/CMakeLists.txt | 3 +++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CMakeOptions.txt b/CMakeOptions.txt index 0a453917d..55375116e 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -6,7 +6,7 @@ if(EMSCRIPTEN) # When configuring web builds with "emcmake cmake -B build -S .", set PLATFORM to Web by default SET(PLATFORM Web CACHE STRING "Platform to build for.") endif() -enum_option(PLATFORM "Desktop;Web;WebRGFW;Android;Raspberry Pi;DRM;SDL;RGFW" "Platform to build for.") +enum_option(PLATFORM "Desktop;Web;WebRGFW;Android;Raspberry Pi;DRM;SDL;RGFW;Memory" "Platform to build for.") enum_option(OPENGL_VERSION "OFF;4.3;3.3;2.1;1.1;ES 2.0;ES 3.0;Software" "Force a specific OpenGL Version?") diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index 4a8eb7d55..18680491d 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -156,6 +156,7 @@ elseif ("${PLATFORM}" STREQUAL "SDL") add_compile_definitions(USING_SDL2_PACKAGE) endif() endif() + elseif ("${PLATFORM}" STREQUAL "RGFW") set(PLATFORM_CPP "PLATFORM_DESKTOP_RGFW") @@ -181,6 +182,15 @@ elseif ("${PLATFORM}" STREQUAL "WebRGFW") set(PLATFORM_CPP "PLATFORM_WEB_RGFW") set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") set(CMAKE_STATIC_LIBRARY_SUFFIX ".a") + +elseif ("${PLATFORM}" STREQUAL "Memory") + set(PLATFORM_CPP "PLATFORM_MEMORY") + set(GRAPHICS "GRAPHICS_API_OPENGL_SOFTWARE") + set(OPENGL_VERSION "Software") + + if(WIN32 OR CMAKE_C_COMPILER MATCHES "mingw|mingw32|mingw64") + set(LIBS_PRIVATE winmm) + endif() endif () if (NOT ${OPENGL_VERSION} MATCHES "OFF") diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 6554d1327..ee5469eaa 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -105,6 +105,9 @@ elseif ("${PLATFORM}" STREQUAL "DRM") list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/others/rlgl_standalone.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/others/raylib_opengl_interop.c) +elseif ("${PLATFORM}" MATCHES "Memory") + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/others/raylib_opengl_interop.c) + elseif ("${OPENGL_VERSION}" STREQUAL "Software") list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/others/rlgl_standalone.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/others/raylib_opengl_interop.c) From 019cc889fc808b1cfe18e9e4bc10ccdc5de7d430 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 13 Apr 2026 19:26:28 +0200 Subject: [PATCH 289/319] Updated Notepad++ scripts and autocomplete --- projects/Notepad++/npes_saved_mingw.txt | Bin 8256 -> 0 bytes projects/Notepad++/npes_saved_w64devkit.txt | Bin 10400 -> 17762 bytes .../raylib_npp_parser/raylib_npp.xml | 371 ++++++++++++------ .../raylib_npp_parser/raylib_to_parse.h | 333 ++++++++-------- 4 files changed, 431 insertions(+), 273 deletions(-) delete mode 100644 projects/Notepad++/npes_saved_mingw.txt diff --git a/projects/Notepad++/npes_saved_mingw.txt b/projects/Notepad++/npes_saved_mingw.txt deleted file mode 100644 index c07a430da5d57bb20d6f0302f85343519e68d768..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8256 zcmeHMZEqS!5T4JK`X8=RKh#wLC61!j5|tcq2#N_7ICUdgLKvJDl^2(@)AY|bd7c@@ zw|nNmBsg^>%R=6J+1Z_$XJ($A`|#hM?8%w*C6$p3q__CZWFi;1Gscrk{GZ8GzLjs} z9MAv6RU*IQ*TwavT;a_`<{1Bt@kmEQ+&RFUcY4j)6Fi^d?N~}lw)IE3U95SGl{>O8 zZ{$##@(das;p$jkNL6ak^%VQ-%fHZU1ij;{P)1GK(!$&(g9mHdXezO!sE-^&l!Pth7XYA2sn!<$ve9^j8s2SWRrgxAOg+MU#NdLHXn5`H^q-0xvQw+s*cvz(8ICk zh~;zsm@kNr6!sxj1{n9QeZl;Z`RyTh{QtRw*`SMfsUkBmLk$<+qc69aM^?vcS&?#A zG3z=q>WU+lSY032e5(vA5xaeTzoer%@*^X#iX3!~_#Mfv{47tuy2#CHUmUB9V#{d* z#Pub{L`c3?Jk)#V4PUa}doHe_(j1~1H4xENMf!54vjYy2YQ)9-#-cDrcd8g?HL`4N zG1A8Tn^nr{SXR@Tx7q_|H5FS#)f_p95_q=`enowbp%>Xo3mB|xzIv(r?Dq?P1%&~cmg?zR5YhGVHYxk-(lx2EVn8PI&T?bU-Uvn=N4y8dq3Q9IR3ggnAiMp`zJgf@FcdcI1D>EmUWA}FsDFp8Ek3aZ)JEr)FvFeW?655 zSudQOz@jtcp)1vXn>mmBFh6^(9_ZT4x$I<=l|wD#s1;?AZ{hQbM$!O#yV6|g-M6q_ z7p+4kN*T6}?c_G|QdEe(=HYbXxn!O1!8+NYVP@ZSZJ)pCJo6!$<>qVl)wb+4Z?-B= ztL##)Ua^>wG+?1Aencbn&f_ve`W$`Z7}arY-R#Qo!i-6_$Ib&!UDUByZJ5znQh#MAj9n_3+8Aa{qZ`k+hDPEWx6L|7ta(U&Uo@N ztC+pM*HMgshi`2jTX)NATk!5nc(a;gcw~Hra?1qU`2u%>)jB{^6TgO@s9L#LN>G!S zd(X5oSvST{&)mZVS=swbtXD~z8~#COR>x2Ukqs_I}ODZp^|$u sh3vc!Xd|x4d>-!H86>-KR+)S7Q}!+0zqri6{ys%d_sdNm-Isds5z_mLIsgCw diff --git a/projects/Notepad++/npes_saved_w64devkit.txt b/projects/Notepad++/npes_saved_w64devkit.txt index 767fa274d9778860a26893655eef4c4b026e8590..262d19c2382c21394a0117d9e81a2c003ba4dcc1 100644 GIT binary patch literal 17762 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 diff --git a/projects/Notepad++/raylib_npp_parser/raylib_npp.xml b/projects/Notepad++/raylib_npp_parser/raylib_npp.xml index ee965bd76..21c996ee1 100644 --- a/projects/Notepad++/raylib_npp_parser/raylib_npp.xml +++ b/projects/Notepad++/raylib_npp_parser/raylib_npp.xml @@ -191,7 +191,7 @@ - + @@ -360,7 +360,7 @@ - + @@ -498,8 +498,12 @@ - - + + + + + + @@ -507,11 +511,13 @@ - - - + + + + + @@ -524,35 +530,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -588,12 +566,69 @@ - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -605,7 +640,7 @@ - + @@ -615,6 +650,11 @@ + + + + + @@ -653,7 +693,7 @@ - + @@ -667,12 +707,12 @@ - + - + @@ -694,9 +734,16 @@ - - - + + + + + + + + + + @@ -716,15 +763,15 @@ - + - - + + @@ -746,7 +793,12 @@ - + + + + + + @@ -823,6 +875,11 @@ + + + + + @@ -868,12 +925,12 @@ - + - + @@ -1098,6 +1155,15 @@ + + + + + + + + + @@ -1106,6 +1172,21 @@ + + + + + + + + + + + + + + + @@ -1126,22 +1207,6 @@ - - - - - - - - - - - - - - - - @@ -1166,6 +1231,14 @@ + + + + + + + + @@ -1175,6 +1248,14 @@ + + + + + + + + @@ -1252,8 +1333,8 @@ - + @@ -1637,7 +1718,7 @@ - + @@ -2099,7 +2180,7 @@ - + @@ -2107,7 +2188,7 @@ - + @@ -2188,13 +2269,13 @@ - + - + @@ -2388,7 +2469,7 @@ - + @@ -2405,7 +2486,7 @@ - + @@ -2419,9 +2500,10 @@ - + + @@ -2531,6 +2613,15 @@ + + + + + + + + + @@ -2604,7 +2695,20 @@ - + + + + + + + + + + + + + + @@ -2635,80 +2739,121 @@ - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + - - + + - + - + - + - + - + - + - + - + - + - - + - + @@ -2995,7 +3140,7 @@ - + @@ -3180,22 +3325,20 @@ - + - + - - + + - - - - - - - + + + + + @@ -3333,7 +3476,7 @@ - + @@ -3406,7 +3549,7 @@ - + @@ -3514,7 +3657,7 @@ - + @@ -3598,7 +3741,7 @@ - + @@ -3638,5 +3781,3 @@ - - diff --git a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h index 1bd26fbc3..bd6c4c3f4 100644 --- a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h +++ b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h @@ -49,7 +49,7 @@ RLAPI Vector2 GetWindowScaleDPI(void); // Get window RLAPI const char *GetMonitorName(int monitor); // Get the human-readable, UTF-8 encoded name of the specified monitor RLAPI void SetClipboardText(const char *text); // Set clipboard text content RLAPI const char *GetClipboardText(void); // Get clipboard text content -RLAPI Image GetClipboardImage(void); // Get clipboard image +RLAPI Image GetClipboardImage(void); // Get clipboard image content RLAPI void EnableEventWaiting(void); // Enable waiting for events on EndDrawing(), no automatic event polling RLAPI void DisableEventWaiting(void); // Disable waiting for events on EndDrawing(), automatic events polling @@ -91,10 +91,10 @@ RLAPI Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode); // Lo RLAPI bool IsShaderValid(Shader shader); // Check if a shader is valid (loaded on GPU) RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location RLAPI int GetShaderLocationAttrib(Shader shader, const char *attribName); // Get shader attribute location -RLAPI void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType); // Set shader uniform value -RLAPI void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count); // Set shader uniform value vector -RLAPI void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat); // Set shader uniform value (matrix 4x4) -RLAPI void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture); // Set shader uniform value for texture (sampler2d) +RLAPI void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType); // Set shader uniform value +RLAPI void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count); // Set shader uniform value vector +RLAPI void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat); // Set shader uniform value (matrix 4x4) +RLAPI void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture); // Set shader uniform value and bind the texture (sampler2d) RLAPI void UnloadShader(Shader shader); // Unload shader from GPU memory (VRAM) // Screen-space-related functions @@ -109,99 +109,106 @@ RLAPI Matrix GetCameraMatrix(Camera camera); // Get c RLAPI Matrix GetCameraMatrix2D(Camera2D camera); // Get camera 2d transform matrix // Timing-related functions -RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum) -RLAPI float GetFrameTime(void); // Get time in seconds for last frame drawn (delta time) -RLAPI double GetTime(void); // Get elapsed time in seconds since InitWindow() -RLAPI int GetFPS(void); // Get current FPS +RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum) +RLAPI float GetFrameTime(void); // Get time in seconds for last frame drawn (delta time) +RLAPI double GetTime(void); // Get elapsed time in seconds since InitWindow() +RLAPI int GetFPS(void); // Get current FPS // Custom frame control functions // NOTE: Those functions are intended for advanced users that want full control over the frame processing // By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents() // To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL -RLAPI void SwapScreenBuffer(void); // Swap back buffer with front buffer (screen drawing) -RLAPI void PollInputEvents(void); // Register all input events -RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution) +RLAPI void SwapScreenBuffer(void); // Swap back buffer with front buffer (screen drawing) +RLAPI void PollInputEvents(void); // Register all input events +RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution) // Random values generation functions -RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator -RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included) +RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator +RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included) RLAPI int *LoadRandomSequence(unsigned int count, int min, int max); // Load random values sequence, no values repeated -RLAPI void UnloadRandomSequence(int *sequence); // Unload random values sequence +RLAPI void UnloadRandomSequence(int *sequence); // Unload random values sequence // Misc. functions -RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format) -RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS) -RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available) +RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format) +RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS) +RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available) -// NOTE: Following functions implemented in module [utils] -//------------------------------------------------------------------ -RLAPI void TraceLog(int logLevel, const char *text, ...); // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) -RLAPI void SetTraceLogLevel(int logLevel); // Set the current threshold (minimum) log level -RLAPI void *MemAlloc(unsigned int size); // Internal memory allocator -RLAPI void *MemRealloc(void *ptr, unsigned int size); // Internal memory reallocator -RLAPI void MemFree(void *ptr); // Internal memory free +// Logging system +RLAPI void SetTraceLogLevel(int logLevel); // Set the current threshold (minimum) log level +RLAPI void TraceLog(int logLevel, const char *text, ...); // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) +RLAPI void SetTraceLogCallback(TraceLogCallback callback); // Set custom trace log -// Set custom callbacks -// WARNING: Callbacks setup is intended for advanced users -RLAPI void SetTraceLogCallback(TraceLogCallback callback); // Set custom trace log -RLAPI void SetLoadFileDataCallback(LoadFileDataCallback callback); // Set custom file binary data loader -RLAPI void SetSaveFileDataCallback(SaveFileDataCallback callback); // Set custom file binary data saver -RLAPI void SetLoadFileTextCallback(LoadFileTextCallback callback); // Set custom file text data loader -RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom file text data saver +// Memory management, using internal allocators +RLAPI void *MemAlloc(unsigned int size); // Internal memory allocator +RLAPI void *MemRealloc(void *ptr, unsigned int size); // Internal memory reallocator +RLAPI void MemFree(void *ptr); // Internal memory free -// Files management functions +// File system management functions RLAPI unsigned char *LoadFileData(const char *fileName, int *dataSize); // Load file data as byte array (read) -RLAPI void UnloadFileData(unsigned char *data); // Unload file data allocated by LoadFileData() +RLAPI void UnloadFileData(unsigned char *data); // Unload file data allocated by LoadFileData() RLAPI bool SaveFileData(const char *fileName, void *data, int dataSize); // Save data to file from byte array (write), returns true on success RLAPI bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName); // Export data to code (.h), returns true on success -RLAPI char *LoadFileText(const char *fileName); // Load text data from file (read), returns a '\0' terminated string -RLAPI void UnloadFileText(char *text); // Unload file text data allocated by LoadFileText() -RLAPI bool SaveFileText(const char *fileName, char *text); // Save text data to file (write), string must be '\0' terminated, returns true on success -//------------------------------------------------------------------ +RLAPI char *LoadFileText(const char *fileName); // Load text data from file (read), returns a '\0' terminated string +RLAPI void UnloadFileText(char *text); // Unload file text data allocated by LoadFileText() +RLAPI bool SaveFileText(const char *fileName, const char *text); // Save text data to file (write), string must be '\0' terminated, returns true on success -// File system functions -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 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 -RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filename string without extension (uses static string) -RLAPI const char *GetDirectoryPath(const char *filePath); // Get full path for a given fileName with path (uses static string) -RLAPI const char *GetPrevDirectoryPath(const char *dirPath); // Get previous directory path for a given path (uses static string) -RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string) -RLAPI const char *GetApplicationDirectory(void); // Get the directory of the running application (uses static string) -RLAPI int MakeDirectory(const char *dirPath); // Create directories (including full path requested), returns 0 on success -RLAPI bool ChangeDirectory(const char *dir); // Change working directory, return true on success -RLAPI bool IsPathFile(const char *path); // Check if a given path is a file or a directory -RLAPI bool IsFileNameValid(const char *fileName); // Check if fileName is valid for the platform/OS -RLAPI FilePathList LoadDirectoryFiles(const char *dirPath); // Load directory filepaths -RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result -RLAPI void UnloadDirectoryFiles(FilePathList files); // Unload filepaths -RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window -RLAPI FilePathList LoadDroppedFiles(void); // Load dropped filepaths -RLAPI void UnloadDroppedFiles(FilePathList files); // Unload dropped filepaths -RLAPI long GetFileModTime(const char *fileName); // Get file modification time (last write time) +// File access custom callbacks +// WARNING: Callbacks setup is intended for advanced users +RLAPI void SetLoadFileDataCallback(LoadFileDataCallback callback); // Set custom file binary data loader +RLAPI void SetSaveFileDataCallback(SaveFileDataCallback callback); // Set custom file binary data saver +RLAPI void SetLoadFileTextCallback(LoadFileTextCallback callback); // Set custom file text data loader +RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom file text data saver + +RLAPI int FileRename(const char *fileName, const char *fileRename); // Rename file (if exists) +RLAPI int FileRemove(const char *fileName); // Remove file (if exists) +RLAPI int FileCopy(const char *srcPath, const char *dstPath); // Copy file from one path to another, dstPath created if it doesn't exist +RLAPI int FileMove(const char *srcPath, const char *dstPath); // Move file from one directory to another, dstPath created if it doesn't exist +RLAPI int FileTextReplace(const char *fileName, const char *search, const char *replacement); // Replace text in an existing file +RLAPI int FileTextFindIndex(const char *fileName, const char *search); // Find text in existing file +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 (recommended include point: .png, .wav) +RLAPI int GetFileLength(const char *fileName); // Get file length in bytes (NOTE: GetFileSize() conflicts with windows.h) +RLAPI long GetFileModTime(const char *fileName); // Get file modification time (last write time) +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 +RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filename string without extension (uses static string) +RLAPI const char *GetDirectoryPath(const char *filePath); // Get full path for a given fileName with path (uses static string) +RLAPI const char *GetPrevDirectoryPath(const char *dirPath); // Get previous directory path for a given path (uses static string) +RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string) +RLAPI const char *GetApplicationDirectory(void); // Get the directory of the running application (uses static string) +RLAPI int MakeDirectory(const char *dirPath); // Create directories (including full path requested), returns 0 on success +RLAPI bool ChangeDirectory(const char *dirPath); // Change working directory, return true on success +RLAPI bool IsPathFile(const char *path); // Check if a given path is a file or a directory +RLAPI bool IsFileNameValid(const char *fileName); // Check if fileName is valid for the platform/OS +RLAPI FilePathList LoadDirectoryFiles(const char *dirPath); // Load directory filepaths, files and directories, no subdirs scan +RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and subdir scan; some filters available: "*.*", "FILES*", "DIRS*" +RLAPI void UnloadDirectoryFiles(FilePathList files); // Unload filepaths +RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window +RLAPI FilePathList LoadDroppedFiles(void); // Load dropped filepaths +RLAPI void UnloadDroppedFiles(FilePathList files); // Unload dropped filepaths +RLAPI unsigned int GetDirectoryFileCount(const char *dirPath); // Get the file count in a directory +RLAPI unsigned int GetDirectoryFileCountEx(const char *basePath, const char *filter, bool scanSubdirs); // Get the file count in a directory with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result // 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 unsigned char *DecodeDataBase64(const unsigned 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) -RLAPI unsigned int *ComputeSHA1(unsigned char *data, int dataSize); // Compute SHA1 hash code, returns static int[5] (20 bytes) - +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 *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) +RLAPI unsigned int *ComputeSHA256(unsigned char *data, int dataSize); // Compute SHA256 hash code, returns static int[8] (32 bytes) // Automation events functionality -RLAPI AutomationEventList LoadAutomationEventList(const char *fileName); // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS -RLAPI void UnloadAutomationEventList(AutomationEventList list); // Unload automation events list from file -RLAPI bool ExportAutomationEventList(AutomationEventList list, const char *fileName); // Export automation events list as text file -RLAPI void SetAutomationEventList(AutomationEventList *list); // Set automation event list to record to -RLAPI void SetAutomationEventBaseFrame(int frame); // Set automation event internal base frame to start recording -RLAPI void StartAutomationEventRecording(void); // Start recording automation events (AutomationEventList must be set) -RLAPI void StopAutomationEventRecording(void); // Stop recording automation events -RLAPI void PlayAutomationEvent(AutomationEvent event); // Play a recorded automation event +RLAPI AutomationEventList LoadAutomationEventList(const char *fileName); // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS +RLAPI void UnloadAutomationEventList(AutomationEventList list); // Unload automation events list from file +RLAPI bool ExportAutomationEventList(AutomationEventList list, const char *fileName); // Export automation events list as text file +RLAPI void SetAutomationEventList(AutomationEventList *list); // Set automation event list to record to +RLAPI void SetAutomationEventBaseFrame(int frame); // Set automation event internal base frame to start recording +RLAPI void StartAutomationEventRecording(void); // Start recording automation events (AutomationEventList must be set) +RLAPI void StopAutomationEventRecording(void); // Stop recording automation events +RLAPI void PlayAutomationEvent(AutomationEvent event); // Play a recorded automation event //------------------------------------------------------------------------------------ // Input Handling Functions (Module: core) @@ -215,20 +222,20 @@ RLAPI bool IsKeyReleased(int key); // Check if a key RLAPI bool IsKeyUp(int key); // Check if a key is NOT being pressed RLAPI int GetKeyPressed(void); // Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty RLAPI int GetCharPressed(void); // Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty +RLAPI const char *GetKeyName(int key); // Get name of a QWERTY key on the current keyboard layout (eg returns string 'q' for KEY_A on an AZERTY keyboard) RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC) -RLAPI const char *GetKeyName(int key); // Get name of a QWERTY key on the current keyboard layout (eg returns string "q" for KEY_A on an AZERTY keyboard) // Input-related functions: gamepads -RLAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is available -RLAPI const char *GetGamepadName(int gamepad); // Get gamepad internal name id -RLAPI bool IsGamepadButtonPressed(int gamepad, int button); // Check if a gamepad button has been pressed once -RLAPI bool IsGamepadButtonDown(int gamepad, int button); // Check if a gamepad button is being pressed -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 SetGamepadMappings(const char *mappings); // Set internal gamepad mappings (SDL_GameControllerDB) +RLAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is available +RLAPI const char *GetGamepadName(int gamepad); // Get gamepad internal name id +RLAPI bool IsGamepadButtonPressed(int gamepad, int button); // Check if a gamepad button has been pressed once +RLAPI bool IsGamepadButtonDown(int gamepad, int button); // Check if a gamepad button is being pressed +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 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) // Input-related functions: mouse @@ -257,19 +264,19 @@ RLAPI int GetTouchPointCount(void); // Get number of t //------------------------------------------------------------------------------------ // Gestures and Touch Handling Functions (Module: rgestures) //------------------------------------------------------------------------------------ -RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags -RLAPI bool IsGestureDetected(unsigned int gesture); // Check if a gesture have been detected -RLAPI int GetGestureDetected(void); // Get latest detected gesture -RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in seconds -RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector -RLAPI float GetGestureDragAngle(void); // Get gesture drag angle -RLAPI Vector2 GetGesturePinchVector(void); // Get gesture pinch delta -RLAPI float GetGesturePinchAngle(void); // Get gesture pinch angle +RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags +RLAPI bool IsGestureDetected(unsigned int gesture); // Check if a gesture have been detected +RLAPI int GetGestureDetected(void); // Get latest detected gesture +RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in seconds +RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector +RLAPI float GetGestureDragAngle(void); // Get gesture drag angle +RLAPI Vector2 GetGesturePinchVector(void); // Get gesture pinch delta +RLAPI float GetGesturePinchAngle(void); // Get gesture pinch angle //------------------------------------------------------------------------------------ // Camera System Functions (Module: rcamera) //------------------------------------------------------------------------------------ -RLAPI void UpdateCamera(Camera *camera, int mode); // Update camera position for selected mode +RLAPI void UpdateCamera(Camera *camera, int mode); // Update camera position for selected mode RLAPI void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, float zoom); // Update camera movement/rotation //------------------------------------------------------------------------------------ @@ -278,9 +285,9 @@ RLAPI void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, f // Set texture and rectangle to be used on shapes drawing // NOTE: It can be useful when using basic shapes and one single font, // defining a font char white rectangle would allow drawing everything in a single draw call -RLAPI void SetShapesTexture(Texture2D texture, Rectangle source); // Set texture and rectangle to be used on shapes drawing -RLAPI Texture2D GetShapesTexture(void); // Get texture that is used for shapes drawing -RLAPI Rectangle GetShapesTextureRectangle(void); // Get texture source rectangle that is used for shapes drawing +RLAPI void SetShapesTexture(Texture2D texture, Rectangle source); // Set texture and rectangle to be used on shapes drawing +RLAPI Texture2D GetShapesTexture(void); // Get texture that is used for shapes drawing +RLAPI Rectangle GetShapesTextureRectangle(void); // Get texture source rectangle that is used for shapes drawing // Basic shapes drawing functions RLAPI void DrawPixel(int posX, int posY, Color color); // Draw a pixel using geometry [Can be slow, use with care] @@ -290,24 +297,27 @@ RLAPI void DrawLineV(Vector2 startPos, Vector2 endPos, Color color); RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line (using triangles/quads) RLAPI void DrawLineStrip(const Vector2 *points, int pointCount, Color color); // Draw lines sequence (using gl lines) RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw line segment cubic-bezier in-out interpolation +RLAPI void DrawLineDashed(Vector2 startPos, Vector2 endPos, int dashSize, int spaceSize, Color color); // Draw a dashed line RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle -RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw a piece of a circle -RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw circle sector outline -RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color inner, Color outer); // Draw a gradient-filled circle RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) +RLAPI void DrawCircleGradient(Vector2 center, float radius, Color inner, Color outer); // Draw a gradient-filled circle +RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw a piece of a circle +RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw circle sector outline RLAPI void 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 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 RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color); // Draw a color-filled rectangle (Vector version) RLAPI void DrawRectangleRec(Rectangle rec, Color color); // Draw a color-filled rectangle RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color); // Draw a color-filled rectangle with pro parameters RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color 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 @@ -322,11 +332,11 @@ RLAPI void DrawPolyLines(Vector2 center, int sides, float radius, float rotation RLAPI void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, float lineThick, Color color); // Draw a polygon outline of n sides with extended parameters // Splines drawing functions -RLAPI void DrawSplineLinear(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Linear, minimum 2 points -RLAPI void DrawSplineBasis(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: B-Spline, minimum 4 points -RLAPI void DrawSplineCatmullRom(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Catmull-Rom, minimum 4 points -RLAPI void DrawSplineBezierQuadratic(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...] -RLAPI void DrawSplineBezierCubic(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...] +RLAPI void DrawSplineLinear(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Linear, minimum 2 points +RLAPI void DrawSplineBasis(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: B-Spline, minimum 4 points +RLAPI void DrawSplineCatmullRom(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Catmull-Rom, minimum 4 points +RLAPI void DrawSplineBezierQuadratic(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...] +RLAPI void DrawSplineBezierCubic(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...] RLAPI void DrawSplineSegmentLinear(Vector2 p1, Vector2 p2, float thick, Color color); // Draw spline segment: Linear, 2 points RLAPI void DrawSplineSegmentBasis(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color); // Draw spline segment: B-Spline, 4 points RLAPI void DrawSplineSegmentCatmullRom(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color); // Draw spline segment: Catmull-Rom, 4 points @@ -369,7 +379,7 @@ RLAPI Image LoadImageFromScreen(void); RLAPI bool IsImageValid(Image image); // Check if an image is valid (data and parameters) RLAPI void UnloadImage(Image image); // Unload image from CPU memory (RAM) RLAPI bool ExportImage(Image image, const char *fileName); // Export image data to file, returns true on success -RLAPI unsigned char *ExportImageToMemory(Image image, const char *fileType, int *fileSize); // Export image to memory buffer +RLAPI unsigned char *ExportImageToMemory(Image image, const char *fileType, int *fileSize); // Export image to memory buffer, memory must be MemFree() RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes, returns true on success // Image generation functions @@ -399,7 +409,7 @@ RLAPI void ImageAlphaPremultiply(Image *image); RLAPI void ImageBlurGaussian(Image *image, int blurSize); // Apply Gaussian blur using a box blur approximation RLAPI void ImageKernelConvolution(Image *image, const float *kernel, int kernelSize); // Apply custom square convolution kernel to image RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (Bicubic scaling algorithm) -RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight); // Resize image (Nearest-Neighbor scaling algorithm) +RLAPI void ImageResizeNN(Image *image, int newWidth, int newHeight); // Resize image (Nearest-Neighbor scaling algorithm) RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill); // Resize canvas and fill with color RLAPI void ImageMipmaps(Image *image); // Compute all mipmap levels for a provided image RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) @@ -440,8 +450,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, 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 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) @@ -456,8 +466,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 @@ -498,11 +508,11 @@ RLAPI int GetPixelDataSize(int width, int height, int format); // G // Font loading/unloading functions RLAPI Font GetFontDefault(void); // Get the default Font RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM) -RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // 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 +RLAPI Font LoadFontEx(const char *fileName, int fontSize, const int *codepoints, int codepointCount); // 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 RLAPI Font LoadFontFromImage(Image image, Color key, int firstChar); // Load font from Image (XNA style) -RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' +RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, const int *codepoints, int codepointCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' RLAPI bool IsFontValid(Font font); // Check if a font is valid (font data loaded, WARNING: GPU texture not checked) -RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount, int type); // Load font data for further use +RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, const int *codepoints, int codepointCount, int type, int *glyphCount); // Load font data for further use RLAPI Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info RLAPI void UnloadFontData(GlyphInfo *glyphs, int glyphCount); // Unload font chars info data (RAM) RLAPI void UnloadFont(Font font); // Unload font from GPU memory (VRAM) @@ -520,42 +530,51 @@ RLAPI void DrawTextCodepoints(Font font, const int *codepoints, int codepointCou RLAPI void SetTextLineSpacing(int spacing); // Set vertical line spacing when drawing with line-breaks RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font +RLAPI Vector2 MeasureTextCodepoints(Font font, const int *codepoints, int length, float fontSize, float spacing); // Measure string size for an existing array of codepoints for Font RLAPI int GetGlyphIndex(Font font, int codepoint); // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found RLAPI GlyphInfo GetGlyphInfo(Font font, int codepoint); // Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found RLAPI Rectangle GetGlyphAtlasRec(Font font, int codepoint); // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found // Text codepoints management functions (unicode characters) -RLAPI char *LoadUTF8(const int *codepoints, int length); // Load UTF-8 text encoded from codepoints array -RLAPI void UnloadUTF8(char *text); // Unload UTF-8 text encoded from codepoints array -RLAPI int *LoadCodepoints(const char *text, int *count); // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter -RLAPI void UnloadCodepoints(int *codepoints); // Unload codepoints data from memory -RLAPI int GetCodepointCount(const char *text); // Get total number of codepoints in a UTF-8 encoded string -RLAPI int GetCodepoint(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure -RLAPI int GetCodepointNext(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure -RLAPI int GetCodepointPrevious(const char *text, int *codepointSize); // Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure -RLAPI const char *CodepointToUTF8(int codepoint, int *utf8Size); // Encode one codepoint into UTF-8 byte array (array length returned as parameter) +RLAPI char *LoadUTF8(const int *codepoints, int length); // Load UTF-8 text encoded from codepoints array +RLAPI void UnloadUTF8(char *text); // Unload UTF-8 text encoded from codepoints array +RLAPI int *LoadCodepoints(const char *text, int *count); // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter +RLAPI void UnloadCodepoints(int *codepoints); // Unload codepoints data from memory +RLAPI int GetCodepointCount(const char *text); // Get total number of codepoints in a UTF-8 encoded string +RLAPI int GetCodepoint(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure +RLAPI int GetCodepointNext(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure +RLAPI int GetCodepointPrevious(const char *text, int *codepointSize); // Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure +RLAPI const char *CodepointToUTF8(int codepoint, int *utf8Size); // Encode one codepoint into UTF-8 byte array (array length returned as parameter) // Text strings management functions (no UTF-8 strings, only byte chars) -// NOTE: Some strings allocate memory internally for returned strings, just be careful! +// WARNING 1: Most of these functions use internal static buffers[], it's recommended to store returned data on user-side for re-use +// WARNING 2: Some functions allocate memory internally for the returned strings, those strings must be freed by user using MemFree() +RLAPI char **LoadTextLines(const char *text, int *count); // Load text as separate lines ('\n') +RLAPI void UnloadTextLines(char **text, int lineCount); // Unload text lines RLAPI int TextCopy(char *dst, const char *src); // Copy one string to another, returns bytes copied RLAPI bool TextIsEqual(const char *text1, const char *text2); // Check if two text string are equal RLAPI unsigned int TextLength(const char *text); // Get text length, checks for '\0' ending RLAPI const char *TextFormat(const char *text, ...); // Text formatting with variables (sprintf() style) RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string -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 const char *TextJoin(const char **textList, int count, const char *delimiter); // Join text strings with delimiter -RLAPI const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple 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 const char *TextToUpper(const char *text); // Get upper case version of provided string -RLAPI const char *TextToLower(const char *text); // Get lower case version of provided string -RLAPI const char *TextToPascal(const char *text); // Get Pascal case notation version of provided string -RLAPI const char *TextToSnake(const char *text); // Get Snake case notation version of provided string -RLAPI const char *TextToCamel(const char *text); // Get Camel case notation version of provided string - -RLAPI int TextToInteger(const char *text); // Get integer value from text (negative values not supported) -RLAPI float TextToFloat(const char *text); // Get float value from text (negative values not supported) +RLAPI const char *TextRemoveSpaces(const char *text); // Remove text spaces, concat words +RLAPI char *GetTextBetween(const char *text, const char *begin, const char *end); // Get text between two strings +RLAPI char *TextReplace(const char *text, const char *search, const char *replacement); // Replace text string with new string +RLAPI char *TextReplaceAlloc(const char *text, const char *search, const char *replacement); // Replace text string with new string, memory must be MemFree() +RLAPI char *TextReplaceBetween(const char *text, const char *begin, const char *end, const char *replacement); // Replace text between two specific strings +RLAPI char *TextReplaceBetweenAlloc(const char *text, const char *begin, const char *end, const char *replacement); // Replace text between two specific strings, memory must be MemFree() +RLAPI char *TextInsert(const char *text, const char *insert, int position); // Insert text in a defined byte position +RLAPI char *TextInsertAlloc(const char *text, const char *insert, int position); // Insert text in a defined byte position, memory must be MemFree() +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 *search); // 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 +RLAPI char *TextToSnake(const char *text); // Get Snake case notation version of provided string +RLAPI char *TextToCamel(const char *text); // Get Camel case notation version of provided string +RLAPI int TextToInteger(const char *text); // Get integer value from text +RLAPI float TextToFloat(const char *text); // Get float value from text //------------------------------------------------------------------------------------ // Basic 3d Shapes Drawing Functions (Module: models) @@ -601,7 +620,7 @@ RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, floa RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set) RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters RLAPI void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires) -RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint); // Draw a billboard texture +RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint); // Draw a billboard texture RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source RLAPI void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint); // Draw a billboard texture defined by source and rotation @@ -639,21 +658,20 @@ RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); // Model animations loading/unloading functions RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount); // Load model animations from file -RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose (CPU) -RLAPI void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame); // Update model animation mesh bone matrices (GPU skinning) -RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data +RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, float frame); // Update model animation pose (vertex buffers and bone matrices) +RLAPI void UpdateModelAnimationEx(Model model, ModelAnimation animA, float frameA, ModelAnimation animB, float frameB, float blend); // Update model animation pose, blending two animations RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount); // Unload animation array data RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match // Collision detection functions -RLAPI bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2); // Check collision between two spheres -RLAPI bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2); // Check collision between two bounding boxes -RLAPI bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius); // Check collision between box and sphere -RLAPI RayCollision GetRayCollisionSphere(Ray ray, Vector3 center, float radius); // Get collision info between ray and sphere -RLAPI RayCollision GetRayCollisionBox(Ray ray, BoundingBox box); // Get collision info between ray and box -RLAPI RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform); // Get collision info between ray and mesh -RLAPI RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3); // Get collision info between ray and triangle -RLAPI RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4); // Get collision info between ray and quad +RLAPI bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2); // Check collision between two spheres +RLAPI bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2); // Check collision between two bounding boxes +RLAPI bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius); // Check collision between box and sphere +RLAPI RayCollision GetRayCollisionSphere(Ray ray, Vector3 center, float radius); // Get collision info between ray and sphere +RLAPI RayCollision GetRayCollisionBox(Ray ray, BoundingBox box); // Get collision info between ray and box +RLAPI RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform); // Get collision info between ray and mesh +RLAPI RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3); // Get collision info between ray and triangle +RLAPI RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4); // Get collision info between ray and quad //------------------------------------------------------------------------------------ // Audio Loading and Playing Functions (Module: audio) @@ -675,7 +693,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 (default data format: 32 bit float, stereo) RLAPI void UnloadWave(Wave wave); // Unload wave data RLAPI void UnloadSound(Sound sound); // Unload sound RLAPI void UnloadSoundAlias(Sound alias); // Unload a sound alias (does not deallocate sample data) @@ -690,7 +708,7 @@ RLAPI void ResumeSound(Sound sound); // Resume RLAPI bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing RLAPI void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level) RLAPI void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level) -RLAPI void SetSoundPan(Sound sound, float pan); // Set pan for a sound (0.5 is center) +RLAPI void SetSoundPan(Sound sound, float pan); // Set pan for a sound (-1.0 left, 0.0 center, 1.0 right) RLAPI Wave WaveCopy(Wave wave); // Copy a wave to a new wave RLAPI void WaveCrop(Wave *wave, int initFrame, int finalFrame); // Crop a wave to defined frames range RLAPI void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels); // Convert wave data to desired format @@ -711,7 +729,7 @@ RLAPI void ResumeMusicStream(Music music); // Resume RLAPI void SeekMusicStream(Music music, float position); // Seek music to a position (in seconds) RLAPI void SetMusicVolume(Music music, float volume); // Set volume for music (1.0 is max level) RLAPI void SetMusicPitch(Music music, float pitch); // Set pitch for a music (1.0 is base level) -RLAPI void SetMusicPan(Music music, float pan); // Set pan for a music (0.5 is center) +RLAPI void SetMusicPan(Music music, float pan); // Set pan for a music (-1.0 left, 0.0 center, 1.0 right) RLAPI float GetMusicTimeLength(Music music); // Get music time length (in seconds) RLAPI float GetMusicTimePlayed(Music music); // Get current music time played (in seconds) @@ -728,7 +746,7 @@ RLAPI bool IsAudioStreamPlaying(AudioStream stream); // Check i RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level) RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level) -RLAPI void SetAudioStreamPan(AudioStream stream, float pan); // Set pan for audio stream (0.5 is centered) +RLAPI void SetAudioStreamPan(AudioStream stream, float pan); // Set pan for audio stream (-1.0 to 1.0 range, 0.0 is centered) RLAPI void SetAudioStreamBufferSizeDefault(int size); // Default size for new audio streams RLAPI void SetAudioStreamCallback(AudioStream stream, AudioCallback callback); // Audio thread callback to request new data @@ -737,4 +755,3 @@ RLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processo RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline, receives frames x 2 samples as 'float' (stereo) RLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline - From dfbaafa337b8c2a873ca16596e633bf77c7f484e Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Tue, 14 Apr 2026 19:57:50 +0100 Subject: [PATCH 290/319] [zig] Adjusting build.zig to run on 0.16.0 (#5758) * Adjusting build.zig to run on 0.16.0 * changing version to 6 --- build.zig | 12 +----------- build.zig.zon | 4 ++-- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/build.zig b/build.zig index 1aedc58fe..2dc6258af 100644 --- a/build.zig +++ b/build.zig @@ -1,19 +1,9 @@ const std = @import("std"); const builtin = @import("builtin"); -/// Minimum supported version of Zig -const min_ver = "0.16.0-dev.3013+abd131e33"; - 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; - if (order(builtin.zig_version, parse(min_ver) catch unreachable) == .lt) - @compileError("Raylib requires zig version " ++ min_ver); -} - pub const emsdk = struct { const zemscripten = @import("zemscripten"); @@ -489,7 +479,7 @@ fn addExamples( const all = b.step(module, "All " ++ module ++ " examples"); const module_subpath = b.pathJoin(&.{ "examples", module }); - var dir = try std.Io.Dir.cwd().openDir(b.graph.io, b.pathFromRoot(module_subpath), .{ .iterate = true }); + var dir = try b.build_root.handle.openDir(b.graph.io, module_subpath, .{ .iterate = true }); defer dir.close(b.graph.io); var iter = dir.iterate(); diff --git a/build.zig.zon b/build.zig.zon index ee5a84c68..dd25fde73 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,7 +1,7 @@ .{ .name = .raylib, - .version = "5.6.0-dev", - .minimum_zig_version = "0.15.1", + .version = "6.0.0", + .minimum_zig_version = "0.16.0", .fingerprint = 0x13035e5cb8bc1ac2, // Changing this has security and trust implications. From 4628915787b5eb007e2fa8833e516be93e4f2c3c Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Wed, 15 Apr 2026 02:18:30 -0500 Subject: [PATCH 291/319] Update typos/grammar (#5759) --- src/config.h | 8 ++++---- src/raylib.h | 20 ++++++++++---------- src/raymath.h | 6 +++--- src/rcamera.h | 4 ++-- src/rcore.c | 14 +++++++------- src/rgestures.h | 6 +++--- src/rlgl.h | 8 ++++---- src/rmodels.c | 2 +- src/rshapes.c | 4 ++-- src/rtextures.c | 4 ++-- 10 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/config.h b/src/config.h index a3502d772..37a2c050e 100644 --- a/src/config.h +++ b/src/config.h @@ -73,7 +73,7 @@ #define SUPPORT_RPRAND_GENERATOR 1 #endif #ifndef SUPPORT_MOUSE_GESTURES - // Mouse gestures are directly mapped like touches and processed by gestures system + // Mouse gestures are directly mapped like touches and processed by the gestures system #define SUPPORT_MOUSE_GESTURES 1 #endif #ifndef SUPPORT_SSH_KEYBOARD_RPI @@ -125,7 +125,7 @@ #endif // rcore: Configuration values -// NOTE: Below values are alread defined inside [rcore.c] so there is no need to be +// NOTE: Below values are already defined inside [rcore.c] so there is no need to be // redefined here, in case it must be done, uncomment the required line and update // the value; it can also be done on compilation with -DVALUE_TO_REDEFINE=128 //------------------------------------------------------------------------------------ @@ -159,7 +159,7 @@ #endif // rlgl: Configuration values -// NOTE: Below values are alread defined inside [rlgl.h] so there is no need to be +// NOTE: Below values are already defined inside [rlgl.h] so there is no need to be // redefined here, in case it must be done, uncomment the required line and update // the value; it can also be done on compilation with -DVALUE_TO_REDEFINE=128 //------------------------------------------------------------------------------------ @@ -349,7 +349,7 @@ #endif // raudio: Configuration values -// NOTE: Below values are alread defined inside [rlgl.h] so there is no need to be +// NOTE: Below values are already defined inside [rlgl.h] so there is no need to be // redefined here, in case it must be done, uncomment the required line and update // the value; it can also be done on compilation with -DVALUE_TO_REDEFINE=128 //------------------------------------------------------------------------------------ diff --git a/src/raylib.h b/src/raylib.h index 34c5a6488..bdca64329 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -10,7 +10,7 @@ * - Software renderer optional, for systems with no GPU: [rlsw] * - Custom OpenGL abstraction layer (usable as standalone module): [rlgl] * - Multiple Fonts formats supported (TTF, OTF, FNT, BDF, Sprite fonts) -* - Many texture formats supportted, including compressed formats (DXT, ETC, ASTC) +* - Many texture formats supported, including compressed formats (DXT, ETC, ASTC) * - Full 3d support for 3d Shapes, Models, Billboards, Heightmaps and more! * - Flexible Materials system, supporting classic maps and PBR maps * - Animated 3D models supported (skeletal bones animation) (IQM, M3D, GLTF) @@ -35,14 +35,14 @@ * [rcore] sinfl (Micha Mettke) for DEFLATE decompression algorithm * [rcore] sdefl (Micha Mettke) for DEFLATE compression algorithm * [rcore] rprand (Ramon Santamaria) for pseudo-random numbers generation -* [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) -* [rtextures] stb_image_resize2 (Sean Barret) for image resizing algorithms -* [rtextures] stb_perlin (Sean Barret) for Perlin Noise image generation +* [rtextures] qoi (Dominic Szablewski - https://phoboslab.org) for QOI image management +* [rtextures] stb_image (Sean Barrett) for images loading (BMP, TGA, PNG, JPEG, HDR...) +* [rtextures] stb_image_write (Sean Barrett) for image writing (BMP, TGA, PNG, JPG) +* [rtextures] stb_image_resize2 (Sean Barrett) for image resizing algorithms +* [rtextures] stb_perlin (Sean Barrett) for Perlin Noise image generation * [rtextures] rltexgpu (Ramon Santamaria) for GPU-compressed texture formats -* [rtext] stb_truetype (Sean Barret) for ttf fonts loading -* [rtext] stb_rect_pack (Sean Barret) for rectangles packing +* [rtext] stb_truetype (Sean Barrett) for ttf fonts loading +* [rtext] stb_rect_pack (Sean Barrett) for rectangles packing * [rmodels] par_shapes (Philip Rideout) for parametric 3d shapes generation * [rmodels] tinyobj_loader_c (Syoyo Fujita) for models loading (OBJ, MTL) * [rmodels] cgltf (Johannes Kuhlmann) for models loading (glTF) @@ -51,10 +51,10 @@ * [raudio] dr_wav (David Reid) for WAV audio file loading * [raudio] dr_flac (David Reid) for FLAC audio file loading * [raudio] dr_mp3 (David Reid) for MP3 audio file loading -* [raudio] stb_vorbis (Sean Barret) for OGG audio loading +* [raudio] stb_vorbis (Sean Barrett) for OGG audio loading * [raudio] jar_xm (Joshua Reisenauer) for XM audio module loading * [raudio] jar_mod (Joshua Reisenauer) for MOD audio module loading -* [raudio] qoa (Dominic Szablewski - https://phoboslab.org) for QOA audio manage +* [raudio] qoa (Dominic Szablewski - https://phoboslab.org) for QOA audio management * * * LICENSE: zlib/libpng diff --git a/src/raymath.h b/src/raymath.h index 8a0cce1a5..4460ed3f1 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -15,7 +15,7 @@ * - Functions use always a "result" variable for return (except C++ operators) * - Functions are always defined inline * - Angles are always in radians (DEG2RAD/RAD2DEG macros provided for convenience) -* - No compound literals used to make sure libray is compatible with C++ +* - No compound literals used to make sure the library is compatible with C++ * * CONFIGURATION: * #define RAYMATH_IMPLEMENTATION @@ -183,7 +183,7 @@ typedef struct float16 { #if RAYMATH_USE_SIMD_INTRINSICS // SIMD is used on the most costly raymath function MatrixMultiply() // NOTE: Only SSE intrinsics support implemented - // TODO: Consider support for other SIMD instrinsics: + // TODO: Consider support for other SIMD intrinsics: // - SSEx, AVX, AVX2, FMA, NEON, RVV /* #if defined(__SSE4_2__) @@ -1151,7 +1151,7 @@ RMAPI Vector3 Vector3Unproject(Vector3 source, Matrix projection, Matrix view) // Create quaternion from source point Quaternion quat = { source.x, source.y, source.z, 1.0f }; - // Multiply quat point by unprojecte matrix + // Multiply quat point by unprojected matrix Quaternion qtransformed = { // QuaternionTransform(quat, matViewProjInv) matViewProjInv.m0*quat.x + matViewProjInv.m4*quat.y + matViewProjInv.m8*quat.z + matViewProjInv.m12*quat.w, matViewProjInv.m1*quat.x + matViewProjInv.m5*quat.y + matViewProjInv.m9*quat.z + matViewProjInv.m13*quat.w, diff --git a/src/rcamera.h b/src/rcamera.h index 14616a771..e6f5bba21 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -103,7 +103,7 @@ Vector3 position; // Camera position Vector3 target; // Camera target it looks-at Vector3 up; // Camera up vector (rotation over its axis) - float fovy; // Camera field-of-view apperture in Y (degrees) in perspective, used as near plane width in orthographic + float fovy; // Camera field-of-view aperture in Y (degrees) in perspective, used as near plane width in orthographic int projection; // Camera projection type: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC } Camera3D; @@ -352,7 +352,7 @@ void CameraYaw(Camera *camera, float angle, bool rotateAroundTarget) // Rotates the camera around its right vector, pitch is "looking up and down" // - lockView prevents camera overrotation (aka "somersaults") // - rotateAroundTarget defines if rotation is around target or around its position -// - rotateUp rotates the up direction as well (typically only usefull in CAMERA_FREE) +// - rotateUp rotates the up direction as well (typically only useful in CAMERA_FREE) // NOTE: [angle] must be provided in radians void CameraPitch(Camera *camera, float angle, bool lockView, bool rotateAroundTarget, bool rotateUp) { diff --git a/src/rcore.c b/src/rcore.c index f93e25573..92652be0e 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -307,7 +307,7 @@ typedef struct CoreData { Size screen; // Screen current width and height Point position; // Window current position Size previousScreen; // Screen previous width and height (required on fullscreen/borderless-windowed toggle) - Point previousPosition; // Window previous position (required on fullscreeen/borderless-windowed toggle) + Point previousPosition; // Window previous position (required on fullscreen/borderless-windowed toggle) Size render; // Screen framebuffer width and height Point renderOffset; // Screen framebuffer render offset (Not required anymore?) Size currentFbo; // Current framebuffer render width and height (depends on active render texture) @@ -329,7 +329,7 @@ typedef struct CoreData { char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state - // NOTE: Since key press logic involves comparing previous vs currrent key state, + // NOTE: Since key press logic involves comparing previous vs current key state, // key repeats needs to be handled specially char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame @@ -714,7 +714,7 @@ void InitWindow(int width, int height, const char *title) Rectangle rec = GetFontDefault().recs[95]; if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) { - // NOTE: Try to maxime rec padding to avoid pixel bleeding on MSAA filtering + // NOTE: Try to maximize rec padding to avoid pixel bleeding on MSAA filtering SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); } else @@ -2514,9 +2514,9 @@ const char *GetFileNameWithoutExt(const char *filePath) if (filePath != NULL) { strncpy(fileName, GetFileName(filePath), MAX_FILENAME_LENGTH - 1); // Get filename.ext without path - int fileNameLenght = (int)strlen(fileName); // Get size in bytes + int fileNameLength = (int)strlen(fileName); // Get size in bytes - for (int i = fileNameLenght; i > 0; i--) // Reverse search '.' + for (int i = fileNameLength; i > 0; i--) // Reverse search '.' { if (fileName[i] == '.') { @@ -3039,7 +3039,7 @@ unsigned char *DecompressData(const unsigned char *compData, int compDataSize, i char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize) { // 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) + // NOTE: They represent 64 values (6 bits), to encode 3 bytes of data into 4 "sextets" (6bit characters) static const char base64EncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // Compute expected size and padding @@ -3126,7 +3126,7 @@ unsigned char *DecodeDataBase64(const char *text, int *outputSize) int outputCount = 0; for (int i = 0; i < dataSize;) { - // Every 4 sixtets must generate 3 octets + // Every 4 sextets must generate 3 octets if ((i + 2) >= dataSize) { TRACELOG(LOG_WARNING, "BASE64: Decoding error: Input data size is not valid"); diff --git a/src/rgestures.h b/src/rgestures.h index e6cb86300..bb51d72ee 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -124,7 +124,7 @@ void UpdateGestures(void); // Update gestures detec #if defined(RGESTURES_STANDALONE) void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags -bool IsGestureDetected(int gesture); // Check if a gesture have been detected +bool IsGestureDetected(int gesture); // Check if a gesture has been detected int GetGestureDetected(void); // Get latest detected gesture float GetGestureHoldDuration(void); // Get gesture hold time in seconds @@ -297,10 +297,10 @@ void ProcessGestureEvent(GestureEvent event) } else if (event.touchAction == TOUCH_ACTION_UP) { - // A swipe can happen while the current gesture is drag, but (specially for web) also hold, so set upPosition for both cases + // A swipe can happen while the current gesture is drag, but (especially for web) also hold, so set upPosition for both cases if (GESTURES.current == GESTURE_DRAG || GESTURES.current == GESTURE_HOLD) GESTURES.Touch.upPosition = event.position[0]; - // NOTE: GESTURES.Drag.intensity dependent on the resolution of the screen + // NOTE: GESTURES.Drag.intensity is dependent on the resolution of the screen GESTURES.Drag.distance = rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition); GESTURES.Drag.intensity = GESTURES.Drag.distance/(float)((rgGetCurrentTime() - GESTURES.Swipe.startTime)); diff --git a/src/rlgl.h b/src/rlgl.h index 073cecd68..b7854e46e 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -7,10 +7,10 @@ * that provides a pseudo-OpenGL 1.1 immediate-mode style API (rlVertex, rlTranslate, rlRotate...) * * ADDITIONAL NOTES: -* When choosing an OpenGL backend different than OpenGL 1.1, some internal buffer are +* When choosing an OpenGL backend different than OpenGL 1.1, some internal buffers are * initialized on rlglInit() to accumulate vertex data * -* When an internal state change is required all the stored vertex data is rendered in batch, +* When an internal state change is required all the stored vertex data is rendered in a batch, * additionally, rlDrawRenderBatchActive() could be called to force flushing of the batch * * Some resources are also loaded for convenience, here the complete list: @@ -29,7 +29,7 @@ * #define GRAPHICS_API_OPENGL_ES2 * #define GRAPHICS_API_OPENGL_ES3 * Use selected OpenGL graphics backend, should be supported by platform -* Those preprocessor defines are only used on rlgl module, if OpenGL version is +* Those preprocessor defines are only used on the rlgl module, if OpenGL version is * required by any other module, use rlGetVersion() to check it * * #define RLGL_IMPLEMENTATION @@ -49,7 +49,7 @@ * #define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 8192 // Default internal render batch elements limits * #define RL_DEFAULT_BATCH_BUFFERS 1 // Default number of batch buffers (multi-buffering) * #define RL_DEFAULT_BATCH_DRAWCALLS 256 // Default number of batch draw calls (by state changes: mode, texture) -* #define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of textures units that can be activated on batch drawing (SetShaderValueTexture()) +* #define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of texture units that can be activated on batch drawing (SetShaderValueTexture()) * * #define RL_MAX_MATRIX_STACK_SIZE 32 // Maximum size of internal Matrix stack * #define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported diff --git a/src/rmodels.c b/src/rmodels.c index 3dd28c6c1..17b67302a 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -6898,7 +6898,7 @@ static Model LoadM3D(const char *fileName) mi = m3d->face[i].materialid; // Only allocate colors VertexBuffer if there's a color vertex in the model for this material batch - // if all colors are fully transparent black for all verteces of this materal, then assuming no vertex colors + // if all colors are fully transparent black for all vertices of this material, then assuming no vertex colors for (j = i, l = vcolor = 0; (j < (int)m3d->numface) && (mi == m3d->face[j].materialid); j++, l++) { if (!m3d->vertex[m3d->face[j].vertex[0]].color || diff --git a/src/rshapes.c b/src/rshapes.c index b39ecb743..daec36060 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -8,11 +8,11 @@ * 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 +* user-provided texture with SetShapesTexture(), the purpose of this implementation * 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 +* white character of default font [rtext], this way, raylib text and shapes can be drawn with * a single draw call and it also allows users to configure it the same way with their own fonts * * CONFIGURATION: diff --git a/src/rtextures.c b/src/rtextures.c index b13f697d1..a1f6618c7 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -366,7 +366,7 @@ Image LoadImageAnim(const char *fileName, int *frames) return image; } -// Load animated image data +// Load animated image data from memory // - Image.data buffer includes all frames: [image#0][image#1][image#2][...] // - Number of frames is returned through 'frames' parameter // - All frames are returned in RGBA format @@ -1575,7 +1575,7 @@ Image ImageFromChannel(Image image, int selectedChannel) // Check for RGBA formats if (selectedChannel > 3) { - TRACELOG(LOG_WARNING, "ImageFromChannel supports channels 0 to 3 (rgba). Setting channel to alpha."); + TRACELOG(LOG_WARNING, "ImageFromChannel supports channels 0 to 3 (RGBA). Setting channel to alpha."); selectedChannel = 3; } From 0decdcd497aadc8adb05bf3e731ed22356f4eef5 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Wed, 15 Apr 2026 05:20:51 -0500 Subject: [PATCH 292/319] update defines for new format etc (#5760) --- src/config.h | 3 +++ src/raudio.c | 18 +++++++++--------- src/rcore.c | 16 ++++++++-------- src/rmodels.c | 18 +++++++++--------- src/rshapes.c | 4 ++-- src/rtext.c | 16 ++++++++-------- src/rtextures.c | 40 ++++++++++++++++++++-------------------- 7 files changed, 59 insertions(+), 56 deletions(-) diff --git a/src/config.h b/src/config.h index 37a2c050e..c50f2bcbf 100644 --- a/src/config.h +++ b/src/config.h @@ -252,6 +252,9 @@ #ifndef SUPPORT_FILEFORMAT_PIC #define SUPPORT_FILEFORMAT_PIC 0 // Disabled by default #endif +#ifdef SUPPORT_FILEFORMAT_PNM + #define SUPPORT_FILEFORMAT_PNM 0 // Disabled by default +#endif #ifndef SUPPORT_FILEFORMAT_KTX #define SUPPORT_FILEFORMAT_KTX 0 // Disabled by default #endif diff --git a/src/raudio.c b/src/raudio.c index 8107886cb..3cd6b37aa 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -11,22 +11,22 @@ * - Play/Stop/Pause/Resume loaded audio * * CONFIGURATION: -* #define SUPPORT_MODULE_RAUDIO +* #define SUPPORT_MODULE_RAUDIO 1 * 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 SUPPORT_FILEFORMAT_WAV -* #define SUPPORT_FILEFORMAT_OGG -* #define SUPPORT_FILEFORMAT_MP3 -* #define SUPPORT_FILEFORMAT_QOA -* #define SUPPORT_FILEFORMAT_FLAC -* #define SUPPORT_FILEFORMAT_XM -* #define SUPPORT_FILEFORMAT_MOD +* #define SUPPORT_FILEFORMAT_WAV 1 +* #define SUPPORT_FILEFORMAT_OGG 1 +* #define SUPPORT_FILEFORMAT_MP3 1 +* #define SUPPORT_FILEFORMAT_QOA 1 +* #define SUPPORT_FILEFORMAT_FLAC 0 +* #define SUPPORT_FILEFORMAT_XM 1 +* #define SUPPORT_FILEFORMAT_MOD 1 * Selected desired fileformats to be supported for loading. Some of those formats are -* supported by default, to remove support, comment unrequired #define in this module +* supported by default, to remove support, #define as 0 in this module or your build system * * DEPENDENCIES: * miniaudio.h - Audio device management lib (https://github.com/mackron/miniaudio) diff --git a/src/rcore.c b/src/rcore.c index 92652be0e..46d964961 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -35,31 +35,31 @@ * - Memory framebuffer output, using software renderer, no OS required * * CONFIGURATION: -* #define SUPPORT_CAMERA_SYSTEM +* #define SUPPORT_CAMERA_SYSTEM 1 * Camera module is included (rcamera.h) and multiple predefined cameras are available: * free, 1st/3rd person, orbital, custom * -* #define SUPPORT_GESTURES_SYSTEM +* #define SUPPORT_GESTURES_SYSTEM 1 * Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag * -* #define SUPPORT_MOUSE_GESTURES +* #define SUPPORT_MOUSE_GESTURES 1 * Mouse gestures are directly mapped like touches and processed by gestures system * -* #define SUPPORT_BUSY_WAIT_LOOP +* #define SUPPORT_BUSY_WAIT_LOOP 1 * Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used * -* #define SUPPORT_PARTIALBUSY_WAIT_LOOP +* #define SUPPORT_PARTIALBUSY_WAIT_LOOP 0 * Use a partial-busy wait loop, in this case frame sleeps for most of the time and runs a busy-wait-loop at the end * -* #define SUPPORT_SCREEN_CAPTURE +* #define SUPPORT_SCREEN_CAPTURE 1 * Allow automatic screen capture of current screen pressing F12, defined in KeyCallback() * -* #define SUPPORT_COMPRESSION_API +* #define SUPPORT_COMPRESSION_API 1 * Support CompressData() and DecompressData() functions, those functions use zlib implementation * provided by stb_image and stb_image_write libraries, so, those libraries must be enabled on textures module * for linkage * -* #define SUPPORT_AUTOMATION_EVENTS +* #define SUPPORT_AUTOMATION_EVENTS 1 * Support automatic events recording and playing, useful for automated testing systems or AI based game playing * * DEPENDENCIES: diff --git a/src/rmodels.c b/src/rmodels.c index 17b67302a..1b39bed5c 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3,19 +3,19 @@ * rmodels - Basic functions to draw 3d shapes and load and draw 3d models * * CONFIGURATION: -* #define SUPPORT_MODULE_RMODELS +* #define SUPPORT_MODULE_RMODELS 1 * rmodels module is included in the build * -* #define SUPPORT_FILEFORMAT_OBJ -* #define SUPPORT_FILEFORMAT_MTL -* #define SUPPORT_FILEFORMAT_IQM -* #define SUPPORT_FILEFORMAT_GLTF -* #define SUPPORT_FILEFORMAT_GLTF_WRITE -* #define SUPPORT_FILEFORMAT_VOX -* #define SUPPORT_FILEFORMAT_M3D +* #define SUPPORT_FILEFORMAT_OBJ 1 +* #define SUPPORT_FILEFORMAT_MTL 1 +* #define SUPPORT_FILEFORMAT_IQM 1 +* #define SUPPORT_FILEFORMAT_GLTF 1 +* #define SUPPORT_FILEFORMAT_GLTF_WRITE 0 +* #define SUPPORT_FILEFORMAT_VOX 1 +* #define SUPPORT_FILEFORMAT_M3D 1 * Selected desired fileformats to be supported for model data loading * -* #define SUPPORT_MESH_GENERATION +* #define SUPPORT_MESH_GENERATION 1 * Support procedural mesh generation functions, uses external par_shapes.h library * NOTE: Some generated meshes DO NOT include generated texture coordinates * diff --git a/src/rshapes.c b/src/rshapes.c index daec36060..0ceeaa68d 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -16,10 +16,10 @@ * a single draw call and it also allows users to configure it the same way with their own fonts * * CONFIGURATION: -* #define SUPPORT_MODULE_RSHAPES +* #define SUPPORT_MODULE_RSHAPES 1 * rshapes module is included in the build * -* #define SUPPORT_QUADS_DRAW_MODE +* #define SUPPORT_QUADS_DRAW_MODE 1 * Use QUADS instead of TRIANGLES for drawing when possible. Lines-based shapes still use LINES * * diff --git a/src/rtext.c b/src/rtext.c index f71991e33..41f943f26 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -3,22 +3,22 @@ * rtext - Basic functions to load fonts and draw text * * CONFIGURATION: -* #define SUPPORT_MODULE_RTEXT +* #define SUPPORT_MODULE_RTEXT 1 * rtext module is included in the build * -* #define SUPPORT_FILEFORMAT_FNT -* #define SUPPORT_FILEFORMAT_TTF -* #define SUPPORT_FILEFORMAT_BDF +* #define SUPPORT_FILEFORMAT_FNT 1 +* #define SUPPORT_FILEFORMAT_TTF 1 +* #define SUPPORT_FILEFORMAT_BDF 0 * Selected desired fileformats to be supported for loading. Some of those formats are -* supported by default, to remove support, comment unrequired #define in this module +* supported by default, to remove support, #define as 0 in this module or your build system * -* #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH +* #define MAX_TEXT_BUFFER_LENGTH 1024 * TextSplit() function static buffer max size * -* #define MAX_TEXTSPLIT_COUNT +* #define MAX_TEXTSPLIT_COUNT 128 * TextSplit() function static substrings pointers array (pointing to static buffer) * -* #define FONT_ATLAS_CORNER_REC_SIZE +* #define FONT_ATLAS_CORNER_REC_SIZE 3 * On font atlas image generation [GenImageFontAtlas()], add a NxN 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()] diff --git a/src/rtextures.c b/src/rtextures.c index a1f6618c7..8a815abef 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3,31 +3,31 @@ * rtextures - Basic functions to load and draw textures * * CONFIGURATION: -* #define SUPPORT_MODULE_RTEXTURES +* #define SUPPORT_MODULE_RTEXTURES 1 * rtextures module is included in the build * -* #define SUPPORT_FILEFORMAT_BMP -* #define SUPPORT_FILEFORMAT_PNG -* #define SUPPORT_FILEFORMAT_TGA -* #define SUPPORT_FILEFORMAT_JPG -* #define SUPPORT_FILEFORMAT_GIF -* #define SUPPORT_FILEFORMAT_QOI -* #define SUPPORT_FILEFORMAT_PSD -* #define SUPPORT_FILEFORMAT_HDR -* #define SUPPORT_FILEFORMAT_PIC -* #define SUPPORT_FILEFORMAT_PNM -* #define SUPPORT_FILEFORMAT_DDS -* #define SUPPORT_FILEFORMAT_PKM -* #define SUPPORT_FILEFORMAT_KTX -* #define SUPPORT_FILEFORMAT_PVR -* #define SUPPORT_FILEFORMAT_ASTC -* Select desired fileformats to be supported for image data loading. Some of those formats are -* supported by default, to remove support, comment unrequired #define in this module +* #define SUPPORT_FILEFORMAT_BMP 1 +* #define SUPPORT_FILEFORMAT_PNG 1 +* #define SUPPORT_FILEFORMAT_TGA 0 +* #define SUPPORT_FILEFORMAT_JPG 0 +* #define SUPPORT_FILEFORMAT_GIF 1 +* #define SUPPORT_FILEFORMAT_QOI 1 +* #define SUPPORT_FILEFORMAT_PSD 0 +* #define SUPPORT_FILEFORMAT_HDR 0 +* #define SUPPORT_FILEFORMAT_PIC 0 +* #define SUPPORT_FILEFORMAT_PNM 0 +* #define SUPPORT_FILEFORMAT_DDS 1 +* #define SUPPORT_FILEFORMAT_PKM 0 +* #define SUPPORT_FILEFORMAT_KTX 0 +* #define SUPPORT_FILEFORMAT_PVR 0 +* #define SUPPORT_FILEFORMAT_ASTC 0 +* Selected desired fileformats to be supported for image data loading. Some of those formats are +* supported by default, to remove support, #define as 0 in this module or your build system * -* #define SUPPORT_IMAGE_EXPORT +* #define SUPPORT_IMAGE_EXPORT 1 * Support image export in multiple file formats * -* #define SUPPORT_IMAGE_GENERATION +* #define SUPPORT_IMAGE_GENERATION 1 * Support procedural image generation functionality (gradient, spot, perlin-noise, cellular) * * DEPENDENCIES: From 82e980bd42389d6fb2a2c34d093478709b38736b Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 15 Apr 2026 12:37:42 +0200 Subject: [PATCH 293/319] Log info about HighDPI content scaling on display initialization --- src/platforms/rcore_desktop_rgfw.c | 3 ++- src/platforms/rcore_desktop_sdl.c | 3 ++- src/platforms/rcore_desktop_win32.c | 3 ++- src/platforms/rcore_drm.c | 11 +++-------- src/platforms/rcore_template.c | 9 ++------- src/platforms/rcore_web.c | 3 ++- src/platforms/rcore_web_emscripten.c | 3 ++- 7 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 28a617af9..6b021ce66 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -1719,7 +1719,8 @@ int InitPlatform(void) #endif } - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully %s", + FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)? "(HighDPI)" : ""); TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index dd845131e..277421720 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -2068,7 +2068,8 @@ int InitPlatform(void) CORE.Window.currentFbo.width = CORE.Window.render.width; CORE.Window.currentFbo.height = CORE.Window.render.height; - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully %s", + FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)? "(HighDPI)" : ""); TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index 07262aaea..3f2dcd8e3 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -1643,7 +1643,8 @@ int InitPlatform(void) CORE.Window.render.height = CORE.Window.screen.height; CORE.Window.currentFbo.width = CORE.Window.render.width; CORE.Window.currentFbo.height = CORE.Window.render.height; - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully %s", + FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)? "(HighDPI)" : ""); TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index e742cb625..708a78004 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -1549,12 +1549,6 @@ int InitPlatform(void) CORE.Window.render.height = CORE.Window.screen.height; CORE.Window.currentFbo.width = CORE.Window.render.width; CORE.Window.currentFbo.height = CORE.Window.render.height; - - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); - TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); - TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); - TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); - TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); } else { @@ -1580,13 +1574,14 @@ int InitPlatform(void) CORE.Window.render.height = CORE.Window.screen.height; CORE.Window.currentFbo.width = CORE.Window.render.width; CORE.Window.currentFbo.height = CORE.Window.render.height; +#endif - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully (Software Rendering)"); + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully %s", + FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)? "(HighDPI)" : ""); TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); -#endif if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) MinimizeWindow(); diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 368dd3e77..c48484abb 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -474,12 +474,6 @@ int InitPlatform(void) CORE.Window.render.height = CORE.Window.screen.height; CORE.Window.currentFbo.width = CORE.Window.render.width; CORE.Window.currentFbo.height = CORE.Window.render.height; - - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); - TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); - TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); - TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); - TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); } else { @@ -494,7 +488,8 @@ int InitPlatform(void) CORE.Window.currentFbo.width = CORE.Window.render.width; CORE.Window.currentFbo.height = CORE.Window.render.height; - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully %s", + FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)? "(HighDPI)" : ""); TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 8835ecdcc..e7e174127 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -1423,7 +1423,8 @@ int InitPlatform(void) CORE.Window.currentFbo.width = fbWidth; CORE.Window.currentFbo.height = fbHeight; - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully %s", + FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)? "(HighDPI)" : ""); TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); diff --git a/src/platforms/rcore_web_emscripten.c b/src/platforms/rcore_web_emscripten.c index 3b6064255..186813f49 100644 --- a/src/platforms/rcore_web_emscripten.c +++ b/src/platforms/rcore_web_emscripten.c @@ -1245,7 +1245,8 @@ int InitPlatform(void) CORE.Window.currentFbo.width = fbWidth; CORE.Window.currentFbo.height = fbHeight; - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully %s", + FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)? "(HighDPI)" : ""); TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); From d8ebeb8939c179de3e163e66eb2da25d10a3f040 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 15 Apr 2026 21:03:25 +0200 Subject: [PATCH 294/319] Update raymath.h --- src/raymath.h | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/raymath.h b/src/raymath.h index 4460ed3f1..3d59266c5 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -1264,19 +1264,21 @@ RMAPI Vector3 Vector3Refract(Vector3 v, Vector3 n, float r) //---------------------------------------------------------------------------------- // Module Functions Definition - Vector4 math //---------------------------------------------------------------------------------- - +// Get vector zero RMAPI Vector4 Vector4Zero(void) { Vector4 result = { 0.0f, 0.0f, 0.0f, 0.0f }; return result; } +// Get vector one RMAPI Vector4 Vector4One(void) { Vector4 result = { 1.0f, 1.0f, 1.0f, 1.0f }; return result; } +// Add two vectors RMAPI Vector4 Vector4Add(Vector4 v1, Vector4 v2) { Vector4 result = { @@ -1288,6 +1290,7 @@ RMAPI Vector4 Vector4Add(Vector4 v1, Vector4 v2) return result; } +// Add value to vector components RMAPI Vector4 Vector4AddValue(Vector4 v, float add) { Vector4 result = { @@ -1299,6 +1302,7 @@ RMAPI Vector4 Vector4AddValue(Vector4 v, float add) return result; } +// Substract vectors RMAPI Vector4 Vector4Subtract(Vector4 v1, Vector4 v2) { Vector4 result = { @@ -1310,6 +1314,7 @@ RMAPI Vector4 Vector4Subtract(Vector4 v1, Vector4 v2) return result; } +// Substract value from vector components RMAPI Vector4 Vector4SubtractValue(Vector4 v, float add) { Vector4 result = { @@ -1321,18 +1326,21 @@ RMAPI Vector4 Vector4SubtractValue(Vector4 v, float add) return result; } +// Vector length RMAPI float Vector4Length(Vector4 v) { float result = sqrtf((v.x*v.x) + (v.y*v.y) + (v.z*v.z) + (v.w*v.w)); return result; } +// Vector square length RMAPI float Vector4LengthSqr(Vector4 v) { float result = (v.x*v.x) + (v.y*v.y) + (v.z*v.z) + (v.w*v.w); return result; } +// Vectors dot product RMAPI float Vector4DotProduct(Vector4 v1, Vector4 v2) { float result = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z + v1.w*v2.w); @@ -1358,6 +1366,7 @@ RMAPI float Vector4DistanceSqr(Vector4 v1, Vector4 v2) return result; } +// Scale vector components by value (multiply) RMAPI Vector4 Vector4Scale(Vector4 v, float scale) { Vector4 result = { v.x*scale, v.y*scale, v.z*scale, v.w*scale }; @@ -1753,13 +1762,14 @@ RMAPI Matrix MatrixMultiply(Matrix left, Matrix right) return result; } +// Multiply matrix components by value RMAPI Matrix MatrixMultiplyValue(Matrix left, float value) { Matrix result = { - left.m0 * value, left.m4 * value, left.m8 * value, left.m12 * value, - left.m1 * value, left.m5 * value, left.m9 * value, left.m13 * value, - left.m2 * value, left.m6 * value, left.m10 * value, left.m14 * value, - left.m3 * value, left.m7 * value, left.m11 * value, left.m15 * value + left.m0*value, left.m4*value, left.m8*value, left.m12*value, + left.m1*value, left.m5*value, left.m9*value, left.m13*value, + left.m2*value, left.m6*value, left.m10*value, left.m14*value, + left.m3*value, left.m7*value, left.m11*value, left.m15*value }; return result; From 21897f4bb9e326955d9c26772bb3fdef436d8534 Mon Sep 17 00:00:00 2001 From: nadav78 <95895660+nadav78@users.noreply.github.com> Date: Thu, 16 Apr 2026 00:51:47 +0300 Subject: [PATCH 295/319] Remap stb_vorbis malloc/free calls to RL_MALLOC/RL_FREE (#5763) --- src/raudio.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index 3cd6b37aa..9ca5e9c7d 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -210,8 +210,11 @@ typedef struct tagBITMAPINFOHEADER { #endif #if SUPPORT_FILEFORMAT_OGG - // TODO: Remap stb_vorbis malloc()/free() calls to RL_MALLOC/RL_FREE + #define malloc RL_MALLOC + #define free RL_FREE #include "external/stb_vorbis.c" // OGG loading functions + #undef malloc + #undef free #endif #if SUPPORT_FILEFORMAT_MP3 From 1e43c1d3724dd4248a784e3150377c1590488c14 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 15 Apr 2026 23:58:05 +0200 Subject: [PATCH 296/319] Revert "Remap stb_vorbis malloc/free calls to RL_MALLOC/RL_FREE (#5763)" This reverts commit 21897f4bb9e326955d9c26772bb3fdef436d8534. --- src/raudio.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 9ca5e9c7d..3cd6b37aa 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -210,11 +210,8 @@ typedef struct tagBITMAPINFOHEADER { #endif #if SUPPORT_FILEFORMAT_OGG - #define malloc RL_MALLOC - #define free RL_FREE + // TODO: Remap stb_vorbis malloc()/free() calls to RL_MALLOC/RL_FREE #include "external/stb_vorbis.c" // OGG loading functions - #undef malloc - #undef free #endif #if SUPPORT_FILEFORMAT_MP3 From 96e30549f5f79611bd11c1899e36fb6008e886a1 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 16 Apr 2026 00:05:23 +0200 Subject: [PATCH 297/319] Align default values on comments for config variables --- src/raudio.c | 16 ++++++++-------- src/rcore.c | 14 +++++++------- src/rmodels.c | 18 +++++++++--------- src/rshapes.c | 4 ++-- src/rtext.c | 16 ++++++++-------- src/rtextures.c | 36 ++++++++++++++++++------------------ 6 files changed, 52 insertions(+), 52 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 3cd6b37aa..d624755ad 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -11,20 +11,20 @@ * - Play/Stop/Pause/Resume loaded audio * * CONFIGURATION: -* #define SUPPORT_MODULE_RAUDIO 1 +* #define SUPPORT_MODULE_RAUDIO 1 * 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 SUPPORT_FILEFORMAT_WAV 1 -* #define SUPPORT_FILEFORMAT_OGG 1 -* #define SUPPORT_FILEFORMAT_MP3 1 -* #define SUPPORT_FILEFORMAT_QOA 1 -* #define SUPPORT_FILEFORMAT_FLAC 0 -* #define SUPPORT_FILEFORMAT_XM 1 -* #define SUPPORT_FILEFORMAT_MOD 1 +* #define SUPPORT_FILEFORMAT_WAV 1 +* #define SUPPORT_FILEFORMAT_OGG 1 +* #define SUPPORT_FILEFORMAT_MP3 1 +* #define SUPPORT_FILEFORMAT_QOA 1 +* #define SUPPORT_FILEFORMAT_FLAC 0 +* #define SUPPORT_FILEFORMAT_XM 1 +* #define SUPPORT_FILEFORMAT_MOD 1 * Selected desired fileformats to be supported for loading. Some of those formats are * supported by default, to remove support, #define as 0 in this module or your build system * diff --git a/src/rcore.c b/src/rcore.c index 46d964961..eb48cc9ad 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -35,31 +35,31 @@ * - Memory framebuffer output, using software renderer, no OS required * * CONFIGURATION: -* #define SUPPORT_CAMERA_SYSTEM 1 +* #define SUPPORT_CAMERA_SYSTEM 1 * Camera module is included (rcamera.h) and multiple predefined cameras are available: * free, 1st/3rd person, orbital, custom * -* #define SUPPORT_GESTURES_SYSTEM 1 +* #define SUPPORT_GESTURES_SYSTEM 1 * Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag * -* #define SUPPORT_MOUSE_GESTURES 1 +* #define SUPPORT_MOUSE_GESTURES 1 * Mouse gestures are directly mapped like touches and processed by gestures system * -* #define SUPPORT_BUSY_WAIT_LOOP 1 +* #define SUPPORT_BUSY_WAIT_LOOP 1 * Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used * * #define SUPPORT_PARTIALBUSY_WAIT_LOOP 0 * Use a partial-busy wait loop, in this case frame sleeps for most of the time and runs a busy-wait-loop at the end * -* #define SUPPORT_SCREEN_CAPTURE 1 +* #define SUPPORT_SCREEN_CAPTURE 1 * Allow automatic screen capture of current screen pressing F12, defined in KeyCallback() * -* #define SUPPORT_COMPRESSION_API 1 +* #define SUPPORT_COMPRESSION_API 1 * Support CompressData() and DecompressData() functions, those functions use zlib implementation * provided by stb_image and stb_image_write libraries, so, those libraries must be enabled on textures module * for linkage * -* #define SUPPORT_AUTOMATION_EVENTS 1 +* #define SUPPORT_AUTOMATION_EVENTS 1 * Support automatic events recording and playing, useful for automated testing systems or AI based game playing * * DEPENDENCIES: diff --git a/src/rmodels.c b/src/rmodels.c index 1b39bed5c..012bf24c3 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3,19 +3,19 @@ * rmodels - Basic functions to draw 3d shapes and load and draw 3d models * * CONFIGURATION: -* #define SUPPORT_MODULE_RMODELS 1 +* #define SUPPORT_MODULE_RMODELS 1 * rmodels module is included in the build * -* #define SUPPORT_FILEFORMAT_OBJ 1 -* #define SUPPORT_FILEFORMAT_MTL 1 -* #define SUPPORT_FILEFORMAT_IQM 1 -* #define SUPPORT_FILEFORMAT_GLTF 1 -* #define SUPPORT_FILEFORMAT_GLTF_WRITE 0 -* #define SUPPORT_FILEFORMAT_VOX 1 -* #define SUPPORT_FILEFORMAT_M3D 1 +* #define SUPPORT_FILEFORMAT_OBJ 1 +* #define SUPPORT_FILEFORMAT_MTL 1 +* #define SUPPORT_FILEFORMAT_IQM 1 +* #define SUPPORT_FILEFORMAT_GLTF 1 +* #define SUPPORT_FILEFORMAT_GLTF_WRITE 0 +* #define SUPPORT_FILEFORMAT_VOX 1 +* #define SUPPORT_FILEFORMAT_M3D 1 * Selected desired fileformats to be supported for model data loading * -* #define SUPPORT_MESH_GENERATION 1 +* #define SUPPORT_MESH_GENERATION 1 * Support procedural mesh generation functions, uses external par_shapes.h library * NOTE: Some generated meshes DO NOT include generated texture coordinates * diff --git a/src/rshapes.c b/src/rshapes.c index 0ceeaa68d..cdd440cf0 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -16,10 +16,10 @@ * a single draw call and it also allows users to configure it the same way with their own fonts * * CONFIGURATION: -* #define SUPPORT_MODULE_RSHAPES 1 +* #define SUPPORT_MODULE_RSHAPES 1 * rshapes module is included in the build * -* #define SUPPORT_QUADS_DRAW_MODE 1 +* #define SUPPORT_QUADS_DRAW_MODE 1 * Use QUADS instead of TRIANGLES for drawing when possible. Lines-based shapes still use LINES * * diff --git a/src/rtext.c b/src/rtext.c index 41f943f26..7b9d5f2c8 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -3,22 +3,22 @@ * rtext - Basic functions to load fonts and draw text * * CONFIGURATION: -* #define SUPPORT_MODULE_RTEXT 1 +* #define SUPPORT_MODULE_RTEXT 1 * rtext module is included in the build * -* #define SUPPORT_FILEFORMAT_FNT 1 -* #define SUPPORT_FILEFORMAT_TTF 1 -* #define SUPPORT_FILEFORMAT_BDF 0 +* #define SUPPORT_FILEFORMAT_FNT 1 +* #define SUPPORT_FILEFORMAT_TTF 1 +* #define SUPPORT_FILEFORMAT_BDF 0 * Selected desired fileformats to be supported for loading. Some of those formats are * supported by default, to remove support, #define as 0 in this module or your build system * -* #define MAX_TEXT_BUFFER_LENGTH 1024 -* TextSplit() function static buffer max size +* #define MAX_TEXT_BUFFER_LENGTH 1024 +* Text functions using static buffer max size * -* #define MAX_TEXTSPLIT_COUNT 128 +* #define MAX_TEXTSPLIT_COUNT 128 * TextSplit() function static substrings pointers array (pointing to static buffer) * -* #define FONT_ATLAS_CORNER_REC_SIZE 3 +* #define FONT_ATLAS_CORNER_REC_SIZE 3 * On font atlas image generation [GenImageFontAtlas()], add a NxN 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()] diff --git a/src/rtextures.c b/src/rtextures.c index 8a815abef..bfe9efe36 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3,31 +3,31 @@ * rtextures - Basic functions to load and draw textures * * CONFIGURATION: -* #define SUPPORT_MODULE_RTEXTURES 1 +* #define SUPPORT_MODULE_RTEXTURES 1 * rtextures module is included in the build * -* #define SUPPORT_FILEFORMAT_BMP 1 -* #define SUPPORT_FILEFORMAT_PNG 1 -* #define SUPPORT_FILEFORMAT_TGA 0 -* #define SUPPORT_FILEFORMAT_JPG 0 -* #define SUPPORT_FILEFORMAT_GIF 1 -* #define SUPPORT_FILEFORMAT_QOI 1 -* #define SUPPORT_FILEFORMAT_PSD 0 -* #define SUPPORT_FILEFORMAT_HDR 0 -* #define SUPPORT_FILEFORMAT_PIC 0 -* #define SUPPORT_FILEFORMAT_PNM 0 -* #define SUPPORT_FILEFORMAT_DDS 1 -* #define SUPPORT_FILEFORMAT_PKM 0 -* #define SUPPORT_FILEFORMAT_KTX 0 -* #define SUPPORT_FILEFORMAT_PVR 0 -* #define SUPPORT_FILEFORMAT_ASTC 0 +* #define SUPPORT_FILEFORMAT_BMP 1 +* #define SUPPORT_FILEFORMAT_PNG 1 +* #define SUPPORT_FILEFORMAT_TGA 0 +* #define SUPPORT_FILEFORMAT_JPG 0 +* #define SUPPORT_FILEFORMAT_GIF 1 +* #define SUPPORT_FILEFORMAT_QOI 1 +* #define SUPPORT_FILEFORMAT_PSD 0 +* #define SUPPORT_FILEFORMAT_HDR 0 +* #define SUPPORT_FILEFORMAT_PIC 0 +* #define SUPPORT_FILEFORMAT_PNM 0 +* #define SUPPORT_FILEFORMAT_DDS 1 +* #define SUPPORT_FILEFORMAT_PKM 0 +* #define SUPPORT_FILEFORMAT_KTX 0 +* #define SUPPORT_FILEFORMAT_PVR 0 +* #define SUPPORT_FILEFORMAT_ASTC 0 * Selected desired fileformats to be supported for image data loading. Some of those formats are * supported by default, to remove support, #define as 0 in this module or your build system * -* #define SUPPORT_IMAGE_EXPORT 1 +* #define SUPPORT_IMAGE_EXPORT 1 * Support image export in multiple file formats * -* #define SUPPORT_IMAGE_GENERATION 1 +* #define SUPPORT_IMAGE_GENERATION 1 * Support procedural image generation functionality (gradient, spot, perlin-noise, cellular) * * DEPENDENCIES: From 86aa0950bd8c25da78fe1fc05b2a7b5f7eaa8b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nilso=20J=C3=BAnior?= <162613094+nilsojunior@users.noreply.github.com> Date: Thu, 16 Apr 2026 03:37:04 -0300 Subject: [PATCH 298/319] [raymath] Refactor `QuaternionFromAxisAngle` (#5766) Checking if lenght equals 0 inside the if statement is not necessary. --- src/raymath.h | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/raymath.h b/src/raymath.h index 3d59266c5..9398258dd 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -2505,19 +2505,14 @@ RMAPI Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle) { Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; - float axisLength = sqrtf(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z); + float length = sqrtf(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z); - if (axisLength != 0.0f) + if (length != 0.0f) { angle *= 0.5f; - float length = 0.0f; - float ilength = 0.0f; - // Vector3Normalize(axis) - length = axisLength; - if (length == 0.0f) length = 1.0f; - ilength = 1.0f/length; + float ilength = 1.0f/length; axis.x *= ilength; axis.y *= ilength; axis.z *= ilength; From 49f88dc6ed674a3432a74fe52d98ff2c60886537 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 16 Apr 2026 11:12:50 +0200 Subject: [PATCH 299/319] Update config.h --- src/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.h b/src/config.h index c50f2bcbf..97f5ba544 100644 --- a/src/config.h +++ b/src/config.h @@ -252,7 +252,7 @@ #ifndef SUPPORT_FILEFORMAT_PIC #define SUPPORT_FILEFORMAT_PIC 0 // Disabled by default #endif -#ifdef SUPPORT_FILEFORMAT_PNM +#ifndef SUPPORT_FILEFORMAT_PNM #define SUPPORT_FILEFORMAT_PNM 0 // Disabled by default #endif #ifndef SUPPORT_FILEFORMAT_KTX From 6ef36c0a1733a04ed38990e7094a5e710ce1164b Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Thu, 16 Apr 2026 06:01:54 -0500 Subject: [PATCH 300/319] fix examples makefile define (#5767) --- examples/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Makefile b/examples/Makefile index 6b15d22ff..334f59694 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -510,7 +510,7 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_WIN32) # Libraries for Windows desktop compilation LDFLAGS += -L..\src LDLIBS = -lraylib -lgdi32 -lwinmm -lshcore - ifneq ($(GRAPHICS),GRAPHICS_API_OPENGL_11_SOFTWARE) + ifneq ($(GRAPHICS),GRAPHICS_API_OPENGL_SOFTWARE) LDLIBS += -lopengl32 endif endif From be768e27f9a599b1dc6463a1867606a526985ef2 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Fri, 17 Apr 2026 10:55:04 -0500 Subject: [PATCH 301/319] fix issue with gettime (#5772) --- src/platforms/rcore_desktop_rgfw.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 6b021ce66..fc357e364 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -1131,7 +1131,9 @@ void SwapScreenBuffer(void) // Get elapsed time measure in seconds since InitTimer() double GetTime(void) { - double time = get_time_seconds() - CORE.Time.base; + // CORE.Time.base is nanoseconds as integer + double baseTime = (double)CORE.Time.base / 1e9; + double time = get_time_seconds() - baseTime; return time; } @@ -1654,7 +1656,6 @@ int InitPlatform(void) RGFW_setGlobalHints_OpenGL(hints); platform.window = RGFW_createWindow((CORE.Window.title != 0)? CORE.Window.title : " ", 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, flags | RGFW_windowOpenGL); - CORE.Time.base = get_time_seconds(); #ifndef PLATFORM_WEB_RGFW i32 screenSizeWidth; From 94f3c094e73112bccbb558819485d3b3d803e999 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 17 Apr 2026 17:56:47 +0200 Subject: [PATCH 302/319] Update raudio.c --- src/raudio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index d624755ad..f05567cc8 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1702,8 +1702,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, // Copy data to allocated memory for default UnloadMusicStream unsigned char *newData = (unsigned char *)RL_MALLOC(dataSize); - int it = dataSize/sizeof(unsigned char); - for (int i = 0; i < it; i++) newData[i] = data[i]; + for (int i = 0; i < dataSize; i++) newData[i] = data[i]; // Memory loaded version for jar_mod_load_file() if (dataSize && (dataSize < 32*1024*1024)) From 14fe0cbfd8c74ce179f23b3aaaf75c846e741390 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 17 Apr 2026 17:56:49 +0200 Subject: [PATCH 303/319] Update rlgl.h --- src/rlgl.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index b7854e46e..5c6e95afb 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1139,21 +1139,23 @@ typedef struct rlglData { //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- +static bool isGpuReady = false; static double rlCullDistanceNear = RL_CULL_DISTANCE_NEAR; static double rlCullDistanceFar = RL_CULL_DISTANCE_FAR; #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) static rlglData RLGL = { 0 }; -#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 -static bool isGpuReady = false; +#endif #if defined(GRAPHICS_API_OPENGL_ES2) && !defined(GRAPHICS_API_OPENGL_ES3) +// VAO functions entry points // NOTE: VAO functionality is exposed through extensions (OES) static PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays = NULL; static PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray = NULL; static PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays = NULL; -// NOTE: Instancing functionality could also be available through extension +// Instancing functionality entry points +// NOTE: Instancing functionality could be available through extensions static PFNGLDRAWARRAYSINSTANCEDEXTPROC glDrawArraysInstanced = NULL; static PFNGLDRAWELEMENTSINSTANCEDEXTPROC glDrawElementsInstanced = NULL; static PFNGLVERTEXATTRIBDIVISOREXTPROC glVertexAttribDivisor = NULL; From 7c96c24cf936235afccbebe6100ca1b0fc7db4fe Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 17 Apr 2026 17:57:05 +0200 Subject: [PATCH 304/319] Update rcore.c --- src/rcore.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index eb48cc9ad..b36d9f825 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -379,14 +379,14 @@ typedef struct CoreData { } Gamepad; } Input; struct { - double current; // Current time measure - double previous; // Previous time measure - double update; // Time measure for frame update - double draw; // Time measure for frame draw - double frame; // Time measure for one frame - double target; // Desired time for one frame, if 0 not applied - unsigned long long int base; // Base time measure for hi-res timer (PLATFORM_ANDROID, PLATFORM_DRM) - unsigned int frameCounter; // Frame counter + double current; // Current time measure (seconds) + double previous; // Previous time measure (seconds) + double update; // Time measure for frame update (seconds) + double draw; // Time measure for frame draw (seconds) + double frame; // Time measure for one frame (seconds) + double target; // Desired time for one frame, if 0 not applied (seconds) + unsigned long long int base; // Base time measure for hi-res timer (ticks or nanoseconds) + unsigned int frameCounter; // Frame counter (frames) } Time; } CoreData; From 1cbe438f9385c782116fec1e7574c1a86693fc17 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 17 Apr 2026 18:03:29 +0200 Subject: [PATCH 305/319] Update rcore_desktop_rgfw.c --- src/platforms/rcore_desktop_rgfw.c | 36 ++++++++++++++++-------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index fc357e364..a1ba74f52 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -1132,7 +1132,7 @@ void SwapScreenBuffer(void) double GetTime(void) { // CORE.Time.base is nanoseconds as integer - double baseTime = (double)CORE.Time.base / 1e9; + double baseTime = (double)CORE.Time.base*1e-9; double time = get_time_seconds() - baseTime; return time; @@ -1793,34 +1793,36 @@ double get_time_seconds(void) #if defined(_WIN32) static LARGE_INTEGER freq = { 0 }; - static int freq_init = 0; - LARGE_INTEGER counter; - if (!freq_init) { + static bool freqInitialized = false; + LARGE_INTEGER counter = { 0 }; + if (!freqInitialized) + { + // Lazy initialization QueryPerformanceFrequency(&freq); - freq_init = 1; + freqInitialized = true; } QueryPerformanceCounter(&counter); - currentTime = (double)counter.QuadPart / (double)freq.QuadPart; + currentTime = (double)counter.QuadPart/(double)freq.QuadPart; #elif defined(__EMSCRIPTEN__) - currentTime = emscripten_get_now() / 1000.0; + currentTime = emscripten_get_now()/1000.0; #elif defined(__APPLE__) - static mach_timebase_info_data_t tb; - static int tb_initialized = 0; - - if (!tb_initialized) { + static mach_timebase_info_data_t tb = { 0 }; + static bool tbInitialized = false; + if (!tbInitialized) + { mach_timebase_info(&tb); - tb_initialized = 1; + tbInitialized = true; } uint64_t ticks = mach_absolute_time(); - currentTime = (double)ticks * (double)tb.numer / (double)tb.denom / 1e9; + currentTime = (double)ticks*(double)tb.numer/(double)tb.denom/1e9; #elif defined(__linux__) - struct timespec ts; + struct timespec ts = { 0 }; clock_gettime(CLOCK_MONOTONIC, &ts); - currentTime = (double)ts.tv_sec + (double)ts.tv_nsec / 1e9; + currentTime = (double)ts.tv_sec + (double)ts.tv_nsec/1e9; #else - // fallback to cstd - currentTime = (double)clock() / (double)CLOCKS_PER_SEC; + // Fallback to cstd + currentTime = (double)clock()/(double)CLOCKS_PER_SEC; #endif return currentTime; From dde8c8e07e1936a2879fd9b68517340697296375 Mon Sep 17 00:00:00 2001 From: areynaldo <49327220+areynaldo@users.noreply.github.com> Date: Fri, 17 Apr 2026 18:04:08 +0200 Subject: [PATCH 306/319] [build-windows.bat] Update build-windows script (#5769) * Update build-windows script * Use vswhere in build-windows script --- projects/scripts/build-windows.bat | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/projects/scripts/build-windows.bat b/projects/scripts/build-windows.bat index 2c085f8d6..311da23f7 100644 --- a/projects/scripts/build-windows.bat +++ b/projects/scripts/build-windows.bat @@ -26,13 +26,13 @@ REM Checks if cl is available and skips to the argument loop if it is REM (Prevents calling vcvarsall every time you run this script) WHERE cl >nul 2>nul IF %ERRORLEVEL% == 0 goto READ_ARGS + REM Activate the msvc build environment if cl isn't available yet -IF EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" ( - set VC_INIT="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" -) ELSE IF EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" ( - set VC_INIT="C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" -) ELSE IF EXIST "C:\Program Files (x86)\Microsoft Visual C++ Build Tools\vcbuildtools.bat" ( - set VC_INIT="C:\Program Files (x86)\Microsoft Visual C++ Build Tools\vcbuildtools.bat" +for /f "tokens=*" %%i in ( + '"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath 2^>nul' +) do set VS_PATH=%%i +IF defined VS_PATH ( + set VC_INIT="%VS_PATH%\VC\Auxiliary\Build\vcvarsall.bat" ) ELSE ( REM Initialize your vc environment here if the defaults don't work REM set VC_INIT="C:\your\path\here\vcvarsall.bat" @@ -167,7 +167,7 @@ IF NOT EXIST !TEMP_DIR!\ ( cd !TEMP_DIR! REM raylib source folder set "RAYLIB_DEFINES=/D_DEFAULT_SOURCE /DPLATFORM_DESKTOP /DGRAPHICS_API_OPENGL_33" - set RAYLIB_C_FILES="!RAYLIB_SRC!\rcore.c" "!RAYLIB_SRC!\rshapes.c" "!RAYLIB_SRC!\rtextures.c" "!RAYLIB_SRC!\rtext.c" "!RAYLIB_SRC!\rmodels.c" "!RAYLIB_SRC!\utils.c" "!RAYLIB_SRC!\raudio.c" "!RAYLIB_SRC!\rglfw.c" + set RAYLIB_C_FILES="!RAYLIB_SRC!\rcore.c" "!RAYLIB_SRC!\rshapes.c" "!RAYLIB_SRC!\rtextures.c" "!RAYLIB_SRC!\rtext.c" "!RAYLIB_SRC!\rmodels.c" "!RAYLIB_SRC!\raudio.c" "!RAYLIB_SRC!\rglfw.c" set RAYLIB_INCLUDE_FLAGS=/I"!RAYLIB_SRC!" /I"!RAYLIB_SRC!\external\glfw\include" IF DEFINED REALLY_QUIET ( From f0a043bb75c54974cba1642df0d24333c4315d6f Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Fri, 17 Apr 2026 11:09:57 -0500 Subject: [PATCH 307/319] [Platform/RGFW] add support for software rendering (#5773) * add support for software rendering * null check on freeing * add back line * rename framebuffer to surface * add guard on free * update makefile to prevent software on web * update for mac --- src/Makefile | 11 +- src/platforms/rcore_desktop_rgfw.c | 185 +++++++++++++++++++++++++++-- 2 files changed, 186 insertions(+), 10 deletions(-) diff --git a/src/Makefile b/src/Makefile index 997165b4a..13f132707 100644 --- a/src/Makefile +++ b/src/Makefile @@ -275,11 +275,20 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) GRAPHICS ?= GRAPHICS_API_OPENGL_ES2 #GRAPHICS = GRAPHICS_API_OPENGL_SOFTWARE # Uncomment to use software rendering endif -ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) # On HTML5 OpenGL ES 2.0 is used, emscripten translates it to WebGL 1.0 GRAPHICS ?= GRAPHICS_API_OPENGL_ES2 #GRAPHICS = GRAPHICS_API_OPENGL_ES3 endif +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB_RGFW) + # On HTML5 OpenGL ES 2.0 is used, emscripten translates it to WebGL 1.0 + GRAPHICS ?= GRAPHICS_API_OPENGL_ES2 + #GRAPHICS = GRAPHICS_API_OPENGL_ES3 + + ifeq ($(GRAPHICS),GRAPHICS_API_OPENGL_SOFTWARE) + $(error WEB_RGFW: Software rendering not supported!) + endif +endif ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) # By default use OpenGL ES 2.0 on Android GRAPHICS ?= GRAPHICS_API_OPENGL_ES2 diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index a1ba74f52..1a4831bf1 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -177,6 +177,13 @@ typedef struct { RGFW_window *window; // Native display device (physical screen connection) RGFW_monitor *monitor; mg_gamepads minigamepad; + + #if defined(GRAPHICS_API_OPENGL_SOFTWARE) + RGFW_surface *surface; + u8 *surfacePixels; + i32 surfaceWidth; + i32 surfaceHeight; + #endif } PlatformData; //---------------------------------------------------------------------------------- @@ -1121,7 +1128,35 @@ void DisableCursor(void) // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { - RGFW_window_swapBuffers_OpenGL(platform.window); + #if defined(GRAPHICS_API_OPENGL_SOFTWARE) + { + if (platform.surface) + { + // copy rlsw pixel data to the surface framebuffer + swReadPixels(0, 0, platform.surfaceWidth, platform.surfaceHeight, SW_RGBA, SW_UNSIGNED_BYTE, platform.surfacePixels); + + // Mac wants a different pixel order. I cant seem to get this to work any other way + #if defined(__APPLE__) + unsigned char temp = 0; + unsigned char *p = NULL; + for (int i = 0; i < (platform.surfaceWidth * platform.surfaceHeight); i += 1) + { + p = platform.surfacePixels + (i * 4); + temp = p[0]; + p[0] = p[2]; + p[2] = temp; + } + #endif + + // blit surface to the window + RGFW_window_blitSurface(platform.window, platform.surface); + } + } + #else + { + RGFW_window_swapBuffers_OpenGL(platform.window); + } + #endif } //---------------------------------------------------------------------------------- @@ -1315,6 +1350,9 @@ void PollInputEvents(void) // Window events are also polled (Minimized, maximized, close...) case RGFW_windowResized: { + // set flag that the window was resized + CORE.Window.resizedLastFrame = true; + #if defined(__APPLE__) if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) { @@ -1363,7 +1401,42 @@ void PollInputEvents(void) CORE.Window.currentFbo.width = CORE.Window.screen.width; CORE.Window.currentFbo.height = CORE.Window.screen.height; #endif - CORE.Window.resizedLastFrame = true; + + #if defined(GRAPHICS_API_OPENGL_SOFTWARE) + #if defined(__APPLE__) + RGFW_monitor* currentMonitor = RGFW_window_getMonitor(platform.window); + CORE.Window.screenScale = MatrixScale(currentMonitor->pixelRatio, currentMonitor->pixelRatio, 1.0f); + SetupViewport(platform.window->w * currentMonitor->pixelRatio, platform.window->h * currentMonitor->pixelRatio); + + CORE.Window.render.width = CORE.Window.screen.width * currentMonitor->pixelRatio; + CORE.Window.render.height = CORE.Window.screen.height * currentMonitor->pixelRatio; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + #endif + platform.surfaceWidth = CORE.Window.currentFbo.width; + platform.surfaceHeight = CORE.Window.currentFbo.height; + + // in software mode we dont have the viewport so we need to reverse the highdpi changes + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) + { + Vector2 scaleDpi = GetWindowScaleDPI(); + platform.surfaceWidth *= scaleDpi.x; + platform.surfaceHeight *= scaleDpi.y; + } + + if (platform.surfacePixels != NULL) + { + RL_FREE(platform.surfacePixels); + platform.surfacePixels = RL_MALLOC(platform.surfaceWidth * platform.surfaceHeight * 4); + } + + if (platform.surface != NULL) + { + RGFW_surface_free(platform.surface); + platform.surface = RGFW_window_createSurface(platform.window, platform.surfacePixels, platform.surfaceWidth, platform.surfaceHeight, RGFW_formatBGRA8); + swResize(platform.surfaceWidth, platform.surfaceHeight); + } + #endif } break; case RGFW_windowMaximized: { @@ -1641,6 +1714,12 @@ int InitPlatform(void) hints->major = 4; hints->minor = 3; } + else if (rlGetVersion() == RL_OPENGL_SOFTWARE) + { + hints->major = 1; + hints->minor = 1; + hints->renderer = RGFW_glSoftware; + } if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) hints->samples = 4; @@ -1720,6 +1799,39 @@ int InitPlatform(void) #endif } + #if defined(GRAPHICS_API_OPENGL_SOFTWARE) + // apple always scales for retina + #if defined(__APPLE__) + RGFW_monitor* currentMonitor = RGFW_window_getMonitor(platform.window); + CORE.Window.screenScale = MatrixScale(currentMonitor->pixelRatio, currentMonitor->pixelRatio, 1.0f); + + CORE.Window.render.width = CORE.Window.screen.width * currentMonitor->pixelRatio; + CORE.Window.render.height = CORE.Window.screen.height * currentMonitor->pixelRatio; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + #endif + + platform.surfaceWidth = CORE.Window.currentFbo.width; + platform.surfaceHeight = CORE.Window.currentFbo.height; + + platform.surfacePixels = RL_MALLOC(platform.surfaceWidth * platform.surfaceHeight * 4); + if (platform.surfacePixels == NULL) + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize software pixel buffer"); + return -1; + } + + platform.surface = RGFW_window_createSurface(platform.window, platform.surfacePixels, platform.surfaceWidth, platform.surfaceHeight, RGFW_formatBGRA8); + + if (platform.surface == NULL) + { + RL_FREE(platform.surfacePixels); + + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize software surface"); + return -1; + } + #endif + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully %s", FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)? "(HighDPI)" : ""); TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); @@ -1750,20 +1862,63 @@ int InitPlatform(void) //---------------------------------------------------------------------------- #if defined(RGFW_WAYLAND) - if (RGFW_usingWayland()) TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Wayland): Initialized successfully"); - else TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11 (fallback)): Initialized successfully"); + if (rlGetVersion() == RL_OPENGL_SOFTWARE) + { + if (RGFW_usingWayland()) TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Wayland, Software): Initialized successfully"); + else TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11, Software (fallback)): Initialized successfully"); + } + else + { + if (RGFW_usingWayland()) TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Wayland): Initialized successfully"); + else TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11 (fallback)): Initialized successfully"); + } #elif defined(RGFW_X11) #if defined(__APPLE__) - TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11 (MacOS)): Initialized successfully"); + if (rlGetVersion() == RL_OPENGL_SOFTWARE) + { + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11, Software, (MacOS)): Initialized successfully"); + } + else + { + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11, (MacOS)): Initialized successfully"); + } #else - TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11): Initialized successfully"); + if (rlGetVersion() == RL_OPENGL_SOFTWARE) + { + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11, Software): Initialized successfully"); + } + else + { + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11): Initialized successfully"); + } #endif #elif defined (RGFW_WINDOWS) - TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Win32): Initialized successfully"); + if (rlGetVersion() == RL_OPENGL_SOFTWARE) + { + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Win32, Software): Initialized successfully"); + } + else + { + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Win32): Initialized successfully"); + } #elif defined(RGFW_WASM) - TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - WASMs): Initialized successfully"); + if (rlGetVersion() == RL_OPENGL_SOFTWARE) + { + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - WASMs, Software): Initialized successfully"); + } + else + { + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - WASMs): Initialized successfully"); + } #elif defined(RGFW_MACOS) - TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - MacOS): Initialized successfully"); + if (rlGetVersion() == RL_OPENGL_SOFTWARE) + { + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - MacOS, Software): Initialized successfully"); + } + else + { + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - MacOS): Initialized successfully"); + } #endif mg_gamepads_init(&platform.minigamepad); @@ -1776,6 +1931,18 @@ void ClosePlatform(void) { mg_gamepads_free(&platform.minigamepad); RGFW_window_close(platform.window); + + #if defined(GRAPHICS_API_OPENGL_SOFTWARE) + if (platform.surfacePixels != NULL) + { + RL_FREE(platform.surfacePixels); + } + + if (platform.surface != NULL) + { + RGFW_surface_free(platform.surface); + } + #endif } // Keycode mapping From 9a3283f698881963c3f8e589353ddcb4599ab5d7 Mon Sep 17 00:00:00 2001 From: Ziya Date: Sat, 18 Apr 2026 20:40:07 +0400 Subject: [PATCH 308/319] [examples] `shapes_collision_ellipses` (#5722) * Added shapes_collision_ellipses example * Address review comments from raysan * Address review comments * update collision ellipses screenshot * Restore shapes_following_eyes.c to upstream * Rename shapes_collision_ellipses to shapes_ellipse_collision and fix header --- examples/shapes/shapes_ellipse_collision.c | 133 +++++++++++++++++++ examples/shapes/shapes_ellipse_collision.png | Bin 0 -> 16967 bytes 2 files changed, 133 insertions(+) create mode 100644 examples/shapes/shapes_ellipse_collision.c create mode 100644 examples/shapes/shapes_ellipse_collision.png diff --git a/examples/shapes/shapes_ellipse_collision.c b/examples/shapes/shapes_ellipse_collision.c new file mode 100644 index 000000000..4a6d7e911 --- /dev/null +++ b/examples/shapes/shapes_ellipse_collision.c @@ -0,0 +1,133 @@ +/******************************************************************************************* +* +* raylib [shapes] example - ellipse collision +* +* Example complexity rating: [★★☆☆] 2/4 +* +* Example originally created with raylib 5.5, last time updated with raylib 5.5 +* +* Example contributed by Ziya (@Monjaris) +* +* 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 Ziya (@Monjaris) +* +********************************************************************************************/ + +#include "raylib.h" +#include + +// Check if point is inside ellipse +static bool CheckCollisionPointEllipse(Vector2 point, Vector2 center, float rx, float ry) +{ + float dx = (point.x - center.x)/rx; + float dy = (point.y - center.y)/ry; + return (dx*dx + dy*dy) <= 1.0f; +} + +// Check if two ellipses collide +// Uses radial boundary distance in the direction between centers — scales correctly with radii +static bool CheckCollisionEllipses(Vector2 c1, float rx1, float ry1, Vector2 c2, float rx2, float ry2) +{ + float dx = c2.x - c1.x; + float dy = c2.y - c1.y; + float dist = sqrtf(dx*dx + dy*dy); + + // Ellipses are on top of each other + if (dist == 0.0f) return true; + + float theta = atan2f(dy, dx); + float cosT = cosf(theta); + float sinT = sinf(theta); + + // Radial distance from center to ellipse boundary in direction theta + // r(theta) = (rx * ry) / sqrt((ry*cos)^2 + (rx*sin)^2) + float r1 = (rx1*ry1)/sqrtf((ry1*cosT)*(ry1*cosT) + (rx1*sinT)*(rx1*sinT)); + float r2 = (rx2*ry2)/sqrtf((ry2*cosT)*(ry2*cosT) + (rx2*sinT)*(rx2*sinT)); + + return dist <= (r1 + r2); +} + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - collision ellipses"); + SetTargetFPS(60); + + Vector2 ellipseACenter = { (float)screenWidth/4, (float)screenHeight/2 }; + float ellipseARx = 120.0f; + float ellipseARy = 70.0f; + + Vector2 ellipseBCenter = { (float)screenWidth*3/4, (float)screenHeight/2 }; + float ellipseBRx = 90.0f; + float ellipseBRy = 140.0f; + + // 0 = controlling A, 1 = controlling B + int controlled = 0; + + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KEY_A)) controlled = 0; + if (IsKeyPressed(KEY_B)) controlled = 1; + + if (controlled == 0) ellipseACenter = GetMousePosition(); + else ellipseBCenter = GetMousePosition(); + + bool ellipsesCollide = CheckCollisionEllipses( + ellipseACenter, ellipseARx, ellipseARy, + ellipseBCenter, ellipseBRx, ellipseBRy + ); + + bool mouseInA = CheckCollisionPointEllipse(GetMousePosition(), ellipseACenter, ellipseARx, ellipseARy); + bool mouseInB = CheckCollisionPointEllipse(GetMousePosition(), ellipseBCenter, ellipseBRx, ellipseBRy); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawEllipse((int)ellipseACenter.x, (int)ellipseACenter.y, ellipseARx, ellipseARy, ellipsesCollide ? RED : BLUE); + + DrawEllipse((int)ellipseBCenter.x, (int)ellipseBCenter.y, ellipseBRx, ellipseBRy, ellipsesCollide ? RED : GREEN); + + DrawEllipseLines((int)ellipseACenter.x, (int)ellipseACenter.y, ellipseARx, ellipseARy, WHITE); + + DrawEllipseLines((int)ellipseBCenter.x, (int)ellipseBCenter.y, ellipseBRx, ellipseBRy, WHITE); + + DrawCircleV(ellipseACenter, 4, WHITE); + DrawCircleV(ellipseBCenter, 4, WHITE); + + if (ellipsesCollide) DrawText("ELLIPSES COLLIDE", screenWidth/2 - 120, 40, 28, RED); + else DrawText("NO COLLISION", screenWidth/2 - 80, 40, 28, DARKGRAY); + + DrawText(controlled == 0 ? "Controlling: A" : "Controlling: B", 20, screenHeight - 40, 20, YELLOW); + + if (mouseInA && controlled != 0) DrawText("Mouse inside ellipse A", 20, screenHeight - 70, 20, BLUE); + if (mouseInB && controlled != 1) DrawText("Mouse inside ellipse B", 20, screenHeight - 70, 20, GREEN); + + DrawText("Press [A] or [B] to switch control", 20, 20, 20, GRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + CloseWindow(); + + return 0; +} diff --git a/examples/shapes/shapes_ellipse_collision.png b/examples/shapes/shapes_ellipse_collision.png new file mode 100644 index 0000000000000000000000000000000000000000..30c66c164a3ad2ef0e4f17d7bd2d9b6a4ec638e0 GIT binary patch literal 16967 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 yO?^; Date: Sat, 18 Apr 2026 18:53:03 +0200 Subject: [PATCH 309/319] Update rcore_drm.c --- 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 708a78004..98c35f7e8 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -1426,7 +1426,7 @@ int InitPlatform(void) if (eglGetPlatformDisplayEXT != NULL) platform.device = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, platform.gbmDevice, NULL); } - // In case extension not found or display could not be retrieved, try useing legacy version + // In case extension not found or display could not be retrieved, try using legacy version if (platform.device == EGL_NO_DISPLAY) platform.device = eglGetDisplay((EGLNativeDisplayType)platform.gbmDevice); #endif if (platform.device == EGL_NO_DISPLAY) From 9060ac7c95c371bdae7fbfafd2b54dd485c32832 Mon Sep 17 00:00:00 2001 From: Itwerntme <72857681+Itwernme@users.noreply.github.com> Date: Sun, 19 Apr 2026 12:50:15 +0100 Subject: [PATCH 310/319] Mouse delta calculation with scaling (#5779) Added scaling from SetMouseScale to mouse GetMouseDelta --- src/rcore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index b36d9f825..d99919b62 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -4125,8 +4125,8 @@ Vector2 GetMouseDelta(void) { Vector2 delta = { 0 }; - delta.x = CORE.Input.Mouse.currentPosition.x - CORE.Input.Mouse.previousPosition.x; - delta.y = CORE.Input.Mouse.currentPosition.y - CORE.Input.Mouse.previousPosition.y; + delta.x = (CORE.Input.Mouse.currentPosition.x - CORE.Input.Mouse.previousPosition.x)*CORE.Input.Mouse.scale.x; + delta.y = (CORE.Input.Mouse.currentPosition.y - CORE.Input.Mouse.previousPosition.y)*CORE.Input.Mouse.scale.y; return delta; } From 90e9145978d9dfffae19e60d819080d171b16450 Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Sun, 19 Apr 2026 12:51:52 +0100 Subject: [PATCH 311/319] Making it similar to CheckCollisionSpheres to remove pow function (#5776) --- src/rmodels.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 012bf24c3..62b8a51e5 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -4131,18 +4131,15 @@ bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius) { bool collision = false; - float dmin = 0; + Vector3 closestPoint = { + Clamp(center.x, box.min.x, box.max.x), + Clamp(center.y, box.min.y, box.max.y), + Clamp(center.z, box.min.z, box.max.z) + }; - if (center.x < box.min.x) dmin += powf(center.x - box.min.x, 2); - else if (center.x > box.max.x) dmin += powf(center.x - box.max.x, 2); + float distanceSquared = Vector3DistanceSqr(center, closestPoint); - if (center.y < box.min.y) dmin += powf(center.y - box.min.y, 2); - else if (center.y > box.max.y) dmin += powf(center.y - box.max.y, 2); - - if (center.z < box.min.z) dmin += powf(center.z - box.min.z, 2); - else if (center.z > box.max.z) dmin += powf(center.z - box.max.z, 2); - - if (dmin <= (radius*radius)) collision = true; + collision = distanceSquared <= (radius * radius); return collision; } From cc752037b9bcf7bebc141830f3f94bea16e244b9 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 19 Apr 2026 13:54:39 +0200 Subject: [PATCH 312/319] Update rmodels.c --- src/rmodels.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 62b8a51e5..6d3169ec0 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -4105,7 +4105,7 @@ bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, floa // Check for distances squared to avoid sqrtf() float radSum = radius1 + radius2; - if (Vector3DistanceSqr(center1, center2) <= radSum*radSum) collision = true; + if (Vector3DistanceSqr(center1, center2) <= (radSum*radSum)) collision = true; return collision; } @@ -4137,9 +4137,7 @@ bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius) Clamp(center.z, box.min.z, box.max.z) }; - float distanceSquared = Vector3DistanceSqr(center, closestPoint); - - collision = distanceSquared <= (radius * radius); + if (Vector3DistanceSqr(center, closestPoint) <= (radius*radius)) collision = true; return collision; } From a32b53f4d633d54fcc0a3ba825cdfc850af83f99 Mon Sep 17 00:00:00 2001 From: HaxSam Date: Sun, 19 Apr 2026 13:57:58 +0200 Subject: [PATCH 313/319] [build.zig] Refactor (#5764) * Add better structure for build.zig Temporarily disabled the tests * Cleanup build.zig a bit * Fixed zemscripten and cleanup other platforms * Make opengl_version selection more restritive * Add traslateC to build; Renable examples * Add raygui build to build.zig * Deny glfw from android target * Fix android platform includes * Add Zig project example * Add Zig project mention to README * Set right name for web build in zig example * Cleanup last parts of build.zig * Add linking method for build.zig * Fix lshcore link for glfw and rgfw build.zig * Fix weird sdl linkage build.zig * Add zig example to zig project * Fix win32, mac build bugs in build.zig * Rename argument lshcore to shcore build.zig --- build.zig | 860 +++++++++++++++---------- build.zig.zon | 5 + projects/README.md | 1 + projects/Zig/README.md | 84 +++ projects/Zig/build.zig | 87 +++ projects/Zig/build.zig.zon | 23 + projects/Zig/src/core_basic_window.c | 83 +++ projects/Zig/src/core_basic_window.zig | 73 +++ 8 files changed, 858 insertions(+), 358 deletions(-) create mode 100644 projects/Zig/README.md create mode 100644 projects/Zig/build.zig create mode 100644 projects/Zig/build.zig.zon create mode 100644 projects/Zig/src/core_basic_window.c create mode 100644 projects/Zig/src/core_basic_window.zig diff --git a/build.zig b/build.zig index 2dc6258af..eb1eb5cc6 100644 --- a/build.zig +++ b/build.zig @@ -1,14 +1,11 @@ const std = @import("std"); const builtin = @import("builtin"); -const emccOutputDir = "zig-out" ++ std.fs.path.sep_str ++ "htmlout" ++ std.fs.path.sep_str; -const emccOutputFile = "index.html"; - pub const emsdk = struct { const zemscripten = @import("zemscripten"); - pub fn shell(b: *std.Build) std.Build.LazyPath { - return b.dependency("raylib", .{}).path("src/shell.html"); + pub fn shell(raylib_dep: *std.Build.Dependency) std.Build.LazyPath { + return raylib_dep.path("src/shell.html"); } pub const FlagsOptions = struct { @@ -30,7 +27,10 @@ pub const emsdk = struct { pub const SettingsOptions = struct { optimize: std.builtin.OptimizeMode, - es3: bool = true, + es3: bool = false, + glfw3: bool = true, + memory_growth: bool = false, + total_memory: u32 = 134217728, emsdk_allocator: zemscripten.EmsdkAllocator = .emmalloc, }; @@ -40,10 +40,24 @@ pub const emsdk = struct { .emsdk_allocator = options.emsdk_allocator, }); - if (options.es3) + if (options.es3) { emcc_settings.put("FULL_ES3", "1") catch unreachable; - emcc_settings.put("USE_GLFW", "3") catch unreachable; + emcc_settings.put("MIN_WEBGL_VERSION", "2") catch unreachable; + emcc_settings.put("MAX_WEBGL_VERSION", "2") catch unreachable; + } + if (options.glfw3) { + emcc_settings.put("USE_GLFW", "3") catch unreachable; + } + + const total_memory = std.fmt.allocPrint(allocator, "{d}", .{options.total_memory}) catch unreachable; + emcc_settings.put("EXPORTED_RUNTIME_METHODS", "['requestFullscreen']") catch unreachable; + emcc_settings.put("TOTAL_MEMORY", total_memory) catch unreachable; + emcc_settings.put("FORCE_FILESYSTEM", "1") catch unreachable; + emcc_settings.put("EXPORTED_RUNTIME_METHODS", "ccall") catch unreachable; + + if (options.memory_growth) + emcc_settings.put("ALLOW_MEMORY_GROWTH", "1") catch unreachable; return emcc_settings; } @@ -70,45 +84,96 @@ pub const emsdk = struct { } }; -fn setDesktopPlatform(raylib: *std.Build.Step.Compile, platform: PlatformBackend) void { - switch (platform) { - .glfw => raylib.root_module.addCMacro("PLATFORM_DESKTOP_GLFW", ""), - .rgfw => raylib.root_module.addCMacro("PLATFORM_DESKTOP_RGFW", ""), - .sdl => raylib.root_module.addCMacro("PLATFORM_DESKTOP_SDL", ""), - .android => raylib.root_module.addCMacro("PLATFORM_ANDROID", ""), - else => {}, +pub fn linkWindows(mod: *std.Build.Module, opengl: bool, comptime shcore: bool) void { + if (opengl) mod.linkSystemLibrary("opengl32", .{}); + mod.linkSystemLibrary("winmm", .{}); + mod.linkSystemLibrary("gdi32", .{}); + if (shcore) mod.linkSystemLibrary("shcore", .{}); +} + +fn findWaylandScanner(b: *std.Build) void { + _ = b.findProgram(&.{"wayland-scanner"}, &.{}) catch { + std.log.err( + \\ `wayland-scanner` may not be installed on the system. + \\ You can switch to X11 in your `build.zig` by changing `Options.linux_display_backend` + , .{}); + @panic("`wayland-scanner` not found"); + }; +} + +pub fn linkLinux(mod: *std.Build.Module, comptime display_backend: LinuxDisplayBackend) void { + if (display_backend == .None) { + mod.linkSystemLibrary("GL", .{}); + } + + if (display_backend == .X11) { + mod.linkSystemLibrary("X11", .{}); + mod.linkSystemLibrary("Xrandr", .{}); + mod.linkSystemLibrary("Xinerama", .{}); + mod.linkSystemLibrary("Xi", .{}); + mod.linkSystemLibrary("Xcursor", .{}); + } + + if (display_backend == .Wayland) { + mod.linkSystemLibrary("wayland-client", .{}); + mod.linkSystemLibrary("wayland-cursor", .{}); + mod.linkSystemLibrary("wayland-egl", .{}); + mod.linkSystemLibrary("xkbcommon", .{}); } } +pub fn linkBSD(_: *std.Build, mod: *std.Build.Module) void { + mod.linkSystemLibrary("GL", .{}); +} + +pub fn linkMacOS(b: *std.Build, mod: *std.Build.Module) void { + // Include xcode_frameworks for cross compilation + if (b.lazyDependency("xcode_frameworks", .{})) |dep| { + mod.addSystemFrameworkPath(dep.path("Frameworks")); + mod.addSystemIncludePath(dep.path("include")); + mod.addLibraryPath(dep.path("lib")); + } + + mod.linkFramework("Foundation", .{}); + mod.linkFramework("CoreServices", .{}); + mod.linkFramework("CoreGraphics", .{}); + mod.linkFramework("AppKit", .{}); + mod.linkFramework("IOKit", .{}); +} + fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, options: Options) !*std.Build.Step.Compile { + const raylib_mod = b.createModule(.{ + .optimize = optimize, + .target = target, + .link_libc = true, + }); + const raylib = b.addLibrary(.{ .name = "raylib", .linkage = options.linkage, - .root_module = b.createModule(.{ - .optimize = optimize, - .target = target, - .link_libc = true, - }), + .root_module = raylib_mod, }); - raylib.root_module.addCMacro("_GNU_SOURCE", ""); - raylib.root_module.addCMacro("GL_SILENCE_DEPRECATION", "199309L"); + raylib_mod.addCMacro("_GNU_SOURCE", ""); + raylib_mod.addCMacro("GL_SILENCE_DEPRECATION", "199309L"); - var raylib_flags_arr: std.ArrayList([]const u8) = .empty; - defer raylib_flags_arr.deinit(b.allocator); + var arena: std.heap.ArenaAllocator = .init(b.allocator); + defer arena.deinit(); - try raylib_flags_arr.append( - b.allocator, - "-std=gnu99", - ); + var raylib_flags_arr: std.array_list.Managed([]const u8) = .init(arena.allocator()); + var c_source_files: std.array_list.Managed([]const u8) = .init(arena.allocator()); + + try c_source_files.append("src/rcore.c"); + + if (target.result.os.tag == .emscripten) { + try raylib_flags_arr.append("-std=gnu99"); + } else { + try raylib_flags_arr.append("-std=c99"); + } if (options.linkage == .dynamic) { - try raylib_flags_arr.append( - b.allocator, - "-fPIC", - ); - - raylib.root_module.addCMacro("BUILD_LIBTYPE_SHARED", ""); + raylib_mod.pic = true; + raylib_mod.addCMacro("BUILD_LIBTYPE_SHARED", ""); } if (options.config.len > 0) { @@ -120,237 +185,290 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. // Apply config flags supplied by the user while (config_iter.next()) |config_flag| { - try raylib_flags_arr.append(b.allocator, config_flag); + try raylib_flags_arr.append(config_flag); } } - // No GLFW required on PLATFORM_DRM - if (options.platform != .drm) { - raylib.root_module.addIncludePath(b.path("src/external/glfw/include")); - } - - var c_source_files: std.ArrayList([]const u8) = try .initCapacity(b.allocator, 2); - c_source_files.appendSliceAssumeCapacity(&.{"src/rcore.c"}); - + raylib_mod.addCMacro("SUPPORT_MODULE_RSHAPES", &.{@as(u8, @intFromBool(options.rshapes)) + 0x30}); if (options.rshapes) { - raylib.root_module.addCMacro("SUPPORT_MODULE_RSHAPES", "1"); - try c_source_files.append(b.allocator, "src/rshapes.c"); - } else { - raylib.root_module.addCMacro("SUPPORT_MODULE_RSHAPES", "0"); + try c_source_files.append("src/rshapes.c"); } - + raylib_mod.addCMacro("SUPPORT_MODULE_RTEXTURES", &.{@as(u8, @intFromBool(options.rtextures)) + 0x30}); if (options.rtextures) { - raylib.root_module.addCMacro("SUPPORT_MODULE_RTEXTURES", "1"); - try c_source_files.append(b.allocator, "src/rtextures.c"); - } else { - raylib.root_module.addCMacro("SUPPORT_MODULE_RTEXTURES", "0"); + try c_source_files.append("src/rtextures.c"); } - + raylib_mod.addCMacro("SUPPORT_MODULE_RTEXT", &.{@as(u8, @intFromBool(options.rtext)) + 0x30}); if (options.rtext) { - raylib.root_module.addCMacro("SUPPORT_MODULE_RTEXT", "1"); - try c_source_files.append(b.allocator, "src/rtext.c"); - } else { - raylib.root_module.addCMacro("SUPPORT_MODULE_RTEXT", "0"); + try c_source_files.append("src/rtext.c"); } - + raylib_mod.addCMacro("SUPPORT_MODULE_RMODELS", &.{@as(u8, @intFromBool(options.rmodels)) + 0x30}); if (options.rmodels) { - raylib.root_module.addCMacro("SUPPORT_MODULE_RMODELS", "1"); - try c_source_files.append(b.allocator, "src/rmodels.c"); - } else { - raylib.root_module.addCMacro("SUPPORT_MODULE_RMODELS", "0"); + try c_source_files.append("src/rmodels.c"); } - + raylib_mod.addCMacro("SUPPORT_MODULE_RAUDIO", &.{@as(u8, @intFromBool(options.raudio)) + 0x30}); if (options.raudio) { - raylib.root_module.addCMacro("SUPPORT_MODULE_RAUDIO", "1"); - try c_source_files.append(b.allocator, "src/raudio.c"); - } else { - raylib.root_module.addCMacro("SUPPORT_MODULE_RAUDIO", "0"); + try c_source_files.append("src/raudio.c"); } - if (options.opengl_version != .auto) { - raylib.root_module.addCMacro(options.opengl_version.toCMacroStr(), ""); - } - - raylib.root_module.addIncludePath(b.path("src/platforms")); - switch (target.result.os.tag) { - .windows => { - switch (options.platform) { - .glfw => try c_source_files.append(b.allocator, "src/rglfw.c"), - .rgfw, .sdl, .drm, .android => {}, + raylib_mod.addIncludePath(b.path("src/platforms")); + switch (options.platform) { + .glfw => { + var opengl_version: OpenglVersion = options.opengl_version; + if (opengl_version == .gl_soft) { + @panic("The opengl version is not supported by this platform"); } - raylib.root_module.linkSystemLibrary("winmm", .{}); - raylib.root_module.linkSystemLibrary("gdi32", .{}); - raylib.root_module.linkSystemLibrary("opengl32", .{}); + raylib_mod.addIncludePath(b.path("src/external/glfw/include")); - setDesktopPlatform(raylib, options.platform); - }, - .linux => { - if (options.platform == .drm) { - if (options.opengl_version == .auto) { - raylib.root_module.linkSystemLibrary("GLESv2", .{}); - raylib.root_module.addCMacro("GRAPHICS_API_OPENGL_ES2", ""); + if (target.result.os.tag != .emscripten) { + if (opengl_version == .auto) { + opengl_version = OpenglVersion.gl_3_3; } - - if (options.opengl_version != .gl_soft) { - raylib.root_module.linkSystemLibrary("EGL", .{}); - raylib.root_module.linkSystemLibrary("gbm", .{}); - } - raylib.root_module.linkSystemLibrary("libdrm", .{ .use_pkg_config = .force }); - - raylib.root_module.addCMacro("PLATFORM_DRM", ""); - raylib.root_module.addCMacro("EGL_NO_X11", ""); - raylib.root_module.addCMacro("DEFAULT_BATCH_BUFFER_ELEMENT", ""); - } else if (target.result.abi.isAndroid()) { - - //these are the only tag options per https://developer.android.com/ndk/guides/other_build_systems - const hostTuple = switch (builtin.target.os.tag) { - .linux => "linux-x86_64", - .windows => "windows-x86_64", - .macos => "darwin-x86_64", - else => @panic("unsupported host OS"), - }; - - const androidTriple = switch (target.result.cpu.arch) { - .x86 => "i686-linux-android", - .x86_64 => "x86_64-linux-android", - .arm => "arm-linux-androideabi", - .aarch64 => "aarch64-linux-android", - .riscv64 => "riscv64-linux-android", - else => error.InvalidAndroidTarget, - } catch @panic("invalid android target!"); - const androidNdkPathString: []const u8 = options.android_ndk; - if (androidNdkPathString.len < 1) @panic("no ndk path provided and ANDROID_NDK_HOME is not set"); - const androidApiLevel: []const u8 = options.android_api_version; - - const androidSysroot = try std.fs.path.join(b.allocator, &.{ androidNdkPathString, "/toolchains/llvm/prebuilt/", hostTuple, "/sysroot" }); - const androidLibPath = try std.fs.path.join(b.allocator, &.{ androidSysroot, "/usr/lib/", androidTriple }); - const androidApiSpecificPath = try std.fs.path.join(b.allocator, &.{ androidLibPath, androidApiLevel }); - const androidIncludePath = try std.fs.path.join(b.allocator, &.{ androidSysroot, "/usr/include" }); - const androidArchIncludePath = try std.fs.path.join(b.allocator, &.{ androidIncludePath, androidTriple }); - const androidAsmPath = try std.fs.path.join(b.allocator, &.{ androidIncludePath, "/asm-generic" }); - const androidGluePath = try std.fs.path.join(b.allocator, &.{ androidNdkPathString, "/sources/android/native_app_glue/" }); - - raylib.root_module.addLibraryPath(.{ .cwd_relative = androidLibPath }); - raylib.root_module.addLibraryPath(.{ .cwd_relative = androidApiSpecificPath }); - raylib.root_module.addSystemIncludePath(.{ .cwd_relative = androidIncludePath }); - raylib.root_module.addSystemIncludePath(.{ .cwd_relative = androidArchIncludePath }); - raylib.root_module.addSystemIncludePath(.{ .cwd_relative = androidAsmPath }); - raylib.root_module.addSystemIncludePath(.{ .cwd_relative = androidGluePath }); - - var libcData: std.ArrayList(u8) = .empty; - var aw: std.Io.Writer.Allocating = .fromArrayList(b.allocator, &libcData); - try (std.zig.LibCInstallation{ - .include_dir = androidIncludePath, - .sys_include_dir = androidIncludePath, - .crt_dir = androidApiSpecificPath, - }).render(&aw.writer); - const libcFile = b.addWriteFiles().add("android-libc.txt", try libcData.toOwnedSlice(b.allocator)); - raylib.setLibCFile(libcFile); - - if (options.opengl_version == .auto) { - raylib.root_module.linkSystemLibrary("GLESv2", .{}); - raylib.root_module.addCMacro("GRAPHICS_API_OPENGL_ES2", ""); - } - raylib.root_module.linkSystemLibrary("EGL", .{}); - - setDesktopPlatform(raylib, .android); - } else { - switch (options.platform) { - .glfw => try c_source_files.append(b.allocator, "src/rglfw.c"), - .rgfw, .sdl, .drm, .android => {}, - } - - if (options.linux_display_backend == .X11 or options.linux_display_backend == .Both) { - raylib.root_module.addCMacro("_GLFW_X11", ""); - raylib.root_module.linkSystemLibrary("GLX", .{}); - raylib.root_module.linkSystemLibrary("X11", .{}); - raylib.root_module.linkSystemLibrary("Xcursor", .{}); - raylib.root_module.linkSystemLibrary("Xext", .{}); - raylib.root_module.linkSystemLibrary("Xfixes", .{}); - raylib.root_module.linkSystemLibrary("Xi", .{}); - raylib.root_module.linkSystemLibrary("Xinerama", .{}); - raylib.root_module.linkSystemLibrary("Xrandr", .{}); - raylib.root_module.linkSystemLibrary("Xrender", .{}); - } - - if (options.linux_display_backend == .Wayland or options.linux_display_backend == .Both) { - _ = b.findProgram(&.{"wayland-scanner"}, &.{}) catch { - std.log.err( - \\ `wayland-scanner` may not be installed on the system. - \\ You can switch to X11 in your `build.zig` by changing `Options.linux_display_backend` - , .{}); - @panic("`wayland-scanner` not found"); - }; - raylib.root_module.addCMacro("_GLFW_WAYLAND", ""); - raylib.root_module.linkSystemLibrary("EGL", .{}); - raylib.root_module.linkSystemLibrary("wayland-client", .{}); - raylib.root_module.linkSystemLibrary("xkbcommon", .{}); - waylandGenerate(b, raylib, "wayland.xml", "wayland-client-protocol"); - waylandGenerate(b, raylib, "xdg-shell.xml", "xdg-shell-client-protocol"); - waylandGenerate(b, raylib, "xdg-decoration-unstable-v1.xml", "xdg-decoration-unstable-v1-client-protocol"); - waylandGenerate(b, raylib, "viewporter.xml", "viewporter-client-protocol"); - waylandGenerate(b, raylib, "relative-pointer-unstable-v1.xml", "relative-pointer-unstable-v1-client-protocol"); - waylandGenerate(b, raylib, "pointer-constraints-unstable-v1.xml", "pointer-constraints-unstable-v1-client-protocol"); - waylandGenerate(b, raylib, "fractional-scale-v1.xml", "fractional-scale-v1-client-protocol"); - waylandGenerate(b, raylib, "xdg-activation-v1.xml", "xdg-activation-v1-client-protocol"); - waylandGenerate(b, raylib, "idle-inhibit-unstable-v1.xml", "idle-inhibit-unstable-v1-client-protocol"); - } - setDesktopPlatform(raylib, options.platform); - } - }, - .freebsd, .openbsd, .netbsd, .dragonfly => { - try c_source_files.append(b.allocator, "src/rglfw.c"); - raylib.root_module.linkSystemLibrary("GL", .{}); - raylib.root_module.linkSystemLibrary("rt", .{}); - raylib.root_module.linkSystemLibrary("dl", .{}); - raylib.root_module.linkSystemLibrary("m", .{}); - raylib.root_module.linkSystemLibrary("X11", .{}); - raylib.root_module.linkSystemLibrary("Xrandr", .{}); - raylib.root_module.linkSystemLibrary("Xinerama", .{}); - raylib.root_module.linkSystemLibrary("Xi", .{}); - raylib.root_module.linkSystemLibrary("Xxf86vm", .{}); - raylib.root_module.linkSystemLibrary("Xcursor", .{}); - - setDesktopPlatform(raylib, options.platform); - }, - .macos => { - // Include xcode_frameworks for cross compilation - if (b.lazyDependency("xcode_frameworks", .{})) |dep| { - raylib.root_module.addSystemFrameworkPath(dep.path("Frameworks")); - raylib.root_module.addSystemIncludePath(dep.path("include")); - raylib.root_module.addLibraryPath(dep.path("lib")); + raylib_mod.addCMacro("PLATFORM_DESKTOP_GLFW", ""); + try c_source_files.append("src/rglfw.c"); } - // On macos rglfw.c include Objective-C files. - try raylib_flags_arr.append(b.allocator, "-ObjC"); - raylib.root_module.addCSourceFile(.{ - .file = b.path("src/rglfw.c"), - .flags = raylib_flags_arr.items, - }); - _ = raylib_flags_arr.pop(); - raylib.root_module.linkFramework("Foundation", .{}); - raylib.root_module.linkFramework("CoreServices", .{}); - raylib.root_module.linkFramework("CoreGraphics", .{}); - raylib.root_module.linkFramework("AppKit", .{}); - raylib.root_module.linkFramework("IOKit", .{}); + switch (target.result.os.tag) { + .windows => linkWindows(raylib_mod, true, false), + .linux => { + if (target.result.abi.isAndroid()) { + @panic("Target is not supported with this platform"); + } - setDesktopPlatform(raylib, options.platform); + linkLinux(raylib_mod, .None); + + if (options.linux_display_backend == .X11 or options.linux_display_backend == .Both) { + raylib_mod.addCMacro("_GLFW_X11", ""); + linkLinux(raylib_mod, .X11); + } + + if (options.linux_display_backend == .Wayland or options.linux_display_backend == .Both) { + findWaylandScanner(b); + + raylib_mod.addCMacro("_GLFW_WAYLAND", ""); + linkLinux(raylib_mod, .Wayland); + try waylandGenerate(b, raylib, "src/external/glfw/deps/wayland/", false); + } + }, + .freebsd, .openbsd, .netbsd, .dragonfly => linkBSD(b, raylib_mod), + .macos => { + // On macos rglfw.c include Objective-C files. + _ = c_source_files.pop(); + try raylib_flags_arr.append("-ObjC"); + raylib_mod.addCSourceFile(.{ + .file = b.path("src/rglfw.c"), + .flags = raylib_flags_arr.items, + }); + _ = raylib_flags_arr.pop(); + + linkMacOS(b, raylib_mod); + }, + .emscripten => { + switch (opengl_version) { + .auto => opengl_version = OpenglVersion.gles_2, + .gles_2, .gles_3, .gl_soft => {}, + else => @panic("opengl version not supported"), + } + + raylib_mod.addCMacro("PLATFORM_WEB", ""); + + const activate_emsdk_step = emsdk.zemscripten.activateEmsdkStep(b); + raylib.step.dependOn(activate_emsdk_step); + }, + else => @panic("Target is not supported with this platform"), + } + raylib_mod.addCMacro(opengl_version.toCMacroStr(), ""); }, - .emscripten => { - const activate_emsdk_step = emsdk.zemscripten.activateEmsdkStep(b); - raylib.step.dependOn(activate_emsdk_step); - raylib.root_module.addCMacro("PLATFORM_WEB", ""); + .rgfw => { + var opengl_version: OpenglVersion = options.opengl_version; + + if (target.result.os.tag != .emscripten) { + if (opengl_version == .auto) { + opengl_version = OpenglVersion.gl_3_3; + } + raylib_mod.addCMacro("PLATFORM_DESKTOP_RGFW", ""); + } + + switch (target.result.os.tag) { + .windows => linkWindows(raylib_mod, true, false), + .linux => { + if (target.result.abi.isAndroid()) { + @panic("Target is not supported with this platform"); + } + + linkLinux(raylib_mod, .None); + + if (options.linux_display_backend == .X11 or options.linux_display_backend == .Both) { + raylib_mod.addCMacro("RGFW_X11", ""); + raylib_mod.addCMacro("RGFW_UNIX", ""); + + linkLinux(raylib_mod, .X11); + } + + if (options.linux_display_backend == .Wayland or options.linux_display_backend == .Both) { + findWaylandScanner(b); + + if (options.linux_display_backend != .Both) { + raylib_mod.addCMacro("RGFW_NO_X11", ""); + } + + raylib_mod.addCMacro("RGFW_WAYLAND", ""); + raylib_mod.addCMacro("EGLAPIENTRY", ""); + + linkLinux(raylib_mod, .Wayland); + + try waylandGenerate(b, raylib, "src/external/RGFW/deps/wayland/", true); + } + }, + .freebsd, .openbsd, .netbsd, .dragonfly => linkBSD(b, raylib_mod), + .macos => linkMacOS(b, raylib_mod), + .emscripten => { + switch (opengl_version) { + .auto => opengl_version = OpenglVersion.gles_2, + .gles_2, .gles_3, .gl_soft => {}, + else => @panic("opengl version not supported"), + } + + raylib_mod.addCMacro("PLATFORM_WEB_RGFW", ""); + const activate_emsdk_step = emsdk.zemscripten.activateEmsdkStep(b); + raylib.step.dependOn(activate_emsdk_step); + }, + else => @panic("Target is not supported with this platform"), + } + + raylib_mod.addCMacro(opengl_version.toCMacroStr(), ""); + }, + .sdl, .sdl2, .sdl3 => { if (options.opengl_version == .auto) { - raylib.root_module.addCMacro("GRAPHICS_API_OPENGL_ES3", ""); + raylib_mod.addCMacro(OpenglVersion.gl_3_3.toCMacroStr(), ""); + } else { + raylib_mod.addCMacro(options.opengl_version.toCMacroStr(), ""); + } + + raylib_mod.addCMacro("PLATFORM_DESKTOP_SDL", ""); + + if (options.platform == .sdl2) { + raylib_mod.addCMacro("USING_SDL2_PACKAGE", ""); + } + if (options.platform == .sdl3) { + raylib_mod.addCMacro("USING_SDL3_PACKAGE", ""); } }, - else => { - @panic("Unsupported OS"); + .memory => { + if (options.opengl_version != .auto and options.opengl_version != .gl_soft) { + @panic("The opengl version is not supported by this platform"); + } + raylib_mod.addCMacro(OpenglVersion.gl_soft.toCMacroStr(), ""); + raylib_mod.addCMacro("PLATFORM_MEMORY", ""); + }, + .win32 => { + if (target.result.os.tag != .windows) { + @panic("Target is not supported with this platform"); + } + + if (options.opengl_version == .auto) { + raylib_mod.addCMacro(OpenglVersion.gl_3_3.toCMacroStr(), ""); + } else { + raylib_mod.addCMacro(options.opengl_version.toCMacroStr(), ""); + } + + raylib_mod.addCMacro("PLATFORM_DESKTOP_WIN32", ""); + + linkWindows(raylib_mod, options.opengl_version != .gl_soft, true); + }, + .drm => { + if (target.result.os.tag != .linux) { + @panic("Target is not supported with this platform"); + } + + raylib_mod.addCMacro("PLATFORM_DRM", ""); + raylib_mod.addCMacro("EGL_NO_X11", ""); + raylib_mod.addCMacro("DEFAULT_BATCH_BUFFER_ELEMENT", ""); + + try raylib_flags_arr.append("-Werror=implicit-function-declaration"); + + raylib_mod.linkSystemLibrary("libdrm", .{ .use_pkg_config = .force }); + raylib_mod.linkSystemLibrary("drm", .{}); + raylib_mod.linkSystemLibrary("gbm", .{}); + + switch (options.opengl_version) { + .auto, .gles_2 => { + raylib_mod.addCMacro(OpenglVersion.gles_2.toCMacroStr(), ""); + raylib_mod.linkSystemLibrary("GLESv2", .{}); + raylib_mod.linkSystemLibrary("EGL", .{}); + }, + .gl_soft => {}, + else => @panic("The opengl version is not supported by this platform"), + } + }, + .android => { + if (!target.result.abi.isAndroid()) { + @panic("Target is not supported with this platform"); + } + + raylib_mod.addCMacro("PLATFORM_ANDROID", ""); + + raylib_mod.linkSystemLibrary("EGL", .{}); + switch (options.opengl_version) { + .auto, .gles_2 => { + raylib_mod.addCMacro(OpenglVersion.gles_2.toCMacroStr(), ""); + raylib_mod.linkSystemLibrary("GLESv2", .{}); + }, + else => @panic("The opengl version is not supported by this platform"), + } + + //these are the only tag options per https://developer.android.com/ndk/guides/other_build_systems + const hostTuple = switch (builtin.target.os.tag) { + .linux => "linux-x86_64", + .windows => "windows-x86_64", + .macos => "darwin-x86_64", + else => @panic("unsupported host OS"), + }; + + const androidTriple = switch (target.result.cpu.arch) { + .x86 => "i686-linux-android", + .x86_64 => "x86_64-linux-android", + .arm => "arm-linux-androideabi", + .aarch64 => "aarch64-linux-android", + .riscv64 => "riscv64-linux-android", + else => error.InvalidAndroidTarget, + } catch @panic("invalid android target!"); + const androidNdkPathString: []const u8 = options.android_ndk; + if (androidNdkPathString.len < 1) @panic("no ndk path provided and ANDROID_NDK_HOME is not set"); + const androidApiLevel: []const u8 = options.android_api_version; + + const androidSysroot = try std.fs.path.join(b.allocator, &.{ androidNdkPathString, "/toolchains/llvm/prebuilt/", hostTuple, "/sysroot" }); + const androidLibPath = try std.fs.path.join(b.allocator, &.{ androidSysroot, "/usr/lib/", androidTriple }); + const androidApiSpecificPath = try std.fs.path.join(b.allocator, &.{ androidLibPath, androidApiLevel }); + const androidIncludePath = try std.fs.path.join(b.allocator, &.{ androidSysroot, "/usr/include" }); + const androidArchIncludePath = try std.fs.path.join(b.allocator, &.{ androidIncludePath, androidTriple }); + const androidAsmPath = try std.fs.path.join(b.allocator, &.{ androidIncludePath, "/asm-generic" }); + const androidGluePath = try std.fs.path.join(b.allocator, &.{ androidNdkPathString, "/sources/android/native_app_glue/" }); + + raylib_mod.addLibraryPath(.{ .cwd_relative = androidLibPath }); + raylib_mod.addLibraryPath(.{ .cwd_relative = androidApiSpecificPath }); + raylib_mod.addSystemIncludePath(.{ .cwd_relative = androidIncludePath }); + raylib_mod.addSystemIncludePath(.{ .cwd_relative = androidArchIncludePath }); + raylib_mod.addSystemIncludePath(.{ .cwd_relative = androidAsmPath }); + raylib_mod.addSystemIncludePath(.{ .cwd_relative = androidGluePath }); + + const libc_data = try std.fmt.allocPrint(b.allocator, + \\include_dir={0s}/sysroot/usr/include + \\sys_include_dir={0s}/sysroot/usr/include/aarch64-linux-android + \\crt_dir={0s}/sysroot/usr/lib/aarch64-linux-android/24 + \\static_lib_dir={0s}/sysroot/usr/lib/aarch64-linux-android/24 + \\msvc_lib_dir= + \\kernel32_lib_dir= + \\gcc_dir= + \\ + , .{androidNdkPathString}); + const write_step = b.addWriteFiles(); + const libcFile = write_step.add("android-libc.txt", libc_data); + raylib.setLibCFile(libcFile); }, } - raylib.root_module.addCSourceFiles(.{ + raylib_mod.addCSourceFiles(.{ .files = c_source_files.items, .flags = raylib_flags_arr.items, }); @@ -358,17 +476,33 @@ 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, options: Options) void { - const raylib_dep = b.dependencyFromBuildZig(@This(), options); - var gen_step = b.addWriteFiles(); - raylib.step.dependOn(&gen_step.step); +fn addRaygui(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, raylib: *std.Build.Step.Compile) void { + if (b.lazyDependency("raygui", .{ + .target = target, + .optimize = optimize, + .link_libc = true, + })) |raygui_dep| { + var gen_step = b.addWriteFiles(); + raylib.step.dependOn(&gen_step.step); - const raygui_c_path = gen_step.add("raygui.c", "#define RAYGUI_IMPLEMENTATION\n#include \"raygui.h\"\n"); - raylib.root_module.addCSourceFile(.{ .file = raygui_c_path }); - raylib.root_module.addIncludePath(raygui_dep.path("src")); - raylib.root_module.addIncludePath(raylib_dep.path("src")); + const raygui_c_path = gen_step.add("raygui.c", "#define RAYGUI_IMPLEMENTATION\n#include \"raygui.h\"\n"); + raylib.root_module.addCSourceFile(.{ .file = raygui_c_path }); + raylib.root_module.addIncludePath(raygui_dep.path("src")); + raylib.root_module.addIncludePath(b.path("src")); - raylib.installHeader(raygui_dep.path("src/raygui.h"), "raygui.h"); + raylib.installHeader(raygui_dep.path("src/raygui.h"), "raygui.h"); + + const c = b.addTranslateC(.{ + .root_source_file = raygui_dep.path("src/raygui.h"), + .target = target, + .optimize = optimize, + .link_libc = true, + }); + c.addIncludePath(b.path("src")); + const c_mod = c.createModule(); + c_mod.linkLibrary(raylib); + b.modules.put(b.graph.arena, "raygui", c_mod) catch @panic("OOM"); + } } pub const Options = struct { @@ -377,6 +511,7 @@ pub const Options = struct { rshapes: bool = true, rtext: bool = true, rtextures: bool = true, + raygui: bool = false, platform: PlatformBackend = .glfw, linkage: std.builtin.LinkMode = .static, linux_display_backend: LinuxDisplayBackend = .X11, @@ -396,6 +531,7 @@ pub const Options = struct { .rtext = b.option(bool, "rtext", "Compile with text support") orelse defaults.rtext, .rtextures = b.option(bool, "rtextures", "Compile with textures support") orelse defaults.rtextures, .rshapes = b.option(bool, "rshapes", "Compile with shapes support") orelse defaults.rshapes, + .raygui = b.option(bool, "raygui", "Include raygui") orelse defaults.raygui, .linkage = b.option(std.builtin.LinkMode, "linkage", "Compile as shared or static library") orelse defaults.linkage, .linux_display_backend = b.option(LinuxDisplayBackend, "linux_display_backend", "Linux display backend to use") orelse defaults.linux_display_backend, .opengl_version = b.option(OpenglVersion, "opengl_version", "OpenGL version to use") orelse defaults.opengl_version, @@ -441,15 +577,38 @@ pub const PlatformBackend = enum { glfw, rgfw, sdl, + sdl2, + sdl3, + memory, + win32, drm, android, }; +fn translateCMod( + comptime header: []const u8, + b: *std.Build, + target: std.Build.ResolvedTarget, + optimize: std.builtin.OptimizeMode, + raylib: *std.Build.Step.Compile, +) void { + const c = b.addTranslateC(.{ + .root_source_file = b.path("src/" ++ header ++ ".h"), + .target = target, + .optimize = optimize, + .link_libc = true, + }); + const c_mod = c.createModule(); + c_mod.linkLibrary(raylib); + b.modules.put(b.graph.arena, header, c_mod) catch @panic("OOM"); +} + pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); + const options: Options = .getOptions(b); - const lib = try compileRaylib(b, target, optimize, Options.getOptions(b)); + const lib = try compileRaylib(b, target, optimize, options); lib.installHeader(b.path("src/raylib.h"), "raylib.h"); lib.installHeader(b.path("src/rcamera.h"), "rcamera.h"); @@ -458,15 +617,24 @@ pub fn build(b: *std.Build) !void { b.installArtifact(lib); - const examples = b.step("examples", "Build/Install all examples"); - examples.dependOn(try addExamples("audio", b, target, optimize, lib)); - examples.dependOn(try addExamples("core", b, target, optimize, lib)); - examples.dependOn(try addExamples("models", b, target, optimize, lib)); - examples.dependOn(try addExamples("others", b, target, optimize, lib)); - examples.dependOn(try addExamples("shaders", b, target, optimize, lib)); - examples.dependOn(try addExamples("shapes", b, target, optimize, lib)); - examples.dependOn(try addExamples("text", b, target, optimize, lib)); - examples.dependOn(try addExamples("textures", b, target, optimize, lib)); + translateCMod("raylib", b, target, optimize, lib); + translateCMod("rcamera", b, target, optimize, lib); + translateCMod("raymath", b, target, optimize, lib); + translateCMod("rlgl", b, target, optimize, lib); + + if (options.raygui) { + addRaygui(b, target, optimize, lib); + } + + const examples = b.step("examples", "build/install all examples"); + examples.dependOn(try addExamples("core", b, target, optimize, lib, options.platform)); + examples.dependOn(try addExamples("audio", b, target, optimize, lib, options.platform)); + examples.dependOn(try addExamples("models", b, target, optimize, lib, options.platform)); + examples.dependOn(try addExamples("shaders", b, target, optimize, lib, options.platform)); + examples.dependOn(try addExamples("shapes", b, target, optimize, lib, options.platform)); + examples.dependOn(try addExamples("text", b, target, optimize, lib, options.platform)); + examples.dependOn(try addExamples("textures", b, target, optimize, lib, options.platform)); + examples.dependOn(try addExamples("others", b, target, optimize, lib, options.platform)); } fn addExamples( @@ -475,6 +643,7 @@ fn addExamples( target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, raylib: *std.Build.Step.Compile, + platform: PlatformBackend, ) !*std.Build.Step { const all = b.step(module, "All " ++ module ++ " examples"); const module_subpath = b.pathJoin(&.{ "examples", module }); @@ -485,118 +654,88 @@ fn addExamples( var iter = dir.iterate(); while (try iter.next(b.graph.io)) |entry| { if (entry.kind != .file) continue; - const extension_idx = std.mem.lastIndexOf(u8, entry.name, ".c") orelse continue; - const name = entry.name[0..extension_idx]; - const filename = try std.fmt.allocPrint(b.allocator, "{s}.c", .{name}); - const path = b.pathJoin(&.{ module_subpath, filename }); - // 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 filetype = std.fs.path.extension(entry.name); + if (!std.mem.eql(u8, filetype, ".c")) continue; + + const filename = std.fs.path.stem(entry.name); + const path = b.pathJoin(&.{ module_subpath, entry.name }); const exe_mod = b.createModule(.{ .target = target, .optimize = optimize, + .link_libc = true, }); - exe_mod.addCSourceFile(.{ .file = b.path(path), .flags = &.{} }); + exe_mod.addCSourceFile(.{ .file = b.path(path) }); exe_mod.linkLibrary(raylib); - const run_step = b.step(name, name); + if (platform == .sdl) { + exe_mod.linkSystemLibrary("SDL2", .{}); + exe_mod.linkSystemLibrary("SDL3", .{}); + } + if (platform == .sdl2) { + exe_mod.linkSystemLibrary("SDL2", .{}); + } + if (platform == .sdl3) { + exe_mod.linkSystemLibrary("SDL3", .{}); + } + + if (std.mem.eql(u8, filename, "rlgl_standalone")) { + if (platform != .glfw) continue; + exe_mod.addIncludePath(b.path("src")); + exe_mod.addIncludePath(b.path("src/external/glfw/include")); + } + if (std.mem.eql(u8, filename, "raylib_opengl_interop")) { + if (platform == .drm) continue; + if (target.result.os.tag == .macos) continue; + exe_mod.addIncludePath(b.path("src/external")); + } + + const run_step = b.step(filename, filename); + + // web exports are completely separate + if (target.query.os_tag == .emscripten) { + exe_mod.addCMacro("PLATFORM_WEB", ""); - if (target.result.os.tag == .emscripten) { const wasm = b.addLibrary(.{ - .name = name, - .linkage = .static, + .name = filename, .root_module = exe_mod, }); - if (std.mem.eql(u8, name, "rlgl_standalone")) { - exe_mod.addIncludePath(b.path("src")); - exe_mod.addIncludePath(b.path("src/external/glfw/include")); - } - if (std.mem.eql(u8, name, "raylib_opengl_interop")) { - exe_mod.addIncludePath(b.path("src/external")); - } - + const install_dir: std.Build.InstallDir = .{ .custom = b.fmt("web/{s}/{s}", .{ module, filename }) }; const emcc_flags = emsdk.emccDefaultFlags(b.allocator, .{ .optimize = optimize }); const emcc_settings = emsdk.emccDefaultSettings(b.allocator, .{ .optimize = optimize }); - const install_dir: std.Build.InstallDir = .{ .custom = "htmlout" }; const emcc_step = emsdk.emccStep(b, raylib, wasm, .{ .optimize = optimize, .flags = emcc_flags, .settings = emcc_settings, .shell_file_path = b.path("src/shell.html"), - .embed_paths = &.{ - .{ - .src_path = b.pathJoin(&.{ module_subpath, "resources" }), - .virtual_path = "resources", - }, - }, .install_dir = install_dir, }); + b.getInstallStep().dependOn(emcc_step); const html_filename = try std.fmt.allocPrint(b.allocator, "{s}.html", .{wasm.name}); const emrun_step = emsdk.emrunStep( b, b.getInstallPath(install_dir, html_filename), - &.{"--no_browser"}, + &.{}, ); - emrun_step.dependOn(emcc_step); + emrun_step.dependOn(emcc_step); run_step.dependOn(emrun_step); all.dependOn(emcc_step); } else { - // special examples that test using these external dependencies directly - // alongside raylib - if (std.mem.eql(u8, name, "rlgl_standalone")) { - exe_mod.addIncludePath(b.path("src")); - exe_mod.addIncludePath(b.path("src/external/glfw/include")); - if (!hasCSource(raylib.root_module, "rglfw.c")) { - exe_mod.addCSourceFile(.{ .file = b.path("src/rglfw.c"), .flags = &.{} }); - } - } - if (std.mem.eql(u8, name, "raylib_opengl_interop")) { - exe_mod.addIncludePath(b.path("src/external")); - } - - switch (target.result.os.tag) { - .windows => { - exe_mod.linkSystemLibrary("winmm", .{}); - exe_mod.linkSystemLibrary("gdi32", .{}); - exe_mod.linkSystemLibrary("opengl32", .{}); - - exe_mod.addCMacro("PLATFORM_DESKTOP", ""); - }, - .linux => { - exe_mod.linkSystemLibrary("GL", .{}); - exe_mod.linkSystemLibrary("rt", .{}); - exe_mod.linkSystemLibrary("dl", .{}); - exe_mod.linkSystemLibrary("m", .{}); - exe_mod.linkSystemLibrary("X11", .{}); - - exe_mod.addCMacro("PLATFORM_DESKTOP", ""); - }, - .macos => { - exe_mod.linkFramework("Foundation", .{}); - exe_mod.linkFramework("Cocoa", .{}); - exe_mod.linkFramework("OpenGL", .{}); - exe_mod.linkFramework("CoreAudio", .{}); - exe_mod.linkFramework("CoreVideo", .{}); - exe_mod.linkFramework("IOKit", .{}); - - exe_mod.addCMacro("PLATFORM_DESKTOP", ""); - }, - else => { - @panic("Unsupported OS"); - }, - } + exe_mod.addCMacro("PLATFORM_DESKTOP", ""); const exe = b.addExecutable(.{ - .name = name, + .name = filename, .root_module = exe_mod, + .use_lld = target.result.os.tag == .windows, }); + b.installArtifact(exe); - const install_cmd = b.addInstallArtifact(exe, .{}); + const install_cmd = b.addInstallArtifact(exe, .{ .dest_sub_path = b.fmt("{s}/{s}", .{ module, filename }) }); const run_cmd = b.addRunArtifact(exe); run_cmd.cwd = b.path(module_subpath); @@ -606,41 +745,46 @@ fn addExamples( all.dependOn(&install_cmd.step); } } - return all; } fn waylandGenerate( b: *std.Build, raylib: *std.Build.Step.Compile, - comptime protocol: []const u8, - comptime basename: []const u8, -) void { - const waylandDir = "src/external/glfw/deps/wayland"; - const protocolDir = b.pathJoin(&.{ waylandDir, protocol }); - const clientHeader = basename ++ ".h"; - const privateCode = basename ++ "-code.h"; + comptime waylandDir: []const u8, + comptime source: bool, +) !void { + const dir = try b.build_root.handle.openDir(b.graph.io, waylandDir, .{ .iterate = true }); + defer dir.close(b.graph.io); - const client_step = b.addSystemCommand(&.{ "wayland-scanner", "client-header" }); - client_step.addFileArg(b.path(protocolDir)); - raylib.root_module.addIncludePath(client_step.addOutputFileArg(clientHeader).dirname()); + var iter = dir.iterate(); + while (try iter.next(b.graph.io)) |entry| { + if (entry.kind != .file) continue; + const protocolDir = b.pathJoin(&.{ waylandDir, entry.name }); - const private_step = b.addSystemCommand(&.{ "wayland-scanner", "private-code" }); - private_step.addFileArg(b.path(protocolDir)); - raylib.root_module.addIncludePath(private_step.addOutputFileArg(privateCode).dirname()); + const filename = std.fs.path.stem(entry.name); - raylib.step.dependOn(&client_step.step); - raylib.step.dependOn(&private_step.step); -} - -fn hasCSource(module: *std.Build.Module, name: []const u8) bool { - for (module.link_objects.items) |o| switch (o) { - .c_source_file => |c| if (switch (c.file) { - .src_path => |s| std.ascii.endsWithIgnoreCase(s.sub_path, name), - .generated, .cwd_relative, .dependency => false, - }) return true, - .c_source_files => |s| for (s.files) |c| if (std.ascii.endsWithIgnoreCase(c, name)) return true, - else => {}, - }; - return false; + const clientHeader = b.fmt("{s}-client-protocol.h", .{filename}); + const client_step = b.addSystemCommand(&.{ "wayland-scanner", "client-header" }); + client_step.addFileArg(b.path(protocolDir)); + raylib.root_module.addIncludePath(client_step.addOutputFileArg(clientHeader).dirname()); + raylib.step.dependOn(&client_step.step); + + if (comptime source) { + const privateCode = b.fmt("{s}-client-protocol-code.c", .{filename}); + const private_step = b.addSystemCommand(&.{ "wayland-scanner", "private-code" }); + private_step.addFileArg(b.path(protocolDir)); + raylib.root_module.addCSourceFile(.{ + .file = private_step.addOutputFileArg(privateCode), + .flags = &.{ "-std=c99", "-O2" }, + }); + raylib.step.dependOn(&private_step.step); + } else { + const privateCodeHeader = b.fmt("{s}-client-protocol-code.h", .{filename}); + const private_head_step = b.addSystemCommand(&.{ "wayland-scanner", "private-code" }); + private_head_step.addFileArg(b.path(protocolDir)); + raylib.root_module.addIncludePath(private_head_step.addOutputFileArg(privateCodeHeader).dirname()); + raylib.step.dependOn(&private_head_step.step); + } + } } diff --git a/build.zig.zon b/build.zig.zon index dd25fde73..07a1f7400 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -11,6 +11,11 @@ .hash = "N-V-__8AALShqgXkvqYU6f__FrA22SMWmi2TXCJjNTO1m8XJ", .lazy = true, }, + .raygui = .{ + .url = "git+https://github.com/raysan5/raygui#3b2855842ab578a034f827c38cf8f62c042fc983", + .hash = "N-V-__8AAHvybwBw1kyBGn0BW_s1RqIpycNjLf_XbE-fpLUF", + .lazy = true, + }, .emsdk = .{ .url = "git+https://github.com/emscripten-core/emsdk#4.0.9", .hash = "N-V-__8AAJl1DwBezhYo_VE6f53mPVm00R-Fk28NPW7P14EQ", diff --git a/projects/README.md b/projects/README.md index af24f1547..2758cb86e 100644 --- a/projects/README.md +++ b/projects/README.md @@ -13,6 +13,7 @@ IDE | Platform(s) | Source | Example(s) [SublimeText](https://www.sublimetext.com/) | Windows, Linux, macOS | ✔️ | ✔️ [VS2019](https://www.visualstudio.com) | Windows | ✔️ | ✔️ [VSCode](https://code.visualstudio.com/) | Windows, macOS | ❌ | ✔️ +[Zig](https://ziglang.org) | Windows, Linux, macOS, Web | ✔️ | ✔️ scripts | Windows, Linux, macOS | ✔️ | ✔️ *New IDEs config files are welcome!* diff --git a/projects/Zig/README.md b/projects/Zig/README.md new file mode 100644 index 000000000..a7c24e093 --- /dev/null +++ b/projects/Zig/README.md @@ -0,0 +1,84 @@ +# Starting your raylib project with Zig (0.16.0) + +## How to compile and run it + +To compile the project: + +```sh +zig build +``` + +To run the project: + +```sh +zig build run +``` + +## Compile with different optimization + +To change from debug to release build you can do it with the `-Doptimze=` flag. + +``` +Debug +ReleaseSafe +ReleaseFast +ReleaseSmall +``` + +## Choose a different platform + +To compile with a different platform you can use the `-Dplatform=` flag. +Here all the options: + +``` +glfw +rgfw +sdl +sdl2 +sdl3 +memory +win32 +drm +android +``` + +In this example the platform `sdl` and `sdl2` are not supported + +Important for the android platform you also have to compile for the right target + +## Compile for a different target + +To compile for a different [target](https://ziglang.org/download/0.16.0/release-notes.html#Support-Table) you can use the `-Dtarget=` flag. +Not all targets are supported + +## Example: Compile for web and run it + +To compile for the web we use emscripten and you run it like that: + +```sh +zig build -Dtarget=wasm32-emscripten +``` + +To run it we do: + +```sh +zig build run -Dtarget=wasm32-emscripten +``` + +And to make a relase build we do: + +```sh +zig build -Dtarget=wasm32-emscripten -Doptimize=ReleaseFast +``` + +If we want to use rgfw for the web build we could do: + +```sh +zig build -Dplatform=rgfw -Dtarget=wasm32-emscripten -Doptimize=ReleaseFast +``` + +## Compiling the Zig code? Just add `-Dzig` and try out zig ;) + +## More Resources + +See [Zig Build System](https://ziglang.org/learn/build-system/) diff --git a/projects/Zig/build.zig b/projects/Zig/build.zig new file mode 100644 index 000000000..917e97b71 --- /dev/null +++ b/projects/Zig/build.zig @@ -0,0 +1,87 @@ +const std = @import("std"); +const rl = @import("raylib"); + +pub fn build(b: *std.Build) !void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + const platform = b.option(rl.PlatformBackend, "platform", "select the platform") orelse rl.PlatformBackend.glfw; + const zig = b.option(bool, "zig", "compile zig code") orelse false; + + const raylib_dep = b.dependency("raylib", .{ + .target = target, + .optimize = optimize, + .platform = platform, + }); + const raylib_artifact = raylib_dep.artifact("raylib"); + + if (platform == .sdl3) { + if (b.lazyDependency("sdl3", .{ .optimize = optimize, .target = target })) |dep| { + raylib_artifact.root_module.linkLibrary(dep.artifact("SDL3")); + } + } + + var exe_mod: *std.Build.Module = undefined; + + if (zig) { + exe_mod = b.createModule(.{ + .root_source_file = b.path("src/core_basic_window.zig"), + .target = target, + .optimize = optimize, + }); + exe_mod.addImport("raylib", raylib_dep.module("raylib")); + } else { + exe_mod = b.createModule(.{ + .target = target, + .optimize = optimize, + .link_libc = true, + }); + exe_mod.addCSourceFile(.{ .file = b.path("src/core_basic_window.c") }); + exe_mod.linkLibrary(raylib_artifact); + } + + const run_step = b.step("run", "Run the app"); + + // web exports are completely separate + if (target.query.os_tag == .emscripten) { + const emsdk = rl.emsdk; + const wasm = b.addLibrary(.{ + .name = "core_basic_window_web", + .root_module = exe_mod, + }); + + const install_dir: std.Build.InstallDir = .{ .custom = "web" }; + const emcc_flags = emsdk.emccDefaultFlags(b.allocator, .{ .optimize = optimize }); + const emcc_settings = emsdk.emccDefaultSettings(b.allocator, .{ .optimize = optimize }); + + const emcc_step = emsdk.emccStep(b, raylib_artifact, wasm, .{ + .optimize = optimize, + .flags = emcc_flags, + .settings = emcc_settings, + .shell_file_path = emsdk.shell(raylib_dep), + .install_dir = install_dir, + }); + b.getInstallStep().dependOn(emcc_step); + + const html_filename = try std.fmt.allocPrint(b.allocator, "{s}.html", .{wasm.name}); + const emrun_step = emsdk.emrunStep( + b, + b.getInstallPath(install_dir, html_filename), + &.{}, + ); + + emrun_step.dependOn(emcc_step); + run_step.dependOn(emrun_step); + } else { + const exe = b.addExecutable(.{ + .name = "core_basic_window", + .root_module = exe_mod, + .use_lld = target.result.os.tag == .windows, + }); + b.installArtifact(exe); + + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(b.getInstallStep()); + + run_step.dependOn(&run_cmd.step); + } +} diff --git a/projects/Zig/build.zig.zon b/projects/Zig/build.zig.zon new file mode 100644 index 000000000..77bf4291c --- /dev/null +++ b/projects/Zig/build.zig.zon @@ -0,0 +1,23 @@ +.{ + .name = .example, + .version = "0.0.1", + .minimum_zig_version = "0.16.0", + .paths = .{""}, + + .dependencies = .{ + .raylib = .{ + .path = "../../", + }, + .emsdk = .{ + .url = "git+https://github.com/emscripten-core/emsdk?ref=4.0.9#3bcf1dcd01f040f370e10fe673a092d9ed79ebb5", + .hash = "N-V-__8AAJl1DwBezhYo_VE6f53mPVm00R-Fk28NPW7P14EQ", + }, + .sdl3 = .{ + .url = "git+https://codeberg.org/7Games/zig-sdl3?ref=master#6d418ef3ddae99098414a96a88bf5e5fdb41785e", + .hash = "sdl3-0.1.9-NmT1QwiEJwByePqkmArtppCHQn8Y7kiSWcncT_Mop8ie", + .lazy = true, + }, + }, + + .fingerprint = 0x6eec9b9f1a9d7aca, +} diff --git a/projects/Zig/src/core_basic_window.c b/projects/Zig/src/core_basic_window.c new file mode 100644 index 000000000..cb23a6f87 --- /dev/null +++ b/projects/Zig/src/core_basic_window.c @@ -0,0 +1,83 @@ +/******************************************************************************************* +* +* raylib [core] example - Basic window (adapted for HTML5 platform) +* +* This example is prepared to compile for PLATFORM_WEB and PLATFORM_DESKTOP +* As you will notice, code structure is slightly different to the other examples... +* To compile it for PLATFORM_WEB just uncomment #define PLATFORM_WEB at beginning +* +* This example has been created using raylib 1.3 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2015 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#if defined(PLATFORM_WEB) + #include +#endif + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +int screenWidth = 800; +int screenHeight = 450; + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +void UpdateDrawFrame(void); // Update and Draw one frame + +//---------------------------------------------------------------------------------- +// Program main entry point +//---------------------------------------------------------------------------------- +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [core] example - basic 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 + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- +void UpdateDrawFrame(void) +{ + // Update + //---------------------------------------------------------------------------------- + // TODO: Update your variables here + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- +} diff --git a/projects/Zig/src/core_basic_window.zig b/projects/Zig/src/core_basic_window.zig new file mode 100644 index 000000000..c26efc0d6 --- /dev/null +++ b/projects/Zig/src/core_basic_window.zig @@ -0,0 +1,73 @@ +//******************************************************************************************* +//* +//* raylib [core] example - Basic window (adapted for HTML5 platform) +//* +//* This example is prepared to compile for PLATFORM_WEB and PLATFORM_DESKTOP +//* As you will notice, code structure is slightly different to the other examples... +//* To compile it for PLATFORM_WEB just uncomment #define PLATFORM_WEB at beginning +//* +//* This example has been created using raylib 6.0 (www.raylib.com) +//* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +//* +//* Copyright (c) 2015 Ramon Santamaria (@raysan5) +//* Rewrite in Zig by HaxSam (@haxsam) +//* +//******************************************************************************************* + +const rl = @import("raylib"); +const std = @import("std"); +const builtin = @import("builtin"); + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +const screenWidth: c_int = 800; +const screenHeight: c_int = 450; + +//---------------------------------------------------------------------------------- +// Program main entry point +//---------------------------------------------------------------------------------- +pub fn main() void { + // Initialization + //-------------------------------------------------------------------------------------- + rl.InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window"); + + if (builtin.os.tag == .emscripten) { + std.os.emscripten.emscripten_set_main_loop(UpdateDrawFrame, 0, 1); + } else { + rl.SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!rl.WindowShouldClose()) // Detect window close button or ESC key + { + UpdateDrawFrame(); + } + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + rl.CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- +fn UpdateDrawFrame() callconv(.c) void { + // Update + //---------------------------------------------------------------------------------- + // TODO: Update your variables here + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + rl.BeginDrawing(); + + rl.ClearBackground(rl.RAYWHITE); + + rl.DrawText("Congrats! You created your first window!", 190, 200, 20, rl.LIGHTGRAY); + + rl.EndDrawing(); + //---------------------------------------------------------------------------------- +} From d87bf55607008253419ba92687ffcc0005a87aff Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 19 Apr 2026 20:03:29 +0200 Subject: [PATCH 314/319] Update CHANGELOG --- CHANGELOG | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index e4e06ce47..31ed2f74d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -115,6 +115,7 @@ Detailed changes: [rcore][GLFW] REVIEWED: `GetGamepadButtonPressed()`, return GAMEPAD_BUTTON_*_TRIGGER_2 if pressed (#4742) by @whaleymar [rcore][GLFW] REVIEWED: Update `mappings.h` using `GenerateMappings.cmake` by @sleeptightAnsiC [rcore][RGFW] ADDED: New backend option: `PLATFORM_WEB_RGFW` and update RGFW (#4480) by @colleagueRiley +[rcore][RGFW] ADDED: Support for software rendering (#5773) by @CrackedPixel [rcore][RGFW] REVIEWED: Added missing Right Control key by @M374LX [rcore][RGFW] REVIEWED: Changed `RGFW_window_eventWait` timeout to -1 by @doggymangc [rcore][RGFW] REVIEWED: Duplicate entries removed from `keyMappingRGFW` (#5242) by @iamahuman1395 @@ -326,6 +327,7 @@ Detailed changes: [rmodels] REVIEWED: `DrawSphereEx()`, normals support (#4926) by @karl-zylinski [rmodels] REVIEWED: `ExportMesh()`, improve OBJ vertex data precision and lower memory usage (#4496) by @mikeemm [rmodels] REVIEWED: `CheckCollisionSpheres()`, simplified using `Vector3DistanceSqr()` (#5695) by @Bigfoot71 +[rmodels] REVIEWED: `CheckCollisionBoxSphere()`, improved performance and consistency (#5776) by @maiconpintoabreu [raudio] REVIEWED: Fix a glitch at the end of a sound (#5578) by @mackron [raudio] REVIEWED: Improvements to device configuration (#5577) by @mackron [raudio] REVIEWED: Initialize sound alias properties as if it was a new sound (#5123) by @JeffM2501 @@ -410,11 +412,12 @@ Detailed changes: [build][CMake] REVIEWED: Set `libm` as public so it can be linked by consumer (#5193) by @brccabral [build][Cmake] REVIEWED: Expose PLATFORM_WEB_RGFW (#5579) by @crisserpl2 [build][Zig] ADDED: Android target to build by @lumenkeyes +[build][Zig] REDESIGNED: Complete refactor of build.zig to support Zig 0.16.0 (#5764) by @HaxSam [build][Zig] REVIEWED: Link EGL for Android by @lumenkeyes [build][Zig] REVIEWED: Approach to build for web with zig-build (#5157) by @haxsam [build][Zig] REVIEWED: Fix build accessing env vars (#5490) by @iisakki [build][Zig] REVIEWED: Fix emscripten building by @johnnymarler -[build][Zig] REVIWED: Move examples/build.zig into main build.zig by @johnnymarler +[build][Zig] REVIEWED: Move examples/build.zig into main build.zig by @johnnymarler [build][Zig] REVIEWED: Fix raygui inclusion in windows crosscompilation (#4489) by @kimierik [build][Zig] REVIEWED: Issue on emscripten run if the user has not installed emsdk by @emilhakimov415 [build][Zig] REVIEWED: Make X11 the default display backend instead of choosing at runtime (#5168) by @Not-Nik @@ -462,6 +465,7 @@ Detailed changes: [examples] ADDED: `shapes_rlgl_triangle` example (#5353) by @robinsaviary [examples] ADDED: `shapes_starfield` (#5255) by @themushroompirates [examples] ADDED: `shapes_triangle_strip` (#5240) by @Jopestpe +[examples] ADDED: `shapes_collision_ellipses` (#5722) by @Monjaris [examples] ADDED: `text_inline_styling` by @raysan5 [examples] ADDED: `text_strings_management` (#5379) by @davidbuzatto [examples] ADDED: `text_words_alignment` (#5254) by @themushroompirates From d6445b55d62d950911f86f3863e7713eae825838 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 19 Apr 2026 20:03:31 +0200 Subject: [PATCH 315/319] Update core_basic_window.c --- examples/core/core_basic_window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/core/core_basic_window.c b/examples/core/core_basic_window.c index 73d75683a..c1009fc76 100644 --- a/examples/core/core_basic_window.c +++ b/examples/core/core_basic_window.c @@ -22,7 +22,7 @@ * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software * -* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) * ********************************************************************************************/ From a4680fc67734754138650138f11c54dc798ede56 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 19 Apr 2026 20:03:35 +0200 Subject: [PATCH 316/319] Update HISTORY.md --- HISTORY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index a6ff00468..1ef03e474 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -530,8 +530,8 @@ A new `raylib` release is finally ready and, again, this is the **biggest `rayli Some astonishing numbers for this release: - - **+320** closed issues (for a TOTAL of **+2140**!) - - **+1950** commits since previous RELEASE (for a TOTAL of **+9700**!) + - **+330** closed issues (for a TOTAL of **+2150**!) + - **+2000** commits since previous RELEASE (for a TOTAL of **+9760**!) - **+20** new functions ADDED to raylib API (for a TOTAL of **600**!) - **+50** new examples to learn from (for a TOTAL of **+210**!) - **+210** new contributors (for a TOTAL of **+850**!) From 7495a77a79b706d6d59cdafa7becc2403fb009b4 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 22 Apr 2026 13:41:15 +0200 Subject: [PATCH 317/319] Update CHANGELOG --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 31ed2f74d..eaf467294 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -17,7 +17,7 @@ KEY CHANGES: - New File System API - New Text Management API - New tool: [rexm] raylib examples manager - - Added +50 new examples to learn from + - Added +70 new examples to learn from Detailed changes: From 932558ca7a54a867f04db3fe4e237ebd887fb8cc Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 22 Apr 2026 13:41:27 +0200 Subject: [PATCH 318/319] Update HISTORY.md --- HISTORY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 1ef03e474..2d6a4b77d 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -533,7 +533,7 @@ Some astonishing numbers for this release: - **+330** closed issues (for a TOTAL of **+2150**!) - **+2000** commits since previous RELEASE (for a TOTAL of **+9760**!) - **+20** new functions ADDED to raylib API (for a TOTAL of **600**!) - - **+50** new examples to learn from (for a TOTAL of **+210**!) + - **+70** new examples to learn from (for a TOTAL of **+215**!) - **+210** new contributors (for a TOTAL of **+850**!) Highlights for `raylib 6.0`: @@ -591,7 +591,7 @@ COMMANDS: update : Validate and update examples collection, generates report ``` - - **`NEW` +50 new examples**: Thanks to `rexm` and the simplification on examples management, this new raylib release includes +50 new examples to leearn from, most of them contributed by community. + - **`NEW` +70 new examples**: Thanks to `rexm` and the simplification on examples management, this new raylib release includes +70 new examples to learn from, most of them contributed by community. Multiple examples have also been renamed for consistency and all examples header and structure have been reviewed and unified. Make sure to check raylib [CHANGELOG](https://github.com/raysan5/raylib/blob/master/CHANGELOG) for a detailed list of changes! From dbc56a87da87d973a9c5baa4e7438a9d20121d28 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 23 Apr 2026 00:18:40 +0200 Subject: [PATCH 319/319] Update HISTORY.md --- HISTORY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 2d6a4b77d..a1b60a49c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -597,8 +597,8 @@ Make sure to check raylib [CHANGELOG](https://github.com/raysan5/raylib/blob/mas I want to **thank all the contributors (+850!**) that along the years have **greatly improved raylib** and pushed it further and better day after day. And **many thanks to raylib community and all raylib users** for supporting the library along those many years. -Finally, I want to thank [puffer.ai](https://puffer.ai/) and [comma.ai](https://comma.ai/) for **supporting and sponsoring the project** as platinum sponsors, along many others individuals that have been sponsoring raylib along the years. Thanks to all of you for allowing me to keep working on this library! +Finally, I want to thank [puffer.ai](https://puffer.ai/) and [comma.ai](https://comma.ai/) for **using raylib and supporting the project** as platinum sponsors, along many others individuals that have been sponsoring raylib along the years. Thanks to all of you for allowing me to keep working on this library! -**After +12 years of development, `raylib 6.0` is today one of the bests libraries to enjoy games/tools/graphic programming!** +**After +12 years of development, `raylib 6.0` is today one of the bests libraries to enjoy games/tools/graphics programming!** **Enjoy graphics programming with raylib!** :)

G0dINAu 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 UR$2sm1od$#$f!!!N}7NFKd_WlbpQYW literal 0 HcmV?d00001 From 406861a4a1c2b568f214e87b4ebe2efec41b6c3f Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 24 Feb 2026 12:32:48 +0100 Subject: [PATCH 076/319] Update raudio.c --- src/raudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index 2740462b4..0f8b5101f 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -2487,7 +2487,7 @@ static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, f { float *runningFramesOut = framesOut + (totalOutputFramesProcessed*audioBuffer->converter.channelsOut); ma_uint64 outputFramesToProcessThisIteration = frameCount - totalOutputFramesProcessed; - ma_uint64 inputFramesToProcessThisIteration = 0; + //ma_uint64 inputFramesToProcessThisIteration = 0; // Process any residual input frames from the previous read first. if (audioBuffer->converterResidualCount > 0) From 25e521553d48d790b00dd5e554ff67290abb4995 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 24 Feb 2026 12:33:00 +0100 Subject: [PATCH 077/319] Update models_animation_blending.c --- examples/models/models_animation_blending.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/examples/models/models_animation_blending.c b/examples/models/models_animation_blending.c index 46577a353..b6ff5439c 100644 --- a/examples/models/models_animation_blending.c +++ b/examples/models/models_animation_blending.c @@ -184,7 +184,7 @@ int main(void) if (animCurrentFrame0 >= anims[animIndex0].keyframeCount) animCurrentFrame0 = 0.0f; UpdateModelAnimation(model, anims[animIndex0], animCurrentFrame0); //UpdateModelAnimationEx(model, anims[animIndex0], animCurrentFrame0, - // anims[animIndex1], animCurrentFrame1, 0.0f); + // anims[animIndex1], animCurrentFrame1, 0.0f); // Same as above, first animation frame blend } else if (currentAnimPlaying == 1) { @@ -193,7 +193,7 @@ int main(void) if (animCurrentFrame1 >= anims[animIndex1].keyframeCount) animCurrentFrame1 = 0.0f; UpdateModelAnimation(model, anims[animIndex1], animCurrentFrame1); //UpdateModelAnimationEx(model, anims[animIndex0], animCurrentFrame0, - // anims[animIndex1], animCurrentFrame1, 1.0f); + // anims[animIndex1], animCurrentFrame1, 1.0f); // Same as above, second animation frame blend } } } @@ -221,6 +221,15 @@ int main(void) // Draw UI elements //--------------------------------------------------------------------------------------------- + if (dropdownEditMode0) GuiDisable(); + GuiSlider((Rectangle){ 10, 38, 160, 12 }, + NULL, TextFormat("x%.1f", animFrameSpeed0), &animFrameSpeed0, 0.1f, 2.0f); + GuiEnable(); + if (dropdownEditMode1) GuiDisable(); + GuiSlider((Rectangle){ GetScreenWidth() - 170, 38, 160, 12 }, + TextFormat("%.1fx", animFrameSpeed1), NULL, &animFrameSpeed1, 0.1f, 2.0f); + GuiEnable(); + // Draw animation selectors for blending transition // NOTE: Transition does not start until requested GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 1); From aec6e85ec3c5a29787d71c60373b36c76b1ffc90 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 24 Feb 2026 12:40:17 +0100 Subject: [PATCH 078/319] Update raygui.h --- examples/models/raygui.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/models/raygui.h b/examples/models/raygui.h index 3bf7638be..03e487912 100644 --- a/examples/models/raygui.h +++ b/examples/models/raygui.h @@ -5128,11 +5128,11 @@ static const char *GetTextIcon(const char *text, int *iconId) // Get text divided into lines (by line-breaks '\n') // WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator! -static char **GetTextLines(const char *text, int *count) +static const char **GetTextLines(const char *text, int *count) { #define RAYGUI_MAX_TEXT_LINES 128 - static char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; + static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings int textLength = (int)strlen(text); @@ -5202,7 +5202,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C // WARNING: GuiTextSplit() function can't be used now because it can have already been used // before the GuiDrawText() call and its buffer is static, it would be overriden :( int lineCount = 0; - char **lines = GetTextLines(text, &lineCount); + const char **lines = GetTextLines(text, &lineCount); // Text style variables //int alignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT); From 29b584411928db2455fef9343b9a2a603a206871 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 24 Feb 2026 12:48:54 +0100 Subject: [PATCH 079/319] REVIEWED: Comments and header --- examples/models/models_animation_blend_custom.c | 7 ++++--- examples/models/models_animation_blending.c | 13 ++++++------- examples/models/models_animation_timing.c | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/models/models_animation_blend_custom.c b/examples/models/models_animation_blend_custom.c index f846a125b..0b83c75d2 100644 --- a/examples/models/models_animation_blend_custom.c +++ b/examples/models/models_animation_blend_custom.c @@ -6,15 +6,16 @@ * * Example originally created with raylib 5.5, last time updated with raylib 5.5 * +* Example contributed by dmitrii-brand (@dmitrii-brand) and reviewed by Ramon Santamaria (@raysan5) +* * DETAILS: Example demonstrates per-bone animation blending, allowing smooth transitions * between two animations by interpolating bone transforms. This is useful for: * - Blending movement animations (walk/run) with action animations (jump/attack) * - Creating smooth animation transitions * - Layering animations (e.g., upper body attack while lower body walks) * -* Example contributed by dmitrii-brand (@dmitrii-brand) and reviewed by Ramon Santamaria (@raysan5) -* -* NOTE: Due to limitations in the Apple OpenGL driver, this feature does not work on MacOS +* WARNING: GPU skinning must be enabled in raylib with a compilation flag, +* if not enabled, CPU skinning will be used instead * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software diff --git a/examples/models/models_animation_blending.c b/examples/models/models_animation_blending.c index b6ff5439c..8b1b0f220 100644 --- a/examples/models/models_animation_blending.c +++ b/examples/models/models_animation_blending.c @@ -2,20 +2,19 @@ * * raylib [models] example - animation blending * -* Example complexity rating: [☆☆☆☆] 0/4 +* Example complexity rating: [★★★★] 4/4 * * Example originally created with raylib 5.5, last time updated with raylib 5.6-dev * -* Example contributed by Kirandeep (@Kirandeep-Singh-Khehra) +* Example contributed by Kirandeep (@Kirandeep-Singh-Khehra) and reviewed by Ramon Santamaria (@raysan5) +* +* WARNING: GPU skinning must be enabled in raylib with a compilation flag, +* if not enabled, CPU skinning will be used instead * * 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) 2024 Kirandeep (@Kirandeep-Singh-Khehra) -* -* Note: Due to limitations in the Apple OpenGL driver, this feature does not work on MacOS -* Note: This example uses CPU for updating meshes. -* For GPU skinning see comments with 'INFO:'. +* Copyright (c) 2024-2026 Kirandeep (@Kirandeep-Singh-Khehra) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/models/models_animation_timing.c b/examples/models/models_animation_timing.c index d845e3a9c..097e19225 100644 --- a/examples/models/models_animation_timing.c +++ b/examples/models/models_animation_timing.c @@ -2,7 +2,7 @@ * * raylib [models] example - animation timing * -* Example complexity rating: [★★☆☆] 2/4 +* Example complexity rating: [★★★☆] 3/4 * * Example originally created with raylib 5.6, last time updated with raylib 5.6 * From 4ebe7d62159257cae48d47afd4d8ce2ecf0afb6e Mon Sep 17 00:00:00 2001 From: Krzysztof Szenk Date: Tue, 24 Feb 2026 12:56:16 +0100 Subject: [PATCH 080/319] win32 clipboard: fix for BI_ALPHABITFIELDS narrow support (#5586) * win32 clipbaord: fix for BI_ALPHABITFIELDS narrow support * Define BI_ALPHABITFIELDS even if wingdi headers are already included since BI_ALPHABITFIELDS is not always defined there --- src/external/win32_clipboard.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/external/win32_clipboard.h b/src/external/win32_clipboard.h index 1cb05457d..c85b3882a 100644 --- a/src/external/win32_clipboard.h +++ b/src/external/win32_clipboard.h @@ -178,7 +178,9 @@ typedef struct tagRGBQUAD { #define BI_CMYK 0x000B #define BI_CMYKRLE8 0x000C #define BI_CMYKRLE4 0x000D +#endif +#ifndef BI_ALPHABITFIELDS // Bitmap not compressed and that the color table consists of four DWORD color masks, // that specify the red, green, blue, and alpha components of each pixel #define BI_ALPHABITFIELDS 0x0006 @@ -317,7 +319,7 @@ static int GetPixelDataOffset(BITMAPINFOHEADER bih) // If (bih.biCompression == BI_RGB) no need to be offset more if (bih.biCompression == BI_BITFIELDS) offset += 3*rgbaSize; - else if (bih.biCompression == BI_ALPHABITFIELDS) offset += 4*rgbaSize; // Not widely supported, but valid + else if (bih.biCompression == BI_ALPHABITFIELDS) offset += 4 * rgbaSize; // Not widely supported, but valid } } From ad82393bb8a621ef2604e8261755ca0ac37c1a3e Mon Sep 17 00:00:00 2001 From: Borin Ouch Date: Wed, 25 Feb 2026 03:35:34 -0800 Subject: [PATCH 081/319] Fix reversed window blur/focus logic for web (#5590) --- src/platforms/rcore_web.c | 4 ++-- src/platforms/rcore_web_emscripten.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 5430cf5a9..c33e72682 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -1482,8 +1482,8 @@ static void WindowIconifyCallback(GLFWwindow *window, int iconified) // GLFW3: Called on windows get/lose focus static void WindowFocusCallback(GLFWwindow *window, int focused) { - if (focused) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // The window was focused - else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // The window lost focus + if (focused) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // The window was focused + else FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // The window lost focus } // GLFW3: Called on file-drop over the window diff --git a/src/platforms/rcore_web_emscripten.c b/src/platforms/rcore_web_emscripten.c index 13e685939..b6a0690a6 100644 --- a/src/platforms/rcore_web_emscripten.c +++ b/src/platforms/rcore_web_emscripten.c @@ -1321,8 +1321,8 @@ static EM_BOOL EmscriptenFocusCallback(int eventType, const EmscriptenFocusEvent switch (eventType) { - case EMSCRIPTEN_EVENT_BLUR: FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); break; // The canvas lost focus - case EMSCRIPTEN_EVENT_FOCUS: FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); break; + case EMSCRIPTEN_EVENT_BLUR: FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); break; // The canvas lost focus + case EMSCRIPTEN_EVENT_FOCUS: FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); break; default: consumed = 0; break; } From ecaa1d3d189e02ac251cc644918db2655463f6e1 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 25 Feb 2026 23:22:48 +0100 Subject: [PATCH 082/319] REVIEWED: Attribute name and location, for consistency --- src/rcore.c | 2 +- src/rlgl.h | 30 +++++++++++++----------------- src/rmodels.c | 40 ++++++++++++++++++++-------------------- 3 files changed, 34 insertions(+), 38 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 1090cfc9c..4c7854921 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1305,7 +1305,7 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode) shader.locs[SHADER_LOC_VERTEX_COLOR] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR); shader.locs[SHADER_LOC_VERTEX_BONEIDS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES); shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS); - shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORMS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORMS); + shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORM] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORM); // Get handles to GLSL uniform locations (vertex shader) shader.locs[SHADER_LOC_MATRIX_MVP] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MVP); diff --git a/src/rlgl.h b/src/rlgl.h index 67817a3c9..cb618d94a 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -345,16 +345,14 @@ #ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES 6 #endif -#ifdef SUPPORT_GPU_SKINNING - #ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES - #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES 7 - #endif - #ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS - #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8 - #endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES + #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES 7 #endif -#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORMS - #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORMS 9 +#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS + #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8 +#endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORM + #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORM 9 #endif //---------------------------------------------------------------------------------- @@ -1018,8 +1016,8 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES #define RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES "boneMatrices" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEMATRICES #endif -#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORMS - #define RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORMS "instanceTransform" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORMS +#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORM + #define RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORM "instanceTransform" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORM #endif #ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_MVP @@ -4335,20 +4333,18 @@ unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId) glAttachShader(programId, vShaderId); glAttachShader(programId, fShaderId); - // NOTE: Default attribute shader locations must be Bound before linking + // Default attribute shader locations must be bound before linking + // NOTE: There is no problem with binding a generic attribute index to an attribute variable name + // that is never used; if some attrib name is no found on the shader, it locations becomes -1 glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION); glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD); glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL); glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR); glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT); glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2); - glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORMS, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORMS); -#ifdef SUPPORT_GPU_SKINNING + glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORM, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORM); glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES); glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS); -#endif - - // NOTE: If some attrib name is no found on the shader, it locations becomes -1 glLinkProgram(programId); diff --git a/src/rmodels.c b/src/rmodels.c index 1649fb8f3..d2bc4f707 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1688,7 +1688,7 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Instancing required variables - float16 *instanceTransforms = NULL; + float16 *instanceTransform = NULL; unsigned int instancesVboId = 0; // Bind shader program @@ -1737,10 +1737,10 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i if (material.shader.locs[SHADER_LOC_MATRIX_PROJECTION] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_PROJECTION], matProjection); // Create instances buffer - instanceTransforms = (float16 *)RL_CALLOC(instances, sizeof(float16)); + instanceTransform = (float16 *)RL_CALLOC(instances, sizeof(float16)); // Fill buffer with instances transformations as float16 arrays - for (int i = 0; i < instances; i++) instanceTransforms[i] = MatrixToFloatV(transforms[i]); + for (int i = 0; i < instances; i++) instanceTransform[i] = MatrixToFloatV(transforms[i]); // Enable mesh VAO to attach new buffer rlEnableVertexArray(mesh.vaoId); @@ -1749,16 +1749,16 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i // It isn't clear which would be reliably faster in all cases and on all platforms, // anecdotally glMapBuffer() seems very slow (syncs) while glBufferSubData() seems // no faster, since all the transform matrices are transferred anyway - instancesVboId = rlLoadVertexBuffer(instanceTransforms, instances*sizeof(float16), false); + instancesVboId = rlLoadVertexBuffer(instanceTransform, instances*sizeof(float16), false); - // Instances transformation matrices are sent to shader attribute location: SHADER_LOC_VERTEX_INSTANCETRANSFORMS - if (material.shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORMS] != -1) + // Instances transformation matrices are sent to shader attribute location: SHADER_LOC_VERTEX_INSTANCETRANSFORM + if (material.shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORM] != -1) { for (unsigned int i = 0; i < 4; i++) { - rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORMS] + i); - rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORMS] + i, 4, RL_FLOAT, 0, sizeof(Matrix), i*sizeof(Vector4)); - rlSetVertexAttributeDivisor(material.shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORMS] + i, 1); + rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORM] + i); + rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORM] + i, 4, RL_FLOAT, 0, sizeof(Matrix), i*sizeof(Vector4)); + rlSetVertexAttributeDivisor(material.shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORM] + i, 1); } } @@ -1918,7 +1918,7 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i // Remove instance transforms buffer rlUnloadVertexBuffer(instancesVboId); - RL_FREE(instanceTransforms); + RL_FREE(instanceTransform); #endif } @@ -4404,11 +4404,10 @@ static void BuildPoseFromParentJoints(BoneInfo *bones, int boneCount, Transform #if defined(SUPPORT_FILEFORMAT_OBJ) // Load OBJ mesh data -// -// Keep the following information in mind when reading this +// Notes to keep in mind: // - A mesh is created for every material present in the obj file -// - the model.meshCount is therefore the materialCount returned from tinyobj -// - the mesh is automatically triangulated by tinyobj +// - The model.meshCount is therefore the materialCount returned from tinyobj +// - The mesh is automatically triangulated by tinyobj static Model LoadOBJ(const char *fileName) { tinyobj_attrib_t objAttributes = { 0 }; @@ -4555,7 +4554,8 @@ static Model LoadOBJ(const char *fileName) model.meshes[i].texcoords = (float *)MemAlloc(sizeof(float)*vertexCount*2); model.meshes[i].colors = (unsigned char *)MemAlloc(sizeof(unsigned char)*vertexCount*4); #else - if (objAttributes.texcoords != NULL && objAttributes.num_texcoords > 0) model.meshes[i].texcoords = (float *)MemAlloc(sizeof(float)*vertexCount*2); + if (objAttributes.texcoords != NULL && objAttributes.num_texcoords > 0) + model.meshes[i].texcoords = (float *)MemAlloc(sizeof(float)*vertexCount*2); else model.meshes[i].texcoords = NULL; model.meshes[i].colors = NULL; #endif @@ -4657,12 +4657,12 @@ static Model LoadOBJ(const char *fileName) // Load IQM mesh data static Model LoadIQM(const char *fileName) { - #define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number - #define IQM_VERSION 2 // only IQM version 2 supported + #define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number + #define IQM_VERSION 2 // Only IQM version 2 supported - #define BONE_NAME_LENGTH 32 // BoneInfo name string length - #define MESH_NAME_LENGTH 32 // Mesh name string length - #define MATERIAL_NAME_LENGTH 32 // Material name string length + #define BONE_NAME_LENGTH 32 // BoneInfo name string length + #define MESH_NAME_LENGTH 32 // Mesh name string length + #define MATERIAL_NAME_LENGTH 32 // Material name string length int dataSize = 0; unsigned char *fileData = LoadFileData(fileName, &dataSize); From 006216bfcbe4423ce36710eabb8a94e677c7ce4f Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Wed, 25 Feb 2026 17:35:15 -0600 Subject: [PATCH 083/319] [raylib.h] renamed SHADER_LOC_VERTEX_INSTANCETRANSFORMS (#5592) * renamed SHADER_LOC_VERTEX_INSTANCETRANSFORMS to SHADER_LOC_VERTEX_INSTANCETRANSFORM * rlparser: update raylib_api.* by CI --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/raylib.h | 2 +- tools/rlparser/output/raylib_api.json | 2 +- tools/rlparser/output/raylib_api.lua | 2 +- tools/rlparser/output/raylib_api.txt | 2 +- tools/rlparser/output/raylib_api.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index bd64bbe6f..afe3aa2ce 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -814,7 +814,7 @@ typedef enum { SHADER_LOC_VERTEX_BONEIDS, // Shader location: vertex attribute: bone indices SHADER_LOC_VERTEX_BONEWEIGHTS, // Shader location: vertex attribute: bone weights SHADER_LOC_MATRIX_BONETRANSFORMS, // Shader location: matrix attribute: bone transforms (animation) - SHADER_LOC_VERTEX_INSTANCETRANSFORMS // Shader location: vertex attribute: instance transforms + SHADER_LOC_VERTEX_INSTANCETRANSFORM // Shader location: vertex attribute: instance transforms } ShaderLocationIndex; #define SHADER_LOC_MAP_DIFFUSE SHADER_LOC_MAP_ALBEDO diff --git a/tools/rlparser/output/raylib_api.json b/tools/rlparser/output/raylib_api.json index 3f56434cd..7785549f0 100644 --- a/tools/rlparser/output/raylib_api.json +++ b/tools/rlparser/output/raylib_api.json @@ -2556,7 +2556,7 @@ "description": "Shader location: matrix attribute: bone transforms (animation)" }, { - "name": "SHADER_LOC_VERTEX_INSTANCETRANSFORMS", + "name": "SHADER_LOC_VERTEX_INSTANCETRANSFORM", "value": 29, "description": "Shader location: vertex attribute: instance transforms" } diff --git a/tools/rlparser/output/raylib_api.lua b/tools/rlparser/output/raylib_api.lua index 85edb02f1..ffbb51d94 100644 --- a/tools/rlparser/output/raylib_api.lua +++ b/tools/rlparser/output/raylib_api.lua @@ -2556,7 +2556,7 @@ return { description = "Shader location: matrix attribute: bone transforms (animation)" }, { - name = "SHADER_LOC_VERTEX_INSTANCETRANSFORMS", + name = "SHADER_LOC_VERTEX_INSTANCETRANSFORM", value = 29, description = "Shader location: vertex attribute: instance transforms" } diff --git a/tools/rlparser/output/raylib_api.txt b/tools/rlparser/output/raylib_api.txt index da57f2cfd..c42f5ddde 100644 --- a/tools/rlparser/output/raylib_api.txt +++ b/tools/rlparser/output/raylib_api.txt @@ -834,7 +834,7 @@ Enum 09: ShaderLocationIndex (30 values) Value[SHADER_LOC_VERTEX_BONEIDS]: 26 Value[SHADER_LOC_VERTEX_BONEWEIGHTS]: 27 Value[SHADER_LOC_MATRIX_BONETRANSFORMS]: 28 - Value[SHADER_LOC_VERTEX_INSTANCETRANSFORMS]: 29 + Value[SHADER_LOC_VERTEX_INSTANCETRANSFORM]: 29 Enum 10: ShaderUniformDataType (13 values) Name: ShaderUniformDataType Description: Shader uniform data type diff --git a/tools/rlparser/output/raylib_api.xml b/tools/rlparser/output/raylib_api.xml index 6c83dc5df..ca20800e8 100644 --- a/tools/rlparser/output/raylib_api.xml +++ b/tools/rlparser/output/raylib_api.xml @@ -540,7 +540,7 @@ - + From b57526d71eb2ebb55a0f477801249b5d5a07d9bb Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Wed, 25 Feb 2026 17:38:37 -0600 Subject: [PATCH 084/319] update makefile and such (#5591) --- .gitignore | 5 ++ examples/Makefile | 15 +++++- src/Makefile | 86 ++++++++++++++++++++++++++++-- src/external/RGFW/RGFW.h | 14 ++--- src/platforms/rcore_desktop_rgfw.c | 1 + 5 files changed, 110 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index f7b2cccf6..00a751a73 100644 --- a/.gitignore +++ b/.gitignore @@ -132,3 +132,8 @@ tools/rexm/rexm # CI emsdk-cache/ raylib.com/ + +# Wayland files +src/*protocol.h +src/*protocol-code.h +src/*protocol-code.c diff --git a/examples/Makefile b/examples/Makefile index ff989a9dd..632cdb157 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -95,6 +95,11 @@ USE_EXTERNAL_GLFW ?= FALSE GLFW_LINUX_ENABLE_WAYLAND ?= FALSE GLFW_LINUX_ENABLE_X11 ?= TRUE +# Enable support for X11 by default on Linux when using GLFW +# NOTE: Wayland is disabled by default, only enable if you are sure +RGFW_LINUX_ENABLE_WAYLAND ?= FALSE +RGFW_LINUX_ENABLE_X11 ?= TRUE + # 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 @@ -475,7 +480,15 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW) # 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 + LDLIBS = -lraylib -lm + + ifeq ($(RGFW_LINUX_ENABLE_X11),TRUE) + LDLIBS += -lGL -lX11 -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor -lpthread -ldl -lrt + endif + + ifeq ($(RGFW_LINUX_ENABLE_WAYLAND),TRUE) + LDLIBS += -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon + endif # Explicit link to libc ifeq ($(RAYLIB_LIBTYPE),SHARED) diff --git a/src/Makefile b/src/Makefile index b4ea8f0a9..8f107a749 100644 --- a/src/Makefile +++ b/src/Makefile @@ -115,6 +115,11 @@ USE_EXTERNAL_GLFW ?= FALSE GLFW_LINUX_ENABLE_WAYLAND ?= FALSE GLFW_LINUX_ENABLE_X11 ?= TRUE +# Enable support for X11 by default on Linux when using GLFW +# NOTE: Wayland is disabled by default, only enable if you are sure +RGFW_LINUX_ENABLE_WAYLAND ?= FALSE +RGFW_LINUX_ENABLE_X11 ?= TRUE + # 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 @@ -289,6 +294,17 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) CC = clang endif endif +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW) + ifeq ($(PLATFORM_OS),OSX) + # OSX default compiler + CC = clang + GLFW_OSX = -x objective-c + endif + ifeq ($(PLATFORM_OS),BSD) + # FreeBSD, OpenBSD, NetBSD, DragonFly default compiler + CC = clang + endif +endif ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) ifeq ($(USE_RPI_CROSSCOMPILER),TRUE) # Define RPI cross-compiler @@ -450,7 +466,6 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) endif ifeq ($(GLFW_LINUX_ENABLE_WAYLAND),TRUE) CFLAGS += -D_GLFW_WAYLAND - LDFLAGS += $(shell pkg-config wayland-client wayland-cursor wayland-egl xkbcommon --libs) WL_PROTOCOLS_DIR := external/glfw/deps/wayland @@ -472,6 +487,33 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) endif endif endif +# Use Wayland display on Linux desktop +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW) + ifeq ($(PLATFORM_OS), LINUX) + ifeq ($(RGFW_LINUX_ENABLE_X11),TRUE) + CFLAGS += -DRGFW_X11 -DRGFW_UNIX + endif + ifeq ($(RGFW_LINUX_ENABLE_WAYLAND),TRUE) + CFLAGS += -DRGFW_WAYLAND -DEGLAPIENTRY= + + WL_PROTOCOLS_DIR := external/RGFW/deps/wayland + + wl_generate = \ + $(eval protocol=$(1)) \ + $(eval basename=$(2)) \ + $(shell wayland-scanner client-header $(protocol) $(RAYLIB_SRC_PATH)/$(basename).h) \ + $(shell wayland-scanner private-code $(protocol) $(RAYLIB_SRC_PATH)/$(basename)-code.c) + + $(call wl_generate, $(WL_PROTOCOLS_DIR)/pointer-constraints-unstable-v1.xml, pointer-constraints-unstable-v1-client-protocol) + $(call wl_generate, $(WL_PROTOCOLS_DIR)/pointer-warp-v1.xml, pointer-warp-v1-client-protocol) + $(call wl_generate, $(WL_PROTOCOLS_DIR)/relative-pointer-unstable-v1.xml, relative-pointer-unstable-v1-client-protocol) + $(call wl_generate, $(WL_PROTOCOLS_DIR)/xdg-decoration-unstable-v1.xml, xdg-decoration-unstable-v1-client-protocol) + $(call wl_generate, $(WL_PROTOCOLS_DIR)/xdg-output-unstable-v1.xml, xdg-output-unstable-v1-client-protocol) + $(call wl_generate, $(WL_PROTOCOLS_DIR)/xdg-shell.xml, xdg-shell-client-protocol) + $(call wl_generate, $(WL_PROTOCOLS_DIR)/xdg-toplevel-icon-v1.xml, xdg-toplevel-icon-v1-client-protocol) + endif + endif +endif CFLAGS += $(CUSTOM_CFLAGS) @@ -583,6 +625,10 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(GLFW_LINUX_ENABLE_X11),TRUE) LDLIBS += -lX11 endif + + ifeq ($(GLFW_LINUX_ENABLE_WAYLAND),TRUE) + LDFLAGS += $(shell pkg-config wayland-client wayland-cursor wayland-egl xkbcommon --libs) + endif # TODO: On ARM 32bit arch, miniaudio requires atomics library #LDLIBS += -latomic endif @@ -618,7 +664,15 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW) ifeq ($(PLATFORM_OS),LINUX) # Libraries for Debian GNU/Linux desktop compipling # NOTE: Required packages: libegl1-mesa-dev - LDLIBS = -lGL -lX11 -lXrandr -lXinerama -lXi -lXcursor -lm -lpthread -ldl -lrt + LDLIBS = -lm + + ifeq ($(RGFW_LINUX_ENABLE_X11),TRUE) + LDLIBS += -lGL -lX11 -lXrandr -lXinerama -lXi -lXcursor -lpthread -ldl -lrt + endif + + ifeq ($(RGFW_LINUX_ENABLE_WAYLAND),TRUE) + LDFLAGS += $(shell pkg-config wayland-client wayland-cursor wayland-egl xkbcommon --libs) + endif # Explicit link to libc ifeq ($(RAYLIB_LIBTYPE),SHARED) @@ -665,6 +719,21 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) OBJS += rglfw.o endif endif +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW) + ifeq ($(RGFW_LINUX_ENABLE_WAYLAND),TRUE) + PROTOCOL_CODE_FILES = \ + pointer-constraints-unstable-v1-client-protocol-code \ + pointer-warp-v1-client-protocol-code \ + relative-pointer-unstable-v1-client-protocol-code \ + xdg-decoration-unstable-v1-client-protocol-code \ + xdg-output-unstable-v1-client-protocol-code \ + xdg-shell-client-protocol-code \ + xdg-toplevel-icon-v1-client-protocol-code + + PROTO_OBJS = $(addsuffix .o, $(PROTOCOL_CODE_FILES)) + OBJS += $(PROTO_OBJS) + endif +endif ifeq ($(RAYLIB_MODULE_MODELS),TRUE) OBJS += rmodels.o endif @@ -801,6 +870,17 @@ endif android_native_app_glue.o : $(NATIVE_APP_GLUE)/android_native_app_glue.c $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) +# Compile Wayland files +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW) + ifeq ($(RGFW_LINUX_ENABLE_WAYLAND),TRUE) + %-client-protocol-code.o: %-client-protocol-code.c + $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) + + %-client-protocol-code.h: external/RGFW/deps/wayland/%.xml + wayland-scanner private-code $< $@ + endif +endif + # Install generated and needed files to desired directories. # On GNU/Linux and BSDs, there are some standard directories that contain extra # libraries and header files. These directories (often /usr/local/lib and @@ -885,7 +965,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).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 $(RAYLIB_RELEASE_PATH)/*-protocol-code.c ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) rm -fv $(NATIVE_APP_GLUE)/android_native_app_glue.o endif diff --git a/src/external/RGFW/RGFW.h b/src/external/RGFW/RGFW.h index 6e59c6ca0..427db0168 100644 --- a/src/external/RGFW/RGFW.h +++ b/src/external/RGFW/RGFW.h @@ -8667,13 +8667,13 @@ struct wl_surface* RGFW_window_getWindow_Wayland(RGFW_window* win) { return win- /* wayland global garbage (wayland bad, X11 is fine (ish) (not really)) */ -#include "xdg-shell.h" -#include "xdg-toplevel-icon-v1.h" -#include "xdg-decoration-unstable-v1.h" -#include "relative-pointer-unstable-v1.h" -#include "pointer-constraints-unstable-v1.h" -#include "xdg-output-unstable-v1.h" -#include "pointer-warp-v1.h" +#include "xdg-shell-client-protocol.h" +#include "xdg-toplevel-icon-v1-client-protocol.h" +#include "xdg-decoration-unstable-v1-client-protocol.h" +#include "relative-pointer-unstable-v1-client-protocol.h" +#include "pointer-constraints-unstable-v1-client-protocol.h" +#include "xdg-output-unstable-v1-client-protocol.h" +#include "pointer-warp-v1-client-protocol.h" void RGFW_toggleWaylandMaximized(RGFW_window* win, RGFW_bool maximized); diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index e04097e98..d31f41e4d 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -1691,6 +1691,7 @@ int InitPlatform(void) // Close platform void ClosePlatform(void) { + mg_gamepads_free(&platform.minigamepad); RGFW_window_close(platform.window); } From 304e489eddbd8881b5c31952424e079bfe1eb0de Mon Sep 17 00:00:00 2001 From: ghera Date: Thu, 26 Feb 2026 00:44:00 +0100 Subject: [PATCH 085/319] ANDROID: Fix broken LoadMusicStream due to missing fopen redirect in raudio.c (#5589) * ANDROID: Fix broken LoadMusicStream due to missing fopen macro override in raudio * ANDROID: Add missing forward declaration before fopen macro override in raudio --- src/raudio.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/raudio.c b/src/raudio.c index 0f8b5101f..fcdb27702 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -180,6 +180,11 @@ typedef struct tagBITMAPINFOHEADER { #include // Required for: FILE, fopen(), fclose(), fread() #include // Required for: strcmp() [Used in IsFileExtension(), LoadWaveFromMemory(), LoadMusicStreamFromMemory()] +#if defined(PLATFORM_ANDROID) + FILE *android_fopen(const char *fileName, const char *mode); + #define fopen(name, mode) android_fopen(name, mode) +#endif + #if defined(RAUDIO_STANDALONE) #ifndef TRACELOG #define TRACELOG(level, ...) printf(__VA_ARGS__) From 5361265a7d5d1ef662077350ef3db9747333b95b Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Feb 2026 08:19:28 +0100 Subject: [PATCH 086/319] WARNING: BREAKING: REDESIGNED: raylib build features config system #4411 #4554 Redesigned to support disabling features on compilation with `-DSUPPORT_FEATURE=0` REMOVED: `SUPPORT_DEFAULT_FONT`, always supported REMOVED: `SUPPORT_IMAGE_MANIPULATION `, always supported REMOVED: `SUPPORT_TEXT_MANIPULATION`, always supported REDESIGNED: `SUPPORT_FONT_ATLAS_WHITE_REC` to `FONT_ATLAS_CORNER_REC_SIZE` REVIEWED: Config values (other than 0-1) are already defined on respective modules Other config tweaks here and there --- src/config.h | 498 +++++++++++++++------------ src/external/rlsw.h | 4 +- src/platforms/rcore_android.c | 10 +- src/platforms/rcore_desktop_glfw.c | 16 +- src/platforms/rcore_desktop_sdl.c | 11 +- src/platforms/rcore_drm.c | 20 +- src/platforms/rcore_memory.c | 2 +- src/platforms/rcore_web.c | 18 +- src/platforms/rcore_web_emscripten.c | 10 +- src/raudio.c | 114 +++--- src/raymath.h | 8 +- src/rcore.c | 198 +++++------ src/rlgl.h | 47 +-- src/rmodels.c | 84 ++--- src/rshapes.c | 30 +- src/rtext.c | 85 ++--- src/rtextures.c | 137 ++++---- 17 files changed, 646 insertions(+), 646 deletions(-) diff --git a/src/config.h b/src/config.h index 5fb4f5545..1c45a1170 100644 --- a/src/config.h +++ b/src/config.h @@ -2,7 +2,9 @@ * * raylib configuration flags * -* This file defines all the configuration flags for the different raylib modules +* This file defines the configuration flags for different raylib features per-module +* +* NOTE: Additional values are configured per-module and can be set on compile time * * LICENSE: zlib/libpng * @@ -28,288 +30,342 @@ #ifndef CONFIG_H #define CONFIG_H +#if !defined(EXTERNAL_CONFIG_FLAGS) + //------------------------------------------------------------------------------------ // Module selection - Some modules could be avoided // Mandatory modules: rcore, rlgl //------------------------------------------------------------------------------------ -#if !defined(EXTERNAL_CONFIG_FLAGS) - #define SUPPORT_MODULE_RSHAPES 1 - #define SUPPORT_MODULE_RTEXTURES 1 - #define SUPPORT_MODULE_RTEXT 1 // WARNING: It requires SUPPORT_MODULE_RTEXTURES to load sprite font textures - #define SUPPORT_MODULE_RMODELS 1 - #define SUPPORT_MODULE_RAUDIO 1 +#ifndef SUPPORT_MODULE_RSHAPES + #define SUPPORT_MODULE_RSHAPES 1 +#endif +#ifndef SUPPORT_MODULE_RTEXTURES + #define SUPPORT_MODULE_RTEXTURES 1 +#endif +#ifndef SUPPORT_MODULE_RTEXT + #define SUPPORT_MODULE_RTEXT 1 // WARNING: It requires SUPPORT_MODULE_RTEXTURES to load sprite font textures +#endif +#ifndef SUPPORT_MODULE_RMODELS + #define SUPPORT_MODULE_RMODELS 1 +#endif +#ifndef SUPPORT_MODULE_RAUDIO + #define SUPPORT_MODULE_RAUDIO 1 #endif //------------------------------------------------------------------------------------ // Module: rcore - Configuration Flags //------------------------------------------------------------------------------------ -#if !defined(EXTERNAL_CONFIG_FLAGS) -// Standard file io library (stdio.h) included -#define SUPPORT_STANDARD_FILEIO 1 -// Show TRACELOG() output messages -#define SUPPORT_TRACELOG 1 -// Camera module is included (rcamera.h) and multiple predefined cameras are available: free, 1st/3rd person, orbital -#define SUPPORT_CAMERA_SYSTEM 1 -// Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag -#define SUPPORT_GESTURES_SYSTEM 1 -// Include pseudo-random numbers generator (rprand.h), based on Xoshiro128** and SplitMix64 -#define SUPPORT_RPRAND_GENERATOR 1 -// Mouse gestures are directly mapped like touches and processed by gestures system -#define SUPPORT_MOUSE_GESTURES 1 -// Reconfigure standard input to receive key inputs, works with SSH connection -#define SUPPORT_SSH_KEYBOARD_RPI 1 -// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions -// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often -#define SUPPORT_WINMM_HIGHRES_TIMER 1 -// Use busy wait loop for timing sync, if not defined, a high-resolution timer is set up and used -//#define SUPPORT_BUSY_WAIT_LOOP 1 -// Use a partial-busy wait loop, in this case frame sleeps for most of the time, but then runs a busy loop at the end for accuracy -#define SUPPORT_PARTIALBUSY_WAIT_LOOP 1 -// Allow automatic screen capture of current screen pressing F12, defined in KeyCallback() -// WARNING: It also requires SUPPORT_IMAGE_EXPORT and SUPPORT_FILEFORMAT_PNG flags -#define SUPPORT_SCREEN_CAPTURE 1 -// Support CompressData() and DecompressData() functions -#define SUPPORT_COMPRESSION_API 1 -// Support automatic generated events, loading and recording of those events when required -#define SUPPORT_AUTOMATION_EVENTS 1 -// Support custom frame control, only for advanced users -// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents() -// Enabling this flag allows manual control of the frame processes, use at your own risk -//#define SUPPORT_CUSTOM_FRAME_CONTROL 1 -// Support for clipboard image loading -// NOTE: Only working on SDL3, GLFW (Windows) and RGFW (Windows) -#define SUPPORT_CLIPBOARD_IMAGE 1 +#ifndef SUPPORT_TRACELOG + // Show TRACELOG() output messages + #define SUPPORT_TRACELOG 1 +#endif +#ifndef SUPPORT_CAMERA_SYSTEM + // Camera module is included (rcamera.h) and multiple predefined + // cameras are available: free, 1st/3rd person, orbital + #define SUPPORT_CAMERA_SYSTEM 1 +#endif +#ifndef SUPPORT_GESTURES_SYSTEM + // Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag + #define SUPPORT_GESTURES_SYSTEM 1 +#endif +#ifndef SUPPORT_RPRAND_GENERATOR + // Include pseudo-random numbers generator (rprand.h), based on Xoshiro128** and SplitMix64 + #define SUPPORT_RPRAND_GENERATOR 1 +#endif +#ifndef SUPPORT_MOUSE_GESTURES + // Mouse gestures are directly mapped like touches and processed by gestures system + #define SUPPORT_MOUSE_GESTURES 1 +#endif +#ifndef SUPPORT_SSH_KEYBOARD_RPI + // Reconfigure standard input to receive key inputs, works with SSH connection + #define SUPPORT_SSH_KEYBOARD_RPI 1 +#endif +#ifndef SUPPORT_WINMM_HIGHRES_TIMER + // Setting a higher resolution can improve the accuracy of time-out intervals in wait functions + // However, it can also reduce overall system performance, because the thread scheduler switches tasks more often + #define SUPPORT_WINMM_HIGHRES_TIMER 1 +#endif +#if !defined(SUPPORT_BUSY_WAIT_LOOP) && !SUPPORT_PARTIALBUSY_WAIT_LOOP + // Use busy wait loop for timing sync, if not defined, a high-resolution timer is set up and used + #define SUPPORT_BUSY_WAIT_LOOP 0 // Disabled by default +#endif +#if !defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) && !SUPPORT_BUSY_WAIT_LOOP + // Use a partial-busy wait loop, in this case frame sleeps for most of the time, + // but then runs a busy loop at the end for accuracy + #define SUPPORT_PARTIALBUSY_WAIT_LOOP 1 +#endif +#ifndef SUPPORT_SCREEN_CAPTURE + // Allow automatic screen capture of current screen pressing F12, defined in KeyCallback() + // WARNING: It requires SUPPORT_FILEFORMAT_PNG flag + #define SUPPORT_SCREEN_CAPTURE 1 +#endif +#ifndef SUPPORT_COMPRESSION_API + // Support CompressData() and DecompressData() functions + #define SUPPORT_COMPRESSION_API 1 +#endif +#ifndef SUPPORT_AUTOMATION_EVENTS + // Support automatic generated events, loading and recording of those events when required + #define SUPPORT_AUTOMATION_EVENTS 1 +#endif +#ifndef SUPPORT_CUSTOM_FRAME_CONTROL + // Support custom frame control, only for advanced users + // By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents() + // Enabling this flag allows manual control of the frame processes, use at your own risk + #define SUPPORT_CUSTOM_FRAME_CONTROL 0 // Disabled by default +#endif +#ifndef SUPPORT_CLIPBOARD_IMAGE + // Support for clipboard image loading + // NOTE: Only working on SDL3, GLFW (Windows) and RGFW (Windows) + // WARNING: It requires support for some additional flags: + // - SUPPORT_MODULE_RTEXTURES + // - SUPPORT_FILEFORMAT_BMP (Windows clipboard) + // - SUPPORT_FILEFORMAT_PNG (Wayland clipboard) + // - SUPPORT_FILEFORMAT_JPG + #define SUPPORT_CLIPBOARD_IMAGE 1 #endif -// NOTE: Clipboard image loading requires support for some image file formats -// TODO: Those defines should probably be removed from here, letting the user manage them -#if defined(SUPPORT_CLIPBOARD_IMAGE) - #ifndef SUPPORT_MODULE_RTEXTURES - #define SUPPORT_MODULE_RTEXTURES 1 - #endif - #ifndef STBI_REQUIRED - #define STBI_REQUIRED - #endif - #ifndef SUPPORT_FILEFORMAT_BMP // For clipboard image on Windows - #define SUPPORT_FILEFORMAT_BMP 1 - #endif - #ifndef SUPPORT_FILEFORMAT_PNG // Wayland uses png for prints, at least it was on 22 LTS ubuntu - #define SUPPORT_FILEFORMAT_PNG 1 - #endif - #ifndef SUPPORT_FILEFORMAT_JPG - #define SUPPORT_FILEFORMAT_JPG 1 - #endif -#endif - -#if defined(SUPPORT_TRACELOG) +#if SUPPORT_TRACELOG #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) #else #define TRACELOG(level, ...) (void)0 #endif // rcore: Configuration values +// NOTE: Below values are alread defined inside [rcore.c] so there is no need to be +// redefined here, in case it must be done, just uncomment the required line and update +// the value; it can also be done on compilation with -DVALUE_TO_REDEFINE=128 +//------------------------------------------------------------------------------------ +//#define MAX_TRACELOG_MSG_LENGTH 256 // Max length of one trace-log message +//#define MAX_FILEPATH_CAPACITY 8192 // Maximum file paths capacity +//#define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value) +//#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 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 10 // Maximum number of touch points supported +//#define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue +//#define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue +//#define MAX_DECOMPRESSION_SIZE 64 // Max size allocated for decompression in MB +//#define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record //------------------------------------------------------------------------------------ -#define MAX_TRACELOG_MSG_LENGTH 256 // Max length of one trace-log message -#define MAX_FILEPATH_CAPACITY 8192 // Maximum file paths capacity -#define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value) - -#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 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 10 // Maximum number of touch points supported -#define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue -#define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue - -#define MAX_DECOMPRESSION_SIZE 64 // Max size allocated for decompression in MB - -#define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record //------------------------------------------------------------------------------------ // Module: rlgl - Configuration values //------------------------------------------------------------------------------------ -#if !defined(EXTERNAL_CONFIG_FLAGS) -//#define SUPPORT_GPU_SKINNING 1 // GPU skinning, comment if your GPU does not support more than 8 VBOs - -// Enable OpenGL Debug Context (only available on OpenGL 4.3) -//#define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT 1 // OpenGL debug context requested - -// Show OpenGL extensions and capabilities detailed logs on init -//#define RLGL_SHOW_GL_DETAILS_INFO 1 // Show OpenGL detailed info on initialization (limits and extensions) +#ifndef RLGL_ENABLE_OPENGL_DEBUG_CONTEXT + // Request OpenGL debug context (only available on OpenGL 4.3) + #define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT 0 +#endif +#ifndef RLGL_SHOW_GL_DETAILS_INFO + // Show OpenGL detailed info on initialization, + // supported GL extensions and GL capabilities + #define RLGL_SHOW_GL_DETAILS_INFO 0 #endif -//#define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 4096 // Default internal render batch elements limits -#define RL_DEFAULT_BATCH_BUFFERS 1 // Default number of batch buffers (multi-buffering) -#define RL_DEFAULT_BATCH_DRAWCALLS 256 // Default number of batch draw calls (by state changes: mode, texture) -#define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of textures units that can be activated on batch drawing (SetShaderValueTexture()) - -#define RL_MAX_MATRIX_STACK_SIZE 32 // Maximum size of internal Matrix stack - -#define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported - -#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 +// rlgl: Configuration values +// NOTE: Below values are alread defined inside [rlgl.h] so there is no need to be +// redefined here, in case it must be done, just uncomment the required line and update +// the value; it can also be done on compilation with -DVALUE_TO_REDEFINE=128 +//------------------------------------------------------------------------------------ +//#define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 4096 // Default internal render batch elements limits +//#define RL_DEFAULT_BATCH_BUFFERS 1 // Default number of batch buffers (multi-buffering) +//#define RL_DEFAULT_BATCH_DRAWCALLS 256 // Default number of batch draw calls (by state changes: mode, texture) +//#define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of textures units that can be activated on batch drawing (SetShaderValueTexture()) +//#define RL_MAX_MATRIX_STACK_SIZE 32 // Maximum size of internal Matrix stack +//#define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported +//#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 -#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD 1 -#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL 2 -#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR 3 -#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT 4 -#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 5 -#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES 6 -#if defined(SUPPORT_GPU_SKINNING) - #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES 7 - #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8 -#endif -#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORMS 9 +//#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION 0 +//#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD 1 +//#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL 2 +//#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR 3 +//#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT 4 +//#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 5 +//#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES 6 +//#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES 7 +//#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8 +//#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORM 9 // Default shader vertex attribute/uniform names to set location points -// NOTE: When a new shader is loaded, locations are tried to be set for convenience, -// if the following names are found in the shader, if not, it's up to the user to set locations -#define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION -#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD -#define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL -#define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR -#define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT -#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 -#define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES "vertexBoneIndices" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES -#define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS - -#define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix -#define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix -#define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix -#define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix -#define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView)) -#define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (tint color, multiplied by texture color) -#define RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES "boneMatrices" // bone matrices -#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0) -#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) +// NOTE: When a new shader is loaded, locations are tried to be set for convenience, looking for the names defined here +// In case custom shader names are used, it's up to the user to set locations with GetShaderLocation*() functions +//#define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION +//#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD +//#define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL +//#define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR +//#define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT +//#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 +//#define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES "vertexBoneIndices" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES +//#define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS +//#define RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORM "instanceTransform" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORM +//#define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix +//#define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix +//#define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix +//#define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix +//#define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView)) +//#define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (tint color, multiplied by texture color) +//#define RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES "boneMatrices" // bone matrices +//#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0) +//#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 //------------------------------------------------------------------------------------ -#if !defined(EXTERNAL_CONFIG_FLAGS) -// Use QUADS instead of TRIANGLES for drawing when possible -// Some lines-based shapes could still use lines -#define SUPPORT_QUADS_DRAW_MODE 1 +#ifndef SUPPORT_QUADS_DRAW_MODE + // Use QUADS instead of TRIANGLES for drawing when possible + // Some lines-based shapes could still use lines + #define SUPPORT_QUADS_DRAW_MODE 1 #endif -// rshapes: Configuration values -//------------------------------------------------------------------------------------ -#define SPLINE_SEGMENT_DIVISIONS 24 // Spline segments subdivisions - //------------------------------------------------------------------------------------ // Module: rtextures - Configuration Flags //------------------------------------------------------------------------------------ -#if !defined(EXTERNAL_CONFIG_FLAGS) // Selected desired fileformats to be supported for image data loading -#define SUPPORT_FILEFORMAT_PNG 1 -//#define SUPPORT_FILEFORMAT_BMP 1 -//#define SUPPORT_FILEFORMAT_TGA 1 -//#define SUPPORT_FILEFORMAT_JPG 1 -#define SUPPORT_FILEFORMAT_GIF 1 -#define SUPPORT_FILEFORMAT_QOI 1 -//#define SUPPORT_FILEFORMAT_PSD 1 -#define SUPPORT_FILEFORMAT_DDS 1 -//#define SUPPORT_FILEFORMAT_HDR 1 -//#define SUPPORT_FILEFORMAT_PIC 1 -//#define SUPPORT_FILEFORMAT_KTX 1 -//#define SUPPORT_FILEFORMAT_ASTC 1 -//#define SUPPORT_FILEFORMAT_PKM 1 -//#define SUPPORT_FILEFORMAT_PVR 1 +#ifndef SUPPORT_FILEFORMAT_PNG + #define SUPPORT_FILEFORMAT_PNG 1 +#endif +#ifndef SUPPORT_FILEFORMAT_BMP + // NOTE: BMP support required for clipboard images on Windows + #define SUPPORT_FILEFORMAT_BMP 1 +#endif +#ifndef SUPPORT_FILEFORMAT_TGA + #define SUPPORT_FILEFORMAT_TGA 0 // Disabled by default +#endif +#ifndef SUPPORT_FILEFORMAT_JPG + #define SUPPORT_FILEFORMAT_JPG 0 // Disabled by default +#endif +#ifndef SUPPORT_FILEFORMAT_GIF + #define SUPPORT_FILEFORMAT_GIF 1 +#endif +#ifndef SUPPORT_FILEFORMAT_QOI + #define SUPPORT_FILEFORMAT_QOI 1 +#endif +#ifndef SUPPORT_FILEFORMAT_PSD + #define SUPPORT_FILEFORMAT_PSD 0 // Disabled by default +#endif +#ifndef SUPPORT_FILEFORMAT_DDS + #define SUPPORT_FILEFORMAT_DDS 1 +#endif +#ifndef SUPPORT_FILEFORMAT_HDR + #define SUPPORT_FILEFORMAT_HDR 0 // Disabled by default +#endif +#ifndef SUPPORT_FILEFORMAT_PIC + #define SUPPORT_FILEFORMAT_PIC 0 // Disabled by default +#endif +#ifndef SUPPORT_FILEFORMAT_KTX + #define SUPPORT_FILEFORMAT_KTX 0 // Disabled by default +#endif +#ifndef SUPPORT_FILEFORMAT_ASTC + #define SUPPORT_FILEFORMAT_ASTC 0 // Disabled by default +#endif +#ifndef SUPPORT_FILEFORMAT_PKM + #define SUPPORT_FILEFORMAT_PKM 0 // Disabled by default +#endif +#ifndef SUPPORT_FILEFORMAT_PVR + #define SUPPORT_FILEFORMAT_PVR 0 // Disabled by default +#endif -// Support image export functionality (.png, .bmp, .tga, .jpg, .qoi) -#define SUPPORT_IMAGE_EXPORT 1 -// Support procedural image generation functionality (gradient, spot, perlin-noise, cellular) -#define SUPPORT_IMAGE_GENERATION 1 -// Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... -// If not defined, still some functions are supported: ImageFormat(), ImageCrop(), ImageToPOT() -#define SUPPORT_IMAGE_MANIPULATION 1 +#ifndef SUPPORT_IMAGE_EXPORT + // Support image export functionality (.png, .bmp, .tga, .jpg, .qoi) + // NOTE: Image export requires stb_image_write.h library + #define SUPPORT_IMAGE_EXPORT 1 +#endif +#ifndef SUPPORT_IMAGE_GENERATION + // Support procedural image generation functionality: gradient, spot, perlin-noise, cellular... + // NOTE: Perlin noise requires stb_perlin.h library + #define SUPPORT_IMAGE_GENERATION 1 #endif //------------------------------------------------------------------------------------ // Module: rtext - Configuration Flags //------------------------------------------------------------------------------------ -#if !defined(EXTERNAL_CONFIG_FLAGS) -// Default font is loaded on window initialization to be available for the user to render simple text -// NOTE: If enabled, uses external module functions to load default raylib font -#define SUPPORT_DEFAULT_FONT 1 // Selected desired font fileformats to be supported for loading -#define SUPPORT_FILEFORMAT_TTF 1 -#define SUPPORT_FILEFORMAT_FNT 1 -//#define SUPPORT_FILEFORMAT_BDF 1 - -// Support text management functions -// If not defined, still some functions are supported: TextLength(), TextFormat() -#define SUPPORT_TEXT_MANIPULATION 1 - -// 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()] -#define SUPPORT_FONT_ATLAS_WHITE_REC 1 - -// Support conservative font atlas size estimation -//#define SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE 1 +#ifndef SUPPORT_FILEFORMAT_TTF + #define SUPPORT_FILEFORMAT_TTF 1 +#endif +#ifndef SUPPORT_FILEFORMAT_FNT + #define SUPPORT_FILEFORMAT_FNT 1 +#endif +#ifndef SUPPORT_FILEFORMAT_BDF + #define SUPPORT_FILEFORMAT_BDF 0 // Disabled by default #endif - -// rtext: Configuration values -//------------------------------------------------------------------------------------ -#define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions: - // TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal(), TextSplit() -#define MAX_TEXTSPLIT_COUNT 128 // Maximum number of substrings to split: TextSplit() //------------------------------------------------------------------------------------ // Module: rmodels - Configuration Flags //------------------------------------------------------------------------------------ -#if !defined(EXTERNAL_CONFIG_FLAGS) // Selected desired model fileformats to be supported for loading -#define SUPPORT_FILEFORMAT_OBJ 1 -#define SUPPORT_FILEFORMAT_MTL 1 -#define SUPPORT_FILEFORMAT_IQM 1 -#define SUPPORT_FILEFORMAT_GLTF 1 -#define SUPPORT_FILEFORMAT_VOX 1 -#define SUPPORT_FILEFORMAT_M3D 1 -// Support procedural mesh generation functions, uses external par_shapes.h library -// NOTE: Some generated meshes DO NOT include generated texture coordinates -#define SUPPORT_MESH_GENERATION 1 +#ifndef SUPPORT_FILEFORMAT_OBJ + #define SUPPORT_FILEFORMAT_OBJ 1 #endif - -// rmodels: Configuration values -//------------------------------------------------------------------------------------ -#define MAX_MATERIAL_MAPS 12 // Maximum number of shader maps supported - -#ifdef SUPPORT_GPU_SKINNING - // NOTE: Two additional vertex buffers required to store bone indices and bone weights - #define MAX_MESH_VERTEX_BUFFERS 9 // Maximum vertex buffers (VBO) per mesh -#else - #define MAX_MESH_VERTEX_BUFFERS 7 // Maximum vertex buffers (VBO) per mesh +#ifndef SUPPORT_FILEFORMAT_MTL + #define SUPPORT_FILEFORMAT_MTL 1 +#endif +#ifndef SUPPORT_FILEFORMAT_IQM + #define SUPPORT_FILEFORMAT_IQM 1 +#endif +#ifndef SUPPORT_FILEFORMAT_GLTF + #define SUPPORT_FILEFORMAT_GLTF 1 +#endif +#ifndef SUPPORT_FILEFORMAT_VOX + #define SUPPORT_FILEFORMAT_VOX 1 +#endif +#ifndef SUPPORT_FILEFORMAT_M3D + #define SUPPORT_FILEFORMAT_M3D 1 +#endif +#ifndef SUPPORT_MESH_GENERATION + // Support procedural mesh generation functions, uses external par_shapes.h library + // NOTE: Some generated meshes DO NOT include generated texture coordinates + #define SUPPORT_MESH_GENERATION 1 +#endif +#ifndef SUPPORT_GPU_SKINNING + // GPU skinning disabled by default, some GPUs do not support more than 8 VBOs + #define SUPPORT_GPU_SKINNING 0 #endif //------------------------------------------------------------------------------------ // Module: raudio - Configuration Flags //------------------------------------------------------------------------------------ -#if !defined(EXTERNAL_CONFIG_FLAGS) // Desired audio fileformats to be supported for loading -#define SUPPORT_FILEFORMAT_WAV 1 -#define SUPPORT_FILEFORMAT_OGG 1 -#define SUPPORT_FILEFORMAT_MP3 1 -#define SUPPORT_FILEFORMAT_QOA 1 -//#define SUPPORT_FILEFORMAT_FLAC 1 -#define SUPPORT_FILEFORMAT_XM 1 -#define SUPPORT_FILEFORMAT_MOD 1 +#ifndef SUPPORT_FILEFORMAT_WAV + #define SUPPORT_FILEFORMAT_WAV 1 +#endif +#ifndef SUPPORT_FILEFORMAT_OGG + #define SUPPORT_FILEFORMAT_OGG 1 +#endif +#ifndef SUPPORT_FILEFORMAT_MP3 + #define SUPPORT_FILEFORMAT_MP3 1 +#endif +#ifndef SUPPORT_FILEFORMAT_QOA + #define SUPPORT_FILEFORMAT_QOA 1 +#endif +#ifndef SUPPORT_FILEFORMAT_FLAC + #define SUPPORT_FILEFORMAT_FLAC 0 // Disabled by default +#endif +#ifndef SUPPORT_FILEFORMAT_XM + #define SUPPORT_FILEFORMAT_XM 1 +#endif +#ifndef SUPPORT_FILEFORMAT_MOD + #define SUPPORT_FILEFORMAT_MOD 1 #endif // raudio: Configuration values +// NOTE: Below values are alread defined inside [rlgl.h] so there is no need to be +// redefined here, in case it must be done, just uncomment the required line and update +// the value; it can also be done on compilation with -DVALUE_TO_REDEFINE=128 +//------------------------------------------------------------------------------------ +//#define AUDIO_DEVICE_FORMAT ma_format_f32 // Device output format (miniaudio: float-32bit) +//#define AUDIO_DEVICE_CHANNELS 2 // Device output channels: stereo +//#define AUDIO_DEVICE_SAMPLE_RATE 0 // Device sample rate (device default) +//#define AUDIO_DEVICE_PERIOD_SIZE_IN_FRAMES 0 // Device period size (controls latency, 0 defaults to 10ms) +//#define MAX_AUDIO_BUFFER_POOL_CHANNELS 16 // Maximum number of audio pool channels //------------------------------------------------------------------------------------ -#define AUDIO_DEVICE_FORMAT ma_format_f32 // Device output format (miniaudio: float-32bit) -#define AUDIO_DEVICE_CHANNELS 2 // Device output channels: stereo -#define AUDIO_DEVICE_SAMPLE_RATE 0 // Device sample rate (device default) -#define AUDIO_DEVICE_PERIOD_SIZE_IN_FRAMES 0 // Device period size (controls latency, 0 defaults to 10ms) -#define MAX_AUDIO_BUFFER_POOL_CHANNELS 16 // Maximum number of audio pool channels +#endif // !EXTERNAL_CONFIG_FLAGS #endif // CONFIG_H diff --git a/src/external/rlsw.h b/src/external/rlsw.h index aad99f937..37d8e98b1 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -644,7 +644,7 @@ SWAPI void swBindTexture(uint32_t id); #define SW_ARCH_RISCV #endif -#if defined(RLSW_USE_SIMD_INTRINSICS) +#if RLSW_USE_SIMD_INTRINSICS // Check for SIMD vector instructions // NOTE: Compiler is responsible to enable required flags for host device, // supported features are detected at compiler init but varies depending on compiler @@ -695,7 +695,7 @@ SWAPI void swBindTexture(uint32_t id); #define SW_HAS_RVV #include #endif -#endif // RLSW_USE_SIMD_INTRINSICS +#endif #ifdef __cplusplus #define SW_CURLY_INIT(name) name diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 74c7b3792..d7fdca403 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -713,7 +713,7 @@ const char *GetKeyName(int key) // Register all input events void PollInputEvents(void) { -#if defined(SUPPORT_GESTURES_SYSTEM) +#if SUPPORT_GESTURES_SYSTEM // NOTE: Gestures update must be called every frame to reset gestures correctly // because ProcessGestureEvent() is just called on an event, not every frame UpdateGestures(); @@ -1065,11 +1065,11 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) // Initialize hi-res timer InitTimer(); - #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + #if SUPPORT_MODULE_RTEXT // Load default font // WARNING: External function: Module required: rtext LoadFontDefault(); - #if defined(SUPPORT_MODULE_RSHAPES) + #if SUPPORT_MODULE_RSHAPES // Set font white rectangle for shapes drawing, so shapes and text can be batched together // WARNING: rshapes module is required, if not available, default internal white rectangle is used Rectangle rec = GetFontDefault().recs[95]; @@ -1085,7 +1085,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) } #endif #else - #if defined(SUPPORT_MODULE_RSHAPES) + #if SUPPORT_MODULE_RSHAPES // Set default texture and rectangle to be used for shapes drawing // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; @@ -1347,7 +1347,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) } } -#if defined(SUPPORT_GESTURES_SYSTEM) +#if SUPPORT_GESTURES_SYSTEM GestureEvent gestureEvent = { 0 }; gestureEvent.pointCount = 0; diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index b79d5d629..79a94b08b 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -65,7 +65,7 @@ #define GLFW_NATIVE_INCLUDE_NONE // To avoid some symbols re-definition in windows.h #include "GLFW/glfw3native.h" - #if defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + #if SUPPORT_WINMM_HIGHRES_TIMER && !SUPPORT_BUSY_WAIT_LOOP // NOTE: Those functions require linking with winmm library //#pragma warning(disable: 4273) __declspec(dllimport) unsigned int __stdcall timeEndPeriod(unsigned int uPeriod); @@ -1048,7 +1048,7 @@ Image GetClipboardImage(void) { Image image = { 0 }; -#if defined(SUPPORT_CLIPBOARD_IMAGE) +#if SUPPORT_CLIPBOARD_IMAGE #if defined(_WIN32) unsigned long long int dataSize = 0; void *bmpData = NULL; @@ -1062,7 +1062,7 @@ Image GetClipboardImage(void) #else TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform"); #endif -#endif // SUPPORT_CLIPBOARD_IMAGE +#endif return image; } @@ -1202,7 +1202,7 @@ const char *GetKeyName(int key) // Register all input events void PollInputEvents(void) { -#if defined(SUPPORT_GESTURES_SYSTEM) +#if SUPPORT_GESTURES_SYSTEM // NOTE: Gestures update must be called every frame to reset gestures correctly // because ProcessGestureEvent() is just called on an event, not every frame UpdateGestures(); @@ -1514,7 +1514,7 @@ int InitPlatform(void) glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); -#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) +#if RLGL_ENABLE_OPENGL_DEBUG_CONTEXT glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context #endif } @@ -1819,7 +1819,7 @@ void ClosePlatform(void) glfwDestroyWindow(platform.handle); glfwTerminate(); -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) +#if defined(_WIN32) && SUPPORT_WINMM_HIGHRES_TIMER && !SUPPORT_BUSY_WAIT_LOOP timeEndPeriod(1); // Restore time period #endif } @@ -2049,7 +2049,7 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int CORE.Input.Mouse.currentButtonState[button] = action; CORE.Input.Touch.currentTouchState[button] = action; -#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) +#if SUPPORT_GESTURES_SYSTEM && defined(SUPPORT_MOUSE_GESTURES) // Process mouse events as touches to be able to use mouse-gestures GestureEvent gestureEvent = { 0 }; @@ -2084,7 +2084,7 @@ static void MouseCursorPosCallback(GLFWwindow *window, double x, double y) CORE.Input.Mouse.currentPosition.y = (float)y; CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; -#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) +#if SUPPORT_GESTURES_SYSTEM && defined(SUPPORT_MOUSE_GESTURES) // Process mouse events as touches to be able to use mouse-gestures GestureEvent gestureEvent = { 0 }; diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 789457d2f..c16177f99 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -423,7 +423,6 @@ void *SDL_GetClipboardData(const char *mime_type, size_t *size) // We could possibly implement it ourselves in this case for some easier platforms return NULL; } - #endif // USING_VERSION_SDL3 //---------------------------------------------------------------------------------- @@ -1163,7 +1162,7 @@ Image GetClipboardImage(void) { Image image = { 0 }; -#if defined(SUPPORT_CLIPBOARD_IMAGE) +#if SUPPORT_CLIPBOARD_IMAGE // Let's hope compiler put these arrays in static memory const char *imageFormats[] = { "image/bmp", @@ -1350,7 +1349,7 @@ const char *GetKeyName(int key) // Register all input events void PollInputEvents(void) { -#if defined(SUPPORT_GESTURES_SYSTEM) +#if SUPPORT_GESTURES_SYSTEM // NOTE: Gestures update must be called every frame to reset gestures correctly // because ProcessGestureEvent() is just called on an event, not every frame UpdateGestures(); @@ -1892,7 +1891,7 @@ void PollInputEvents(void) default: break; } -#if defined(SUPPORT_GESTURES_SYSTEM) +#if SUPPORT_GESTURES_SYSTEM if (touchAction > -1) { // Process mouse events as touches to be able to use mouse-gestures @@ -1989,7 +1988,7 @@ int InitPlatform(void) SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - #if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) + #if RLGL_ENABLE_OPENGL_DEBUG_CONTEXT SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); // Enable OpenGL Debug Context #endif } @@ -2113,7 +2112,7 @@ int InitPlatform(void) // NOTE: No need to call InitTimer(), let SDL manage it internally CORE.Time.previous = GetTime(); // Get time as double - #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + #if defined(_WIN32) && SUPPORT_WINMM_HIGHRES_TIMER && !SUPPORT_BUSY_WAIT_LOOP SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "1"); // SDL equivalent of timeBeginPeriod() and timeEndPeriod() #endif //---------------------------------------------------------------------------- diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 619a42256..3fbea09ba 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -80,7 +80,7 @@ #include // Required for: EBUSY, EAGAIN #define MAX_DRM_CACHED_BUFFERS 3 -#endif // SUPPORT_DRM_CACHE +#endif #ifndef EGL_OPENGL_ES3_BIT #define EGL_OPENGL_ES3_BIT 0x40 @@ -160,7 +160,7 @@ 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 +#endif //---------------------------------------------------------------------------------- // Global Variables Definition @@ -253,7 +253,7 @@ static const short linuxToRaylibMap[KEYMAP_SIZE] = { int InitPlatform(void); // Initialize platform (graphics, inputs and more) void ClosePlatform(void); // Close platform -#if defined(SUPPORT_SSH_KEYBOARD_RPI) +#if SUPPORT_SSH_KEYBOARD_RPI static void InitKeyboard(void); // Initialize raw keyboard system static void RestoreKeyboard(void); // Restore keyboard system static void ProcessKeyboard(void); // Process keyboard events @@ -1065,7 +1065,7 @@ const char *GetKeyName(int key) // Register all input events void PollInputEvents(void) { -#if defined(SUPPORT_GESTURES_SYSTEM) +#if SUPPORT_GESTURES_SYSTEM // NOTE: Gestures update must be called every frame to reset gestures correctly // because ProcessGestureEvent() is just called on an event, not every frame UpdateGestures(); @@ -1088,7 +1088,7 @@ void PollInputEvents(void) PollKeyboardEvents(); -#if defined(SUPPORT_SSH_KEYBOARD_RPI) +#if SUPPORT_SSH_KEYBOARD_RPI // 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(); @@ -1621,7 +1621,7 @@ int InitPlatform(void) //---------------------------------------------------------------------------- InitEvdevInput(); // Evdev inputs initialization -#if defined(SUPPORT_SSH_KEYBOARD_RPI) +#if SUPPORT_SSH_KEYBOARD_RPI InitKeyboard(); // Keyboard init (stdin) #endif //---------------------------------------------------------------------------- @@ -1741,7 +1741,7 @@ void ClosePlatform(void) } } -#if defined(SUPPORT_SSH_KEYBOARD_RPI) +#if SUPPORT_SSH_KEYBOARD_RPI // Initialize Keyboard system (using standard input) static void InitKeyboard(void) { @@ -1900,7 +1900,7 @@ static void ProcessKeyboard(void) } } } -#endif // SUPPORT_SSH_KEYBOARD_RPI +#endif // SUPPORT_SSH_KEYBOARD_RPI // Initialize user input from evdev(/dev/input/event) // this means mouse, keyboard or gamepad devices @@ -2196,7 +2196,7 @@ static void PollKeyboardEvents(void) // Check if the event is a key event if (event.type != EV_KEY) continue; -#if defined(SUPPORT_SSH_KEYBOARD_RPI) +#if SUPPORT_SSH_KEYBOARD_RPI // If the event was a key, we know a working keyboard is connected, so disable the SSH keyboard platform.eventKeyboardMode = true; #endif @@ -2552,7 +2552,7 @@ static void PollMouseEvents(void) CORE.Input.Touch.pointId[i] = -1; } -#if defined(SUPPORT_GESTURES_SYSTEM) +#if SUPPORT_GESTURES_SYSTEM if (touchAction > -1) { GestureEvent gestureEvent = { 0 }; diff --git a/src/platforms/rcore_memory.c b/src/platforms/rcore_memory.c index a6fa46ba3..05579513f 100644 --- a/src/platforms/rcore_memory.c +++ b/src/platforms/rcore_memory.c @@ -435,7 +435,7 @@ const char *GetKeyName(int key) // Register all input events void PollInputEvents(void) { -#if defined(SUPPORT_GESTURES_SYSTEM) +#if SUPPORT_GESTURES_SYSTEM // NOTE: Gestures update must be called every frame to reset gestures correctly // because ProcessGestureEvent() is just called on an event, not every frame UpdateGestures(); diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index c33e72682..263db7fb8 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -1007,7 +1007,7 @@ const char *GetKeyName(int key) // Register all input events void PollInputEvents(void) { -#if defined(SUPPORT_GESTURES_SYSTEM) +#if SUPPORT_GESTURES_SYSTEM // NOTE: Gestures update must be called every frame to reset gestures correctly // because ProcessGestureEvent() is just called on an event, not every frame UpdateGestures(); @@ -1210,7 +1210,7 @@ int InitPlatform(void) glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); -#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) +#if RLGL_ENABLE_OPENGL_DEBUG_CONTEXT glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context #endif } @@ -1475,15 +1475,15 @@ static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float s // GLFW3: Called on windows minimized/restored static void WindowIconifyCallback(GLFWwindow *window, int iconified) { - if (iconified) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // The window was iconified - else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // The window was restored + if (iconified) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // The window was iconified + else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // The window was restored } // GLFW3: Called on windows get/lose focus static void WindowFocusCallback(GLFWwindow *window, int focused) { - if (focused) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // The window was focused - else FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // The window lost focus + if (focused) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // The window was focused + else FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // The window lost focus } // GLFW3: Called on file-drop over the window @@ -1564,7 +1564,7 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int CORE.Input.Mouse.currentButtonState[button] = action; CORE.Input.Touch.currentTouchState[button] = action; -#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) +#if SUPPORT_GESTURES_SYSTEM && defined(SUPPORT_MOUSE_GESTURES) // Process mouse events as touches to be able to use mouse-gestures GestureEvent gestureEvent = { 0 }; @@ -1605,7 +1605,7 @@ static void MouseMoveCallback(GLFWwindow *window, double x, double y) CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; } -#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) +#if SUPPORT_GESTURES_SYSTEM && defined(SUPPORT_MOUSE_GESTURES) // Process mouse events as touches to be able to use mouse-gestures GestureEvent gestureEvent = { 0 }; @@ -1748,7 +1748,7 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent CORE.Input.Mouse.currentPosition.y = CORE.Input.Touch.position[0].y; } -#if defined(SUPPORT_GESTURES_SYSTEM) +#if SUPPORT_GESTURES_SYSTEM GestureEvent gestureEvent = { 0 }; gestureEvent.pointCount = CORE.Input.Touch.pointCount; diff --git a/src/platforms/rcore_web_emscripten.c b/src/platforms/rcore_web_emscripten.c index b6a0690a6..ffaa38c27 100644 --- a/src/platforms/rcore_web_emscripten.c +++ b/src/platforms/rcore_web_emscripten.c @@ -983,7 +983,7 @@ const char *GetKeyName(int key) // Register all input events void PollInputEvents(void) { -#if defined(SUPPORT_GESTURES_SYSTEM) +#if SUPPORT_GESTURES_SYSTEM // NOTE: Gestures update must be called every frame to reset gestures correctly // because ProcessGestureEvent() is just called on an event, not every frame UpdateGestures(); @@ -1321,7 +1321,7 @@ static EM_BOOL EmscriptenFocusCallback(int eventType, const EmscriptenFocusEvent switch (eventType) { - case EMSCRIPTEN_EVENT_BLUR: FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); break; // The canvas lost focus + case EMSCRIPTEN_EVENT_BLUR: FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); break; case EMSCRIPTEN_EVENT_FOCUS: FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); break; default: consumed = 0; break; } @@ -1457,7 +1457,7 @@ static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent default: break; } -#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) +#if SUPPORT_GESTURES_SYSTEM && defined(SUPPORT_MOUSE_GESTURES) // Process mouse events as touches to be able to use mouse-gestures GestureEvent gestureEvent = { 0 }; @@ -1529,7 +1529,7 @@ static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseE CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; } -#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) +#if SUPPORT_GESTURES_SYSTEM && defined(SUPPORT_MOUSE_GESTURES) // Process mouse events as touches to be able to use mouse-gestures GestureEvent gestureEvent = { 0 }; @@ -1639,7 +1639,7 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent CORE.Input.Mouse.currentPosition.y = CORE.Input.Touch.position[0].y; } -#if defined(SUPPORT_GESTURES_SYSTEM) +#if SUPPORT_GESTURES_SYSTEM GestureEvent gestureEvent = { 0 }; gestureEvent.pointCount = CORE.Input.Touch.pointCount; diff --git a/src/raudio.c b/src/raudio.c index fcdb27702..367615ad1 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -77,7 +77,7 @@ #include "config.h" // Defines module configuration flags #endif -#if defined(SUPPORT_MODULE_RAUDIO) || defined(RAUDIO_STANDALONE) +#if SUPPORT_MODULE_RAUDIO || defined(RAUDIO_STANDALONE) #if defined(_WIN32) // To avoid conflicting windows.h symbols with raylib, some flags are defined @@ -205,7 +205,7 @@ typedef struct tagBITMAPINFOHEADER { #endif #endif -#if defined(SUPPORT_FILEFORMAT_WAV) +#if SUPPORT_FILEFORMAT_WAV #define DRWAV_MALLOC RL_MALLOC #define DRWAV_REALLOC RL_REALLOC #define DRWAV_FREE RL_FREE @@ -214,12 +214,12 @@ typedef struct tagBITMAPINFOHEADER { #include "external/dr_wav.h" // WAV loading functions #endif -#if defined(SUPPORT_FILEFORMAT_OGG) +#if SUPPORT_FILEFORMAT_OGG // TODO: Remap stb_vorbis malloc()/free() calls to RL_MALLOC/RL_FREE #include "external/stb_vorbis.c" // OGG loading functions #endif -#if defined(SUPPORT_FILEFORMAT_MP3) +#if SUPPORT_FILEFORMAT_MP3 #define DRMP3_MALLOC RL_MALLOC #define DRMP3_REALLOC RL_REALLOC #define DRMP3_FREE RL_FREE @@ -228,7 +228,7 @@ typedef struct tagBITMAPINFOHEADER { #include "external/dr_mp3.h" // MP3 loading functions #endif -#if defined(SUPPORT_FILEFORMAT_QOA) +#if SUPPORT_FILEFORMAT_QOA #define QOA_MALLOC RL_MALLOC #define QOA_FREE RL_FREE @@ -248,7 +248,7 @@ typedef struct tagBITMAPINFOHEADER { #endif #endif -#if defined(SUPPORT_FILEFORMAT_FLAC) +#if SUPPORT_FILEFORMAT_FLAC #define DRFLAC_MALLOC RL_MALLOC #define DRFLAC_REALLOC RL_REALLOC #define DRFLAC_FREE RL_FREE @@ -258,7 +258,7 @@ typedef struct tagBITMAPINFOHEADER { #include "external/dr_flac.h" // FLAC loading functions #endif -#if defined(SUPPORT_FILEFORMAT_XM) +#if SUPPORT_FILEFORMAT_XM #define JARXM_MALLOC RL_MALLOC #define JARXM_FREE RL_FREE @@ -275,7 +275,7 @@ typedef struct tagBITMAPINFOHEADER { #endif #endif -#if defined(SUPPORT_FILEFORMAT_MOD) +#if SUPPORT_FILEFORMAT_MOD #define JARMOD_MALLOC RL_MALLOC #define JARMOD_FREE RL_FREE @@ -813,7 +813,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int Wave wave = { 0 }; if (false) { } -#if defined(SUPPORT_FILEFORMAT_WAV) +#if SUPPORT_FILEFORMAT_WAV else if ((strcmp(fileType, ".wav") == 0) || (strcmp(fileType, ".WAV") == 0)) { drwav wav = { 0 }; @@ -835,7 +835,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int drwav_uninit(&wav); } #endif -#if defined(SUPPORT_FILEFORMAT_OGG) +#if SUPPORT_FILEFORMAT_OGG else if ((strcmp(fileType, ".ogg") == 0) || (strcmp(fileType, ".OGG") == 0)) { stb_vorbis *oggData = stb_vorbis_open_memory((unsigned char *)fileData, dataSize, NULL, NULL); @@ -857,7 +857,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int else TRACELOG(LOG_WARNING, "WAVE: Failed to load OGG data"); } #endif -#if defined(SUPPORT_FILEFORMAT_MP3) +#if SUPPORT_FILEFORMAT_MP3 else if ((strcmp(fileType, ".mp3") == 0) || (strcmp(fileType, ".MP3") == 0)) { drmp3_config config = { 0 }; @@ -877,7 +877,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int } #endif -#if defined(SUPPORT_FILEFORMAT_QOA) +#if SUPPORT_FILEFORMAT_QOA else if ((strcmp(fileType, ".qoa") == 0) || (strcmp(fileType, ".QOA") == 0)) { qoa_desc qoa = { 0 }; @@ -896,7 +896,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int } #endif -#if defined(SUPPORT_FILEFORMAT_FLAC) +#if SUPPORT_FILEFORMAT_FLAC else if ((strcmp(fileType, ".flac") == 0) || (strcmp(fileType, ".FLAC") == 0)) { unsigned long long int totalFrameCount = 0; @@ -1078,7 +1078,7 @@ bool ExportWave(Wave wave, const char *fileName) bool success = false; if (false) { } -#if defined(SUPPORT_FILEFORMAT_WAV) +#if SUPPORT_FILEFORMAT_WAV else if (IsFileExtension(fileName, ".wav")) { drwav wav = { 0 }; @@ -1101,7 +1101,7 @@ bool ExportWave(Wave wave, const char *fileName) drwav_free(fileData, NULL); } #endif -#if defined(SUPPORT_FILEFORMAT_QOA) +#if SUPPORT_FILEFORMAT_QOA else if (IsFileExtension(fileName, ".qoa")) { if (wave.sampleSize == 16) @@ -1359,7 +1359,7 @@ Music LoadMusicStream(const char *fileName) bool musicLoaded = false; if (false) { } -#if defined(SUPPORT_FILEFORMAT_WAV) +#if SUPPORT_FILEFORMAT_WAV else if (IsFileExtension(fileName, ".wav")) { drwav *ctxWav = (drwav *)RL_CALLOC(1, sizeof(drwav)); @@ -1383,7 +1383,7 @@ Music LoadMusicStream(const char *fileName) } } #endif -#if defined(SUPPORT_FILEFORMAT_OGG) +#if SUPPORT_FILEFORMAT_OGG else if (IsFileExtension(fileName, ".ogg")) { // Open ogg audio stream @@ -1409,7 +1409,7 @@ Music LoadMusicStream(const char *fileName) } } #endif -#if defined(SUPPORT_FILEFORMAT_MP3) +#if SUPPORT_FILEFORMAT_MP3 else if (IsFileExtension(fileName, ".mp3")) { drmp3 *ctxMp3 = (drmp3 *)RL_CALLOC(1, sizeof(drmp3)); @@ -1430,7 +1430,7 @@ Music LoadMusicStream(const char *fileName) } } #endif -#if defined(SUPPORT_FILEFORMAT_QOA) +#if SUPPORT_FILEFORMAT_QOA else if (IsFileExtension(fileName, ".qoa")) { qoaplay_desc *ctxQoa = qoaplay_open(fileName); @@ -1449,7 +1449,7 @@ Music LoadMusicStream(const char *fileName) else{} //No uninit required } #endif -#if defined(SUPPORT_FILEFORMAT_FLAC) +#if SUPPORT_FILEFORMAT_FLAC else if (IsFileExtension(fileName, ".flac")) { drflac *ctxFlac = drflac_open_file(fileName, NULL); @@ -1471,7 +1471,7 @@ Music LoadMusicStream(const char *fileName) } } #endif -#if defined(SUPPORT_FILEFORMAT_XM) +#if SUPPORT_FILEFORMAT_XM else if (IsFileExtension(fileName, ".xm")) { jar_xm_context_t *ctxXm = NULL; @@ -1500,7 +1500,7 @@ Music LoadMusicStream(const char *fileName) } } #endif -#if defined(SUPPORT_FILEFORMAT_MOD) +#if SUPPORT_FILEFORMAT_MOD else if (IsFileExtension(fileName, ".mod")) { jar_mod_context_t *ctxMod = (jar_mod_context_t *)RL_CALLOC(1, sizeof(jar_mod_context_t)); @@ -1551,7 +1551,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, bool musicLoaded = false; if (false) { } -#if defined(SUPPORT_FILEFORMAT_WAV) +#if SUPPORT_FILEFORMAT_WAV else if ((strcmp(fileType, ".wav") == 0) || (strcmp(fileType, ".WAV") == 0)) { drwav *ctxWav = (drwav *)RL_CALLOC(1, sizeof(drwav)); @@ -1577,7 +1577,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, } } #endif -#if defined(SUPPORT_FILEFORMAT_OGG) +#if SUPPORT_FILEFORMAT_OGG else if ((strcmp(fileType, ".ogg") == 0) || (strcmp(fileType, ".OGG") == 0)) { // Open ogg audio stream @@ -1603,7 +1603,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, } } #endif -#if defined(SUPPORT_FILEFORMAT_MP3) +#if SUPPORT_FILEFORMAT_MP3 else if ((strcmp(fileType, ".mp3") == 0) || (strcmp(fileType, ".MP3") == 0)) { drmp3 *ctxMp3 = (drmp3 *)RL_CALLOC(1, sizeof(drmp3)); @@ -1625,7 +1625,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, } } #endif -#if defined(SUPPORT_FILEFORMAT_QOA) +#if SUPPORT_FILEFORMAT_QOA else if ((strcmp(fileType, ".qoa") == 0) || (strcmp(fileType, ".QOA") == 0)) { qoaplay_desc *ctxQoa = NULL; @@ -1648,7 +1648,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, else{} //No uninit required } #endif -#if defined(SUPPORT_FILEFORMAT_FLAC) +#if SUPPORT_FILEFORMAT_FLAC else if ((strcmp(fileType, ".flac") == 0) || (strcmp(fileType, ".FLAC") == 0)) { drflac *ctxFlac = drflac_open_memory((const void *)data, dataSize, NULL); @@ -1670,7 +1670,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, } } #endif -#if defined(SUPPORT_FILEFORMAT_XM) +#if SUPPORT_FILEFORMAT_XM else if ((strcmp(fileType, ".xm") == 0) || (strcmp(fileType, ".XM") == 0)) { jar_xm_context_t *ctxXm = NULL; @@ -1699,7 +1699,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, } } #endif -#if defined(SUPPORT_FILEFORMAT_MOD) +#if SUPPORT_FILEFORMAT_MOD else if ((strcmp(fileType, ".mod") == 0) || (strcmp(fileType, ".MOD") == 0)) { jar_mod_context_t *ctxMod = (jar_mod_context_t *)RL_MALLOC(sizeof(jar_mod_context_t)); @@ -1775,25 +1775,25 @@ void UnloadMusicStream(Music music) if (music.ctxData != NULL) { if (false) { } -#if defined(SUPPORT_FILEFORMAT_WAV) +#if SUPPORT_FILEFORMAT_WAV else if (music.ctxType == MUSIC_AUDIO_WAV) drwav_uninit((drwav *)music.ctxData); #endif -#if defined(SUPPORT_FILEFORMAT_OGG) +#if SUPPORT_FILEFORMAT_OGG else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData); #endif -#if defined(SUPPORT_FILEFORMAT_MP3) +#if SUPPORT_FILEFORMAT_MP3 else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); } #endif -#if defined(SUPPORT_FILEFORMAT_QOA) +#if SUPPORT_FILEFORMAT_QOA else if (music.ctxType == MUSIC_AUDIO_QOA) qoaplay_close((qoaplay_desc *)music.ctxData); #endif -#if defined(SUPPORT_FILEFORMAT_FLAC) +#if SUPPORT_FILEFORMAT_FLAC else if (music.ctxType == MUSIC_AUDIO_FLAC) { drflac_close((drflac *)music.ctxData); drflac_free((drflac *)music.ctxData, NULL); } #endif -#if defined(SUPPORT_FILEFORMAT_XM) +#if SUPPORT_FILEFORMAT_XM else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData); #endif -#if defined(SUPPORT_FILEFORMAT_MOD) +#if SUPPORT_FILEFORMAT_MOD else if (music.ctxType == MUSIC_MODULE_MOD) { jar_mod_unload((jar_mod_context_t *)music.ctxData); RL_FREE(music.ctxData); } #endif } @@ -1824,25 +1824,25 @@ void StopMusicStream(Music music) switch (music.ctxType) { -#if defined(SUPPORT_FILEFORMAT_WAV) +#if SUPPORT_FILEFORMAT_WAV case MUSIC_AUDIO_WAV: drwav_seek_to_first_pcm_frame((drwav *)music.ctxData); break; #endif -#if defined(SUPPORT_FILEFORMAT_OGG) +#if SUPPORT_FILEFORMAT_OGG case MUSIC_AUDIO_OGG: stb_vorbis_seek_start((stb_vorbis *)music.ctxData); break; #endif -#if defined(SUPPORT_FILEFORMAT_MP3) +#if SUPPORT_FILEFORMAT_MP3 case MUSIC_AUDIO_MP3: drmp3_seek_to_start_of_stream((drmp3 *)music.ctxData); break; #endif -#if defined(SUPPORT_FILEFORMAT_QOA) +#if SUPPORT_FILEFORMAT_QOA case MUSIC_AUDIO_QOA: qoaplay_rewind((qoaplay_desc *)music.ctxData); break; #endif -#if defined(SUPPORT_FILEFORMAT_FLAC) +#if SUPPORT_FILEFORMAT_FLAC case MUSIC_AUDIO_FLAC: drflac__seek_to_first_frame((drflac *)music.ctxData); break; #endif -#if defined(SUPPORT_FILEFORMAT_XM) +#if SUPPORT_FILEFORMAT_XM case MUSIC_MODULE_XM: jar_xm_reset((jar_xm_context_t *)music.ctxData); break; #endif -#if defined(SUPPORT_FILEFORMAT_MOD) +#if SUPPORT_FILEFORMAT_MOD case MUSIC_MODULE_MOD: jar_mod_seek_start((jar_mod_context_t *)music.ctxData); break; #endif default: break; @@ -1859,16 +1859,16 @@ void SeekMusicStream(Music music, float position) switch (music.ctxType) { -#if defined(SUPPORT_FILEFORMAT_WAV) +#if SUPPORT_FILEFORMAT_WAV case MUSIC_AUDIO_WAV: drwav_seek_to_pcm_frame((drwav *)music.ctxData, positionInFrames); break; #endif -#if defined(SUPPORT_FILEFORMAT_OGG) +#if SUPPORT_FILEFORMAT_OGG case MUSIC_AUDIO_OGG: stb_vorbis_seek_frame((stb_vorbis *)music.ctxData, positionInFrames); break; #endif -#if defined(SUPPORT_FILEFORMAT_MP3) +#if SUPPORT_FILEFORMAT_MP3 case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, positionInFrames); break; #endif -#if defined(SUPPORT_FILEFORMAT_QOA) +#if SUPPORT_FILEFORMAT_QOA case MUSIC_AUDIO_QOA: { int qoaFrame = positionInFrames/QOA_FRAME_LEN; @@ -1878,7 +1878,7 @@ void SeekMusicStream(Music music, float position) positionInFrames = ((qoaplay_desc *)music.ctxData)->sample_position; } break; #endif -#if defined(SUPPORT_FILEFORMAT_FLAC) +#if SUPPORT_FILEFORMAT_FLAC case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, positionInFrames); break; #endif default: break; @@ -1942,7 +1942,7 @@ void UpdateMusicStream(Music music) switch (music.ctxType) { - #if defined(SUPPORT_FILEFORMAT_WAV) + #if SUPPORT_FILEFORMAT_WAV case MUSIC_AUDIO_WAV: { if (music.stream.sampleSize == 16) @@ -1969,7 +1969,7 @@ void UpdateMusicStream(Music music) } } break; #endif - #if defined(SUPPORT_FILEFORMAT_OGG) + #if SUPPORT_FILEFORMAT_OGG case MUSIC_AUDIO_OGG: { while (true) @@ -1982,7 +1982,7 @@ void UpdateMusicStream(Music music) } } break; #endif - #if defined(SUPPORT_FILEFORMAT_MP3) + #if SUPPORT_FILEFORMAT_MP3 case MUSIC_AUDIO_MP3: { while (true) @@ -1995,7 +1995,7 @@ void UpdateMusicStream(Music music) } } break; #endif - #if defined(SUPPORT_FILEFORMAT_QOA) + #if SUPPORT_FILEFORMAT_QOA case MUSIC_AUDIO_QOA: { unsigned int frameCountRead = qoaplay_decode((qoaplay_desc *)music.ctxData, (float *)AUDIO.System.pcmBuffer, framesToStream); @@ -2012,7 +2012,7 @@ void UpdateMusicStream(Music music) */ } break; #endif - #if defined(SUPPORT_FILEFORMAT_FLAC) + #if SUPPORT_FILEFORMAT_FLAC case MUSIC_AUDIO_FLAC: { while (true) @@ -2025,7 +2025,7 @@ void UpdateMusicStream(Music music) } } break; #endif - #if defined(SUPPORT_FILEFORMAT_XM) + #if SUPPORT_FILEFORMAT_XM case MUSIC_MODULE_XM: { // NOTE: Internally we consider 2 channels generation, so sampleCount/2 @@ -2036,7 +2036,7 @@ void UpdateMusicStream(Music music) } break; #endif - #if defined(SUPPORT_FILEFORMAT_MOD) + #if SUPPORT_FILEFORMAT_MOD case MUSIC_MODULE_MOD: { // NOTE: 3rd parameter (nbsample) specify the number of stereo 16bits samples desired, so sampleCount/2 @@ -2094,7 +2094,7 @@ float GetMusicTimePlayed(Music music) float secondsPlayed = 0.0f; if (music.stream.buffer != NULL) { -#if defined(SUPPORT_FILEFORMAT_XM) +#if SUPPORT_FILEFORMAT_XM if (music.ctxType == MUSIC_MODULE_XM) { uint64_t framesPlayed = 0; @@ -2952,4 +2952,4 @@ static bool SaveFileText(const char *fileName, char *text) #undef AudioBuffer -#endif // SUPPORT_MODULE_RAUDIO +#endif // SUPPORT_MODULE_RAUDIO diff --git a/src/raymath.h b/src/raymath.h index 214495a2c..e3befaa08 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -30,7 +30,7 @@ * #define RAYMATH_DISABLE_CPP_OPERATORS * Disables C++ operator overloads for raymath types. * -* #define RAYMATH_USE_SIMD_INTRINSICS +* #define RAYMATH_USE_SIMD_INTRINSICS 1 * Try to enable SIMD intrinsics for MatrixMultiply() * Note that users enabling it must be aware of the target platform where application will * run to support the selected SIMD intrinsic, for now, only SSE is supported @@ -180,7 +180,7 @@ typedef struct float16 { #include // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), floor(), fminf(), fmaxf(), fabsf() -#if defined(RAYMATH_USE_SIMD_INTRINSICS) +#if RAYMATH_USE_SIMD_INTRINSICS // SIMD is used on the most costly raymath function MatrixMultiply() // NOTE: Only SSE intrinsics support implemented // TODO: Consider support for other SIMD instrinsics: @@ -3112,6 +3112,6 @@ inline const Matrix& operator *= (Matrix& lhs, const Matrix& rhs) return lhs; } //------------------------------------------------------------------------------- -#endif // C++ operators +#endif // C++ operators -#endif // RAYMATH_H +#endif // RAYMATH_H diff --git a/src/rcore.c b/src/rcore.c index 4c7854921..c7f33f48d 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -35,10 +35,6 @@ * - Memory framebuffer output, using software renderer, no OS required * * CONFIGURATION: -* #define SUPPORT_DEFAULT_FONT (default) -* Default font is loaded on window initialization to be available for the user to render simple text -* NOTE: If enabled, uses external module functions to load default raylib font (module: text) -* * #define SUPPORT_CAMERA_SYSTEM * Camera module is included (rcamera.h) and multiple predefined cameras are available: * free, 1st/3rd person, orbital, custom @@ -126,17 +122,17 @@ #define RAYMATH_IMPLEMENTATION #include "raymath.h" // Vector2, Vector3, Quaternion and Matrix functionality -#if defined(SUPPORT_GESTURES_SYSTEM) +#if SUPPORT_GESTURES_SYSTEM #define RGESTURES_IMPLEMENTATION #include "rgestures.h" // Gestures detection functionality #endif -#if defined(SUPPORT_CAMERA_SYSTEM) +#if SUPPORT_CAMERA_SYSTEM #define RCAMERA_IMPLEMENTATION #include "rcamera.h" // Camera system functionality #endif -#if defined(SUPPORT_COMPRESSION_API) +#if SUPPORT_COMPRESSION_API #define SINFL_IMPLEMENTATION #define SINFL_NO_SIMD #include "external/sinfl.h" // Deflate (RFC 1951) decompressor @@ -145,7 +141,7 @@ #include "external/sdefl.h" // Deflate (RFC 1951) compressor #endif -#if defined(SUPPORT_RPRAND_GENERATOR) +#if SUPPORT_RPRAND_GENERATOR #define RPRAND_IMPLEMENTATION #include "external/rprand.h" #endif @@ -181,7 +177,7 @@ #elif defined(__APPLE__) #include #include -#endif // OSs +#endif #define _CRT_INTERNAL_NONSTDC_NAMES 1 #include // Required for: stat(), S_ISREG [Used in GetFileModTime(), IsFilePath()] @@ -407,11 +403,11 @@ static SaveFileDataCallback saveFileData = NULL; // SaveFileText callback fun static LoadFileTextCallback loadFileText = NULL; // LoadFileText callback function pointer static SaveFileTextCallback saveFileText = NULL; // SaveFileText callback function pointer -#if defined(SUPPORT_SCREEN_CAPTURE) +#if SUPPORT_SCREEN_CAPTURE static int screenshotCounter = 0; // Screenshots counter #endif -#if defined(SUPPORT_AUTOMATION_EVENTS) +#if SUPPORT_AUTOMATION_EVENTS // Automation events type typedef enum AutomationEventType { EVENT_NONE = 0, @@ -503,8 +499,7 @@ static bool automationEventRecording = false; // Recording automat // Module Functions Declaration // NOTE: Those functions are common for all platforms! //---------------------------------------------------------------------------------- - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) +#if SUPPORT_MODULE_RTEXT extern void LoadFontDefault(void); // [Module: text] Loads default font on InitWindow() extern void UnloadFontDefault(void); // [Module: text] Unloads default font from GPU memory #endif @@ -517,7 +512,7 @@ static void SetupViewport(int width, int height); // Set viewport for static void ScanDirectoryFiles(const char *basePath, FilePathList *list, const char *filter, unsigned int expectedFileCount, bool scanSubdirs); // Scan all files and directories in a base path -#if defined(SUPPORT_AUTOMATION_EVENTS) +#if SUPPORT_AUTOMATION_EVENTS static void RecordAutomationEvent(void); // Record frame events (to internal events array) #endif @@ -526,30 +521,30 @@ static void RecordAutomationEvent(void); // Record frame events (to internal eve __declspec(dllimport) void __stdcall Sleep(unsigned long msTimeout); // Required for: WaitTime() #endif -#if !defined(SUPPORT_MODULE_RTEXT) +#if !SUPPORT_MODULE_RTEXT const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' -#endif // !SUPPORT_MODULE_RTEXT +#endif #if defined(PLATFORM_DESKTOP) #define PLATFORM_DESKTOP_GLFW #endif // Using '#pragma message' because '#warning' is not adopted by MSVC -#if defined(SUPPORT_CLIPBOARD_IMAGE) - #if !defined(SUPPORT_MODULE_RTEXTURES) +#if SUPPORT_CLIPBOARD_IMAGE + #if !SUPPORT_MODULE_RTEXTURES #pragma message ("WARNING: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly") #endif // It's nice to have support Bitmap on Linux as well, but not as necessary as Windows - #if !defined(SUPPORT_FILEFORMAT_BMP) && defined(_WIN32) + #if !SUPPORT_FILEFORMAT_BMP && defined(_WIN32) #pragma message ("WARNING: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_FILEFORMAT_BMP, specially on Windows") #endif // From what I've tested applications on Wayland saves images on clipboard as PNG - #if (!defined(SUPPORT_FILEFORMAT_PNG) || !defined(SUPPORT_FILEFORMAT_JPG)) && !defined(_WIN32) + #if (!SUPPORT_FILEFORMAT_PNG || !SUPPORT_FILEFORMAT_JPG) && !defined(_WIN32) #pragma message ("WARNING: Getting image from the clipboard might not work without SUPPORT_FILEFORMAT_PNG or SUPPORT_FILEFORMAT_JPG") #endif -#endif // SUPPORT_CLIPBOARD_IMAGE +#endif // Include platform-specific submodules #if defined(PLATFORM_DESKTOP_GLFW) @@ -653,27 +648,27 @@ void InitWindow(int width, int height, const char *title) TRACELOG(LOG_INFO, "Supported raylib modules:"); TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); -#if defined(SUPPORT_MODULE_RSHAPES) +#if SUPPORT_MODULE_RSHAPES TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); #else TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); #endif -#if defined(SUPPORT_MODULE_RTEXTURES) +#if SUPPORT_MODULE_RTEXTURES TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); #else TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); #endif -#if defined(SUPPORT_MODULE_RTEXT) +#if SUPPORT_MODULE_RTEXT TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); #else TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); #endif -#if defined(SUPPORT_MODULE_RMODELS) +#if SUPPORT_MODULE_RMODELS TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); #else TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); #endif -#if defined(SUPPORT_MODULE_RAUDIO) +#if SUPPORT_MODULE_RAUDIO TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); #else TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); @@ -712,29 +707,27 @@ void InitWindow(int width, int height, const char *title) // Setup default viewport SetupViewport(CORE.Window.render.width, CORE.Window.render.height); -#if defined(SUPPORT_MODULE_RTEXT) - #if defined(SUPPORT_DEFAULT_FONT) - // Load default font - // WARNING: External function: Module required: rtext - LoadFontDefault(); - #if defined(SUPPORT_MODULE_RSHAPES) - // Set font white rectangle for shapes drawing, so shapes and text can be batched together - // WARNING: rshapes module is required, if not available, default internal white rectangle is used - Rectangle rec = GetFontDefault().recs[95]; - if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) - { - // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); - } - else - { - // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); - } - #endif +#if SUPPORT_MODULE_RTEXT + // Load default font + // WARNING: External function: Module required: rtext + LoadFontDefault(); + #if SUPPORT_MODULE_RSHAPES + // Set font white rectangle for shapes drawing, so shapes and text can be batched together + // WARNING: rshapes module is required, if not available, default internal white rectangle is used + Rectangle rec = GetFontDefault().recs[95]; + if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) + { + // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); + } + else + { + // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); + } #endif #else - #if defined(SUPPORT_MODULE_RSHAPES) + #if SUPPORT_MODULE_RSHAPES // Set default texture and rectangle to be used for shapes drawing // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; @@ -754,7 +747,7 @@ void InitWindow(int width, int height, const char *title) // Close window and unload OpenGL context void CloseWindow(void) { -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) +#if SUPPORT_MODULE_RTEXT UnloadFontDefault(); // WARNING: Module required: rtext #endif @@ -908,11 +901,11 @@ void EndDrawing(void) { rlDrawRenderBatchActive(); // Update and draw internal render batch -#if defined(SUPPORT_AUTOMATION_EVENTS) +#if SUPPORT_AUTOMATION_EVENTS if (automationEventRecording) RecordAutomationEvent(); // Event recording #endif -#if !defined(SUPPORT_CUSTOM_FRAME_CONTROL) +#if !SUPPORT_CUSTOM_FRAME_CONTROL SwapScreenBuffer(); // Copy back buffer to front buffer (screen) // Frame time control system @@ -937,13 +930,13 @@ void EndDrawing(void) PollInputEvents(); // Poll user events (before next frame update) #endif -#if defined(SUPPORT_SCREEN_CAPTURE) +#if SUPPORT_SCREEN_CAPTURE if (IsKeyPressed(KEY_F12)) { TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); screenshotCounter++; } -#endif // SUPPORT_SCREEN_CAPTURE +#endif CORE.Time.frameCounter++; } @@ -1674,14 +1667,14 @@ void WaitTime(double seconds) { if (seconds < 0) return; // Security check -#if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) +#if SUPPORT_BUSY_WAIT_LOOP || SUPPORT_PARTIALBUSY_WAIT_LOOP double destinationTime = GetTime() + seconds; #endif -#if defined(SUPPORT_BUSY_WAIT_LOOP) +#if SUPPORT_BUSY_WAIT_LOOP while (GetTime() < destinationTime) { } #else - #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) + #if SUPPORT_PARTIALBUSY_WAIT_LOOP double sleepSeconds = seconds - seconds*0.05; // NOTE: We reserve a percentage of the time for busy waiting #else double sleepSeconds = seconds; @@ -1705,7 +1698,7 @@ void WaitTime(double seconds) usleep(sleepSeconds*1000000.0); #endif - #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) + #if SUPPORT_PARTIALBUSY_WAIT_LOOP while (GetTime() < destinationTime) { } #endif #endif @@ -1721,7 +1714,7 @@ void WaitTime(double seconds) // Set the seed for the random number generator void SetRandomSeed(unsigned int seed) { -#if defined(SUPPORT_RPRAND_GENERATOR) +#if SUPPORT_RPRAND_GENERATOR rprand_set_seed(seed); #else srand(seed); @@ -1740,7 +1733,7 @@ int GetRandomValue(int min, int max) min = tmp; } -#if defined(SUPPORT_RPRAND_GENERATOR) +#if SUPPORT_RPRAND_GENERATOR value = rprand_get_value(min, max); #else // WARNING: Ranges higher than RAND_MAX will return invalid results @@ -1779,6 +1772,7 @@ int GetRandomValue(int min, int max) value = min + (int)(r%m); } #endif + return value; } @@ -1787,7 +1781,7 @@ int *LoadRandomSequence(unsigned int count, int min, int max) { int *values = NULL; -#if defined(SUPPORT_RPRAND_GENERATOR) +#if SUPPORT_RPRAND_GENERATOR values = rprand_load_sequence(count, min, max); #else if (count > ((unsigned int)abs(max - min) + 1)) return values; // Security check @@ -1818,13 +1812,14 @@ int *LoadRandomSequence(unsigned int count, int min, int max) } } #endif + return values; } // Unload random values sequence void UnloadRandomSequence(int *sequence) { -#if defined(SUPPORT_RPRAND_GENERATOR) +#if SUPPORT_RPRAND_GENERATOR rprand_unload_sequence(sequence); #else RL_FREE(sequence); @@ -1835,7 +1830,7 @@ void UnloadRandomSequence(int *sequence) // NOTE: Provided fileName should not contain paths, saving to working directory void TakeScreenshot(const char *fileName) { -#if defined(SUPPORT_MODULE_RTEXTURES) +#if SUPPORT_MODULE_RTEXTURES // Security check to (partially) avoid malicious code if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } @@ -1883,7 +1878,7 @@ void SetTraceLogLevel(int logType) { logTypeLevel = logType; } // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) void TraceLog(int logType, const char *text, ...) { -#if defined(SUPPORT_TRACELOG) +#if SUPPORT_TRACELOG // Message has level below current threshold, don't emit if ((logType < logTypeLevel) || (text == NULL)) return; @@ -1932,8 +1927,7 @@ void TraceLog(int logType, const char *text, ...) va_end(args); if (logType == LOG_FATAL) exit(EXIT_FAILURE); // If fatal logging, exit program - -#endif // SUPPORT_TRACELOG +#endif } // Set custom trace log @@ -1977,12 +1971,8 @@ unsigned char *LoadFileData(const char *fileName, int *dataSize) if (fileName != NULL) { - if (loadFileData) - { - data = loadFileData(fileName, dataSize); - return data; - } -#if defined(SUPPORT_STANDARD_FILEIO) + if (loadFileData) return loadFileData(fileName, dataSize); + FILE *file = fopen(fileName, "rb"); if (file != NULL) @@ -2026,9 +2016,6 @@ unsigned char *LoadFileData(const char *fileName, int *dataSize) fclose(file); } else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName); -#else - TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback"); -#endif } else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); @@ -2048,11 +2035,8 @@ bool SaveFileData(const char *fileName, void *data, int dataSize) if (fileName != NULL) { - if (saveFileData) - { - return saveFileData(fileName, data, dataSize); - } -#if defined(SUPPORT_STANDARD_FILEIO) + if (saveFileData) return saveFileData(fileName, data, dataSize); + FILE *file = fopen(fileName, "wb"); if (file != NULL) @@ -2069,9 +2053,6 @@ bool SaveFileData(const char *fileName, void *data, int dataSize) if (result == 0) success = true; } else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName); -#else - TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback"); -#endif } else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); @@ -2139,12 +2120,8 @@ char *LoadFileText(const char *fileName) if (fileName != NULL) { - if (loadFileText) - { - text = loadFileText(fileName); - return text; - } -#if defined(SUPPORT_STANDARD_FILEIO) + if (loadFileText) return loadFileText(fileName); + FILE *file = fopen(fileName, "rt"); if (file != NULL) @@ -2180,9 +2157,6 @@ char *LoadFileText(const char *fileName) fclose(file); } else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName); -#else - TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback"); -#endif } else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); @@ -2202,11 +2176,8 @@ bool SaveFileText(const char *fileName, const char *text) if (fileName != NULL) { - if (saveFileText) - { - return saveFileText(fileName, text); - } -#if defined(SUPPORT_STANDARD_FILEIO) + if (saveFileText) return saveFileText(fileName, text); + FILE *file = fopen(fileName, "wt"); if (file != NULL) @@ -2220,9 +2191,6 @@ bool SaveFileText(const char *fileName, const char *text) if (result == 0) success = true; } else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName); -#else - TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback"); -#endif } else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); @@ -2332,7 +2300,7 @@ int FileTextReplace(const char *fileName, const char *search, const char *replac char *fileText = NULL; char *fileTextUpdated = { 0 }; -#if defined(SUPPORT_MODULE_RTEXT) +#if SUPPORT_MODULE_RTEXT if (FileExists(fileName)) { fileText = LoadFileText(fileName); @@ -3025,7 +2993,7 @@ unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDa unsigned char *compData = NULL; -#if defined(SUPPORT_COMPRESSION_API) +#if SUPPORT_COMPRESSION_API // Compress data and generate a valid DEFLATE stream 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); @@ -3045,7 +3013,7 @@ unsigned char *DecompressData(const unsigned char *compData, int compDataSize, i { unsigned char *data = NULL; -#if defined(SUPPORT_COMPRESSION_API) +#if SUPPORT_COMPRESSION_API // Decompress data from a valid DEFLATE stream unsigned char *data0 = (unsigned char *)RL_CALLOC(MAX_DECOMPRESSION_SIZE*1024*1024, 1); int size = sinflate(data0, MAX_DECOMPRESSION_SIZE*1024*1024, compData, compDataSize); @@ -3582,7 +3550,7 @@ AutomationEventList LoadAutomationEventList(const char *fileName) list.events = (AutomationEvent *)RL_CALLOC(MAX_AUTOMATION_EVENTS, sizeof(AutomationEvent)); list.capacity = MAX_AUTOMATION_EVENTS; -#if defined(SUPPORT_AUTOMATION_EVENTS) +#if SUPPORT_AUTOMATION_EVENTS if (fileName == NULL) TRACELOG(LOG_INFO, "AUTOMATION: New empty events list loaded successfully"); else { @@ -3658,7 +3626,7 @@ AutomationEventList LoadAutomationEventList(const char *fileName) // Unload automation events list from file void UnloadAutomationEventList(AutomationEventList list) { -#if defined(SUPPORT_AUTOMATION_EVENTS) +#if SUPPORT_AUTOMATION_EVENTS RL_FREE(list.events); #endif } @@ -3668,7 +3636,7 @@ bool ExportAutomationEventList(AutomationEventList list, const char *fileName) { bool success = false; -#if defined(SUPPORT_AUTOMATION_EVENTS) +#if SUPPORT_AUTOMATION_EVENTS // Export events as binary file // NOTE: Code not used, only for reference if required in the future /* @@ -3726,7 +3694,7 @@ bool ExportAutomationEventList(AutomationEventList list, const char *fileName) // Setup automation event list to record to void SetAutomationEventList(AutomationEventList *list) { -#if defined(SUPPORT_AUTOMATION_EVENTS) +#if SUPPORT_AUTOMATION_EVENTS currentEventList = list; #endif } @@ -3740,7 +3708,7 @@ void SetAutomationEventBaseFrame(int frame) // Start recording automation events (AutomationEventList must be set) void StartAutomationEventRecording(void) { -#if defined(SUPPORT_AUTOMATION_EVENTS) +#if SUPPORT_AUTOMATION_EVENTS automationEventRecording = true; #endif } @@ -3748,7 +3716,7 @@ void StartAutomationEventRecording(void) // Stop recording automation events void StopAutomationEventRecording(void) { -#if defined(SUPPORT_AUTOMATION_EVENTS) +#if SUPPORT_AUTOMATION_EVENTS automationEventRecording = false; #endif } @@ -3756,7 +3724,7 @@ void StopAutomationEventRecording(void) // Play a recorded automation event void PlayAutomationEvent(AutomationEvent event) { -#if defined(SUPPORT_AUTOMATION_EVENTS) +#if SUPPORT_AUTOMATION_EVENTS // WARNING: When should event be played? After/before/replace PollInputEvents()? -> Up to the user! if (!automationEventRecording) @@ -3805,7 +3773,7 @@ void PlayAutomationEvent(AutomationEvent event) { CORE.Input.Gamepad.axisState[event.params[0]][event.params[1]] = ((float)event.params[2]/32768.0f); } break; - #if defined(SUPPORT_GESTURES_SYSTEM) + #if SUPPORT_GESTURES_SYSTEM case INPUT_GESTURE: GESTURES.current = event.params[0]; break; // param[0]: gesture (enum Gesture) -> rgestures.h: GESTURES.current #endif // Window event @@ -3813,9 +3781,8 @@ void PlayAutomationEvent(AutomationEvent event) case WINDOW_MAXIMIZE: MaximizeWindow(); break; case WINDOW_MINIMIZE: MinimizeWindow(); break; case WINDOW_RESIZE: SetWindowSize(event.params[0], event.params[1]); break; - // Custom event - #if defined(SUPPORT_SCREEN_CAPTURE) + #if SUPPORT_SCREEN_CAPTURE case ACTION_TAKE_SCREENSHOT: { TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); @@ -4259,7 +4226,7 @@ void InitTimer(void) // However, it can also reduce overall system performance, because the thread scheduler switches tasks more often // High resolutions can also prevent the CPU power management system from entering power-saving modes // Setting a higher resolution does not improve the accuracy of the high-resolution performance counter -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) && !defined(PLATFORM_DESKTOP_SDL) +#if defined(_WIN32) && SUPPORT_WINMM_HIGHRES_TIMER && !SUPPORT_BUSY_WAIT_LOOP && !defined(PLATFORM_DESKTOP_SDL) timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms) #endif @@ -4352,7 +4319,7 @@ static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath); // Maybe it's a file... } -#if defined(SUPPORT_AUTOMATION_EVENTS) +#if SUPPORT_AUTOMATION_EVENTS // Automation event recording // Checking events in current frame and save them into currentEventList // NOTE: Recording is by default done at EndDrawing(), before PollInputEvents() @@ -4593,7 +4560,7 @@ static void RecordAutomationEvent(void) } //------------------------------------------------------------------------------------- -#if defined(SUPPORT_GESTURES_SYSTEM) +#if SUPPORT_GESTURES_SYSTEM // Gestures input currentEventList->events recording //------------------------------------------------------------------------------------- if (GESTURES.current != GESTURE_NONE) @@ -4655,5 +4622,4 @@ const char *TextFormat(const char *text, ...) return currentBuffer; } - -#endif // !SUPPORT_MODULE_RTEXT +#endif diff --git a/src/rlgl.h b/src/rlgl.h index cb618d94a..84f37616a 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1134,7 +1134,7 @@ typedef struct rlglData { } ExtSupported; // Extensions supported flags } rlglData; -#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 +#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 //---------------------------------------------------------------------------------- // Global Variables Definition @@ -1144,7 +1144,7 @@ static double rlCullDistanceFar = RL_CULL_DISTANCE_FAR; #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) static rlglData RLGL = { 0 }; -#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 +#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 static bool isGpuReady = false; #if defined(GRAPHICS_API_OPENGL_ES2) && !defined(GRAPHICS_API_OPENGL_ES3) @@ -1165,10 +1165,10 @@ static PFNGLVERTEXATTRIBDIVISOREXTPROC glVertexAttribDivisor = NULL; #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) static void rlLoadShaderDefault(void); // Load default shader static void rlUnloadShaderDefault(void); // Unload default shader -#if defined(RLGL_SHOW_GL_DETAILS_INFO) +#if RLGL_SHOW_GL_DETAILS_INFO static const char *rlGetCompressedFormatName(int format); // Get compressed format official GL identifier name -#endif // RLGL_SHOW_GL_DETAILS_INFO -#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 +#endif +#endif static int rlGetPixelDataSize(int width, int height, int format); // Get pixel data size in bytes (image or texture) @@ -2210,7 +2210,7 @@ void rlSetBlendFactorsSeparate(int glSrcRGB, int glDstRGB, int glSrcAlpha, int g //---------------------------------------------------------------------------------- // Module Functions Definition - OpenGL Debug //---------------------------------------------------------------------------------- -#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) && defined(GRAPHICS_API_OPENGL_43) +#if defined(GRAPHICS_API_OPENGL_43) && RLGL_ENABLE_OPENGL_DEBUG_CONTEXT static void GLAPIENTRY rlDebugMessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam) { // Ignore non-significant error/warning codes (NVidia drivers) @@ -2276,8 +2276,8 @@ void rlglInit(int width, int height) { isGpuReady = true; - // Enable OpenGL debug context if required -#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) && defined(GRAPHICS_API_OPENGL_43) + // Enable OpenGL debug context if requested (and supported) +#if defined(GRAPHICS_API_OPENGL_43) && RLGL_ENABLE_OPENGL_DEBUG_CONTEXT if ((glDebugMessageCallback != NULL) && (glDebugMessageControl != NULL)) { glDebugMessageCallback(rlDebugMessageCallback, 0); @@ -2321,7 +2321,7 @@ void rlglInit(int width, int height) RLGL.State.projection = rlMatrixIdentity(); RLGL.State.modelview = rlMatrixIdentity(); RLGL.State.currentMatrix = &RLGL.State.modelview; -#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 +#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) // Initialize software renderer backend @@ -2405,7 +2405,7 @@ void rlLoadExtensions(void *loader) glGetIntegerv(GL_NUM_EXTENSIONS, &numExt); TRACELOG(RL_LOG_INFO, "GL: Supported extensions count: %i", numExt); -#if defined(RLGL_SHOW_GL_DETAILS_INFO) +#if RLGL_SHOW_GL_DETAILS_INFO // Get supported extensions list // WARNING: glGetStringi() not available on OpenGL 2.1 TRACELOG(RL_LOG_INFO, "GL: OpenGL extensions:"); @@ -2447,7 +2447,7 @@ void rlLoadExtensions(void *loader) RLGL.ExtSupported.ssbo = GLAD_GL_ARB_shader_storage_buffer_object; #endif -#endif // GRAPHICS_API_OPENGL_33 +#endif // GRAPHICS_API_OPENGL_33 #if defined(GRAPHICS_API_OPENGL_ES3) // Register supported extensions flags @@ -2503,7 +2503,7 @@ void rlLoadExtensions(void *loader) TRACELOG(RL_LOG_INFO, "GL: Supported extensions count: %i", numExt); -#if defined(RLGL_SHOW_GL_DETAILS_INFO) +#if RLGL_SHOW_GL_DETAILS_INFO TRACELOG(RL_LOG_INFO, "GL: OpenGL extensions:"); for (int i = 0; i < numExt; i++) TRACELOG(RL_LOG_INFO, " %s", extList[i]); #endif @@ -2613,7 +2613,7 @@ void rlLoadExtensions(void *loader) // Free extensions pointers RL_FREE(extList); RL_FREE(extensionsDup); // Duplicated string must be deallocated -#endif // GRAPHICS_API_OPENGL_ES2 +#endif // GRAPHICS_API_OPENGL_ES2 // Check OpenGL information and capabilities //------------------------------------------------------------------------------ @@ -2633,7 +2633,7 @@ void rlLoadExtensions(void *loader) #endif glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &RLGL.ExtSupported.maxAnisotropyLevel); -#if defined(RLGL_SHOW_GL_DETAILS_INFO) +#if RLGL_SHOW_GL_DETAILS_INFO // Show some OpenGL GPU capabilities TRACELOG(RL_LOG_INFO, "GL: OpenGL capabilities:"); GLint capability = 0; @@ -2664,8 +2664,9 @@ void rlLoadExtensions(void *loader) TRACELOG(RL_LOG_INFO, " GL_MAX_VERTEX_ATTRIB_BINDINGS: %i", capability); glGetIntegerv(GL_MAX_UNIFORM_LOCATIONS, &capability); TRACELOG(RL_LOG_INFO, " GL_MAX_UNIFORM_LOCATIONS: %i", capability); -#endif // GRAPHICS_API_OPENGL_43 -#else // RLGL_SHOW_GL_DETAILS_INFO +#endif + +#else // !RLGL_SHOW_GL_DETAILS_INFO // Show some basic info about GL supported features if (RLGL.ExtSupported.vao) TRACELOG(RL_LOG_INFO, "GL: VAO extension detected, VAO functions loaded successfully"); @@ -2679,9 +2680,9 @@ void rlLoadExtensions(void *loader) if (RLGL.ExtSupported.texCompASTC) TRACELOG(RL_LOG_INFO, "GL: ASTC compressed textures supported"); if (RLGL.ExtSupported.computeShader) TRACELOG(RL_LOG_INFO, "GL: Compute shaders supported"); if (RLGL.ExtSupported.ssbo) TRACELOG(RL_LOG_INFO, "GL: Shader storage buffer objects supported"); -#endif // RLGL_SHOW_GL_DETAILS_INFO +#endif -#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 +#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 } // Get OpenGL procedure address @@ -3291,7 +3292,7 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, return id; } #endif -#endif // GRAPHICS_API_OPENGL_11 +#endif // GRAPHICS_API_OPENGL_11 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); @@ -5109,7 +5110,7 @@ static void rlUnloadShaderDefault(void) TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Default shader unloaded successfully", RLGL.State.defaultShaderId); } -#if defined(RLGL_SHOW_GL_DETAILS_INFO) +#if RLGL_SHOW_GL_DETAILS_INFO // Get compressed format official GL identifier name static const char *rlGetCompressedFormatName(int format) { @@ -5183,9 +5184,9 @@ static const char *rlGetCompressedFormatName(int format) default: return "GL_COMPRESSED_UNKNOWN"; break; } } -#endif // RLGL_SHOW_GL_DETAILS_INFO +#endif -#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 +#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 // Get pixel data size in bytes (image or texture) // NOTE: Size depends on pixel format @@ -5388,4 +5389,4 @@ static Matrix rlMatrixInvert(Matrix mat) } #endif -#endif // RLGL_IMPLEMENTATION \ No newline at end of file +#endif // RLGL_IMPLEMENTATION diff --git a/src/rmodels.c b/src/rmodels.c index d2bc4f707..9d83b7b4d 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -44,7 +44,7 @@ #include "config.h" // Defines module configuration flags -#if defined(SUPPORT_MODULE_RMODELS) +#if SUPPORT_MODULE_RMODELS #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 #include "raymath.h" // Required for: Vector3, Quaternion and Matrix functionality @@ -54,7 +54,7 @@ #include // Required for: memcmp(), strlen(), strncpy() #include // Required for: sinf(), cosf(), sqrtf(), fabsf() -#if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL) +#if SUPPORT_FILEFORMAT_OBJ || SUPPORT_FILEFORMAT_MTL #define TINYOBJ_MALLOC RL_MALLOC #define TINYOBJ_CALLOC RL_CALLOC #define TINYOBJ_REALLOC RL_REALLOC @@ -64,7 +64,7 @@ #include "external/tinyobj_loader_c.h" // OBJ/MTL file formats loading #endif -#if defined(SUPPORT_FILEFORMAT_GLTF) +#if SUPPORT_FILEFORMAT_GLTF #define CGLTF_MALLOC RL_MALLOC #define CGLTF_FREE RL_FREE @@ -72,7 +72,7 @@ #include "external/cgltf.h" // glTF file format loading #endif -#if defined(SUPPORT_FILEFORMAT_VOX) +#if SUPPORT_FILEFORMAT_VOX #define VOX_MALLOC RL_MALLOC #define VOX_CALLOC RL_CALLOC #define VOX_REALLOC RL_REALLOC @@ -82,7 +82,7 @@ #include "external/vox_loader.h" // VOX file format loading (MagikaVoxel) #endif -#if defined(SUPPORT_FILEFORMAT_M3D) +#if SUPPORT_FILEFORMAT_M3D #define M3D_MALLOC RL_MALLOC #define M3D_REALLOC RL_REALLOC #define M3D_FREE RL_FREE @@ -91,7 +91,7 @@ #include "external/m3d.h" // Model3D file format loading #endif -#if defined(SUPPORT_MESH_GENERATION) +#if SUPPORT_MESH_GENERATION #define PAR_MALLOC(T, N) ((T *)RL_MALLOC(N*sizeof(T))) #define PAR_CALLOC(T, N) ((T *)RL_CALLOC(N*sizeof(T), 1)) #define PAR_REALLOC(T, BUF, N) ((T *)RL_REALLOC(BUF, sizeof(T)*(N))) @@ -126,7 +126,13 @@ #define MAX_MATERIAL_MAPS 12 // Maximum number of maps supported #endif #ifndef MAX_MESH_VERTEX_BUFFERS +#if SUPPORT_GPU_SKINNING + // NOTE: Two additional vertex buffers required to store bone indices and bone weights + // WARNING: Some GPUs could not support more than 8 VBOs #define MAX_MESH_VERTEX_BUFFERS 9 // Maximum vertex buffers (VBO) per mesh +#else + #define MAX_MESH_VERTEX_BUFFERS 7 // Maximum vertex buffers (VBO) per mesh +#endif #endif #ifndef MAX_FILEPATH_LENGTH #define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value) @@ -145,25 +151,25 @@ //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -#if defined(SUPPORT_FILEFORMAT_OBJ) +#if SUPPORT_FILEFORMAT_OBJ static Model LoadOBJ(const char *fileName); // Load OBJ mesh data #endif -#if defined(SUPPORT_FILEFORMAT_IQM) +#if SUPPORT_FILEFORMAT_IQM static Model LoadIQM(const char *fileName); // Load IQM mesh data static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCount); // Load IQM animation data #endif -#if defined(SUPPORT_FILEFORMAT_GLTF) +#if SUPPORT_FILEFORMAT_GLTF static Model LoadGLTF(const char *fileName); // Load GLTF mesh data static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCount); // Load GLTF animation data #endif -#if defined(SUPPORT_FILEFORMAT_VOX) +#if SUPPORT_FILEFORMAT_VOX static Model LoadVOX(const char *filename); // Load VOX mesh data #endif -#if defined(SUPPORT_FILEFORMAT_M3D) +#if SUPPORT_FILEFORMAT_M3D static Model LoadM3D(const char *filename); // Load M3D mesh data static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCount); // Load M3D animation data #endif -#if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL) +#if SUPPORT_FILEFORMAT_OBJ || SUPPORT_FILEFORMAT_MTL static void ProcessMaterialsOBJ(Material *rayMaterials, tinyobj_material_t *materials, int materialCount); // Process obj materials #endif @@ -1099,19 +1105,19 @@ Model LoadModel(const char *fileName) { Model model = { 0 }; -#if defined(SUPPORT_FILEFORMAT_OBJ) +#if SUPPORT_FILEFORMAT_OBJ if (IsFileExtension(fileName, ".obj")) model = LoadOBJ(fileName); #endif -#if defined(SUPPORT_FILEFORMAT_IQM) +#if SUPPORT_FILEFORMAT_IQM if (IsFileExtension(fileName, ".iqm")) model = LoadIQM(fileName); #endif -#if defined(SUPPORT_FILEFORMAT_GLTF) +#if SUPPORT_FILEFORMAT_GLTF if (IsFileExtension(fileName, ".gltf") || IsFileExtension(fileName, ".glb")) model = LoadGLTF(fileName); #endif -#if defined(SUPPORT_FILEFORMAT_VOX) +#if SUPPORT_FILEFORMAT_VOX if (IsFileExtension(fileName, ".vox")) model = LoadVOX(fileName); #endif -#if defined(SUPPORT_FILEFORMAT_M3D) +#if SUPPORT_FILEFORMAT_M3D if (IsFileExtension(fileName, ".m3d")) model = LoadM3D(fileName); #endif @@ -1276,7 +1282,7 @@ void UploadMesh(Mesh *mesh, bool dynamic) mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT] = 0; // Vertex buffer: tangents mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2] = 0; // Vertex buffer: texcoords2 mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES] = 0; // Vertex buffer: indices -#ifdef SUPPORT_GPU_SKINNING +#if SUPPORT_GPU_SKINNING mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES] = 0; // Vertex buffer: boneIndices mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS] = 0; // Vertex buffer: boneWeights #endif @@ -1377,7 +1383,7 @@ void UploadMesh(Mesh *mesh, bool dynamic) rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2); } -#ifdef SUPPORT_GPU_SKINNING +#if SUPPORT_GPU_SKINNING if (mesh->boneIndices != NULL) { // Enable vertex attribute: boneIndices (shader-location = 7) @@ -1609,7 +1615,7 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]); } -#ifdef SUPPORT_GPU_SKINNING +#if SUPPORT_GPU_SKINNING // Bind mesh VBO data: vertex bone ids (shader-location = 6, if available) if (material.shader.locs[SHADER_LOC_VERTEX_BONEIDS] != -1) { @@ -1848,7 +1854,7 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]); } -#ifdef SUPPORT_GPU_SKINNING +#if SUPPORT_GPU_SKINNING // Bind mesh VBO data: vertex bone ids (shader-location = 6, if available) if (material.shader.locs[SHADER_LOC_VERTEX_BONEIDS] != -1) { @@ -2125,7 +2131,7 @@ bool ExportMeshAsCode(Mesh mesh, const char *fileName) return success; } -#if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL) +#if SUPPORT_FILEFORMAT_OBJ || SUPPORT_FILEFORMAT_MTL // Process obj materials static void ProcessMaterialsOBJ(Material *materials, tinyobj_material_t *mats, int materialCount) { @@ -2169,7 +2175,7 @@ Material *LoadMaterials(const char *fileName, int *materialCount) // TODO: Support IQM and GLTF for materials parsing -#if defined(SUPPORT_FILEFORMAT_MTL) +#if SUPPORT_FILEFORMAT_MTL if (IsFileExtension(fileName, ".mtl")) { tinyobj_material_t *mats = NULL; @@ -2263,13 +2269,13 @@ ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount) { ModelAnimation *animations = NULL; -#if defined(SUPPORT_FILEFORMAT_IQM) +#if SUPPORT_FILEFORMAT_IQM if (IsFileExtension(fileName, ".iqm")) animations = LoadModelAnimationsIQM(fileName, animCount); #endif -#if defined(SUPPORT_FILEFORMAT_M3D) +#if SUPPORT_FILEFORMAT_M3D if (IsFileExtension(fileName, ".m3d")) animations = LoadModelAnimationsM3D(fileName, animCount); #endif -#if defined(SUPPORT_FILEFORMAT_GLTF) +#if SUPPORT_FILEFORMAT_GLTF if (IsFileExtension(fileName, ".gltf;.glb")) animations = LoadModelAnimationsGLTF(fileName, animCount); #endif @@ -2548,7 +2554,7 @@ bool IsModelAnimationValid(Model model, ModelAnimation anim) return result; } -#if defined(SUPPORT_MESH_GENERATION) +#if SUPPORT_MESH_GENERATION // Generate polygonal mesh Mesh GenMeshPoly(int sides, float radius) { @@ -3691,7 +3697,7 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) return mesh; } -#endif // SUPPORT_MESH_GENERATION +#endif // SUPPORT_MESH_GENERATION // Compute mesh bounding box limits // NOTE: minVertex and maxVertex should be transformed by model transform matrix @@ -4378,7 +4384,7 @@ RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Ve //---------------------------------------------------------------------------------- // Module Internal Functions Definition //---------------------------------------------------------------------------------- -#if defined(SUPPORT_FILEFORMAT_IQM) || defined(SUPPORT_FILEFORMAT_GLTF) +#if SUPPORT_FILEFORMAT_IQM || SUPPORT_FILEFORMAT_GLTF // Build pose from parent joints // NOTE: Required for animations loading (required by IQM and GLTF) static void BuildPoseFromParentJoints(BoneInfo *bones, int boneCount, Transform *transforms) @@ -4402,7 +4408,7 @@ static void BuildPoseFromParentJoints(BoneInfo *bones, int boneCount, Transform } #endif -#if defined(SUPPORT_FILEFORMAT_OBJ) +#if SUPPORT_FILEFORMAT_OBJ // Load OBJ mesh data // Notes to keep in mind: // - A mesh is created for every material present in the obj file @@ -4653,7 +4659,7 @@ static Model LoadOBJ(const char *fileName) } #endif -#if defined(SUPPORT_FILEFORMAT_IQM) +#if SUPPORT_FILEFORMAT_IQM // Load IQM mesh data static Model LoadIQM(const char *fileName) { @@ -4834,7 +4840,7 @@ static Model LoadIQM(const char *fileName) model.meshes[i].triangleCount = imesh[i].num_triangles; model.meshes[i].indices = (unsigned short *)RL_CALLOC(model.meshes[i].triangleCount*3, sizeof(unsigned short)); -#if !defined(SUPPORT_GPU_SKINNING) +#if !SUPPORT_GPU_SKINNING // 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 = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); @@ -5276,7 +5282,7 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou #endif -#if defined(SUPPORT_FILEFORMAT_GLTF) +#if SUPPORT_FILEFORMAT_GLTF // Load file data callback for cgltf static cgltf_result LoadFileGLTFCallback(const struct cgltf_memory_options *memoryOptions, const struct cgltf_file_options *fileOptions, const char *path, cgltf_size *size, void **data) { @@ -6317,7 +6323,7 @@ static Model LoadGLTF(const char *fileName) } } -#if !defined(SUPPORT_GPU_SKINNING) +#if !SUPPORT_GPU_SKINNING // Animated vertex data (CPU skinning) 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)); @@ -6676,7 +6682,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo } #endif -#if defined(SUPPORT_FILEFORMAT_VOX) +#if SUPPORT_FILEFORMAT_VOX // Load VOX (MagicaVoxel) mesh data static Model LoadVOX(const char *fileName) { @@ -6786,7 +6792,7 @@ static Model LoadVOX(const char *fileName) } #endif -#if defined(SUPPORT_FILEFORMAT_M3D) +#if SUPPORT_FILEFORMAT_M3D // Hook LoadFileData()/UnloadFileData() calls to M3D loaders unsigned char *m3d_loaderhook(char *fn, unsigned int *len) { return LoadFileData((const char *)fn, (int *)len); } void m3d_freehook(void *data) { UnloadFileData((unsigned char *)data); } @@ -6924,7 +6930,7 @@ static Model LoadM3D(const char *fileName) { model.meshes[k].boneIndices = (unsigned char *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); model.meshes[k].boneWeights = (float *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(float)); -#if !defined(SUPPORT_GPU_SKINNING) +#if !SUPPORT_GPU_SKINNING model.meshes[k].animVertices = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float)); model.meshes[k].animNormals = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float)); #endif @@ -7131,7 +7137,7 @@ static Model LoadM3D(const char *fileName) { model.meshes[i].boneCount = model.skeleton.boneCount; -#if !defined(SUPPORT_GPU_SKINNING) +#if !SUPPORT_GPU_SKINNING // Initialize vertex buffers for CPU skinning memcpy(model.meshes[i].animVertices, model.meshes[i].vertices, model.meshes[i].vertexCount*3*sizeof(float)); memcpy(model.meshes[i].animNormals, model.meshes[i].normals, model.meshes[i].vertexCount*3*sizeof(float)); @@ -7268,4 +7274,4 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou } #endif -#endif // SUPPORT_MODULE_RMODELS +#endif // SUPPORT_MODULE_RMODELS diff --git a/src/rshapes.c b/src/rshapes.c index 35614bd6c..a8684e353 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -48,7 +48,7 @@ #include "config.h" // Defines module configuration flags -#if defined(SUPPORT_MODULE_RSHAPES) +#if SUPPORT_MODULE_RSHAPES #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 @@ -128,7 +128,7 @@ void DrawPixel(int posX, int posY, Color color) // Draw a pixel (Vector version) void DrawPixelV(Vector2 position, Color color) { -#if defined(SUPPORT_QUADS_DRAW_MODE) +#if SUPPORT_QUADS_DRAW_MODE rlSetTexture(GetShapesTexture().id); Rectangle shapeRect = GetShapesTextureRectangle(); @@ -353,7 +353,7 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA float stepLength = (endAngle - startAngle)/(float)segments; float angle = startAngle; -#if defined(SUPPORT_QUADS_DRAW_MODE) +#if SUPPORT_QUADS_DRAW_MODE rlSetTexture(GetShapesTexture().id); Rectangle shapeRect = GetShapesTextureRectangle(); @@ -594,7 +594,7 @@ void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startA float stepLength = (endAngle - startAngle)/(float)segments; float angle = startAngle; -#if defined(SUPPORT_QUADS_DRAW_MODE) +#if SUPPORT_QUADS_DRAW_MODE rlSetTexture(GetShapesTexture().id); Rectangle shapeRect = GetShapesTextureRectangle(); @@ -774,7 +774,7 @@ void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color bottomRight.y = y + (dx + rec.width)*sinRotation + (dy + rec.height)*cosRotation; } -#if defined(SUPPORT_QUADS_DRAW_MODE) +#if SUPPORT_QUADS_DRAW_MODE rlSetTexture(GetShapesTexture().id); Rectangle shapeRect = GetShapesTextureRectangle(); @@ -884,7 +884,7 @@ void DrawRectangleLines(int posX, int posY, int width, int height, Color color) /* // Previous implementation, it has issues... but it does not require view matrix... -#if defined(SUPPORT_QUADS_DRAW_MODE) +#if SUPPORT_QUADS_DRAW_MODE DrawRectangle(posX, posY, width, 1, color); DrawRectangle(posX + width - 1, posY + 1, 1, height - 2, color); DrawRectangle(posX, posY + height - 1, width, 1, color); @@ -994,7 +994,7 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co const Vector2 centers[4] = { point[8], point[9], point[10], point[11] }; const float angles[4] = { 180.0f, 270.0f, 0.0f, 90.0f }; -#if defined(SUPPORT_QUADS_DRAW_MODE) +#if SUPPORT_QUADS_DRAW_MODE rlSetTexture(GetShapesTexture().id); Rectangle shapeRect = GetShapesTextureRectangle(); @@ -1248,7 +1248,7 @@ void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, int segments, f if (lineThick > 1) { -#if defined(SUPPORT_QUADS_DRAW_MODE) +#if SUPPORT_QUADS_DRAW_MODE rlSetTexture(GetShapesTexture().id); Rectangle shapeRect = GetShapesTextureRectangle(); @@ -1422,7 +1422,7 @@ void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, int segments, f // NOTE: Vertex must be provided in counter-clockwise order void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color) { -#if defined(SUPPORT_QUADS_DRAW_MODE) +#if SUPPORT_QUADS_DRAW_MODE rlSetTexture(GetShapesTexture().id); Rectangle shapeRect = GetShapesTextureRectangle(); @@ -1538,7 +1538,7 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col float centralAngle = rotation*DEG2RAD; float angleStep = 360.0f/(float)sides*DEG2RAD; -#if defined(SUPPORT_QUADS_DRAW_MODE) +#if SUPPORT_QUADS_DRAW_MODE rlSetTexture(GetShapesTexture().id); Rectangle shapeRect = GetShapesTextureRectangle(); @@ -1607,7 +1607,7 @@ void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, fl float exteriorAngle = 360.0f/(float)sides*DEG2RAD; float innerRadius = radius - (lineThick*cosf(DEG2RAD*exteriorAngle/2.0f)); -#if defined(SUPPORT_QUADS_DRAW_MODE) +#if SUPPORT_QUADS_DRAW_MODE rlSetTexture(GetShapesTexture().id); Rectangle shapeRect = GetShapesTextureRectangle(); @@ -1663,7 +1663,7 @@ void DrawSplineLinear(const Vector2 *points, int pointCount, float thick, Color { if (pointCount < 2) return; -#if defined(SUPPORT_SPLINE_MITERS) +#if SUPPORT_SPLINE_MITERS Vector2 prevNormal = (Vector2){-(points[1].y - points[0].y), (points[1].x - points[0].x)}; float prevLength = sqrtf(prevNormal.x*prevNormal.x + prevNormal.y*prevNormal.y); @@ -1770,8 +1770,8 @@ void DrawSplineLinear(const Vector2 *points, int pointCount, float thick, Color } #endif -#if defined(SUPPORT_SPLINE_SEGMENT_CAPS) - // TODO: Add spline segment rounded caps at the begin/end of the spline +#if SUPPORT_SPLINE_SEGMENT_CAPS + // TODO: Add spline segment rounded caps at the begin/end of the spline? #endif } @@ -2484,4 +2484,4 @@ static float EaseCubicInOut(float t, float b, float c, float d) return result; } -#endif // SUPPORT_MODULE_RSHAPES +#endif // SUPPORT_MODULE_RSHAPES diff --git a/src/rtext.c b/src/rtext.c index 840b452d3..162281224 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -6,27 +6,23 @@ * #define SUPPORT_MODULE_RTEXT * 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 -* * #define SUPPORT_FILEFORMAT_FNT * #define SUPPORT_FILEFORMAT_TTF * #define SUPPORT_FILEFORMAT_BDF * Selected desired fileformats to be supported for loading. Some of those formats are * supported by default, to remove support, just comment unrequired #define in this module * -* #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()] -* * #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH * TextSplit() function static buffer max size * * #define MAX_TEXTSPLIT_COUNT * TextSplit() function static substrings pointers array (pointing to static buffer) * +* #define FONT_ATLAS_CORNER_REC_SIZE +* On font atlas image generation [GenImageFontAtlas()], add a NxN 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()] +* * DEPENDENCIES: * stb_truetype - Load TTF file and rasterize characters data * stb_rect_pack - Rectangles packing algorithms, required for font atlas generation @@ -57,7 +53,7 @@ #include "config.h" // Defines module configuration flags -#if defined(SUPPORT_MODULE_RTEXT) +#if SUPPORT_MODULE_RTEXT #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 -> Only DrawTextPro() @@ -67,7 +63,7 @@ #include // Required for: va_list, va_start(), vsprintf(), va_end() [Used in TextFormat()] #include // Required for: toupper(), tolower() [Used in TextToUpper(), TextToLower()] -#if defined(SUPPORT_FILEFORMAT_TTF) || defined(SUPPORT_FILEFORMAT_BDF) +#if SUPPORT_FILEFORMAT_TTF || SUPPORT_FILEFORMAT_BDF #if defined(__GNUC__) // GCC and Clang #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" @@ -83,7 +79,7 @@ #endif #endif -#if defined(SUPPORT_FILEFORMAT_TTF) +#if SUPPORT_FILEFORMAT_TTF #if defined(__GNUC__) // GCC and Clang #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" @@ -105,14 +101,18 @@ // Defines and Macros //---------------------------------------------------------------------------------- #ifndef MAX_TEXT_BUFFER_LENGTH - #define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions: - // TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal(), TextSplit() -#endif -#ifndef MAX_TEXT_UNICODE_CHARS - #define MAX_TEXT_UNICODE_CHARS 512 // Maximum number of unicode codepoints: GetCodepoints() + #define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions: + // TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal(), TextSplit() #endif #ifndef MAX_TEXTSPLIT_COUNT - #define MAX_TEXTSPLIT_COUNT 128 // Maximum number of substrings to split: TextSplit() + #define MAX_TEXTSPLIT_COUNT 128 // Maximum number of substrings to split: TextSplit() +#endif + +#ifndef FONT_ATLAS_CORNER_REC_SIZE + // On font atlas image generation [GenImageFontAtlas()], add a 3x3 pixels white rectangle + // at the bottom-right corner of the atlas. It can be useful for shapes drawing, to allow + // drawing text and shapes with a single draw call [SetShapesTexture()] + #define FONT_ATLAS_CORNER_REC_SIZE 3 // Size of white rectangle drawn on font atlas on font loading #endif //---------------------------------------------------------------------------------- @@ -123,12 +123,12 @@ //---------------------------------------------------------------------------------- // Global variables //---------------------------------------------------------------------------------- -#if defined(SUPPORT_DEFAULT_FONT) // Default font provided by raylib // NOTE: Default font is loaded on InitWindow() and disposed on CloseWindow() [module: core] static Font defaultFont = { 0 }; -#endif -static int textLineSpacing = 2; // Text vertical line spacing in pixels (between lines) + +// Text vertical line spacing in pixels (between lines) +static int textLineSpacing = 2; //---------------------------------------------------------------------------------- // Other Modules Functions Declaration (required by text) @@ -138,22 +138,19 @@ static int textLineSpacing = 2; // Text vertical line spacing in pixels (between //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -#if defined(SUPPORT_FILEFORMAT_FNT) +#if SUPPORT_FILEFORMAT_FNT static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file) #endif -#if defined(SUPPORT_FILEFORMAT_BDF) +#if SUPPORT_FILEFORMAT_BDF static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, const int *codepoints, int codepointCount, int *outFontSize); #endif -#if defined(SUPPORT_DEFAULT_FONT) extern void LoadFontDefault(void); extern void UnloadFontDefault(void); -#endif //---------------------------------------------------------------------------------- // Module Functions Definition //---------------------------------------------------------------------------------- -#if defined(SUPPORT_DEFAULT_FONT) // Load raylib default font extern void LoadFontDefault(void) { @@ -329,17 +326,11 @@ extern void UnloadFontDefault(void) defaultFont.glyphs = NULL; defaultFont.recs = NULL; } -#endif // SUPPORT_DEFAULT_FONT // Get the default font, useful to be used with extended parameters Font GetFontDefault() { -#if defined(SUPPORT_DEFAULT_FONT) return defaultFont; -#else - Font font = { 0 }; - return font; -#endif } // Load Font from file into GPU memory (VRAM) @@ -361,15 +352,15 @@ Font LoadFont(const char *fileName) Font font = { 0 }; -#if defined(SUPPORT_FILEFORMAT_TTF) +#if SUPPORT_FILEFORMAT_TTF if (IsFileExtension(fileName, ".ttf") || IsFileExtension(fileName, ".otf")) font = LoadFontEx(fileName, FONT_TTF_DEFAULT_SIZE, NULL, FONT_TTF_DEFAULT_NUMCHARS); else #endif -#if defined(SUPPORT_FILEFORMAT_FNT) +#if SUPPORT_FILEFORMAT_FNT if (IsFileExtension(fileName, ".fnt")) font = LoadBMFont(fileName); else #endif -#if defined(SUPPORT_FILEFORMAT_BDF) +#if SUPPORT_FILEFORMAT_BDF if (IsFileExtension(fileName, ".bdf")) font = LoadFontEx(fileName, FONT_TTF_DEFAULT_SIZE, NULL, FONT_TTF_DEFAULT_NUMCHARS); else #endif @@ -548,7 +539,7 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int font.baseSize = fontSize; font.glyphPadding = 0; -#if defined(SUPPORT_FILEFORMAT_TTF) +#if SUPPORT_FILEFORMAT_TTF if (TextIsEqual(fileExtLower, ".ttf") || TextIsEqual(fileExtLower, ".otf")) { @@ -556,7 +547,7 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int } else #endif -#if defined(SUPPORT_FILEFORMAT_BDF) +#if SUPPORT_FILEFORMAT_BDF if (TextIsEqual(fileExtLower, ".bdf")) { font.glyphs = LoadFontDataBDF(fileData, dataSize, codepoints, (codepointCount > 0)? codepointCount : 95, &font.baseSize); @@ -568,7 +559,7 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int font.glyphs = NULL; } -#if defined(SUPPORT_FILEFORMAT_TTF) || defined(SUPPORT_FILEFORMAT_BDF) +#if SUPPORT_FILEFORMAT_TTF || SUPPORT_FILEFORMAT_BDF if (font.glyphs != NULL) { font.glyphPadding = FONT_TTF_DEFAULT_CHARS_PADDING; @@ -629,7 +620,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz GlyphInfo *glyphs = NULL; int glyphCounter = 0; -#if defined(SUPPORT_FILEFORMAT_TTF) +#if SUPPORT_FILEFORMAT_TTF // Load font data (including pixel data) from TTF memory file // NOTE: Loaded information should be enough to generate font image atlas, using any packaging method if (fileData != NULL) @@ -789,7 +780,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz // Generate image font atlas using chars info // NOTE: Packing method: 0-Default, 1-Skyline -#if defined(SUPPORT_FILEFORMAT_TTF) || defined(SUPPORT_FILEFORMAT_BDF) +#if SUPPORT_FILEFORMAT_TTF || SUPPORT_FILEFORMAT_BDF Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod) { Image atlas = { 0 }; @@ -961,13 +952,12 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp RL_FREE(context); } -#if defined(SUPPORT_FONT_ATLAS_WHITE_REC) // Add a 3x3 white rectangle at the bottom-right corner of the generated atlas, // useful to use as the white texture to draw shapes with raylib // Security: ensure the atlas is large enough to hold a 3x3 rectangle - if ((atlas.width >= 3) && (atlas.height >= 3)) + if ((FONT_ATLAS_CORNER_REC_SIZE > 0) && (atlas.width >= 3) && (atlas.height >= 3)) { - for (int i = 0, k = atlas.width*atlas.height - 1; i < 3; i++) + for (int i = 0, k = atlas.width*atlas.height - 1; i < FONT_ATLAS_CORNER_REC_SIZE; i++) { ((unsigned char *)atlas.data)[k - 0] = 255; ((unsigned char *)atlas.data)[k - 1] = 255; @@ -975,7 +965,6 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp k -= atlas.width; } } -#endif // Convert image data from GRAYSCALE to GRAY_ALPHA unsigned char *dataGrayAlpha = (unsigned char *)RL_MALLOC(atlas.width*atlas.height*sizeof(unsigned char)*2); // Two channels @@ -1601,7 +1590,6 @@ float TextToFloat(const char *text) return value*sign; } -#if defined(SUPPORT_TEXT_MANIPULATION) // Copy one string to another, returns bytes copied // NOTE: Alternative implementation to strcpy(dst, src) from C standard library int TextCopy(char *dst, const char *src) @@ -2195,7 +2183,6 @@ const char *CodepointToUTF8(int codepoint, int *utf8Size) return utf8; } -#endif // SUPPORT_TEXT_MANIPULATION // Get next codepoint in a UTF-8 encoded text, scanning until '\0' is found // When an invalid UTF-8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned @@ -2370,7 +2357,7 @@ int GetCodepointPrevious(const char *text, int *codepointSize) //---------------------------------------------------------------------------------- // Module Internal Functions Definition //---------------------------------------------------------------------------------- -#if defined(SUPPORT_FILEFORMAT_FNT) || defined(SUPPORT_FILEFORMAT_BDF) +#if SUPPORT_FILEFORMAT_FNT || SUPPORT_FILEFORMAT_BDF // Read a line from memory // REQUIRES: memcpy() // NOTE: Returns the number of bytes read @@ -2384,7 +2371,7 @@ static int GetLine(const char *origin, char *buffer, int maxLength) } #endif -#if defined(SUPPORT_FILEFORMAT_FNT) +#if SUPPORT_FILEFORMAT_FNT // Load a BMFont file (AngelCode font file) // REQUIRES: strstr(), sscanf(), strrchr(), memcpy() static Font LoadBMFont(const char *fileName) @@ -2561,7 +2548,7 @@ static Font LoadBMFont(const char *fileName) } #endif -#if defined(SUPPORT_FILEFORMAT_BDF) +#if SUPPORT_FILEFORMAT_BDF // Convert hexadecimal to decimal (single digit) static unsigned char HexToInt(char hex) { diff --git a/src/rtextures.c b/src/rtextures.c index 1b147796e..a134c4659 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -27,10 +27,6 @@ * #define SUPPORT_IMAGE_EXPORT * Support image export in multiple file formats * -* #define SUPPORT_IMAGE_MANIPULATION -* Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... -* If not defined only some image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageResize*() -* * #define SUPPORT_IMAGE_GENERATION * Support procedural image generation functionality (gradient, spot, perlin-noise, cellular) * @@ -65,7 +61,7 @@ #include "config.h" // Defines module configuration flags -#if defined(SUPPORT_MODULE_RTEXTURES) +#if SUPPORT_MODULE_RTEXTURES #include "rlgl.h" // OpenGL abstraction layer to multiple versions @@ -103,32 +99,32 @@ #define STBI_NO_PNM #endif -#if defined(SUPPORT_FILEFORMAT_DDS) +#if SUPPORT_FILEFORMAT_DDS #define RL_GPUTEX_SUPPORT_DDS #endif -#if defined(SUPPORT_FILEFORMAT_PKM) +#if SUPPORT_FILEFORMAT_PKM #define RL_GPUTEX_SUPPORT_PKM #endif -#if defined(SUPPORT_FILEFORMAT_KTX) +#if SUPPORT_FILEFORMAT_KTX #define RL_GPUTEX_SUPPORT_KTX #endif -#if defined(SUPPORT_FILEFORMAT_PVR) +#if SUPPORT_FILEFORMAT_PVR #define RL_GPUTEX_SUPPORT_PVR #endif -#if defined(SUPPORT_FILEFORMAT_ASTC) +#if SUPPORT_FILEFORMAT_ASTC #define RL_GPUTEX_SUPPORT_ASTC #endif // Image fileformats not supported by default -#if (defined(SUPPORT_FILEFORMAT_BMP) || \ - defined(SUPPORT_FILEFORMAT_PNG) || \ - defined(SUPPORT_FILEFORMAT_TGA) || \ - defined(SUPPORT_FILEFORMAT_JPG) || \ - defined(SUPPORT_FILEFORMAT_PSD) || \ - defined(SUPPORT_FILEFORMAT_GIF) || \ - defined(SUPPORT_FILEFORMAT_HDR) || \ - defined(SUPPORT_FILEFORMAT_PIC) || \ - defined(SUPPORT_FILEFORMAT_PNM)) +#if (SUPPORT_FILEFORMAT_BMP || \ + SUPPORT_FILEFORMAT_PNG || \ + SUPPORT_FILEFORMAT_TGA || \ + SUPPORT_FILEFORMAT_JPG || \ + SUPPORT_FILEFORMAT_PSD || \ + SUPPORT_FILEFORMAT_GIF || \ + SUPPORT_FILEFORMAT_HDR || \ + SUPPORT_FILEFORMAT_PIC || \ + SUPPORT_FILEFORMAT_PNM) #if defined(__GNUC__) // GCC and Clang #pragma GCC diagnostic push @@ -177,7 +173,7 @@ #endif #endif -#if defined(SUPPORT_FILEFORMAT_QOI) +#if SUPPORT_FILEFORMAT_QOI #define QOI_MALLOC RL_MALLOC #define QOI_FREE RL_FREE @@ -195,7 +191,7 @@ #endif -#if defined(SUPPORT_IMAGE_EXPORT) +#if SUPPORT_IMAGE_EXPORT #define STBIW_MALLOC RL_MALLOC #define STBIW_FREE RL_FREE #define STBIW_REALLOC RL_REALLOC @@ -204,7 +200,7 @@ #include "external/stb_image_write.h" // Required for: stbi_write_*() #endif -#if defined(SUPPORT_IMAGE_GENERATION) +#if SUPPORT_IMAGE_GENERATION #define STB_PERLIN_IMPLEMENTATION #include "external/stb_perlin.h" // Required for: stb_perlin_fbm_noise3 #endif @@ -268,15 +264,15 @@ Image LoadImage(const char *fileName) { Image image = { 0 }; -#if defined(SUPPORT_FILEFORMAT_PNG) || \ - defined(SUPPORT_FILEFORMAT_BMP) || \ - defined(SUPPORT_FILEFORMAT_TGA) || \ - defined(SUPPORT_FILEFORMAT_JPG) || \ - defined(SUPPORT_FILEFORMAT_GIF) || \ - defined(SUPPORT_FILEFORMAT_PIC) || \ - defined(SUPPORT_FILEFORMAT_HDR) || \ - defined(SUPPORT_FILEFORMAT_PNM) || \ - defined(SUPPORT_FILEFORMAT_PSD) +#if SUPPORT_FILEFORMAT_PNG || \ + SUPPORT_FILEFORMAT_BMP || \ + SUPPORT_FILEFORMAT_TGA || \ + SUPPORT_FILEFORMAT_JPG || \ + SUPPORT_FILEFORMAT_GIF || \ + SUPPORT_FILEFORMAT_PIC || \ + SUPPORT_FILEFORMAT_HDR || \ + SUPPORT_FILEFORMAT_PNM || \ + SUPPORT_FILEFORMAT_PSD #define STBI_REQUIRED #endif @@ -338,7 +334,7 @@ Image LoadImageAnim(const char *fileName, int *frames) Image image = { 0 }; int frameCount = 0; -#if defined(SUPPORT_FILEFORMAT_GIF) +#if SUPPORT_FILEFORMAT_GIF if (IsFileExtension(fileName, ".gif")) { int dataSize = 0; @@ -383,7 +379,7 @@ Image LoadImageAnimFromMemory(const char *fileType, const unsigned char *fileDat // Security check for input data if ((fileType == NULL) || (fileData == NULL) || (dataSize == 0)) return image; -#if defined(SUPPORT_FILEFORMAT_GIF) +#if SUPPORT_FILEFORMAT_GIF if ((strcmp(fileType, ".gif") == 0) || (strcmp(fileType, ".GIF") == 0)) { if (fileData != NULL) @@ -430,30 +426,30 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i } if ((false) -#if defined(SUPPORT_FILEFORMAT_PNG) +#if SUPPORT_FILEFORMAT_PNG || (strcmp(fileType, ".png") == 0) || (strcmp(fileType, ".PNG") == 0) #endif -#if defined(SUPPORT_FILEFORMAT_BMP) +#if SUPPORT_FILEFORMAT_BMP || (strcmp(fileType, ".bmp") == 0) || (strcmp(fileType, ".BMP") == 0) #endif -#if defined(SUPPORT_FILEFORMAT_TGA) +#if SUPPORT_FILEFORMAT_TGA || (strcmp(fileType, ".tga") == 0) || (strcmp(fileType, ".TGA") == 0) #endif -#if defined(SUPPORT_FILEFORMAT_JPG) +#if SUPPORT_FILEFORMAT_JPG || (strcmp(fileType, ".jpg") == 0) || (strcmp(fileType, ".jpeg") == 0) || (strcmp(fileType, ".JPG") == 0) || (strcmp(fileType, ".JPEG") == 0) #endif -#if defined(SUPPORT_FILEFORMAT_GIF) +#if SUPPORT_FILEFORMAT_GIF || (strcmp(fileType, ".gif") == 0) || (strcmp(fileType, ".GIF") == 0) #endif -#if defined(SUPPORT_FILEFORMAT_PIC) +#if SUPPORT_FILEFORMAT_PIC || (strcmp(fileType, ".pic") == 0) || (strcmp(fileType, ".PIC") == 0) #endif -#if defined(SUPPORT_FILEFORMAT_PNM) +#if SUPPORT_FILEFORMAT_PNM || (strcmp(fileType, ".ppm") == 0) || (strcmp(fileType, ".pgm") == 0) || (strcmp(fileType, ".PPM") == 0) || (strcmp(fileType, ".PGM") == 0) #endif -#if defined(SUPPORT_FILEFORMAT_PSD) +#if SUPPORT_FILEFORMAT_PSD || (strcmp(fileType, ".psd") == 0) || (strcmp(fileType, ".PSD") == 0) #endif ) @@ -478,7 +474,7 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i } #endif } -#if defined(SUPPORT_FILEFORMAT_HDR) +#if SUPPORT_FILEFORMAT_HDR else if ((strcmp(fileType, ".hdr") == 0) || (strcmp(fileType, ".HDR") == 0)) { #if defined(STBI_REQUIRED) @@ -501,7 +497,7 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i #endif } #endif -#if defined(SUPPORT_FILEFORMAT_QOI) +#if SUPPORT_FILEFORMAT_QOI else if ((strcmp(fileType, ".qoi") == 0) || (strcmp(fileType, ".QOI") == 0)) { if (fileData != NULL) @@ -515,31 +511,31 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i } } #endif -#if defined(SUPPORT_FILEFORMAT_DDS) +#if SUPPORT_FILEFORMAT_DDS else if ((strcmp(fileType, ".dds") == 0) || (strcmp(fileType, ".DDS") == 0)) { image.data = rl_load_dds_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); } #endif -#if defined(SUPPORT_FILEFORMAT_PKM) +#if SUPPORT_FILEFORMAT_PKM else if ((strcmp(fileType, ".pkm") == 0) || (strcmp(fileType, ".PKM") == 0)) { image.data = rl_load_pkm_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); } #endif -#if defined(SUPPORT_FILEFORMAT_KTX) +#if SUPPORT_FILEFORMAT_KTX else if ((strcmp(fileType, ".ktx") == 0) || (strcmp(fileType, ".KTX") == 0)) { image.data = rl_load_ktx_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); } #endif -#if defined(SUPPORT_FILEFORMAT_PVR) +#if SUPPORT_FILEFORMAT_PVR else if ((strcmp(fileType, ".pvr") == 0) || (strcmp(fileType, ".PVR") == 0)) { image.data = rl_load_pvr_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); } #endif -#if defined(SUPPORT_FILEFORMAT_ASTC) +#if SUPPORT_FILEFORMAT_ASTC else if ((strcmp(fileType, ".astc") == 0) || (strcmp(fileType, ".ASTC") == 0)) { image.data = rl_load_astc_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); @@ -628,7 +624,7 @@ bool ExportImage(Image image, const char *fileName) // Security check for input data if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return result; -#if defined(SUPPORT_IMAGE_EXPORT) +#if SUPPORT_IMAGE_EXPORT int channels = 4; bool allocatedData = false; unsigned char *imgData = (unsigned char *)image.data; @@ -645,7 +641,7 @@ bool ExportImage(Image image, const char *fileName) } if (false) { } // Required to attach following 'else' cases -#if defined(SUPPORT_FILEFORMAT_PNG) +#if SUPPORT_FILEFORMAT_PNG else if (IsFileExtension(fileName, ".png")) { int dataSize = 0; @@ -654,17 +650,17 @@ bool ExportImage(Image image, const char *fileName) RL_FREE(fileData); } #endif -#if defined(SUPPORT_FILEFORMAT_BMP) +#if SUPPORT_FILEFORMAT_BMP else if (IsFileExtension(fileName, ".bmp")) result = stbi_write_bmp(fileName, image.width, image.height, channels, imgData); #endif -#if defined(SUPPORT_FILEFORMAT_TGA) +#if SUPPORT_FILEFORMAT_TGA else if (IsFileExtension(fileName, ".tga")) result = stbi_write_tga(fileName, image.width, image.height, channels, imgData); #endif -#if defined(SUPPORT_FILEFORMAT_JPG) +#if SUPPORT_FILEFORMAT_JPG else if (IsFileExtension(fileName, ".jpg") || IsFileExtension(fileName, ".jpeg")) result = stbi_write_jpg(fileName, image.width, image.height, channels, imgData, 90); // JPG quality: between 1 and 100 #endif -#if defined(SUPPORT_FILEFORMAT_QOI) +#if SUPPORT_FILEFORMAT_QOI else if (IsFileExtension(fileName, ".qoi")) { channels = 0; @@ -684,7 +680,7 @@ bool ExportImage(Image image, const char *fileName) } } #endif -#if defined(SUPPORT_FILEFORMAT_KTX) +#if SUPPORT_FILEFORMAT_KTX else if (IsFileExtension(fileName, ".ktx")) { result = rl_save_ktx(fileName, image.data, image.width, image.height, image.format, image.mipmaps); @@ -699,7 +695,7 @@ bool ExportImage(Image image, const char *fileName) else TRACELOG(LOG_WARNING, "IMAGE: Export image format requested not supported"); if (allocatedData) RL_FREE(imgData); -#endif // SUPPORT_IMAGE_EXPORT +#endif // SUPPORT_IMAGE_EXPORT if (result != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image exported successfully", fileName); else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image", fileName); @@ -716,7 +712,6 @@ unsigned char *ExportImageToMemory(Image image, const char *fileType, int *dataS // Security check for input data if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return NULL; -#if defined(SUPPORT_IMAGE_EXPORT) int channels = 4; if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) channels = 1; @@ -724,13 +719,13 @@ unsigned char *ExportImageToMemory(Image image, const char *fileType, int *dataS else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) channels = 3; else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) channels = 4; -#if defined(SUPPORT_FILEFORMAT_PNG) +#if SUPPORT_IMAGE_EXPORT if ((strcmp(fileType, ".png") == 0) || (strcmp(fileType, ".PNG") == 0)) { fileData = stbi_write_png_to_mem((const unsigned char *)image.data, image.width*channels, image.width, image.height, channels, dataSize); } -#endif - +#else + TRACELOG(LOG_WARNING, "IMAGE: To export image, enable flag SUPPORT_IMAGE_EXPORT"); #endif return fileData; @@ -741,8 +736,6 @@ bool ExportImageAsCode(Image image, const char *fileName) { bool success = false; -#if defined(SUPPORT_IMAGE_EXPORT) - #ifndef TEXT_BYTES_PER_LINE #define TEXT_BYTES_PER_LINE 20 #endif @@ -785,8 +778,6 @@ bool ExportImageAsCode(Image image, const char *fileName) RL_FREE(txtData); -#endif // SUPPORT_IMAGE_EXPORT - if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image as code exported successfully", fileName); else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image as code", fileName); @@ -814,7 +805,7 @@ Image GenImageColor(int width, int height, Color color) return image; } -#if defined(SUPPORT_IMAGE_GENERATION) +#if SUPPORT_IMAGE_GENERATION // Generate image: linear gradient // The direction value specifies the direction of the gradient (in degrees) // with 0 being vertical (from top to bottom), 90 being horizontal (from left to right) @@ -1137,7 +1128,7 @@ Image GenImageText(int width, int height, const char *text) return image; } -#endif // SUPPORT_IMAGE_GENERATION +#endif // SUPPORT_IMAGE_GENERATION //------------------------------------------------------------------------------------ // Image manipulation functions @@ -1451,9 +1442,7 @@ void ImageFormat(Image *image, int newFormat) if (image->mipmaps > 1) { image->mipmaps = 1; - #if defined(SUPPORT_IMAGE_MANIPULATION) if (image->data != NULL) ImageMipmaps(image); - #endif } } else TRACELOG(LOG_WARNING, "IMAGE: Data format is compressed, can not be converted"); @@ -1464,7 +1453,7 @@ void ImageFormat(Image *image, int newFormat) Image ImageText(const char *text, int fontSize, Color color) { Image imText = { 0 }; -#if defined(SUPPORT_MODULE_RTEXT) +#if SUPPORT_MODULE_RTEXT int defaultFontSize = 10; // Default Font chars height in pixel if (fontSize < defaultFontSize) fontSize = defaultFontSize; int spacing = fontSize/defaultFontSize; @@ -1481,7 +1470,7 @@ Image ImageText(const char *text, int fontSize, Color color) Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint) { Image imText = { 0 }; -#if defined(SUPPORT_MODULE_RTEXT) +#if SUPPORT_MODULE_RTEXT if (text == NULL) return imText; int textLength = (int)strlen(text); // Get length of text in bytes @@ -1873,7 +1862,6 @@ void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, i } } -#if defined(SUPPORT_IMAGE_MANIPULATION) // Convert image to POT (power-of-two) // NOTE: It could be useful on OpenGL ES 2.0 (RPI, HTML5) void ImageToPOT(Image *image, Color fill) @@ -2944,7 +2932,6 @@ void ImageColorReplace(Image *image, Color color, Color replace) (format == PIXELFORMAT_COMPRESSED_ETC2_RGB) || (format == PIXELFORMAT_COMPRESSED_PVRT_RGB)) ImageFormat(image, format); } -#endif // SUPPORT_IMAGE_MANIPULATION // Load color data from image as a Color array (RGBA - 32bit) // NOTE: Memory allocated should be freed using UnloadImageColors(); @@ -4095,7 +4082,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color // Draw text (default font) within an image (destination) void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color) { -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) +#if SUPPORT_MODULE_RTEXT // Make sure default font is loaded to be used on image text drawing if (GetFontDefault().texture.id == 0) LoadFontDefault(); @@ -4234,13 +4221,11 @@ TextureCubemap LoadTextureCubemap(Image image, int layout) ImageFormat(&faces, image.format); Image mipmapped = ImageCopy(image); - #if defined(SUPPORT_IMAGE_MANIPULATION) if (image.mipmaps > 1) { ImageMipmaps(&mipmapped); ImageMipmaps(&faces); } - #endif for (int i = 0; i < 6; i++) ImageDraw(&faces, mipmapped, faceRecs[i], (Rectangle){ 0, (float)size*i, (float)size, (float)size }, WHITE); @@ -5599,4 +5584,4 @@ static Vector4 *LoadImageDataNormalized(Image image) return pixels; } -#endif // SUPPORT_MODULE_RTEXTURES \ No newline at end of file +#endif // SUPPORT_MODULE_RTEXTURES From 7a3cecc0106b7af7727eddafc6b32d6bc2093467 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Thu, 26 Feb 2026 08:45:40 -0800 Subject: [PATCH 087/319] Fix a 64 bit to 32 bit int cast warning. (#5594) --- src/raudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index 367615ad1..ec2b474d1 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -2545,7 +2545,7 @@ static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, f } memcpy(audioBuffer->converterResidual, inputBuffer + inputFramesProcessedThisIteration*bpf, (size_t)(residualFrameCount * bpf)); - audioBuffer->converterResidualCount = residualFrameCount; + audioBuffer->converterResidualCount = (unsigned int)residualFrameCount; } if (inputFramesInInternalFormatCount < estimatedInputFrameCount) break; // Reached the end of the sound From 72b206624f1d0416867b736548d50560d83d2a72 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Thu, 26 Feb 2026 09:33:08 -0800 Subject: [PATCH 088/319] MSVC warnings (#5595) --- examples/core/core_compute_hash.c | 2 +- examples/core/core_directory_files.c | 2 +- examples/core/core_highdpi_testbed.c | 6 ++--- examples/core/core_viewport_scaling.c | 10 +++---- examples/models/models_animation_blending.c | 12 ++++----- examples/models/models_animation_timing.c | 6 ++--- examples/models/models_bone_socket.c | 4 +-- .../shaders/shaders_shadowmap_rendering.c | 2 +- examples/shapes/shapes_math_sine_cosine.c | 26 +++++++++---------- examples/shapes/shapes_penrose_tile.c | 2 +- examples/shapes/shapes_pie_chart.c | 2 +- examples/shapes/shapes_starfield_effect.c | 8 +++--- examples/text/text_words_alignment.c | 4 +-- 13 files changed, 43 insertions(+), 43 deletions(-) diff --git a/examples/core/core_compute_hash.c b/examples/core/core_compute_hash.c index 505ffea7b..f7d3ba9ac 100644 --- a/examples/core/core_compute_hash.c +++ b/examples/core/core_compute_hash.c @@ -60,7 +60,7 @@ int main(void) //---------------------------------------------------------------------------------- if (btnComputeHashes) { - int textInputLen = strlen(textInput); + int textInputLen = (int)strlen(textInput); // Encode data to Base64 string (includes NULL terminator), memory must be MemFree() base64Text = EncodeDataBase64((unsigned char *)textInput, textInputLen, &base64TextSize); diff --git a/examples/core/core_directory_files.c b/examples/core/core_directory_files.c index b0a204d37..a5b19078f 100644 --- a/examples/core/core_directory_files.c +++ b/examples/core/core_directory_files.c @@ -78,7 +78,7 @@ int main(void) GuiSetStyle(LISTVIEW, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); GuiSetStyle(LISTVIEW, TEXT_PADDING, 40); - GuiListViewEx((Rectangle){ 0, 50, GetScreenWidth(), GetScreenHeight() - 50 }, + GuiListViewEx((Rectangle){ 0, 50, (float)GetScreenWidth(), (float)GetScreenHeight() - 50 }, files.paths, files.count, &listScrollIndex, &listItemActive, &listItemFocused); EndDrawing(); diff --git a/examples/core/core_highdpi_testbed.c b/examples/core/core_highdpi_testbed.c index 1c39a9d2c..cfff1f3c5 100644 --- a/examples/core/core_highdpi_testbed.c +++ b/examples/core/core_highdpi_testbed.c @@ -86,10 +86,10 @@ int main(void) // Draw mouse position DrawCircleV(GetMousePosition(), 20, MAROON); - DrawRectangle(mousePos.x - 25, mousePos.y, 50, 2, BLACK); - DrawRectangle(mousePos.x, mousePos.y - 25, 2, 50, BLACK); + DrawRectangleRec((Rectangle) { mousePos.x - 25, mousePos.y, 50, 2 }, BLACK); + DrawRectangleRec((Rectangle) { mousePos.x, mousePos.y - 25, 2, 50 }, BLACK); DrawText(TextFormat("[%i,%i]", GetMouseX(), GetMouseY()), mousePos.x - 44, - (mousePos.y > GetScreenHeight() - 60)? mousePos.y - 46 : mousePos.y + 30, 20, BLACK); + (mousePos.y > GetScreenHeight() - 60)? (int)mousePos.y - 46 : (int)mousePos.y + 30, 20, BLACK); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/examples/core/core_viewport_scaling.c b/examples/core/core_viewport_scaling.c index 4a608e96d..716b2f42d 100644 --- a/examples/core/core_viewport_scaling.c +++ b/examples/core/core_viewport_scaling.c @@ -177,10 +177,10 @@ int main(void) DrawRectangleRec(increaseTypeButton, SKYBLUE); DrawRectangleRec(decreaseResolutionButton, SKYBLUE); DrawRectangleRec(increaseResolutionButton, SKYBLUE); - DrawText("<", decreaseTypeButton.x + 3, decreaseTypeButton.y + 1, 10, BLACK); - DrawText(">", increaseTypeButton.x + 3, increaseTypeButton.y + 1, 10, BLACK); - DrawText("<", decreaseResolutionButton.x + 3, decreaseResolutionButton.y + 1, 10, BLACK); - DrawText(">", increaseResolutionButton.x + 3, increaseResolutionButton.y + 1, 10, BLACK); + DrawText("<", (int)decreaseTypeButton.x + 3, (int)decreaseTypeButton.y + 1, 10, BLACK); + DrawText(">", (int)increaseTypeButton.x + 3, (int)increaseTypeButton.y + 1, 10, BLACK); + DrawText("<", (int)decreaseResolutionButton.x + 3, (int)decreaseResolutionButton.y + 1, 10, BLACK); + DrawText(">", (int)increaseResolutionButton.x + 3, (int)increaseResolutionButton.y + 1, 10, BLACK); EndDrawing(); //---------------------------------------------------------------------------------- @@ -308,7 +308,7 @@ static void ResizeRenderSize(ViewportType viewportType, int *screenWidth, int *s } UnloadRenderTexture(*target); - *target = LoadRenderTexture(sourceRect->width, -sourceRect->height); + *target = LoadRenderTexture((int)sourceRect->width, -(int)sourceRect->height); } // Example how to calculate position on RenderTexture diff --git a/examples/models/models_animation_blending.c b/examples/models/models_animation_blending.c index 8b1b0f220..e44583299 100644 --- a/examples/models/models_animation_blending.c +++ b/examples/models/models_animation_blending.c @@ -225,7 +225,7 @@ int main(void) NULL, TextFormat("x%.1f", animFrameSpeed0), &animFrameSpeed0, 0.1f, 2.0f); GuiEnable(); if (dropdownEditMode1) GuiDisable(); - GuiSlider((Rectangle){ GetScreenWidth() - 170, 38, 160, 12 }, + GuiSlider((Rectangle){ GetScreenWidth() - 170.0f, 38, 160, 12 }, TextFormat("%.1fx", animFrameSpeed1), NULL, &animFrameSpeed1, 0.1f, 2.0f); GuiEnable(); @@ -241,23 +241,23 @@ int main(void) GuiProgressBar((Rectangle){ 180, 14, 440, 16 }, NULL, NULL, &animBlendProgress, 0.0f, 1.0f); GuiSetStyle(PROGRESSBAR, PROGRESS_SIDE, 0); // Reset to Left-->Right - if (GuiDropdownBox((Rectangle){ GetScreenWidth() - 170, 10, 160, 24 }, TextJoin(animNames, animCount, ";"), + if (GuiDropdownBox((Rectangle){ GetScreenWidth() - 170.0f, 10, 160, 24 }, TextJoin(animNames, animCount, ";"), &animIndex1, dropdownEditMode1)) dropdownEditMode1 = !dropdownEditMode1; // Draw playing timeline with keyframes for anim0[] - GuiProgressBar((Rectangle){ 60, GetScreenHeight() - 60, GetScreenWidth() - 180, 20 }, "ANIM 0", + GuiProgressBar((Rectangle){ 60, GetScreenHeight() - 60.0f, GetScreenWidth() - 180.0f, 20 }, "ANIM 0", TextFormat("FRAME: %.2f / %i", animFrameProgress0, anims[animIndex0].keyframeCount), &animFrameProgress0, 0.0f, (float)anims[animIndex0].keyframeCount); for (int i = 0; i < anims[animIndex0].keyframeCount; i++) - DrawRectangle(60 + ((float)(GetScreenWidth() - 180)/(float)anims[animIndex0].keyframeCount)*(float)i, + DrawRectangle(60 + (int)(((float)(GetScreenWidth() - 180)/(float)anims[animIndex0].keyframeCount)*(float)i), GetScreenHeight() - 60, 1, 20, BLUE); // Draw playing timeline with keyframes for anim1[] - GuiProgressBar((Rectangle){ 60, GetScreenHeight() - 30, GetScreenWidth() - 180, 20 }, "ANIM 1", + GuiProgressBar((Rectangle){ 60, GetScreenHeight() - 30.0f, GetScreenWidth() - 180.0f, 20 }, "ANIM 1", TextFormat("FRAME: %.2f / %i", animFrameProgress1, anims[animIndex1].keyframeCount), &animFrameProgress1, 0.0f, (float)anims[animIndex1].keyframeCount); for (int i = 0; i < anims[animIndex1].keyframeCount; i++) - DrawRectangle(60 + ((float)(GetScreenWidth() - 180)/(float)anims[animIndex1].keyframeCount)*(float)i, + DrawRectangle(60 + (int)(((float)(GetScreenWidth() - 180)/(float)anims[animIndex1].keyframeCount)*(float)i), GetScreenHeight() - 30, 1, 20, BLUE); //--------------------------------------------------------------------------------------------- diff --git a/examples/models/models_animation_timing.c b/examples/models/models_animation_timing.c index 097e19225..030ad81e1 100644 --- a/examples/models/models_animation_timing.c +++ b/examples/models/models_animation_timing.c @@ -108,12 +108,12 @@ int main(void) &animFrameSpeed, 0.1f, 2.0f); // Draw playing timeline with keyframes - GuiLabel((Rectangle){ 10, GetScreenHeight() - 64, GetScreenWidth() - 20, 24 }, + GuiLabel((Rectangle){ 10, GetScreenHeight() - 64.0f, GetScreenWidth() - 20.0f, 24 }, TextFormat("CURRENT FRAME: %.2f / %i", animFrameProgress, anims[animIndex].keyframeCount)); - GuiProgressBar((Rectangle){ 10, GetScreenHeight() - 40, GetScreenWidth() - 20, 24 }, NULL, NULL, + GuiProgressBar((Rectangle){ 10, GetScreenHeight() - 40.0f, GetScreenWidth() - 20.0f, 24 }, NULL, NULL, &animFrameProgress, 0.0f, (float)anims[animIndex].keyframeCount); for (int i = 0; i < anims[animIndex].keyframeCount; i++) - DrawRectangle(10 + ((float)(GetScreenWidth() - 20)/(float)anims[animIndex].keyframeCount)*(float)i, + DrawRectangle(10 + (int)(((float)(GetScreenWidth() - 20)/(float)anims[animIndex].keyframeCount)*(float)i), GetScreenHeight() - 40, 1, 24, BLUE); EndDrawing(); diff --git a/examples/models/models_bone_socket.c b/examples/models/models_bone_socket.c index 37b677c4a..ff44671c2 100644 --- a/examples/models/models_bone_socket.c +++ b/examples/models/models_bone_socket.c @@ -116,7 +116,7 @@ int main(void) // Update model animation ModelAnimation anim = modelAnimations[animIndex]; animCurrentFrame = (animCurrentFrame + 1)%anim.keyframeCount; - UpdateModelAnimation(characterModel, anim, animCurrentFrame); + UpdateModelAnimation(characterModel, anim, (float)animCurrentFrame); //---------------------------------------------------------------------------------- // Draw @@ -129,7 +129,7 @@ int main(void) // Draw character Quaternion characterRotate = QuaternionFromAxisAngle((Vector3){ 0.0f, 1.0f, 0.0f }, angle*DEG2RAD); characterModel.transform = MatrixMultiply(QuaternionToMatrix(characterRotate), MatrixTranslate(position.x, position.y, position.z)); - UpdateModelAnimation(characterModel, anim, animCurrentFrame); + UpdateModelAnimation(characterModel, anim, (float)animCurrentFrame); DrawMesh(characterModel.meshes[0], characterModel.materials[1], characterModel.transform); // Draw equipments (hat, sword, shield) diff --git a/examples/shaders/shaders_shadowmap_rendering.c b/examples/shaders/shaders_shadowmap_rendering.c index 9bf84eae7..3b98ab5b6 100644 --- a/examples/shaders/shaders_shadowmap_rendering.c +++ b/examples/shaders/shaders_shadowmap_rendering.c @@ -117,7 +117,7 @@ int main(void) frameCounter++; frameCounter %= (anims[0].keyframeCount); - UpdateModelAnimation(robot, anims[0], frameCounter); + UpdateModelAnimation(robot, anims[0], (float)frameCounter); // Move light with arrow keys const float cameraSpeed = 0.05f; diff --git a/examples/shapes/shapes_math_sine_cosine.c b/examples/shapes/shapes_math_sine_cosine.c index c3646cabd..c9964a9ff 100644 --- a/examples/shapes/shapes_math_sine_cosine.c +++ b/examples/shapes/shapes_math_sine_cosine.c @@ -89,7 +89,7 @@ int main(void) // Cotangent (orange) DrawLineEx((Vector2){ center.x , limitMin.y }, (Vector2){ cotangentPoint.x, limitMin.y }, 2.0f, ORANGE); - DrawLineDashed(center, cotangentPoint, 10.0f, 4.0f, ORANGE); + DrawLineDashed(center, cotangentPoint, 10, 4, ORANGE); // Side background DrawLine(580, 0, 580, GetScreenHeight(), (Color){ 218, 218, 218, 255 }); @@ -106,48 +106,48 @@ int main(void) DrawLineEx((Vector2){ start.x, start.y + start.height/2 }, (Vector2){ start.x + start.width, start.y + start.height/2 }, 2.0f, GRAY); // Wave graph axis labels - DrawText("1", start.x - 8, start.y, 6, GRAY); - DrawText("0", start.x - 8, start.y + start.height/2 - 6, 6, GRAY); - DrawText("-1", start.x - 12, start.y + start.height - 8, 6, GRAY); - DrawText("0", start.x - 2, start.y + start.height + 4, 6, GRAY); - DrawText("360", start.x + start.width - 8, start.y + start.height + 4, 6, GRAY); + DrawText("1", (int)start.x - 8, (int)start.y, 6, GRAY); + DrawText("0", (int)start.x - 8, (int)start.y + (int)start.height/2 - 6, 6, GRAY); + DrawText("-1", (int)start.x - 12, (int)start.y + (int)start.height - 8, 6, GRAY); + DrawText("0", (int)start.x - 2, (int)start.y + (int)start.height + 4, 6, GRAY); + DrawText("360", (int)start.x + (int)start.width - 8, (int)start.y + (int)start.height + 4, 6, GRAY); // Sine (red - vertical) DrawLineEx((Vector2){ center.x, center.y }, (Vector2){ center.x, point.y }, 2.0f, RED); - DrawLineDashed((Vector2){ point.x, center.y }, (Vector2){ point.x, point.y }, 10.0f, 4.0f, RED); + DrawLineDashed((Vector2){ point.x, center.y }, (Vector2){ point.x, point.y }, 10, 4, RED); DrawText(TextFormat("Sine %.2f", sinRad), 640, 190, 6, RED); DrawCircleV((Vector2){ start.x + (angle/360.0f)*start.width, start.y + ((-sinRad + 1)*start.height/2.0f) }, 4.0f, RED); DrawSplineLinear(sinePoints, WAVE_POINTS, 1.0f, RED); // Cosine (blue - horizontal) DrawLineEx((Vector2){ center.x, center.y }, (Vector2){ point.x, center.y }, 2.0f, BLUE); - DrawLineDashed((Vector2){ center.x , point.y }, (Vector2){ point.x, point.y }, 10.0f, 4.0f, BLUE); + DrawLineDashed((Vector2){ center.x , point.y }, (Vector2){ point.x, point.y }, 10, 4, BLUE); DrawText(TextFormat("Cosine %.2f", cosRad), 640, 210, 6, BLUE); DrawCircleV((Vector2){ start.x + (angle/360.0f)*start.width, start.y + ((-cosRad + 1)*start.height/2.0f) }, 4.0f, BLUE); DrawSplineLinear(cosPoints, WAVE_POINTS, 1.0f, BLUE); // Tangent (purple) DrawLineEx((Vector2){ limitMax.x , center.y }, (Vector2){ limitMax.x, tangentPoint.y }, 2.0f, PURPLE); - DrawLineDashed(center, tangentPoint, 10.0f, 4.0f, PURPLE); + DrawLineDashed(center, tangentPoint, 10, 4, PURPLE); DrawText(TextFormat("Tangent %.2f", tangent), 640, 230, 6, PURPLE); // Cotangent (orange) DrawText(TextFormat("Cotangent %.2f", cotangent), 640, 250, 6, ORANGE); // Complementary angle (beige) - DrawCircleSectorLines(center, radius*0.6f , -angle, -90.0f , 36.0f, BEIGE); + DrawCircleSectorLines(center, radius*0.6f , -angle, -90.0f , 36, BEIGE); DrawText(TextFormat("Complementary %0.f°",complementary), 640, 150, 6, BEIGE); // Supplementary angle (darkblue) - DrawCircleSectorLines(center, radius*0.5f , -angle, -180.0f , 36.0f, DARKBLUE); + DrawCircleSectorLines(center, radius*0.5f , -angle, -180.0f , 36, DARKBLUE); DrawText(TextFormat("Supplementary %0.f°",supplementary), 640, 130, 6, DARKBLUE); // Explementary angle (pink) - DrawCircleSectorLines(center, radius*0.4f , -angle, -360.0f , 36.0f, PINK); + DrawCircleSectorLines(center, radius*0.4f , -angle, -360.0f , 36, PINK); DrawText(TextFormat("Explementary %0.f°",explementary), 640, 170, 6, PINK); // Current angle - arc (lime), radius (black), endpoint (black) - DrawCircleSectorLines(center, radius*0.7f , -angle, 0.0f, 36.0f, LIME); + DrawCircleSectorLines(center, radius*0.7f , -angle, 0.0f, 36, LIME); DrawLineEx((Vector2){ center.x , center.y }, point, 2.0f, BLACK); DrawCircleV(point, 4.0f, BLACK); diff --git a/examples/shapes/shapes_penrose_tile.c b/examples/shapes/shapes_penrose_tile.c index cf41852f8..f94eae763 100644 --- a/examples/shapes/shapes_penrose_tile.c +++ b/examples/shapes/shapes_penrose_tile.c @@ -30,7 +30,7 @@ //---------------------------------------------------------------------------------- typedef struct TurtleState { Vector2 origin; - double angle; + float angle; } TurtleState; typedef struct PenroseLSystem { diff --git a/examples/shapes/shapes_pie_chart.c b/examples/shapes/shapes_pie_chart.c index baf0a3652..5ef6802dc 100644 --- a/examples/shapes/shapes_pie_chart.c +++ b/examples/shapes/shapes_pie_chart.c @@ -160,7 +160,7 @@ int main(void) // Draw inner circle to create donut effect // TODO: This is a hacky solution, better use DrawRing() - if (showDonut) DrawCircle(center.x, center.y, donutInnerRadius, RAYWHITE); + if (showDonut) DrawCircleV(center, donutInnerRadius, RAYWHITE); startAngle += sweepAngle; } diff --git a/examples/shapes/shapes_starfield_effect.c b/examples/shapes/shapes_starfield_effect.c index ca409c926..484f4fb78 100644 --- a/examples/shapes/shapes_starfield_effect.c +++ b/examples/shapes/shapes_starfield_effect.c @@ -47,8 +47,8 @@ int main(void) // Setup the stars with a random position for (int i = 0; i < STAR_COUNT; i++) { - stars[i].x = GetRandomValue(-screenWidth*0.5f, screenWidth*0.5f); - stars[i].y = GetRandomValue(-screenHeight*0.5f, screenHeight*0.5f); + stars[i].x = (float)GetRandomValue(-screenWidth / 2, (int)screenWidth / 2); + stars[i].y = (float)GetRandomValue(-screenHeight / 2, (int)screenHeight / 2); stars[i].z = 1.0f; } @@ -85,8 +85,8 @@ int main(void) if ((stars[i].z < 0.0f) || (starsScreenPos[i].x < 0) || (starsScreenPos[i].y < 0.0f) || (starsScreenPos[i].x > screenWidth) || (starsScreenPos[i].y > screenHeight)) { - stars[i].x = GetRandomValue(-screenWidth*0.5f, screenWidth*0.5f); - stars[i].y = GetRandomValue(-screenHeight*0.5f, screenHeight*0.5f); + stars[i].x = (float)GetRandomValue(-screenWidth / 2, screenWidth / 2); + stars[i].y = (float)GetRandomValue(-screenHeight / 2, screenHeight / 2); stars[i].z = 1.0f; } } diff --git a/examples/text/text_words_alignment.c b/examples/text/text_words_alignment.c index f7ffa74af..8017f33eb 100644 --- a/examples/text/text_words_alignment.c +++ b/examples/text/text_words_alignment.c @@ -109,7 +109,7 @@ int main(void) DrawRectangleRec(textContainerRect, BLUE); // Get the size of the text to draw - Vector2 textSize = MeasureTextEx(font, words[wordIndex], fontSize, fontSize*.1f); + Vector2 textSize = MeasureTextEx(font, words[wordIndex], (float)fontSize, fontSize*.1f); // Calculate the top-left text position based on the rectangle and alignment Vector2 textPos = (Vector2){ @@ -118,7 +118,7 @@ int main(void) }; // Draw the text - DrawTextEx(font, words[wordIndex], textPos, fontSize, fontSize*.1f, RAYWHITE); + DrawTextEx(font, words[wordIndex], textPos, (float)fontSize, fontSize*.1f, RAYWHITE); EndDrawing(); //---------------------------------------------------------------------------------- From f23a900e91b961f77303d83f88655c4cf570446a Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Thu, 26 Feb 2026 16:22:38 -0600 Subject: [PATCH 089/319] fix memory leak found by maiconpintoabreu (#5598) --- src/rcore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index c7f33f48d..7a0ddbcc6 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3546,11 +3546,11 @@ AutomationEventList LoadAutomationEventList(const char *fileName) { AutomationEventList list = { 0 }; +#if SUPPORT_AUTOMATION_EVENTS // Allocate and empty automation event list, ready to record new events list.events = (AutomationEvent *)RL_CALLOC(MAX_AUTOMATION_EVENTS, sizeof(AutomationEvent)); list.capacity = MAX_AUTOMATION_EVENTS; - -#if SUPPORT_AUTOMATION_EVENTS + if (fileName == NULL) TRACELOG(LOG_INFO, "AUTOMATION: New empty events list loaded successfully"); else { From d869db1572e9a89cb850d11daf454bf206a2c8e6 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Feb 2026 23:37:45 +0100 Subject: [PATCH 090/319] Update raylib.sln --- projects/VS2022/raylib.sln | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 02fe4b197..51782892c 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -5631,7 +5631,7 @@ Global {C54703BF-D68A-480D-BE27-49B62E45D582} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {9CD8BCAD-F212-4BCC-BA98-899743CE3279} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} {0981CA28-E4A5-4DF1-987F-A41D09131EFC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {6777EC3C-077C-42FC-B4AD-B799CE55CCE4} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} {A61DAD9C-271C-4E95-81AA-DB4CD58564D4} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} @@ -5645,7 +5645,7 @@ Global {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {8E132D5A-2C00-48D0-8747-97E41356F26F} = {278D8859-20B1-428F-8448-064F46E1F021} {A4662163-83E7-4309-8CAA-B0BF13655FE6} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {5F4B766F-DD52-4B53-B6C3-BC7611E17F20} = {278D8859-20B1-428F-8448-064F46E1F021} + {5F4B766F-DD52-4B53-B6C3-BC7611E17F20} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {0C442799-B09C-4CD1-9538-711B6E85E9BF} = {278D8859-20B1-428F-8448-064F46E1F021} {DFB40A10-F8B7-412A-BCC3-5EE49294D816} = {278D8859-20B1-428F-8448-064F46E1F021} From 92a1b804652720d73c8889de4daf8bf354021f60 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Feb 2026 23:38:06 +0100 Subject: [PATCH 091/319] REVIEWED: Flags checks, fixes #5597 --- src/rcore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 7a0ddbcc6..b21d1ddff 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1607,7 +1607,7 @@ int GetFPS(void) { int fps = 0; -#if !defined(SUPPORT_CUSTOM_FRAME_CONTROL) +#if !SUPPORT_CUSTOM_FRAME_CONTROL #define FPS_CAPTURE_FRAMES_COUNT 30 // 30 captures #define FPS_AVERAGE_TIME_SECONDS 0.5f // 500 milliseconds #define FPS_STEP (FPS_AVERAGE_TIME_SECONDS/FPS_CAPTURE_FRAMES_COUNT) @@ -4582,7 +4582,7 @@ static void RecordAutomationEvent(void) } #endif -#if !defined(SUPPORT_MODULE_RTEXT) +#if !SUPPORT_MODULE_RTEXT // Formatting of text with variables to 'embed' // WARNING: String returned will expire after this function is called MAX_TEXTFORMAT_BUFFERS times const char *TextFormat(const char *text, ...) From c686e087b36e3c5f7befe395ce2bdd514daecca6 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Feb 2026 23:40:03 +0100 Subject: [PATCH 092/319] Update rtextures.c --- src/rtextures.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index a134c4659..7cddcb3bc 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -71,31 +71,31 @@ #include // Required for: sprintf() [Used in ExportImageAsCode()] // Support only desired texture formats on stb_image -#if !defined(SUPPORT_FILEFORMAT_BMP) +#if !SUPPORT_FILEFORMAT_BMP #define STBI_NO_BMP #endif -#if !defined(SUPPORT_FILEFORMAT_PNG) +#if !SUPPORT_FILEFORMAT_PNG #define STBI_NO_PNG #endif -#if !defined(SUPPORT_FILEFORMAT_TGA) +#if !SUPPORT_FILEFORMAT_TGA #define STBI_NO_TGA #endif -#if !defined(SUPPORT_FILEFORMAT_JPG) +#if !SUPPORT_FILEFORMAT_JPG #define STBI_NO_JPEG // Image format .jpg and .jpeg #endif -#if !defined(SUPPORT_FILEFORMAT_PSD) +#if !SUPPORT_FILEFORMAT_PSD #define STBI_NO_PSD #endif -#if !defined(SUPPORT_FILEFORMAT_GIF) +#if !SUPPORT_FILEFORMAT_GIF #define STBI_NO_GIF #endif -#if !defined(SUPPORT_FILEFORMAT_PIC) +#if !SUPPORT_FILEFORMAT_PIC #define STBI_NO_PIC #endif -#if !defined(SUPPORT_FILEFORMAT_HDR) +#if !SUPPORT_FILEFORMAT_HDR #define STBI_NO_HDR #endif -#if !defined(SUPPORT_FILEFORMAT_PNM) +#if !SUPPORT_FILEFORMAT_PNM #define STBI_NO_PNM #endif From d8861cc35fa3d173dcd3317ebb795ad2348e99a5 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Thu, 26 Feb 2026 16:41:33 -0600 Subject: [PATCH 093/319] change defined() to 0/1 check (#5599) --- src/config.h | 4 ++-- src/platforms/rcore_desktop_glfw.c | 4 ++-- src/platforms/rcore_desktop_rgfw.c | 8 ++++---- src/platforms/rcore_template.c | 2 +- src/platforms/rcore_web.c | 4 ++-- src/platforms/rcore_web_emscripten.c | 4 ++-- src/rtextures.c | 10 +++++----- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/config.h b/src/config.h index 1c45a1170..7bedd4f4c 100644 --- a/src/config.h +++ b/src/config.h @@ -85,11 +85,11 @@ // However, it can also reduce overall system performance, because the thread scheduler switches tasks more often #define SUPPORT_WINMM_HIGHRES_TIMER 1 #endif -#if !defined(SUPPORT_BUSY_WAIT_LOOP) && !SUPPORT_PARTIALBUSY_WAIT_LOOP +#if !SUPPORT_BUSY_WAIT_LOOP && !SUPPORT_PARTIALBUSY_WAIT_LOOP // Use busy wait loop for timing sync, if not defined, a high-resolution timer is set up and used #define SUPPORT_BUSY_WAIT_LOOP 0 // Disabled by default #endif -#if !defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) && !SUPPORT_BUSY_WAIT_LOOP +#if !SUPPORT_PARTIALBUSY_WAIT_LOOP && !SUPPORT_BUSY_WAIT_LOOP // Use a partial-busy wait loop, in this case frame sleeps for most of the time, // but then runs a busy loop at the end for accuracy #define SUPPORT_PARTIALBUSY_WAIT_LOOP 1 diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 79a94b08b..1e3ae2b45 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -2049,7 +2049,7 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int CORE.Input.Mouse.currentButtonState[button] = action; CORE.Input.Touch.currentTouchState[button] = action; -#if SUPPORT_GESTURES_SYSTEM && defined(SUPPORT_MOUSE_GESTURES) +#if SUPPORT_GESTURES_SYSTEM && SUPPORT_MOUSE_GESTURES // Process mouse events as touches to be able to use mouse-gestures GestureEvent gestureEvent = { 0 }; @@ -2084,7 +2084,7 @@ static void MouseCursorPosCallback(GLFWwindow *window, double x, double y) CORE.Input.Mouse.currentPosition.y = (float)y; CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; -#if SUPPORT_GESTURES_SYSTEM && defined(SUPPORT_MOUSE_GESTURES) +#if SUPPORT_GESTURES_SYSTEM && SUPPORT_MOUSE_GESTURES // Process mouse events as touches to be able to use mouse-gestures GestureEvent gestureEvent = { 0 }; diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index d31f41e4d..178845f16 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -992,7 +992,7 @@ const char *GetClipboardText(void) return RGFW_readClipboard(NULL); } -#if defined(SUPPORT_CLIPBOARD_IMAGE) +#if SUPPORT_CLIPBOARD_IMAGE #if defined(_WIN32) #define WIN32_CLIPBOARD_IMPLEMENTATION #define WINUSER_ALREADY_INCLUDED @@ -1006,7 +1006,7 @@ const char *GetClipboardText(void) Image GetClipboardImage(void) { Image image = { 0 }; -#if defined(SUPPORT_CLIPBOARD_IMAGE) +#if SUPPORT_CLIPBOARD_IMAGE #if defined(_WIN32) unsigned long long int dataSize = 0; // moved into _WIN32 scope until other platforms gain support void *fileData = NULL; // moved into _WIN32 scope until other platforms gain support @@ -1142,7 +1142,7 @@ const char *GetKeyName(int key) // Register all input events void PollInputEvents(void) { -#if defined(SUPPORT_GESTURES_SYSTEM) +#if SUPPORT_GESTURES_SYSTEM // NOTE: Gestures update must be called every frame to reset gestures correctly // because ProcessGestureEvent() is just called on an event, not every frame UpdateGestures(); @@ -1408,7 +1408,7 @@ void PollInputEvents(void) default: break; } -#if defined(SUPPORT_GESTURES_SYSTEM) +#if SUPPORT_GESTURES_SYSTEM if (touchAction > -1) { // Process mouse events as touches to be able to use mouse-gestures diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 87cd2e21e..45c150942 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -405,7 +405,7 @@ const char *GetKeyName(int key) // Register all input events void PollInputEvents(void) { -#if defined(SUPPORT_GESTURES_SYSTEM) +#if SUPPORT_GESTURES_SYSTEM // NOTE: Gestures update must be called every frame to reset gestures correctly // because ProcessGestureEvent() is just called on an event, not every frame UpdateGestures(); diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 263db7fb8..5fd91eff6 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -1564,7 +1564,7 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int CORE.Input.Mouse.currentButtonState[button] = action; CORE.Input.Touch.currentTouchState[button] = action; -#if SUPPORT_GESTURES_SYSTEM && defined(SUPPORT_MOUSE_GESTURES) +#if SUPPORT_GESTURES_SYSTEM && SUPPORT_MOUSE_GESTURES // Process mouse events as touches to be able to use mouse-gestures GestureEvent gestureEvent = { 0 }; @@ -1605,7 +1605,7 @@ static void MouseMoveCallback(GLFWwindow *window, double x, double y) CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; } -#if SUPPORT_GESTURES_SYSTEM && defined(SUPPORT_MOUSE_GESTURES) +#if SUPPORT_GESTURES_SYSTEM && SUPPORT_MOUSE_GESTURES // Process mouse events as touches to be able to use mouse-gestures GestureEvent gestureEvent = { 0 }; diff --git a/src/platforms/rcore_web_emscripten.c b/src/platforms/rcore_web_emscripten.c index ffaa38c27..995881233 100644 --- a/src/platforms/rcore_web_emscripten.c +++ b/src/platforms/rcore_web_emscripten.c @@ -1457,7 +1457,7 @@ static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent default: break; } -#if SUPPORT_GESTURES_SYSTEM && defined(SUPPORT_MOUSE_GESTURES) +#if SUPPORT_GESTURES_SYSTEM && SUPPORT_MOUSE_GESTURES // Process mouse events as touches to be able to use mouse-gestures GestureEvent gestureEvent = { 0 }; @@ -1529,7 +1529,7 @@ static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseE CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; } -#if SUPPORT_GESTURES_SYSTEM && defined(SUPPORT_MOUSE_GESTURES) +#if SUPPORT_GESTURES_SYSTEM && SUPPORT_MOUSE_GESTURES // Process mouse events as touches to be able to use mouse-gestures GestureEvent gestureEvent = { 0 }; diff --git a/src/rtextures.c b/src/rtextures.c index 7cddcb3bc..2aef6f5a0 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -150,11 +150,11 @@ #endif #endif -#if (defined(SUPPORT_FILEFORMAT_DDS) || \ - defined(SUPPORT_FILEFORMAT_PKM) || \ - defined(SUPPORT_FILEFORMAT_KTX) || \ - defined(SUPPORT_FILEFORMAT_PVR) || \ - defined(SUPPORT_FILEFORMAT_ASTC)) +#if (SUPPORT_FILEFORMAT_DDS || \ + SUPPORT_FILEFORMAT_PKM || \ + SUPPORT_FILEFORMAT_KTX || \ + SUPPORT_FILEFORMAT_PVR || \ + SUPPORT_FILEFORMAT_ASTC) #if defined(__GNUC__) // GCC and Clang #pragma GCC diagnostic push From 05a34b09ea628dec0805379b5ef24182383a910b Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Thu, 26 Feb 2026 22:45:34 +0000 Subject: [PATCH 094/319] Swaping #pragma message with TRACELOG inside the clipboard_image function (#5596) Co-authored-by: Ray --- src/platforms/rcore_desktop_glfw.c | 13 +++++++++++-- src/platforms/rcore_desktop_rgfw.c | 11 ++++++++++- src/platforms/rcore_desktop_sdl.c | 18 +++++++++++++++++- src/raymath.h | 2 +- src/rcore.c | 16 ---------------- 5 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 1e3ae2b45..9d2cd018d 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1049,7 +1049,10 @@ Image GetClipboardImage(void) Image image = { 0 }; #if SUPPORT_CLIPBOARD_IMAGE +#if SUPPORT_MODULE_RTEXTURES #if defined(_WIN32) + +#if SUPPORT_FILEFORMAT_BMP unsigned long long int dataSize = 0; void *bmpData = NULL; int width = 0; @@ -1059,10 +1062,16 @@ Image GetClipboardImage(void) if (bmpData == NULL) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data."); else image = LoadImageFromMemory(".bmp", (const unsigned char *)bmpData, (int)dataSize); +#else + TRACELOG(LOG_WARNING, "WARNING: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_FILEFORMAT_BMP, specially on Windows"); +#endif // SUPPORT_FILEFORMAT_BMP #else TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform"); -#endif -#endif +#endif // defined(_WIN32) +#else // !SUPPORT_MODULE_RTEXTURES + TRACELOG(LOG_WARNING, "Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly"); +#endif // SUPPORT_MODULE_RTEXTURES +#endif // SUPPORT_CLIPBOARD_IMAGE return image; } diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 178845f16..532371f35 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -1007,7 +1007,10 @@ Image GetClipboardImage(void) { Image image = { 0 }; #if SUPPORT_CLIPBOARD_IMAGE +#if SUPPORT_MODULE_RTEXTURES #if defined(_WIN32) + +#if SUPPORT_FILEFORMAT_BMP unsigned long long int dataSize = 0; // moved into _WIN32 scope until other platforms gain support void *fileData = NULL; // moved into _WIN32 scope until other platforms gain support @@ -1017,9 +1020,15 @@ Image GetClipboardImage(void) if (fileData == NULL) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data"); else image = LoadImageFromMemory(".bmp", (const unsigned char *)fileData, dataSize); +#else + TRACELOG(LOG_WARNING, "WARNING: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_FILEFORMAT_BMP, specially on Windows"); +#endif // SUPPORT_FILEFORMAT_BMP #else TRACELOG(LOG_WARNING, "Clipboard image: PLATFORM_DESKTOP_RGFW doesn't implement GetClipboardImage() for this OS"); -#endif +#endif // defined(_WIN32) +#else // !SUPPORT_MODULE_RTEXTURES + TRACELOG(LOG_WARNING, "Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly"); +#endif // SUPPORT_MODULE_RTEXTURES #endif // SUPPORT_CLIPBOARD_IMAGE return image; diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index c16177f99..90d388b95 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1163,6 +1163,22 @@ Image GetClipboardImage(void) Image image = { 0 }; #if SUPPORT_CLIPBOARD_IMAGE +#if !SUPPORT_MODULE_RTEXTURES + TRACELOG(LOG_WARNING, "Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly"); + return image; +#endif + +// It's nice to have support Bitmap on Linux as well, but not as necessary as Windows +#if !SUPPORT_FILEFORMAT_BMP && defined(_WIN32) + TRACELOG(LOG_WARNING, "WARNING: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_FILEFORMAT_BMP, specially on Windows"); + return image; +#endif + +// From what I've tested applications on Wayland saves images on clipboard as PNG +#if (!SUPPORT_FILEFORMAT_PNG || !SUPPORT_FILEFORMAT_JPG) && !defined(_WIN32) + TRACELOG(LOG_WARNING, "WARNING: Getting image from the clipboard might not work without SUPPORT_FILEFORMAT_PNG or SUPPORT_FILEFORMAT_JPG"); + return image; +#endif // Let's hope compiler put these arrays in static memory const char *imageFormats[] = { "image/bmp", @@ -1197,7 +1213,7 @@ Image GetClipboardImage(void) } if (!IsImageValid(image)) TRACELOG(LOG_WARNING, "Clipboard: Couldn't get clipboard data. ERROR: %s", SDL_GetError()); -#endif +#endif // SUPPORT_CLIPBOARD_IMAGE return image; } diff --git a/src/raymath.h b/src/raymath.h index e3befaa08..ab1be1409 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -2708,7 +2708,7 @@ RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotatio stabilizer = fmaxf(stabilizer, fabsf(matColumns[i].x)); stabilizer = fmaxf(stabilizer, fabsf(matColumns[i].y)); stabilizer = fmaxf(stabilizer, fabsf(matColumns[i].z)); - }; + } matColumns[0] = Vector3Scale(matColumns[0], 1.0f / stabilizer); matColumns[1] = Vector3Scale(matColumns[1], 1.0f / stabilizer); matColumns[2] = Vector3Scale(matColumns[2], 1.0f / stabilizer); diff --git a/src/rcore.c b/src/rcore.c index b21d1ddff..85a5c0b3b 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -529,22 +529,6 @@ const char *TextFormat(const char *text, ...); // Formatting of text with variab #define PLATFORM_DESKTOP_GLFW #endif -// Using '#pragma message' because '#warning' is not adopted by MSVC -#if SUPPORT_CLIPBOARD_IMAGE - #if !SUPPORT_MODULE_RTEXTURES - #pragma message ("WARNING: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly") - #endif - - // It's nice to have support Bitmap on Linux as well, but not as necessary as Windows - #if !SUPPORT_FILEFORMAT_BMP && defined(_WIN32) - #pragma message ("WARNING: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_FILEFORMAT_BMP, specially on Windows") - #endif - - // From what I've tested applications on Wayland saves images on clipboard as PNG - #if (!SUPPORT_FILEFORMAT_PNG || !SUPPORT_FILEFORMAT_JPG) && !defined(_WIN32) - #pragma message ("WARNING: Getting image from the clipboard might not work without SUPPORT_FILEFORMAT_PNG or SUPPORT_FILEFORMAT_JPG") - #endif -#endif // Include platform-specific submodules #if defined(PLATFORM_DESKTOP_GLFW) From 2b3218c3dbfb819a8311bd4e97b1b0f414958e30 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Feb 2026 23:59:30 +0100 Subject: [PATCH 095/319] REVIEWED: Start working on `raylib 6.0` release... --- CHANGELOG | 12 ++++++++++-- examples/Makefile | 2 +- examples/Makefile.Web | 2 +- src/Makefile | 19 +++++++++++++------ src/raylib.h | 8 ++++---- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 29e4d8174..698cc2c80 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,16 @@ changelog Current Release: raylib 5.5 (18 November 2024) +------------------------------------------------------------------------- +Release: raylib 6.0 (?? March 2026) +------------------------------------------------------------------------- +KEY CHANGES: + - TODO... + +Detailed changes: + +TODO... + ------------------------------------------------------------------------- Release: raylib 5.5 (18 November 2024) ------------------------------------------------------------------------- @@ -15,8 +25,6 @@ KEY CHANGES: Detailed changes: -WIP: Last update with commit from 02-Nov-2024 - [rcore] ADDED: Working directory info at initialization by @Ray [rcore] ADDED: `GetClipboardImage()`, supported by multiple backends (#4459) by @evertonse [rcore] ADDED: `MakeDirectory()`, supporting recursive directory creation by @Ray diff --git a/examples/Makefile b/examples/Makefile index 632cdb157..1aa23b3b7 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -69,7 +69,7 @@ endif # Define required raylib variables PROJECT_NAME ?= raylib_examples -RAYLIB_VERSION ?= 5.5.0 +RAYLIB_VERSION ?= 6.0.0 RAYLIB_PATH ?= .. # Define raylib source code path diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 1fa797bf1..648e5eef4 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -69,7 +69,7 @@ endif # Define required raylib variables PROJECT_NAME ?= raylib_examples -RAYLIB_VERSION ?= 5.5.0 +RAYLIB_VERSION ?= 6.0.0 RAYLIB_PATH ?= .. # Define raylib source code path diff --git a/src/Makefile b/src/Makefile index 8f107a749..84f44e10a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -20,15 +20,22 @@ # - Linux (X11 desktop mode) # - macOS/OSX (x64, arm64 (not tested)) # - Others (not tested) -# > PLATFORM_WEB_RGFW: +# > PLATFORM_DESKTOP_WIN32 (native Win32): +# - Windows (Win32, Win64) +# > PLATFORM_WEB (GLFW + Emscripten): # - HTML5 (WebAssembly) -# > PLATFORM_WEB: +# > PLATFORM_WEB_EMSCRIPTEN (Emscripten): # - HTML5 (WebAssembly) -# > PLATFORM_DRM: +# > PLATFORM_WEB_RGFW (Emscripten): +# - HTML5 (WebAssembly) +# > PLATFORM_DRM (native DRM): # - Raspberry Pi 0-5 (DRM/KMS) # - Linux DRM subsystem (KMS mode) -# > PLATFORM_ANDROID: +# - Embedded devices (with GPU) +# > PLATFORM_ANDROID (native NDK): # - Android (ARM, ARM64) +# > PLATFORM_MEMORY +# - Memory framebuffer output, using software renderer, no OS required # # Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline. # Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline. @@ -68,8 +75,8 @@ else endif # Define required raylib variables -RAYLIB_VERSION = 5.5.0 -RAYLIB_API_VERSION = 550 +RAYLIB_VERSION = 6.0.0 +RAYLIB_API_VERSION = 600 # Define raylib source code path RAYLIB_SRC_PATH ?= ../src diff --git a/src/raylib.h b/src/raylib.h index afe3aa2ce..e542ad517 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* raylib v5.6-dev - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) +* raylib v6.0 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) * * FEATURES: * - NO external dependencies, all required libraries included with raylib @@ -85,10 +85,10 @@ #include // Required for: va_list - Only used by TraceLogCallback -#define RAYLIB_VERSION_MAJOR 5 -#define RAYLIB_VERSION_MINOR 6 +#define RAYLIB_VERSION_MAJOR 6 +#define RAYLIB_VERSION_MINOR 0 #define RAYLIB_VERSION_PATCH 0 -#define RAYLIB_VERSION "5.6-dev" +#define RAYLIB_VERSION "6.0" // Function specifiers in case library is build/used as a shared library // NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll From 178aca0fd01a4aaad94244412fe04acab5710b55 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 26 Feb 2026 23:00:48 +0000 Subject: [PATCH 096/319] rlparser: update raylib_api.* by CI --- tools/rlparser/output/raylib_api.json | 6 +++--- tools/rlparser/output/raylib_api.lua | 6 +++--- tools/rlparser/output/raylib_api.txt | 6 +++--- tools/rlparser/output/raylib_api.xml | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tools/rlparser/output/raylib_api.json b/tools/rlparser/output/raylib_api.json index 7785549f0..f1d4c1af3 100644 --- a/tools/rlparser/output/raylib_api.json +++ b/tools/rlparser/output/raylib_api.json @@ -9,13 +9,13 @@ { "name": "RAYLIB_VERSION_MAJOR", "type": "INT", - "value": 5, + "value": 6, "description": "" }, { "name": "RAYLIB_VERSION_MINOR", "type": "INT", - "value": 6, + "value": 0, "description": "" }, { @@ -27,7 +27,7 @@ { "name": "RAYLIB_VERSION", "type": "STRING", - "value": "5.6-dev", + "value": "6.0", "description": "" }, { diff --git a/tools/rlparser/output/raylib_api.lua b/tools/rlparser/output/raylib_api.lua index ffbb51d94..9a65ccd2e 100644 --- a/tools/rlparser/output/raylib_api.lua +++ b/tools/rlparser/output/raylib_api.lua @@ -9,13 +9,13 @@ return { { name = "RAYLIB_VERSION_MAJOR", type = "INT", - value = 5, + value = 6, description = "" }, { name = "RAYLIB_VERSION_MINOR", type = "INT", - value = 6, + value = 0, description = "" }, { @@ -27,7 +27,7 @@ return { { name = "RAYLIB_VERSION", type = "STRING", - value = "5.6-dev", + value = "6.0", description = "" }, { diff --git a/tools/rlparser/output/raylib_api.txt b/tools/rlparser/output/raylib_api.txt index c42f5ddde..38aa20c3f 100644 --- a/tools/rlparser/output/raylib_api.txt +++ b/tools/rlparser/output/raylib_api.txt @@ -9,12 +9,12 @@ Define 001: RAYLIB_H Define 002: RAYLIB_VERSION_MAJOR Name: RAYLIB_VERSION_MAJOR Type: INT - Value: 5 + Value: 6 Description: Define 003: RAYLIB_VERSION_MINOR Name: RAYLIB_VERSION_MINOR Type: INT - Value: 6 + Value: 0 Description: Define 004: RAYLIB_VERSION_PATCH Name: RAYLIB_VERSION_PATCH @@ -24,7 +24,7 @@ Define 004: RAYLIB_VERSION_PATCH Define 005: RAYLIB_VERSION Name: RAYLIB_VERSION Type: STRING - Value: "5.6-dev" + Value: "6.0" Description: Define 006: __declspec(x) Name: __declspec(x) diff --git a/tools/rlparser/output/raylib_api.xml b/tools/rlparser/output/raylib_api.xml index ca20800e8..be09bbd02 100644 --- a/tools/rlparser/output/raylib_api.xml +++ b/tools/rlparser/output/raylib_api.xml @@ -2,10 +2,10 @@ - - + + - + From 28e40d502a972ecae453a0f3642ca8439a0f0b8b Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Fri, 27 Feb 2026 07:19:06 +0000 Subject: [PATCH 097/319] #if reduced as possible (#5600) --- src/platforms/rcore_desktop_glfw.c | 13 +++---------- src/platforms/rcore_desktop_rgfw.c | 12 +++--------- src/platforms/rcore_desktop_sdl.c | 1 - 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 9d2cd018d..5b62339e2 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1048,11 +1048,8 @@ Image GetClipboardImage(void) { Image image = { 0 }; -#if SUPPORT_CLIPBOARD_IMAGE -#if SUPPORT_MODULE_RTEXTURES +#if SUPPORT_CLIPBOARD_IMAGE && SUPPORT_MODULE_RTEXTURES #if defined(_WIN32) - -#if SUPPORT_FILEFORMAT_BMP unsigned long long int dataSize = 0; void *bmpData = NULL; int width = 0; @@ -1062,15 +1059,11 @@ Image GetClipboardImage(void) if (bmpData == NULL) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data."); else image = LoadImageFromMemory(".bmp", (const unsigned char *)bmpData, (int)dataSize); -#else - TRACELOG(LOG_WARNING, "WARNING: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_FILEFORMAT_BMP, specially on Windows"); -#endif // SUPPORT_FILEFORMAT_BMP #else TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform"); #endif // defined(_WIN32) -#else // !SUPPORT_MODULE_RTEXTURES - TRACELOG(LOG_WARNING, "Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly"); -#endif // SUPPORT_MODULE_RTEXTURES +#else + TRACELOG(LOG_WARNING, "Clipboard image: SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly"); #endif // SUPPORT_CLIPBOARD_IMAGE return image; diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 532371f35..ed471645c 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -1006,11 +1006,9 @@ const char *GetClipboardText(void) Image GetClipboardImage(void) { Image image = { 0 }; -#if SUPPORT_CLIPBOARD_IMAGE -#if SUPPORT_MODULE_RTEXTURES +#if SUPPORT_CLIPBOARD_IMAGE && SUPPORT_MODULE_RTEXTURES #if defined(_WIN32) -#if SUPPORT_FILEFORMAT_BMP unsigned long long int dataSize = 0; // moved into _WIN32 scope until other platforms gain support void *fileData = NULL; // moved into _WIN32 scope until other platforms gain support @@ -1020,15 +1018,11 @@ Image GetClipboardImage(void) if (fileData == NULL) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data"); else image = LoadImageFromMemory(".bmp", (const unsigned char *)fileData, dataSize); -#else - TRACELOG(LOG_WARNING, "WARNING: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_FILEFORMAT_BMP, specially on Windows"); -#endif // SUPPORT_FILEFORMAT_BMP #else TRACELOG(LOG_WARNING, "Clipboard image: PLATFORM_DESKTOP_RGFW doesn't implement GetClipboardImage() for this OS"); #endif // defined(_WIN32) -#else // !SUPPORT_MODULE_RTEXTURES - TRACELOG(LOG_WARNING, "Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly"); -#endif // SUPPORT_MODULE_RTEXTURES +#else + TRACELOG(LOG_WARNING, "Clipboard image: SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly"); #endif // SUPPORT_CLIPBOARD_IMAGE return image; diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 90d388b95..b20fded9c 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1177,7 +1177,6 @@ Image GetClipboardImage(void) // From what I've tested applications on Wayland saves images on clipboard as PNG #if (!SUPPORT_FILEFORMAT_PNG || !SUPPORT_FILEFORMAT_JPG) && !defined(_WIN32) TRACELOG(LOG_WARNING, "WARNING: Getting image from the clipboard might not work without SUPPORT_FILEFORMAT_PNG or SUPPORT_FILEFORMAT_JPG"); - return image; #endif // Let's hope compiler put these arrays in static memory const char *imageFormats[] = { From f583674327e5f087143aec8ae71e9c2a09925a53 Mon Sep 17 00:00:00 2001 From: moe li Date: Fri, 27 Feb 2026 15:36:46 +0800 Subject: [PATCH 098/319] fix memory corruption in GenImageFontAtlas reallocation (#5602) --- src/rtext.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index 162281224..118b79e96 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -858,14 +858,15 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp if (offsetY > (atlas.height - fontSize - padding)) { TRACELOG(LOG_WARNING, "FONT: Updating atlas size to fit all characters"); - - // TODO: Increment atlas size (atlas.height*2) and continue adding glyphs + + // Update atlas size to fit all characters int updatedAtlasHeight = atlas.height*2; - int updatedAtlasDataSize = atlas.width*atlas.height; + int updatedAtlasDataSize = atlas.width*updatedAtlasHeight; unsigned char *updatedAtlasData = (unsigned char *)RL_CALLOC(updatedAtlasDataSize, 1); memcpy(updatedAtlasData, atlas.data, atlasDataSize); RL_FREE(atlas.data); + atlas.data = updatedAtlasData; atlas.height = updatedAtlasHeight; atlasDataSize = updatedAtlasDataSize; } From 950c064448a7ef1d82b2d01114dd6d4923bb1d04 Mon Sep 17 00:00:00 2001 From: ghera Date: Mon, 2 Mar 2026 12:18:04 +0100 Subject: [PATCH 099/319] [rcore][android] Replace `android_fopen()` with linker `--wrap=fopen` (#5605) * ANDROID: replace android_fopen with linker --wrap=fopen * ANDROID: add --wrap=fopen linker flag to src/Makefile --- src/CMakeLists.txt | 6 ++++++ src/Makefile | 3 +++ src/platforms/rcore_android.c | 31 ++++++++++++------------------- src/raudio.c | 5 ----- 4 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 76c985969..dad7dd582 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -75,6 +75,12 @@ if (${PLATFORM} MATCHES "Web") endif() endif() +if (${PLATFORM} MATCHES "Android") + # Wrap fopen at link time so all code (including third-party libs) goes + # through __wrap_fopen, which handles Android APK asset loading + target_link_options(raylib INTERFACE -Wl,--wrap=fopen) +endif() + set_target_properties(raylib PROPERTIES PUBLIC_HEADER "${raylib_public_headers}" VERSION ${PROJECT_VERSION} diff --git a/src/Makefile b/src/Makefile index 84f44e10a..e53514701 100644 --- a/src/Makefile +++ b/src/Makefile @@ -614,6 +614,9 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) LDFLAGS += -Lsrc # Avoid unresolved symbol pointing to external main() LDFLAGS += -Wl,-undefined,dynamic_lookup + # Wrap fopen at link time so all code (including third-party libs) goes + # through __wrap_fopen, which handles Android APK asset loading + LDFLAGS += -Wl,--wrap=fopen endif # Define libraries required on linking: LDLIBS diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index d7fdca403..9cf3b543b 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -275,12 +275,12 @@ static int android_write(void *cookie, const char *buf, int size); static fpos_t android_seek(void *cookie, fpos_t offset, int whence); static int android_close(void *cookie); -FILE *android_fopen(const char *fileName, const char *mode); // Replacement for fopen() -> Read-only! +FILE *__real_fopen(const char *fileName, const char *mode); // Real fopen, provided by the linker (--wrap=fopen) +FILE *__wrap_fopen(const char *fileName, const char *mode); // Replacement for fopen() + FILE *funopen(const void *cookie, int (*readfn)(void *, char *, int), int (*writefn)(void *, const char *, int), fpos_t (*seekfn)(void *, fpos_t, int), int (*closefn)(void *)); -#define fopen(name, mode) android_fopen(name, mode) - //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- @@ -1524,25 +1524,20 @@ static void SetupFramebuffer(int width, int height) } } -// Replacement for fopen() +// Replacement for fopen(), used as linker wrap entry point (-Wl,--wrap=fopen) // REF: https://developer.android.com/ndk/reference/group/asset -FILE *android_fopen(const char *fileName, const char *mode) +FILE *__wrap_fopen(const char *fileName, const char *mode) { FILE *file = NULL; - + + // NOTE: AAsset provides access to read-only asset, write operations use regular fopen if (mode[0] == 'w') { - // NOTE: fopen() is mapped to android_fopen() that only grants read access to - // assets directory through AAssetManager but it could be required to write data - // using the standard stdio FILE access functions - // REF: https://stackoverflow.com/questions/11294487/android-writing-saving-files-from-native-code-only - #undef fopen - file = fopen(TextFormat("%s/%s", platform.app->activity->internalDataPath, fileName), mode); - #define fopen(name, mode) android_fopen(name, mode) + file = __real_fopen(TextFormat("%s/%s", platform.app->activity->internalDataPath, fileName), mode); + if (file == NULL) file = __real_fopen(fileName, mode); } else { - // NOTE: AAsset provides access to read-only asset AAsset *asset = AAssetManager_open(platform.app->activity->assetManager, fileName, AASSET_MODE_UNKNOWN); if (asset != NULL) @@ -1552,14 +1547,12 @@ FILE *android_fopen(const char *fileName, const char *mode) } else { - #undef fopen // Just do a regular open if file is not found in the assets - file = fopen(TextFormat("%s/%s", platform.app->activity->internalDataPath, fileName), mode); - if (file == NULL) file = fopen(fileName, mode); - #define fopen(name, mode) android_fopen(name, mode) + file = __real_fopen(TextFormat("%s/%s", platform.app->activity->internalDataPath, fileName), mode); + if (file == NULL) file = __real_fopen(fileName, mode); } } - + return file; } diff --git a/src/raudio.c b/src/raudio.c index ec2b474d1..afe0a7814 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -180,11 +180,6 @@ typedef struct tagBITMAPINFOHEADER { #include // Required for: FILE, fopen(), fclose(), fread() #include // Required for: strcmp() [Used in IsFileExtension(), LoadWaveFromMemory(), LoadMusicStreamFromMemory()] -#if defined(PLATFORM_ANDROID) - FILE *android_fopen(const char *fileName, const char *mode); - #define fopen(name, mode) android_fopen(name, mode) -#endif - #if defined(RAUDIO_STANDALONE) #ifndef TRACELOG #define TRACELOG(level, ...) printf(__VA_ARGS__) From 70a58a6ec6accb56a5c074405de88c6ea935d058 Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Mon, 2 Mar 2026 11:19:42 +0000 Subject: [PATCH 100/319] [platform][glfw][rgfw] Implementing clipboard image linux (#5603) * Testing linux implementation * Add implementation for ClipboardImage on Linux * Adding another check to make sure that only X11 include X11 libs * Adding some comments to explain the magic numbers --- src/platforms/rcore_desktop_glfw.c | 50 ++++++++++++++++++++++++++++++ src/platforms/rcore_desktop_rgfw.c | 49 +++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 5b62339e2..57ac76ed9 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1043,6 +1043,11 @@ const char *GetClipboardText(void) return glfwGetClipboardString(platform.handle); } +#if SUPPORT_CLIPBOARD_IMAGE && defined(__linux__) && defined(_GLFW_X11) + #include + #include +#endif + // Get clipboard image Image GetClipboardImage(void) { @@ -1059,6 +1064,51 @@ Image GetClipboardImage(void) if (bmpData == NULL) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data."); else image = LoadImageFromMemory(".bmp", (const unsigned char *)bmpData, (int)dataSize); + +#elif defined(__linux__) && defined(_GLFW_X11) + + // Implementation based on https://github.com/ColleagueRiley/Clipboard-Copy-Paste/blob/main/x11.c + Display* dpy = XOpenDisplay(NULL); + if (!dpy) return image; + + Window root = DefaultRootWindow(dpy); + Window win = XCreateSimpleWindow( + dpy, // The connection to the X Server + root, // The 'Parent' window (usually the desktop/root) + 0, 0, // X and Y position on the screen + 1, 1, // Width and Height (1x1 pixel) + 0, // Border width + 0, // Border color + 0 // Background color + ); + + Atom clipboard = XInternAtom(dpy, "CLIPBOARD", False); + Atom targetType = XInternAtom(dpy, "image/png", False); // Ask for PNG + Atom property = XInternAtom(dpy, "RAYLIB_CLIPBOARD_MANAGER", False); + + // Request the data: "Convert whatever is in CLIPBOARD to image/png and put it in RAYLIB_CLIPBOARD_MANAGER" + XConvertSelection(dpy, clipboard, targetType, property, win, CurrentTime); + + // Wait for the SelectionNotify event + XEvent ev; + XNextEvent(dpy, &ev); + + Atom actualType; + int actualFormat; + unsigned long nitems, bytesAfter; + unsigned char* data = NULL; + + // Read the data from our ghost window's property + XGetWindowProperty(dpy, win, property, 0, ~0L, False, AnyPropertyType, + &actualType, &actualFormat, &nitems, &bytesAfter, &data); + + if (data != NULL) { + image = LoadImageFromMemory(".png", data, (int)nitems); + XFree(data); + } + + XDestroyWindow(dpy, win); + XCloseDisplay(dpy); #else TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform"); #endif // defined(_WIN32) diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index ed471645c..651f1206a 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -999,6 +999,9 @@ const char *GetClipboardText(void) #define WINBASE_ALREADY_INCLUDED #define WINGDI_ALREADY_INCLUDED #include "../external/win32_clipboard.h" +#elif defined(__linux__) && defined(DRGFW_X11) + #include + #include #endif #endif @@ -1006,6 +1009,7 @@ const char *GetClipboardText(void) Image GetClipboardImage(void) { Image image = { 0 }; + #if SUPPORT_CLIPBOARD_IMAGE && SUPPORT_MODULE_RTEXTURES #if defined(_WIN32) @@ -1018,6 +1022,51 @@ Image GetClipboardImage(void) if (fileData == NULL) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data"); else image = LoadImageFromMemory(".bmp", (const unsigned char *)fileData, dataSize); + +#elif defined(__linux__) && defined(DRGFW_X11) + + // Implementation based on https://github.com/ColleagueRiley/Clipboard-Copy-Paste/blob/main/x11.c + Display* dpy = XOpenDisplay(NULL); + if (!dpy) return image; + + Window root = DefaultRootWindow(dpy); + Window win = XCreateSimpleWindow( + dpy, // The connection to the X Server + root, // The 'Parent' window (usually the desktop/root) + 0, 0, // X and Y position on the screen + 1, 1, // Width and Height (1x1 pixel) + 0, // Border width + 0, // Border color + 0 // Background color + ); + + Atom clipboard = XInternAtom(dpy, "CLIPBOARD", False); + Atom targetType = XInternAtom(dpy, "image/png", False); // Ask for PNG + Atom property = XInternAtom(dpy, "RAYLIB_CLIPBOARD_MANAGER", False); + + // Request the data: "Convert whatever is in CLIPBOARD to image/png and put it in RAYLIB_CLIPBOARD_MANAGER" + XConvertSelection(dpy, clipboard, targetType, property, win, CurrentTime); + + // Wait for the SelectionNotify event + XEvent ev; + XNextEvent(dpy, &ev); + + Atom actualType; + int actualFormat; + unsigned long nitems, bytesAfter; + unsigned char* data = NULL; + + // Read the data from our ghost window's property + XGetWindowProperty(dpy, win, property, 0, ~0L, False, AnyPropertyType, + &actualType, &actualFormat, &nitems, &bytesAfter, &data); + + if (data != NULL) { + image = LoadImageFromMemory(".png", data, (int)nitems); + XFree(data); + } + + XDestroyWindow(dpy, win); + XCloseDisplay(dpy); #else TRACELOG(LOG_WARNING, "Clipboard image: PLATFORM_DESKTOP_RGFW doesn't implement GetClipboardImage() for this OS"); #endif // defined(_WIN32) From 0b9239eca2f6a58b93ecce530e8f34753d5c5557 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 2 Mar 2026 12:24:29 +0100 Subject: [PATCH 101/319] REVIEWED: Code formating --- src/platforms/rcore_desktop_glfw.c | 20 +++++++++++--------- src/platforms/rcore_desktop_rgfw.c | 20 +++++++++++--------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 57ac76ed9..19603d8d3 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1067,8 +1067,8 @@ Image GetClipboardImage(void) #elif defined(__linux__) && defined(_GLFW_X11) - // Implementation based on https://github.com/ColleagueRiley/Clipboard-Copy-Paste/blob/main/x11.c - Display* dpy = XOpenDisplay(NULL); + // REF: https://github.com/ColleagueRiley/Clipboard-Copy-Paste/blob/main/x11.c + Display *dpy = XOpenDisplay(NULL); if (!dpy) return image; Window root = DefaultRootWindow(dpy); @@ -1090,19 +1090,21 @@ Image GetClipboardImage(void) XConvertSelection(dpy, clipboard, targetType, property, win, CurrentTime); // Wait for the SelectionNotify event - XEvent ev; + XEvent ev = { 0 }; XNextEvent(dpy, &ev); - Atom actualType; - int actualFormat; - unsigned long nitems, bytesAfter; - unsigned char* data = NULL; + Atom actualType = { 0 }; + int actualFormat = 0; + unsigned long nitems = 0; + unsigned long bytesAfter = 0; + unsigned char *data = NULL; // Read the data from our ghost window's property XGetWindowProperty(dpy, win, property, 0, ~0L, False, AnyPropertyType, - &actualType, &actualFormat, &nitems, &bytesAfter, &data); + &actualType, &actualFormat, &nitems, &bytesAfter, &data); - if (data != NULL) { + if (data != NULL) + { image = LoadImageFromMemory(".png", data, (int)nitems); XFree(data); } diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 651f1206a..f4f388998 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -1025,8 +1025,8 @@ Image GetClipboardImage(void) #elif defined(__linux__) && defined(DRGFW_X11) - // Implementation based on https://github.com/ColleagueRiley/Clipboard-Copy-Paste/blob/main/x11.c - Display* dpy = XOpenDisplay(NULL); + // REF: https://github.com/ColleagueRiley/Clipboard-Copy-Paste/blob/main/x11.c + Display *dpy = XOpenDisplay(NULL); if (!dpy) return image; Window root = DefaultRootWindow(dpy); @@ -1048,19 +1048,21 @@ Image GetClipboardImage(void) XConvertSelection(dpy, clipboard, targetType, property, win, CurrentTime); // Wait for the SelectionNotify event - XEvent ev; + XEvent ev = { 0 }; XNextEvent(dpy, &ev); - Atom actualType; - int actualFormat; - unsigned long nitems, bytesAfter; - unsigned char* data = NULL; + Atom actualType = { 0 }; + int actualFormat = 0; + unsigned long nitems = 0; + unsigned long bytesAfter = 0; + unsigned char *data = NULL; // Read the data from our ghost window's property XGetWindowProperty(dpy, win, property, 0, ~0L, False, AnyPropertyType, - &actualType, &actualFormat, &nitems, &bytesAfter, &data); + &actualType, &actualFormat, &nitems, &bytesAfter, &data); - if (data != NULL) { + if (data != NULL) + { image = LoadImageFromMemory(".png", data, (int)nitems); XFree(data); } From 8596c940aebe1d15bb2d6ee5fd4e0e671361af44 Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Mon, 2 Mar 2026 11:34:02 +0000 Subject: [PATCH 102/319] Add clipboard image example to showcase the chipboard image functionality (#5604) --- examples/textures/textures_clipboard_image.c | 125 ++++++++++++++++++ .../textures/textures_clipboard_image.png | Bin 0 -> 201242 bytes 2 files changed, 125 insertions(+) create mode 100644 examples/textures/textures_clipboard_image.c create mode 100644 examples/textures/textures_clipboard_image.png diff --git a/examples/textures/textures_clipboard_image.c b/examples/textures/textures_clipboard_image.c new file mode 100644 index 000000000..5cf87c1c2 --- /dev/null +++ b/examples/textures/textures_clipboard_image.c @@ -0,0 +1,125 @@ +/******************************************************************************************* +* +* raylib [textures] example - clipboard image +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* Example originally created with raylib 5.5, last time updated with raylib 5.6 +* +* Example contributed by Maicon Santana (@maiconpintoabreu) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2026-2026 Maicon Santana (@maiconpintoabreu) +* +********************************************************************************************/ + +#define RCORE_PLATFORM_RGFW +#include "raylib.h" + +#define MAX_IAMGE_COLLECTION_AMOUNT 1000 + +typedef struct ImageCollection { + Texture2D texture; + Vector2 position; +} ImageCollection; + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [textures] example - clipboard_image"); + + ImageCollection collection[MAX_IAMGE_COLLECTION_AMOUNT] = {0}; + int currentCollectionIndex = 0; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + // TODO: Update variables / Implement example logic at this point + //---------------------------------------------------------------------------------- + + if(IsKeyPressed(KEY_R)) + { + currentCollectionIndex = 0; + + // Unload Texture to avoid Memory leak + for (int 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

5=3z&^$j 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 s$8SCT%`DKW|Nr6t& Date: Thu, 19 Feb 2026 17:01:27 +0100 Subject: [PATCH 033/319] REVIEWED: examples: moved some examples out of others --- examples/Makefile | 7 ------- .../core_window_web.c} | 12 ++++++------ examples/core/core_window_web.png | Bin 0 -> 15094 bytes examples/examples_list.txt | 6 ------ examples/others/web_basic_window.png | Bin 10297 -> 0 bytes .../resources/shaders/glsl430/gol.glsl | 0 .../resources/shaders/glsl430/gol_render.glsl | 0 .../shaders/glsl430/gol_transfert.glsl | 0 .../shaders_rlgl_compute.c} | 7 ++++--- .../shaders_rlgl_compute.png} | Bin 10 files changed, 10 insertions(+), 22 deletions(-) rename examples/{others/web_basic_window.c => core/core_window_web.c} (93%) create mode 100644 examples/core/core_window_web.png delete mode 100644 examples/others/web_basic_window.png rename examples/{others => shaders}/resources/shaders/glsl430/gol.glsl (100%) rename examples/{others => shaders}/resources/shaders/glsl430/gol_render.glsl (100%) rename examples/{others => shaders}/resources/shaders/glsl430/gol_transfert.glsl (100%) rename examples/{others/rlgl_compute_shader.c => shaders/shaders_rlgl_compute.c} (97%) rename examples/{others/rlgl_compute_shader.png => shaders/shaders_rlgl_compute.png} (100%) diff --git a/examples/Makefile b/examples/Makefile index 4f86ad5c0..2db2d8453 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -729,13 +729,6 @@ AUDIO = \ audio/audio_spectrum_visualizer \ audio/audio_stream_effects -OTHERS = \ - others/easings_testbed \ - others/embedded_files_loading \ - others/raylib_opengl_interop \ - others/rlgl_compute_shader \ - others/rlgl_standalone \ - others/web_basic_window #EXAMPLES_LIST_END # Define processes to execute diff --git a/examples/others/web_basic_window.c b/examples/core/core_window_web.c similarity index 93% rename from examples/others/web_basic_window.c rename to examples/core/core_window_web.c index 217c47fbc..3b1b50748 100644 --- a/examples/others/web_basic_window.c +++ b/examples/core/core_window_web.c @@ -1,14 +1,14 @@ /******************************************************************************************* * -* raylib [others] example - basic window -* -* This example has been adapted to compile for PLATFORM_WEB and PLATFORM_DESKTOP -* As you will notice, code structure is slightly different to the other examples +* raylib [core] example - window web * * Example complexity rating: [★☆☆☆] 1/4 * * Example originally created with raylib 1.3, last time updated with raylib 5.5 * +* This example has been adapted to compile for PLATFORM_WEB and PLATFORM_DESKTOP +* As you will notice, code structure is slightly different to the other examples +* * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software * @@ -40,7 +40,7 @@ int main(void) { // Initialization //-------------------------------------------------------------------------------------- - InitWindow(screenWidth, screenHeight, "raylib [others] example - web basic window"); + InitWindow(screenWidth, screenHeight, "raylib [core] example - window web"); #if defined(PLATFORM_WEB) emscripten_set_main_loop(UpdateDrawFrame, 0, 1); @@ -79,7 +79,7 @@ void UpdateDrawFrame(void) ClearBackground(RAYWHITE); - DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); + DrawText("Welcome to raylib web structure!", 220, 200, 20, SKYBLUE); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/examples/core/core_window_web.png b/examples/core/core_window_web.png new file mode 100644 index 0000000000000000000000000000000000000000..32886d665c07f38031ab8ce00696ae2a718d8d95 GIT binary patch literal 15094 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYU_8XZ1{4ubUVDOp!D_Fki(^PdT=Jh^Utia^ z1sX^(Emp)qlyEREyBHwH+jvnx0xQ8-)p{U5u-U`G)Ii)2=qSoXt^+2 zE{v87qvgVAxqxl_bF_3EEgeTo$I;S}*wQh>dBFz}ab}Kww~LUGInKt5d?zxpaL&&x zh~@y#zTD2>xM0ZQA$xLh$&>sgeKLK0Gn|A>*A+O3%V#|_z3@(U)yr&?_@1zG@I+9v zDvO)7!s5(A6Bak;1S~{Dp5g%`scT@x%<@bdzMlP?cHeDrvI$Fu9Mj^LIVKYkGci0V z96je6dY0|SH+%Df=R`)5G;gD#2-A^yEY2A%vqOs6W>+OG=DB%9rSe{vuRF9#>y-=7K9@1~oxAo*u!RrB znHC!>EM8fK#T#I+0sUfN#cVX|2GFYa#HGapT{3So92UtnH_^{yGyh#_GrtT1s@cqA7GZko39wbEn z5f!2mOn(o5**D>zU==^t+HY|@TbI}deDptY@8ZO&TT4`u9`COIQn%z{6)=3-V!`IB zy&}e3Mh?ap6he8?Sk`ToEiQx_Y^KiTvHU zEq81KUQWF7p=Bw1(q-|LFWYauY`pQs?|qG9PR(Pj{chUp!FDc`h6GnRp5Qw0xy(X=p{7Dn3(=zX2ha$&Sw7%dk@%Z1T$ z0ox$ZXz4gwI*yi(qow0$={Q=OV literal 0 HcmV?d00001 diff --git a/examples/examples_list.txt b/examples/examples_list.txt index eda62ee13..196fd15a7 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -211,9 +211,3 @@ audio;audio_stream_effects;★★★★;4.2;5.0;2022;2025;"Ramon Santamaria";@ra audio;audio_sound_multi;★★☆☆;5.0;5.0;2023;2025;"Jeffery Myers";@JeffM2501 audio;audio_sound_positioning;★★☆☆;5.5;5.5;2025;2025;"Le Juez Victor";@Bigfoot71 audio;audio_spectrum_visualizer;★★★☆;6.0;5.6-dev;2025;2025;"IANN";@meisei4 -others;rlgl_standalone;★★★★;1.6;4.0;2014;2025;"Ramon Santamaria";@raysan5 -others;rlgl_compute_shader;★★★★;4.0;4.0;2021;2025;"Teddy Astie";@tsnake41 -others;easings_testbed;★★★☆;2.5;3.0;2019;2025;"Juan Miguel López";@flashback-fx -others;raylib_opengl_interop;★★★★;3.8;4.0;2021;2025;"Stephan Soller";@arkanis -others;embedded_files_loading;★★☆☆;3.0;3.5;2020;2025;"Kristian Holmgren";@defutura -others;web_basic_window;★☆☆☆;5.6-dev;5.6-dev;2014;2025;"Ramon Santamaria";@raysan5 diff --git a/examples/others/web_basic_window.png b/examples/others/web_basic_window.png deleted file mode 100644 index 346184417a436343a769ba38a9ad9cfda0195181..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10297 zcmeHNdr(tX8ovPwtb*t`tzf8RsjKd8D-SDCgd{K^I$MIZ(%@J~s0vjWd4vcALV~zH zhS9L&w2Oko9cN)Tx8$nWqC65rEUZd04virSi8M&a4JJUiggozF5Nvk_{@Xuq=FUmZ zx#xV}m*4r$_qzAu0eX`Es*S4v0Px?xFX12nc#;5s(CzDujchpD+=qP;*aws10f{`= zfOWjG^0JDGGYgLc2Qp8bD%ws@JVZLZWBbnS5fMq2crpNN{oDS8xI-lwZ-$;- zCibqQeXd9OpHWA%eJ<;O5AKLh6qcwbVhLwrK7KTENc4x&zr?!{hgpVB)uFY5-~G=; zi>Rb}_VD`!1n)rBQnSia_Wp(f|JtB;BY~_}RMfu8YwfR-0$DdANi^sBPf;)7uZt_6 z3V_N_9)6kngK_Xxco*R5H{Uomt|fUS2;TMm!fS%ol5QOz%nh9#PS{1vez-XCTLK}J z=H>f#)xy%N1cJwrbIszDs@o4Tmjs;MNuqgO0N(WjS)?5#8tcg|b;3mZ#r!DwxkZWo zi_pC&1;&_`MH6&=m)UZ0uz3AQZoU7a zM2EaFw(Cd>lD%z?d3*Wh2K^`&r}uiFU&3KfCBSBRoL%a(1dF(2t-o#F(yiV_T$Jcv zei++O+HX+p!$t-pb5m2>FZ-`q!r>OQ0xtbqzSbt6Z7GOal;{8k>#{(L#iu_c1~1{j zVy=KoU2>1K&No|^yC~887B35QYU-Qls9j4qbV*mhC93WMkGxL)lX`LMX~$Op8Wg|a z+EkMKqJ zpKnFg3|_c|VpjGtc|%)8`)x0@I~B$0t@UsIEJ;-~OEuaZWeA7qa#BS+UMFMFQXhI! zxnSQ%WoYm?BjS#|n)6u4L>}C;cQ8WuNI_G*0yOK=E#)c>GgiT%@?%@=T#uf=4>8a+ zrEYmGmWF7fX^rH%VqO;=0_`@?#79aYiW}C6Z9F?iGta+RWx3{VoK1%uKeNSVP^xL~ zxOd9>+PyyOllk=1_SZ_Y`@y8x!Q^Q`q0D|SrG%{+*(uFwFt?f3>_GWYoZv2V=p-LO zPIFA0ALQEDym@Eccc#)%-K7ZG+(dDM{D(2a=9}o*Utph^bPEb!vcbkV8-=02B1ChBx6snl50FuP*bUaZv?NrY`_or)yLGH!O=6sLq6RYbwsBp^@OVb7!oW$U zisZ%7!gdj=*00qYSeTI{_{u@N1?ul`>0m{S zyWYHg>-B$PJMZ>B_MmGf?zTN~R4GK^)j!Q7Rv=hg4q9#FbrUeBf#;lw-WpcSBh-(f zmQo3(7Ca5oQLK2QW;bLgU`vXn!(O(>b~VGQG&;*ApgCZ`MYkMr*;?3FC9QVnfTg)% z;L{Z9hF}qkCiuAA<~-|(Y=k)2V;JHt@ zJ&wK64pEI?BSRsv`u`|kV{LG={CFD##b>q2XDr$JLaQZ3(aNPW^l*Vn8c;h>Hnthl zN*UqsoNVMeWm{vwHG5S-n`XvhsDPveS|nxwfo+hqVHiVImP8)8a^9f7^3tFmut4%m zWa>(l0pTNhqbj0*3g*$FGDJV%Y*fXj=OH$R$P5p~m|~gr{jP@ltLJM&UVW%v2V7NX z?~)n)c?d#v%o#Y4#@dci4zv=qTPr>UGeZ2E4AMqbgv+M9k;Zev2-G%t zuT1J&rz;!VhLLJrQzN4WHli+Dbr-B-P)p2QH$>US5TRhN?BsT#)C5t+&23skwfrU~ zSoK&7Tb8p)X{?o!KC^z&p2jIe++T*Y+M`Vkx*4trVI$VDbUn4-0vlz~Ny0M4h7)Xl zt_jm*-`>8aU}Z zHz<6`kr(%mD2XQU_fr$b+OgZ*%h2g^WVI7ER;YB4{E2RpqdVtRS${IdAuos|vhOWI zs{5KWB2}l#r{|CQDT9nGa@$i1MkyWChEE~4 zn~sLQzQh-j-2?KG87-0Ui*@X-_OSKgT|hnlq=~stRR~qsW`o5YqL=}e#_T{aecw{Wxhm;pAY +#include // Required for: NULL // IMPORTANT: This must match gol*.glsl GOL_WIDTH constant // This must be a multiple of 16 (check golLogic compute dispatch) diff --git a/examples/others/rlgl_compute_shader.png b/examples/shaders/shaders_rlgl_compute.png similarity index 100% rename from examples/others/rlgl_compute_shader.png rename to examples/shaders/shaders_rlgl_compute.png From 781c37972a56e977b01e0a540ba7a96688eecdb4 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Feb 2026 17:11:02 +0100 Subject: [PATCH 034/319] Updated examples, removed others category processing --- examples/Makefile | 1 + examples/Makefile.Web | 4 + examples/README.md | 13 +- examples/examples_list.txt | 7 + examples/shapes/shapes_easings_testbed.c | 23 +- .../examples/shapes_easings_testbed.vcxproj | 569 ++++++++++++++++++ projects/VS2022/raylib.sln | 165 +---- tools/rexm/reports/examples_issues.md | 7 - tools/rexm/reports/examples_validation.md | 9 +- tools/rexm/rexm.c | 30 +- 10 files changed, 638 insertions(+), 190 deletions(-) create mode 100644 projects/VS2022/examples/shapes_easings_testbed.vcxproj diff --git a/examples/Makefile b/examples/Makefile index 2db2d8453..a6b2d6941 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -578,6 +578,7 @@ SHAPES = \ shapes/shapes_easings_ball \ shapes/shapes_easings_box \ shapes/shapes_easings_rectangles \ + shapes/shapes_easings_testbed \ shapes/shapes_following_eyes \ shapes/shapes_hilbert_curve \ shapes/shapes_kaleidoscope \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 3841dff29..0e9515382 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -563,6 +563,7 @@ SHAPES = \ shapes/shapes_easings_ball \ shapes/shapes_easings_box \ shapes/shapes_easings_rectangles \ + shapes/shapes_easings_testbed \ shapes/shapes_following_eyes \ shapes/shapes_hilbert_curve \ shapes/shapes_kaleidoscope \ @@ -917,6 +918,9 @@ shapes/shapes_easings_box: shapes/shapes_easings_box.c shapes/shapes_easings_rectangles: shapes/shapes_easings_rectangles.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +shapes/shapes_easings_testbed: shapes/shapes_easings_testbed.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + shapes/shapes_following_eyes: shapes/shapes_following_eyes.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) diff --git a/examples/README.md b/examples/README.md index 6b2c1950b..d96ca5c10 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,7 +17,7 @@ You may find it easier to use than other toolchains, especially when it comes to - `zig build [module]` to compile all examples for a module (e.g. `zig build core`) - `zig build [example]` to compile _and run_ a particular example (e.g. `zig build core_basic_window`) -## EXAMPLES COLLECTION [TOTAL: 208] +## EXAMPLES COLLECTION [TOTAL: 203] ### category: core [48] @@ -74,7 +74,7 @@ Examples using raylib [core](../src/rcore.c) module platform functionality: wind | [core_compute_hash](core/core_compute_hash.c) | core_compute_hash | ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Ramon Santamaria](https://github.com/raysan5) | | [core_keyboard_testbed](core/core_keyboard_testbed.c) | core_keyboard_testbed | ⭐⭐☆☆ | 5.6 | 5.6 | [Ramon Santamaria](https://github.com/raysan5) | -### category: shapes [39] +### category: shapes [40] Examples using raylib shapes drawing functionality, provided by raylib [shapes](../src/rshapes.c) module. @@ -119,6 +119,7 @@ Examples using raylib shapes drawing functionality, provided by raylib [shapes]( | [shapes_ball_physics](shapes/shapes_ball_physics.c) | shapes_ball_physics | ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [David Buzatto](https://github.com/davidbuzatto) | | [shapes_penrose_tile](shapes/shapes_penrose_tile.c) | shapes_penrose_tile | ⭐⭐⭐⭐️ | 5.5 | 5.6-dev | [David Buzatto](https://github.com/davidbuzatto) | | [shapes_hilbert_curve](shapes/shapes_hilbert_curve.c) | shapes_hilbert_curve | ⭐⭐⭐☆ | 5.6 | 5.6 | [Hamza RAHAL](https://github.com/hmz-rhl) | +| [shapes_easings_testbed](shapes/shapes_easings_testbed.c) | shapes_easings_testbed | ⭐⭐⭐☆ | 2.5 | 2.5 | [Juan Miguel López](https://github.com/flashback-fx) | ### category: textures [30] @@ -270,18 +271,12 @@ Examples using raylib audio functionality, including sound/music loading and pla | [audio_sound_positioning](audio/audio_sound_positioning.c) | audio_sound_positioning | ⭐⭐☆☆ | 5.5 | 5.5 | [Le Juez Victor](https://github.com/Bigfoot71) | | [audio_spectrum_visualizer](audio/audio_spectrum_visualizer.c) | audio_spectrum_visualizer | ⭐⭐⭐☆ | 6.0 | 5.6-dev | [IANN](https://github.com/meisei4) | -### category: others [6] +### category: others [0] 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 | |-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| [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) | -| [web_basic_window](others/web_basic_window.c) | web_basic_window | ⭐☆☆☆ | 5.6-dev | 5.6-dev | [Ramon Santamaria](https://github.com/raysan5) | Some example missing? As always, contributions are welcome, feel free to send new examples! Here is an [examples template](examples_template.c) with instructions to start with! diff --git a/examples/examples_list.txt b/examples/examples_list.txt index 196fd15a7..0ad7a763a 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -96,6 +96,7 @@ shapes;shapes_rlgl_triangle;★★☆☆;5.6-dev;5.6-dev;2025;2025;"Robin";@Robi shapes;shapes_ball_physics;★★☆☆;5.6-dev;5.6-dev;2025;2025;"David Buzatto";@davidbuzatto shapes;shapes_penrose_tile;★★★★;5.5;5.6-dev;2025;2025;"David Buzatto";@davidbuzatto shapes;shapes_hilbert_curve;★★★☆;5.6;5.6;2025;2025;"Hamza RAHAL";@hmz-rhl +shapes;shapes_easings_testbed;★★★☆;2.5;2.5;2019;2025;"Juan Miguel López";@flashback-fx textures;textures_logo_raylib;★☆☆☆;1.0;1.0;2014;2025;"Ramon Santamaria";@raysan5 textures;textures_srcrec_dstrec;★★★☆;1.3;1.3;2015;2025;"Ramon Santamaria";@raysan5 textures;textures_image_drawing;★★☆☆;1.4;1.4;2016;2025;"Ramon Santamaria";@raysan5 @@ -211,3 +212,9 @@ audio;audio_stream_effects;★★★★;4.2;5.0;2022;2025;"Ramon Santamaria";@ra audio;audio_sound_multi;★★☆☆;5.0;5.0;2023;2025;"Jeffery Myers";@JeffM2501 audio;audio_sound_positioning;★★☆☆;5.5;5.5;2025;2025;"Le Juez Victor";@Bigfoot71 audio;audio_spectrum_visualizer;★★★☆;6.0;5.6-dev;2025;2025;"IANN";@meisei4 +ei4 +ei4 +meisei4 +ei4 +ei4 +ei4 diff --git a/examples/shapes/shapes_easings_testbed.c b/examples/shapes/shapes_easings_testbed.c index a3ff23a7c..3f4f2528a 100644 --- a/examples/shapes/shapes_easings_testbed.c +++ b/examples/shapes/shapes_easings_testbed.c @@ -68,6 +68,15 @@ typedef struct EasingFuncs { float (*func)(float, float, float, float); } EasingFuncs; +//------------------------------------------------------------------------------------ +// Module Functions Declaration +//------------------------------------------------------------------------------------ +// Function used when "no easing" is selected for any axis +static float NoEase(float t, float b, float c, float d); + +//------------------------------------------------------------------------------------ +// Global Variables Definition +//------------------------------------------------------------------------------------ // Easing functions reference data static const EasingFuncs easings[] = { [EASE_LINEAR_NONE] = { .name = "EaseLinearNone", .func = EaseLinearNone }, @@ -101,11 +110,7 @@ static const EasingFuncs easings[] = { [EASING_NONE] = { .name = "None", .func = NoEase }, }; -//------------------------------------------------------------------------------------ -// Module Functions Declaration -//------------------------------------------------------------------------------------ -// Function used when "no easing" is selected for any axis -static float NoEase(float t, float b, float c, float d); + //------------------------------------------------------------------------------------ // Program main entry point @@ -191,8 +196,8 @@ int main(void) // Movement computation if (!paused && ((boundedT && t < d) || !boundedT)) { - ballPosition.x = Easings[easingX].func(t, 100.0f, 700.0f - 170.0f, d); - ballPosition.y = Easings[easingY].func(t, 100.0f, 400.0f - 170.0f, d); + ballPosition.x = easings[easingX].func(t, 100.0f, 700.0f - 170.0f, d); + ballPosition.y = easings[easingY].func(t, 100.0f, 400.0f - 170.0f, d); t += 1.0f; } //---------------------------------------------------------------------------------- @@ -204,8 +209,8 @@ int main(void) ClearBackground(RAYWHITE); // Draw information text - DrawText(TextFormat("Easing x: %s", Easings[easingX].name), 20, FONT_SIZE, FONT_SIZE, LIGHTGRAY); - DrawText(TextFormat("Easing y: %s", Easings[easingY].name), 20, FONT_SIZE*2, FONT_SIZE, LIGHTGRAY); + DrawText(TextFormat("Easing x: %s", easings[easingX].name), 20, FONT_SIZE, FONT_SIZE, LIGHTGRAY); + DrawText(TextFormat("Easing y: %s", easings[easingY].name), 20, FONT_SIZE*2, FONT_SIZE, LIGHTGRAY); DrawText(TextFormat("t (%c) = %.2f d = %.2f", (boundedT == true)? 'b' : 'u', t, d), 20, FONT_SIZE*3, FONT_SIZE, LIGHTGRAY); // Draw instructions text diff --git a/projects/VS2022/examples/shapes_easings_testbed.vcxproj b/projects/VS2022/examples/shapes_easings_testbed.vcxproj new file mode 100644 index 000000000..b2d63b10c --- /dev/null +++ b/projects/VS2022/examples/shapes_easings_testbed.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {4250CE87-9AA0-43BB-AB47-0636548922B5} + Win32Proj + shapes_easings_testbed + 10.0 + shapes_easings_testbed + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 740d1bcbe..7b4c4b30a 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -21,8 +21,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shaders", "shaders", "{5317 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "audio", "audio", "{CC132A4D-D081-4C26-BFB9-AB11984054F8}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "others", "others", "{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_animation", "examples\textures_sprite_animation.vcxproj", "{C25D2CC6-80CA-4C8A-BE3B-2E0F4EA5D0CC}" @@ -231,12 +229,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_texture_rendering", EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_texture_waves", "examples\shaders_texture_waves.vcxproj", "{291B4975-8EFF-4C7C-8AF3-44A77B8491B8}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "embedded_files_loading", "examples\embedded_files_loading.vcxproj", "{FDE6080B-E203-4066-910D-AD0302566008}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "easings_testbed", "examples\easings_testbed.vcxproj", "{E1B6D565-9D7C-46B7-9202-ECF54974DE50}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rlgl_standalone", "examples\rlgl_standalone.vcxproj", "{C8765523-58F8-4C8E-9914-693396F6F0FF}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_loading_vox", "examples\models_loading_vox.vcxproj", "{2F1B955B-275E-4D8E-8864-06FEC44D7912}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_loading_gltf", "examples\models_loading_gltf.vcxproj", "{F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}" @@ -255,8 +247,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_basic_screen_manager", EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_custom_frame_control", "examples\core_custom_frame_control.vcxproj", "{658A1B85-554E-4A5D-973A-FFE592CDD5F2}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rlgl_compute_shader", "examples\rlgl_compute_shader.vcxproj", "{07CA51AD-72AE-46A2-AAED-DC3E3F807976}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_3d_drawing", "examples\text_3d_drawing.vcxproj", "{27B110CC-43C0-400A-89D9-245E681647D7}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_polygon_drawing", "examples\textures_polygon_drawing.vcxproj", "{1DE84812-E143-4C4B-A61D-9267AAD55401}" @@ -363,8 +353,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_ascii_rendering", " EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_monitor_detector", "examples\core_monitor_detector.vcxproj", "{FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "web_basic_window", "examples\web_basic_window.vcxproj", "{A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_kaleidoscope", "examples\shapes_kaleidoscope.vcxproj", "{0C442799-B09C-4CD1-9538-711B6E85E9BF}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_recursive_tree", "examples\shapes_recursive_tree.vcxproj", "{DFB40A10-F8B7-412A-BCC3-5EE49294D816}" @@ -437,6 +425,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_keyboard_testbed", "ex EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_framebuffer_rendering", "examples\textures_framebuffer_rendering.vcxproj", "{F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_easings_testbed", "examples\shapes_easings_testbed.vcxproj", "{4250CE87-9AA0-43BB-AB47-0636548922B5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|ARM64 = Debug.DLL|ARM64 @@ -2973,76 +2963,6 @@ Global {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release|x64.Build.0 = Release|x64 {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release|x86.ActiveCfg = Release|Win32 {291B4975-8EFF-4C7C-8AF3-44A77B8491B8}.Release|x86.Build.0 = Release|Win32 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug|ARM64.Build.0 = Debug|ARM64 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug|x64.ActiveCfg = Debug|x64 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug|x64.Build.0 = Debug|x64 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug|x86.ActiveCfg = Debug|Win32 - {FDE6080B-E203-4066-910D-AD0302566008}.Debug|x86.Build.0 = Debug|Win32 - {FDE6080B-E203-4066-910D-AD0302566008}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {FDE6080B-E203-4066-910D-AD0302566008}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {FDE6080B-E203-4066-910D-AD0302566008}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {FDE6080B-E203-4066-910D-AD0302566008}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {FDE6080B-E203-4066-910D-AD0302566008}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {FDE6080B-E203-4066-910D-AD0302566008}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {FDE6080B-E203-4066-910D-AD0302566008}.Release|ARM64.ActiveCfg = Release|ARM64 - {FDE6080B-E203-4066-910D-AD0302566008}.Release|ARM64.Build.0 = Release|ARM64 - {FDE6080B-E203-4066-910D-AD0302566008}.Release|x64.ActiveCfg = Release|x64 - {FDE6080B-E203-4066-910D-AD0302566008}.Release|x64.Build.0 = Release|x64 - {FDE6080B-E203-4066-910D-AD0302566008}.Release|x86.ActiveCfg = Release|Win32 - {FDE6080B-E203-4066-910D-AD0302566008}.Release|x86.Build.0 = Release|Win32 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug|ARM64.Build.0 = Debug|ARM64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug|x64.ActiveCfg = Debug|x64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug|x64.Build.0 = Debug|x64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug|x86.ActiveCfg = Debug|Win32 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Debug|x86.Build.0 = Debug|Win32 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release|ARM64.ActiveCfg = Release|ARM64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release|ARM64.Build.0 = Release|ARM64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release|x64.ActiveCfg = Release|x64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release|x64.Build.0 = Release|x64 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release|x86.ActiveCfg = Release|Win32 - {E1B6D565-9D7C-46B7-9202-ECF54974DE50}.Release|x86.Build.0 = Release|Win32 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug|ARM64.Build.0 = Debug|ARM64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug|x64.ActiveCfg = Debug|x64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug|x64.Build.0 = Debug|x64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug|x86.ActiveCfg = Debug|Win32 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Debug|x86.Build.0 = Debug|Win32 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|ARM64.ActiveCfg = Release|ARM64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|ARM64.Build.0 = Release|ARM64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|x64.ActiveCfg = Release|x64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|x64.Build.0 = Release|x64 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|x86.ActiveCfg = Release|Win32 - {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|x86.Build.0 = Release|Win32 {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 @@ -3259,30 +3179,6 @@ Global {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release|x64.Build.0 = Release|x64 {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release|x86.ActiveCfg = Release|Win32 {658A1B85-554E-4A5D-973A-FFE592CDD5F2}.Release|x86.Build.0 = Release|Win32 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug|ARM64.Build.0 = Debug|ARM64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug|x64.ActiveCfg = Debug|x64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug|x64.Build.0 = Debug|x64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug|x86.ActiveCfg = Debug|Win32 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Debug|x86.Build.0 = Debug|Win32 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release|ARM64.ActiveCfg = Release|ARM64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release|ARM64.Build.0 = Release|ARM64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release|x64.ActiveCfg = Release|x64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release|x64.Build.0 = Release|x64 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release|x86.ActiveCfg = Release|Win32 - {07CA51AD-72AE-46A2-AAED-DC3E3F807976}.Release|x86.Build.0 = Release|Win32 {27B110CC-43C0-400A-89D9-245E681647D7}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 {27B110CC-43C0-400A-89D9-245E681647D7}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 {27B110CC-43C0-400A-89D9-245E681647D7}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 @@ -4555,30 +4451,6 @@ Global {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release|x64.Build.0 = Release|x64 {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release|x86.ActiveCfg = Release|Win32 {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3}.Release|x86.Build.0 = Release|Win32 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug|ARM64.Build.0 = Debug|ARM64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug|x64.ActiveCfg = Debug|x64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug|x64.Build.0 = Debug|x64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug|x86.ActiveCfg = Debug|Win32 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Debug|x86.Build.0 = Debug|Win32 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release|ARM64.ActiveCfg = Release|ARM64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release|ARM64.Build.0 = Release|ARM64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release|x64.ActiveCfg = Release|x64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release|x64.Build.0 = Release|x64 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release|x86.ActiveCfg = Release|Win32 - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC}.Release|x86.Build.0 = Release|Win32 {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 {0C442799-B09C-4CD1-9538-711B6E85E9BF}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 @@ -5443,6 +5315,30 @@ Global {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|x64.Build.0 = Release|x64 {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|x86.ActiveCfg = Release|Win32 {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|x86.Build.0 = Release|Win32 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Debug|ARM64.Build.0 = Debug|ARM64 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Debug|x64.ActiveCfg = Debug|x64 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Debug|x64.Build.0 = Debug|x64 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Debug|x86.ActiveCfg = Debug|Win32 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Debug|x86.Build.0 = Debug|Win32 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Release|ARM64.ActiveCfg = Release|ARM64 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Release|ARM64.Build.0 = Release|ARM64 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Release|x64.ActiveCfg = Release|x64 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Release|x64.Build.0 = Release|x64 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Release|x86.ActiveCfg = Release|Win32 + {4250CE87-9AA0-43BB-AB47-0636548922B5}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -5455,7 +5351,6 @@ Global {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} @@ -5560,9 +5455,6 @@ Global {11F33A39-74B7-4018-B5F9-CC285A673A8F} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {A6F5E35E-B4A7-41B3-853A-75558E6E0715} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {291B4975-8EFF-4C7C-8AF3-44A77B8491B8} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {FDE6080B-E203-4066-910D-AD0302566008} = {E9D708A5-9C1F-4B84-A795-C5F191801762} - {E1B6D565-9D7C-46B7-9202-ECF54974DE50} = {E9D708A5-9C1F-4B84-A795-C5F191801762} - {C8765523-58F8-4C8E-9914-693396F6F0FF} = {E9D708A5-9C1F-4B84-A795-C5F191801762} {2F1B955B-275E-4D8E-8864-06FEC44D7912} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {F2DB2E59-76BF-4D81-859A-AFC289C046C0} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} @@ -5572,7 +5464,6 @@ Global {3CFF7AB8-32CB-4D6D-9FED-53DBEF277359} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {8B1AF423-00F1-4924-AC54-F77D402D2AC9} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {658A1B85-554E-4A5D-973A-FFE592CDD5F2} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {07CA51AD-72AE-46A2-AAED-DC3E3F807976} = {E9D708A5-9C1F-4B84-A795-C5F191801762} {27B110CC-43C0-400A-89D9-245E681647D7} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} {1DE84812-E143-4C4B-A61D-9267AAD55401} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} {4A87569C-4BD3-4113-B4B9-573D65B3D3F8} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} @@ -5610,7 +5501,7 @@ Global {C54703BF-D68A-480D-BE27-49B62E45D582} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {9CD8BCAD-F212-4BCC-BA98-899743CE3279} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} {0981CA28-E4A5-4DF1-987F-A41D09131EFC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {278D8859-20B1-428F-8448-064F46E1F021} {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {6777EC3C-077C-42FC-B4AD-B799CE55CCE4} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} {A61DAD9C-271C-4E95-81AA-DB4CD58564D4} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} @@ -5626,7 +5517,6 @@ Global {A4662163-83E7-4309-8CAA-B0BF13655FE6} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {5F4B766F-DD52-4B53-B6C3-BC7611E17F20} = {278D8859-20B1-428F-8448-064F46E1F021} {FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {A9C422E7-0F03-4DBC-AC93-5C3EF4942DEC} = {E9D708A5-9C1F-4B84-A795-C5F191801762} {0C442799-B09C-4CD1-9538-711B6E85E9BF} = {278D8859-20B1-428F-8448-064F46E1F021} {DFB40A10-F8B7-412A-BCC3-5EE49294D816} = {278D8859-20B1-428F-8448-064F46E1F021} {BB58A5FB-1A35-4471-86D0-A5189EC541B3} = {278D8859-20B1-428F-8448-064F46E1F021} @@ -5663,6 +5553,7 @@ Global {DC163251-16C3-4B72-B965-ACDBA0F02BD1} = {278D8859-20B1-428F-8448-064F46E1F021} {D35D2FDA-B53F-4F70-81CA-24D95812B89C} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {4250CE87-9AA0-43BB-AB47-0636548922B5} = {278D8859-20B1-428F-8448-064F46E1F021} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} diff --git a/tools/rexm/reports/examples_issues.md b/tools/rexm/reports/examples_issues.md index 081170806..a648295cc 100644 --- a/tools/rexm/reports/examples_issues.md +++ b/tools/rexm/reports/examples_issues.md @@ -20,10 +20,3 @@ Example elements validated: ``` | **EXAMPLE NAME** | [C] | [CAT]| [INFO]|[PNG]|[WPNG]| [RES]| [MK] |[MKWEB]| [VCX]| [SOL]|[RDME]|[JS] | [WOUT]|[WMETA]| |:---------------------------------|:---:|:----:|:-----:|:---:|:----:|:----:|:----:|:-----:|:----:|:----:|:----:|:---:|:-----:|:-----:| -| core_highdpi_testbed | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| rlgl_standalone | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| rlgl_compute_shader | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| easings_testbed | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| raylib_opengl_interop | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | -| embedded_files_loading | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| web_basic_window | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | diff --git a/tools/rexm/reports/examples_validation.md b/tools/rexm/reports/examples_validation.md index d8f4a9521..90283ace7 100644 --- a/tools/rexm/reports/examples_validation.md +++ b/tools/rexm/reports/examples_validation.md @@ -62,7 +62,7 @@ Example elements validated: | core_viewport_scaling | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_input_actions | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_directory_files | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| core_highdpi_testbed | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_highdpi_testbed | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_screen_recording | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_clipboard_text | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_text_file_loading | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -107,6 +107,7 @@ Example elements validated: | shapes_ball_physics | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_penrose_tile | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_hilbert_curve | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shapes_easings_testbed | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | textures_logo_raylib | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | textures_srcrec_dstrec | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | textures_image_drawing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -222,9 +223,3 @@ Example elements validated: | audio_sound_multi | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_sound_positioning | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_spectrum_visualizer | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| rlgl_standalone | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| rlgl_compute_shader | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| easings_testbed | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| raylib_opengl_interop | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | -| embedded_files_loading | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| web_basic_window | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 70001a5f3..30ef6411f 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -63,7 +63,7 @@ #endif #define REXM_MAX_EXAMPLES 512 -#define REXM_MAX_EXAMPLE_CATEGORIES 8 +#define REXM_MAX_EXAMPLE_CATEGORIES 7 #define REXM_MAX_BUFFER_SIZE (2*1024*1024) // 2MB @@ -83,7 +83,7 @@ //---------------------------------------------------------------------------------- // raylib example info struct typedef struct { - char category[16]; // Example category: core, shapes, textures, text, models, shaders, audio, [others] + char category[16]; // Example category: core, shapes, textures, text, models, shaders, audio char name[128]; // Example name: _name_part int stars; // Example stars count: ★☆☆☆ char verCreated[12]; // Example raylib creation version @@ -151,7 +151,7 @@ typedef enum { OP_TESTLOG = 9, // Process available examples logs to generate report } rlExampleOperation; -static const char *exCategories[REXM_MAX_EXAMPLE_CATEGORIES] = { "core", "shapes", "textures", "text", "models", "shaders", "audio", "others" }; +static const char *exCategories[REXM_MAX_EXAMPLE_CATEGORIES] = { "core", "shapes", "textures", "text", "models", "shaders", "audio" }; // Paths required for examples management // NOTE: Paths can be provided with environment variables @@ -170,7 +170,7 @@ static const char *exVSProjectSolutionFile = NULL; // Env REXM_EXAMPLES_VS2022_S static int UpdateRequiredFiles(void); // Load examples collection information -// NOTE 1: Load by category: "ALL", "core", "shapes", "textures", "text", "models", "shaders", others" +// NOTE 1: Load by category: "ALL", "core", "shapes", "textures", "text", "models", "shaders", audio" // NOTE 2: Sort examples list on request flag static rlExampleInfo *LoadExampleData(const char *filter, bool sort, int *exCount); static void UnloadExampleData(rlExampleInfo *exInfo); @@ -590,7 +590,6 @@ int main(int argc, char *argv[]) else if (TextIsEqual(exCategory, "models")) nextCategoryIndex = 5; else if (TextIsEqual(exCategory, "shaders")) nextCategoryIndex = 6; else if (TextIsEqual(exCategory, "audio")) nextCategoryIndex = 7; - else if (TextIsEqual(exCategory, "others")) nextCategoryIndex = -1; // Add to EOF // Get required example info from example file header (if provided) @@ -1039,7 +1038,7 @@ int main(int argc, char *argv[]) if (nextCatIndex > (REXM_MAX_EXAMPLE_CATEGORIES - 1)) nextCatIndex = -1; // EOF // Find position to add new example on list, just before the following category - // Category order: core, shapes, textures, text, models, shaders, audio, [others] + // Category order: core, shapes, textures, text, models, shaders, audio int exListNextCatIndex = -1; if (nextCatIndex != -1) exListNextCatIndex = TextFindIndex(exList, exCategories[nextCatIndex]); else exListNextCatIndex = exListLen; // EOF @@ -1972,7 +1971,6 @@ static int UpdateRequiredFiles(void) //------------------------------------------------------------------------------------------------ // Edit: raylib/examples/Makefile.Web --> Update from collection - // NOTE: We avoid the "others" category on web building //------------------------------------------------------------------------------------------------ LOG("INFO: Updating raylib/examples/Makefile.Web\n"); char *mkwText = LoadFileText(TextFormat("%s/Makefile.Web", exBasePath)); @@ -1985,8 +1983,7 @@ static int UpdateRequiredFiles(void) memcpy(mkwTextUpdated, mkwText, mkwListStartIndex); mkwIndex = sprintf(mkwTextUpdated + mkwListStartIndex, "#EXAMPLES_LIST_START\n"); - // NOTE: We avoid the "others" category on web building - for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES - 1; i++) + for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES; i++) { mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i]))); @@ -2011,8 +2008,7 @@ static int UpdateRequiredFiles(void) 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 < REXM_MAX_EXAMPLE_CATEGORIES - 1; i++) + for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES; i++) { mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("# Compile %s examples\n", TextToUpper(exCategories[i]))); @@ -2159,12 +2155,6 @@ static int UpdateRequiredFiles(void) 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, 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"); - } // Table header required mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "| example | image | difficulty
level | version
created | last version
updated | original
developer |\n"); @@ -2227,8 +2217,7 @@ static int UpdateRequiredFiles(void) char starsText[16] = { 0 }; - // NOTE: We avoid "others" category - for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES - 1; i++) + for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES; i++) { int exCollectionCount = 0; rlExampleInfo *exCollection = LoadExampleData(exCategories[i], false, &exCollectionCount); @@ -2295,8 +2284,7 @@ static rlExampleInfo *LoadExampleData(const char *filter, bool sort, int *exCoun (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'))) // TODO: Get others category? + (lines[i][0] == 'a'))) // audio { rlExampleInfo info = { 0 }; int result = ParseExampleInfoLine(lines[i], &info); From 0a7c7569aaf4491e80073a4ef95961d75302f5e6 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Feb 2026 17:11:32 +0100 Subject: [PATCH 035/319] Update examples_list.txt --- examples/examples_list.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/examples/examples_list.txt b/examples/examples_list.txt index 0ad7a763a..6dd4aee40 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -212,9 +212,3 @@ audio;audio_stream_effects;★★★★;4.2;5.0;2022;2025;"Ramon Santamaria";@ra audio;audio_sound_multi;★★☆☆;5.0;5.0;2023;2025;"Jeffery Myers";@JeffM2501 audio;audio_sound_positioning;★★☆☆;5.5;5.5;2025;2025;"Le Juez Victor";@Bigfoot71 audio;audio_spectrum_visualizer;★★★☆;6.0;5.6-dev;2025;2025;"IANN";@meisei4 -ei4 -ei4 -meisei4 -ei4 -ei4 -ei4 From b9f16a28d358ae041b0474cb9c297b7affd33526 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Feb 2026 17:13:20 +0100 Subject: [PATCH 036/319] REXM: Update examples collection --- examples/Makefile | 2 +- examples/Makefile.Web | 4 + examples/README.md | 12 +- examples/examples_list.txt | 1 + examples/shaders/shaders_rlgl_compute.c | 4 +- .../examples/shaders_rlgl_compute.vcxproj | 569 ++++++++++++++++++ projects/VS2022/raylib.sln | 27 + tools/rexm/reports/examples_validation.md | 1 + 8 files changed, 608 insertions(+), 12 deletions(-) create mode 100644 projects/VS2022/examples/shaders_rlgl_compute.vcxproj diff --git a/examples/Makefile b/examples/Makefile index a6b2d6941..5f3f44773 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -708,6 +708,7 @@ SHADERS = \ shaders/shaders_palette_switch \ shaders/shaders_postprocessing \ shaders/shaders_raymarching_rendering \ + shaders/shaders_rlgl_compute \ shaders/shaders_rounded_rectangle \ shaders/shaders_shadowmap_rendering \ shaders/shaders_shapes_textures \ @@ -729,7 +730,6 @@ AUDIO = \ audio/audio_sound_positioning \ audio/audio_spectrum_visualizer \ audio/audio_stream_effects - #EXAMPLES_LIST_END # Define processes to execute diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 0e9515382..3f0c28506 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -693,6 +693,7 @@ SHADERS = \ shaders/shaders_palette_switch \ shaders/shaders_postprocessing \ shaders/shaders_raymarching_rendering \ + shaders/shaders_rlgl_compute \ shaders/shaders_rounded_rectangle \ shaders/shaders_shadowmap_rendering \ shaders/shaders_shapes_textures \ @@ -1465,6 +1466,9 @@ shaders/shaders_raymarching_rendering: shaders/shaders_raymarching_rendering.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_rlgl_compute: shaders/shaders_rlgl_compute.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + 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 \ diff --git a/examples/README.md b/examples/README.md index d96ca5c10..69f6d71d1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,7 +17,7 @@ You may find it easier to use than other toolchains, especially when it comes to - `zig build [module]` to compile all examples for a module (e.g. `zig build core`) - `zig build [example]` to compile _and run_ a particular example (e.g. `zig build core_basic_window`) -## EXAMPLES COLLECTION [TOTAL: 203] +## EXAMPLES COLLECTION [TOTAL: 204] ### category: core [48] @@ -215,7 +215,7 @@ Examples using raylib models functionality, including models loading/generation | [models_decals](models/models_decals.c) | models_decals | ⭐⭐⭐⭐️ | 5.6-dev | 5.6-dev | [JP Mortiboys](https://github.com/themushroompirates) | | [models_directional_billboard](models/models_directional_billboard.c) | models_directional_billboard | ⭐⭐☆☆ | 5.6-dev | 5.6 | [Robin](https://github.com/RobinsAviary) | -### category: shaders [33] +### category: shaders [34] 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. @@ -254,6 +254,7 @@ Examples using raylib shaders functionality, including shaders loading, paramete | [shaders_rounded_rectangle](shaders/shaders_rounded_rectangle.c) | shaders_rounded_rectangle | ⭐⭐⭐☆ | 5.5 | 5.5 | [Anstro Pleuton](https://github.com/anstropleuton) | | [shaders_depth_rendering](shaders/shaders_depth_rendering.c) | shaders_depth_rendering | ⭐⭐⭐☆ | 5.6-dev | 5.6-dev | [Luís Almeida](https://github.com/luis605) | | [shaders_game_of_life](shaders/shaders_game_of_life.c) | shaders_game_of_life | ⭐⭐⭐☆ | 5.6 | 5.6 | [Jordi Santonja](https://github.com/JordSant) | +| [shaders_rlgl_compute](shaders/shaders_rlgl_compute.c) | shaders_rlgl_compute | ⭐⭐⭐⭐️ | 4.0 | 4.0 | [Teddy Astie](https://github.com/tsnake41) | ### category: audio [9] @@ -271,12 +272,5 @@ Examples using raylib audio functionality, including sound/music loading and pla | [audio_sound_positioning](audio/audio_sound_positioning.c) | audio_sound_positioning | ⭐⭐☆☆ | 5.5 | 5.5 | [Le Juez Victor](https://github.com/Bigfoot71) | | [audio_spectrum_visualizer](audio/audio_spectrum_visualizer.c) | audio_spectrum_visualizer | ⭐⭐⭐☆ | 6.0 | 5.6-dev | [IANN](https://github.com/meisei4) | -### category: others [0] - -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 | -|-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| - 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! diff --git a/examples/examples_list.txt b/examples/examples_list.txt index 6dd4aee40..a8f8cb00c 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -203,6 +203,7 @@ shaders;shaders_lightmap_rendering;★★★☆;4.5;4.5;2019;2025;"Jussi Viitala shaders;shaders_rounded_rectangle;★★★☆;5.5;5.5;2025;2025;"Anstro Pleuton";@anstropleuton shaders;shaders_depth_rendering;★★★☆;5.6-dev;5.6-dev;2025;2025;"Luís Almeida";@luis605 shaders;shaders_game_of_life;★★★☆;5.6;5.6;2025;2025;"Jordi Santonja";@JordSant +shaders;shaders_rlgl_compute;★★★★;4.0;4.0;2021;2025;"Teddy Astie";@tsnake41 audio;audio_module_playing;★☆☆☆;1.5;3.5;2016;2025;"Ramon Santamaria";@raysan5 audio;audio_music_stream;★☆☆☆;1.3;4.2;2015;2025;"Ramon Santamaria";@raysan5 audio;audio_raw_stream;★★★☆;1.6;4.2;2015;2025;"Ramon Santamaria";@raysan5 diff --git a/examples/shaders/shaders_rlgl_compute.c b/examples/shaders/shaders_rlgl_compute.c index fd8a4cec7..af3bb6fe4 100644 --- a/examples/shaders/shaders_rlgl_compute.c +++ b/examples/shaders/shaders_rlgl_compute.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [others] example - compute shader +* raylib [shaders] example - rlgl compute * * WARNING: This example requires raylib compiled with OpenGL 4.3 version for * compute shaders support, shaders used in this example are #version 430 @@ -58,7 +58,7 @@ int main(void) const int screenWidth = GOL_WIDTH; const int screenHeight = GOL_WIDTH; - InitWindow(screenWidth, screenHeight, "raylib [others] example - compute shader"); + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - rlgl compute"); const Vector2 resolution = { (float)screenWidth, (float)screenHeight }; unsigned int brushSize = 8; diff --git a/projects/VS2022/examples/shaders_rlgl_compute.vcxproj b/projects/VS2022/examples/shaders_rlgl_compute.vcxproj new file mode 100644 index 000000000..583898f19 --- /dev/null +++ b/projects/VS2022/examples/shaders_rlgl_compute.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {6B1A933E-71B8-4C1F-9E79-02D98830E671} + Win32Proj + shaders_rlgl_compute + 10.0 + shaders_rlgl_compute + + + + 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/raylib.sln b/projects/VS2022/raylib.sln index 7b4c4b30a..6575b6307 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -427,6 +427,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_framebuffer_render EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_easings_testbed", "examples\shapes_easings_testbed.vcxproj", "{4250CE87-9AA0-43BB-AB47-0636548922B5}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_rlgl_compute", "examples\shaders_rlgl_compute.vcxproj", "{6B1A933E-71B8-4C1F-9E79-02D98830E671}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|ARM64 = Debug.DLL|ARM64 @@ -5339,6 +5341,30 @@ Global {4250CE87-9AA0-43BB-AB47-0636548922B5}.Release|x64.Build.0 = Release|x64 {4250CE87-9AA0-43BB-AB47-0636548922B5}.Release|x86.ActiveCfg = Release|Win32 {4250CE87-9AA0-43BB-AB47-0636548922B5}.Release|x86.Build.0 = Release|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.Build.0 = Debug|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.ActiveCfg = Debug|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.Build.0 = Debug|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.ActiveCfg = Debug|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.Build.0 = Debug|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.ActiveCfg = Release|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.Build.0 = Release|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.ActiveCfg = Release|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.Build.0 = Release|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.ActiveCfg = Release|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -5554,6 +5580,7 @@ Global {D35D2FDA-B53F-4F70-81CA-24D95812B89C} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} {4250CE87-9AA0-43BB-AB47-0636548922B5} = {278D8859-20B1-428F-8448-064F46E1F021} + {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} diff --git a/tools/rexm/reports/examples_validation.md b/tools/rexm/reports/examples_validation.md index 90283ace7..633fcae2f 100644 --- a/tools/rexm/reports/examples_validation.md +++ b/tools/rexm/reports/examples_validation.md @@ -214,6 +214,7 @@ Example elements validated: | shaders_rounded_rectangle | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_depth_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_game_of_life | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shaders_rlgl_compute | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_module_playing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_music_stream | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_raw_stream | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | From dea67fa18a3c4b345b79981c7f82a69d8ae5f63a Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Feb 2026 17:15:26 +0100 Subject: [PATCH 037/319] REXM: Update examples collection --- examples/Makefile | 1 + examples/Makefile.Web | 7 + examples/README.md | 5 +- examples/examples_list.txt | 1 + examples/shapes/shapes_easings_testbed.c | 2 - .../models_animation_bone_blending.vcxproj | 569 ++++++++++++++++++ projects/VS2022/raylib.sln | 27 + tools/rexm/reports/examples_issues.md | 1 + tools/rexm/reports/examples_validation.md | 1 + 9 files changed, 610 insertions(+), 4 deletions(-) create mode 100644 projects/VS2022/examples/models_animation_bone_blending.vcxproj diff --git a/examples/Makefile b/examples/Makefile index 5f3f44773..103cdc0fa 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -656,6 +656,7 @@ TEXT = \ text/text_writing_anim MODELS = \ + models/models_animation_bone_blending \ models/models_animation_gpu_skinning \ models/models_animation_playing \ models/models_basic_voxel \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 3f0c28506..663751ad1 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -641,6 +641,7 @@ TEXT = \ text/text_writing_anim MODELS = \ + models/models_animation_bone_blending \ models/models_animation_gpu_skinning \ models/models_animation_playing \ models/models_basic_voxel \ @@ -1193,6 +1194,12 @@ 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_bone_blending: models/models_animation_bone_blending.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_animation_gpu_skinning: models/models_animation_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 \ diff --git a/examples/README.md b/examples/README.md index 69f6d71d1..2b7474cff 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,7 +17,7 @@ You may find it easier to use than other toolchains, especially when it comes to - `zig build [module]` to compile all examples for a module (e.g. `zig build core`) - `zig build [example]` to compile _and run_ a particular example (e.g. `zig build core_basic_window`) -## EXAMPLES COLLECTION [TOTAL: 204] +## EXAMPLES COLLECTION [TOTAL: 205] ### category: core [48] @@ -181,7 +181,7 @@ Examples using raylib text functionality, including sprite fonts loading/generat | [text_words_alignment](text/text_words_alignment.c) | text_words_alignment | ⭐☆☆☆ | 5.6-dev | 5.6-dev | [JP Mortiboys](https://github.com/themushroompirates) | | [text_strings_management](text/text_strings_management.c) | text_strings_management | ⭐⭐⭐☆ | 5.6-dev | 5.6-dev | [David Buzatto](https://github.com/davidbuzatto) | -### category: models [27] +### category: models [28] Examples using raylib models functionality, including models loading/generation and drawing, provided by raylib [models](../src/rmodels.c) module. @@ -214,6 +214,7 @@ Examples using raylib models functionality, including models loading/generation | [models_rotating_cube](models/models_rotating_cube.c) | models_rotating_cube | ⭐☆☆☆ | 5.6-dev | 5.6-dev | [Jopestpe](https://github.com/jopestpe) | | [models_decals](models/models_decals.c) | models_decals | ⭐⭐⭐⭐️ | 5.6-dev | 5.6-dev | [JP Mortiboys](https://github.com/themushroompirates) | | [models_directional_billboard](models/models_directional_billboard.c) | models_directional_billboard | ⭐⭐☆☆ | 5.6-dev | 5.6 | [Robin](https://github.com/RobinsAviary) | +| [models_animation_bone_blending](models/models_animation_bone_blending.c) | models_animation_bone_blending | ⭐⭐⭐⭐️ | 5.5 | 5.5 | [dmitrii-brand](https://github.com/dmitrii-brand) | ### category: shaders [34] diff --git a/examples/examples_list.txt b/examples/examples_list.txt index a8f8cb00c..be0def2d8 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -170,6 +170,7 @@ models;models_basic_voxel;★★☆☆;5.5;5.5;2025;2025;"Tim Little";@timlittle models;models_rotating_cube;★☆☆☆;5.6-dev;5.6-dev;2025;2025;"Jopestpe";@jopestpe models;models_decals;★★★★;5.6-dev;5.6-dev;2025;2025;"JP Mortiboys";@themushroompirates models;models_directional_billboard;★★☆☆;5.6-dev;5.6;2025;2025;"Robin";@RobinsAviary +models;models_animation_bone_blending;★★★★;5.5;5.5;2026;2026;"dmitrii-brand";@dmitrii-brand shaders;shaders_ascii_rendering;★★☆☆;5.5;5.6;2025;2025;"Maicon Santana";@maiconpintoabreu shaders;shaders_basic_lighting;★★★★;3.0;4.2;2019;2025;"Chris Camacho";@chriscamacho shaders;shaders_model_shader;★★☆☆;1.3;3.7;2014;2025;"Ramon Santamaria";@raysan5 diff --git a/examples/shapes/shapes_easings_testbed.c b/examples/shapes/shapes_easings_testbed.c index 3f4f2528a..ce9b7d485 100644 --- a/examples/shapes/shapes_easings_testbed.c +++ b/examples/shapes/shapes_easings_testbed.c @@ -110,8 +110,6 @@ static const EasingFuncs easings[] = { [EASING_NONE] = { .name = "None", .func = NoEase }, }; - - //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ diff --git a/projects/VS2022/examples/models_animation_bone_blending.vcxproj b/projects/VS2022/examples/models_animation_bone_blending.vcxproj new file mode 100644 index 000000000..f03989949 --- /dev/null +++ b/projects/VS2022/examples/models_animation_bone_blending.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {6B1A933E-71B8-4C1F-9E79-02D98830E671} + Win32Proj + models_animation_bone_blending + 10.0 + models_animation_bone_blending + + + + 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/raylib.sln b/projects/VS2022/raylib.sln index 6575b6307..fdbf427ce 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -429,6 +429,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_easings_testbed", "e EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_rlgl_compute", "examples\shaders_rlgl_compute.vcxproj", "{6B1A933E-71B8-4C1F-9E79-02D98830E671}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_bone_blending", "examples\models_animation_bone_blending.vcxproj", "{6B1A933E-71B8-4C1F-9E79-02D98830E671}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|ARM64 = Debug.DLL|ARM64 @@ -5365,6 +5367,30 @@ Global {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.Build.0 = Release|x64 {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.ActiveCfg = Release|Win32 {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.Build.0 = Release|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.Build.0 = Debug|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.ActiveCfg = Debug|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.Build.0 = Debug|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.ActiveCfg = Debug|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.Build.0 = Debug|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.ActiveCfg = Release|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.Build.0 = Release|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.ActiveCfg = Release|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.Build.0 = Release|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.ActiveCfg = Release|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -5581,6 +5607,7 @@ Global {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} {4250CE87-9AA0-43BB-AB47-0636548922B5} = {278D8859-20B1-428F-8448-064F46E1F021} {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} diff --git a/tools/rexm/reports/examples_issues.md b/tools/rexm/reports/examples_issues.md index a648295cc..3fcc136e5 100644 --- a/tools/rexm/reports/examples_issues.md +++ b/tools/rexm/reports/examples_issues.md @@ -20,3 +20,4 @@ Example elements validated: ``` | **EXAMPLE NAME** | [C] | [CAT]| [INFO]|[PNG]|[WPNG]| [RES]| [MK] |[MKWEB]| [VCX]| [SOL]|[RDME]|[JS] | [WOUT]|[WMETA]| |:---------------------------------|:---:|:----:|:-----:|:---:|:----:|:----:|:----:|:-----:|:----:|:----:|:----:|:---:|:-----:|:-----:| +| models_animation_bone_blending | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | diff --git a/tools/rexm/reports/examples_validation.md b/tools/rexm/reports/examples_validation.md index 633fcae2f..b2c05ebba 100644 --- a/tools/rexm/reports/examples_validation.md +++ b/tools/rexm/reports/examples_validation.md @@ -181,6 +181,7 @@ Example elements validated: | models_rotating_cube | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_decals | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_directional_billboard | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_animation_bone_blending | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_ascii_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_basic_lighting | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_model_shader | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | From 1f4e1bc477feccf3ed3a355a578f7c14e2fd171b Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Feb 2026 17:18:50 +0100 Subject: [PATCH 038/319] REXM: Update examples collection --- examples/Makefile | 1 + examples/Makefile.Web | 7 ++++++ examples/README.md | 5 ++-- examples/examples_list.txt | 1 + examples/models/models_animation_blending.c | 6 ++--- projects/VS2022/raylib.sln | 27 +++++++++++++++++++++ tools/rexm/reports/examples_validation.md | 1 + 7 files changed, 43 insertions(+), 5 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 103cdc0fa..a1ddb12a0 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -656,6 +656,7 @@ TEXT = \ text/text_writing_anim MODELS = \ + models/models_animation_blending \ models/models_animation_bone_blending \ models/models_animation_gpu_skinning \ models/models_animation_playing \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 663751ad1..c308fa8f7 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -641,6 +641,7 @@ TEXT = \ text/text_writing_anim MODELS = \ + models/models_animation_blending \ models/models_animation_bone_blending \ models/models_animation_gpu_skinning \ models/models_animation_playing \ @@ -1194,6 +1195,12 @@ 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_blending: models/models_animation_blending.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file models/resources/models/gltf/robot.glb@resources/models/gltf/robot.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_animation_bone_blending: models/models_animation_bone_blending.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file models/resources/models/gltf/greenman.glb@resources/models/gltf/greenman.glb \ diff --git a/examples/README.md b/examples/README.md index 2b7474cff..26ba4d3d6 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,7 +17,7 @@ You may find it easier to use than other toolchains, especially when it comes to - `zig build [module]` to compile all examples for a module (e.g. `zig build core`) - `zig build [example]` to compile _and run_ a particular example (e.g. `zig build core_basic_window`) -## EXAMPLES COLLECTION [TOTAL: 205] +## EXAMPLES COLLECTION [TOTAL: 206] ### category: core [48] @@ -181,7 +181,7 @@ Examples using raylib text functionality, including sprite fonts loading/generat | [text_words_alignment](text/text_words_alignment.c) | text_words_alignment | ⭐☆☆☆ | 5.6-dev | 5.6-dev | [JP Mortiboys](https://github.com/themushroompirates) | | [text_strings_management](text/text_strings_management.c) | text_strings_management | ⭐⭐⭐☆ | 5.6-dev | 5.6-dev | [David Buzatto](https://github.com/davidbuzatto) | -### category: models [28] +### category: models [29] Examples using raylib models functionality, including models loading/generation and drawing, provided by raylib [models](../src/rmodels.c) module. @@ -215,6 +215,7 @@ Examples using raylib models functionality, including models loading/generation | [models_decals](models/models_decals.c) | models_decals | ⭐⭐⭐⭐️ | 5.6-dev | 5.6-dev | [JP Mortiboys](https://github.com/themushroompirates) | | [models_directional_billboard](models/models_directional_billboard.c) | models_directional_billboard | ⭐⭐☆☆ | 5.6-dev | 5.6 | [Robin](https://github.com/RobinsAviary) | | [models_animation_bone_blending](models/models_animation_bone_blending.c) | models_animation_bone_blending | ⭐⭐⭐⭐️ | 5.5 | 5.5 | [dmitrii-brand](https://github.com/dmitrii-brand) | +| [models_animation_blending](models/models_animation_blending.c) | models_animation_blending | ☆☆☆☆ | 5.5 | 5.6-dev | [Kirandeep](https://github.com/Kirandeep-Singh-Khehra) | ### category: shaders [34] diff --git a/examples/examples_list.txt b/examples/examples_list.txt index be0def2d8..ededf8e5a 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -171,6 +171,7 @@ models;models_rotating_cube;★☆☆☆;5.6-dev;5.6-dev;2025;2025;"Jopestpe";@j models;models_decals;★★★★;5.6-dev;5.6-dev;2025;2025;"JP Mortiboys";@themushroompirates models;models_directional_billboard;★★☆☆;5.6-dev;5.6;2025;2025;"Robin";@RobinsAviary models;models_animation_bone_blending;★★★★;5.5;5.5;2026;2026;"dmitrii-brand";@dmitrii-brand +models;models_animation_blending;☆☆☆☆;5.5;5.6-dev;2024;2024;"Kirandeep";@Kirandeep-Singh-Khehra shaders;shaders_ascii_rendering;★★☆☆;5.5;5.6;2025;2025;"Maicon Santana";@maiconpintoabreu shaders;shaders_basic_lighting;★★★★;3.0;4.2;2019;2025;"Chris Camacho";@chriscamacho shaders;shaders_model_shader;★★☆☆;1.3;3.7;2014;2025;"Ramon Santamaria";@raysan5 diff --git a/examples/models/models_animation_blending.c b/examples/models/models_animation_blending.c index 225c1bfbb..b33dbe85d 100644 --- a/examples/models/models_animation_blending.c +++ b/examples/models/models_animation_blending.c @@ -1,8 +1,8 @@ /******************************************************************************************* * -* raylib [core] example - Model animation blending +* raylib [models] example - animation blending * -* Example originally created with raylib 5.5 +* Example originally created with raylib 5.5, last time updated with raylib 5.6-dev * * Example contributed by Kirandeep (@Kirandeep-Singh-Khehra) * @@ -37,7 +37,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [models] example - Model Animation Blending"); + InitWindow(screenWidth, screenHeight, "raylib [models] example - animation blending"); // Define the camera to look into our 3d world Camera camera = { 0 }; diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index fdbf427ce..6cd7e8efc 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -431,6 +431,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_rlgl_compute", "exa EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_bone_blending", "examples\models_animation_bone_blending.vcxproj", "{6B1A933E-71B8-4C1F-9E79-02D98830E671}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_blending", "examples\models_animation_blending.vcxproj", "{6B1A933E-71B8-4C1F-9E79-02D98830E671}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|ARM64 = Debug.DLL|ARM64 @@ -5391,6 +5393,30 @@ Global {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.Build.0 = Release|x64 {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.ActiveCfg = Release|Win32 {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.Build.0 = Release|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.Build.0 = Debug|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.ActiveCfg = Debug|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.Build.0 = Debug|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.ActiveCfg = Debug|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.Build.0 = Debug|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.ActiveCfg = Release|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.Build.0 = Release|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.ActiveCfg = Release|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.Build.0 = Release|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.ActiveCfg = Release|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -5608,6 +5634,7 @@ Global {4250CE87-9AA0-43BB-AB47-0636548922B5} = {278D8859-20B1-428F-8448-064F46E1F021} {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} diff --git a/tools/rexm/reports/examples_validation.md b/tools/rexm/reports/examples_validation.md index b2c05ebba..391c007cb 100644 --- a/tools/rexm/reports/examples_validation.md +++ b/tools/rexm/reports/examples_validation.md @@ -182,6 +182,7 @@ Example elements validated: | models_decals | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_directional_billboard | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_animation_bone_blending | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_animation_blending | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_ascii_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_basic_lighting | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_model_shader | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | From 1a5e22808c35350ec98fec3664d73569916af4af Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Feb 2026 17:19:44 +0100 Subject: [PATCH 039/319] REXM: Update examples collection --- examples/Makefile | 1 + examples/Makefile.Web | 4 + examples/README.md | 5 +- examples/examples_list.txt | 1 + .../VS2022/examples/core_window_web.vcxproj | 569 ++++++++++++++++++ projects/VS2022/raylib.sln | 27 + tools/rexm/reports/examples_validation.md | 1 + 7 files changed, 606 insertions(+), 2 deletions(-) create mode 100644 projects/VS2022/examples/core_window_web.vcxproj diff --git a/examples/Makefile b/examples/Makefile index a1ddb12a0..40906ebfc 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -561,6 +561,7 @@ CORE = \ core/core_window_flags \ core/core_window_letterbox \ core/core_window_should_close \ + core/core_window_web \ core/core_world_screen SHAPES = \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index c308fa8f7..b91f59267 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -546,6 +546,7 @@ CORE = \ core/core_window_flags \ core/core_window_letterbox \ core/core_window_should_close \ + core/core_window_web \ core/core_world_screen SHAPES = \ @@ -875,6 +876,9 @@ core/core_window_letterbox: core/core_window_letterbox.c core/core_window_should_close: core/core_window_should_close.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +core/core_window_web: core/core_window_web.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + core/core_world_screen: core/core_world_screen.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) diff --git a/examples/README.md b/examples/README.md index 26ba4d3d6..2b8b9dd98 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,9 +17,9 @@ You may find it easier to use than other toolchains, especially when it comes to - `zig build [module]` to compile all examples for a module (e.g. `zig build core`) - `zig build [example]` to compile _and run_ a particular example (e.g. `zig build core_basic_window`) -## EXAMPLES COLLECTION [TOTAL: 206] +## EXAMPLES COLLECTION [TOTAL: 207] -### category: core [48] +### category: core [49] Examples using raylib [core](../src/rcore.c) module platform functionality: window creation, inputs, drawing modes and system functionality. @@ -73,6 +73,7 @@ Examples using raylib [core](../src/rcore.c) module platform functionality: wind | [core_text_file_loading](core/core_text_file_loading.c) | core_text_file_loading | ⭐☆☆☆ | 5.5 | 5.6 | [Aanjishnu Bhattacharyya](https://github.com/NimComPoo-04) | | [core_compute_hash](core/core_compute_hash.c) | core_compute_hash | ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Ramon Santamaria](https://github.com/raysan5) | | [core_keyboard_testbed](core/core_keyboard_testbed.c) | core_keyboard_testbed | ⭐⭐☆☆ | 5.6 | 5.6 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_window_web](core/core_window_web.c) | core_window_web | ⭐☆☆☆ | 1.3 | 5.5 | [Ramon Santamaria](https://github.com/raysan5) | ### category: shapes [40] diff --git a/examples/examples_list.txt b/examples/examples_list.txt index ededf8e5a..85b997ec4 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -57,6 +57,7 @@ core;core_clipboard_text;★★☆☆;5.6-dev;5.6-dev;2025;2025;"Ananth S";@Anan core;core_text_file_loading;★☆☆☆;5.5;5.6;0;0;"Aanjishnu Bhattacharyya";@NimComPoo-04 core;core_compute_hash;★★☆☆;5.6-dev;5.6-dev;2025;2025;"Ramon Santamaria";@raysan5 core;core_keyboard_testbed;★★☆☆;5.6;5.6;2026;2026;"Ramon Santamaria";@raysan5 +core;core_window_web;★☆☆☆;1.3;5.5;2015;2025;"Ramon Santamaria";@raysan5 shapes;shapes_basic_shapes;★☆☆☆;1.0;4.2;2014;2025;"Ramon Santamaria";@raysan5 shapes;shapes_bouncing_ball;★☆☆☆;2.5;2.5;2013;2025;"Ramon Santamaria";@raysan5 shapes;shapes_bullet_hell;★☆☆☆;5.6;5.6;2025;2025;"Zero";@zerohorsepower diff --git a/projects/VS2022/examples/core_window_web.vcxproj b/projects/VS2022/examples/core_window_web.vcxproj new file mode 100644 index 000000000..4d7a0e741 --- /dev/null +++ b/projects/VS2022/examples/core_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 + + + + {6B1A933E-71B8-4C1F-9E79-02D98830E671} + Win32Proj + core_window_web + 10.0 + core_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/raylib.sln b/projects/VS2022/raylib.sln index 6cd7e8efc..7f8fbbbf0 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -433,6 +433,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_bone_blend EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_blending", "examples\models_animation_blending.vcxproj", "{6B1A933E-71B8-4C1F-9E79-02D98830E671}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_window_web", "examples\core_window_web.vcxproj", "{6B1A933E-71B8-4C1F-9E79-02D98830E671}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|ARM64 = Debug.DLL|ARM64 @@ -5417,6 +5419,30 @@ Global {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.Build.0 = Release|x64 {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.ActiveCfg = Release|Win32 {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.Build.0 = Release|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.Build.0 = Debug|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.ActiveCfg = Debug|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.Build.0 = Debug|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.ActiveCfg = Debug|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.Build.0 = Debug|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.ActiveCfg = Release|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.Build.0 = Release|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.ActiveCfg = Release|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.Build.0 = Release|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.ActiveCfg = Release|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -5635,6 +5661,7 @@ Global {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} diff --git a/tools/rexm/reports/examples_validation.md b/tools/rexm/reports/examples_validation.md index 391c007cb..9c0c02956 100644 --- a/tools/rexm/reports/examples_validation.md +++ b/tools/rexm/reports/examples_validation.md @@ -68,6 +68,7 @@ Example elements validated: | core_text_file_loading | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_compute_hash | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_keyboard_testbed | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_window_web | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_basic_shapes | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_bouncing_ball | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_bullet_hell | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | From 4a89da3300c10965a92884aaff98c89f807ed0fc Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Feb 2026 17:22:45 +0100 Subject: [PATCH 040/319] Update VS2022 examples solution --- .../VS2022/examples/core_window_web.vcxproj | 2 +- .../models_animation_blending.vcxproj | 4 +- .../models_animation_bone_blending.vcxproj | 2 +- .../examples/shaders_rlgl_compute.vcxproj | 2 +- projects/VS2022/raylib.sln | 210 +++++++++--------- 5 files changed, 110 insertions(+), 110 deletions(-) diff --git a/projects/VS2022/examples/core_window_web.vcxproj b/projects/VS2022/examples/core_window_web.vcxproj index 4d7a0e741..d26befac0 100644 --- a/projects/VS2022/examples/core_window_web.vcxproj +++ b/projects/VS2022/examples/core_window_web.vcxproj @@ -51,7 +51,7 @@ - {6B1A933E-71B8-4C1F-9E79-02D98830E671} + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3} Win32Proj core_window_web 10.0 diff --git a/projects/VS2022/examples/models_animation_blending.vcxproj b/projects/VS2022/examples/models_animation_blending.vcxproj index bf8a8b27a..3eca7a6f2 100644 --- a/projects/VS2022/examples/models_animation_blending.vcxproj +++ b/projects/VS2022/examples/models_animation_blending.vcxproj @@ -35,7 +35,7 @@ - {AFDDE100-2D36-4749-817D-12E54C56312F} + {BB9C957D-34F1-46AE-B64A-9E0499C1746D} Win32Proj models_animation_blending 10.0 @@ -384,4 +384,4 @@ - + \ No newline at end of file diff --git a/projects/VS2022/examples/models_animation_bone_blending.vcxproj b/projects/VS2022/examples/models_animation_bone_blending.vcxproj index f03989949..475ad47f0 100644 --- a/projects/VS2022/examples/models_animation_bone_blending.vcxproj +++ b/projects/VS2022/examples/models_animation_bone_blending.vcxproj @@ -51,7 +51,7 @@ - {6B1A933E-71B8-4C1F-9E79-02D98830E671} + {AC751FE1-C986-4B6A-92A8-28ED89DEE671} Win32Proj models_animation_bone_blending 10.0 diff --git a/projects/VS2022/examples/shaders_rlgl_compute.vcxproj b/projects/VS2022/examples/shaders_rlgl_compute.vcxproj index 583898f19..d10553409 100644 --- a/projects/VS2022/examples/shaders_rlgl_compute.vcxproj +++ b/projects/VS2022/examples/shaders_rlgl_compute.vcxproj @@ -51,7 +51,7 @@ - {6B1A933E-71B8-4C1F-9E79-02D98830E671} + {AEA9D0D4-B810-4624-BC75-10A291584ED6} Win32Proj shaders_rlgl_compute 10.0 diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 7f8fbbbf0..873db235b 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -427,13 +427,13 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_framebuffer_render EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_easings_testbed", "examples\shapes_easings_testbed.vcxproj", "{4250CE87-9AA0-43BB-AB47-0636548922B5}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_rlgl_compute", "examples\shaders_rlgl_compute.vcxproj", "{6B1A933E-71B8-4C1F-9E79-02D98830E671}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_rlgl_compute", "examples\shaders_rlgl_compute.vcxproj", "{AEA9D0D4-B810-4624-BC75-10A291584ED6}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_bone_blending", "examples\models_animation_bone_blending.vcxproj", "{6B1A933E-71B8-4C1F-9E79-02D98830E671}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_bone_blending", "examples\models_animation_bone_blending.vcxproj", "{AC751FE1-C986-4B6A-92A8-28ED89DEE671}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_blending", "examples\models_animation_blending.vcxproj", "{6B1A933E-71B8-4C1F-9E79-02D98830E671}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_blending", "examples\models_animation_blending.vcxproj", "{BB9C957D-34F1-46AE-B64A-9E0499C1746D}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_window_web", "examples\core_window_web.vcxproj", "{6B1A933E-71B8-4C1F-9E79-02D98830E671}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_window_web", "examples\core_window_web.vcxproj", "{4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -5347,102 +5347,102 @@ Global {4250CE87-9AA0-43BB-AB47-0636548922B5}.Release|x64.Build.0 = Release|x64 {4250CE87-9AA0-43BB-AB47-0636548922B5}.Release|x86.ActiveCfg = Release|Win32 {4250CE87-9AA0-43BB-AB47-0636548922B5}.Release|x86.Build.0 = Release|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.Build.0 = Debug|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.ActiveCfg = Debug|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.Build.0 = Debug|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.ActiveCfg = Debug|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.Build.0 = Debug|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.ActiveCfg = Release|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.Build.0 = Release|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.ActiveCfg = Release|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.Build.0 = Release|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.ActiveCfg = Release|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.Build.0 = Release|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.Build.0 = Debug|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.ActiveCfg = Debug|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.Build.0 = Debug|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.ActiveCfg = Debug|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.Build.0 = Debug|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.ActiveCfg = Release|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.Build.0 = Release|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.ActiveCfg = Release|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.Build.0 = Release|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.ActiveCfg = Release|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.Build.0 = Release|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.Build.0 = Debug|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.ActiveCfg = Debug|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.Build.0 = Debug|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.ActiveCfg = Debug|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.Build.0 = Debug|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.ActiveCfg = Release|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.Build.0 = Release|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.ActiveCfg = Release|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.Build.0 = Release|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.ActiveCfg = Release|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.Build.0 = Release|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.Build.0 = Debug|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.ActiveCfg = Debug|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.Build.0 = Debug|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.ActiveCfg = Debug|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.Build.0 = Debug|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.ActiveCfg = Release|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.Build.0 = Release|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.ActiveCfg = Release|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.Build.0 = Release|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.ActiveCfg = Release|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.Build.0 = Release|Win32 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Debug|ARM64.Build.0 = Debug|ARM64 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Debug|x64.ActiveCfg = Debug|x64 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Debug|x64.Build.0 = Debug|x64 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Debug|x86.ActiveCfg = Debug|Win32 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Debug|x86.Build.0 = Debug|Win32 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Release|ARM64.ActiveCfg = Release|ARM64 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Release|ARM64.Build.0 = Release|ARM64 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Release|x64.ActiveCfg = Release|x64 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Release|x64.Build.0 = Release|x64 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Release|x86.ActiveCfg = Release|Win32 + {AEA9D0D4-B810-4624-BC75-10A291584ED6}.Release|x86.Build.0 = Release|Win32 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Debug|ARM64.Build.0 = Debug|ARM64 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Debug|x64.ActiveCfg = Debug|x64 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Debug|x64.Build.0 = Debug|x64 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Debug|x86.ActiveCfg = Debug|Win32 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Debug|x86.Build.0 = Debug|Win32 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Release|ARM64.ActiveCfg = Release|ARM64 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Release|ARM64.Build.0 = Release|ARM64 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Release|x64.ActiveCfg = Release|x64 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Release|x64.Build.0 = Release|x64 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Release|x86.ActiveCfg = Release|Win32 + {AC751FE1-C986-4B6A-92A8-28ED89DEE671}.Release|x86.Build.0 = Release|Win32 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|x64 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Debug.DLL|ARM64.Build.0 = Debug.DLL|x64 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Debug|ARM64.ActiveCfg = Debug|x64 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Debug|ARM64.Build.0 = Debug|x64 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Debug|x64.ActiveCfg = Debug|x64 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Debug|x64.Build.0 = Debug|x64 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Debug|x86.ActiveCfg = Debug|Win32 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Debug|x86.Build.0 = Debug|Win32 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Release.DLL|ARM64.ActiveCfg = Release.DLL|x64 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Release.DLL|ARM64.Build.0 = Release.DLL|x64 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Release|ARM64.ActiveCfg = Release|x64 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Release|ARM64.Build.0 = Release|x64 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Release|x64.ActiveCfg = Release|x64 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Release|x64.Build.0 = Release|x64 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Release|x86.ActiveCfg = Release|Win32 + {BB9C957D-34F1-46AE-B64A-9E0499C1746D}.Release|x86.Build.0 = Release|Win32 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Debug|ARM64.Build.0 = Debug|ARM64 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Debug|x64.ActiveCfg = Debug|x64 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Debug|x64.Build.0 = Debug|x64 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Debug|x86.ActiveCfg = Debug|Win32 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Debug|x86.Build.0 = Debug|Win32 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Release|ARM64.ActiveCfg = Release|ARM64 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Release|ARM64.Build.0 = Release|ARM64 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Release|x64.ActiveCfg = Release|x64 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Release|x64.Build.0 = Release|x64 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Release|x86.ActiveCfg = Release|Win32 + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -5605,7 +5605,7 @@ Global {C54703BF-D68A-480D-BE27-49B62E45D582} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {9CD8BCAD-F212-4BCC-BA98-899743CE3279} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} {0981CA28-E4A5-4DF1-987F-A41D09131EFC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {278D8859-20B1-428F-8448-064F46E1F021} + {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {6777EC3C-077C-42FC-B4AD-B799CE55CCE4} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} {A61DAD9C-271C-4E95-81AA-DB4CD58564D4} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} @@ -5658,10 +5658,10 @@ Global {D35D2FDA-B53F-4F70-81CA-24D95812B89C} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} {4250CE87-9AA0-43BB-AB47-0636548922B5} = {278D8859-20B1-428F-8448-064F46E1F021} - {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} - {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {AEA9D0D4-B810-4624-BC75-10A291584ED6} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {AC751FE1-C986-4B6A-92A8-28ED89DEE671} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {BB9C957D-34F1-46AE-B64A-9E0499C1746D} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} From 90dd9aef727ab1903a80e6c9783e0da2376bad0c Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Feb 2026 17:40:22 +0100 Subject: [PATCH 041/319] REVIEWED: `GenImageFontAtlas()`, no need for the conservative approach flag --- src/rtext.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index 0fd2d8e6b..e0aa4fddd 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -818,25 +818,11 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp totalWidth += glyphs[i].image.width + 2*padding; } -//#define SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE -#if defined(SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE) - int rowCount = 0; - int imageSize = 64; // Define minimum starting value to avoid unnecessary calculation steps for very small images - - // NOTE: maxGlyphWidth is maximum possible space left at the end of row - while (totalWidth > (imageSize - maxGlyphWidth)*rowCount) - { - imageSize *= 2; // Double the size of image (to keep POT) - rowCount = imageSize/(fontSize + 2*padding); // Calculate new row count for the new image size - } - - atlas.width = imageSize; // Atlas bitmap width - atlas.height = imageSize; // Atlas bitmap height -#else int paddedFontSize = fontSize + 2*padding; - // No need for a so-conservative atlas generation - // NOTE: Multiplying total expected are by 1.2f scale factor + // Estimate image atlas size from available data + // NOTE: Multiplying total expected area by 1.2f scale factor but in case + // some glyphs do not fit, the atlas height is scaled x2 to fit them float totalArea = totalWidth*paddedFontSize*1.2f; float imageMinSize = sqrtf(totalArea); int imageSize = (int)powf(2, ceilf(logf(imageMinSize)/logf(2))); @@ -851,7 +837,6 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp atlas.width = imageSize; // Atlas bitmap width atlas.height = imageSize; // Atlas bitmap height } -#endif int atlasDataSize = atlas.width*atlas.height; // Save total size for bounds checking atlas.data = (unsigned char *)RL_CALLOC(atlasDataSize, 1); // Create a bitmap to store characters (8 bpp) From 4a3c49cdcbf09857a675179e63f27e5a6ffe9b22 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Feb 2026 17:55:13 +0100 Subject: [PATCH 042/319] REVIEWED: `rlLoadTextureDepth()`, address inconsistencies with WebGL 2.0 for sized depth formats #5500 --- src/rlgl.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/rlgl.h b/src/rlgl.h index d05bc42cb..3168b6a63 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3432,6 +3432,13 @@ unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer) else glInternalFormat = GL_DEPTH_COMPONENT16; } #endif +#if defined(GRAPHICS_API_OPENGL_ES3) + // NOTE: This sized internal format should also work for WebGL 2.0 + // WARNING: Specification only allows GL_DEPTH_COMPONENT32F for GL_FLOAT type + // REF: https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glTexImage2D.xhtml + if (RLGL.ExtSupported.maxDepthBits == 24) glInternalFormat = GL_DEPTH_COMPONENT24; + else glInternalFormat = GL_DEPTH_COMPONENT16; +#endif if (!useRenderBuffer && RLGL.ExtSupported.texDepth) { From ce617cd8146b85adce4c0527e150cc151457be99 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 20 Feb 2026 11:46:46 +0100 Subject: [PATCH 043/319] Update rlgl.h --- src/rlgl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rlgl.h b/src/rlgl.h index 3168b6a63..0cde3e6eb 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3422,7 +3422,7 @@ unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer) // Possible formats: GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32 and GL_DEPTH_COMPONENT32F unsigned int glInternalFormat = GL_DEPTH_COMPONENT; -#if (defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_ES3)) +#if defined(GRAPHICS_API_OPENGL_ES2) // WARNING: WebGL platform requires unsized internal format definition (GL_DEPTH_COMPONENT) // while other platforms using OpenGL ES 2.0 require/support sized internal formats depending on the GPU capabilities if (!RLGL.ExtSupported.texDepthWebGL || useRenderBuffer) From 0aacd330d4f8dc3a86805362ac10b426e4c3ca8e Mon Sep 17 00:00:00 2001 From: David Reid Date: Fri, 20 Feb 2026 22:46:41 +1000 Subject: [PATCH 044/319] [raudio] Remove usage of `ma_data_converter_get_required_input_frame_count()` (#5568) * Audio: Remove use of ma_data_converter_get_required_input_frame_count(). This function is being removed from miniaudio. To make this work with the current architecture of raylib it requires the use of a cache. This commit implements a generic solution that works across all AudioBuffer types (static, streams and callback based), but the static case could be optimized to avoid the cache by incorporating the functionality of ReadAudioBufferFramesInInternalFormat() into ReadAudioBufferFramesInMixingFormat(). It would be unpractical to avoid the cache with streams and callback-based AudioBuffers however so this commit sticks with a generic solution. * Audio: Correct usage of miniaudio's dynamic rate adjustment. This affects pitch shifting. The output rate is being modified with ma_data_converter_set_rate(), but then that value is being used in the computation of the output rate the next time SetAudioBufferPitch() which results in a cascade. The correct way to do this is to use an anchored output rate as the basis for the calculation after pitch shifting. In this case, it's the device's sample rate that acts as the anchor. * Audio: Optimize memory usage for data conversion. This reduces the per-AudioBuffer conversion cache from 256 PCM frames down to 8. --- src/raudio.c | 96 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 20 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 75547d285..8322f1baf 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -295,6 +295,10 @@ typedef struct tagBITMAPINFOHEADER { #define MAX_AUDIO_BUFFER_POOL_CHANNELS 16 // Audio pool channels #endif +#ifndef AUDIO_BUFFER_RESIDUAL_CAPACITY + #define AUDIO_BUFFER_RESIDUAL_CAPACITY 8 // In PCM frames. For resampling and pitch shifting. +#endif + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- @@ -337,6 +341,8 @@ typedef enum { // Audio buffer struct struct rAudioBuffer { ma_data_converter converter; // Audio data converter + unsigned char* converterResidual; // Cached residual input frames for use by the converter + unsigned int converterResidualCount; // The number of valid frames sitting in converterResidual AudioCallback callback; // Audio buffer callback for buffer filling on audio threads rAudioProcessor *processor; // Audio processor @@ -586,6 +592,15 @@ AudioBuffer *LoadAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sam return NULL; } + // A cache for use by the converter is necessary when resampling because + // when generating output frames a different number of input frames will + // be consumed. Any residual input frames need to be kept track of to + // ensure there are no discontinuities. Since raylib supports pitch + // shifting, which is done through resampling, a cache will always be + // required. This will be kept relatively small to avoid too much wastage. + audioBuffer->converterResidualCount = 0; + audioBuffer->converterResidual = (unsigned char*)RL_CALLOC(AUDIO_BUFFER_RESIDUAL_CAPACITY*ma_get_bytes_per_frame(format, channels), 1); + // Init audio buffer values audioBuffer->volume = 1.0f; audioBuffer->pitch = 1.0f; @@ -621,6 +636,7 @@ void UnloadAudioBuffer(AudioBuffer *buffer) { UntrackAudioBuffer(buffer); ma_data_converter_uninit(&buffer->converter, NULL); + RL_FREE(buffer->converterResidual); RL_FREE(buffer->data); RL_FREE(buffer); } @@ -705,7 +721,7 @@ void SetAudioBufferPitch(AudioBuffer *buffer, float pitch) // Note that this changes the duration of the sound: // - higher pitches will make the sound faster // - lower pitches make it slower - ma_uint32 outputSampleRate = (ma_uint32)((float)buffer->converter.sampleRateOut/pitch); + ma_uint32 outputSampleRate = (ma_uint32)((float)AUDIO.System.device.sampleRate/pitch); ma_data_converter_set_rate(&buffer->converter, buffer->converter.sampleRateIn, outputSampleRate); buffer->pitch = pitch; @@ -2456,38 +2472,78 @@ static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, f // NOTE: Continuously converting data from the AudioBuffer's internal format to the mixing format, // which should be defined by the output format of the data converter. // This is done until frameCount frames have been output. - // The important detail to remember is that more data than required should neeveer be read, - // for the specified number of output frames. - // This can be achieved with ma_data_converter_get_required_input_frame_count() + ma_uint32 bpf = ma_get_bytes_per_frame(audioBuffer->converter.formatIn, audioBuffer->converter.channelsIn); ma_uint8 inputBuffer[4096] = { 0 }; - ma_uint32 inputBufferFrameCap = sizeof(inputBuffer)/ma_get_bytes_per_frame(audioBuffer->converter.formatIn, audioBuffer->converter.channelsIn); - + ma_uint32 inputBufferFrameCap = sizeof(inputBuffer)/bpf; + ma_uint32 totalOutputFramesProcessed = 0; while (totalOutputFramesProcessed < frameCount) { + float *runningFramesOut = framesOut + (totalOutputFramesProcessed*audioBuffer->converter.channelsOut); ma_uint64 outputFramesToProcessThisIteration = frameCount - totalOutputFramesProcessed; ma_uint64 inputFramesToProcessThisIteration = 0; - - (void)ma_data_converter_get_required_input_frame_count(&audioBuffer->converter, outputFramesToProcessThisIteration, &inputFramesToProcessThisIteration); - if (inputFramesToProcessThisIteration > inputBufferFrameCap) + + // Process any residual input frames from the previous read first. + if (audioBuffer->converterResidualCount > 0) { - inputFramesToProcessThisIteration = inputBufferFrameCap; + ma_uint64 inputFramesProcessedThisIteration = audioBuffer->converterResidualCount; + ma_uint64 outputFramesProcessedThisIteration = outputFramesToProcessThisIteration; + ma_data_converter_process_pcm_frames(&audioBuffer->converter, audioBuffer->converterResidual, &inputFramesProcessedThisIteration, runningFramesOut, &outputFramesProcessedThisIteration); + + // Make sure the data in the cache is consumed. This can be optimized to use a cursor instead of a memmove(). + memmove(audioBuffer->converterResidual, audioBuffer->converterResidual + inputFramesProcessedThisIteration*bpf, (size_t)(AUDIO_BUFFER_RESIDUAL_CAPACITY - inputFramesProcessedThisIteration) * bpf); + audioBuffer->converterResidualCount -= (ma_uint32)inputFramesProcessedThisIteration; // Safe cast + + totalOutputFramesProcessed += (ma_uint32)outputFramesProcessedThisIteration; // Safe cast } + else + { + // Getting here means there are no residual frames from the previous read. Fresh data can now be + // pulled from the AudioBuffer and processed. + // + // A best guess needs to be used made to determine how many input frames to pull from the + // buffer. There are three possible outcomes: 1) exact; 2) underestimated; 3) overestimated. + // + // When the guess is exactly correct or underestimated there is nothing special to handle - it'll be + // handled naturally by the loop. + // + // When the guess is overestimated, that's when it gets more complicated. In this case, any overflow + // needs to be stored in a buffer for later processing by the next read. + ma_uint32 estimatedInputFrameCount = (ma_uint32)(((float)audioBuffer->converter.resampler.sampleRateIn / audioBuffer->converter.resampler.sampleRateOut) * outputFramesToProcessThisIteration); + if (estimatedInputFrameCount == 0) + { + estimatedInputFrameCount = 1; // Make sure at least one input frame is read. + } - float *runningFramesOut = framesOut + (totalOutputFramesProcessed*audioBuffer->converter.channelsOut); + if (estimatedInputFrameCount > inputBufferFrameCap) + { + estimatedInputFrameCount = inputBufferFrameCap; + } - // At this point we can convert the data to our mixing format - ma_uint64 inputFramesProcessedThisIteration = ReadAudioBufferFramesInInternalFormat(audioBuffer, inputBuffer, (ma_uint32)inputFramesToProcessThisIteration); - ma_uint64 outputFramesProcessedThisIteration = outputFramesToProcessThisIteration; - ma_data_converter_process_pcm_frames(&audioBuffer->converter, inputBuffer, &inputFramesProcessedThisIteration, runningFramesOut, &outputFramesProcessedThisIteration); + estimatedInputFrameCount = ReadAudioBufferFramesInInternalFormat(audioBuffer, inputBuffer, estimatedInputFrameCount); - totalOutputFramesProcessed += (ma_uint32)outputFramesProcessedThisIteration; // Safe cast + ma_uint64 inputFramesProcessedThisIteration = estimatedInputFrameCount; + ma_uint64 outputFramesProcessedThisIteration = outputFramesToProcessThisIteration; + ma_data_converter_process_pcm_frames(&audioBuffer->converter, inputBuffer, &inputFramesProcessedThisIteration, runningFramesOut, &outputFramesProcessedThisIteration); - if (inputFramesProcessedThisIteration < inputFramesToProcessThisIteration) break; // Ran out of input data + if (estimatedInputFrameCount > inputFramesProcessedThisIteration) + { + // Getting here means the estimated input frame count was overestimated. The residual needs + // be stored for later use. + ma_uint64 residualFrameCount = estimatedInputFrameCount - inputFramesProcessedThisIteration; - // This should never be hit, but added here for safety - // Ensures we get out of the loop when no input nor output frames are processed - if ((inputFramesProcessedThisIteration == 0) && (outputFramesProcessedThisIteration == 0)) break; + // A safety check to make sure the capacity of the residual cache is not exceeded. + if (residualFrameCount > AUDIO_BUFFER_RESIDUAL_CAPACITY) + { + residualFrameCount = AUDIO_BUFFER_RESIDUAL_CAPACITY; + } + + memcpy(audioBuffer->converterResidual, inputBuffer + inputFramesProcessedThisIteration*bpf, (size_t)(residualFrameCount * bpf)); + audioBuffer->converterResidualCount = residualFrameCount; + } + + totalOutputFramesProcessed += (ma_uint32)outputFramesProcessedThisIteration; + } } return totalOutputFramesProcessed; From f33823cefea894d633279823059234b82d9ba8ca Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 20 Feb 2026 15:55:38 +0100 Subject: [PATCH 045/319] Update textures_screen_buffer.c --- examples/textures/textures_screen_buffer.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/textures/textures_screen_buffer.c b/examples/textures/textures_screen_buffer.c index 503b8d249..9f40f180b 100644 --- a/examples/textures/textures_screen_buffer.c +++ b/examples/textures/textures_screen_buffer.c @@ -66,11 +66,9 @@ int main(void) // Grow flameRoot for (int x = 2; x < flameWidth; x++) { - unsigned char flame = flameRootBuffer[x]; - if (flame == 255) continue; + int flame = (int)flameRootBuffer[x]; flame += GetRandomValue(0, 2); - if (flame > 255) flame = 255; - flameRootBuffer[x] = flame; + flameRootBuffer[x] = (flameInc > 255)? 255: (unsigned char)flame; } // Transfer flameRoot to indexBuffer From d996bf2bbd48ff21ccfbd68b87b06ca11eed29e0 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 20 Feb 2026 16:06:59 +0100 Subject: [PATCH 046/319] Update textures_screen_buffer.c --- examples/textures/textures_screen_buffer.c | 35 ++++++++++++---------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/examples/textures/textures_screen_buffer.c b/examples/textures/textures_screen_buffer.c index 9f40f180b..9c0f25031 100644 --- a/examples/textures/textures_screen_buffer.c +++ b/examples/textures/textures_screen_buffer.c @@ -52,6 +52,7 @@ int main(void) float hue = t*t; float saturation = t; float value = t; + palette[i] = ColorFromHSV(250.0f + 150.0f*hue, saturation, value); } @@ -68,7 +69,7 @@ int main(void) { int flame = (int)flameRootBuffer[x]; flame += GetRandomValue(0, 2); - flameRootBuffer[x] = (flameInc > 255)? 255: (unsigned char)flame; + flameRootBuffer[x] = (flame > 255)? 255: (unsigned char)flame; } // Transfer flameRoot to indexBuffer @@ -81,8 +82,7 @@ int main(void) // Clear top row, because it can't move any higher for (int x = 0; x < imageWidth; x++) { - if (indexBuffer[x] == 0) continue; - indexBuffer[x] = 0; + if (indexBuffer[x] != 0) indexBuffer[x] = 0; } // Skip top row, it is already cleared @@ -92,18 +92,22 @@ int main(void) { unsigned int i = x + y*imageWidth; unsigned char colorIndex = indexBuffer[i]; - if (colorIndex == 0) continue; - - // Move pixel a row above - indexBuffer[i] = 0; - int moveX = GetRandomValue(0, 2) - 1; - int newX = x + moveX; - if (newX < 0 || newX >= imageWidth) continue; - - unsigned int iabove = i - imageWidth + moveX; - int decay = GetRandomValue(0, 3); - colorIndex -= (decay < colorIndex)? decay : colorIndex; - indexBuffer[iabove] = colorIndex; + + if (colorIndex != 0) + { + // Move pixel a row above + indexBuffer[i] = 0; + int moveX = GetRandomValue(0, 2) - 1; + int newX = x + moveX; + + if ((newX > 0) && (newX < imageWidth)) + { + unsigned int iabove = i - imageWidth + moveX; + int decay = GetRandomValue(0, 3); + colorIndex -= (decay < colorIndex)? decay : colorIndex; + indexBuffer[iabove] = colorIndex; + } + } } } @@ -115,6 +119,7 @@ int main(void) unsigned int i = x + y*imageWidth; unsigned char colorIndex = indexBuffer[i]; Color col = palette[colorIndex]; + ImageDrawPixel(&screenImage, x, y, col); } } From 2454b3ed4b6b49af46f89b3796ca94facd548ff0 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 20 Feb 2026 16:27:08 +0100 Subject: [PATCH 047/319] REVIEWED: `TextReplace()` and `TextLength()`, avoid using `strcpy()` --- src/rtext.c | 58 ++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index e0aa4fddd..840b452d3 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1497,15 +1497,14 @@ void UnloadTextLines(char **lines, int lineCount) } // Get text length in bytes, check for \0 character +// NOTE: Alternative: use strlen(text) unsigned int TextLength(const char *text) { unsigned int length = 0; if (text != NULL) - { - // NOTE: Alternative: use strlen(text) - - while (*text++) length++; + { + while (text[length] != '\0') length++; } return length; @@ -1718,7 +1717,7 @@ char *TextReplace(const char *text, const char *search, const char *replacement) { char *result = NULL; - if ((text != NULL) && (search != NULL)) + if ((text != NULL) && (search != NULL) && (search[0] != '\0')) { if (replacement == NULL) replacement = ""; @@ -1732,8 +1731,6 @@ char *TextReplace(const char *text, const char *search, const char *replacement) textLen = TextLength(text); searchLen = TextLength(search); - if (searchLen == 0) return NULL; // Empty search causes infinite loop during count - replaceLen = TextLength(replacement); // Count the number of replacements needed @@ -1742,34 +1739,37 @@ char *TextReplace(const char *text, const char *search, const char *replacement) // Allocate returning string and point temp to it int tempLen = textLen + (replaceLen - searchLen)*count + 1; - temp = result = (char *)RL_MALLOC(tempLen); + temp = result = (char *)RL_CALLOC(tempLen, sizeof(char)); - if (!result) return NULL; // Memory could not be allocated - - // First time through the loop, all the variable are set correctly from here on, - // - 'temp' points to the end of the result string - // - 'insertPoint' points to the next occurrence of replace in text - // - 'text' points to the remainder of text after "end of replace" - while (count--) + if (result != NULL) // Memory was allocated { - insertPoint = (char *)strstr(text, search); - lastReplacePos = (int)(insertPoint - text); - - memcpy(temp, text, lastReplacePos); - temp += lastReplacePos; - - if (replaceLen > 0) + // First time through the loop, all the variable are set correctly from here on, + // - 'temp' points to the end of the result string + // - 'insertPoint' points to the next occurrence of replace in text + // - 'text' points to the remainder of text after "end of replace" + while (count > 0) { - memcpy(temp, replacement, replaceLen); - temp += replaceLen; + insertPoint = (char *)strstr(text, search); + lastReplacePos = (int)(insertPoint - text); + + memcpy(temp, text, lastReplacePos); + temp += lastReplacePos; + + if (replaceLen > 0) + { + memcpy(temp, replacement, replaceLen); + temp += replaceLen; + } + + text += (lastReplacePos + searchLen); // Move to next "end of replace" + + count--; } - text += lastReplacePos + searchLen; // Move to next "end of replace" + // Copy remaind text part after replacement to result (pointed by moving temp) + // NOTE: Text pointer internal copy has been updated along the process + strncpy(temp, text, TextLength(text)); } - - // Copy remaind text part after replacement to result (pointed by moving temp) - strcpy(temp, text); // OK - //strncpy(temp, text, tempLen - 1); // WRONG } return result; From d03a59ca3e8d59d607b9734dc420cc1c4411ec55 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 20 Feb 2026 16:36:13 +0100 Subject: [PATCH 048/319] Update core_directory_files.c --- examples/core/core_directory_files.c | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/examples/core/core_directory_files.c b/examples/core/core_directory_files.c index f879b933e..b0a204d37 100644 --- a/examples/core/core_directory_files.c +++ b/examples/core/core_directory_files.c @@ -81,27 +81,6 @@ int main(void) GuiListViewEx((Rectangle){ 0, 50, GetScreenWidth(), GetScreenHeight() - 50 }, files.paths, files.count, &listScrollIndex, &listItemActive, &listItemFocused); - /* - for (int i = 0; i < (int)files.count; i++) - { - Color color = Fade(LIGHTGRAY, 0.3f); - - if (!IsPathFile(files.paths[i]) && DirectoryExists(files.paths[i])) - { - if (GuiButton((Rectangle){0.0f, 85.0f + 40.0f*(float)i, screenWidth, 40}, "")) - { - TextCopy(directory, files.paths[i]); - UnloadDirectoryFiles(files); - files = LoadDirectoryFiles(directory); - continue; - } - } - - DrawRectangle(0, 85 + 40*i, screenWidth, 40, color); - DrawText(GetFileName(files.paths[i]), 120, 100 + 40*i, 10, GRAY); - } - */ - EndDrawing(); //---------------------------------------------------------------------------------- } From 19e6352d37ead88a1a22d3a6462f31aad0ce95f8 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 20 Feb 2026 18:16:49 +0100 Subject: [PATCH 049/319] Update shapes_easings_testbed.c --- examples/shapes/shapes_easings_testbed.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shapes/shapes_easings_testbed.c b/examples/shapes/shapes_easings_testbed.c index ce9b7d485..1c9fcf741 100644 --- a/examples/shapes/shapes_easings_testbed.c +++ b/examples/shapes/shapes_easings_testbed.c @@ -17,7 +17,7 @@ #include "raylib.h" -#include "reasings.h" // Required for easing functions +#include "reasings.h" // Required for: easing functions #define FONT_SIZE 20 From d148d9515ba913a16a79c214b762b833e4c2f352 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 20 Feb 2026 17:44:34 +0000 Subject: [PATCH 050/319] Fix text input on SDL3 (#5574) --- src/platforms/rcore_desktop_sdl.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index ca49e6a8e..371d907f7 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -2016,6 +2016,22 @@ int InitPlatform(void) // Init window #if defined(USING_VERSION_SDL3) platform.window = SDL_CreateWindow(CORE.Window.title, CORE.Window.screen.width, CORE.Window.screen.height, flags); + + // NOTE: SDL3 no longer enables TextInput by default, so this is needed to preserve the behaviour and keep GetCharPressed working. + // This code is derived from SDL before the change was made: https://github.com/libsdl-org/SDL/commit/72fc6f86e5d605a3787222bc7dc18c5379047f4a. + const char *enableOSK = SDL_GetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD); + if (enableOSK == NULL) + { + SDL_SetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD, "0"); + } + if (!SDL_StartTextInput(platform.window)) + { + TRACELOG(LOG_WARNING, "SDL: Failed to start text input: %s", SDL_GetError()); + } + if (enableOSK == NULL) + { + SDL_SetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD, NULL); + } #else platform.window = SDL_CreateWindow(CORE.Window.title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, CORE.Window.screen.width, CORE.Window.screen.height, flags); #endif From 0343cb6a3762fba8d3f5870321a8490fb9f39b1b Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 20 Feb 2026 18:47:53 +0100 Subject: [PATCH 051/319] Update rcore_desktop_sdl.c --- src/platforms/rcore_desktop_sdl.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 371d907f7..789457d2f 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -2017,21 +2017,14 @@ int InitPlatform(void) #if defined(USING_VERSION_SDL3) platform.window = SDL_CreateWindow(CORE.Window.title, CORE.Window.screen.width, CORE.Window.screen.height, flags); - // NOTE: SDL3 no longer enables TextInput by default, so this is needed to preserve the behaviour and keep GetCharPressed working. - // This code is derived from SDL before the change was made: https://github.com/libsdl-org/SDL/commit/72fc6f86e5d605a3787222bc7dc18c5379047f4a. + + // NOTE: SDL3 no longer enables text input by default, + // it is needed to be enabled manually to keep GetCharPressed() working + // REF: https://github.com/libsdl-org/SDL/commit/72fc6f86e5d605a3787222bc7dc18c5379047f4a const char *enableOSK = SDL_GetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD); - if (enableOSK == NULL) - { - SDL_SetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD, "0"); - } - if (!SDL_StartTextInput(platform.window)) - { - TRACELOG(LOG_WARNING, "SDL: Failed to start text input: %s", SDL_GetError()); - } - if (enableOSK == NULL) - { - SDL_SetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD, NULL); - } + if (enableOSK == NULL) SDL_SetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD, "0"); + if (!SDL_StartTextInput(platform.window)) TRACELOG(LOG_WARNING, "SDL: Failed to start text input: %s", SDL_GetError()); + if (enableOSK == NULL) SDL_SetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD, NULL); #else platform.window = SDL_CreateWindow(CORE.Window.title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, CORE.Window.screen.width, CORE.Window.screen.height, flags); #endif From 29b9c050c78b14afaddf0087d8f70f3f6d7c80f1 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Fri, 20 Feb 2026 13:18:28 -0600 Subject: [PATCH 052/319] fix example (#5575) --- examples/core/core_directory_files.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/core/core_directory_files.c b/examples/core/core_directory_files.c index b0a204d37..922d47d5b 100644 --- a/examples/core/core_directory_files.c +++ b/examples/core/core_directory_files.c @@ -79,7 +79,7 @@ int main(void) GuiSetStyle(LISTVIEW, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); GuiSetStyle(LISTVIEW, TEXT_PADDING, 40); GuiListViewEx((Rectangle){ 0, 50, GetScreenWidth(), GetScreenHeight() - 50 }, - files.paths, files.count, &listScrollIndex, &listItemActive, &listItemFocused); + (const char**)files.paths, files.count, &listScrollIndex, &listItemActive, &listItemFocused); EndDrawing(); //---------------------------------------------------------------------------------- From 09f22f3c86d7be5e1d4743768a087703d1822d71 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 20 Feb 2026 23:02:43 +0100 Subject: [PATCH 053/319] REVIEWED: Avoid `const char **` usage (aligned with raylib) --- examples/core/core_directory_files.c | 2 +- examples/core/raygui.h | 38 ++++++++++++++-------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/examples/core/core_directory_files.c b/examples/core/core_directory_files.c index 922d47d5b..b0a204d37 100644 --- a/examples/core/core_directory_files.c +++ b/examples/core/core_directory_files.c @@ -79,7 +79,7 @@ int main(void) GuiSetStyle(LISTVIEW, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); GuiSetStyle(LISTVIEW, TEXT_PADDING, 40); GuiListViewEx((Rectangle){ 0, 50, GetScreenWidth(), GetScreenHeight() - 50 }, - (const char**)files.paths, files.count, &listScrollIndex, &listItemActive, &listItemFocused); + files.paths, files.count, &listScrollIndex, &listItemActive, &listItemFocused); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/examples/core/raygui.h b/examples/core/raygui.h index 67c16be45..83102e749 100644 --- a/examples/core/raygui.h +++ b/examples/core/raygui.h @@ -772,7 +772,7 @@ RAYGUIAPI int GuiWindowBox(Rectangle bounds, const char *title); RAYGUIAPI int GuiGroupBox(Rectangle bounds, const char *text); // Group Box control with text name RAYGUIAPI int GuiLine(Rectangle bounds, const char *text); // Line separator control, could contain text RAYGUIAPI int GuiPanel(Rectangle bounds, const char *text); // Panel control, useful to group controls -RAYGUIAPI int GuiTabBar(Rectangle bounds, const char **text, int count, int *active); // Tab Bar control, returns TAB to be closed or -1 +RAYGUIAPI int GuiTabBar(Rectangle bounds, char **text, int count, int *active); // Tab Bar control, returns TAB to be closed or -1 RAYGUIAPI int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view); // Scroll Panel control // Basic controls set @@ -800,7 +800,7 @@ RAYGUIAPI int GuiGrid(Rectangle bounds, const char *text, float spacing, int sub // Advance controls set RAYGUIAPI int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active); // List View control -RAYGUIAPI int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus); // List View with extended parameters +RAYGUIAPI int GuiListViewEx(Rectangle bounds, char **text, int count, int *scrollIndex, int *active, int *focus); // List View with extended parameters RAYGUIAPI int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons); // Message Box control, displays a message RAYGUIAPI int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, bool *secretViewActive); // Text Input Box control, ask for text, supports secret RAYGUIAPI int GuiColorPicker(Rectangle bounds, const char *text, Color *color); // Color Picker control (multiple color controls) @@ -1526,7 +1526,7 @@ static Color GetColor(int hexValue); // Returns a Color struct fr static int ColorToInt(Color color); // Returns hexadecimal value for a Color static bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle static const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' -static const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings +static char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings static int TextToInteger(const char *text); // Get integer value from text static float TextToFloat(const char *text); // Get float value from text @@ -1549,7 +1549,7 @@ static const char *GetTextIcon(const char *text, int *iconId); // Get text icon static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, Color tint); // Gui draw text using default font static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color); // Gui draw rectangle using default raygui style -static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow); // Split controls text into multiple strings +static char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow); // Split controls text into multiple strings static Vector3 ConvertHSVtoRGB(Vector3 hsv); // Convert color data from HSV to RGB static Vector3 ConvertRGBtoHSV(Vector3 rgb); // Convert color data from RGB to HSV @@ -1783,7 +1783,7 @@ int GuiPanel(Rectangle bounds, const char *text) // Tab Bar control // NOTE: Using GuiToggle() for the TABS -int GuiTabBar(Rectangle bounds, const char **text, int count, int *active) +int GuiTabBar(Rectangle bounds, char **text, int count, int *active) { #define RAYGUI_TABBAR_ITEM_WIDTH 148 @@ -2168,7 +2168,7 @@ int GuiToggleGroup(Rectangle bounds, const char *text, int *active) // Get substrings items from text (items pointers) int rows[RAYGUI_TOGGLEGROUP_MAX_ITEMS] = { 0 }; int itemCount = 0; - const char **items = GuiTextSplit(text, ';', &itemCount, rows); + char **items = GuiTextSplit(text, ';', &itemCount, rows); int prevRow = rows[0]; @@ -2212,7 +2212,7 @@ int GuiToggleSlider(Rectangle bounds, const char *text, int *active) // Get substrings items from text (items pointers) int itemCount = 0; - const char **items = NULL; + char **items = NULL; if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL); @@ -2356,7 +2356,7 @@ int GuiComboBox(Rectangle bounds, const char *text, int *active) // Get substrings items from text (items pointers, lengths and count) int itemCount = 0; - const char **items = GuiTextSplit(text, ';', &itemCount, NULL); + char **items = GuiTextSplit(text, ';', &itemCount, NULL); if (*active < 0) *active = 0; else if (*active > (itemCount - 1)) *active = itemCount - 1; @@ -2422,7 +2422,7 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod // Get substrings items from text (items pointers, lengths and count) int itemCount = 0; - const char **items = GuiTextSplit(text, ';', &itemCount, NULL); + char **items = GuiTextSplit(text, ';', &itemCount, NULL); Rectangle boundsOpen = bounds; boundsOpen.height = (itemCount + 1)*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); @@ -3602,7 +3602,7 @@ int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *activ { int result = 0; int itemCount = 0; - const char **items = NULL; + char **items = NULL; if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL); @@ -3612,7 +3612,7 @@ int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *activ } // List View control with extended parameters -int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus) +int GuiListViewEx(Rectangle bounds, char **text, int count, int *scrollIndex, int *active, int *focus) { int result = 0; GuiState state = guiState; @@ -4140,7 +4140,7 @@ int GuiMessageBox(Rectangle bounds, const char *title, const char *message, cons int result = -1; // Returns clicked button from buttons list, 0 refers to closed window button int buttonCount = 0; - const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); + char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); Rectangle buttonBounds = { 0 }; buttonBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING; buttonBounds.y = bounds.y + bounds.height - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT - RAYGUI_MESSAGEBOX_BUTTON_PADDING; @@ -4199,7 +4199,7 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co int result = -1; int buttonCount = 0; - const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); + char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); Rectangle buttonBounds = { 0 }; buttonBounds.x = bounds.x + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; buttonBounds.y = bounds.y + bounds.height - RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; @@ -5119,11 +5119,11 @@ static const char *GetTextIcon(const char *text, int *iconId) // Get text divided into lines (by line-breaks '\n') // WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator! -static const char **GetTextLines(const char *text, int *count) +static char **GetTextLines(const char *text, int *count) { #define RAYGUI_MAX_TEXT_LINES 128 - static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; + static char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings int textLength = (int)strlen(text); @@ -5194,7 +5194,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C // WARNING: GuiTextSplit() function can't be used now because it can have already been used // before the GuiDrawText() call and its buffer is static, it would be overriden :( int lineCount = 0; - const char **lines = GetTextLines(text, &lineCount); + char **lines = GetTextLines(text, &lineCount); // Text style variables //int alignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT); @@ -5444,7 +5444,7 @@ static void GuiTooltip(Rectangle controlRec) // Split controls text into multiple strings // Also check for multiple columns (required by GuiToggleGroup()) -static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow) +static char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow) { // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, @@ -5463,7 +5463,7 @@ static const char **GuiTextSplit(const char *text, char delimiter, int *count, i #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE 1024 #endif - static const char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL }; // String pointers array (points to buffer data) + static char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL }; // String pointers array (points to buffer data) static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 }; // Buffer data (text input copy with '\0' added) memset(buffer, 0, RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE); @@ -5863,7 +5863,7 @@ static void DrawRectangleGradientV(int posX, int posY, int width, int height, Co } // Split string into multiple strings -const char **TextSplit(const char *text, char delimiter, int *count) +char **TextSplit(const char *text, char delimiter, int *count) { // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, From c519e9f566225ee8a0fd4a1cd7c58497c31ab11b Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 20 Feb 2026 23:56:11 +0100 Subject: [PATCH 054/319] REVIEWED: Simplified `char **` approach --- examples/core/raygui.h | 11 +++++---- examples/models/raygui.h | 47 +++++++++++++++++++-------------------- examples/shaders/raygui.h | 47 +++++++++++++++++++-------------------- examples/shapes/raygui.h | 47 +++++++++++++++++++-------------------- 4 files changed, 74 insertions(+), 78 deletions(-) diff --git a/examples/core/raygui.h b/examples/core/raygui.h index 83102e749..42c5ae7bc 100644 --- a/examples/core/raygui.h +++ b/examples/core/raygui.h @@ -5131,12 +5131,11 @@ static char **GetTextLines(const char *text, int *count) lines[0] = text; *count = 1; - for (int i = 0, k = 0; (i < textLength) && (*count < RAYGUI_MAX_TEXT_LINES); i++) + for (int i = 0; (i < textLength) && (*count < RAYGUI_MAX_TEXT_LINES); i++) { - if (text[i] == '\n') + if ((text[i] == '\n') && ((i + 1) < textLength)) { - k++; - lines[k] = &text[i + 1]; // WARNING: next value is valid? + lines[*count] = &text[i + 1]; *count += 1; } } @@ -5463,8 +5462,8 @@ static char **GuiTextSplit(const char *text, char delimiter, int *count, int *te #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE 1024 #endif - static char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL }; // String pointers array (points to buffer data) - static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 }; // Buffer data (text input copy with '\0' added) + static char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL }; // String pointers array (points to buffer data) + static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 }; // Buffer data (text input copy with '\0' added) memset(buffer, 0, RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE); result[0] = buffer; diff --git a/examples/models/raygui.h b/examples/models/raygui.h index 67c16be45..42c5ae7bc 100644 --- a/examples/models/raygui.h +++ b/examples/models/raygui.h @@ -772,7 +772,7 @@ RAYGUIAPI int GuiWindowBox(Rectangle bounds, const char *title); RAYGUIAPI int GuiGroupBox(Rectangle bounds, const char *text); // Group Box control with text name RAYGUIAPI int GuiLine(Rectangle bounds, const char *text); // Line separator control, could contain text RAYGUIAPI int GuiPanel(Rectangle bounds, const char *text); // Panel control, useful to group controls -RAYGUIAPI int GuiTabBar(Rectangle bounds, const char **text, int count, int *active); // Tab Bar control, returns TAB to be closed or -1 +RAYGUIAPI int GuiTabBar(Rectangle bounds, char **text, int count, int *active); // Tab Bar control, returns TAB to be closed or -1 RAYGUIAPI int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view); // Scroll Panel control // Basic controls set @@ -800,7 +800,7 @@ RAYGUIAPI int GuiGrid(Rectangle bounds, const char *text, float spacing, int sub // Advance controls set RAYGUIAPI int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active); // List View control -RAYGUIAPI int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus); // List View with extended parameters +RAYGUIAPI int GuiListViewEx(Rectangle bounds, char **text, int count, int *scrollIndex, int *active, int *focus); // List View with extended parameters RAYGUIAPI int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons); // Message Box control, displays a message RAYGUIAPI int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, bool *secretViewActive); // Text Input Box control, ask for text, supports secret RAYGUIAPI int GuiColorPicker(Rectangle bounds, const char *text, Color *color); // Color Picker control (multiple color controls) @@ -1526,7 +1526,7 @@ static Color GetColor(int hexValue); // Returns a Color struct fr static int ColorToInt(Color color); // Returns hexadecimal value for a Color static bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle static const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' -static const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings +static char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings static int TextToInteger(const char *text); // Get integer value from text static float TextToFloat(const char *text); // Get float value from text @@ -1549,7 +1549,7 @@ static const char *GetTextIcon(const char *text, int *iconId); // Get text icon static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, Color tint); // Gui draw text using default font static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color); // Gui draw rectangle using default raygui style -static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow); // Split controls text into multiple strings +static char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow); // Split controls text into multiple strings static Vector3 ConvertHSVtoRGB(Vector3 hsv); // Convert color data from HSV to RGB static Vector3 ConvertRGBtoHSV(Vector3 rgb); // Convert color data from RGB to HSV @@ -1783,7 +1783,7 @@ int GuiPanel(Rectangle bounds, const char *text) // Tab Bar control // NOTE: Using GuiToggle() for the TABS -int GuiTabBar(Rectangle bounds, const char **text, int count, int *active) +int GuiTabBar(Rectangle bounds, char **text, int count, int *active) { #define RAYGUI_TABBAR_ITEM_WIDTH 148 @@ -2168,7 +2168,7 @@ int GuiToggleGroup(Rectangle bounds, const char *text, int *active) // Get substrings items from text (items pointers) int rows[RAYGUI_TOGGLEGROUP_MAX_ITEMS] = { 0 }; int itemCount = 0; - const char **items = GuiTextSplit(text, ';', &itemCount, rows); + char **items = GuiTextSplit(text, ';', &itemCount, rows); int prevRow = rows[0]; @@ -2212,7 +2212,7 @@ int GuiToggleSlider(Rectangle bounds, const char *text, int *active) // Get substrings items from text (items pointers) int itemCount = 0; - const char **items = NULL; + char **items = NULL; if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL); @@ -2356,7 +2356,7 @@ int GuiComboBox(Rectangle bounds, const char *text, int *active) // Get substrings items from text (items pointers, lengths and count) int itemCount = 0; - const char **items = GuiTextSplit(text, ';', &itemCount, NULL); + char **items = GuiTextSplit(text, ';', &itemCount, NULL); if (*active < 0) *active = 0; else if (*active > (itemCount - 1)) *active = itemCount - 1; @@ -2422,7 +2422,7 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod // Get substrings items from text (items pointers, lengths and count) int itemCount = 0; - const char **items = GuiTextSplit(text, ';', &itemCount, NULL); + char **items = GuiTextSplit(text, ';', &itemCount, NULL); Rectangle boundsOpen = bounds; boundsOpen.height = (itemCount + 1)*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); @@ -3602,7 +3602,7 @@ int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *activ { int result = 0; int itemCount = 0; - const char **items = NULL; + char **items = NULL; if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL); @@ -3612,7 +3612,7 @@ int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *activ } // List View control with extended parameters -int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus) +int GuiListViewEx(Rectangle bounds, char **text, int count, int *scrollIndex, int *active, int *focus) { int result = 0; GuiState state = guiState; @@ -4140,7 +4140,7 @@ int GuiMessageBox(Rectangle bounds, const char *title, const char *message, cons int result = -1; // Returns clicked button from buttons list, 0 refers to closed window button int buttonCount = 0; - const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); + char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); Rectangle buttonBounds = { 0 }; buttonBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING; buttonBounds.y = bounds.y + bounds.height - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT - RAYGUI_MESSAGEBOX_BUTTON_PADDING; @@ -4199,7 +4199,7 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co int result = -1; int buttonCount = 0; - const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); + char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); Rectangle buttonBounds = { 0 }; buttonBounds.x = bounds.x + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; buttonBounds.y = bounds.y + bounds.height - RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; @@ -5119,11 +5119,11 @@ static const char *GetTextIcon(const char *text, int *iconId) // Get text divided into lines (by line-breaks '\n') // WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator! -static const char **GetTextLines(const char *text, int *count) +static char **GetTextLines(const char *text, int *count) { #define RAYGUI_MAX_TEXT_LINES 128 - static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; + static char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings int textLength = (int)strlen(text); @@ -5131,12 +5131,11 @@ static const char **GetTextLines(const char *text, int *count) lines[0] = text; *count = 1; - for (int i = 0, k = 0; (i < textLength) && (*count < RAYGUI_MAX_TEXT_LINES); i++) + for (int i = 0; (i < textLength) && (*count < RAYGUI_MAX_TEXT_LINES); i++) { - if (text[i] == '\n') + if ((text[i] == '\n') && ((i + 1) < textLength)) { - k++; - lines[k] = &text[i + 1]; // WARNING: next value is valid? + lines[*count] = &text[i + 1]; *count += 1; } } @@ -5194,7 +5193,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C // WARNING: GuiTextSplit() function can't be used now because it can have already been used // before the GuiDrawText() call and its buffer is static, it would be overriden :( int lineCount = 0; - const char **lines = GetTextLines(text, &lineCount); + char **lines = GetTextLines(text, &lineCount); // Text style variables //int alignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT); @@ -5444,7 +5443,7 @@ static void GuiTooltip(Rectangle controlRec) // Split controls text into multiple strings // Also check for multiple columns (required by GuiToggleGroup()) -static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow) +static char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow) { // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, @@ -5463,8 +5462,8 @@ static const char **GuiTextSplit(const char *text, char delimiter, int *count, i #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE 1024 #endif - static const char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL }; // String pointers array (points to buffer data) - static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 }; // Buffer data (text input copy with '\0' added) + static char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL }; // String pointers array (points to buffer data) + static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 }; // Buffer data (text input copy with '\0' added) memset(buffer, 0, RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE); result[0] = buffer; @@ -5863,7 +5862,7 @@ static void DrawRectangleGradientV(int posX, int posY, int width, int height, Co } // Split string into multiple strings -const char **TextSplit(const char *text, char delimiter, int *count) +char **TextSplit(const char *text, char delimiter, int *count) { // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, diff --git a/examples/shaders/raygui.h b/examples/shaders/raygui.h index 67c16be45..42c5ae7bc 100644 --- a/examples/shaders/raygui.h +++ b/examples/shaders/raygui.h @@ -772,7 +772,7 @@ RAYGUIAPI int GuiWindowBox(Rectangle bounds, const char *title); RAYGUIAPI int GuiGroupBox(Rectangle bounds, const char *text); // Group Box control with text name RAYGUIAPI int GuiLine(Rectangle bounds, const char *text); // Line separator control, could contain text RAYGUIAPI int GuiPanel(Rectangle bounds, const char *text); // Panel control, useful to group controls -RAYGUIAPI int GuiTabBar(Rectangle bounds, const char **text, int count, int *active); // Tab Bar control, returns TAB to be closed or -1 +RAYGUIAPI int GuiTabBar(Rectangle bounds, char **text, int count, int *active); // Tab Bar control, returns TAB to be closed or -1 RAYGUIAPI int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view); // Scroll Panel control // Basic controls set @@ -800,7 +800,7 @@ RAYGUIAPI int GuiGrid(Rectangle bounds, const char *text, float spacing, int sub // Advance controls set RAYGUIAPI int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active); // List View control -RAYGUIAPI int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus); // List View with extended parameters +RAYGUIAPI int GuiListViewEx(Rectangle bounds, char **text, int count, int *scrollIndex, int *active, int *focus); // List View with extended parameters RAYGUIAPI int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons); // Message Box control, displays a message RAYGUIAPI int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, bool *secretViewActive); // Text Input Box control, ask for text, supports secret RAYGUIAPI int GuiColorPicker(Rectangle bounds, const char *text, Color *color); // Color Picker control (multiple color controls) @@ -1526,7 +1526,7 @@ static Color GetColor(int hexValue); // Returns a Color struct fr static int ColorToInt(Color color); // Returns hexadecimal value for a Color static bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle static const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' -static const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings +static char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings static int TextToInteger(const char *text); // Get integer value from text static float TextToFloat(const char *text); // Get float value from text @@ -1549,7 +1549,7 @@ static const char *GetTextIcon(const char *text, int *iconId); // Get text icon static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, Color tint); // Gui draw text using default font static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color); // Gui draw rectangle using default raygui style -static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow); // Split controls text into multiple strings +static char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow); // Split controls text into multiple strings static Vector3 ConvertHSVtoRGB(Vector3 hsv); // Convert color data from HSV to RGB static Vector3 ConvertRGBtoHSV(Vector3 rgb); // Convert color data from RGB to HSV @@ -1783,7 +1783,7 @@ int GuiPanel(Rectangle bounds, const char *text) // Tab Bar control // NOTE: Using GuiToggle() for the TABS -int GuiTabBar(Rectangle bounds, const char **text, int count, int *active) +int GuiTabBar(Rectangle bounds, char **text, int count, int *active) { #define RAYGUI_TABBAR_ITEM_WIDTH 148 @@ -2168,7 +2168,7 @@ int GuiToggleGroup(Rectangle bounds, const char *text, int *active) // Get substrings items from text (items pointers) int rows[RAYGUI_TOGGLEGROUP_MAX_ITEMS] = { 0 }; int itemCount = 0; - const char **items = GuiTextSplit(text, ';', &itemCount, rows); + char **items = GuiTextSplit(text, ';', &itemCount, rows); int prevRow = rows[0]; @@ -2212,7 +2212,7 @@ int GuiToggleSlider(Rectangle bounds, const char *text, int *active) // Get substrings items from text (items pointers) int itemCount = 0; - const char **items = NULL; + char **items = NULL; if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL); @@ -2356,7 +2356,7 @@ int GuiComboBox(Rectangle bounds, const char *text, int *active) // Get substrings items from text (items pointers, lengths and count) int itemCount = 0; - const char **items = GuiTextSplit(text, ';', &itemCount, NULL); + char **items = GuiTextSplit(text, ';', &itemCount, NULL); if (*active < 0) *active = 0; else if (*active > (itemCount - 1)) *active = itemCount - 1; @@ -2422,7 +2422,7 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod // Get substrings items from text (items pointers, lengths and count) int itemCount = 0; - const char **items = GuiTextSplit(text, ';', &itemCount, NULL); + char **items = GuiTextSplit(text, ';', &itemCount, NULL); Rectangle boundsOpen = bounds; boundsOpen.height = (itemCount + 1)*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); @@ -3602,7 +3602,7 @@ int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *activ { int result = 0; int itemCount = 0; - const char **items = NULL; + char **items = NULL; if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL); @@ -3612,7 +3612,7 @@ int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *activ } // List View control with extended parameters -int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus) +int GuiListViewEx(Rectangle bounds, char **text, int count, int *scrollIndex, int *active, int *focus) { int result = 0; GuiState state = guiState; @@ -4140,7 +4140,7 @@ int GuiMessageBox(Rectangle bounds, const char *title, const char *message, cons int result = -1; // Returns clicked button from buttons list, 0 refers to closed window button int buttonCount = 0; - const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); + char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); Rectangle buttonBounds = { 0 }; buttonBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING; buttonBounds.y = bounds.y + bounds.height - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT - RAYGUI_MESSAGEBOX_BUTTON_PADDING; @@ -4199,7 +4199,7 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co int result = -1; int buttonCount = 0; - const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); + char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); Rectangle buttonBounds = { 0 }; buttonBounds.x = bounds.x + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; buttonBounds.y = bounds.y + bounds.height - RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; @@ -5119,11 +5119,11 @@ static const char *GetTextIcon(const char *text, int *iconId) // Get text divided into lines (by line-breaks '\n') // WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator! -static const char **GetTextLines(const char *text, int *count) +static char **GetTextLines(const char *text, int *count) { #define RAYGUI_MAX_TEXT_LINES 128 - static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; + static char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings int textLength = (int)strlen(text); @@ -5131,12 +5131,11 @@ static const char **GetTextLines(const char *text, int *count) lines[0] = text; *count = 1; - for (int i = 0, k = 0; (i < textLength) && (*count < RAYGUI_MAX_TEXT_LINES); i++) + for (int i = 0; (i < textLength) && (*count < RAYGUI_MAX_TEXT_LINES); i++) { - if (text[i] == '\n') + if ((text[i] == '\n') && ((i + 1) < textLength)) { - k++; - lines[k] = &text[i + 1]; // WARNING: next value is valid? + lines[*count] = &text[i + 1]; *count += 1; } } @@ -5194,7 +5193,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C // WARNING: GuiTextSplit() function can't be used now because it can have already been used // before the GuiDrawText() call and its buffer is static, it would be overriden :( int lineCount = 0; - const char **lines = GetTextLines(text, &lineCount); + char **lines = GetTextLines(text, &lineCount); // Text style variables //int alignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT); @@ -5444,7 +5443,7 @@ static void GuiTooltip(Rectangle controlRec) // Split controls text into multiple strings // Also check for multiple columns (required by GuiToggleGroup()) -static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow) +static char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow) { // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, @@ -5463,8 +5462,8 @@ static const char **GuiTextSplit(const char *text, char delimiter, int *count, i #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE 1024 #endif - static const char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL }; // String pointers array (points to buffer data) - static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 }; // Buffer data (text input copy with '\0' added) + static char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL }; // String pointers array (points to buffer data) + static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 }; // Buffer data (text input copy with '\0' added) memset(buffer, 0, RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE); result[0] = buffer; @@ -5863,7 +5862,7 @@ static void DrawRectangleGradientV(int posX, int posY, int width, int height, Co } // Split string into multiple strings -const char **TextSplit(const char *text, char delimiter, int *count) +char **TextSplit(const char *text, char delimiter, int *count) { // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h index 67c16be45..42c5ae7bc 100644 --- a/examples/shapes/raygui.h +++ b/examples/shapes/raygui.h @@ -772,7 +772,7 @@ RAYGUIAPI int GuiWindowBox(Rectangle bounds, const char *title); RAYGUIAPI int GuiGroupBox(Rectangle bounds, const char *text); // Group Box control with text name RAYGUIAPI int GuiLine(Rectangle bounds, const char *text); // Line separator control, could contain text RAYGUIAPI int GuiPanel(Rectangle bounds, const char *text); // Panel control, useful to group controls -RAYGUIAPI int GuiTabBar(Rectangle bounds, const char **text, int count, int *active); // Tab Bar control, returns TAB to be closed or -1 +RAYGUIAPI int GuiTabBar(Rectangle bounds, char **text, int count, int *active); // Tab Bar control, returns TAB to be closed or -1 RAYGUIAPI int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view); // Scroll Panel control // Basic controls set @@ -800,7 +800,7 @@ RAYGUIAPI int GuiGrid(Rectangle bounds, const char *text, float spacing, int sub // Advance controls set RAYGUIAPI int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active); // List View control -RAYGUIAPI int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus); // List View with extended parameters +RAYGUIAPI int GuiListViewEx(Rectangle bounds, char **text, int count, int *scrollIndex, int *active, int *focus); // List View with extended parameters RAYGUIAPI int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons); // Message Box control, displays a message RAYGUIAPI int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, bool *secretViewActive); // Text Input Box control, ask for text, supports secret RAYGUIAPI int GuiColorPicker(Rectangle bounds, const char *text, Color *color); // Color Picker control (multiple color controls) @@ -1526,7 +1526,7 @@ static Color GetColor(int hexValue); // Returns a Color struct fr static int ColorToInt(Color color); // Returns hexadecimal value for a Color static bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle static const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' -static const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings +static char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings static int TextToInteger(const char *text); // Get integer value from text static float TextToFloat(const char *text); // Get float value from text @@ -1549,7 +1549,7 @@ static const char *GetTextIcon(const char *text, int *iconId); // Get text icon static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, Color tint); // Gui draw text using default font static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color); // Gui draw rectangle using default raygui style -static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow); // Split controls text into multiple strings +static char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow); // Split controls text into multiple strings static Vector3 ConvertHSVtoRGB(Vector3 hsv); // Convert color data from HSV to RGB static Vector3 ConvertRGBtoHSV(Vector3 rgb); // Convert color data from RGB to HSV @@ -1783,7 +1783,7 @@ int GuiPanel(Rectangle bounds, const char *text) // Tab Bar control // NOTE: Using GuiToggle() for the TABS -int GuiTabBar(Rectangle bounds, const char **text, int count, int *active) +int GuiTabBar(Rectangle bounds, char **text, int count, int *active) { #define RAYGUI_TABBAR_ITEM_WIDTH 148 @@ -2168,7 +2168,7 @@ int GuiToggleGroup(Rectangle bounds, const char *text, int *active) // Get substrings items from text (items pointers) int rows[RAYGUI_TOGGLEGROUP_MAX_ITEMS] = { 0 }; int itemCount = 0; - const char **items = GuiTextSplit(text, ';', &itemCount, rows); + char **items = GuiTextSplit(text, ';', &itemCount, rows); int prevRow = rows[0]; @@ -2212,7 +2212,7 @@ int GuiToggleSlider(Rectangle bounds, const char *text, int *active) // Get substrings items from text (items pointers) int itemCount = 0; - const char **items = NULL; + char **items = NULL; if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL); @@ -2356,7 +2356,7 @@ int GuiComboBox(Rectangle bounds, const char *text, int *active) // Get substrings items from text (items pointers, lengths and count) int itemCount = 0; - const char **items = GuiTextSplit(text, ';', &itemCount, NULL); + char **items = GuiTextSplit(text, ';', &itemCount, NULL); if (*active < 0) *active = 0; else if (*active > (itemCount - 1)) *active = itemCount - 1; @@ -2422,7 +2422,7 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod // Get substrings items from text (items pointers, lengths and count) int itemCount = 0; - const char **items = GuiTextSplit(text, ';', &itemCount, NULL); + char **items = GuiTextSplit(text, ';', &itemCount, NULL); Rectangle boundsOpen = bounds; boundsOpen.height = (itemCount + 1)*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); @@ -3602,7 +3602,7 @@ int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *activ { int result = 0; int itemCount = 0; - const char **items = NULL; + char **items = NULL; if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL); @@ -3612,7 +3612,7 @@ int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *activ } // List View control with extended parameters -int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus) +int GuiListViewEx(Rectangle bounds, char **text, int count, int *scrollIndex, int *active, int *focus) { int result = 0; GuiState state = guiState; @@ -4140,7 +4140,7 @@ int GuiMessageBox(Rectangle bounds, const char *title, const char *message, cons int result = -1; // Returns clicked button from buttons list, 0 refers to closed window button int buttonCount = 0; - const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); + char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); Rectangle buttonBounds = { 0 }; buttonBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING; buttonBounds.y = bounds.y + bounds.height - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT - RAYGUI_MESSAGEBOX_BUTTON_PADDING; @@ -4199,7 +4199,7 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co int result = -1; int buttonCount = 0; - const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); + char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); Rectangle buttonBounds = { 0 }; buttonBounds.x = bounds.x + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; buttonBounds.y = bounds.y + bounds.height - RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; @@ -5119,11 +5119,11 @@ static const char *GetTextIcon(const char *text, int *iconId) // Get text divided into lines (by line-breaks '\n') // WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator! -static const char **GetTextLines(const char *text, int *count) +static char **GetTextLines(const char *text, int *count) { #define RAYGUI_MAX_TEXT_LINES 128 - static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; + static char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings int textLength = (int)strlen(text); @@ -5131,12 +5131,11 @@ static const char **GetTextLines(const char *text, int *count) lines[0] = text; *count = 1; - for (int i = 0, k = 0; (i < textLength) && (*count < RAYGUI_MAX_TEXT_LINES); i++) + for (int i = 0; (i < textLength) && (*count < RAYGUI_MAX_TEXT_LINES); i++) { - if (text[i] == '\n') + if ((text[i] == '\n') && ((i + 1) < textLength)) { - k++; - lines[k] = &text[i + 1]; // WARNING: next value is valid? + lines[*count] = &text[i + 1]; *count += 1; } } @@ -5194,7 +5193,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C // WARNING: GuiTextSplit() function can't be used now because it can have already been used // before the GuiDrawText() call and its buffer is static, it would be overriden :( int lineCount = 0; - const char **lines = GetTextLines(text, &lineCount); + char **lines = GetTextLines(text, &lineCount); // Text style variables //int alignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT); @@ -5444,7 +5443,7 @@ static void GuiTooltip(Rectangle controlRec) // Split controls text into multiple strings // Also check for multiple columns (required by GuiToggleGroup()) -static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow) +static char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow) { // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, @@ -5463,8 +5462,8 @@ static const char **GuiTextSplit(const char *text, char delimiter, int *count, i #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE 1024 #endif - static const char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL }; // String pointers array (points to buffer data) - static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 }; // Buffer data (text input copy with '\0' added) + static char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL }; // String pointers array (points to buffer data) + static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 }; // Buffer data (text input copy with '\0' added) memset(buffer, 0, RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE); result[0] = buffer; @@ -5863,7 +5862,7 @@ static void DrawRectangleGradientV(int posX, int posY, int width, int height, Co } // Split string into multiple strings -const char **TextSplit(const char *text, char delimiter, int *count) +char **TextSplit(const char *text, char delimiter, int *count) { // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, From 005ff74eb08a0bb6b9ccb825b7c84a8fe146b096 Mon Sep 17 00:00:00 2001 From: David Reid Date: Sat, 21 Feb 2026 17:02:40 +1000 Subject: [PATCH 055/319] Audio: Improvements to device configuration (#5577) * Audio: Stop setting capture config options. Since the device is being configured as a playback device, all capture config options are unused and therefore need to not be set. * Audio: Stop pre-silencing the miniaudio output buffer. raylib already manually silences the output buffer prior to mixing so there is no reason to have miniaudio also do it. It can therefore be disabled via the device config to make data processing slightly more efficient. * Audio: Stop forcing fixed sized processing callbacks. There is no requirement for raylib to have guaranteed fixed sized audio processing. By disabling it, audio processing can be made more efficient by not having to run the data through an internal intermediary buffer. * Audio: Make the period size (latency) configurable. The default period size is 10ms, but this is inappropriate for certain platforms so it is useful to be able to allow those platforms to configure the period size as required. * Audio: Fix documentation for pan. The pan if -1..1, not 0..1. --- src/config.h | 1 + src/raudio.c | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/config.h b/src/config.h index 5e7c6f1d2..3beaeeceb 100644 --- a/src/config.h +++ b/src/config.h @@ -303,6 +303,7 @@ #define AUDIO_DEVICE_FORMAT ma_format_f32 // Device output format (miniaudio: float-32bit) #define AUDIO_DEVICE_CHANNELS 2 // Device output channels: stereo #define AUDIO_DEVICE_SAMPLE_RATE 0 // Device sample rate (device default) +#define AUDIO_DEVICE_PERIOD_SIZE_IN_FRAMES 0 // Device period size (controls latency, 0 defaults to 10ms) #define MAX_AUDIO_BUFFER_POOL_CHANNELS 16 // Maximum number of audio pool channels diff --git a/src/raudio.c b/src/raudio.c index 8322f1baf..848b48c4d 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -290,6 +290,9 @@ typedef struct tagBITMAPINFOHEADER { #ifndef AUDIO_DEVICE_SAMPLE_RATE #define AUDIO_DEVICE_SAMPLE_RATE 0 // Device output sample rate #endif +#ifndef AUDIO_DEVICE_PERIOD_SIZE_IN_FRAMES + #define AUDIO_DEVICE_PERIOD_SIZE_IN_FRAMES 0 // Device latency. 0 defaults to 10ms +#endif #ifndef MAX_AUDIO_BUFFER_POOL_CHANNELS #define MAX_AUDIO_BUFFER_POOL_CHANNELS 16 // Audio pool channels @@ -349,7 +352,7 @@ struct rAudioBuffer { float volume; // Audio buffer volume float pitch; // Audio buffer pitch - float pan; // Audio buffer pan (0.0f to 1.0f) + float pan; // Audio buffer pan (-1.0f to 1.0f) bool playing; // Audio buffer state: AUDIO_PLAYING bool paused; // Audio buffer state: AUDIO_PAUSED @@ -477,12 +480,12 @@ void InitAudioDevice(void) config.playback.pDeviceID = NULL; // NULL for the default playback AUDIO.System.device config.playback.format = AUDIO_DEVICE_FORMAT; config.playback.channels = AUDIO_DEVICE_CHANNELS; - config.capture.pDeviceID = NULL; // NULL for the default capture AUDIO.System.device - config.capture.format = ma_format_s16; - config.capture.channels = 1; config.sampleRate = AUDIO_DEVICE_SAMPLE_RATE; + config.periodSizeInFrames = AUDIO_DEVICE_PERIOD_SIZE_IN_FRAMES; config.dataCallback = OnSendAudioDataToDevice; config.pUserData = NULL; + config.noPreSilencedOutputBuffer = true; // raylib pre-silences the output buffer manually + config.noFixedSizedCallback = true; // raylib does not require fixed sized callback guarantees. This bypasses an internal intermediary buffer result = ma_device_init(&AUDIO.System.context, &config, &AUDIO.System.device); if (result != MA_SUCCESS) From 0c91f230fd9a7056bb51870dbb8c5ec305c02e10 Mon Sep 17 00:00:00 2001 From: David Reid Date: Sat, 21 Feb 2026 17:04:32 +1000 Subject: [PATCH 056/319] Audio: Fix a glitch at the end of a sound. (#5578) This is happening because the processing function keeps reading audio data from the AudioBuffer even after it has been marked as stopped. There is also an error in ReadAudioBufferFramesInInternalFormat() where if it is called on a stopped sound, it'll still return audio frames. This has also been addressed with this commit. --- src/raudio.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 848b48c4d..4866f32f9 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -2377,6 +2377,12 @@ static void OnLog(void *pUserData, ma_uint32 level, const char *pMessage) // Reads audio data from an AudioBuffer object in internal format static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, void *framesOut, ma_uint32 frameCount) { + // Don't read anything if the sound is not playing + if (!audioBuffer->playing) + { + return 0; + } + // Using audio buffer callback if (audioBuffer->callback) { @@ -2522,18 +2528,20 @@ static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, f { estimatedInputFrameCount = inputBufferFrameCap; } + + ma_uint32 inputFramesInInternalFormatCount = ReadAudioBufferFramesInInternalFormat(audioBuffer, inputBuffer, estimatedInputFrameCount); - estimatedInputFrameCount = ReadAudioBufferFramesInInternalFormat(audioBuffer, inputBuffer, estimatedInputFrameCount); - - ma_uint64 inputFramesProcessedThisIteration = estimatedInputFrameCount; + ma_uint64 inputFramesProcessedThisIteration = inputFramesInInternalFormatCount; ma_uint64 outputFramesProcessedThisIteration = outputFramesToProcessThisIteration; ma_data_converter_process_pcm_frames(&audioBuffer->converter, inputBuffer, &inputFramesProcessedThisIteration, runningFramesOut, &outputFramesProcessedThisIteration); - if (estimatedInputFrameCount > inputFramesProcessedThisIteration) + totalOutputFramesProcessed += (ma_uint32)outputFramesProcessedThisIteration; + + if (inputFramesInInternalFormatCount > inputFramesProcessedThisIteration) { // Getting here means the estimated input frame count was overestimated. The residual needs // be stored for later use. - ma_uint64 residualFrameCount = estimatedInputFrameCount - inputFramesProcessedThisIteration; + ma_uint64 residualFrameCount = inputFramesInInternalFormatCount - inputFramesProcessedThisIteration; // A safety check to make sure the capacity of the residual cache is not exceeded. if (residualFrameCount > AUDIO_BUFFER_RESIDUAL_CAPACITY) @@ -2545,7 +2553,9 @@ static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, f audioBuffer->converterResidualCount = residualFrameCount; } - totalOutputFramesProcessed += (ma_uint32)outputFramesProcessedThisIteration; + if (inputFramesInInternalFormatCount < estimatedInputFrameCount) { + break; // Reached the end of the sound + } } } From 11e3e6e0b994d0543cb83e253438e08402dba014 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 21 Feb 2026 09:09:43 +0100 Subject: [PATCH 057/319] REVIEWED: Formating, tested sound examples --- src/raudio.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 4866f32f9..2740462b4 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -2378,10 +2378,7 @@ static void OnLog(void *pUserData, ma_uint32 level, const char *pMessage) static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, void *framesOut, ma_uint32 frameCount) { // Don't read anything if the sound is not playing - if (!audioBuffer->playing) - { - return 0; - } + if (!audioBuffer->playing) return 0; // Using audio buffer callback if (audioBuffer->callback) @@ -2519,16 +2516,9 @@ static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, f // When the guess is overestimated, that's when it gets more complicated. In this case, any overflow // needs to be stored in a buffer for later processing by the next read. ma_uint32 estimatedInputFrameCount = (ma_uint32)(((float)audioBuffer->converter.resampler.sampleRateIn / audioBuffer->converter.resampler.sampleRateOut) * outputFramesToProcessThisIteration); - if (estimatedInputFrameCount == 0) - { - estimatedInputFrameCount = 1; // Make sure at least one input frame is read. - } + if (estimatedInputFrameCount == 0) estimatedInputFrameCount = 1; // Make sure at least one input frame is read. + if (estimatedInputFrameCount > inputBufferFrameCap) estimatedInputFrameCount = inputBufferFrameCap; - if (estimatedInputFrameCount > inputBufferFrameCap) - { - estimatedInputFrameCount = inputBufferFrameCap; - } - ma_uint32 inputFramesInInternalFormatCount = ReadAudioBufferFramesInInternalFormat(audioBuffer, inputBuffer, estimatedInputFrameCount); ma_uint64 inputFramesProcessedThisIteration = inputFramesInInternalFormatCount; @@ -2553,9 +2543,7 @@ static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, f audioBuffer->converterResidualCount = residualFrameCount; } - if (inputFramesInInternalFormatCount < estimatedInputFrameCount) { - break; // Reached the end of the sound - } + if (inputFramesInInternalFormatCount < estimatedInputFrameCount) break; // Reached the end of the sound } } From 7a42778e7b6bf5a0e9e0fabf3ae81e132d8b1497 Mon Sep 17 00:00:00 2001 From: Krzysztof Szenk Date: Sun, 22 Feb 2026 10:40:53 +0100 Subject: [PATCH 058/319] Expose PLATFORM_WEB_RGFW to cmake (#5579) --- cmake/LibraryConfigurations.cmake | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index ffc12edda..5ccd74485 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -167,6 +167,11 @@ elseif ("${PLATFORM}" MATCHES "RGFW") set(LIBS_PRIVATE ${X11_LIBRARIES} ${OPENGL_LIBRARIES}) endif () + +elseif ("${PLATFORM}" MATCHES "WebRGFW") + set(PLATFORM_CPP "PLATFORM_WEB_RGFW") + set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") + set(CMAKE_STATIC_LIBRARY_SUFFIX ".a") endif () if (NOT ${OPENGL_VERSION} MATCHES "OFF") From d2c4aa11e56bb5cad6c33c684e878b9d8f9aa1c3 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Sun, 22 Feb 2026 14:35:54 -0600 Subject: [PATCH 059/319] update examples cmakelists (#5580) --- examples/CMakeLists.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index b482df0fc..6554d1327 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -189,6 +189,18 @@ foreach (example_source ${example_sources}) target_link_libraries(${example_name} "-framework OpenGL") endif() + elseif (${PLATFORM} MATCHES "RGFW") + if (APPLE) + target_link_libraries(${example_name} "-framework IOKit") + target_link_libraries(${example_name} "-framework Cocoa") + target_link_libraries(${example_name} "-framework OpenGL") + elseif (WIN32) + target_link_libraries(${example_name} "-lgdi32") + target_link_libraries(${example_name} "-lwinmm") + elseif (UNIX) + target_link_libraries(${example_name} "-lX11") + target_link_libraries(${example_name} "-lXrandr") + endif() endif () endforeach () From 8b181b1574dd480a78c6b60a825aeae6b73c66d5 Mon Sep 17 00:00:00 2001 From: Krzysztof Szenk Date: Sun, 22 Feb 2026 21:36:37 +0100 Subject: [PATCH 060/319] Fix for PR #5579 (WebRGFW platform exposed to cmake) (#5581) * Expose PLATFORM_WEB_RGFW to cmake * Fix for WebRGFW platform exposed to cmake --- CMakeOptions.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeOptions.txt b/CMakeOptions.txt index cb1b95b0c..0a453917d 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -6,7 +6,7 @@ if(EMSCRIPTEN) # When configuring web builds with "emcmake cmake -B build -S .", set PLATFORM to Web by default SET(PLATFORM Web CACHE STRING "Platform to build for.") endif() -enum_option(PLATFORM "Desktop;Web;Android;Raspberry Pi;DRM;SDL;RGFW" "Platform to build for.") +enum_option(PLATFORM "Desktop;Web;WebRGFW;Android;Raspberry Pi;DRM;SDL;RGFW" "Platform to build for.") enum_option(OPENGL_VERSION "OFF;4.3;3.3;2.1;1.1;ES 2.0;ES 3.0;Software" "Force a specific OpenGL Version?") From fbd83cafc7c51ecc38be7f7b19c185a42f333cb0 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Sun, 22 Feb 2026 15:21:09 -0600 Subject: [PATCH 061/319] [Backend/RGFW] Update RGFW to 2.0.0 (#5582) * initial update * minor changes * added minigamepad * updates * updates - 2.0-dev * update char press * update gamepad * update gamepad * update (web) * update rename, spacing, etc * updates * updates for mac-arm64 * moved RGFW into folder * highdpi fixes * update windowPos (bug remaining somewhere) * temp stash, macos fixes for pixelratio * highdpi resize window fixes * remove unneeded makefiles * fix undef oopsie * update fullscreen/borderless * update macos defines for older macs and newer ones * update resizing window --- src/external/RGFW.h | 11074 ----------- src/external/RGFW/RGFW.h | 15891 ++++++++++++++++ src/external/RGFW/deps/minigamepad.h | 5242 +++++ .../pointer-constraints-unstable-v1.xml | 334 + .../RGFW/deps/wayland/pointer-warp-v1.xml | 72 + .../wayland/relative-pointer-unstable-v1.xml | 136 + .../wayland/xdg-decoration-unstable-v1.xml | 160 + .../deps/wayland/xdg-output-unstable-v1.xml | 222 + src/external/RGFW/deps/wayland/xdg-shell.xml | 1418 ++ .../deps/wayland/xdg-toplevel-icon-v1.xml | 205 + src/platforms/rcore_desktop_rgfw.c | 997 +- 11 files changed, 24353 insertions(+), 11398 deletions(-) delete mode 100644 src/external/RGFW.h create mode 100644 src/external/RGFW/RGFW.h create mode 100644 src/external/RGFW/deps/minigamepad.h create mode 100644 src/external/RGFW/deps/wayland/pointer-constraints-unstable-v1.xml create mode 100644 src/external/RGFW/deps/wayland/pointer-warp-v1.xml create mode 100644 src/external/RGFW/deps/wayland/relative-pointer-unstable-v1.xml create mode 100644 src/external/RGFW/deps/wayland/xdg-decoration-unstable-v1.xml create mode 100644 src/external/RGFW/deps/wayland/xdg-output-unstable-v1.xml create mode 100644 src/external/RGFW/deps/wayland/xdg-shell.xml create mode 100644 src/external/RGFW/deps/wayland/xdg-toplevel-icon-v1.xml mode change 100644 => 100755 src/platforms/rcore_desktop_rgfw.c diff --git a/src/external/RGFW.h b/src/external/RGFW.h deleted file mode 100644 index c01ce1177..000000000 --- a/src/external/RGFW.h +++ /dev/null @@ -1,11074 +0,0 @@ -/* -* -* RGFW 1.7.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) 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.0f, 1.0f, 1.0f, 1.0f); - 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) || !defined(RGFW_MEMSET) - #include -#endif - -#ifndef RGFW_MEMSET - #define RGFW_MEMSET(ptr, value, num) memset(ptr, value, num) -#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 - -#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 - #ifndef RGFWDEF - #define RGFWDEF - #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 - #include - #endif - - #define RGFW_UNIX - #include -#endif -#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 - #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 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 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 RGFW_area { 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 */ - i32 wOffset; /*!< width offset for window */ - i32 hOffset; /*!< height offset for window */ - HICON hIconSmall, hIconBig; /*!< source window icons */ - #if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL) - HGLRC ctx; /*!< source graphics context */ - #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 - RGFW_rect r; -#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 */ - - /*! 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 */ - -#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 */ - -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); - -/*! 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); -/*! 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); - -/*! 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, NULL, err} -#define RGFW_DEBUG_CTX_MON(monitor) {_RGFW.root, &monitor, 0} -#else -#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); -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); -/*!< 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_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) - #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); - -/* for C++ / C89 */ -#define RGFW_eventQueuePushEx(eventInit) { RGFW_event e; eventInit; RGFW_eventQueuePush(e); } - -/*! - 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_enter = RGFW_return, - - 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_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, - 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 -#define RGFW_WAYLAND_LABEL wayland:; -#else -#define RGFW_GOTO_WAYLAND(fallback) -#define RGFW_WAYLAND_LABEL -#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 -*/ - - - -/* - 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 - -u32 RGFW_apiKeycodes[RGFW_keyLast] = { 0 }; - -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(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 - 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, 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 - 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 - 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 -}; -#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]; -} - -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 { - RGFW_bool current : 1; - RGFW_bool prev : 1; -} RGFW_keyState; - -RGFW_keyState RGFW_keyboard[RGFW_keyLast] = { {0, 0} }; - -RGFWDEF void RGFW_resetKeyPrev(void); -void RGFW_resetKeyPrev(void) { - size_t i; /*!< reset each previous state */ - 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 -*/ - -/* 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_windowMinimizedCallback(win, win->r); - } else if (RGFW_window_isMaximized(win)) { - win->_flags |= RGFW_windowMaximize; - 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_eventQueuePushEx(e.type = RGFW_windowRestored; e._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) || defined(RGFW_WAYLAND) - RGFW_mouse* hiddenMouse; - #endif - RGFW_event events[RGFW_MAX_EVENTS]; - -} RGFW_globalStruct; -#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_bool _RGFW_init = RGFW_FALSE; -RGFW_globalStruct _RGFW; -#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) { - RGFW_event* ev; - if (_RGFW.eventLen == 0) return NULL; - - ev = (RGFW_event*)&_RGFW.events[_RGFW.eventIndex]; - - _RGFW.eventLen--; - if (_RGFW.eventLen >= 0 && _RGFW.eventIndex < _RGFW.eventLen) { - _RGFW.eventIndex++; - } else if (_RGFW.eventLen == 0) { - _RGFW.eventIndex = 0; - } - - if (ev->_win != win && ev->_win != NULL) { - RGFW_eventQueuePush(*ev); - 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_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 event; - event = win->event; - RGFW_window_close(win); - return &event; - } - - if (win->event.type != RGFW_DNDInit) win->event.type = 0; - - /* check queued events */ - 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_FALSE) 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->exitKey = RGFW_escape; - win->event.droppedFilesCount = 0; - - win->_flags = 0 | (win->_flags & RGFW_WINDOW_ALLOC); - win->_flags |= flags; - win->event.keyMod = 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)); - } -} - -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_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; -#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_monitorMode mode; - 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); -} - -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)|| (win->exitKey && RGFW_isPressed(win, win->exitKey))); -} - -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); - - -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_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); } - -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); -} -#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_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) - 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; -} - -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) - -#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(void); -i32* RGFW_initFormatAttribs(void) { - 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 (_RGFW.root->_flags & RGFW_windowOpenglSoftware) { - 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) { -#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 - #endif - #ifdef RGFW_X11 - 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; - - 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 - #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 - - 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(0x2096, 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); -} - -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); - - 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) -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) - 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 == RGFW_FALSE) 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) -RGFW_WAYLAND_LABEL - 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].prev = 0; - RGFW_gamepadPressed[index][j].current = 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_eventQueuePushEx(e.type = RGFW_quit; e._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_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); -} -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_eventQueuePushEx(e.type = RGFW_mouseLeave; - e.point = win->event.point; - e._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_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); -} -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); - - /* flip right and middle button codes */ - 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_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)); -} -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_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); - - 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_eventQueuePushEx(e.type = RGFW_focusIn; e._win = RGFW_key_win); - RGFW_focusCallback(RGFW_key_win, RGFW_TRUE); - - 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); - - RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); - if (RGFW_key_win == win) - RGFW_key_win = NULL; - - RGFW_eventQueuePushEx(e.type = RGFW_focusOut; e._win = win); - 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); - - 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_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)); -} -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); - 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) { - 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_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 -#if defined(RGFW_OPENGL) && defined(RGFW_X11) - typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); -#endif - -#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 - - void* X11Xihandle = NULL; -#endif - -#if !defined(RGFW_NO_X11_EXT_PRELOAD) && defined(RGFW_X11) - 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) && defined(RGFW_X11) - 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 - -#ifdef RGFW_X11 -const char* RGFW_instName = NULL; -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) { - 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 - -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 - RGFW_WAYLAND_LABEL {} - 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 - RGFW_WAYLAND_LABEL{} - #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 - RGFW_WAYLAND_LABEL - 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 - RGFW_WAYLAND_LABEL - 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 - RGFW_WAYLAND_LABEL - 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)); \ -} - -#ifdef RGFW_X11 -void RGFW_window_getVisual(RGFW_window* win) { -#if defined(RGFW_OPENGL) && !defined(RGFW_EGL) - i32* visual_attribs = RGFW_initFormatAttribs(); - 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"); - - 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 - 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 -} -#endif -#ifndef RGFW_EGL -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; - 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); -#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); -#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 - 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; - 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); - - _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); - - 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 */ - 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 -RGFW_WAYLAND_LABEL - _RGFW.wl_display = wl_display_connect(NULL); -#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); - -#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); - - /* 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.event_mask = event_mask; - - /* 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 | 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_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); - - win->src.r = win->r; - - RGFW_window_show(win); - return win; /*return newly created window */ -#endif -#ifdef RGFW_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; - 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.display = _RGFW.display; - win->src.window = _RGFW.helperWindow; - 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); - 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_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_setName(win, name); - 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 - 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(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_UNUSED(event); -#ifdef RGFW_X11 - 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); -#endif -} - -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); - - -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(); - - 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); - 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; - 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.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); - 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); - - - if ((win->_flags & RGFW_HOLD_MOUSE)) RGFW_window_mouseHold(win, RGFW_AREA(win->r.w, win->r.h)); - break; - case FocusOut: - win->event.type = RGFW_focusOut; - RGFW_focusCallback(win, 0); - RGFW_window_focusLost(win); - 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->src.r.w || E.xconfigure.height != win->src.r.h) { - win->event.type = RGFW_windowResized; - 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->src.r.x || E.xconfigure.y != win->src.r.y) { - win->event.type = RGFW_windowMoved; - 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; - } - - 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 - RGFW_WAYLAND_LABEL - 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 - RGFW_WAYLAND_LABEL - 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 - 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 - 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); - RGFW_GOTO_WAYLAND(0); - - if (a.w == 0 && a.h == 0) - return; -#ifdef RGFW_X11 - 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); - 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)); - - 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); - 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)); - - 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); -#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); - 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); -} -#endif - -void RGFW_window_maximize(RGFW_window* win) { - win->_oldRect = win->r; - 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); - 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); - - 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); -} -#endif - -void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) { - RGFW_ASSERT(win != NULL); - 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; -#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_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); -#ifdef RGFW_X11 - XFlush(win->src.display); -#endif -} - -RGFW_bool RGFW_window_isFloating(RGFW_window* win) { - RGFW_GOTO_WAYLAND(0); -#ifdef RGFW_X11 - 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); -#endif -#ifdef RGFW_WAYLAND - RGFW_WAYLAND_LABEL RGFW_UNUSED(win); -#endif - 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); - - 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*)buf, sizeof(buf) - ); - #endif - #ifdef RGFW_WAYLAND - RGFW_WAYLAND_LABEL - 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); - 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); - - return; - } - - 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 */ - -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 - RGFW_WAYLAND_LABEL RGFW_UNUSED(icon); RGFW_UNUSED(a); RGFW_UNUSED(channels); RGFW_UNUSED(type); - 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 - RGFW_WAYLAND_LABEL - 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 - RGFW_WAYLAND_LABEL - 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 - RGFW_WAYLAND_LABEL - 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 - RGFW_WAYLAND_LABEL - 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 - 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]); - 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 - RGFW_WAYLAND_LABEL - 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 - RGFW_WAYLAND_LABEL - /* 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 - 1); - _RGFW.clipboard[_RGFW.clipboard_len - 1] = '\0'; - return (RGFW_ssize_t)_RGFW.clipboard_len - 1; - } - - 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) - RGFW_WAYLAND_LABEL RGFW_UNUSED(str); RGFW_UNUSED(strCapacity); - 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 - 1); - _RGFW.clipboard[textLen - 1] = '\0'; - _RGFW.clipboard_len = textLen; - #endif - #ifdef RGFW_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_GOTO_WAYLAND(0); -#ifdef RGFW_X11 - 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; -#endif -#ifdef RGFW_WAYLAND - RGFW_WAYLAND_LABEL - return RGFW_FALSE; -#endif -} - -RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - 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); - - 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); -#endif -#ifdef RGFW_WAYLAND -RGFW_WAYLAND_LABEL; -#endif - 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 - - -#ifdef RGFW_X11 -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; -} -#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); - - 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_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; - 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_STRNCPY(monitor.name, info->name, sizeof(monitor.name) - 1); - monitor.name[sizeof(monitor.name) - 1] = '\0'; - - 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; -#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) { - 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 - RGFW_WAYLAND_LABEL RGFW_UNUSED(len); - 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 - RGFW_WAYLAND_LABEL 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 -RGFW_WAYLAND_LABEL RGFW_UNUSED(mon); RGFW_UNUSED(mode); RGFW_UNUSED(request); -#endif - return RGFW_FALSE; -} - -RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { - 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 mon; - } - - 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 -RGFW_WAYLAND_LABEL -#endif - return mon; - -} - -#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 - RGFW_WAYLAND_LABEL - #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 - RGFW_WAYLAND_LABEL -#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 || _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 */ - 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); - - 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) - 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_DEBUG_CTX(NULL, 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); - - RGFW_GOTO_WAYLAND(0); - #ifdef RGFW_X11 - /* 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 - RGFW_WAYLAND_LABEL - - RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a window was freed"); - - 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)) - 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 - -#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); - 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_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_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); - - 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_eventQueuePushEx(e.type = RGFW_windowMoved; e._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) - win->src.wOffset; - int height = (windowRect.bottom - windowRect.top) - win->src.hOffset; - 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((u32)(windowRect.right - windowRect.left) - (u32)win->src.wOffset, - (u32)(windowRect.bottom - windowRect.top) - (u32)win->src.hOffset)); - } - - win->r.w = (windowRect.right - windowRect.left) - (i32)win->src.wOffset; - win->r.h = (windowRect.bottom - windowRect.top) - (i32)win->src.hOffset; - RGFW_eventQueuePushEx(e.type = RGFW_windowResized; e._win = win); - RGFW_windowResizedCallback(win, win->r); - 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_eventQueuePushEx(e.type = RGFW_scaleUpdated; e.scaleX = scaleX; e.scaleY = scaleY; e._win = win); - return DefWindowProcW(hWnd, message, wParam, lParam); - } - #endif - case WM_GETMINMAXINFO: { - MINMAXINFO* mmi = (MINMAXINFO*) lParam; - mmi->ptMinTrackSize.x = (LONG)(win->src.minSize.w + win->src.wOffset); - mmi->ptMinTrackSize.y = (LONG)(win->src.minSize.h + win->src.hOffset); - if (win->src.maxSize.w == 0 && win->src.maxSize.h == 0) - return DefWindowProcW(hWnd, message, wParam, lParam); - - mmi->ptMaxTrackSize.x = (LONG)(win->src.maxSize.w + win->src.wOffset); - 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_eventQueuePushEx(e.type = RGFW_windowRefresh; e._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)); \ - RGFW_ASSERT(name##SRC != NULL); \ - } - -#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(_RGFW.root, 0), "Failed to load XInputGetState"); - if (XInputGetKeystrokeSRC == NULL) - RGFW_sendDebugInfo(RGFW_typeError, RGFW_errFailedFuncLoad, RGFW_DEBUG_CTX(_RGFW.root, 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; - 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); RGFW_ASSERT(x != NULL); } - -#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, 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) { -#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 (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(); - - 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 (!(pfd.dwFlags & PFD_GENERIC_ACCELERATED)) { - win->_flags |= RGFW_windowOpenglSoftware; - } - - 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); -#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) { -#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 - -#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, 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_DEBUG_CTX(NULL, 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.wOffset = (u32)(windowRect.right - windowRect.left) - (u32)(clientRect.right - clientRect.left); - win->src.window = CreateWindowW(Class.lpszClassName, (wchar_t*)wide_name, window_style, win->r.x, win->r.y, win->r.w + (i32)win->src.wOffset, win->r.h + (i32)win->src.hOffset, 0, 0, inh, 0); - SetPropW(win->src.window, L"RGFW", win); - RGFW_window_resize(win, RGFW_AREA(win->r.w, win->r.h)); /* so WM_GETMINMAXINFO gets called again */ - - 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_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 + (i32)win->src.wOffset, 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); -} - -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; - } - - 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; - - 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; - - DEVMODEA 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_STRNCPY(monitor.name, mdd.DeviceString, sizeof(monitor.name) - 1); - monitor.name[sizeof(monitor.name) - 1] = '\0'; - 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) { - POINT p = { mon.x, mon.y }; - HMONITOR src = MonitorFromPoint(p, MONITOR_DEFAULTTOPRIMARY); - - MONITORINFOEX monitorInfo; - monitorInfo.cbSize = sizeof(MONITORINFOEX); - GetMonitorInfoA(src, (LPMONITORINFO)&monitorInfo); - - DISPLAY_DEVICEA 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, (const char*)monitorInfo.szDevice) != 0) - continue; - - DEVMODEA 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 (ChangeDisplaySettingsExA((LPCSTR)dd.DeviceName, (DEVMODE *)&dm, NULL, CDS_TEST, NULL) == DISP_CHANGE_SUCCESSFUL) { - if (ChangeDisplaySettingsExA((LPCSTR)dd.DeviceName, (DEVMODE *)&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_DEBUG_CTX(NULL, 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 + (i32)win->src.wOffset, 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 = (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); -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); -} - -typedef RGFW_ENUM(NSInteger, NSOpenGLContextParameter) { - 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 */ - 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 -/* 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) { - 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_eventQueuePushEx(e.type = RGFW_quit; e._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_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; -} -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_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")); - - 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; -} - -#ifndef RGFW_NO_IOKIT -#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) { - 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_eventQueuePushEx(e.type = intValue ? RGFW_gamepadButtonPressed: RGFW_gamepadButtonReleased; - e.button = button; - e.gamepad = (u16)index; - e._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_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); - } - } -} - -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_eventQueuePushEx(e.type = RGFW_gamepadConnected; - e.gamepad = (u16)i; - e._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_eventQueuePushEx(e.type = RGFW_gamepadDisconnected; - e.gamepad = (u16)index; - e._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_eventQueuePushEx(e.type = RGFW_windowRestored; e._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_eventQueuePushEx(e.type = RGFW_windowMinimized; e._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_eventQueuePushEx(e.type = RGFW_focusIn; e._win = win); - - RGFW_focusCallback(win, RGFW_TRUE); - - 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) { - RGFW_UNUSED(sel); - RGFW_window* win = NULL; - object_getInstanceVariable(self, "RGFW_window", (void**)&win); - if (win == NULL) return; - - RGFW_window_focusLost(win); - RGFW_eventQueuePushEx(e.type = RGFW_focusOut; e._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_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_eventQueuePushEx(e.type = RGFW_windowRestored; e._win = win); - RGFW_windowRestoredCallback(win, win->r); - - } - - - RGFW_eventQueuePushEx(e.type = RGFW_windowResized; e._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_eventQueuePushEx(e.type = RGFW_windowMoved; e._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_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) { - RGFW_UNUSED(rect); RGFW_UNUSED(_cmd); - RGFW_window* win = NULL; - object_getInstanceVariable(self, "RGFW_window", (void**)&win); - if (win == NULL) return; - - RGFW_eventQueuePushEx(e.type = RGFW_windowRefresh; e._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) { -#ifdef RGFW_OPENGL - 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"); - win->_flags |= RGFW_windowOpenglSoftware; - void* subAttrs = RGFW_initFormatAttribs(); - 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); -#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) { -#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). - */ - 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_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) { - 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_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 = 29, - NSEventTypeMagnify = 30, - NSEventTypeSwipe = 31, - NSEventTypeRotate = 18, - NSEventTypeBeginGesture = 19, - NSEventTypeEndGesture = 20, - - NSEventTypeSmartMagnify = 32, - NSEventTypeQuickLook = 33, - - NSEventTypePressure = 34, - NSEventTypeDirectTouch = 37, - - NSEventTypeChangeMode = 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")); -} - -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; - - 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_getFallbackRefreshRate(CGDirectDisplayID displayID); - -u32 RGFW_osx_getRefreshRate(CGDirectDisplayID display, CGDisplayModeRef mode) { - if (mode) { - u32 refreshRate = (u32)CGDisplayModeGetRefreshRate(mode); - if (refreshRate != 0) return refreshRate; - } - -#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) { - 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_DEBUG_CTX(NULL, 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_DEBUG_CTX(NULL, 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_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) { - 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_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();"); - - 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_eventQueuePushEx(e.type = RGFW_focusIn; e._win = _RGFW.root); - _RGFW.root->_flags |= RGFW_windowFocus; - RGFW_focusCallback(_RGFW.root, 1); - - 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); - - RGFW_eventQueuePushEx(e.type = RGFW_focusOut; e._win = _RGFW.root); - RGFW_window_focusLost(_RGFW.root); - 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_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)); - 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_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; - - 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_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; - - 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_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); - - 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_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_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_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); - } - 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_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_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); 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_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; - 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_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; - - 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_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; - - 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.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); -} - -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_STRNCPY((char*)_RGFW.root->event.droppedFiles[index], file, RGFW_MAX_PATH - 1); - _RGFW.root->event.droppedFiles[index][RGFW_MAX_PATH - 1] = '\0'; -} - -#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) { -#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) { -#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); - RGFW_window_initOpenGL(win); - - #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; -} - -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); - 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_DEBUG_CTX(NULL, 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_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) { -#ifdef RGFW_OPENGL - return (RGFW_proc)emscripten_webgl_get_proc_address(procname); -#else - return NULL -#endif -} - -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/external/RGFW/RGFW.h b/src/external/RGFW/RGFW.h new file mode 100644 index 000000000..6e59c6ca0 --- /dev/null +++ b/src/external/RGFW/RGFW.h @@ -0,0 +1,15891 @@ +/* +* +* RGFW 2.0.0-dev + +* Copyright (C) 2022-26 Riley Mabb (@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_EGL - (optional) compile with OpenGL functions, allowing you to use to use EGL instead of the native OpenGL functions + #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 + #define RGFW_NATIVE - (optional) define native RGFW types that use native API structures + + #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 and don't use libShcore (win32) + #define RGFW_NO_XRANDR - do use XRandr (X11) + #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_NO_INFO - do not define the RGFW_info struct (without RGFW_IMPLEMENTATION) + #define RGFW_NO_GLXWINDOW - do not use GLXWindow + + #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", 100, 100, 500, 500, (u64)0); + RGFW_event event; + + RGFW_window_setExitKey(win, RGFW_escape); + RGFW_window_setIcon(win, icon, 3, 3, RGFW_formatRGBA8); + + while (RGFW_window_shouldClose(win) == RGFW_FALSE) { + while (RGFW_window_checkEvent(win, &event)) { + if (event.type == RGFW_quit) + break; + } + } + + 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 : Code review, helped with X11, MacOS and Windows support, Silicon, siliapp.h -> referencing + + stb : This project is heavily inspired by the stb single header files + + SDL, GLFW and other online resources : reference implementations + + contributors : (feel free to put yourself here if you contribute) + krisvers (@krisvers) -> code review + EimaMei (@SaCode) -> code review + Nycticebus (@Code-Nycticebus) -> bug fixes + Rob Rohan (@robrohan) -> 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) + @therealmarrakesh -> documentation + @DarekParodia -> code review (X11) (C++) + @NishiOwO -> fix BSD support, fix OSMesa example + @BaynariKattu -> code review and documentation + Miguel Pinto (@konopimi) -> code review, fix vulkan example + @m-doescode -> code review (wayland) + Robert Gonzalez (@uni-dos) -> code review (wayland) + @TheLastVoyager -> code review + @yehoravramenko -> code review (winapi) + @halocupcake -> code review (OpenGL) + @GideonSerf -> documentation + Alexandre Almeida (@M374LX) -> code review (keycodes) + Vũ Xuân Trường (@wanwanvxt) -> code review (winapi) + Lucas (@lightspeedlucas) -> code review (msvc++) + Jeffery Myers (@JeffM2501) -> code review (msvc) + Zeni (@zenitsuyo) -> documentation + TheYahton (@TheYahton) -> documentation + nonexistant_object (@DiarrheaMcgee) + AC Gaudette (@acgaudette) +*/ + +#if _MSC_VER + #pragma comment(lib, "gdi32") + #pragma comment(lib, "shell32") + #pragma comment(lib, "User32") + #pragma comment(lib, "Advapi32") + #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 + +#if defined(RGFW_EGL) && !defined(RGFW_OPENGL) + #define RGFW_OPENGL +#endif + +/* these OS macros look better & are standardized */ +/* plus it helps with cross-compiling */ + +#ifdef __EMSCRIPTEN__ + #define RGFW_WASM +#endif + +#if defined(RGFW_X11) && defined(__APPLE__) && !defined(RGFW_CUSTOM_BACKEND) + #define RGFW_MACOS_X11 + #define RGFW_UNIX +#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 +#endif +#if defined(RGFW_WAYLAND) + #define RGFW_DEBUG /* wayland will be in debug mode by default for now */ + #define RGFW_UNIX + #ifdef RGFW_OPENGL + #define RGFW_EGL + #endif + #ifdef RGFW_X11 + #define RGFW_DYNAMIC + #endif +#endif +#if (!defined(RGFW_WAYLAND) && !defined(RGFW_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 +#elif defined(__APPLE__) && !defined(RGFW_MACOS_X11) && !defined(RGFW_X11) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND) + #define RGFW_MACOS +#endif + +#ifndef RGFW_ASSERT + #include + #define RGFW_ASSERT assert +#endif + +#if !defined(__STDC_VERSION__) + #define RGFW_C89 +#endif + +#if !defined(RGFW_SNPRINTF) && (defined(RGFW_X11) || defined(RGFW_WAYLAND)) + + /* required for X11 errors */ + #include + + #ifdef RGFW_C89 + #include + static int RGFW_c89_snprintf(char *dst, size_t size, const char *format, ...) { + va_list args; + size_t count = 0; + va_start(args, format); + count = (size_t)vsprintf(dst, format, args); + RGFW_ASSERT(count + 1 < size && "Buffer overflow"); + va_end(args); + return (int)count; + } + #define RGFW_SNPRINTF RGFW_c89_snprintf + #else + #define RGFW_SNPRINTF snprintf + #endif /*RGFW_C89*/ +#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_ROUNDF + #define RGFW_ROUNDF(x) (float)((i32)((x) + ((x) < 0.0f ? -0.5f : 0.5f))) +#endif + +#ifndef RGFW_MIN + #define RGFW_MIN(x, y) ((x < y) ? x : y) +#endif + +#ifndef RGFW_ALLOC + #include + #define RGFW_ALLOC malloc + #define RGFW_FREE free +#endif + +#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 + #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 !defined(RGFW_PRINTF) && ( defined(RGFW_DEBUG) || defined(RGFW_WAYLAND) ) + /* required when using RGFW_DEBUG */ + #include + #define RGFW_PRINTF printf +#endif + +#ifndef RGFW_MAX_PATH + #define RGFW_MAX_PATH 260 /* max length of a path (for drag andn drop) */ +#endif +#ifndef RGFW_MAX_DROPS + #define RGFW_MAX_DROPS 260 /* max items you can drop at once */ +#endif + +#ifndef RGFW_MAX_EVENTS + #define RGFW_MAX_EVENTS 32 +#endif + +#ifndef RGFW_MAX_MONITORS + #define RGFW_MAX_MONITORS 6 +#endif + +#ifndef RGFW_COCOA_FRAME_NAME + #define RGFW_COCOA_FRAME_NAME NULL +#endif + +#ifdef RGFW_WIN95 /* for windows 95 testing (not that it really works) */ + #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 + #ifndef RGFWDEF + #define RGFWDEF + #endif +#endif + +#ifndef RGFWDEF + #ifdef RGFW_C89 + #define RGFWDEF __inline + #else + #define RGFWDEF inline + #endif +#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 + +typedef ptrdiff_t RGFW_ssize_t; + +#ifndef RGFW_BOOL_DEFINED + #define RGFW_BOOL_DEFINED + typedef u8 RGFW_bool; +#endif + +#define RGFW_BOOL(x) (RGFW_bool)((x) != 0) /* force a value to be 0 or 1 */ +#define RGFW_TRUE (RGFW_bool)1 +#define RGFW_FALSE (RGFW_bool)0 + +#define RGFW_ENUM(type, name) type name; enum +#define RGFW_BIT(x) (1 << (x)) + +#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 + +#endif + + +/*! @brief The stucture that contains information about the current RGFW instance */ +typedef struct RGFW_info RGFW_info; + +/*! @brief The window stucture for interfacing with the window */ +typedef struct RGFW_window RGFW_window; + +/*! @brief The source window stucture for interfacing with the underlying windowing API (e.g. winapi, wayland, cocoa, etc) */ +typedef struct RGFW_window_src RGFW_window_src; + +/*! @brief The color format for pixel data */ +typedef RGFW_ENUM(u8, RGFW_format) { + RGFW_formatRGB8 = 0, /*!< 8-bit RGB (3 channels) */ + RGFW_formatBGR8, /*!< 8-bit BGR (3 channels) */ + RGFW_formatRGBA8, /*!< 8-bit RGBA (4 channels) */ + RGFW_formatARGB8, /*!< 8-bit RGBA (4 channels) */ + RGFW_formatBGRA8, /*!< 8-bit BGRA (4 channels) */ + RGFW_formatABGR8, /*!< 8-bit BGRA (4 channels) */ + RGFW_formatCount +}; + +/*! @brief layout struct for mapping out format types */ +typedef struct RGFW_colorLayout { i32 r, g, b, a; u32 channels; } RGFW_colorLayout; + +/*! @brief function type converting raw image data between formats */ +typedef void (* RGFW_convertImageDataFunc)(u8* dest_data, u8* src_data, const RGFW_colorLayout* srcLayout, const RGFW_colorLayout* destLayout, size_t count); + +/*! @brief a stucture for interfacing with the underlying native image (e.g. XImage, HBITMAP, etc) */ +typedef struct RGFW_nativeImage RGFW_nativeImage; + +/*! @brief a stucture for interfacing with pixel data as a renderable surface */ +typedef struct RGFW_surface RGFW_surface; + +/*! @brief gamma struct for monitors */ +typedef struct RGFW_gammaRamp { + u16* red; /*!< array for the red channel */ + u16* green; /*!< array for the green channel */ + u16* blue; /*!< array for the blue channel */ + size_t count; /*! count of elements in each channel */ +} RGFW_gammaRamp; + +/*! @brief monitor mode data | can be changed by the user (with functions)*/ +typedef struct RGFW_monitorMode { + i32 w, h; /*!< monitor workarea size */ + float refreshRate; /*!< monitor refresh rate */ + u8 red, blue, green; /*!< sizeof rgb values */ + void* src; /*!< source API mode */ +} RGFW_monitorMode; + +/*! @brief structure for monitor node and source monitor data */ +typedef struct RGFW_monitorNode RGFW_monitorNode; + +/*! @brief 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; /*!< current mode of the monitor */ + void* userPtr; /*!< pointer for user data */ + RGFW_monitorNode* node; /*!< source node data of the monitor */ +} RGFW_monitor; + +/*! @brief what type of request you are making for the monitor */ +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 +}; + +/*! a raw pointer to the underlying mouse handle for setting and creating custom mouse icons */ +typedef void RGFW_mouse; + +/*! @brief RGFW's abstract keycodes */ +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_equal = '=', + RGFW_equals = RGFW_equal, + 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_enter = RGFW_return, + 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_F13, + RGFW_F14, + RGFW_F15, + RGFW_F16, + RGFW_F17, + RGFW_F18, + RGFW_F19, + RGFW_F20, + RGFW_F21, + RGFW_F22, + RGFW_F23, + RGFW_F24, + RGFW_F25, + 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_menu, + RGFW_end, + RGFW_home, + RGFW_pageUp, + RGFW_pageDown, + RGFW_numLock, + RGFW_kpSlash, + RGFW_kpMultiply, + RGFW_kpPlus, + RGFW_kpMinus, + RGFW_kpEqual, + RGFW_kpEquals = RGFW_kpEqual, + RGFW_kp1, + RGFW_kp2, + RGFW_kp3, + RGFW_kp4, + RGFW_kp5, + RGFW_kp6, + RGFW_kp7, + RGFW_kp8, + RGFW_kp9, + RGFW_kp0, + RGFW_kpPeriod, + RGFW_kpReturn, + RGFW_scrollLock, + RGFW_printScreen, + RGFW_pause, + RGFW_world1, + RGFW_world2, + RGFW_keyLast = 256 /* padding for alignment ~(175 by default) */ +}; + +/*! @brief abstract mouse button codes */ +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_mouseMisc1, RGFW_mouseMisc2, RGFW_mouseMisc3, RGFW_mouseMisc4, RGFW_mouseMisc5, + RGFW_mouseFinal +}; + +/*! abstract key modifier codes */ +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) +}; + +/*! @brief codes for the event types that can be sent */ +typedef RGFW_ENUM(u8, RGFW_eventType) { + RGFW_eventNone = 0, /*!< no event has been sent */ + RGFW_keyPressed, /*!< a key has been pressed */ + RGFW_keyReleased, /*!< a key has been released */ + RGFW_keyChar, /*!< keyboard character input event specifically for utf8 input */ + RGFW_mouseButtonPressed, /*!< a mouse button has been pressed (left,middle,right) */ + RGFW_mouseButtonReleased, /*!< a mouse button has been released (left,middle,right) */ + RGFW_mouseScroll, /*!< a mouse scroll event */ + 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_x, y + + RGFW_event.button.value holds which mouse button was pressed + */ + 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->x, win->y, win->w and win->h + */ + RGFW_quit, /*!< the user clicked the quit button */ + RGFW_dataDrop, /*!< a file has been dropped into the window */ + RGFW_dataDrag, /*!< the start of a drag and drop event, when the file is being dragged */ + /* drop data note + The x and y coords of the drop are stored in the vector RGFW_x, y + + RGFW_event.drop.count holds how many files were dropped + + This is also the size of the array which stores all the dropped file string, + RGFW_event.drop.files + */ + RGFW_windowMaximized, /*!< the window was maximized */ + RGFW_windowMinimized, /*!< the window was minimized */ + RGFW_windowRestored, /*!< the window was restored */ + RGFW_scaleUpdated, /*!< content scale factor changed */ + RGFW_monitorConnected, /*!< a monitor has been connected */ + RGFW_monitorDisconnected /*!< a monitor has been disconnected */ +}; + +/*! @brief flags for toggling wether or not an event should be processed */ +typedef RGFW_ENUM(u32, RGFW_eventFlag) { + RGFW_keyPressedFlag = RGFW_BIT(RGFW_keyPressed), + RGFW_keyReleasedFlag = RGFW_BIT(RGFW_keyReleased), + RGFW_keyCharFlag = RGFW_BIT(RGFW_keyChar), + RGFW_mouseScrollFlag = RGFW_BIT(RGFW_mouseScroll), + RGFW_mouseButtonPressedFlag = RGFW_BIT(RGFW_mouseButtonPressed), + RGFW_mouseButtonReleasedFlag = RGFW_BIT(RGFW_mouseButtonReleased), + RGFW_mousePosChangedFlag = RGFW_BIT(RGFW_mousePosChanged), + RGFW_mouseEnterFlag = RGFW_BIT(RGFW_mouseEnter), + RGFW_mouseLeaveFlag = RGFW_BIT(RGFW_mouseLeave), + RGFW_windowMovedFlag = RGFW_BIT(RGFW_windowMoved), + RGFW_windowResizedFlag = RGFW_BIT(RGFW_windowResized), + RGFW_focusInFlag = RGFW_BIT(RGFW_focusIn), + RGFW_focusOutFlag = RGFW_BIT(RGFW_focusOut), + RGFW_windowRefreshFlag = RGFW_BIT(RGFW_windowRefresh), + RGFW_windowMaximizedFlag = RGFW_BIT(RGFW_windowMaximized), + RGFW_windowMinimizedFlag = RGFW_BIT(RGFW_windowMinimized), + RGFW_windowRestoredFlag = RGFW_BIT(RGFW_windowRestored), + RGFW_scaleUpdatedFlag = RGFW_BIT(RGFW_scaleUpdated), + RGFW_quitFlag = RGFW_BIT(RGFW_quit), + RGFW_dataDropFlag = RGFW_BIT(RGFW_dataDrop), + RGFW_dataDragFlag = RGFW_BIT(RGFW_dataDrag), + RGFW_monitorConnectedFlag = RGFW_BIT(RGFW_monitorConnected), + RGFW_monitorDisconnectedFlag = RGFW_BIT(RGFW_monitorDisconnected), + + RGFW_keyEventsFlag = RGFW_keyPressedFlag | RGFW_keyReleasedFlag | RGFW_keyCharFlag, + RGFW_mouseEventsFlag = RGFW_mouseButtonPressedFlag | RGFW_mouseButtonReleasedFlag | RGFW_mousePosChangedFlag | RGFW_mouseEnterFlag | RGFW_mouseLeaveFlag | RGFW_mouseScrollFlag , + RGFW_windowEventsFlag = RGFW_windowMovedFlag | RGFW_windowResizedFlag | RGFW_windowRefreshFlag | RGFW_windowMaximizedFlag | RGFW_windowMinimizedFlag | RGFW_windowRestoredFlag | RGFW_scaleUpdatedFlag, + RGFW_focusEventsFlag = RGFW_focusInFlag | RGFW_focusOutFlag, + RGFW_dataDropEventsFlag = RGFW_dataDropFlag | RGFW_dataDragFlag, + RGFW_monitorEventsFlag = RGFW_monitorConnectedFlag | RGFW_monitorDisconnectedFlag, + RGFW_allEventFlags = RGFW_keyEventsFlag | RGFW_mouseEventsFlag | RGFW_windowEventsFlag | RGFW_focusEventsFlag | RGFW_dataDropEventsFlag | RGFW_quitFlag | RGFW_monitorEventsFlag +}; + +/*! Event structure(s) and union for checking/getting events */ + +/*! @brief common event data across all events */ +typedef struct RGFW_commonEvent { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_window* win; /*!< the window this event applies to (for event queue events) */ +} RGFW_commonEvent; + +/*! @brief event data for any mouse button event (press/release) */ +typedef struct RGFW_mouseButtonEvent { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_window* win; /*!< the window this event applies to (for event queue events) */ + RGFW_mouseButton value; /* !< which mouse button was pressed */ +} RGFW_mouseButtonEvent; + +/*! @brief event data for any mouse scroll event */ +typedef struct RGFW_mouseScrollEvent { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_window* win; /*!< the window this event applies to (for event queue events) */ + float x, y; /*!< the raw mouse scroll value */ +} RGFW_mouseScrollEvent; + +/*! @brief event data for any mouse position event (RGFW_mousePosChanged) */ +typedef struct RGFW_mousePosEvent { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_window* win; /*!< the window this event applies to (for event queue events) */ + i32 x, y; /*!< mouse x, y of event (or drop point) */ + float vecX, vecY; /*!< raw mouse movement */ +} RGFW_mousePosEvent; + +/*! @brief event data for a key press/release event */ +typedef struct RGFW_keyEvent { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_window* win; /*!< the window this event applies to (for event queue events) */ + RGFW_key value; /*!< the physical key of the event, refers to where key is physically */ + RGFW_bool repeat; /*!< key press event repeated (the key is being held) */ + RGFW_keymod mod; +} RGFW_keyEvent; + +/*! @brief event data for a key character event */ +typedef struct RGFW_keyCharEvent { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_window* win; /*!< the window this event applies to (for event queue events) */ + u32 value; /*!< the unicode value of the key */ +} RGFW_keyCharEvent; + +/*! @brief event data for any data drop event */ +typedef struct RGFW_dataDropEvent { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_window* win; /*!< the window this event applies to (for event queue events) */ + /* 260 max paths with a max length of 260 */ + char** files; /*!< dropped files */ + size_t count; /*!< how many files were dropped */ +} RGFW_dataDropEvent; + +/*! @brief event data for any data drag event */ +typedef struct RGFW_dataDragEvent { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_window* win; /*!< the window this event applies to (for event queue events) */ + i32 x, y; /*!< mouse x, y of event (or drop point) */ +} RGFW_dataDragEvent; + +/*! @brief event data for when the window scale (DPI) is updated */ +typedef struct RGFW_scaleUpdatedEvent { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_window* win; /*!< the window this event applies to (for event queue events) */ + float x, y; /*!< DPI scaling */ +} RGFW_scaleUpdatedEvent; + +/*! @brief event data for when a monitor is connected, disconnected or updated */ +typedef struct RGFW_monitorEvent { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_window* win; /*!< the window this event applies to (for event queue events) */ + const RGFW_monitor* monitor; /*!< the monitor that this event applies to */ +} RGFW_monitorEvent; + +/*! @brief union for all of the event stucture types */ +typedef union RGFW_event { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_commonEvent common; /*!< common event data (e.g.) type and win */ + RGFW_mouseButtonEvent button; /*!< data for a button press/release */ + RGFW_mouseScrollEvent scroll; /*!< data for a mouse scroll */ + RGFW_mousePosEvent mouse; /*!< data for mouse motion events */ + RGFW_keyEvent key; /*!< data for key press/release/hold events */ + RGFW_keyCharEvent keyChar; /*!< data for key character events */ + RGFW_dataDropEvent drop; /*!< dropping a file events */ + RGFW_dataDragEvent drag; /*!< data for dragging a file events */ + RGFW_scaleUpdatedEvent scale; /*!< data for dpi scaling update events */ + RGFW_monitorEvent monitor; /*!< data for monitor events */ +} RGFW_event; + +/*! + @!brief codes for for RGFW_the code is stupid and C++ waitForEvent + waitMS -> Allows the function to keep checking for events even after there are no more events + 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 +}; + +/*! @brief optional bitwise arguments for making a windows, these can be OR'd together */ +typedef RGFW_ENUM(u32, RGFW_windowFlags) { + RGFW_windowNoBorder = RGFW_BIT(0), /*!< the window doesn't have a border */ + RGFW_windowNoResize = RGFW_BIT(1), /*!< the window cannot be resized by the user */ + RGFW_windowAllowDND = RGFW_BIT(2), /*!< the window supports drag and drop */ + RGFW_windowHideMouse = RGFW_BIT(3), /*! the window should hide the mouse (can be toggled later on using `RGFW_window_showMouse`) */ + RGFW_windowFullscreen = RGFW_BIT(4), /*!< the window is fullscreen by default */ + RGFW_windowTransparent = RGFW_BIT(5), /*!< the window is transparent (only properly works on X11 and MacOS, although it's meant for for windows) */ + RGFW_windowCenter = RGFW_BIT(6), /*! center the window on the screen */ + RGFW_windowRawMouse = RGFW_BIT(7), /*!< use raw mouse mouse on window creation */ + RGFW_windowScaleToMonitor = RGFW_BIT(8), /*! scale the window to the screen */ + RGFW_windowHide = RGFW_BIT(9), /*! the window is hidden */ + RGFW_windowMaximize = RGFW_BIT(10), /*!< maximize the window on creation */ + RGFW_windowCenterCursor = RGFW_BIT(11), /*!< center the cursor to the window on creation */ + RGFW_windowFloating = RGFW_BIT(12), /*!< create a floating window */ + RGFW_windowFocusOnShow = RGFW_BIT(13), /*!< focus the window when it's shown */ + RGFW_windowMinimize = RGFW_BIT(14), /*!< focus the window when it's shown */ + RGFW_windowFocus = RGFW_BIT(15), /*!< if the window is in focus */ + RGFW_windowCaptureMouse = RGFW_BIT(16), /*!< capture the mouse mouse mouse on window creation */ + RGFW_windowOpenGL = RGFW_BIT(17), /*!< create an OpenGL context (you can also do this manually with RGFW_window_createContext_OpenGL) */ + RGFW_windowEGL = RGFW_BIT(18), /*!< create an EGL context (you can also do this manually with RGFW_window_createContext_EGL) */ + RGFW_noDeinitOnClose = RGFW_BIT(19), /*!< do not auto deinit RGFW if the window closes and this is the last window open */ + RGFW_windowedFullscreen = RGFW_windowNoBorder | RGFW_windowMaximize, + RGFW_windowCaptureRawMouse = RGFW_windowCaptureMouse | RGFW_windowRawMouse +}; + +/*! @brief the types of icon to set */ +typedef RGFW_ENUM(u8, RGFW_icon) { + RGFW_iconTaskbar = RGFW_BIT(0), + RGFW_iconWindow = RGFW_BIT(1), + RGFW_iconBoth = RGFW_iconTaskbar | RGFW_iconWindow +}; + +/*! @brief standard mouse icons */ +typedef RGFW_ENUM(u8, RGFW_mouseIcons) { + RGFW_mouseNormal = 0, + RGFW_mouseArrow, + RGFW_mouseIbeam, + RGFW_mouseText = RGFW_mouseIbeam, + RGFW_mouseCrosshair, + RGFW_mousePointingHand, + RGFW_mouseResizeEW, + RGFW_mouseResizeNS, + RGFW_mouseResizeNWSE, + RGFW_mouseResizeNESW, + RGFW_mouseResizeNW, + RGFW_mouseResizeN, + RGFW_mouseResizeNE, + RGFW_mouseResizeE, + RGFW_mouseResizeSE, + RGFW_mouseResizeS, + RGFW_mouseResizeSW, + RGFW_mouseResizeW, + RGFW_mouseResizeAll, + RGFW_mouseNotAllowed, + RGFW_mouseWait, + RGFW_mouseProgress, + RGFW_mouseIconCount, + RGFW_mouseIconFinal = 16 /* padding for alignment */ +}; + +/*! @breif flash request type */ +typedef RGFW_ENUM(u8, RGFW_flashRequest) { + RGFW_flashCancel = 0, + RGFW_flashBriefly, + RGFW_flashUntilFocused +}; + +/*! @brief the type of debug message */ +typedef RGFW_ENUM(u8, RGFW_debugType) { + RGFW_typeError = 0, RGFW_typeWarning, RGFW_typeInfo +}; + +/*! @brief error codes for known failure types */ +typedef RGFW_ENUM(u8, RGFW_errorCode) { + RGFW_noError = 0, /*!< no error */ + RGFW_errOutOfMemory, + RGFW_errOpenGLContext, RGFW_errEGLContext, /*!< error with the OpenGL context */ + RGFW_errWayland, RGFW_errX11, + RGFW_errDirectXContext, + RGFW_errIOKit, + RGFW_errClipboard, + RGFW_errFailedFuncLoad, + RGFW_errBuffer, + RGFW_errMetal, + RGFW_errPlatform, + RGFW_errEventQueue, + RGFW_infoWindow, RGFW_infoBuffer, RGFW_infoGlobal, RGFW_infoOpenGL, + RGFW_warningWayland, RGFW_warningOpenGL +}; + +/*! @brief callback function type for debug messags */ +typedef void (* RGFW_debugfunc)(RGFW_debugType type, RGFW_errorCode err, const char* msg); + +/*! @brief RGFW_windowMoved, the window and its new rect value */ +typedef void (* RGFW_windowMovedfunc)(RGFW_window* win, i32 x, i32 y); +/*! @brief RGFW_windowResized, the window and its new rect value */ +typedef void (* RGFW_windowResizedfunc)(RGFW_window* win, i32 w, i32 h); +/*! @brief RGFW_windowRestored, the window and its new rect value */ +typedef void (* RGFW_windowRestoredfunc)(RGFW_window* win, i32 x, i32 y, i32 w, i32 h); +/*! @brief RGFW_windowMaximized, the window and its new rect value */ +typedef void (* RGFW_windowMaximizedfunc)(RGFW_window* win, i32 x, i32 y, i32 w, i32 h); +/*! @brief RGFW_windowMinimized, the window and its new rect value */ +typedef void (* RGFW_windowMinimizedfunc)(RGFW_window* win); +/*! @brief RGFW_quit, the window that was closed */ +typedef void (* RGFW_windowQuitfunc)(RGFW_window* win); +/*! @brief 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); +/*! @brief 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, i32 x, i32 y, RGFW_bool status); +/*! @brief RGFW_mousePosChanged, the window that the move happened on, and the new point of the mouse */ +typedef void (* RGFW_mousePosfunc)(RGFW_window* win, i32 x, i32 y, float vecX, float vecY); +/*! @brief RGFW_dataDrag, the window, the point of the drop on the windows */ +typedef void (* RGFW_dataDragfunc)(RGFW_window* win, i32 x, i32 y); +/*! @brief RGFW_windowRefresh, the window that needs to be refreshed */ +typedef void (* RGFW_windowRefreshfunc)(RGFW_window* win); +/*! @brief RGFW_keyChar, the window that got the event, the unicode key value */ +typedef void (* RGFW_keyCharfunc)(RGFW_window* win, u32 codepoint); +/*! @brief 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) */ + +/*! @brief 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, RGFW_keymod mod, RGFW_bool repeat, RGFW_bool pressed); +/*! @brief 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, RGFW_bool pressed); +/*! @brief RGFW_mouseScroll, the window that got the event, the x scroll value, the y scroll value */ +typedef void (* RGFW_mouseScrollfunc)(RGFW_window* win, float x, float y); +/*! @brief RGFW_dataDrop the window that had the drop, the drop data and the number of files dropped */ +typedef void (* RGFW_dataDropfunc)(RGFW_window* win, char** files, size_t count); +/*! @brief RGFW_scaleUpdated, the window the event was sent to, content scaleX, content scaleY */ +typedef void (* RGFW_scaleUpdatedfunc)(RGFW_window* win, float scaleX, float scaleY); +/*! @brief RGFW_monitorConnected / RGFW_monitorDisconnected, there was a monitor connected/disconnected */ +typedef void (* RGFW_monitorfunc)(RGFW_window* win, const RGFW_monitor* monitor, RGFW_bool connected); + +/*! @brief function pointer equivalent of void* */ +typedef void (*RGFW_proc)(void); + +#if defined(RGFW_OPENGL) + +/*! @brief abstract structure for interfacing with the underlying OpenGL API */ +typedef struct RGFW_glContext RGFW_glContext; + +/*! @brief abstract structure for interfacing with the underlying EGL API */ +typedef struct RGFW_eglContext RGFW_eglContext; + +/*! values for the releaseBehavior hint */ +typedef RGFW_ENUM(i32, RGFW_glReleaseBehavior) { + RGFW_glReleaseFlush = 0, /*!< flush the pipeline will be flushed when the context is release */ + RGFW_glReleaseNone /*!< do nothing on release */ +}; + +/*! values for the profile hint */ +typedef RGFW_ENUM(i32, RGFW_glProfile) { + RGFW_glCore = 0, /*!< the core OpenGL version, e.g. just support for that version */ + RGFW_glForwardCompatibility, /*!< only compatibility for newer versions of OpenGL as well as the requested version */ + RGFW_glCompatibility, /*!< allow compatibility for older versions of OpenGL as well as the requested version */ + RGFW_glES /*!< use OpenGL ES */ +}; + +/*! values for the renderer hint */ +typedef RGFW_ENUM(i32, RGFW_glRenderer) { + RGFW_glAccelerated = 0, /*!< hardware accelerated (GPU) */ + RGFW_glSoftware /*!< software rendered (CPU) */ +}; + +/*! OpenGL initalization hints */ +typedef struct RGFW_glHints { + i32 stencil; /*!< set stencil buffer bit size (0 by default) */ + i32 samples; /*!< set number of sample buffers (0 by default) */ + i32 stereo; /*!< hint the context to use stereoscopic frame buffers for 3D (false by default) */ + i32 auxBuffers; /*!< number of aux buffers (0 by default) */ + i32 doubleBuffer; /*!< request double buffering (true by default) */ + i32 red, green, blue, alpha; /*!< set color bit sizes (all 8 by default) */ + i32 depth; /*!< set depth buffer bit size (24 by default) */ + i32 accumRed, accumGreen, accumBlue, accumAlpha; /*!< set accumulated RGBA bit sizes (all 0 by default) */ + RGFW_bool sRGB; /*!< request sRGA format (false by default) */ + RGFW_bool robustness; /*!< request a "robust" (as in memory-safe) context (false by default). For more information check the overview section: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_robustness.txt */ + RGFW_bool debug; /*!< request OpenGL debugging (false by default). */ + RGFW_bool noError; /*!< request no OpenGL errors (false by default). This causes OpenGL errors to be undefined behavior. For more information check the overview section: https://registry.khronos.org/OpenGL/extensions/KHR/KHR_no_error.txt */ + RGFW_glReleaseBehavior releaseBehavior; /*!< hint how the OpenGL driver should behave when changing contexts (RGFW_glReleaseNone by default). For more information check the overview section: https://registry.khronos.org/OpenGL/extensions/KHR/KHR_context_flush_control.txt */ + RGFW_glProfile profile; /*!< set OpenGL API profile (RGFW_glCore by default) */ + i32 major, minor; /*!< set the OpenGL API profile version (by default RGFW_glMajor is 1, RGFW_glMinor is 0) */ + RGFW_glContext* share; /*!< Share this OpenGL context with newly created OpenGL contexts; defaults to NULL. */ + RGFW_eglContext* shareEGL; /*!< Share this EGL context with newly created OpenGL contexts; defaults to NULL. */ + RGFW_glRenderer renderer; /*!< renderer to use e.g. accelerated or software defaults to accelerated */ +} RGFW_glHints; + +#endif + +/**! + * @brief Allocates memory using the allocator defined by RGFW_ALLOC at compile time. + * @param size The size (in bytes) of the memory block to allocate. + * @return A pointer to the allocated memory block. +*/ +RGFWDEF void* RGFW_alloc(size_t size); + +/**! + * @brief Frees memory using the deallocator defined by RGFW_FREE at compile time. + * @param ptr A pointer to the memory block to free. +*/ +RGFWDEF void RGFW_free(void* ptr); + +/**! + * @brief Returns the size (in bytes) of the RGFW_window structure. + * @return The size of the RGFW_window structure. +*/ +RGFWDEF size_t RGFW_sizeofWindow(void); + +/**! + * @brief Returns the size (in bytes) of the RGFW_window_src structure. + * @return The size of the RGFW_window_src structure. +*/ +RGFWDEF size_t RGFW_sizeofWindowSrc(void); + +/**! + * @brief (Unix) Toggles the use of Wayland. + * This is enabled by default when compiled with `RGFW_WAYLAND`. + * If not using `RGFW_WAYLAND`, Wayland functions are not exposed. + * This function can be used to force the use of XWayland. + * @param wayland A boolean value indicating whether to use Wayland (true) or not (false). +*/ +RGFWDEF void RGFW_useWayland(RGFW_bool wayland); + +/**! + * @brief Checks if Wayland is currently being used. + * @return RGFW_TRUE if using Wayland, RGFW_FALSE otherwise. +*/ +RGFWDEF RGFW_bool RGFW_usingWayland(void); + +/**! + * @brief Retrieves the current Cocoa layer (macOS only). + * @return A pointer to the Cocoa layer, or NULL if the platform is not in use. +*/ +RGFWDEF void* RGFW_getLayer_OSX(void); + +/**! + * @brief Retrieves the current X11 display connection. + * @return A pointer to the X11 display, or NULL if the platform is not in use. +*/ +RGFWDEF void* RGFW_getDisplay_X11(void); + +/**! + * @brief Retrieves the current Wayland display connection. + * @return A pointer to the Wayland display (`struct wl_display*`), or NULL if the platform is not in use. +*/ +RGFWDEF struct wl_display* RGFW_getDisplay_Wayland(void); + +/**! + * @brief Sets the class name for X11 and WinAPI windows. + * Windows with the same class name will be grouped by the window manager. + * By default, the class name matches the root window’s name. + * @param name The class name to assign. +*/ +RGFWDEF void RGFW_setClassName(const char* name); + +/**! + * @brief Sets the X11 instance name. + * By default, the window name will be used as the instance name. + * @param name The X11 instance name to set. +*/ +RGFWDEF void RGFW_setXInstName(const char* name); + +/**! + * @brief (macOS only) Changes the current working directory to the application’s resource folder. +*/ +RGFWDEF void RGFW_moveToMacOSResourceDir(void); + +/*! copy image to another image, respecting each image's format */ +RGFWDEF void RGFW_copyImageData(u8* dest_data, i32 w, i32 h, RGFW_format dest_format, u8* src_data, RGFW_format src_format, RGFW_convertImageDataFunc func); + +/**! + * @brief Returns the size (in bytes) of the RGFW_nativeImage structure. + * @return The size of the RGFW_nativeImage structure. +*/ +RGFWDEF size_t RGFW_sizeofNativeImage(void); + +/**! + * @brief Returns the size (in bytes) of the RGFW_surface structure. + * @return The size of the RGFW_surface structure. +*/ +RGFWDEF size_t RGFW_sizeofSurface(void); + +/**! + * @brief Returns the native format type for the system + * @return the native format type for the system as a RGFW_format enum value +*/ +RGFWDEF RGFW_format RGFW_nativeFormat(void); + +/**! + * @brief Creates a new surface from raw pixel data. + * @param data A pointer to the pixel data buffer. + * @param w The width of the surface in pixels. + * @param h The height of the surface in pixels. + * @param format The pixel format of the data. + * @return A pointer to the newly created RGFW_surface. + * + * NOTE: when you create a surface using RGFW_createSurface / ptr, on X11 it uses the root window's visual + * this means it may fail to render on any other window if the visual does not match + * RGFW_window_createSurface and RGFW_window_createSurfacePtr exist only for X11 to address this issues + * Of course, you can also manually set the root window with RGFW_setRootWindow +*/ +RGFWDEF RGFW_surface* RGFW_createSurface(u8* data, i32 w, i32 h, RGFW_format format); + +/**! + * @brief Creates a surface using a pre-allocated RGFW_surface structure. + * @param data A pointer to the pixel data buffer. + * @param w The width of the surface in pixels. + * @param h The height of the surface in pixels. + * @param format The pixel format of the data. + * @param surface A pointer to a pre-allocated RGFW_surface structure. + * @return RGFW_TRUE if successful, RGFW_FALSE otherwise. +*/ +RGFWDEF RGFW_bool RGFW_createSurfacePtr(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface); + +/**! + * @brief Retrieves the native image associated with a surface. + * @param surface A pointer to the RGFW_surface. + * @return A pointer to the native RGFW_nativeImage associated with the surface. +*/ +RGFWDEF RGFW_nativeImage* RGFW_surface_getNativeImage(RGFW_surface* surface); + +/**! + * @brief Frees the surface pointer and any buffers used for software rendering. + * @param surface A pointer to the RGFW_surface to free. +*/ +RGFWDEF void RGFW_surface_free(RGFW_surface* surface); + +/**! + * @brief Frees only the internal buffers used for software rendering, leaving the surface struct intact. + * @param surface A pointer to the RGFW_surface whose buffers should be freed. +*/ +RGFWDEF void RGFW_surface_freePtr(RGFW_surface* surface); + + +/**! + * @brief Loads a mouse icon from bitmap data (similar to RGFW_window_setIcon). + * @param data A pointer to the bitmap pixel data. + * @param w The width of the mouse icon in pixels. + * @param h The height of the mouse icon in pixels. + * @param format The pixel format of the data. + * @return A pointer to the newly loaded RGFW_mouse structure. + * + * @note The icon is not resized by default. +*/ +RGFWDEF RGFW_mouse* RGFW_loadMouse(u8* data, i32 w, i32 h, RGFW_format format); + +/**! + * @brief Frees the data associated with an RGFW_mouse structure. + * @param mouse A pointer to the RGFW_mouse to free. +*/ +RGFWDEF void RGFW_freeMouse(RGFW_mouse* mouse); + +/**! + * @brief Get an allocated array of the supported modes of a monitor + * @param monitor the source monitor object + * @param count [OUTPUT] the count of the array + * @return the allocated array of supported modes +*/ +RGFWDEF RGFW_monitorMode* RGFW_monitor_getModes(RGFW_monitor* monitor, size_t* count); + +/**! + * @brief Free RGFW allocated modes array + * @param monitor the source monitor object + * @param modes a pointer to an allocated array of modes +*/ +RGFWDEF void RGFW_freeModes(RGFW_monitorMode* modes); + +/**! + * @brief Get the supported modes of a monitor using a pre-allocated array + * @param monitor the source monitor object + * @param modes [OUTPUT] a pointer to an allocated array of modes + * @return the number of (possible) modes, if [modes == NULL] the possible nodes *may* be less than the actual modes +*/ +RGFWDEF size_t RGFW_monitor_getModesPtr(RGFW_monitor* monitor, RGFW_monitorMode** modes); + +/**! + * @brief find the closest monitor mode based on the give mode with size being the highest priority, format being the second and refreshrate being the third. + * @param monitor the source monitor object + * @param mode user filled mode to use for comparison + * @param modes [OUTPUT] a pointer to be filled with the output closest monitor + * @return returns true if a suitable monitor was found and false if no suitable monitor was found at all +*/ + +RGFWDEF RGFW_bool RGFW_monitor_findClosestMode(RGFW_monitor* monitor, RGFW_monitorMode* mode, RGFW_monitorMode* closest); + +/**! + * @brief Get the allocated gamma ramp + * @param monitor the source monitor object +*/ +RGFWDEF RGFW_gammaRamp* RGFW_monitor_getGammaRamp(RGFW_monitor* monitor); + +/**! + * @brief Free the gamma ramp allocated by RGFW + * @param allocated gamma ramp +*/ +RGFWDEF void RGFW_freeGammaRamp(RGFW_gammaRamp* ramp); + +/**! + * @brief Get the monitor's gamma ramp using a pre-allocated struct with allocated data + * @param monitor the source monitor object + * @param ramp [OUTPUT] a pointer to an allocated gamma ramp (can be NULL to just get the count) + * @return the count of the gamma ramp +*/ +RGFWDEF size_t RGFW_monitor_getGammaRampPtr(RGFW_monitor* monitor, RGFW_gammaRamp* ramp); + +/**! + * @brief Set the monitor's gamma ramp using a pre-allocated struct with allocated data + * @param monitor the source monitor object + * @param ramp a pointer to an allocated gamma ramp + * @return a bool if the function was successful +*/ +RGFWDEF RGFW_bool RGFW_monitor_setGammaRamp(RGFW_monitor* monitor, RGFW_gammaRamp* ramp); + +/**! + * @brief Create and set the monitor's gamma ramp with a base gamma exponent + * @param monitor the source monitor object + * @param the gamma exponent + * @return a bool if the function was successful +*/ +RGFWDEF RGFW_bool RGFW_monitor_setGamma(RGFW_monitor* monitor, float gamma); + +/**! + * @brief Create and set the monitor's gamma ramp with a base gamma exponent using a pre-allocated array + * @param monitor the source monitor object + * @param gamma the gamma exponent + * @param pre-allocated gammaramp channel + * @param count the length of the allocated channel array + * @return a bool if the function was successful +*/ +RGFWDEF RGFW_bool RGFW_monitor_setGammaPtr(RGFW_monitor* monitor, float gamma, u16* ptr, size_t count); + +/**! + * @brief Get the workarea of a monitor, meaning the parts not occupied by OS graphics (i.e. the taskbar) + * @param monitor the source monitor object + * @param x [OUTPUT] the x pos of the workarea + * @param y [OUTPUT] the y pos of the workarea + * @param w [OUTPUT] the width of the workarea + * @param h [OUTPUT] the height of the workarea + * @return a bool if the function was successful +*/ +RGFWDEF RGFW_bool RGFW_monitor_getWorkarea(RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height); + +/**! + * @brief Get the position of a monitor (the same as monitor.x / monitor.y) + * @param x [OUTPUT] the x position of the monitor + * @param y [OUTPUT] the y position of the monitor + * @return a bool if the function was successful +*/ +RGFWDEF RGFW_bool RGFW_monitor_getPosition(RGFW_monitor* monitor, i32* x, i32* y); + +/**! + * @brief Get the name of a monitor (the same as monitor.name) + * @return the cstring of the monitor's name +*/ +RGFWDEF const char* RGFW_monitor_getName(RGFW_monitor* monitor); + +/**! + * @brief Get the scale of a monitor (the same as monitor.scaleX / monitor.scaleY) + * @param monitor the source monitor object + * @param x [OUTPUT] the x scale of the monitor + * @param y [OUTPUT] the y scale of the monitor + * @return a bool if the function was successful +*/ +RGFWDEF RGFW_bool RGFW_monitor_getScale(RGFW_monitor* monitor, float* x, float* y); + +/**! + * @brief Get the physical size of a monitor (the same as monitor.physW / monitor.physH) + * @param monitor the source monitor object + * @param w [OUTPUT] the physical width of the monitor + * @param h [OUTPUT] the physical height of the monitor + * @return a bool if the function was successful +*/ +RGFWDEF RGFW_bool RGFW_monitor_getPhysicalSize(RGFW_monitor* monitor, float* w, float* h); + +/**! + * @brief Set the user pointer of a monitor (the same as monitor.userPtr = userPtr) + * @param monitor the source monitor object + * @param userPtr the new user pointer for the monitor +*/ +RGFWDEF void RGFW_monitor_setUserPtr(RGFW_monitor* monitor, void* userPtr); + +/**! + * @brief Get the user pointer of a monitor (the same as monitor.userPtr) + * @param monitor the source monitor object + * @return the user pointer of the monitor +*/ +RGFWDEF void* RGFW_monitor_getUserPtr(RGFW_monitor* monitor); + +/**! + * @brief Get the mode of a monitor (the same as monitor.mode) + * @param monitor the source monitor object + * @param mode [OUTPUT] current mode the monitor + * @return a bool if the function was successful +*/ +RGFWDEF RGFW_bool RGFW_monitor_getMode(RGFW_monitor* monitor, RGFW_monitorMode* mode); + +/**! + * @brief Poll and check for monitor updates (this is called internally on monitor update events and RGFW_init) +*/ +RGFWDEF void RGFW_pollMonitors(void); + +/**! + * @brief Retrieves an array of all available monitors. + * @param len [OUTPUT] A pointer to store the number of monitors found (maximum of RGFW_MAX_MONITORS [6 by default]). + * @return An array of pointers to RGFW_monitor structures. +*/ +RGFWDEF RGFW_monitor** RGFW_getMonitors(size_t* len); + +/**! + * @brief Retrieves the primary monitor. + * @return A pointer to the RGFW_monitor structure representing the primary monitor. +*/ +RGFWDEF RGFW_monitor* RGFW_getPrimaryMonitor(void); + +/**! + * @brief Requests the display mode for a monitor (based on what attributes are directly requested). + * @param mon The monitor to apply the mode change to. + * @param mode The desired RGFW_monitorMode. + * @param request The RGFW_modeRequest describing how to handle the mode change. + * @return RGFW_TRUE if the mode was successfully applied, otherwise RGFW_FALSE. +*/ +RGFWDEF RGFW_bool RGFW_monitor_requestMode(RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request); + +/**! + * @brief Sets a specific display mode for a monitor directly. + * @param mon The monitor to apply the mode change to. + * @param mode The desired RGFW_monitorMode. + * @param request The RGFW_modeRequest describing how to handle the mode change. + * @return RGFW_TRUE if the mode was successfully applied, otherwise RGFW_FALSE. +*/ +RGFWDEF RGFW_bool RGFW_monitor_setMode(RGFW_monitor* mon, RGFW_monitorMode* mode); + +/**! + * @brief Compares two monitor modes to check if they are equivalent. + * @param mon The first monitor mode. + * @param mon2 The second monitor mode. + * @param request The RGFW_modeRequest that defines the comparison parameters. + * @return RGFW_TRUE if both modes are equivalent, otherwise RGFW_FALSE. +*/ +RGFWDEF RGFW_bool RGFW_monitorModeCompare(RGFW_monitorMode* mon, RGFW_monitorMode* mon2, RGFW_modeRequest request); + +/**! + * @brief Scales a monitor’s mode to match a window’s size. + * @param mon The monitor to be scaled. + * @param win The window whose size should be used as a reference. + * @return RGFW_TRUE if the scaling was successful, otherwise RGFW_FALSE. +*/ +RGFWDEF RGFW_bool RGFW_monitor_scaleToWindow(RGFW_monitor* mon, struct RGFW_window* win); + + /**! + * @brief set (enable or disable) raw mouse mode globally + * @param the boolean state of raw mouse mode + * +*/ +RGFWDEF void RGFW_setRawMouseMode(RGFW_bool state); + +/**! +* @brief sleep until RGFW gets an event or the timer ends (defined by OS) +* @param waitMS how long to wait for the next event (in miliseconds) +*/ +RGFWDEF void RGFW_waitForEvent(i32 waitMS); + +/**! +* @brief Set if events should be queued or not (enabled by default if the event queue is checked) +* @param queue boolean value if RGFW should queue events or not +*/ +RGFWDEF void RGFW_setQueueEvents(RGFW_bool queue); + +/**! +* @brief check all the events until there are none left and updates window structure attributes +*/ +RGFWDEF void RGFW_pollEvents(void); + +/**! +* @brief check all the events until there are none left and updates window structure attributes +* queues events if the queue is checked and/or requested +*/ +RGFWDEF void RGFW_stopCheckEvents(void); + +/**! + * @brief polls and pops the next event + * @param event [OUTPUT] a pointer to store the retrieved event + * @return RGFW_TRUE if an event was found, RGFW_FALSE otherwise + * + * NOTE: Using this function without a loop may cause event lag. + * For multi-threaded systems, use RGFW_pollEvents combined with RGFW_checkQueuedEvent. + * + * Example: + * RGFW_event event; + * while (RGFW_checkEvent(win, &event)) { + * // handle event + * } +*/ +RGFWDEF RGFW_bool RGFW_checkEvent(RGFW_event* event); + +/**! + * @brief pops the first queued event + * @param event [OUTPUT] a pointer to store the retrieved event + * @return RGFW_TRUE if an event was found, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_checkQueuedEvent(RGFW_event* event); + +/** * @defgroup Input +* @{ */ + +/**! + * @brief returns true if the key is pressed during the current frame + * @param key the key code of the key you want to check + * @return The boolean value if the key is pressed or not +*/ +RGFWDEF RGFW_bool RGFW_isKeyPressed(RGFW_key key); + +/**! + * @brief returns true if the key was released during the current frame + * @param key the key code of the key you want to check + * @return The boolean value if the key is released or not +*/ +RGFWDEF RGFW_bool RGFW_isKeyReleased(RGFW_key key); + +/**! + * @brief returns true if the key is down + * @param key the key code of the key you want to check + * @return The boolean value if the key is down or not +*/ +RGFWDEF RGFW_bool RGFW_isKeyDown(RGFW_key key); + +/**! + * @brief returns true if the mouse button is pressed during the current frame + * @param button the mouse button code of the button you want to check + * @return The boolean value if the button is pressed or not +*/ +RGFWDEF RGFW_bool RGFW_isMousePressed(RGFW_mouseButton button); + +/**! + * @brief returns true if the mouse button is released during the current frame + * @param button the mouse button code of the button you want to check + * @return The boolean value if the button is released or not +*/ +RGFWDEF RGFW_bool RGFW_isMouseReleased(RGFW_mouseButton button); + +/**! + * @brief returns true if the mouse button is down + * @param button the mouse button code of the button you want to check + * @return The boolean value if the button is down or not +*/ +RGFWDEF RGFW_bool RGFW_isMouseDown(RGFW_mouseButton button); + +/**! + * @brief outputs the current x, y position of the mouse + * @param X [OUTPUT] a pointer for the output X value + * @param Y [OUTPUT] a pointer for the output Y value +*/ +RGFWDEF void RGFW_getMouseScroll(float* x, float* y); + +/**! + * @brief outputs the current x, y movement vector of the mouse + * @param X [OUTPUT] a pointer for the output X vector value + * @param Y [OUTPUT] a pointer for the output Y vector value +*/ +RGFWDEF void RGFW_getMouseVector(float* x, float* y); +/** @} */ + +/**! + * @brief creates a new window + * @param name the requested title of the window + * @param x the requested x position of the window + * @param y the requested y position of the window + * @param w the requested width of the window + * @param h the requested height of the window + * @param flags extra arguments ((u32)0 means no flags used) + * @return A pointer to the newly created window structure + * + * 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, i32 x, i32 y, i32 w, i32 h, RGFW_windowFlags flags); + +/**! + * @brief creates a new window using a pre-allocated window structure + * @param name the requested title of the window + * @param x the requested x position of the window + * @param y the requested y position of the window + * @param w the requested width of the window + * @param h the requested height of the window + * @param flags extra arguments ((u32)0 means no flags used) + * @param win a pointer the pre-allocated window structure + * @return A pointer to the newly created window structure +*/ +RGFWDEF RGFW_window* RGFW_createWindowPtr(const char* name, i32 x, i32 y, i32 w, i32 h, RGFW_windowFlags flags, RGFW_window* win); + +/**! + * @brief creates a new surface structure + * @param win the source window of the surface + * @param data a pointer to the raw data of the structure (you allocate this) + * @param w the width the data + * @param h the height of the data + * @return A pointer to the newly created surface structure + * + * NOTE: when you create a surface using RGFW_createSurface / ptr, on X11 it uses the root window's visual + * this means it may fail to render on any other window if the visual does not match + * RGFW_window_createSurface and RGFW_window_createSurfacePtr exist only for X11 to address this issues + * Of course, you can also manually set the root window with RGFW_setRootWindow + */ +RGFWDEF RGFW_surface* RGFW_window_createSurface(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format); + +/**! + * @brief creates a new surface structure using a pre-allocated surface structure + * @param win the source window of the surface + * @param data a pointer to the raw data of the structure (you allocate this) + * @param w the width the data + * @param h the height of the data + * @param a pointer to the pre-allocated surface structure + * @return a bool if the creation was successful or not +*/ +RGFWDEF RGFW_bool RGFW_window_createSurfacePtr(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface); + +/**! + * @brief set the function/callback used for converting surface data between formats + * @param surface a pointer to the surface + * @param a function pointer for the function to use [if NULL the default function is used] +*/ +RGFWDEF void RGFW_surface_setConvertFunc(RGFW_surface* surface, RGFW_convertImageDataFunc func); + +/**! + * @brief blits a surface stucture to the window + * @param win a pointer the window to blit to + * @param surface a pointer to the surface +*/ +RGFWDEF void RGFW_window_blitSurface(RGFW_window* win, RGFW_surface* surface); + +/**! + * @brief gets the position of the window | with RGFW_window.x and window.y + * @param x [OUTPUT] the x position of the window + * @param y [OUTPUT] the y position of the window + * @return a bool if the function was successful +*/ +RGFWDEF RGFW_bool RGFW_window_getPosition(RGFW_window* win, i32* x, i32* y); /*!< */ + +/**! + * @brief gets the size of the window | with RGFW_window.w and window.h + * @param win a pointer to the window + * @param w [OUTPUT] the width of the window + * @param h [OUTPUT] the height of the window + * @return a bool if the function was successful +*/ +RGFWDEF RGFW_bool RGFW_window_getSize(RGFW_window* win, i32* w, i32* h); + +/**! + * @brief gets the size of the window in exact pixels + * @param win a pointer to the window + * @param w [OUTPUT] the width of the window + * @param h [OUTPUT] the height of the window + * @return a bool if the function was successful +*/ +RGFWDEF RGFW_bool RGFW_window_getSizeInPixels(RGFW_window* win, i32* w, i32* h); + +/**! + * @brief gets the flags of the window | returns RGFW_window._flags + * @param win a pointer to the window + * @return the window flags +*/ +RGFWDEF u32 RGFW_window_getFlags(RGFW_window* win); + +/**! + * @brief returns the exit key assigned to the window + * @param win a pointer to the target window + * @return The key code assigned as the exit key +*/ +RGFWDEF RGFW_key RGFW_window_getExitKey(RGFW_window* win); + +/**! + * @brief sets the exit key for the window + * @param win a pointer to the target window + * @param key the key code to assign as the exit key +*/ +RGFWDEF void RGFW_window_setExitKey(RGFW_window* win, RGFW_key key); + +/**! + * @brief sets the types of events you want the window to receive + * @param win a pointer to the target window + * @param events the event flags to enable (use RGFW_allEventFlags for all) +*/ +RGFWDEF void RGFW_window_setEnabledEvents(RGFW_window* win, RGFW_eventFlag events); + +/**! + * @brief gets the currently enabled events for the window + * @param win a pointer to the target window + * @return The enabled event flags for the window +*/ +RGFWDEF RGFW_eventFlag RGFW_window_getEnabledEvents(RGFW_window* win); + +/**! + * @brief enables all events and disables selected ones + * @param win a pointer to the target window + * @param events the event flags to disable +*/ +RGFWDEF void RGFW_window_setDisabledEvents(RGFW_window* win, RGFW_eventFlag events); + +/**! + * @brief directly enables or disables a specific event or group of events + * @param win a pointer to the target window + * @param event the event flag or group of flags to modify + * @param state RGFW_TRUE to enable, RGFW_FALSE to disable +*/ +RGFWDEF void RGFW_window_setEventState(RGFW_window* win, RGFW_eventFlag event, RGFW_bool state); + +/**! + * @brief gets the user pointer associated with the window + * @param win a pointer to the target window + * @return The user-defined pointer stored in the window +*/ +RGFWDEF void* RGFW_window_getUserPtr(RGFW_window* win); + +/**! + * @brief sets a user pointer for the window + * @param win a pointer to the target window + * @param ptr a pointer to associate with the window +*/ +RGFWDEF void RGFW_window_setUserPtr(RGFW_window* win, void* ptr); + +/**! + * @brief retrieves the platform-specific window source pointer + * @param win a pointer to the target window + * @return A pointer to the internal RGFW_window_src structure +*/ +RGFWDEF RGFW_window_src* RGFW_window_getSrc(RGFW_window* win); + +/**! + * @brief sets the macOS layer object associated with the window + * @param win a pointer to the target window + * @param layer a pointer to the macOS layer object + * @note Only available on macOS platforms +*/ +RGFWDEF void RGFW_window_setLayer_OSX(RGFW_window* win, void* layer); + +/**! + * @brief retrieves the macOS view object associated with the window + * @param win a pointer to the target window + * @return A pointer to the macOS view object, or NULL if not on macOS +*/ +RGFWDEF void* RGFW_window_getView_OSX(RGFW_window* win); + +/**! + * @brief retrieves the macOS window object + * @param win a pointer to the target window + * @return A pointer to the macOS window object, or NULL if not on macOS +*/ +RGFWDEF void* RGFW_window_getWindow_OSX(RGFW_window* win); + +/**! + * @brief retrieves the HWND handle for the window + * @param win a pointer to the target window + * @return A pointer to the Windows HWND handle, or NULL if not on Windows +*/ +RGFWDEF void* RGFW_window_getHWND(RGFW_window* win); + +/**! + * @brief retrieves the HDC handle for the window + * @param win a pointer to the target window + * @return A pointer to the Windows HDC handle, or NULL if not on Windows +*/ +RGFWDEF void* RGFW_window_getHDC(RGFW_window* win); + +/**! + * @brief retrieves the X11 Window handle for the window + * @param win a pointer to the target window + * @return The X11 Window handle, or 0 if not on X11 +*/ +RGFWDEF u64 RGFW_window_getWindow_X11(RGFW_window* win); + +/**! + * @brief retrieves the Wayland surface handle for the window + * @param win a pointer to the target window + * @return A pointer to the Wayland wl_surface, or NULL if not on Wayland +*/ +RGFWDEF struct wl_surface* RGFW_window_getWindow_Wayland(RGFW_window* win); + +/** * @defgroup Window_management +* @{ */ + +/*! 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); + +/**! + * @brief polls and pops the next event with the matching target window in event queue, pushes back events that don't match + * @param win a pointer to the target window + * @param event [OUTPUT] a pointer to store the retrieved event + * @return RGFW_TRUE if an event was found, RGFW_FALSE otherwise + * + * NOTE: Using this function without a loop may cause event lag. + * For multi-threaded systems, use RGFW_pollEvents combined with RGFW_window_checkQueuedEvent. + * + * Example: + * RGFW_event event; + * while (RGFW_window_checkEvent(win, &event)) { + * // handle event + * } +*/ +RGFWDEF RGFW_bool RGFW_window_checkEvent(RGFW_window* win, RGFW_event* event); + +/**! + * @brief pops the first queued event with the matching target window, pushes back events that don't match + * @param win a pointer to the target window + * @param event [OUTPUT] a pointer to store the retrieved event + * @return RGFW_TRUE if an event was found, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_checkQueuedEvent(RGFW_window* win, RGFW_event* event); + +/**! + * @brief checks if a key was pressed while the window is in focus + * @param win a pointer to the target window + * @param key the key code to check + * @return RGFW_TRUE if the key was pressed, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_isKeyPressed(RGFW_window* win, RGFW_key key); + +/**! + * @brief checks if a key is currently being held down + * @param win a pointer to the target window + * @param key the key code to check + * @return RGFW_TRUE if the key is held down, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_isKeyDown(RGFW_window* win, RGFW_key key); + +/**! + * @brief checks if a key was released + * @param win a pointer to the target window + * @param key the key code to check + * @return RGFW_TRUE if the key was released, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_isKeyReleased(RGFW_window* win, RGFW_key key); + +/**! + * @brief checks if a mouse button was pressed + * @param win a pointer to the target window + * @param button the mouse button code to check + * @return RGFW_TRUE if the mouse button was pressed, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_isMousePressed(RGFW_window* win, RGFW_mouseButton button); + +/**! + * @brief checks if a mouse button is currently held down + * @param win a pointer to the target window + * @param button the mouse button code to check + * @return RGFW_TRUE if the mouse button is down, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_isMouseDown(RGFW_window* win, RGFW_mouseButton button); + +/**! + * @brief checks if a mouse button was released + * @param win a pointer to the target window + * @param button the mouse button code to check + * @return RGFW_TRUE if the mouse button was released, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_isMouseReleased(RGFW_window* win, RGFW_mouseButton button); + +/**! + * @brief checks if the mouse left the window (true only for the first frame) + * @param win a pointer to the target window + * @return RGFW_TRUE if the mouse left, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_didMouseLeave(RGFW_window* win); + +/**! + * @brief checks if the mouse entered the window (true only for the first frame) + * @param win a pointer to the target window + * @return RGFW_TRUE if the mouse entered, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_didMouseEnter(RGFW_window* win); + +/**! + * @brief checks if the mouse is currently inside the window bounds + * @param win a pointer to the target window + * @return RGFW_TRUE if the mouse is inside, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_isMouseInside(RGFW_window* win); + +/**! + * @brief checks if there is data being dragged into or within the window + * @param win a pointer to the target window + * @return RGFW_TRUE if data is being dragged, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_isDataDragging(RGFW_window* win); + +/**! + * @brief gets the position of a data drag + * @param win a pointer to the target window + * @param x [OUTPUT] pointer to store the x position + * @param y [OUTPUT] pointer to store the y position + * @return RGFW_TRUE if there is an active drag, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_getDataDrag(RGFW_window* win, i32* x, i32* y); + +/**! + * @brief checks if a data drop occurred in the window (first frame only) + * @param win a pointer to the target window + * @return RGFW_TRUE if data was dropped, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_didDataDrop(RGFW_window* win); + +/**! + * @brief retrieves files from a data drop (drag and drop) + * @param win a pointer to the target window + * @param files [OUTPUT] a pointer to the array of file paths + * @param count [OUTPUT] the number of dropped files + * @return RGFW_TRUE if a data drop occurred, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_getDataDrop(RGFW_window* win, const char*** files, size_t* count); + +/**! + * @brief closes the window and frees its associated structure + * @param win a pointer to the target window +*/ +RGFWDEF void RGFW_window_close(RGFW_window* win); + +/**! + * @brief closes the window without freeing its structure + * @param win a pointer to the target window +*/ +RGFWDEF void RGFW_window_closePtr(RGFW_window* win); + +/**! + * @brief moves the window to a new position on the screen + * @param win a pointer to the target window + * @param x the new x position + * @param y the new y position +*/ +RGFWDEF void RGFW_window_move(RGFW_window* win, i32 x, i32 y); + +/**! + * @brief moves the window to a specific monitor + * @param win a pointer to the target window + * @param m the target monitor +*/ +RGFWDEF void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor* m); + +/**! + * @brief resizes the window to the given dimensions + * @param win a pointer to the target window + * @param w the new width + * @param h the new height +*/ +RGFWDEF void RGFW_window_resize(RGFW_window* win, i32 w, i32 h); + +/**! + * @brief sets the aspect ratio of the window + * @param win a pointer to the target window + * @param w the width ratio + * @param h the height ratio +*/ +RGFWDEF void RGFW_window_setAspectRatio(RGFW_window* win, i32 w, i32 h); + +/**! + * @brief sets the minimum size of the window + * @param win a pointer to the target window + * @param w the minimum width + * @param h the minimum height +*/ +RGFWDEF void RGFW_window_setMinSize(RGFW_window* win, i32 w, i32 h); + +/**! + * @brief sets the maximum size of the window + * @param win a pointer to the target window + * @param w the maximum width + * @param h the maximum height +*/ +RGFWDEF void RGFW_window_setMaxSize(RGFW_window* win, i32 w, i32 h); + +/**! + * @brief sets focus to the window + * @param win a pointer to the target window +*/ +RGFWDEF void RGFW_window_focus(RGFW_window* win); + +/**! + * @brief checks if the window is currently in focus + * @param win a pointer to the target window + * @return RGFW_TRUE if the window is in focus, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_isInFocus(RGFW_window* win); + +/**! + * @brief raises the window to the top of the stack + * @param win a pointer to the target window +*/ +RGFWDEF void RGFW_window_raise(RGFW_window* win); + +/**! + * @brief maximizes the window + * @param win a pointer to the target window +*/ +RGFWDEF void RGFW_window_maximize(RGFW_window* win); + +/**! + * @brief toggles fullscreen mode for the window + * @param win a pointer to the target window + * @param fullscreen RGFW_TRUE to enable fullscreen, RGFW_FALSE to disable +*/ +RGFWDEF void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen); + +/**! + * @brief centers the window on the screen + * @param win a pointer to the target window +*/ +RGFWDEF void RGFW_window_center(RGFW_window* win); + +/**! + * @brief minimizes the window + * @param win a pointer to the target window +*/ +RGFWDEF void RGFW_window_minimize(RGFW_window* win); + +/**! + * @brief restores the window from minimized state + * @param win a pointer to the target window +*/ +RGFWDEF void RGFW_window_restore(RGFW_window* win); + +/**! + * @brief makes the window a floating window + * @param win a pointer to the target window + * @param floating RGFW_TRUE to float, RGFW_FALSE to disable +*/ +RGFWDEF void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating); + +/**! + * @brief sets the opacity level of the window + * @param win a pointer to the target window + * @param opacity the opacity level (0–255) +*/ +RGFWDEF void RGFW_window_setOpacity(RGFW_window* win, u8 opacity); + +/**! + * @brief toggles window borders + * @param win a pointer to the target window + * @param border RGFW_TRUE for bordered, RGFW_FALSE for borderless +*/ +RGFWDEF void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border); + +/**! + * @brief checks if the window is borderless + * @param win a pointer to the target window + * @return RGFW_TRUE if borderless, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_borderless(RGFW_window* win); + +/**! + * @brief toggles drag-and-drop (DND) support for the window + * @param win a pointer to the target window + * @param allow RGFW_TRUE to allow DND, RGFW_FALSE to disable + * @note RGFW_windowAllowDND must still be passed when creating the window +*/ +RGFWDEF void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow); + +/**! + * @brief checks if drag-and-drop (DND) is allowed + * @param win a pointer to the target window + * @return RGFW_TRUE if DND is enabled, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_allowsDND(RGFW_window* win); + +#ifndef RGFW_NO_PASSTHROUGH +/**! + * @brief toggles mouse passthrough for the window + * @param win a pointer to the target window + * @param passthrough RGFW_TRUE to enable passthrough, RGFW_FALSE to disable +*/ +RGFWDEF void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough); +#endif + +/**! + * @brief renames the window + * @param win a pointer to the target window + * @param name the new title string for the window +*/ +RGFWDEF void RGFW_window_setName(RGFW_window* win, const char* name); + +/**! + * @brief sets the icon for the window and taskbar + * @param win a pointer to the target window + * @param data the image data + * @param w the width of the icon + * @param h the height of the icon + * @param format the image format + * @return RGFW_TRUE if successful, RGFW_FALSE otherwise + * + * NOTE: The image may be resized by default. +*/ +RGFWDEF RGFW_bool RGFW_window_setIcon(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format); + +/**! + * @brief sets the icon for the window and/or taskbar + * @param win a pointer to the target window + * @param data the image data + * @param w the width of the icon + * @param h the height of the icon + * @param format the image format + * @param type the target icon type (taskbar, window, or both) + * @return RGFW_TRUE if successful, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_icon type); + +/**! + * @brief sets the mouse icon for the window using a loaded bitmap + * @param win a pointer to the target window + * @param mouse a pointer to the RGFW_mouse struct containing the icon +*/ +RGFWDEF void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse); + +/**! + * @brief Sets the mouse to a standard system cursor. + * @param win The target window. + * @param mouse The standard cursor type (see RGFW_MOUSE enum). + * @return True if the standard cursor was successfully applied. +*/ +RGFWDEF RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, RGFW_mouseIcons mouse); + +/**! + * @brief Sets the mouse to the default cursor icon. + * @param win The target window. + * @return True if the default cursor was successfully set. +*/ +RGFWDEF RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win); + +/**! + * @brief set (enable or disable) raw mouse mode only for the select window + * @param win The target window. + * @param the boolean state of raw mouse mode + * +*/ +RGFWDEF void RGFW_window_setRawMouseMode(RGFW_window* win, RGFW_bool state); + +/**! + * @brief lock/unlock the cursor. + * @param win The target window. + * @param the boolean state of the mouse's capture state + * +*/ +RGFWDEF void RGFW_window_captureMouse(RGFW_window* win, RGFW_bool state); + +/**! + * @brief lock/unlock the cursor and enable raw mpuise mode. + * @param win The target window. + * @param the boolean state of raw mouse mode + * +*/ +RGFWDEF void RGFW_window_captureRawMouse(RGFW_window* win, RGFW_bool state); + +/**! + * @brief Returns true if the mouse is using raw mouse mode + * @param win The target window. + * @return True if the mouse is using raw mouse input mode. +*/ +RGFWDEF RGFW_bool RGFW_window_isRawMouseMode(RGFW_window* win); + + +/**! + * @brief Returns true if the mouse is captured + * @param win The target window. + * @return True if the mouse is being captured. +*/ +RGFWDEF RGFW_bool RGFW_window_isCaptured(RGFW_window* win); + +/**! + * @brief Hides the window from view. + * @param win The target window. +*/ +RGFWDEF void RGFW_window_hide(RGFW_window* win); + +/**! + * @brief Shows the window if it was hidden. + * @param win The target window. +*/ +RGFWDEF void RGFW_window_show(RGFW_window* win); + +/**! + * @breif request a window flash to get attention from the user + * @param win the target window + * @param request the flash operation requested +*/ +RGFWDEF void RGFW_window_flash(RGFW_window* win, RGFW_flashRequest request); + +/**! + * @brief Sets whether the window should close. + * @param win The target window. + * @param shouldClose True to signal the window should close, false to keep it open. + * + * This can override or trigger the `RGFW_window_shouldClose` state by modifying window flags. +*/ +RGFWDEF void RGFW_window_setShouldClose(RGFW_window* win, RGFW_bool shouldClose); + +/**! + * @brief Retrieves the current global mouse position. + * @param x [OUTPUT] Pointer to store the X position of the mouse on the screen. + * @param y [OUTPUT] Pointer to store the Y position of the mouse on the screen. + * @return True if the position was successfully retrieved. +*/ +RGFWDEF RGFW_bool RGFW_getGlobalMouse(i32* x, i32* y); + +/**! + * @brief Retrieves the mouse position relative to the window. + * @param win The target window. + * @param x [OUTPUT] Pointer to store the X position within the window. + * @param y [OUTPUT] Pointer to store the Y position within the window. + * @return True if the position was successfully retrieved. +*/ +RGFWDEF RGFW_bool RGFW_window_getMouse(RGFW_window* win, i32* x, i32* y); + +/**! + * @brief Shows or hides the mouse cursor for the window. + * @param win The target window. + * @param show True to show the mouse, false to hide it. +*/ +RGFWDEF void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show); + +/**! + * @brief Checks if the mouse is currently hidden in the window. + * @param win The target window. + * @return True if the mouse is hidden. +*/ +RGFWDEF RGFW_bool RGFW_window_isMouseHidden(RGFW_window* win); + +/**! + * @brief Moves the mouse to the specified position within the window. + * @param win The target window. + * @param x The new X position. + * @param y The new Y position. +*/ +RGFWDEF void RGFW_window_moveMouse(RGFW_window* win, i32 x, i32 y); + +/**! + * @brief Checks if the window should close. + * @param win The target window. + * @return True if the window should close (for example, if ESC was pressed or a close event occurred). +*/ +RGFWDEF RGFW_bool RGFW_window_shouldClose(RGFW_window* win); + +/**! + * @brief Checks if the window is currently fullscreen. + * @param win The target window. + * @return True if the window is fullscreen. +*/ +RGFWDEF RGFW_bool RGFW_window_isFullscreen(RGFW_window* win); + +/**! + * @brief Checks if the window is currently hidden. + * @param win The target window. + * @return True if the window is hidden. +*/ +RGFWDEF RGFW_bool RGFW_window_isHidden(RGFW_window* win); + +/**! + * @brief Checks if the window is minimized. + * @param win The target window. + * @return True if the window is minimized. +*/ +RGFWDEF RGFW_bool RGFW_window_isMinimized(RGFW_window* win); + +/**! + * @brief Checks if the window is maximized. + * @param win The target window. + * @return True if the window is maximized. +*/ +RGFWDEF RGFW_bool RGFW_window_isMaximized(RGFW_window* win); + +/**! + * @brief Checks if the window is floating. + * @param win The target window. + * @return True if the window is floating. +*/ +RGFWDEF RGFW_bool RGFW_window_isFloating(RGFW_window* win); +/** @} */ + +/** * @defgroup Monitor +* @{ */ + +/**! + * @brief Scales the window to match its monitor’s resolution. + * @param win The target window. + * + * This function is automatically called when the flag `RGFW_scaleToMonitor` + * is used during window creation. +*/ +RGFWDEF void RGFW_window_scaleToMonitor(RGFW_window* win); + +/**! + * @brief Retrieves the monitor structure associated with the window. + * @param win The target window. + * @return The monitor structure of the window. +*/ +RGFWDEF RGFW_monitor* RGFW_window_getMonitor(RGFW_window* win); + +/** @} */ + +/** * @defgroup Clipboard +* @{ */ + +/**! + * @brief Reads clipboard data. + * @param size [OUTPUT] A pointer that will be filled with the size of the clipboard data. + * @return A pointer to the clipboard data as a string. +*/ +RGFWDEF const char* RGFW_readClipboard(size_t* size); + +/**! + * @brief Reads clipboard data into a provided buffer, or returns the required length if str is NULL. + * @param str [OUTPUT] A pointer to the buffer that will receive the clipboard data (or NULL to get required size). + * @param strCapacity The capacity of the provided buffer. + * @return The number of bytes read or required length of clipboard data. +*/ +RGFWDEF RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity); + +/**! + * @brief Writes text to the clipboard. + * @param text The text to be written to the clipboard. + * @param textLen The length of the text being written. +*/ +RGFWDEF void RGFW_writeClipboard(const char* text, u32 textLen); +/** @} */ + + + +/** * @defgroup error handling +* @{ */ +/**! + * @brief Sets the callback function to handle debug messages from RGFW. + * @param func The function pointer to be used as the debug callback. + * @return The previously set debug callback function. +*/ +RGFWDEF RGFW_debugfunc RGFW_setDebugCallback(RGFW_debugfunc func); + +/**! + * @brief Sends a debug message manually through the currently set debug callback. + * @param type The type of debug message being sent. + * @param err The associated error code. + * @param msg The debug message text. +*/ +RGFWDEF void RGFW_sendDebugInfo(RGFW_debugType type, RGFW_errorCode err, const char* msg); +/** @} */ + +/** + + + event callbacks. + These are completely optional, so you can use the normal + RGFW_checkEvent() method if you prefer that + +* @defgroup Callbacks +* @{ +*/ + +/**! + * @brief Sets the callback function for window move events. + * @param func The function to be called when the window is moved. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_windowMovedfunc RGFW_setWindowMovedCallback(RGFW_windowMovedfunc func); + +/**! + * @brief Sets the callback function for window resize events. + * @param func The function to be called when the window is resized. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_windowResizedfunc RGFW_setWindowResizedCallback(RGFW_windowResizedfunc func); + +/**! + * @brief Sets the callback function for window quit events. + * @param func The function to be called when the window receives a quit signal. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_windowQuitfunc RGFW_setWindowQuitCallback(RGFW_windowQuitfunc func); + +/**! + * @brief Sets the callback function for mouse move events. + * @param func The function to be called when the mouse moves within the window. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_mousePosfunc RGFW_setMousePosCallback(RGFW_mousePosfunc func); + +/**! + * @brief Sets the callback function for window refresh events. + * @param func The function to be called when the window needs to be refreshed. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_windowRefreshfunc RGFW_setWindowRefreshCallback(RGFW_windowRefreshfunc func); + +/**! + * @brief Sets the callback function for focus change events. + * @param func The function to be called when the window gains or loses focus. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_focusfunc RGFW_setFocusCallback(RGFW_focusfunc func); + +/**! + * @brief Sets the callback function for mouse notification events. + * @param func The function to be called when a mouse notification event occurs. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_mouseNotifyfunc RGFW_setMouseNotifyCallback(RGFW_mouseNotifyfunc func); + +/**! + * @brief Sets the callback function for data drop events. + * @param func The function to be called when data is dropped into the window. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_dataDropfunc RGFW_setDataDropCallback(RGFW_dataDropfunc func); + +/**! + * @brief Sets the callback function for the start of a data drag event. + * @param func The function to be called when data dragging begins. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_dataDragfunc RGFW_setDataDragCallback(RGFW_dataDragfunc func); + +/**! + * @brief Sets the callback function for key press and release events. + * @param func The function to be called when a key is pressed or released. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_keyfunc RGFW_setKeyCallback(RGFW_keyfunc func); + +/**! + * @brief Sets the callback function for key character events. + * @param func The function to be called when a key is pressed or released. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_keyCharfunc RGFW_setKeyCharCallback(RGFW_keyCharfunc func); + +/**! + * @brief Sets the callback function for mouse button press and release events. + * @param func The function to be called when a mouse button is pressed or released. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_mouseButtonfunc RGFW_setMouseButtonCallback(RGFW_mouseButtonfunc func); + +/**! + * @brief Sets the callback function for mouse scroll events. + * @param func The function to be called when the mouse wheel is scrolled. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_mouseScrollfunc RGFW_setMouseScrollCallback(RGFW_mouseScrollfunc func); + +/**! + * @brief Sets the callback function for window maximize events. + * @param func The function to be called when the window is maximized. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_windowMaximizedfunc RGFW_setWindowMaximizedCallback(RGFW_windowMaximizedfunc func); + +/**! + * @brief Sets the callback function for window minimize events. + * @param func The function to be called when the window is minimized. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_windowMinimizedfunc RGFW_setWindowMinimizedCallback(RGFW_windowMinimizedfunc func); + +/**! + * @brief Sets the callback function for window restore events. + * @param func The function to be called when the window is restored from a minimized or maximized state. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_windowRestoredfunc RGFW_setWindowRestoredCallback(RGFW_windowRestoredfunc func); + +/**! + * @brief Sets the callback function for DPI (scale) update events. + * @param func The function to be called when the window’s DPI or scale changes. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_scaleUpdatedfunc RGFW_setScaleUpdatedCallback(RGFW_scaleUpdatedfunc func); + +/**! + * @brief Sets the callback function for monitor connected and disconnect events. + * @param func The function to be called when a monitor is connected or disconnected. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_monitorfunc RGFW_setMonitorCallback(RGFW_monitorfunc func); + +/** @} */ + +/** * @defgroup graphics_API +* @{ */ + +/*! native rendering API functions */ +#if defined(RGFW_OPENGL) +/* these are native opengl specific functions and will NOT work with EGL */ + +/*!< 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_makeCurrentContext_OpenGL(NULL); on the old thread + then RGFW_window_makeCurrentContext_OpenGL(valid_window) on the new thread +*/ + +/**! + * @brief Sets the global OpenGL hints to the specified pointer. + * @param hints A pointer to the RGFW_glHints structure containing the desired OpenGL settings. +*/ +RGFWDEF void RGFW_setGlobalHints_OpenGL(RGFW_glHints* hints); + +/**! + * @brief Resets the global OpenGL hints to their default values. +*/ +RGFWDEF void RGFW_resetGlobalHints_OpenGL(void); + +/**! + * @brief Gets the current global OpenGL hints pointer. + * @return A pointer to the currently active RGFW_glHints structure. +*/ +RGFWDEF RGFW_glHints* RGFW_getGlobalHints_OpenGL(void); + +/**! + * @brief Creates and allocates an OpenGL context for the specified window. + * @param win A pointer to the target RGFW_window. + * @param hints A pointer to an RGFW_glHints structure defining context creation parameters. + * @return A pointer to the newly created RGFW_glContext. +*/ +RGFWDEF RGFW_glContext* RGFW_window_createContext_OpenGL(RGFW_window* win, RGFW_glHints* hints); + +/**! + * @brief Creates an OpenGL context for the specified window using a preallocated context structure. + * @param win A pointer to the target RGFW_window. + * @param ctx A pointer to an already allocated RGFW_glContext structure. + * @param hints A pointer to an RGFW_glHints structure defining context creation parameters. + * @return RGFW_TRUE on success, RGFW_FALSE on failure. +*/ +RGFWDEF RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints); + +/**! + * @brief Retrieves the OpenGL context associated with a window. + * @param win A pointer to the RGFW_window. + * @return A pointer to the associated RGFW_glContext, or NULL if none exists or if the context is EGL-based. +*/ +RGFWDEF RGFW_glContext* RGFW_window_getContext_OpenGL(RGFW_window* win); + +/**! + * @brief Deletes and frees the OpenGL context. + * @param win A pointer to the RGFW_window. + * @param ctx A pointer to the RGFW_glContext to delete. + * + * @note This is automatically called by RGFW_window_close if the window’s context is not NULL. +*/ +RGFWDEF void RGFW_window_deleteContext_OpenGL(RGFW_window* win, RGFW_glContext* ctx); + +/**! + * @brief Deletes the OpenGL context without freeing its memory. + * @param win A pointer to the RGFW_window. + * @param ctx A pointer to the RGFW_glContext to delete. + * + * @note This is automatically called by RGFW_window_close if the window’s context is not NULL. +*/ +RGFWDEF void RGFW_window_deleteContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx); + +/**! + * @brief Retrieves the native source context from an RGFW_glContext. + * @param ctx A pointer to the RGFW_glContext. + * @return A pointer to the native OpenGL context handle. +*/ +RGFWDEF void* RGFW_glContext_getSourceContext(RGFW_glContext* ctx); + +/**! + * @brief Makes the specified window the current OpenGL rendering target. + * @param win A pointer to the RGFW_window to make current. + * + * @note This is typically called internally by RGFW_window_makeCurrent. +*/ +RGFWDEF void RGFW_window_makeCurrentWindow_OpenGL(RGFW_window* win); + +/**! + * @brief Makes the OpenGL context of the specified window current. + * @param win A pointer to the RGFW_window whose context should be made current. + * + * @note To move a context between threads, call RGFW_window_makeCurrentContext_OpenGL(NULL) + * on the old thread before making it current on the new one. +*/ +RGFWDEF void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win); + +/**! + * @brief Swaps the OpenGL buffers for the specified window. + * @param win A pointer to the RGFW_window whose buffers should be swapped. +*/ +RGFWDEF void RGFW_window_swapBuffers_OpenGL(RGFW_window* win); + +/**! + * @brief Retrieves the current OpenGL context. + * @return A pointer to the currently active OpenGL context (GLX, WGL, Cocoa, or WebGL backend). +*/ +RGFWDEF void* RGFW_getCurrentContext_OpenGL(void); + +/**! + * @brief Retrieves the current OpenGL window. + * @return A pointer to the RGFW_window currently bound as the OpenGL context target. +*/ +RGFWDEF RGFW_window* RGFW_getCurrentWindow_OpenGL(void); + +/**! + * @brief Sets the OpenGL swap interval (vsync). + * @param win A pointer to the RGFW_window. + * @param swapInterval The desired swap interval value (0 to disable vsync, 1 to enable). +*/ +RGFWDEF void RGFW_window_swapInterval_OpenGL(RGFW_window* win, i32 swapInterval); + +/**! + * @brief Retrieves the address of a native OpenGL procedure. + * @param procname The name of the OpenGL function to look up. + * @return A pointer to the function, or NULL if not found. +*/ +RGFWDEF RGFW_proc RGFW_getProcAddress_OpenGL(const char* procname); + +/**! + * @brief Checks whether a specific OpenGL or OpenGL ES API extension is supported. + * @param extension The name of the extension to check. + * @param len The length of the extension string. + * @return RGFW_TRUE if supported, RGFW_FALSE otherwise. +*/ +RGFWDEF RGFW_bool RGFW_extensionSupported_OpenGL(const char* extension, size_t len); + +/**! + * @brief Checks whether a specific platform-dependent OpenGL extension is supported. + * @param extension The name of the extension to check. + * @param len The length of the extension string. + * @return RGFW_TRUE if supported, RGFW_FALSE otherwise. +*/ +RGFWDEF RGFW_bool RGFW_extensionSupportedPlatform_OpenGL(const char* extension, size_t len); + +/* these are EGL specific functions, they may fallback to OpenGL */ +#ifdef RGFW_EGL +/**! + * @brief Creates and allocates an OpenGL/EGL context for the specified window. + * @param win A pointer to the target RGFW_window. + * @param hints A pointer to an RGFW_glHints structure defining context creation parameters. + * @return A pointer to the newly created RGFW_eglContext. +*/ +RGFWDEF RGFW_eglContext* RGFW_window_createContext_EGL(RGFW_window* win, RGFW_glHints* hints); + +/**! + * @brief Creates an OpenGL/EGL context for the specified window using a preallocated context structure. + * @param win A pointer to the target RGFW_window. + * @param ctx A pointer to an already allocated RGFW_eglContext structure. + * @param hints A pointer to an RGFW_glHints structure defining context creation parameters. + * @return RGFW_TRUE on success, RGFW_FALSE on failure. +*/ +RGFWDEF RGFW_bool RGFW_window_createContextPtr_EGL(RGFW_window* win, RGFW_eglContext* ctx, RGFW_glHints* hints); + +/**! + * @brief Frees and deletes an OpenGL/EGL context. + * @param win A pointer to the RGFW_window. + * @param ctx A pointer to the RGFW_eglContext to delete. + * + * @note Automatically called by RGFW_window_close if RGFW owns the context. +*/ +RGFWDEF void RGFW_window_deleteContext_EGL(RGFW_window* win, RGFW_eglContext* ctx); + +/**! + * @brief Deletes an OpenGL/EGL context without freeing its memory. + * @param win A pointer to the RGFW_window. + * @param ctx A pointer to the RGFW_eglContext to delete. + * + * @note Automatically called by RGFW_window_close if RGFW owns the context. +*/ +RGFWDEF void RGFW_window_deleteContextPtr_EGL(RGFW_window* win, RGFW_eglContext* ctx); + +/**! + * @brief Retrieves the OpenGL/EGL context associated with a window. + * @param win A pointer to the RGFW_window. + * @return A pointer to the associated RGFW_eglContext, or NULL if none exists or if the context is a native OpenGL context. +*/ +RGFWDEF RGFW_eglContext* RGFW_window_getContext_EGL(RGFW_window* win); + +/**! + * @brief Retrieves the EGL display handle. + * @return A pointer to the native EGLDisplay. +*/ +RGFWDEF void* RGFW_getDisplay_EGL(void); + +/**! + * @brief Retrieves the native source context from an RGFW_eglContext. + * @param ctx A pointer to the RGFW_eglContext. + * @return A pointer to the native EGLContext handle. +*/ +RGFWDEF void* RGFW_eglContext_getSourceContext(RGFW_eglContext* ctx); + +/**! + * @brief Retrieves the EGL surface handle from an RGFW_eglContext. + * @param ctx A pointer to the RGFW_eglContext. + * @return A pointer to the EGLSurface associated with the context. +*/ +RGFWDEF void* RGFW_eglContext_getSurface(RGFW_eglContext* ctx); + +/**! + * @brief Retrieves the Wayland EGL window handle from an RGFW_eglContext. + * @param ctx A pointer to the RGFW_eglContext. + * @return A pointer to the wl_egl_window associated with the EGL context. +*/ +RGFWDEF struct wl_egl_window* RGFW_eglContext_wlEGLWindow(RGFW_eglContext* ctx); + +/**! + * @brief Swaps the EGL buffers for the specified window. + * @param win A pointer to the RGFW_window whose buffers should be swapped. + * + * @note Typically called by RGFW_window_swapInterval. +*/ +RGFWDEF void RGFW_window_swapBuffers_EGL(RGFW_window* win); + +/**! + * @brief Makes the specified window the current EGL rendering target. + * @param win A pointer to the RGFW_window to make current. + * + * @note This is typically called internally by RGFW_window_makeCurrent. +*/ +RGFWDEF void RGFW_window_makeCurrentWindow_EGL(RGFW_window* win); + +/**! + * @brief Makes the EGL context of the specified window current. + * @param win A pointer to the RGFW_window whose context should be made current. + * + * @note To move a context between threads, call RGFW_window_makeCurrentContext_EGL(NULL) + * on the old thread before making it current on the new one. +*/ +RGFWDEF void RGFW_window_makeCurrentContext_EGL(RGFW_window* win); + +/**! + * @brief Retrieves the current EGL context. + * @return A pointer to the currently active EGLContext. +*/ +RGFWDEF void* RGFW_getCurrentContext_EGL(void); + +/**! + * @brief Retrieves the current EGL window. + * @return A pointer to the RGFW_window currently bound as the EGL context target. +*/ +RGFWDEF RGFW_window* RGFW_getCurrentWindow_EGL(void); + +/**! + * @brief Sets the EGL swap interval (vsync). + * @param win A pointer to the RGFW_window. + * @param swapInterval The desired swap interval value (0 to disable vsync, 1 to enable). +*/ +RGFWDEF void RGFW_window_swapInterval_EGL(RGFW_window* win, i32 swapInterval); + +/**! + * @brief Retrieves the address of a native OpenGL or OpenGL ES procedure in an EGL context. + * @param procname The name of the OpenGL function to look up. + * @return A pointer to the function, or NULL if not found. +*/ +RGFWDEF RGFW_proc RGFW_getProcAddress_EGL(const char* procname); + +/**! + * @brief Checks whether a specific OpenGL or OpenGL ES API extension is supported in the current EGL context. + * @param extension The name of the extension to check. + * @param len The length of the extension string. + * @return RGFW_TRUE if supported, RGFW_FALSE otherwise. +*/ +RGFWDEF RGFW_bool RGFW_extensionSupported_EGL(const char* extension, size_t len); + +/**! + * @brief Checks whether a specific platform-dependent EGL extension is supported in the current context. + * @param extension The name of the extension to check. + * @param len The length of the extension string. + * @return RGFW_TRUE if supported, RGFW_FALSE otherwise. +*/ +RGFWDEF RGFW_bool RGFW_extensionSupportedPlatform_EGL(const char* extension, size_t len); +#endif +#endif + +#ifdef RGFW_VULKAN +#include + +/* if you don't want to use the above macros */ + +/**! + * @brief Retrieves the Vulkan instance extensions required by RGFW. + * @param count [OUTPUT] A pointer that will receive the number of required extensions (typically 2). + * @return A pointer to a static array of required Vulkan instance extension names. +*/ +RGFWDEF const char** RGFW_getRequiredInstanceExtensions_Vulkan(size_t* count); + +/**! + * @brief Creates a Vulkan surface for the specified window. + * @param win A pointer to the RGFW_window for which to create the Vulkan surface. + * @param instance The Vulkan instance used to create the surface. + * @param surface [OUTPUT] A pointer to a VkSurfaceKHR handle that will receive the created surface. + * @return A VkResult indicating success or failure. +*/ +RGFWDEF VkResult RGFW_window_createSurface_Vulkan(RGFW_window* win, VkInstance instance, VkSurfaceKHR* surface); + +/**! + * @brief Checks whether the specified Vulkan physical device and queue family support presentation for RGFW. + * @param instance The Vulkan instance. + * @param physicalDevice The Vulkan physical device to check. + * @param queueFamilyIndex The index of the queue family to query for presentation support. + * @return RGFW_TRUE if presentation is supported, RGFW_FALSE otherwise. +*/ +RGFWDEF RGFW_bool RGFW_getPresentationSupport_Vulkan(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 +/**! + * @brief Creates a DirectX swap chain for the specified RGFW window. + * @param win A pointer to the RGFW_window for which to create the swap chain. + * @param pFactory A pointer to the IDXGIFactory used to create the swap chain. + * @param pDevice A pointer to the DirectX device (e.g., ID3D11Device or ID3D12Device). + * @param swapchain [OUTPUT] A pointer to an IDXGISwapChain pointer that will receive the created swap chain. + * @return An integer result code (0 on success, or a DirectX error code on failure). +*/ +RGFWDEF int RGFW_window_createSwapChain_DirectX(RGFW_window* win, IDXGIFactory* pFactory, IUnknown* pDevice, IDXGISwapChain** swapchain); +#endif +#endif + +#ifdef RGFW_WEBGPU + #include + /**! + * @brief Creates a WebGPU surface for the specified RGFW window. + * @param window A pointer to the RGFW_window for which to create the surface. + * @param instance The WebGPU instance used to create the surface. + * @return The created WGPUSurface handle. + */ + RGFWDEF WGPUSurface RGFW_window_createSurface_WebGPU(RGFW_window* window, WGPUInstance instance); +#endif + +/** @} */ + +/** * @defgroup Supporting +* @{ */ + +/**! + * @brief Sets the root (main) RGFW window. + * @param win A pointer to the RGFW_window to set as the root window. +*/ +RGFWDEF void RGFW_setRootWindow(RGFW_window* win); + +/**! + * @brief Retrieves the current root RGFW window. + * @return A pointer to the current root RGFW_window. +*/ +RGFWDEF RGFW_window* RGFW_getRootWindow(void); + +/**! + * @brief Pushes an event into the standard RGFW event queue. + * @param event A pointer to the RGFW_event to be added to the queue. +*/ +RGFWDEF void RGFW_eventQueuePush(const RGFW_event* event); + +/**! + * @brief Clears all events from the RGFW event queue without processing them. +*/ +RGFWDEF void RGFW_eventQueueFlush(void); + +/**! + * @brief Pops the next event from the RGFW event queue. + * @return A pointer to the popped RGFW_event, or NULL if the queue is empty. +*/ +RGFWDEF RGFW_event* RGFW_eventQueuePop(void); + +/**! + * @brief Pops the next event from the RGFW event queue that matches the target window, pushes back events that don't matchj. + * @param win A pointer to the target RGFW_window. + * @return A pointer to the popped RGFW_event, or NULL if the queue is empty. +*/ +RGFWDEF RGFW_event* RGFW_window_eventQueuePop(RGFW_window* win); + +/**! + * @brief Converts an API keycode to the RGFW unmapped (physical) key. + * @param keycode The platform-specific keycode. + * @return The corresponding RGFW keycode. +*/ +RGFWDEF RGFW_key RGFW_apiKeyToRGFW(u32 keycode); + +/**! + * @brief Converts an RGFW keycode to the unmapped (physical) API key. + * @param keycode The RGFW keycode. + * @return The corresponding platform-specific keycode. +*/ +RGFWDEF u32 RGFW_rgfwToApiKey(RGFW_key keycode); + +/**! + * @brief Converts an physical RGFW keycode to a mapped RGFW keycode. + * @param keycode the physical RGFW keycode. + * @return The corresponding mapped RGFW keycode. +*/ +RGFWDEF RGFW_key RGFW_physicalToMappedKey(RGFW_key keycode); + +/**! + * @brief Retrieves the size of the RGFW_info structure. + * @return The size (in bytes) of RGFW_info. +*/ +RGFWDEF size_t RGFW_sizeofInfo(void); + +/**! + * @brief Initializes the RGFW library. + * @return 0 on success, or a negative error code on failure. + * @note This is automatically called when the first window is created. +*/ +RGFWDEF i32 RGFW_init(void); + +/**! + * @brief Deinitializes the RGFW library. + * @note This is automatically called when the last open window is closed. +*/ +RGFWDEF void RGFW_deinit(void); + +/**! + * @brief Initializes RGFW using a user-provided RGFW_info structure. + * @param info A pointer to an RGFW_info structure to be used for initialization. + * @return 0 on success, or a negative error code on failure. +*/ +RGFWDEF i32 RGFW_init_ptr(RGFW_info* info); + +/**! + * @brief Deinitializes a specific RGFW instance stored in the provided RGFW_info pointer. + * @param info A pointer to the RGFW_info structure representing the instance to deinitialize. +*/ +RGFWDEF void RGFW_deinit_ptr(RGFW_info* info); + +/**! + * @brief Sets the global RGFW_info structure pointer. + * @param info A pointer to the RGFW_info structure to set. +*/ +RGFWDEF void RGFW_setInfo(RGFW_info* info); + +/**! + * @brief Retrieves the global RGFW_info structure pointer. + * @return A pointer to the current RGFW_info structure. +*/ +RGFWDEF RGFW_info* RGFW_getInfo(void); + +/** @} */ +#endif /* RGFW_HEADER */ + +#if !defined(RGFW_NATIVE_HEADER) && (defined(RGFW_NATIVE) || defined(RGFW_IMPLEMENTATION)) +#define RGFW_NATIVE_HEADER + #if (defined(RGFW_OPENGL) || defined(RGFW_WEGL)) && defined(_MSC_VER) + #pragma comment(lib, "opengl32") + #endif + + #ifdef RGFW_OPENGL + struct RGFW_eglContext { + void* ctx; + void* surface; + struct wl_egl_window* eglWindow; + }; + + typedef union RGFW_gfxContext { + RGFW_glContext* native; + RGFW_eglContext* egl; + } RGFW_gfxContext; + + typedef RGFW_ENUM(u32, RGFW_gfxContextType) { + RGFW_gfxNativeOpenGL = RGFW_BIT(0), + RGFW_gfxEGL = RGFW_BIT(1), + RGFW_gfxOwnedByRGFW = RGFW_BIT(2) + }; + #endif + + /*! source data for the window (used by the APIs) */ + #ifdef RGFW_WINDOWS + + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef OEMRESOURCE + #define OEMRESOURCE + #endif + + #include + + struct RGFW_nativeImage { + HBITMAP bitmap; + u8* bitmapBits; + RGFW_format format; + HDC hdcMem; + }; + + #ifdef RGFW_OPENGL + struct RGFW_glContext { HGLRC ctx; }; + #endif + + struct RGFW_window_src { + HWND window; /*!< source window */ + HDC hdc; /*!< source HDC */ + HICON hIconSmall, hIconBig; /*!< source window icons */ + i32 maxSizeW, maxSizeH, minSizeW, minSizeH, aspectRatioW, aspectRatioH; /*!< for setting max/min resize (RGFW_WINDOWS) */ + RGFW_bool actionFrame; /* frame after a caption button was toggled (e.g. minimize, maximize or close) */ + WCHAR highSurrogate; + #ifdef RGFW_OPENGL + RGFW_gfxContext ctx; + RGFW_gfxContextType gfxType; + #endif + }; + +#elif defined(RGFW_UNIX) + #ifdef RGFW_X11 + #include + #include + + #ifndef RGFW_NO_XRANDR + #include + #include + #endif + #endif + + #ifdef RGFW_WAYLAND + #ifdef RGFW_LIBDECOR + #include + #endif + + #include + #include + #endif + + struct RGFW_nativeImage { + #ifdef RGFW_X11 + XImage* bitmap; + #endif + #ifdef RGFW_WAYLAND + struct wl_buffer* wl_buffer; + i32 fd; + struct wl_shm_pool* pool; + #endif + u8* buffer; + RGFW_format format; + }; + + #ifdef RGFW_OPENGL + struct RGFW_glContext { + #ifdef RGFW_X11 + struct __GLXcontextRec* ctx; /*!< source graphics context */ + Window window; + #endif + #ifdef RGFW_WAYLAND + RGFW_eglContext egl; + #endif + }; + #endif + + struct RGFW_window_src { + i32 x, y, w, h; + #ifdef RGFW_OPENGL + RGFW_gfxContext ctx; + RGFW_gfxContextType gfxType; + #endif +#ifdef RGFW_X11 + Window window; /*!< source window */ + Window parent; /*!< parent window */ + GC gc; + XIC ic; + u64 flashEnd; + #ifdef RGFW_ADVANCED_SMOOTH_RESIZE + i64 counter_value; + XID counter; + #endif +#endif /* RGFW_X11 */ + +#if defined(RGFW_WAYLAND) + struct wl_surface* surface; + struct xdg_surface* xdg_surface; + struct xdg_toplevel* xdg_toplevel; + struct zxdg_toplevel_decoration_v1* decoration; + struct zwp_locked_pointer_v1 *locked_pointer; + struct xdg_toplevel_icon_v1 *icon; + u32 decoration_mode; + /* State flags to configure the window */ + RGFW_bool pending_activated; + RGFW_bool activated; + RGFW_bool resizing; + RGFW_bool pending_maximized; + RGFW_bool maximized; + RGFW_bool minimized; + RGFW_bool configured; + + RGFW_bool using_custom_cursor; + struct wl_surface* custom_cursor_surface; + + RGFW_monitorNode* active_monitor; + + struct wl_data_source *data_source; // offer data to other clients + + #ifdef RGFW_LIBDECOR + struct libdecor* decorContext; + #endif +#endif /* RGFW_WAYLAND */ + }; + +#elif defined(RGFW_MACOS) + #include + + struct RGFW_nativeImage { + RGFW_format format; + u8* buffer; + void* rep; + }; + + #ifdef RGFW_OPENGL + struct RGFW_glContext { + void* ctx; + void* format; + }; + #endif + + struct RGFW_window_src { + void* window; + void* view; /* apple viewpoint thingy */ + void* mouse; + void* delegate; + #ifdef RGFW_OPENGL + RGFW_gfxContext ctx; + RGFW_gfxContextType gfxType; + #endif + }; + +#elif defined(RGFW_WASM) + + #include + #include + + struct RGFW_nativeImage { + RGFW_format format; + }; + + #ifdef RGFW_OPENGL + struct RGFW_glContext { + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx; + }; + #endif + + struct RGFW_window_src { + #ifdef RGFW_OPENGL + RGFW_gfxContext ctx; + RGFW_gfxContextType gfxType; + #endif + }; + +#endif + +struct RGFW_surface { + u8* data; + i32 w, h; + RGFW_format format; + RGFW_convertImageDataFunc convertFunc; + RGFW_nativeImage native; +}; + +/*! internal window data that is not specific to the OS */ +typedef struct RGFW_windowInternal { + /*! which key RGFW_window_shouldClose checks. Settting this to RGFW_keyNULL disables the feature. */ + RGFW_key exitKey; + i32 lastMouseX, lastMouseY; /*!< last cusor point (for raw mouse data) */ + + RGFW_bool shouldClose; + RGFW_bool rawMouse; + RGFW_bool captureMouse; + RGFW_bool inFocus; + RGFW_bool mouseInside; + RGFW_keymod mod; + RGFW_eventFlag enabledEvents; + u32 flags; /*!< windows flags (for RGFW to check and modify) */ + i32 oldX, oldY, oldW, oldH; +} RGFW_windowInternal; + +struct RGFW_window { + RGFW_window_src src; /*!< src window data */ + RGFW_windowInternal internal; /*!< internal window data that is not specific to the OS */ + void* userPtr; /* ptr for user data */ + i32 x, y, w, h; /*!< position and size of the window */ +}; /*!< window structure for the window */ + +typedef struct RGFW_windowState { + RGFW_bool mouseEnter; + RGFW_bool dataDragging; + RGFW_bool dataDrop; + size_t filesCount; + i32 dropX, dropY; + RGFW_window* win; /*!< it's not possible for one of these events to happen in the frame that the other event happened */ + + RGFW_bool mouseLeave; + RGFW_window* winLeave; /*!< if a mouse leaves one window and enters the next */ +} RGFW_windowState; + +typedef struct { + RGFW_bool current; + RGFW_bool prev; +} RGFW_keyState; + +struct RGFW_monitorNode { + RGFW_monitor mon; + RGFW_bool disconnected; + RGFW_monitorNode* next; +#ifdef RGFW_WAYLAND + u32 id; /* Add id so wl_outputs can be removed */ + struct wl_output *output; + struct zxdg_output_v1 *xdg_output; + RGFW_monitorMode* modes; + size_t modeCount; +#endif +#if defined(RGFW_X11) && !defined(RGFW_NO_XRANDR) + i32 screen; + RROutput rrOutput; + RRCrtc crtc; +#endif +#ifdef RGFW_WINDOWS + HMONITOR hMonitor; + WCHAR adapterName[32]; + WCHAR deviceName[32]; +#endif +#ifdef RGFW_MACOS + void* screen; + CGDirectDisplayID display; + u32 uintNum; +#endif +}; + +typedef struct RGFW_monitorList { + RGFW_monitorNode* head; + RGFW_monitorNode* cur; +} RGFW_monitorList; + +typedef struct RGFW_monitors { + RGFW_monitorList list; + RGFW_monitorList freeList; + size_t count; + + RGFW_monitorNode* primary; + RGFW_monitorNode data[RGFW_MAX_MONITORS]; +} RGFW_monitors; + +RGFWDEF RGFW_monitorNode* RGFW_monitors_add(const RGFW_monitor* mon); +RGFWDEF void RGFW_monitors_remove(RGFW_monitorNode* node, RGFW_monitorNode* prev); + +struct RGFW_info { + RGFW_window* root; + i32 windowCount; + + RGFW_mouse* hiddenMouse; + + RGFW_event events[RGFW_MAX_EVENTS]; /* A circular buffer (FIFO), using eventBottom/Len */ + + i32 eventBottom; + i32 eventLen; + RGFW_bool queueEvents; + RGFW_bool polledEvents; + + u32 apiKeycodes[RGFW_keyLast]; + #if defined(RGFW_X11) || defined(RGFW_WAYLAND) + RGFW_key keycodes[256]; + #elif defined(RGFW_WINDOWS) + RGFW_key keycodes[512]; + #elif defined(RGFW_MACOS) + RGFW_key keycodes[128]; + #elif defined(RGFW_WASM) + RGFW_key keycodes[256]; + #endif + + const char* className; + RGFW_bool useWaylandBool; + RGFW_bool stopCheckEvents_bool ; + u64 timerOffset; + + char* clipboard_data; + char* clipboard; /* for writing to the clipboard selection */ + size_t clipboard_len; + char filesSrc[RGFW_MAX_PATH * RGFW_MAX_DROPS]; + char** files; + #ifdef RGFW_X11 + Display* display; + XContext context; + Window helperWindow; + const char* instName; + XErrorEvent* x11Error; + i32 xrandrEventBase; + XIM im; + #endif + #ifdef RGFW_WAYLAND + struct wl_display* wl_display; + struct xkb_context *xkb_context; + struct xkb_keymap *keymap; + struct xkb_state *xkb_state; + struct zxdg_decoration_manager_v1 *decoration_manager; + struct zwp_relative_pointer_manager_v1 *relative_pointer_manager; + struct zwp_relative_pointer_v1 *relative_pointer; + struct zwp_pointer_constraints_v1 *constraint_manager; + struct xdg_toplevel_icon_manager_v1 *icon_manager; + + struct zxdg_output_manager_v1 *xdg_output_manager; + + struct wl_data_device_manager *data_device_manager; + struct wl_data_device *data_device; // supports clipboard and DND + struct wp_pointer_warp_v1* wp_pointer_warp; + + struct wl_keyboard* wl_keyboard; + struct wl_pointer* wl_pointer; + struct wl_compositor* compositor; + struct xdg_wm_base* xdg_wm_base; + struct wl_shm* shm; + struct wl_seat *seat; + struct wl_registry *registry; + u32 mouse_enter_serial; + struct wl_cursor_theme* wl_cursor_theme; + struct wl_surface* cursor_surface; + struct xkb_compose_state* composeState; + + RGFW_window* kbOwner; + RGFW_window* mouseOwner; /* what window has access to the mouse */ + + #endif + + RGFW_monitors monitors; + + #ifdef RGFW_UNIX + int eventWait_forceStop[3]; + i32 clock; + #endif + + #ifdef RGFW_MACOS + void* NSApp; + i64 flash; + void* customViewClasses[2]; /* NSView and NSOpenGLView */ + void* customNSAppDelegateClass; + void* customWindowDelegateClass; + void* customNSAppDelegate; + void* tisBundle; + #endif + + #ifdef RGFW_OPENGL + RGFW_window* current; + #endif + #ifdef RGFW_EGL + void* EGL_display; + #endif + + RGFW_bool rawMouse; /* global raw mouse toggle */ + + RGFW_windowState windowState; /*! for checking window state events */ + + RGFW_keyState mouseButtons[RGFW_mouseFinal]; + RGFW_keyState keyboard[RGFW_keyLast]; + float scrollX, scrollY; + float vectorX, vectorY; +}; +#endif /* RGFW_NATIVE_HEADER */ + +#ifdef RGFW_IMPLEMENTATION + +#ifndef RGFW_NO_MATH +#include +#endif + +/* global private API */ + +/* for C++ / C89 */ +RGFWDEF RGFW_window* RGFW_createWindowPlatform(const char* name, RGFW_windowFlags flags, RGFW_window* win); +RGFWDEF void RGFW_window_closePlatform(RGFW_window* win); + +RGFWDEF void RGFW_window_setFlagsInternal(RGFW_window* win, RGFW_windowFlags flags, RGFW_windowFlags cmpFlags); + +RGFWDEF void RGFW_initKeycodes(void); +RGFWDEF void RGFW_initKeycodesPlatform(void); +RGFWDEF void RGFW_resetPrevState(void); +RGFWDEF void RGFW_resetKey(void); +RGFWDEF void RGFW_unloadEGL(void); +RGFWDEF void RGFW_updateKeyModsEx(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll); +RGFWDEF void RGFW_updateKeyMods(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool scroll); +RGFWDEF void RGFW_window_showMouseFlags(RGFW_window* win, RGFW_bool show); +RGFWDEF void RGFW_updateKeyMod(RGFW_window* win, RGFW_keymod mod, RGFW_bool value); +RGFWDEF void RGFW_monitors_refresh(void); + +RGFWDEF void RGFW_windowMaximizedCallback(RGFW_window* win, i32 x, i32 y, i32 w, i32 h); +RGFWDEF void RGFW_windowMinimizedCallback(RGFW_window* win); +RGFWDEF void RGFW_windowRestoredCallback(RGFW_window* win, i32 x, i32 y, i32 w, i32 h); +RGFWDEF void RGFW_windowMovedCallback(RGFW_window* win, i32 x, i32 y); +RGFWDEF void RGFW_windowResizedCallback(RGFW_window* win, i32 w, i32 h); +RGFWDEF void RGFW_windowQuitCallback(RGFW_window* win); +RGFWDEF void RGFW_mousePosCallback(RGFW_window* win, i32 x, i32 y, float vecX, float vecY); +RGFWDEF void RGFW_windowRefreshCallback(RGFW_window* win); +RGFWDEF void RGFW_focusCallback(RGFW_window* win, RGFW_bool inFocus); +RGFWDEF void RGFW_mouseNotifyCallback(RGFW_window* win, i32 x, i32 y, RGFW_bool status); +RGFWDEF void RGFW_dataDropCallback(RGFW_window* win, char** files, size_t count); +RGFWDEF void RGFW_dataDragCallback(RGFW_window* win, i32 x, i32 y); +RGFWDEF void RGFW_keyCharCallback(RGFW_window* win, u32 codepoint); +RGFWDEF void RGFW_keyCallback(RGFW_window* win, RGFW_key key, RGFW_keymod mod, RGFW_bool repeat, RGFW_bool press); +RGFWDEF void RGFW_mouseButtonCallback(RGFW_window* win, RGFW_mouseButton button, RGFW_bool press); +RGFWDEF void RGFW_mouseScrollCallback(RGFW_window* win, float x, float y); +RGFWDEF void RGFW_scaleUpdatedCallback(RGFW_window* win, float scaleX, float scaleY); +RGFWDEF void RGFW_monitorCallback(RGFW_window* win, const RGFW_monitor* monitor, RGFW_bool connected); + +RGFWDEF void RGFW_setBit(u32* var, u32 mask, RGFW_bool set); +RGFWDEF void RGFW_splitBPP(u32 bpp, RGFW_monitorMode* mode); + +RGFWDEF void RGFW_window_captureMousePlatform(RGFW_window* win, RGFW_bool state); +RGFWDEF void RGFW_window_setRawMouseModePlatform(RGFW_window *win, RGFW_bool state); + +RGFWDEF void RGFW_copyImageData64(u8* dest_data, i32 w, i32 h, RGFW_format dest_format, + u8* src_data, RGFW_format src_format, RGFW_bool is64bit, RGFW_convertImageDataFunc func); + +RGFWDEF RGFW_bool RGFW_loadEGL(void); + +#ifdef RGFW_OPENGL +typedef struct RGFW_attribStack { + i32* attribs; + size_t count; + size_t max; +} RGFW_attribStack; +RGFWDEF void RGFW_attribStack_init(RGFW_attribStack* stack, i32* attribs, size_t max); +RGFWDEF void RGFW_attribStack_pushAttrib(RGFW_attribStack* stack, i32 attrib); +RGFWDEF void RGFW_attribStack_pushAttribs(RGFW_attribStack* stack, i32 attrib1, i32 attrib2); + +RGFWDEF RGFW_bool RGFW_extensionSupportedStr(const char* extensions, const char* ext, size_t len); +#endif + +#ifdef RGFW_X11 +RGFWDEF void RGFW_XCreateWindow (XVisualInfo visual, const char* name, RGFW_windowFlags flags, RGFW_window* win); +#endif +#ifdef RGFW_MACOS +RGFWDEF void RGFW_osx_initView(RGFW_window* win); +#endif +/* end of global private API defs */ + +RGFW_info* _RGFW = NULL; +void RGFW_setInfo(RGFW_info* info) { _RGFW = info; } +RGFW_info* RGFW_getInfo(void) { return _RGFW; } + + +void* RGFW_alloc(size_t size) { return RGFW_ALLOC(size); } +void RGFW_free(void* ptr) { RGFW_FREE(ptr); } + +void RGFW_useWayland(RGFW_bool wayland) { RGFW_init(); _RGFW->useWaylandBool = RGFW_BOOL(wayland); } +RGFW_bool RGFW_usingWayland(void) { return _RGFW->useWaylandBool; } + +void RGFW_setRawMouseMode(RGFW_bool state) { + _RGFW->rawMouse = state; + RGFW_window_setRawMouseModePlatform(_RGFW->root, state); +} + +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_IMPLEMENTATION starts with generic RGFW defines + +This is the start of keycode data +*/ + + + +void RGFW_initKeycodes(void) { + RGFW_MEMSET(_RGFW->keycodes, 0, sizeof(_RGFW->keycodes)); + RGFW_initKeycodesPlatform(); + size_t i, y; + for (i = 0; i < RGFW_keyLast; i++) { + for (y = 0; y < (sizeof(_RGFW->keycodes) / sizeof(RGFW_key)); y++) { + if (_RGFW->keycodes[y] == i) { + _RGFW->apiKeycodes[i] = (RGFW_key)y; + break; + } + } + } + + + RGFW_resetKey(); +} + +RGFW_key RGFW_apiKeyToRGFW(u32 keycode) { + /* make sure the key isn't out of bounds */ + if (keycode > (sizeof(_RGFW->keycodes) / sizeof(RGFW_key))) + return 0; + + return _RGFW->keycodes[keycode]; +} + +u32 RGFW_rgfwToApiKey(RGFW_key keycode) { + /* make sure the key isn't out of bounds */ + return _RGFW->apiKeycodes[keycode]; +} + +void RGFW_resetKey(void) { RGFW_MEMSET(_RGFW->keyboard, 0, sizeof(_RGFW->keyboard)); } +/* + this is the end of keycode data +*/ + +/* + 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 +*/ +#define RGFW_CALLBACK_DEFINE(x, x2) \ +RGFW_##x##func RGFW_##x##CallbackSrc = NULL; \ +RGFW_##x##func RGFW_set##x2##Callback(RGFW_##x##func func) { \ + RGFW_##x##func prev = RGFW_##x##CallbackSrc; \ + RGFW_##x##CallbackSrc = 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(dataDrop, DataDrop) +RGFW_CALLBACK_DEFINE(dataDrag, DataDrag) +RGFW_CALLBACK_DEFINE(key, Key) +RGFW_CALLBACK_DEFINE(keyChar, KeyChar) +RGFW_CALLBACK_DEFINE(mouseButton, MouseButton) +RGFW_CALLBACK_DEFINE(mouseScroll, MouseScroll) +RGFW_CALLBACK_DEFINE(scaleUpdated, ScaleUpdated) +RGFW_CALLBACK_DEFINE(monitor, Monitor) +RGFW_CALLBACK_DEFINE(debug, Debug) +#define RGFW_debugCallback(type, err, msg) if (RGFW_debugCallbackSrc) RGFW_debugCallbackSrc(type, err, msg); +#undef RGFW_CALLBACK_DEFINE + +void RGFW_windowMaximizedCallback(RGFW_window* win, i32 x, i32 y, i32 w, i32 h) { + win->internal.flags |= RGFW_windowMaximize; + + if (!(win->internal.enabledEvents & RGFW_windowMaximizedFlag)) return; + + RGFW_event event; + event.type = RGFW_windowMaximized; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_windowMaximizedCallbackSrc) RGFW_windowMaximizedCallbackSrc(win, x, y, w, h); +} + +void RGFW_windowMinimizedCallback(RGFW_window* win) { + win->internal.flags |= RGFW_windowMinimize; + + if (!(win->internal.enabledEvents & RGFW_windowMinimizedFlag)) return; + + RGFW_event event; + event.type = RGFW_windowMinimized; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_windowMinimizedCallbackSrc) RGFW_windowMinimizedCallbackSrc(win); +} + +void RGFW_windowRestoredCallback(RGFW_window* win, i32 x, i32 y, i32 w, i32 h) { + win->internal.flags &= ~(u32)RGFW_windowMinimize; + if (RGFW_window_isMaximized(win) == RGFW_FALSE) win->internal.flags &= ~(u32)RGFW_windowMaximize; + + if (!(win->internal.enabledEvents & RGFW_windowRestoredFlag)) return; + + RGFW_event event; + event.type = RGFW_windowRestored; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_windowRestoredCallbackSrc) RGFW_windowRestoredCallbackSrc(win, x, y, w, h); +} + +void RGFW_windowMovedCallback(RGFW_window* win, i32 x, i32 y) { + win->x = x; + win->y = y; + if (!(win->internal.enabledEvents & RGFW_windowMovedFlag)) return; + + RGFW_event event; + event.type = RGFW_windowMoved; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_windowMovedCallbackSrc) RGFW_windowMovedCallbackSrc(win, x, y); +} + +void RGFW_windowResizedCallback(RGFW_window* win, i32 w, i32 h) { + win->w = w; + win->h = h; + + if (!(win->internal.enabledEvents & RGFW_windowResizedFlag)) return; + RGFW_event event; + event.type = RGFW_windowResized; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_windowResizedCallbackSrc) RGFW_windowResizedCallbackSrc(win, w, h); +} + +void RGFW_windowQuitCallback(RGFW_window* win) { + win->internal.shouldClose = RGFW_TRUE; + + RGFW_event event; + event.type = RGFW_quit; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_windowQuitCallbackSrc) RGFW_windowQuitCallbackSrc(win); +} + +void RGFW_mousePosCallback(RGFW_window* win, i32 x, i32 y, float vecX, float vecY) { + win->internal.lastMouseX = x; + win->internal.lastMouseY = y; + _RGFW->vectorX = vecX; + _RGFW->vectorY = vecY; + + if (!(win->internal.enabledEvents & RGFW_mousePosChangedFlag)) return; + + RGFW_event event; + event.type = RGFW_mousePosChanged; + event.mouse.x = x; + event.mouse.y = y; + event.mouse.vecX = vecX; + event.mouse.vecY = vecY; + event.common.win = win; + + RGFW_eventQueuePush(&event); + + if (RGFW_mousePosCallbackSrc) RGFW_mousePosCallbackSrc(win, x, y, vecX, vecY); +} + +void RGFW_windowRefreshCallback(RGFW_window* win) { + if (!(win->internal.enabledEvents & RGFW_windowRefreshFlag)) return; + RGFW_event event; + event.type = RGFW_windowRefresh; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_windowRefreshCallbackSrc) RGFW_windowRefreshCallbackSrc(win); +} + +void RGFW_focusCallback(RGFW_window* win, RGFW_bool inFocus) { + win->internal.inFocus = inFocus; + + if (win->internal.captureMouse) { + RGFW_window_captureMousePlatform(win, inFocus); + } + + RGFW_event event; + event.common.win = win; + + if (inFocus == RGFW_TRUE) { + if ((win->internal.flags & RGFW_windowFullscreen)) + RGFW_window_raise(win); + + event.type = RGFW_focusIn; + } else if (inFocus == RGFW_FALSE) { + if ((win->internal.flags & RGFW_windowFullscreen)) + RGFW_window_minimize(win); + + size_t key; + for (key = 0; key < RGFW_keyLast; key++) { + if (RGFW_isKeyDown((u8)key) == RGFW_FALSE) continue; + + _RGFW->keyboard[key].current = RGFW_FALSE; + if ((win->internal.enabledEvents & RGFW_BIT(RGFW_keyReleased))) { + RGFW_keyCallback(win, (u8)key, win->internal.mod, RGFW_FALSE, RGFW_FALSE); + } + } + + RGFW_resetKey(); + event.type = RGFW_focusOut; + } + + event.common.win = win; + + RGFW_eventQueuePush(&event); + + if (RGFW_focusCallbackSrc) RGFW_focusCallbackSrc(win, inFocus); +} + +void RGFW_mouseNotifyCallback(RGFW_window* win, i32 x, i32 y, RGFW_bool status) { + win->internal.mouseInside = status; + _RGFW->windowState.win = win; + + win->internal.lastMouseX = x; + win->internal.lastMouseY = y; + + RGFW_event event; + event.common.win = win; + event.mouse.x = x; + event.mouse.y = y; + + if (status) { + if (!(win->internal.enabledEvents & RGFW_mouseEnterFlag)) return; + _RGFW->windowState.mouseEnter = RGFW_TRUE; + _RGFW->windowState.win = win; + event.type = RGFW_mouseEnter; + } else { + if (!(win->internal.enabledEvents & RGFW_mouseLeaveFlag)) return; + _RGFW->windowState.winLeave = win; + _RGFW->windowState.mouseLeave = RGFW_TRUE; + event.type = RGFW_mouseLeave; + } + + RGFW_eventQueuePush(&event); + + if (RGFW_mouseNotifyCallbackSrc) RGFW_mouseNotifyCallbackSrc(win, x, y, status); +} + +void RGFW_dataDropCallback(RGFW_window* win, char** files, size_t count) { + if (!(win->internal.enabledEvents & RGFW_dataDropFlag) || !(win->internal.flags & RGFW_windowAllowDND)) + return; + + _RGFW->windowState.win = win; + _RGFW->windowState.dataDrop = RGFW_TRUE; + _RGFW->windowState.filesCount = count; + + RGFW_event event; + event.type = RGFW_dataDrop; + event.drop.files = files; + event.drop.count = count; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_dataDropCallbackSrc) RGFW_dataDropCallbackSrc(win, files, count); +} + +void RGFW_dataDragCallback(RGFW_window* win, i32 x, i32 y) { + _RGFW->windowState.win = win; + _RGFW->windowState.dataDragging = RGFW_TRUE; + _RGFW->windowState.dropX = x; + _RGFW->windowState.dropY = y; + + if (win->internal.enabledEvents & RGFW_dataDragFlag) return; + + RGFW_event event; + event.type = RGFW_dataDrag; + event.drag.x = x; + event.drag.y = y; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_dataDragCallbackSrc) RGFW_dataDragCallbackSrc(win, x, y); +} + +void RGFW_keyCharCallback(RGFW_window* win, u32 codepoint) { + if (!(win->internal.enabledEvents & RGFW_keyCharFlag)) return; + + RGFW_event event; + event.type = RGFW_keyChar; + event.keyChar.value = codepoint; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_keyCharCallbackSrc) RGFW_keyCharCallbackSrc(win, codepoint); +} + +void RGFW_keyCallback(RGFW_window* win, RGFW_key key, RGFW_keymod mod, RGFW_bool repeat, RGFW_bool press) { + RGFW_event event; + + if (press) { + if (!(win->internal.enabledEvents & RGFW_keyPressedFlag)) return; + event.type = RGFW_keyPressed; + } else { + if (!(win->internal.enabledEvents & RGFW_keyReleasedFlag)) return; + event.type = RGFW_keyReleased; + } + + _RGFW->keyboard[key].prev = _RGFW->keyboard[key].current; + _RGFW->keyboard[key].current = press; + + event.key.value = key; + event.key.mod = repeat; + event.key.mod = mod; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_keyCallbackSrc) RGFW_keyCallbackSrc(win, key, mod, repeat, press); +} + +void RGFW_mouseButtonCallback(RGFW_window* win, RGFW_mouseButton button, RGFW_bool press) { + RGFW_event event; + if (press) { + if (!(win->internal.enabledEvents & RGFW_mouseButtonPressedFlag)) return; + event.type = RGFW_mouseButtonPressed; + } else { + if (!(win->internal.enabledEvents & RGFW_mouseButtonReleasedFlag)) return; + event.type = RGFW_mouseButtonReleased; + } + + _RGFW->mouseButtons[button].prev = _RGFW->mouseButtons[button].current; + _RGFW->mouseButtons[button].current = press; + + event.button.value = button; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_mouseButtonCallbackSrc) RGFW_mouseButtonCallbackSrc(win, button, press); +} + +void RGFW_mouseScrollCallback(RGFW_window* win, float x, float y) { + if (!(win->internal.enabledEvents & RGFW_mouseScrollFlag)) return; + _RGFW->scrollX = x; + _RGFW->scrollY = y; + + RGFW_event event; + event.type = RGFW_mouseScroll; + event.scroll.x = x; + event.scroll.y = y; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_mouseScrollCallbackSrc) RGFW_mouseScrollCallbackSrc(win, x, y); +} + +void RGFW_scaleUpdatedCallback(RGFW_window* win, float scaleX, float scaleY) { + if (win->internal.flags & RGFW_windowScaleToMonitor) RGFW_window_scaleToMonitor(win); + if (!(win->internal.enabledEvents & RGFW_scaleUpdatedFlag)) return; + + RGFW_event event; + event.type = RGFW_scaleUpdated; + event.scale.x = scaleX; + event.scale.y = scaleY; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_scaleUpdatedCallbackSrc) RGFW_scaleUpdatedCallbackSrc(win, scaleX, scaleY); +} + +void RGFW_monitorCallback(RGFW_window* win, const RGFW_monitor* monitor, RGFW_bool connected) { + if (win) { + if (connected && !(win->internal.enabledEvents & RGFW_monitorConnectedFlag)) return; + if (!connected && !(win->internal.enabledEvents & RGFW_monitorDisconnectedFlag)) return; + } + + RGFW_event event; + event.type = (connected) ? (RGFW_monitorConnected) : (RGFW_monitorDisconnected); + event.monitor.monitor = monitor; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_monitorCallbackSrc) RGFW_monitorCallbackSrc(win, monitor, connected); +} + +#ifdef RGFW_DEBUG +#include +#endif + +void RGFW_sendDebugInfo(RGFW_debugType type, RGFW_errorCode err, const char* msg) { + RGFW_debugCallback(type, err, msg); + + #ifdef RGFW_DEBUG + switch (type) { + case RGFW_typeInfo: RGFW_PRINTF("RGFW INFO (%i %i): %s", type, err, msg); break; + case RGFW_typeError: RGFW_PRINTF("RGFW DEBUG (%i %i): %s", type, err, msg); break; + case RGFW_typeWarning: RGFW_PRINTF("RGFW WARNING (%i %i): %s", type, err, msg); break; + default: break; + } + + RGFW_PRINTF("\n"); + #endif +} + +void RGFW_window_checkMode(RGFW_window* win); +void RGFW_window_checkMode(RGFW_window* win) { + if (RGFW_window_isMinimized(win) && (win->internal.enabledEvents & RGFW_windowMinimizedFlag)) { + RGFW_windowMinimizedCallback(win); + } else if (RGFW_window_isMaximized(win) && (win->internal.enabledEvents & RGFW_windowMaximizedFlag)) { + RGFW_windowMaximizedCallback(win, win->x, win->y, win->w, win->h); + } else if ((((win->internal.flags & RGFW_windowMinimize) && !RGFW_window_isMaximized(win)) || + (win->internal.flags & RGFW_windowMaximize && !RGFW_window_isMaximized(win))) && (win->internal.enabledEvents & RGFW_windowRestoredFlag)) { + RGFW_windowRestoredCallback(win, win->x, win->y, win->w, win->h); + } +} + +/* +no more event call back defines +*/ + +size_t RGFW_sizeofInfo(void) { return sizeof(RGFW_info); } +size_t RGFW_sizeofNativeImage(void) { return sizeof(RGFW_nativeImage); } +size_t RGFW_sizeofSurface(void) { return sizeof(RGFW_surface); } +size_t RGFW_sizeofWindow(void) { return sizeof(RGFW_window); } +size_t RGFW_sizeofWindowSrc(void) { return sizeof(RGFW_window_src); } + +RGFW_window_src* RGFW_window_getSrc(RGFW_window* win) { return &win->src; } +RGFW_bool RGFW_window_getPosition(RGFW_window* win, i32* x, i32* y) { if (x) *x = win->x; if (y) *y = win->y; return RGFW_TRUE; } +RGFW_bool RGFW_window_getSize(RGFW_window* win, i32* w, i32* h) { if (w) *w = win->w; if (h) *h = win->h; return RGFW_TRUE; } +u32 RGFW_window_getFlags(RGFW_window* win) { return win->internal.flags; } +RGFW_key RGFW_window_getExitKey(RGFW_window* win) { return win->internal.exitKey; } +void RGFW_window_setExitKey(RGFW_window* win, RGFW_key key) { win->internal.exitKey = key; } +void RGFW_window_setEnabledEvents(RGFW_window* win, RGFW_eventFlag events) { win->internal.enabledEvents = events; } +RGFW_eventFlag RGFW_window_getEnabledEvents(RGFW_window* win) { return win->internal.enabledEvents; } +void RGFW_window_setDisabledEvents(RGFW_window* win, RGFW_eventFlag events) { RGFW_window_setEnabledEvents(win, (RGFW_allEventFlags) & ~(u32)events); } +void RGFW_window_setEventState(RGFW_window* win, RGFW_eventFlag event, RGFW_bool state) { RGFW_setBit(&win->internal.enabledEvents, event, state); } +void* RGFW_window_getUserPtr(RGFW_window* win) { return win->userPtr; } +void RGFW_window_setUserPtr(RGFW_window* win, void* ptr) { win->userPtr = ptr; } + +RGFW_bool RGFW_window_getSizeInPixels(RGFW_window* win, i32* w, i32* h) { + RGFW_monitor* mon = RGFW_window_getMonitor(win); + if (mon == NULL) return RGFW_FALSE; + + if (w) *w = (i32)((float)win->w * mon->pixelRatio); + if (h) *h = (i32)((float)win->h * mon->pixelRatio); + + return RGFW_TRUE; +} + + +#if defined(RGFW_USE_XDL) && defined(RGFW_X11) + #define XDL_IMPLEMENTATION + #include "XDL.h" +#endif + +#ifndef RGFW_FORCE_INIT +RGFW_info _rgfwGlobal; +#endif + +i32 RGFW_init(void) { return RGFW_init_ptr(&_rgfwGlobal); } +void RGFW_deinit(void) { RGFW_deinit_ptr(&_rgfwGlobal); } + +i32 RGFW_initPlatform(void); +void RGFW_deinitPlatform(void); + +i32 RGFW_init_ptr(RGFW_info* info) { + if (info == _RGFW || info == NULL) return 1; + + RGFW_setInfo(info); + RGFW_MEMSET(_RGFW, 0, sizeof(RGFW_info)); + _RGFW->queueEvents = RGFW_FALSE; + _RGFW->polledEvents = RGFW_FALSE; +#ifdef RGFW_WAYLAND + _RGFW->useWaylandBool = RGFW_TRUE; +#endif + + _RGFW->files = (char**)(void*)_RGFW->filesSrc; + u32 i; + for (i = 0; i < RGFW_MAX_DROPS; i++) + _RGFW->files[i] = (char*)(_RGFW->filesSrc + RGFW_MAX_DROPS + (i * RGFW_MAX_PATH)); + + _RGFW->monitors.freeList.head = &_RGFW->monitors.data[0]; + _RGFW->monitors.freeList.cur = _RGFW->monitors.freeList.head; + + for (i = 1; i < RGFW_MAX_MONITORS; i++) { + RGFW_monitorNode* newNode = &_RGFW->monitors.data[i]; + _RGFW->monitors.freeList.cur->next = newNode; + _RGFW->monitors.freeList.cur = _RGFW->monitors.freeList.cur->next; + } + + _RGFW->monitors.list.head = NULL; + _RGFW->monitors.list.head = NULL; + RGFW_initKeycodes(); + i32 out = RGFW_initPlatform(); + + RGFW_pollMonitors(); + + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, "global context initialized"); + + return out; +} + +#ifndef RGFW_EGL +void RGFW_unloadEGL(void) { } +#endif + +void RGFW_deinit_ptr(RGFW_info* info) { + if (info == NULL) return; + + RGFW_setInfo(info); + RGFW_unloadEGL(); + RGFW_deinitPlatform(); + + _RGFW->root = NULL; + _RGFW->windowCount = 0; + RGFW_setInfo(NULL); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, "global context deinitialized"); +} + +RGFW_window* RGFW_createWindow(const char* name, i32 x, i32 y, i32 w, i32 h, RGFW_windowFlags flags) { + RGFW_window* win = (RGFW_window*)RGFW_ALLOC(sizeof(RGFW_window)); + RGFW_ASSERT(win != NULL); + return RGFW_createWindowPtr(name, x, y, w, h, flags, win); +} + +void RGFW_window_close(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_window_closePtr(win); + RGFW_FREE(win); +} + +RGFW_window* RGFW_createWindowPtr(const char* name, i32 x, i32 y, i32 w, i32 h, RGFW_windowFlags flags, RGFW_window* win) { + RGFW_ASSERT(win != NULL); + if (name == NULL) name = "\0"; + + RGFW_MEMSET(win, 0, sizeof(RGFW_window)); + + if (_RGFW == NULL) RGFW_init(); + _RGFW->windowCount++; + + /* rect based the requested flags */ + if (_RGFW->root == NULL) { + RGFW_setRootWindow(win); + } + + /* set and init the new window's data */ + win->x = x; + win->y = y; + win->w = w; + win->h = h; + win->internal.flags = flags; + win->internal.enabledEvents = RGFW_allEventFlags; + + RGFW_window* ret = RGFW_createWindowPlatform(name, flags, win); + +#ifndef RGFW_X11 + RGFW_window_setFlagsInternal(win, flags, 0); +#endif + +#ifdef RGFW_OPENGL + win->src.gfxType = 0; + if (flags & RGFW_windowOpenGL) + RGFW_window_createContext_OpenGL(win, RGFW_getGlobalHints_OpenGL()); +#endif + +#ifdef RGFW_EGL + if (flags & RGFW_windowEGL) + RGFW_window_createContext_EGL(win, RGFW_getGlobalHints_OpenGL()); +#endif + + /* X11 creates the window after the OpenGL context is created (because of visual garbage), + * so we have to wait to set the flags + * This is required so that way the user can create their own OpenGL context after RGFW_createWindow is used + * if a window is created, CreateContext will delete the window and create a new one + * */ +#ifdef RGFW_X11 + RGFW_window_setFlagsInternal(win, flags, 0); +#endif + +#ifdef RGFW_MACOS + /*NOTE: another OpenGL/setFlags related hack, this because OSX the 'view' class must be setup after the NSOpenGL view is made AND after setFlags happens */ + RGFW_osx_initView(win); +#endif + +#ifdef RGFW_WAYLAND + /* recieve all events needed to configure the surface */ + /* also gets the wl_outputs */ + if (RGFW_usingWayland()) { + wl_display_roundtrip(_RGFW->wl_display); + /* NOTE: this is a hack so that way wayland spawns a window, even if nothing is drawn */ + if (!(flags & RGFW_windowOpenGL) && !(flags & RGFW_windowEGL)) { + u8* data = (u8*)RGFW_ALLOC((u32)(win->w * win->h * 3)); + RGFW_MEMSET(data, 0, (u32)(win->w * win->h * 3) * sizeof(u8)); + RGFW_surface* surface = RGFW_createSurface(data, win->w, win->h, RGFW_formatBGR8); + RGFW_window_blitSurface(win, surface); + RGFW_FREE(data); + RGFW_surface_free(surface); + } + } +#endif + + if (!(flags & RGFW_windowHideMouse)) { + RGFW_window_setMouseDefault(win); + } + + RGFW_window_setName(win, name); + if (!(flags & RGFW_windowHide)) { + flags |= RGFW_windowHide; + RGFW_window_show(win); + } + + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, "a new window was created"); + + return ret; +} + +void RGFW_window_closePtr(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + if (win->internal.captureMouse) { + RGFW_window_captureMouse(win, RGFW_FALSE); + } + + #ifdef RGFW_EGL + if ((win->src.gfxType & RGFW_gfxEGL) && win->src.ctx.egl) { + RGFW_window_deleteContext_EGL(win, win->src.ctx.egl); + win->src.ctx.egl = NULL; + } + #endif + + #ifdef RGFW_OPENGL + if ((win->src.gfxType & RGFW_gfxNativeOpenGL) && win->src.ctx.native) { + RGFW_window_deleteContext_OpenGL(win, win->src.ctx.native); + win->src.ctx.native = NULL; + } + #endif + + RGFW_window_closePlatform(win); + + RGFW_clipboard_switch(NULL); + + _RGFW->windowCount--; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, "a window was freed"); + + if (_RGFW->windowCount == 0 && !(win->internal.flags & RGFW_noDeinitOnClose)) RGFW_deinit(); +} + +void RGFW_setQueueEvents(RGFW_bool queue) { _RGFW->queueEvents = RGFW_BOOL(queue); } + +void RGFW_eventQueueFlush(void) { _RGFW->eventLen = 0; } + +void RGFW_eventQueuePush(const RGFW_event* event) { + if (_RGFW->queueEvents == RGFW_FALSE) return; + RGFW_ASSERT(_RGFW->eventLen >= 0); + + if (_RGFW->eventLen >= RGFW_MAX_EVENTS) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errEventQueue, "Event queue limit 'RGFW_MAX_EVENTS' has been reached automatically flushing queue."); + RGFW_eventQueueFlush(); + return; + } + + i32 eventTop = (_RGFW->eventBottom + _RGFW->eventLen) % RGFW_MAX_EVENTS; + _RGFW->eventLen += 1; + _RGFW->events[eventTop] = *event; +} + +RGFW_event* RGFW_eventQueuePop(void) { + RGFW_ASSERT(_RGFW->eventLen >= 0 && _RGFW->eventLen <= RGFW_MAX_EVENTS); + RGFW_event* ev; + + if (_RGFW->eventLen == 0) { + return NULL; + } + + ev = &_RGFW->events[_RGFW->eventBottom]; + _RGFW->eventLen -= 1; + _RGFW->eventBottom = (_RGFW->eventBottom + 1) % RGFW_MAX_EVENTS; + + return ev; +} + +RGFW_bool RGFW_checkEvent(RGFW_event* event) { + if (_RGFW->eventLen == 0 && _RGFW->polledEvents == RGFW_FALSE) { + _RGFW->queueEvents = RGFW_TRUE; + RGFW_pollEvents(); + _RGFW->polledEvents = RGFW_TRUE; + } + + if (RGFW_checkQueuedEvent(event) == RGFW_FALSE) { + _RGFW->polledEvents = RGFW_FALSE; + return RGFW_FALSE; + } + + return RGFW_TRUE; +} + +RGFW_bool RGFW_checkQueuedEvent(RGFW_event* event) { + RGFW_event* ev; + _RGFW->queueEvents = RGFW_TRUE; + /* check queued events */ + ev = RGFW_eventQueuePop(); + if (ev != NULL) { + *event = *ev; + return RGFW_TRUE; + } + + return RGFW_FALSE; +} + +void RGFW_resetPrevState(void) { + size_t i; /*!< reset each previous state */ + for (i = 0; i < RGFW_keyLast; i++) _RGFW->keyboard[i].prev = _RGFW->keyboard[i].current; + for (i = 0; i < RGFW_mouseFinal; i++) _RGFW->mouseButtons[i].prev = _RGFW->mouseButtons[i].current; + _RGFW->scrollX = 0.0f; + _RGFW->scrollY = 0.0f; + _RGFW->vectorX = (float)0.0f; + _RGFW->vectorY = (float)0.0f; + RGFW_MEMSET(&_RGFW->windowState, 0, sizeof(_RGFW->windowState)); +} + +RGFW_bool RGFW_isKeyPressed(RGFW_key key) { + RGFW_ASSERT(_RGFW != NULL); + return _RGFW->keyboard[key].current && !_RGFW->keyboard[key].prev; +} +RGFW_bool RGFW_isKeyDown(RGFW_key key) { + RGFW_ASSERT(_RGFW != NULL); + return _RGFW->keyboard[key].current; +} +RGFW_bool RGFW_isKeyReleased(RGFW_key key) { + RGFW_ASSERT(_RGFW != NULL); + return !_RGFW->keyboard[key].current && _RGFW->keyboard[key].prev; +} + + +RGFW_bool RGFW_isMousePressed(RGFW_mouseButton button) { + RGFW_ASSERT(_RGFW != NULL); + return _RGFW->mouseButtons[button].current && !_RGFW->mouseButtons[button].prev; +} +RGFW_bool RGFW_isMouseDown(RGFW_mouseButton button) { + RGFW_ASSERT(_RGFW != NULL); + return _RGFW->mouseButtons[button].current; +} +RGFW_bool RGFW_isMouseReleased(RGFW_mouseButton button) { + RGFW_ASSERT(_RGFW != NULL); + return !_RGFW->mouseButtons[button].current && _RGFW->mouseButtons[button].prev; +} + +void RGFW_getMouseScroll(float* x, float* y) { + RGFW_ASSERT(_RGFW != NULL); + if (x) *x = _RGFW->scrollX; + if (y) *y = _RGFW->scrollY; +} + +void RGFW_getMouseVector(float* x, float* y) { + RGFW_ASSERT(_RGFW != NULL); + if (x) *x = _RGFW->vectorX; + if (y) *y = _RGFW->vectorY; +} + +RGFW_bool RGFW_window_didMouseLeave(RGFW_window* win) { return _RGFW->windowState.winLeave == win && _RGFW->windowState.mouseLeave; } +RGFW_bool RGFW_window_didMouseEnter(RGFW_window* win) { return _RGFW->windowState.win == win && _RGFW->windowState.mouseEnter; } +RGFW_bool RGFW_window_isMouseInside(RGFW_window* win) { return win->internal.mouseInside; } + +RGFW_bool RGFW_window_isDataDragging(RGFW_window* win) { return RGFW_window_getDataDrag(win, (i32*)NULL, (i32*)NULL); } +RGFW_bool RGFW_window_didDataDrop(RGFW_window* win) { return RGFW_window_getDataDrop(win, (const char***)NULL, (size_t*)NULL);} + + +RGFW_bool RGFW_window_getDataDrag(RGFW_window* win, i32* x, i32* y) { + if (_RGFW->windowState.win != win || _RGFW->windowState.dataDragging == RGFW_FALSE) return RGFW_FALSE; + if (x) *x = _RGFW->windowState.dropX; + if (y) *y = _RGFW->windowState.dropY; + return RGFW_TRUE; +} +RGFW_bool RGFW_window_getDataDrop(RGFW_window* win, const char*** files, size_t* count) { + if (_RGFW->windowState.win != win || _RGFW->windowState.dataDrop == RGFW_FALSE) return RGFW_FALSE; + if (files) *files = (const char**)_RGFW->files; + if (count) *count = _RGFW->windowState.filesCount; + return RGFW_TRUE; +} + +RGFW_bool RGFW_window_checkEvent(RGFW_window* win, RGFW_event* event) { + if (_RGFW->eventLen == 0 && _RGFW->polledEvents == RGFW_FALSE) { + _RGFW->queueEvents = RGFW_TRUE; + RGFW_pollEvents(); + _RGFW->polledEvents = RGFW_TRUE; + } + + if (RGFW_window_checkQueuedEvent(win, event) == RGFW_FALSE) { + _RGFW->polledEvents = RGFW_FALSE; + return RGFW_FALSE; + } + + return RGFW_TRUE; +} + +RGFW_bool RGFW_window_checkQueuedEvent(RGFW_window* win, RGFW_event* event) { + RGFW_event* ev; + RGFW_ASSERT(win != NULL); + _RGFW->queueEvents = RGFW_TRUE; + /* check queued events */ + ev = RGFW_window_eventQueuePop(win); + if (ev == NULL) return RGFW_FALSE; + + *event = *ev; + return RGFW_TRUE; +} + +RGFW_event* RGFW_window_eventQueuePop(RGFW_window* win) { + RGFW_event* ev = RGFW_eventQueuePop(); + if (ev == NULL) return ev; + + for (i32 i = 1; i < _RGFW->eventLen && ev->common.win != win && ev->common.win != NULL; i++) { + RGFW_eventQueuePush(ev); + ev = RGFW_eventQueuePop(); + } + + if (ev->common.win != win && ev->common.win != NULL) { + return NULL; + } + + return ev; +} + +void RGFW_setRootWindow(RGFW_window* win) { _RGFW->root = win; } +RGFW_window* RGFW_getRootWindow(void) { return _RGFW->root; } + +#ifndef RGFW_EGL +RGFW_bool RGFW_loadEGL(void) { return RGFW_FALSE; } +#endif + +void RGFW_window_setFlagsInternal(RGFW_window* win, RGFW_windowFlags flags, RGFW_windowFlags cmpFlags) { + if (flags & RGFW_windowNoBorder) RGFW_window_setBorder(win, 0); + else if (cmpFlags & RGFW_windowNoBorder) RGFW_window_setBorder(win, 1); + if (flags & RGFW_windowScaleToMonitor) RGFW_window_scaleToMonitor(win); + 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_windowCenter) RGFW_window_center(win); + if (flags & RGFW_windowCenterCursor) RGFW_window_moveMouse(win, win->x + (win->w / 2), win->y + (win->h / 2)); + if (flags & RGFW_windowFullscreen) RGFW_window_setFullscreen(win, RGFW_TRUE); + else if (cmpFlags & RGFW_windowFullscreen) RGFW_window_setFullscreen(win, 0); + 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_windowFloating) RGFW_window_setFloating(win, 1); + else if (cmpFlags & RGFW_windowFloating) RGFW_window_setFloating(win, 0); + if (flags & RGFW_windowRawMouse) RGFW_window_setRawMouseMode(win, RGFW_TRUE); + else if (cmpFlags & RGFW_windowRawMouse) RGFW_window_setRawMouseMode(win, RGFW_FALSE); + if (flags & RGFW_windowCaptureMouse) RGFW_window_captureRawMouse(win, RGFW_TRUE); + else if (cmpFlags & RGFW_windowCaptureMouse) RGFW_window_captureMouse(win, RGFW_FALSE); + if (flags & RGFW_windowFocus) RGFW_window_focus(win); + + if (flags & RGFW_windowNoResize) { + RGFW_window_setMaxSize(win, win->w, win->h); + RGFW_window_setMinSize(win, win->w, win->h); + } else if (cmpFlags & RGFW_windowNoResize) { + RGFW_window_setMaxSize(win, 0, 0); + RGFW_window_setMinSize(win, 0, 0); + } + + win->internal.flags = flags; +} + + +void RGFW_window_setFlags(RGFW_window* win, RGFW_windowFlags flags) { RGFW_window_setFlagsInternal(win, flags, win->internal.flags); } + +RGFW_bool RGFW_window_isInFocus(RGFW_window* win) { +#ifdef RGFW_WASM + return RGFW_TRUE; +#else + return RGFW_BOOL(win->internal.inFocus); +#endif +} + +void RGFW_setClassName(const char* name) { RGFW_init(); _RGFW->className = name; } + +#ifndef RGFW_X11 +void RGFW_setXInstName(const char* name) { RGFW_UNUSED(name); } +#endif + +RGFW_bool RGFW_window_getMouse(RGFW_window* win, i32* x, i32* y) { + RGFW_ASSERT(win != NULL); + if (x) *x = win->internal.lastMouseX; + if (y) *y = win->internal.lastMouseY; + return RGFW_TRUE; +} + +RGFW_bool RGFW_window_isKeyPressed(RGFW_window* win, RGFW_key key) { return RGFW_isKeyPressed(key) && RGFW_window_isInFocus(win); } +RGFW_bool RGFW_window_isKeyDown(RGFW_window* win, RGFW_key key) { return RGFW_isKeyDown(key) && RGFW_window_isInFocus(win); } +RGFW_bool RGFW_window_isKeyReleased(RGFW_window* win, RGFW_key key) { return RGFW_isKeyReleased(key) && RGFW_window_isInFocus(win); } + +RGFW_bool RGFW_window_isMousePressed(RGFW_window* win, RGFW_mouseButton button) { return RGFW_isMousePressed(button) && RGFW_window_isInFocus(win); } +RGFW_bool RGFW_window_isMouseDown(RGFW_window* win, RGFW_mouseButton button) { return RGFW_isMouseDown(button) && RGFW_window_isInFocus(win); } +RGFW_bool RGFW_window_isMouseReleased(RGFW_window* win, RGFW_mouseButton button) { return RGFW_isMouseReleased(button) && RGFW_window_isInFocus(win); } + + + +#ifndef RGFW_X11 +void* RGFW_getDisplay_X11(void) { return NULL; } +u64 RGFW_window_getWindow_X11(RGFW_window* win) { RGFW_UNUSED(win); return 0; } +#endif + +#ifndef RGFW_WAYLAND +struct wl_display* RGFW_getDisplay_Wayland(void) { return NULL; } +struct wl_surface* RGFW_window_getWindow_Wayland(RGFW_window* win) { RGFW_UNUSED(win); return NULL; } +#endif + +#ifndef RGFW_WINDOWS +void* RGFW_window_getHWND(RGFW_window* win) { RGFW_UNUSED(win); return NULL; } +void* RGFW_window_getHDC(RGFW_window* win) { RGFW_UNUSED(win); return NULL; } +#endif + +#ifndef RGFW_MACOS +void* RGFW_window_getView_OSX(RGFW_window* win) { RGFW_UNUSED(win); return NULL; } +void RGFW_window_setLayer_OSX(RGFW_window* win, void* layer) { RGFW_UNUSED(win); RGFW_UNUSED(layer); } +void* RGFW_getLayer_OSX(void) { return NULL; } +void* RGFW_window_getWindow_OSX(RGFW_window* win) { RGFW_UNUSED(win); return NULL; } +#endif + +void RGFW_setBit(u32* var, u32 mask, RGFW_bool set) { + if (set) *var |= mask; + else *var &= ~mask; +} + +void RGFW_window_center(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_monitor* mon = RGFW_window_getMonitor(win); + if (mon == NULL) return; + + RGFW_window_move(win, (i32)(mon->mode.w - win->w) / 2, (mon->mode.h - win->h) / 2); +} + +RGFW_bool RGFW_monitor_scaleToWindow(RGFW_monitor* mon, RGFW_window* win) { + RGFW_monitorMode mode; + RGFW_ASSERT(win != NULL); + + mode.w = win->w; + mode.h = win->h; + RGFW_bool ret = RGFW_monitor_requestMode(mon, &mode, RGFW_monitorScale); + + + /* move window to monitor origin so it doesn't move to the next monitor */ + RGFW_window_move(win, mon->x, mon->y); + + return ret; +} + +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) { + RGFW_ASSERT(mon); + RGFW_ASSERT(mon2); + + return (((mon->w == mon2->w && mon->h == mon2->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->internal.shouldClose || (win->internal.exitKey && RGFW_window_isKeyDown(win, win->internal.exitKey))); +} + +void RGFW_window_setShouldClose(RGFW_window* win, RGFW_bool shouldClose) { + if (shouldClose) { + RGFW_windowQuitCallback(win); + } else { + win->internal.shouldClose = RGFW_FALSE; + } +} + +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, (i32)(monitor->scaleX * (float)win->w), (i32)(monitor->scaleY * (float)win->h)); +} + +void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor* m) { + RGFW_window_move(win, m->x + win->x, m->y + win->y); +} + +RGFW_surface* RGFW_createSurface(u8* data, i32 w, i32 h, RGFW_format format) { + RGFW_surface* surface = (RGFW_surface*)RGFW_ALLOC(sizeof(RGFW_surface)); + RGFW_MEMSET(surface, 0, sizeof(RGFW_surface)); + RGFW_createSurfacePtr(data, w, h, format, surface); + return surface; +} + +void RGFW_surface_setConvertFunc(RGFW_surface* surface, RGFW_convertImageDataFunc func) { + surface->convertFunc = func; +} + +void RGFW_surface_free(RGFW_surface* surface) { + RGFW_surface_freePtr(surface); + RGFW_FREE(surface); +} + +RGFW_nativeImage* RGFW_surface_getNativeImage(RGFW_surface* surface) { + return &surface->native; +} + +RGFW_surface* RGFW_window_createSurface(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format) { + RGFW_surface* surface = (RGFW_surface*)RGFW_ALLOC(sizeof(RGFW_surface)); + RGFW_MEMSET(surface, 0, sizeof(RGFW_surface)); + RGFW_window_createSurfacePtr(win, data, w, h, format, surface); + return surface; +} + +#ifndef RGFW_X11 +RGFW_bool RGFW_window_createSurfacePtr(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) { + RGFW_UNUSED(win); + return RGFW_createSurfacePtr(data, w, h, format, surface); +} +#endif + +const RGFW_colorLayout RGFW_layouts[RGFW_formatCount] = { + { 0, 1, 2, 3, 3 }, /* RGFW_formatRGB8 */ + { 2, 1, 0, 3, 3 }, /* RGFW_formatBGR8 */ + { 0, 1, 2, 3, 4 }, /* RGFW_formatRGBA8 */ + { 1, 2, 3, 0, 4 }, /* RGFW_formatARGB8 */ + { 2, 1, 0, 3, 4 }, /* RGFW_formatBGRA8 */ + { 3, 2, 1, 0, 4 }, /* RGFW_formatABGR8 */ +}; + + +void RGFW_copyImageData(u8* dest_data, i32 w, i32 h, RGFW_format dest_format, u8* src_data, RGFW_format src_format, RGFW_convertImageDataFunc func) { + RGFW_copyImageData64(dest_data, w, h, dest_format, src_data, src_format, RGFW_FALSE, func); +} + +RGFWDEF void RGFW_convertImageData64(u8* dest_data, u8* src_data, const RGFW_colorLayout* srcLayout, const RGFW_colorLayout* destLayout, size_t count, RGFW_bool is64bit); +void RGFW_convertImageData64(u8* dest_data, u8* src_data, const RGFW_colorLayout* srcLayout, const RGFW_colorLayout* destLayout, size_t count, RGFW_bool is64bit) { + u32 i, i2 = 0; + u8 rgba[4] = {0}; + + for (i = 0; i < count; i++) { + const u8* src_px = &src_data[i * srcLayout->channels]; + u8* dst_px = &dest_data[i2 * destLayout->channels]; + rgba[0] = src_px[srcLayout->r]; + rgba[1] = src_px[srcLayout->g]; + rgba[2] = src_px[srcLayout->b]; + rgba[3] = (srcLayout->channels == 4) ? src_px[srcLayout->a] : 255; + + dst_px[destLayout->r] = rgba[0]; + dst_px[destLayout->g] = rgba[1]; + dst_px[destLayout->b] = rgba[2]; + if (destLayout->channels == 4) + dst_px[destLayout->a] = rgba[3]; + + i2 += 1 + is64bit; + } +} + +void RGFW_copyImageData64(u8* dest_data, i32 dest_w, i32 dest_h, RGFW_format dest_format, u8* src_data, RGFW_format src_format, RGFW_bool is64bit, RGFW_convertImageDataFunc func) { + RGFW_ASSERT(dest_data && src_data); + + u32 count = (u32)(dest_w * dest_h); + + if (src_format == dest_format) { + u32 channels = (dest_format >= RGFW_formatRGBA8) ? 4 : 3; + RGFW_MEMCPY(dest_data, src_data, count * channels); + return; + } + + const RGFW_colorLayout* srcLayout = &RGFW_layouts[src_format]; + const RGFW_colorLayout* destLayout = &RGFW_layouts[dest_format]; + + if (is64bit || func == NULL) { + RGFW_convertImageData64(dest_data, src_data, srcLayout, destLayout, count, is64bit); + } else { + func(dest_data, src_data, srcLayout, destLayout, count); + } +} + +RGFW_monitorNode* RGFW_monitors_add(const RGFW_monitor* mon) { + RGFW_monitorNode* node = NULL; + if (_RGFW->monitors.freeList.head == NULL) return node; + + node = _RGFW->monitors.freeList.head; + + _RGFW->monitors.freeList.head = node->next; + if (_RGFW->monitors.freeList.head == NULL) { + _RGFW->monitors.freeList.cur = NULL; + } + + node->next = NULL; + + if (_RGFW->monitors.list.head == NULL) { + _RGFW->monitors.list.head = node; + } else { + _RGFW->monitors.list.cur->next = node; + } + + _RGFW->monitors.list.cur = node; + + if (mon) node->mon = *mon; + node->mon.node = node; + node->disconnected = RGFW_FALSE; + + _RGFW->monitors.count += 1; + return node; +} + +void RGFW_monitors_remove(RGFW_monitorNode* node, RGFW_monitorNode* prev) { + _RGFW->monitors.count -= 1; + + /* remove node from the list */ + if (prev != node) { + prev->next = node->next; + } else { /* node is the head */ + _RGFW->monitors.list.head = NULL; + } + + node->next = NULL; + + /* move node to the free list */ + if (_RGFW->monitors.freeList.head == NULL) { + _RGFW->monitors.freeList.head = node; + } else { + _RGFW->monitors.freeList.cur->next = node; + } + + _RGFW->monitors.freeList.cur = node; +} + +void RGFW_monitors_refresh(void) { + RGFW_monitorNode* prev = _RGFW->monitors.list.head; + for (RGFW_monitorNode* node = _RGFW->monitors.list.head; node; node = node->next) { + if (node->disconnected == RGFW_FALSE) continue; + + RGFW_monitorCallback(_RGFW->root, &node->mon, RGFW_FALSE); + RGFW_monitors_remove(node, prev); + prev = node; + } +} + +RGFW_monitorMode* RGFW_monitor_getModes(RGFW_monitor* monitor, size_t* count) { + size_t num = RGFW_monitor_getModesPtr(monitor, NULL); + RGFW_monitorMode* modes = (RGFW_monitorMode*)RGFW_ALLOC(num * sizeof(RGFW_monitorNode)); + num = RGFW_monitor_getModesPtr(monitor, &modes); + + if (count) *count = num; + return modes; +} + +void RGFW_freeModes(RGFW_monitorMode* modes) { + RGFW_FREE(modes); +} + +RGFW_bool RGFW_monitor_findClosestMode(RGFW_monitor* monitor, RGFW_monitorMode* mode, RGFW_monitorMode* closest) { + size_t count = RGFW_monitor_getModesPtr(monitor, NULL); + RGFW_monitorMode* modes = (RGFW_monitorMode*)RGFW_ALLOC(count * sizeof(RGFW_monitorNode)); + count = RGFW_monitor_getModesPtr(monitor, &modes); + + RGFW_monitorMode* chosen = NULL; + + u32 topScore = 1; + for (size_t i = 0; i < count; i++) { + RGFW_monitorMode* mode2 = &modes[i]; + + u32 score = 0; + if (mode->w == mode2->w && mode->h == mode2->h) score += 1000; + if (mode->red == mode2->red && mode->green == mode2->green && mode->blue == mode2->blue) score += 100; + if (mode->refreshRate == mode->refreshRate) score += 10; + + if (score > topScore) { + topScore = score; + chosen = mode2; + } + } + + if (chosen && closest) *closest = *chosen; + + + RGFW_FREE(modes); + + return (chosen == NULL) ? RGFW_FALSE : RGFW_TRUE; +} + +RGFW_bool RGFW_monitor_getPosition(RGFW_monitor* monitor, i32* x, i32* y) { + if (x) *x = monitor->x; + if (y) *y = monitor->y; + return RGFW_TRUE; +} + +const char* RGFW_monitor_getName(RGFW_monitor* monitor) { + return monitor->name; +} + +RGFW_bool RGFW_monitor_getScale(RGFW_monitor* monitor, float* x, float* y) { + if (x) *x = monitor->scaleX; + if (y) *y = monitor->scaleY; + return RGFW_TRUE; +} + +RGFW_bool RGFW_monitor_getPhysicalSize(RGFW_monitor* monitor, float* w, float* h) { + if (w) *w = monitor->physW; + if (h) *h = monitor->physH; + return RGFW_TRUE; +} + +void RGFW_monitor_setUserPtr(RGFW_monitor* monitor, void* userPtr) { + monitor->userPtr = userPtr; +} + +void* RGFW_monitor_getUserPtr(RGFW_monitor* monitor) { + return monitor->userPtr; +} + +RGFW_bool RGFW_monitor_getMode(RGFW_monitor* monitor, RGFW_monitorMode* mode) { + if (mode) *mode = monitor->mode; + return RGFW_TRUE; +} + +RGFW_gammaRamp* RGFW_monitor_getGammaRamp(RGFW_monitor* monitor) { + RGFW_gammaRamp* ramp = (RGFW_gammaRamp*)RGFW_ALLOC(sizeof(RGFW_gammaRamp)); + ramp->count = RGFW_monitor_getGammaRampPtr(monitor, NULL); + ramp->red = (u16*)RGFW_ALLOC(sizeof(u16) * ramp->count); + ramp->green = (u16*)RGFW_ALLOC(sizeof(u16) * ramp->count); + ramp->blue = (u16*)RGFW_ALLOC(sizeof(u16) * ramp->count); + ramp->count = RGFW_monitor_getGammaRampPtr(monitor, ramp); + + return ramp; +} + +void RGFW_freeGammaRamp(RGFW_gammaRamp* ramp) { + RGFW_FREE(ramp->red); + RGFW_FREE(ramp->green); + RGFW_FREE(ramp->blue); + RGFW_FREE(ramp); +} + +RGFW_bool RGFW_monitor_setGammaPtr(RGFW_monitor* monitor, float gamma, u16* ptr, size_t count) { + RGFW_ASSERT(monitor); + RGFW_ASSERT(gamma > 0.0f); + + size_t i; + for (i = 0; i < count; i++) { + float value = (float)i / (float) (count - 1); + #ifndef RGFW_NO_MATH + value = powf(value, 1.f / gamma) * 65535.f + 0.5f; + #endif + value = RGFW_MIN(value, 65535.f); + + ptr[i] = (u16)value; + } + + RGFW_gammaRamp ramp; + ramp.red = ptr; + ramp.green = ptr; + ramp.blue = ptr; + ramp.count = count; + + return RGFW_monitor_setGammaRamp(monitor, &ramp); +} + +RGFW_bool RGFW_monitor_setGamma(RGFW_monitor* monitor, float gamma) { + size_t count = RGFW_monitor_getGammaRampPtr(monitor, NULL); + u16* ptr = (u16*)RGFW_ALLOC(count * sizeof(u16)); + + RGFW_bool ret = RGFW_monitor_setGammaPtr(monitor, gamma, ptr, count); + RGFW_FREE(ptr); + + return ret; +} + +RGFW_monitor** RGFW_getMonitors(size_t* len) { + static RGFW_monitor* monitors[RGFW_MAX_MONITORS]; + RGFW_init(); + if (len != NULL) { + *len = _RGFW->monitors.count; + } + + u8 i = 0; + RGFW_monitorNode* cur_node = _RGFW->monitors.list.head; + while (cur_node != NULL) { + monitors[i] = &cur_node->mon; + i++; + cur_node = cur_node->next; + } + return monitors; +} + +RGFW_monitor* RGFW_getPrimaryMonitor(void) { + if (_RGFW->monitors.primary == NULL) { + _RGFW->monitors.primary = _RGFW->monitors.list.head; + } + + return &_RGFW->monitors.primary->mon; +} + +RGFW_bool RGFW_window_setIcon(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format) { + return RGFW_window_setIconEx(win, data, w, h, format, RGFW_iconBoth); +} + +void RGFW_window_captureMouse(RGFW_window* win, RGFW_bool state) { + win->internal.captureMouse = state; + RGFW_window_captureMousePlatform(win, state); +} + +void RGFW_window_setRawMouseMode(RGFW_window* win, RGFW_bool state) { + win->internal.rawMouse = state; + RGFW_window_setRawMouseModePlatform(win, state); +} + +void RGFW_window_captureRawMouse(RGFW_window* win, RGFW_bool state) { + RGFW_window_captureMouse(win, state); + RGFW_window_setRawMouseMode(win, state); +} + +RGFW_bool RGFW_window_isRawMouseMode(RGFW_window* win) { return RGFW_BOOL(win->internal.rawMouse); } +RGFW_bool RGFW_window_isCaptured(RGFW_window* win) { return RGFW_BOOL(win->internal.captureMouse); } + +void RGFW_updateKeyMod(RGFW_window* win, RGFW_keymod mod, RGFW_bool value) { + if (value) win->internal.mod |= mod; + else win->internal.mod &= ~mod; +} + +void RGFW_updateKeyModsEx(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); +} + +void RGFW_updateKeyMods(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool scroll) { + RGFW_updateKeyModsEx(win, capital, numlock, + RGFW_window_isKeyDown(win, RGFW_controlL) || RGFW_window_isKeyDown(win, RGFW_controlR), + RGFW_window_isKeyDown(win, RGFW_altL) || RGFW_window_isKeyDown(win, RGFW_altR), + RGFW_window_isKeyDown(win, RGFW_shiftL) || RGFW_window_isKeyDown(win, RGFW_shiftR), + RGFW_window_isKeyDown(win, RGFW_superL) || RGFW_window_isKeyDown(win, RGFW_superR), + scroll); +} + +void RGFW_window_showMouseFlags(RGFW_window* win, RGFW_bool show) { + if (show && (win->internal.flags & RGFW_windowHideMouse)) + win->internal.flags ^= RGFW_windowHideMouse; + else if (!show && !(win->internal.flags & RGFW_windowHideMouse)) + win->internal.flags |= RGFW_windowHideMouse; +} + +RGFW_bool RGFW_window_isMouseHidden(RGFW_window* win) { + return (RGFW_bool)RGFW_BOOL(((RGFW_window*)win)->internal.flags & RGFW_windowHideMouse); +} + +RGFW_bool RGFW_window_borderless(RGFW_window* win) { + return (RGFW_bool)RGFW_BOOL(win->internal.flags & RGFW_windowNoBorder); +} + +RGFW_bool RGFW_window_isFullscreen(RGFW_window* win){ return RGFW_BOOL(win->internal.flags & RGFW_windowFullscreen); } +RGFW_bool RGFW_window_allowsDND(RGFW_window* win) { return RGFW_BOOL(win->internal.flags & RGFW_windowAllowDND); } + +#ifndef RGFW_WINDOWS +void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow) { + RGFW_setBit(&win->internal.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_WAYLAND) || defined(RGFW_X11) || defined(RGFW_WINDOWS) +void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) { + RGFW_window_showMouseFlags(win, show); + if (show == RGFW_FALSE) + RGFW_window_setMouse(win, _RGFW->hiddenMouse); + else + RGFW_window_setMouseDefault(win); +} +#endif + +#ifndef RGFW_MACOS +void RGFW_moveToMacOSResourceDir(void) { } +#endif + +RGFWDEF RGFW_bool RGFW_isLatin(const char *string, size_t length); +RGFW_bool RGFW_isLatin(const char *string, size_t length) { + for (size_t i = 0; i < length; i++) { + if ((u8)string[i] >= 0x80) { + return RGFW_TRUE; + } + } + return RGFW_FALSE; +} + +RGFWDEF u32 RGFW_decodeUTF8(const char* string, size_t* starting_index); +u32 RGFW_decodeUTF8(const char* string, size_t* starting_index) { + static const u32 offsets[] = { + 0x00000000u, 0x00003080u, 0x000e2080u, + 0x03c82080u, 0xfa082080u, 0x82082080u + }; + + u32 codepoint = (u8)string[(*starting_index)]; + size_t count; + for (count = 1; (string[count + (*starting_index)] & 0xc0) == 0x80; count++) { + codepoint = (codepoint << 6) + (u8)string[count + (*starting_index)]; + } + + *starting_index += count; + + RGFW_ASSERT(count <= 6); + return codepoint - offsets[count - 1]; +} + +/* + graphics API specific code (end of generic code) + starts here +*/ + + +/* + OpenGL defines start here (Normal, EGL, OSMesa) +*/ + +#if defined(RGFW_OPENGL) +/* EGL, OpenGL */ +#define RGFW_DEFAULT_GL_HINTS { \ + /* Stencil */ 0, \ + /* Samples */ 0, \ + /* Stereo */ RGFW_FALSE, \ + /* AuxBuffers */ 0, \ + /* DoubleBuffer */ RGFW_TRUE, \ + /* Red */ 8, \ + /* Green */ 8, \ + /* Blue */ 8, \ + /* Alpha */ 8, \ + /* Depth */ 24, \ + /* AccumRed */ 0, \ + /* AccumGreen */ 0, \ + /* AccumBlue */ 0, \ + /* AccumAlpha */ 0, \ + /* SRGB */ RGFW_FALSE, \ + /* Robustness */ RGFW_FALSE, \ + /* Debug */ RGFW_FALSE, \ + /* NoError */ RGFW_FALSE, \ + /* ReleaseBehavior */ RGFW_glReleaseNone, \ + /* Profile */ RGFW_glCore, \ + /* Major */ 1, \ + /* Minor */ 0, \ + /* Share */ NULL, \ + /* Share_EGL */ NULL, \ + /* renderer */ RGFW_glAccelerated \ +} + +RGFW_glHints RGFW_globalHints_OpenGL_SRC = RGFW_DEFAULT_GL_HINTS; +RGFW_glHints* RGFW_globalHints_OpenGL = &RGFW_globalHints_OpenGL_SRC; + +void RGFW_resetGlobalHints_OpenGL(void) { +#if !defined(__cplusplus) || defined(RGFW_MACOS) + RGFW_globalHints_OpenGL_SRC = (RGFW_glHints)RGFW_DEFAULT_GL_HINTS; +#else + RGFW_globalHints_OpenGL_SRC = RGFW_DEFAULT_GL_HINTS; +#endif +} +void RGFW_setGlobalHints_OpenGL(RGFW_glHints* hints) { RGFW_globalHints_OpenGL = hints; } +RGFW_glHints* RGFW_getGlobalHints_OpenGL(void) { RGFW_init(); return RGFW_globalHints_OpenGL; } + + +void* RGFW_glContext_getSourceContext(RGFW_glContext* ctx) { + RGFW_UNUSED(ctx); + +#ifdef RGFW_WAYLAND + if (RGFW_usingWayland()) return (void*)ctx->egl.ctx; +#endif + +#if defined(RGFW_X11) + return (void*)ctx->ctx; +#else + return NULL; +#endif +} + +RGFW_glContext* RGFW_window_createContext_OpenGL(RGFW_window* win, RGFW_glHints* hints) { + #ifdef RGFW_WAYLAND + if (RGFW_usingWayland()) { + return (RGFW_glContext*)RGFW_window_createContext_EGL(win, hints); + } + #endif + RGFW_glContext* ctx = (RGFW_glContext*)RGFW_ALLOC(sizeof(RGFW_glContext)); + if (RGFW_window_createContextPtr_OpenGL(win, ctx, hints) == RGFW_FALSE) { + RGFW_FREE(ctx); + win->src.ctx.native = NULL; + return NULL; + } + win->src.gfxType |= RGFW_gfxOwnedByRGFW; + return ctx; +} + +RGFW_glContext* RGFW_window_getContext_OpenGL(RGFW_window* win) { + if (win->src.gfxType & RGFW_windowEGL) return NULL; + return win->src.ctx.native; +} + +void RGFW_window_deleteContext_OpenGL(RGFW_window* win, RGFW_glContext* ctx) { + RGFW_window_deleteContextPtr_OpenGL(win, ctx); + if (win->src.gfxType & RGFW_gfxOwnedByRGFW) RGFW_FREE(ctx); +} + +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; + } + + while (ext[len - 1] == '\0' && len > 3) { + len--; + } + + where = RGFW_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; +} + +RGFWDEF RGFW_bool RGFW_extensionSupported_base(const char* extension, size_t len); +RGFW_bool RGFW_extensionSupported_base(const char* extension, size_t len) { + #ifdef GL_NUM_EXTENSIONS + if (RGFW_globalHints_OpenGL->major >= 3) { + i32 i; + + GLint count = 0; + + RGFW_proc RGFW_glGetStringi = RGFW_getProcAddress_OpenGL("glGetStringi"); + RGFW_proc RGFW_glGetIntegerv = RGFW_getProcAddress_OpenGL("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_OpenGL("glGetString"); + #define RGFW_GL_EXTENSIONS 0x1F03 + if (RGFW_glGetString) { + const char* extensions = ((const char*(*)(u32))RGFW_glGetString)(RGFW_GL_EXTENSIONS); + + if ((extensions != NULL) && RGFW_extensionSupportedStr(extensions, extension, len)) { + return RGFW_TRUE; + } + } + } + return RGFW_FALSE; +} + +RGFW_bool RGFW_extensionSupported_OpenGL(const char* extension, size_t len) { + if (RGFW_extensionSupported_base(extension, len)) return RGFW_TRUE; + return RGFW_extensionSupportedPlatform_OpenGL(extension, len); +} + +void RGFW_window_makeCurrentWindow_OpenGL(RGFW_window* win) { + if (win) { + _RGFW->current = win; + } + + RGFW_window_makeCurrentContext_OpenGL(win); +} + +RGFW_window* RGFW_getCurrentWindow_OpenGL(void) { return _RGFW->current; } +void RGFW_attribStack_init(RGFW_attribStack* stack, i32* attribs, size_t max) { stack->attribs = attribs; stack->count = 0; stack->max = max; } +void RGFW_attribStack_pushAttrib(RGFW_attribStack* stack, i32 attrib) { + RGFW_ASSERT(stack->count < stack->max); + stack->attribs[stack->count] = attrib; + stack->count += 1; +} +void RGFW_attribStack_pushAttribs(RGFW_attribStack* stack, i32 attrib1, i32 attrib2) { + RGFW_attribStack_pushAttrib(stack, attrib1); + RGFW_attribStack_pushAttrib(stack, attrib2); +} + +/* EGL */ +#ifdef RGFW_EGL +#include +#include + +PFNEGLINITIALIZEPROC RGFW_eglInitialize; +PFNEGLGETCONFIGSPROC RGFW_eglGetConfigs; +PFNEGLCHOOSECONFIGPROC RGFW_eglChooseConfig; +PFNEGLCREATEWINDOWSURFACEPROC RGFW_eglCreateWindowSurface; +PFNEGLCREATECONTEXTPROC RGFW_eglCreateContext; +PFNEGLMAKECURRENTPROC RGFW_eglMakeCurrent; +PFNEGLGETDISPLAYPROC RGFW_eglGetDisplay; +PFNEGLSWAPBUFFERSPROC RGFW_eglSwapBuffers; +PFNEGLSWAPINTERVALPROC RGFW_eglSwapInterval; +PFNEGLBINDAPIPROC RGFW_eglBindAPI; +PFNEGLDESTROYCONTEXTPROC RGFW_eglDestroyContext; +PFNEGLTERMINATEPROC RGFW_eglTerminate; +PFNEGLDESTROYSURFACEPROC RGFW_eglDestroySurface; +PFNEGLGETCURRENTCONTEXTPROC RGFW_eglGetCurrentContext; +PFNEGLGETPROCADDRESSPROC RGFW_eglGetProcAddress = NULL; +PFNEGLQUERYSTRINGPROC RGFW_eglQueryString; +PFNEGLGETCONFIGATTRIBPROC RGFW_eglGetConfigAttrib; + +#define EGL_SURFACE_MAJOR_VERSION_KHR 0x3098 +#define EGL_SURFACE_MINOR_VERSION_KHR 0x30fb + +#ifdef RGFW_WINDOWS + #include +#elif defined(RGFW_MACOS) || defined(RGFW_UNIX) + #include +#endif + +#ifdef RGFW_WAYLAND +#include +#endif + +void* RGFW_eglLibHandle = NULL; + +void* RGFW_getDisplay_EGL(void) { return _RGFW->EGL_display; } +void* RGFW_eglContext_getSourceContext(RGFW_eglContext* ctx) { return ctx->ctx; } +void* RGFW_eglContext_getSurface(RGFW_eglContext* ctx) { return ctx->surface; } +struct wl_egl_window* RGFW_eglContext_wlEGLWindow(RGFW_eglContext* ctx) { return ctx->eglWindow; } + +RGFW_bool RGFW_loadEGL(void) { + RGFW_init(); + if (RGFW_eglGetProcAddress != NULL) { + return RGFW_TRUE; + } + +#ifndef RGFW_WASM + #ifdef RGFW_WINDOWS + const char* libNames[] = { "libEGL.dll", "EGL.dll" }; + #elif defined(RGFW_MACOS) || defined(RGFW_UNIX) + /* Linux and macOS */ + const char* libNames[] = { + "libEGL.so.1", /* most common */ + "libEGL.so", /* fallback */ + "/System/Library/Frameworks/OpenGL.framework/OpenGL" /* fallback for older macOS EGL-like systems */ + }; + #endif + + for (size_t i = 0; i < sizeof(libNames) / sizeof(libNames[0]); i++) { + #ifdef RGFW_WINDOWS + RGFW_eglLibHandle = (void*)LoadLibraryA(libNames[i]); + if (RGFW_eglLibHandle) { + RGFW_eglGetProcAddress = (PFNEGLGETPROCADDRESSPROC)(RGFW_proc)GetProcAddress((HMODULE)RGFW_eglLibHandle, "eglGetProcAddress"); + break; + } + #elif defined(RGFW_MACOS) || defined(RGFW_UNIX) + RGFW_eglLibHandle = dlopen(libNames[i], RTLD_LAZY | RTLD_GLOBAL); + if (RGFW_eglLibHandle) { + void* lib = dlsym(RGFW_eglLibHandle, "eglGetProcAddress"); + if (lib != NULL) RGFW_MEMCPY(&RGFW_eglGetProcAddress, &lib, sizeof(PFNEGLGETPROCADDRESSPROC)); + break; + } + #endif + } + + if (!RGFW_eglLibHandle || !RGFW_eglGetProcAddress) { + return RGFW_FALSE; + } + + RGFW_eglInitialize = (PFNEGLINITIALIZEPROC) RGFW_eglGetProcAddress("eglInitialize"); + RGFW_eglGetConfigs = (PFNEGLGETCONFIGSPROC) RGFW_eglGetProcAddress("eglGetConfigs"); + RGFW_eglChooseConfig = (PFNEGLCHOOSECONFIGPROC) RGFW_eglGetProcAddress("eglChooseConfig"); + RGFW_eglCreateWindowSurface = (PFNEGLCREATEWINDOWSURFACEPROC) RGFW_eglGetProcAddress("eglCreateWindowSurface"); + RGFW_eglCreateContext = (PFNEGLCREATECONTEXTPROC) RGFW_eglGetProcAddress("eglCreateContext"); + RGFW_eglMakeCurrent = (PFNEGLMAKECURRENTPROC) RGFW_eglGetProcAddress("eglMakeCurrent"); + RGFW_eglGetDisplay = (PFNEGLGETDISPLAYPROC) RGFW_eglGetProcAddress("eglGetDisplay"); + RGFW_eglSwapBuffers = (PFNEGLSWAPBUFFERSPROC) RGFW_eglGetProcAddress("eglSwapBuffers"); + RGFW_eglSwapInterval = (PFNEGLSWAPINTERVALPROC) RGFW_eglGetProcAddress("eglSwapInterval"); + RGFW_eglBindAPI = (PFNEGLBINDAPIPROC) RGFW_eglGetProcAddress("eglBindAPI"); + RGFW_eglDestroyContext = (PFNEGLDESTROYCONTEXTPROC) RGFW_eglGetProcAddress("eglDestroyContext"); + RGFW_eglTerminate = (PFNEGLTERMINATEPROC) RGFW_eglGetProcAddress("eglTerminate"); + RGFW_eglDestroySurface = (PFNEGLDESTROYSURFACEPROC) RGFW_eglGetProcAddress("eglDestroySurface"); + RGFW_eglQueryString = (PFNEGLQUERYSTRINGPROC) RGFW_eglGetProcAddress("eglQueryString"); + RGFW_eglGetCurrentContext = (PFNEGLGETCURRENTCONTEXTPROC) RGFW_eglGetProcAddress("eglGetCurrentContext"); + RGFW_eglGetConfigAttrib = (PFNEGLGETCONFIGATTRIBPROC) RGFW_eglGetProcAddress("eglGetConfigAttrib"); + +#else + RGFW_eglGetProcAddress = eglGetProcAddress; + RGFW_eglInitialize = (PFNEGLINITIALIZEPROC) eglInitialize; + RGFW_eglGetConfigs = (PFNEGLGETCONFIGSPROC) eglGetConfigs; + RGFW_eglChooseConfig = (PFNEGLCHOOSECONFIGPROC) eglChooseConfig; + RGFW_eglCreateWindowSurface = (PFNEGLCREATEWINDOWSURFACEPROC) eglCreateWindowSurface; + RGFW_eglCreateContext = (PFNEGLCREATECONTEXTPROC) eglCreateContext; + RGFW_eglMakeCurrent = (PFNEGLMAKECURRENTPROC) eglMakeCurrent; + RGFW_eglGetDisplay = (PFNEGLGETDISPLAYPROC) eglGetDisplay; + RGFW_eglSwapBuffers = (PFNEGLSWAPBUFFERSPROC) eglSwapBuffers; + RGFW_eglSwapInterval = (PFNEGLSWAPINTERVALPROC) eglSwapInterval; + RGFW_eglBindAPI = (PFNEGLBINDAPIPROC) eglBindAPI; + RGFW_eglDestroyContext = (PFNEGLDESTROYCONTEXTPROC) eglDestroyContext; + RGFW_eglTerminate = (PFNEGLTERMINATEPROC) eglTerminate; + RGFW_eglDestroySurface = (PFNEGLDESTROYSURFACEPROC) eglDestroySurface; + RGFW_eglQueryString = (PFNEGLQUERYSTRINGPROC) eglQueryString; + RGFW_eglGetCurrentContext = (PFNEGLGETCURRENTCONTEXTPROC) eglGetCurrentContext; + RGFW_eglGetConfigAttrib = (PFNEGLGETCONFIGATTRIBPROC)eglGetConfigAttrib; +#endif + + RGFW_bool out = RGFW_BOOL(RGFW_eglInitialize!= NULL && + RGFW_eglGetConfigs!= NULL && + RGFW_eglChooseConfig!= NULL && + RGFW_eglCreateWindowSurface!= NULL && + RGFW_eglCreateContext!= NULL && + RGFW_eglMakeCurrent!= NULL && + RGFW_eglGetDisplay!= NULL && + RGFW_eglSwapBuffers!= NULL && + RGFW_eglSwapInterval != NULL && + RGFW_eglBindAPI!= NULL && + RGFW_eglDestroyContext!= NULL && + RGFW_eglTerminate!= NULL && + RGFW_eglDestroySurface!= NULL && + RGFW_eglQueryString != NULL && + RGFW_eglGetCurrentContext != NULL && + RGFW_eglGetConfigAttrib != NULL); + + if (out) { + #ifdef RGFW_WINDOWS + HDC dc = GetDC(NULL); + _RGFW->EGL_display = RGFW_eglGetDisplay((EGLNativeDisplayType) dc); + ReleaseDC(NULL, dc); + #elif defined(RGFW_WAYLAND) + if (_RGFW->useWaylandBool) + _RGFW->EGL_display = RGFW_eglGetDisplay((EGLNativeDisplayType) _RGFW->wl_display); + else + #endif + #ifdef RGFW_X11 + _RGFW->EGL_display = RGFW_eglGetDisplay((EGLNativeDisplayType) _RGFW->display); + #else + {} + #endif + #if !defined(RGFW_WAYLAND) && !defined(RGFW_WINDOWS) && !defined(RGFW_X11) + _RGFW->EGL_display = RGFW_eglGetDisplay(EGL_DEFAULT_DISPLAY); + #endif + } + + RGFW_eglInitialize(_RGFW->EGL_display, NULL, NULL); + return out; +} + + +void RGFW_unloadEGL(void) { + if (!RGFW_eglLibHandle) return; + RGFW_eglTerminate(_RGFW->EGL_display); + #ifdef RGFW_WINDOWS + FreeLibrary((HMODULE)RGFW_eglLibHandle); + #elif defined(RGFW_MACOS) || defined(RGFW_UNIX) + dlclose(RGFW_eglLibHandle); + #endif + + RGFW_eglLibHandle = NULL; + RGFW_eglGetProcAddress = NULL; +} + +RGFW_bool RGFW_window_createContextPtr_EGL(RGFW_window* win, RGFW_eglContext* ctx, RGFW_glHints* hints) { + if (RGFW_loadEGL() == RGFW_FALSE) return RGFW_FALSE; + win->src.ctx.egl = ctx; + win->src.gfxType = RGFW_gfxEGL; + +#ifdef RGFW_WAYLAND + if (_RGFW->useWaylandBool) + win->src.ctx.egl->eglWindow = wl_egl_window_create(win->src.surface, win->w, win->h); +#endif + + #ifndef EGL_OPENGL_ES1_BIT + #define EGL_OPENGL_ES1_BIT 0x1 + #endif + + EGLint egl_config[24]; + + { + RGFW_attribStack stack; + RGFW_attribStack_init(&stack, egl_config, 24); + + RGFW_attribStack_pushAttribs(&stack, EGL_SURFACE_TYPE, EGL_WINDOW_BIT); + RGFW_attribStack_pushAttrib(&stack, EGL_RENDERABLE_TYPE); + + if (hints->profile == RGFW_glES) { + switch (hints->major) { + case 1: RGFW_attribStack_pushAttrib(&stack, EGL_OPENGL_ES1_BIT); break; + case 2: RGFW_attribStack_pushAttrib(&stack, EGL_OPENGL_ES2_BIT); break; + case 3: RGFW_attribStack_pushAttrib(&stack, EGL_OPENGL_ES3_BIT); break; + default: break; + } + } else { + RGFW_attribStack_pushAttrib(&stack, EGL_OPENGL_BIT); + } + + RGFW_attribStack_pushAttribs(&stack, EGL_RED_SIZE, hints->red); + RGFW_attribStack_pushAttribs(&stack, EGL_GREEN_SIZE, hints->green); + RGFW_attribStack_pushAttribs(&stack, EGL_BLUE_SIZE, hints->blue); + RGFW_attribStack_pushAttribs(&stack, EGL_ALPHA_SIZE, hints->alpha); + RGFW_attribStack_pushAttribs(&stack, EGL_DEPTH_SIZE, hints->depth); + + RGFW_attribStack_pushAttribs(&stack, EGL_STENCIL_SIZE, hints->stencil); + if (hints->samples) { + RGFW_attribStack_pushAttribs(&stack, EGL_SAMPLE_BUFFERS, 1); + RGFW_attribStack_pushAttribs(&stack, EGL_SAMPLES, hints->samples); + } + + RGFW_attribStack_pushAttribs(&stack, EGL_NONE, EGL_NONE); + } + + EGLint numConfigs, best_config = -1, best_samples = 0; + + RGFW_eglChooseConfig(_RGFW->EGL_display, egl_config, NULL, 0, &numConfigs); + EGLConfig* configs = (EGLConfig*)RGFW_ALLOC(sizeof(EGLConfig) * (u32)numConfigs); + + RGFW_eglChooseConfig(_RGFW->EGL_display, egl_config, configs, numConfigs, &numConfigs); + +#ifdef RGFW_X11 + RGFW_bool transparent = (win->internal.flags & RGFW_windowTransparent); + EGLint best_depth = 0; +#endif + + for (EGLint i = 0; i < numConfigs; i++) { + EGLint visual_id = 0; + EGLint samples = 0; + + RGFW_eglGetConfigAttrib(_RGFW->EGL_display, configs[i], EGL_NATIVE_VISUAL_ID, &visual_id); + RGFW_eglGetConfigAttrib(_RGFW->EGL_display, configs[i], EGL_SAMPLES, &samples); + + if (best_config == -1) best_config = i; + +#ifdef RGFW_X11 + if (_RGFW->useWaylandBool == RGFW_FALSE) { + XVisualInfo vinfo_template; + vinfo_template.visualid = (VisualID)visual_id; + + int num_visuals = 0; + XVisualInfo* vi = XGetVisualInfo(_RGFW->display, VisualIDMask, &vinfo_template, &num_visuals); + if (!vi) continue; + if ((!transparent || vi->depth == 32) && best_depth == 0) { + best_config = i; + best_depth = vi->depth; + } + + if ((!(transparent) || vi->depth == 32) && (samples <= hints->samples && samples > best_samples)) { + best_depth = vi->depth; + best_config = i; + best_samples = samples; + XFree(vi); + continue; + } + } +#endif + + if (samples <= hints->samples && samples > best_samples) { + best_config = i; + best_samples = samples; + } + } + + EGLConfig config = configs[best_config]; + RGFW_FREE(configs); +#ifdef RGFW_X11 + if (_RGFW->useWaylandBool == RGFW_FALSE) { + /* This is required so that way the user can create their own OpenGL context after RGFW_createWindow is used */ + XVisualInfo* result; + XVisualInfo desired; + EGLint visualID = 0, count = 0; + + RGFW_eglGetConfigAttrib(_RGFW->EGL_display, config, EGL_NATIVE_VISUAL_ID, &visualID); + if (visualID) { + desired.visualid = (VisualID)visualID; + result = XGetVisualInfo(_RGFW->display, VisualIDMask, &desired, &count); + } else RGFW_sendDebugInfo(RGFW_typeError, RGFW_errEGLContext, "Failed to fetch a valid EGL VisualID"); + + if (result == NULL || count == 0) { + if (win->src.window == 0) { + /* try to create a EGL context anyway (this will work if you're not using a NVidia driver) */ + win->internal.flags &= ~(u32)RGFW_windowEGL; + RGFW_createWindowPlatform("", win->internal.flags, win); + } + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errEGLContext, "Failed to find a valid visual for the EGL config"); + } else { + RGFW_bool showWindow = RGFW_FALSE; + if (win->src.window) { + showWindow = (RGFW_window_isMinimized(win) == RGFW_FALSE); + RGFW_window_closePlatform(win); + } + + RGFW_XCreateWindow(*result, "", win->internal.flags, win); + + if (showWindow) { + RGFW_window_show(win); + } + XFree(result); + } + } +#endif + + EGLint surf_attribs[9]; + + { + RGFW_attribStack stack; + RGFW_attribStack_init(&stack, surf_attribs, 9); + + const char present_opaque_str[] = "EGL_EXT_present_opaque"; + RGFW_bool opaque_extension_Found = RGFW_extensionSupportedPlatform_EGL(present_opaque_str, sizeof(present_opaque_str)); + + #ifndef EGL_PRESENT_OPAQUE_EXT + #define EGL_PRESENT_OPAQUE_EXT 0x31df + #endif + + #ifndef EGL_GL_COLORSPACE_KHR + #define EGL_GL_COLORSPACE_KHR 0x309D + #ifndef EGL_GL_COLORSPACE_SRGB_KHR + #define EGL_GL_COLORSPACE_SRGB_KHR 0x3089 + #endif + #endif + + const char gl_colorspace_str[] = "EGL_KHR_gl_colorspace"; + RGFW_bool gl_colorspace_Found = RGFW_extensionSupportedPlatform_EGL(gl_colorspace_str, sizeof(gl_colorspace_str)); + + if (hints->sRGB && gl_colorspace_Found) { + RGFW_attribStack_pushAttribs(&stack, EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); + } + + if (!(win->internal.flags & RGFW_windowTransparent) && opaque_extension_Found) + RGFW_attribStack_pushAttribs(&stack, EGL_PRESENT_OPAQUE_EXT, EGL_TRUE); + + if (hints->doubleBuffer == 0) { + RGFW_attribStack_pushAttribs(&stack, EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER); + } + + RGFW_attribStack_pushAttribs(&stack, EGL_NONE, EGL_NONE); + } + #if defined(RGFW_MACOS) + void* layer = RGFW_getLayer_OSX(); + + RGFW_window_setLayer_OSX(win, layer); + + win->src.ctx.egl->surface = RGFW_eglCreateWindowSurface(_RGFW->EGL_display, config, (EGLNativeWindowType) layer, surf_attribs); + #elif defined(RGFW_WINDOWS) + win->src.ctx.egl->surface = RGFW_eglCreateWindowSurface(_RGFW->EGL_display, config, (EGLNativeWindowType) win->src.window, surf_attribs); + #elif defined(RGFW_WAYLAND) + if (_RGFW->useWaylandBool) + win->src.ctx.egl->surface = RGFW_eglCreateWindowSurface(_RGFW->EGL_display, config, (EGLNativeWindowType) win->src.ctx.egl->eglWindow, surf_attribs); + else + #endif + #ifdef RGFW_X11 + win->src.ctx.egl->surface = RGFW_eglCreateWindowSurface(_RGFW->EGL_display, config, (EGLNativeWindowType) win->src.window, surf_attribs); + #else + {} + #endif + #ifdef RGFW_WASM + win->src.ctx.egl->surface = eglCreateWindowSurface(_RGFW->EGL_display, config, 0, 0); + #endif + + if (win->src.ctx.egl->surface == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errEGLContext, "Failed to create an EGL surface."); + return RGFW_FALSE; + } + + EGLint attribs[20]; + { + RGFW_attribStack stack; + RGFW_attribStack_init(&stack, attribs, 20); + + if (hints->major || hints->minor) { + RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_MAJOR_VERSION, hints->major); + RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_MINOR_VERSION, hints->minor); + } + + if (hints->profile == RGFW_glCore) { + RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT); + } else if (hints->profile == RGFW_glCompatibility) { + RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT); + } else if (hints->profile == RGFW_glForwardCompatibility) { + RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE, EGL_TRUE); + } + + + RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_OPENGL_ROBUST_ACCESS, hints->robustness); + RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_OPENGL_DEBUG, hints->debug); + + #ifndef EGL_CONTEXT_RELEASE_BEHAVIOR_KHR + #define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097 + #endif + + #ifndef EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR + #define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098 + #endif + + if (hints->releaseBehavior == RGFW_glReleaseFlush) { + RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR); + } else { + RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, 0x0000); + } + + RGFW_attribStack_pushAttribs(&stack, EGL_NONE, EGL_NONE); + } + + if (hints->profile == RGFW_glES) + RGFW_eglBindAPI(EGL_OPENGL_ES_API); + else + RGFW_eglBindAPI(EGL_OPENGL_API); + + win->src.ctx.egl->ctx = RGFW_eglCreateContext(_RGFW->EGL_display, config, hints->shareEGL, attribs); + + if (win->src.ctx.egl->ctx == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errEGLContext, "Failed to create an EGL context."); + return RGFW_FALSE; + } + + RGFW_eglMakeCurrent(_RGFW->EGL_display, win->src.ctx.egl->surface, win->src.ctx.egl->surface, win->src.ctx.egl->ctx); + RGFW_eglSwapBuffers(_RGFW->EGL_display, win->src.ctx.egl->surface); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "EGL context initalized."); + return RGFW_TRUE; +} + +RGFW_eglContext* RGFW_window_getContext_EGL(RGFW_window* win) { + if (win->src.gfxType == RGFW_windowOpenGL) return NULL; + return win->src.ctx.egl; +} + +void RGFW_window_deleteContextPtr_EGL(RGFW_window* win, RGFW_eglContext* ctx) { + if (_RGFW->EGL_display == NULL) return; + + RGFW_eglDestroySurface(_RGFW->EGL_display, ctx->surface); + RGFW_eglDestroyContext(_RGFW->EGL_display, ctx->ctx); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "EGL context freed"); + #ifdef RGFW_WAYLAND + if (_RGFW->useWaylandBool == RGFW_FALSE) return; + wl_egl_window_destroy(win->src.ctx.egl->eglWindow); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "EGL window context freed"); + #endif + win->src.ctx.egl = NULL; +} + +void RGFW_window_makeCurrentContext_EGL(RGFW_window* win) { if (win) RGFW_ASSERT(win->src.ctx.egl); + if (win == NULL) + RGFW_eglMakeCurrent(_RGFW->EGL_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + else { + RGFW_eglMakeCurrent(_RGFW->EGL_display, win->src.ctx.egl->surface, win->src.ctx.egl->surface, win->src.ctx.egl->ctx); + } +} + +void RGFW_window_swapBuffers_EGL(RGFW_window* win) { + if (RGFW_eglSwapBuffers) + RGFW_eglSwapBuffers(_RGFW->EGL_display, win->src.ctx.egl->surface); + else RGFW_window_swapBuffers_OpenGL(win); +} + +void* RGFW_getCurrentContext_EGL(void) { + return RGFW_eglGetCurrentContext(); +} + +RGFW_proc RGFW_getProcAddress_EGL(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) RGFW_eglGetProcAddress(procname); +} + +RGFW_bool RGFW_extensionSupportedPlatform_EGL(const char* extension, size_t len) { + if (RGFW_loadEGL() == RGFW_FALSE) return RGFW_FALSE; + const char* extensions = RGFW_eglQueryString(_RGFW->EGL_display, EGL_EXTENSIONS); + return extensions != NULL && RGFW_extensionSupportedStr(extensions, extension, len); +} + +void RGFW_window_swapInterval_EGL(RGFW_window* win, i32 swapInterval) { + RGFW_ASSERT(win != NULL); + RGFW_eglSwapInterval(_RGFW->EGL_display, swapInterval); +} + +RGFW_bool RGFW_extensionSupported_EGL(const char* extension, size_t len) { + if (RGFW_extensionSupported_base(extension, len)) return RGFW_TRUE; + return RGFW_extensionSupportedPlatform_EGL(extension, len); +} + +void RGFW_window_makeCurrentWindow_EGL(RGFW_window* win) { + _RGFW->current = win; + RGFW_window_makeCurrentContext_EGL(win); +} + +RGFW_window* RGFW_getCurrentWindow_EGL(void) { return _RGFW->current; } + +RGFW_eglContext* RGFW_window_createContext_EGL(RGFW_window* win, RGFW_glHints* hints) { + RGFW_eglContext* ctx = (RGFW_eglContext*)RGFW_ALLOC(sizeof(RGFW_eglContext)); + if (RGFW_window_createContextPtr_EGL(win, ctx, hints) == RGFW_FALSE) { + RGFW_FREE(ctx); + win->src.ctx.egl = NULL; + return NULL; + } + win->src.gfxType |= RGFW_gfxOwnedByRGFW; + return ctx; +} + +void RGFW_window_deleteContext_EGL(RGFW_window* win, RGFW_eglContext* ctx) { + RGFW_window_deleteContextPtr_EGL(win, ctx); + if (win->src.gfxType & RGFW_gfxOwnedByRGFW) RGFW_FREE(ctx); +} + +#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_getRequiredInstanceExtensions_Vulkan(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; +} + +#ifndef RGFW_MACOS +VkResult RGFW_window_createSurface_Vulkan(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 + + VkXlibSurfaceCreateInfoKHR x11 = { VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, 0, 0, (Display*) _RGFW->display, (Window) win->src.window }; + return vkCreateXlibSurfaceKHR(instance, &x11, NULL, surface); +#endif +#if defined(RGFW_WAYLAND) + + VkWaylandSurfaceCreateInfoKHR wayland = { VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, 0, 0, (struct wl_display*) _RGFW->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); +#endif +} +#endif + +RGFW_bool RGFW_getPresentationSupport_Vulkan(VkPhysicalDevice physicalDevice, u32 queueFamilyIndex) { + if (_RGFW == NULL) RGFW_init(); +#ifdef RGFW_X11 + + Visual* visual = DefaultVisual(_RGFW->display, DefaultScreen(_RGFW->display)); + RGFW_bool out = vkGetPhysicalDeviceXlibPresentationSupportKHR(physicalDevice, queueFamilyIndex, _RGFW->display, XVisualIDFromVisual(visual)); + return out; +#endif +#if defined(RGFW_WAYLAND) + + RGFW_bool wlout = vkGetPhysicalDeviceWaylandPresentationSupportKHR(physicalDevice, queueFamilyIndex, _RGFW->wl_display); + return wlout; +#elif defined(RGFW_WINDOWS) + RGFW_bool out = vkGetPhysicalDeviceWin32PresentationSupportKHR(physicalDevice, queueFamilyIndex); + return out; +#elif defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11) + RGFW_UNUSED(physicalDevice); + RGFW_UNUSED(queueFamilyIndex); + return RGFW_FALSE; /* TODO */ +#endif +} +#endif /* end of RGFW_vulkan */ + +/* +This is where OS specific stuff starts +*/ + +/* start of unix (wayland or X11 (unix) ) defines */ + +#ifdef RGFW_UNIX +#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; + } +} + +RGFWDEF u64 RGFW_linux_getTimeNS(void); +u64 RGFW_linux_getTimeNS(void) { + struct timespec ts; + const u64 scale_factor = 1000000000; + clock_gettime(_RGFW->clock, &ts); + return (u64)ts.tv_sec * scale_factor + (u64)ts.tv_nsec; +} + +void RGFW_waitForEvent(i32 waitMS) { + if (waitMS == 0) return; + + 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[2]; + fds[0].fd = 0; + fds[0].events = POLLIN; + fds[0].revents = 0; + fds[1].fd = _RGFW->eventWait_forceStop[0]; + fds[1].events = POLLIN; + fds[1].revents = 0; + + + if (RGFW_usingWayland()) { + #ifdef RGFW_WAYLAND + fds[0].fd = wl_display_get_fd(_RGFW->wl_display); + + /* empty the queue */ + while (wl_display_prepare_read(_RGFW->wl_display) != 0) { + /* error occured when dispatching the queue */ + if (wl_display_dispatch_pending(_RGFW->wl_display) == -1) { + return; + } + } + + /* send any pending requests to the compositor */ + while (wl_display_flush(_RGFW->wl_display) == -1) { + + /* queue is full dispatch them */ + if (errno == EAGAIN) { + if (wl_display_dispatch_pending(_RGFW->wl_display) == -1) { + return; + } + } else { + return; + } + } + #endif + } else { + #ifdef RGFW_X11 + fds[0].fd = ConnectionNumber(_RGFW->display); + #endif + } + + + u64 start = RGFW_linux_getTimeNS(); + if (RGFW_usingWayland()) { + #ifdef RGFW_WAYLAND + while (wl_display_dispatch_pending(_RGFW->wl_display) == 0) { + if (poll(fds, 1, waitMS) <= 0) { + wl_display_cancel_read(_RGFW->wl_display); + break; + } else { + if (wl_display_read_events(_RGFW->wl_display) == -1) + return; + } + + if (waitMS != RGFW_eventWaitNext) { + waitMS -= (i32)(RGFW_linux_getTimeNS() - start) / (i32)1e+6; + } + } + + /* queue contains events from read, dispatch them */ + if (wl_display_dispatch_pending(_RGFW->wl_display) == -1) { + return; + } + #endif + } else { + #ifdef RGFW_X11 + while (XPending(_RGFW->display) == 0) { + if (poll(fds, 1, waitMS) <= 0) + break; + + if (waitMS != RGFW_eventWaitNext) { + waitMS -= (i32)(RGFW_linux_getTimeNS() - start) / (i32)1e+6; + } + } + #endif + } + + /* drain any data in the stop request */ + if (_RGFW->eventWait_forceStop[2]) { + char data[64]; + RGFW_MEMSET(data, 0, sizeof(data)); + (void)!read(_RGFW->eventWait_forceStop[0], data, sizeof(data)); + + _RGFW->eventWait_forceStop[2] = 0; + } +} + +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; +} + +#ifdef RGFW_X11 +RGFWDEF i32 RGFW_initPlatform_X11(void); +RGFWDEF void RGFW_deinitPlatform_X11(void); +#endif +#ifdef RGFW_WAYLAND +RGFWDEF i32 RGFW_initPlatform_Wayland(void); +RGFWDEF void RGFW_deinitPlatform_Wayland(void); +#endif + +RGFWDEF void RGFW_load_X11(void); +RGFWDEF void RGFW_load_Wayland(void); + +#if !defined(RGFW_X11) || !defined(RGFW_WAYLAND) +void RGFW_load_X11(void) { } +void RGFW_load_Wayland(void) { } +#endif + +/* + * Sadly we have to use magic linux keycodes + * We can't use X11 functions, because that breaks Wayland, but they use the same keycodes so there's no use redeffing them + * We can't use linux enums, because the headers don't exist on BSD + */ +void RGFW_initKeycodesPlatform(void) { + _RGFW->keycodes[49] = RGFW_backtick; + _RGFW->keycodes[19] = RGFW_0; + _RGFW->keycodes[10] = RGFW_1; + _RGFW->keycodes[11] = RGFW_2; + _RGFW->keycodes[12] = RGFW_3; + _RGFW->keycodes[13] = RGFW_4; + _RGFW->keycodes[14] = RGFW_5; + _RGFW->keycodes[15] = RGFW_6; + _RGFW->keycodes[16] = RGFW_7; + _RGFW->keycodes[17] = RGFW_8; + _RGFW->keycodes[18] = RGFW_9; + _RGFW->keycodes[65] = RGFW_space; + _RGFW->keycodes[38] = RGFW_a; + _RGFW->keycodes[56] = RGFW_b; + _RGFW->keycodes[54] = RGFW_c; + _RGFW->keycodes[40] = RGFW_d; + _RGFW->keycodes[26] = RGFW_e; + _RGFW->keycodes[41] = RGFW_f; + _RGFW->keycodes[42] = RGFW_g; + _RGFW->keycodes[43] = RGFW_h; + _RGFW->keycodes[31] = RGFW_i; + _RGFW->keycodes[44] = RGFW_j; + _RGFW->keycodes[45] = RGFW_k; + _RGFW->keycodes[46] = RGFW_l; + _RGFW->keycodes[58] = RGFW_m; + _RGFW->keycodes[57] = RGFW_n; + _RGFW->keycodes[32] = RGFW_o; + _RGFW->keycodes[33] = RGFW_p; + _RGFW->keycodes[24] = RGFW_q; + _RGFW->keycodes[27] = RGFW_r; + _RGFW->keycodes[39] = RGFW_s; + _RGFW->keycodes[28] = RGFW_t; + _RGFW->keycodes[30] = RGFW_u; + _RGFW->keycodes[55] = RGFW_v; + _RGFW->keycodes[25] = RGFW_w; + _RGFW->keycodes[53] = RGFW_x; + _RGFW->keycodes[29] = RGFW_y; + _RGFW->keycodes[52] = RGFW_z; + _RGFW->keycodes[60] = RGFW_period; + _RGFW->keycodes[59] = RGFW_comma; + _RGFW->keycodes[61] = RGFW_slash; + _RGFW->keycodes[34] = RGFW_bracket; + _RGFW->keycodes[35] = RGFW_closeBracket; + _RGFW->keycodes[47] = RGFW_semicolon; + _RGFW->keycodes[48] = RGFW_apostrophe; + _RGFW->keycodes[51] = RGFW_backSlash; + _RGFW->keycodes[36] = RGFW_return; + _RGFW->keycodes[119] = RGFW_delete; + _RGFW->keycodes[77] = RGFW_numLock; + _RGFW->keycodes[106] = RGFW_kpSlash; + _RGFW->keycodes[63] = RGFW_kpMultiply; + _RGFW->keycodes[86] = RGFW_kpPlus; + _RGFW->keycodes[82] = RGFW_kpMinus; + _RGFW->keycodes[87] = RGFW_kp1; + _RGFW->keycodes[88] = RGFW_kp2; + _RGFW->keycodes[89] = RGFW_kp3; + _RGFW->keycodes[83] = RGFW_kp4; + _RGFW->keycodes[84] = RGFW_kp5; + _RGFW->keycodes[85] = RGFW_kp6; + _RGFW->keycodes[81] = RGFW_kp9; + _RGFW->keycodes[90] = RGFW_kp0; + _RGFW->keycodes[91] = RGFW_kpPeriod; + _RGFW->keycodes[104] = RGFW_kpReturn; + _RGFW->keycodes[20] = RGFW_minus; + _RGFW->keycodes[21] = RGFW_equals; + _RGFW->keycodes[22] = RGFW_backSpace; + _RGFW->keycodes[23] = RGFW_tab; + _RGFW->keycodes[66] = RGFW_capsLock; + _RGFW->keycodes[50] = RGFW_shiftL; + _RGFW->keycodes[37] = RGFW_controlL; + _RGFW->keycodes[64] = RGFW_altL; + _RGFW->keycodes[133] = RGFW_superL; + _RGFW->keycodes[105] = RGFW_controlR; + _RGFW->keycodes[134] = RGFW_superR; + _RGFW->keycodes[62] = RGFW_shiftR; + _RGFW->keycodes[108] = RGFW_altR; + _RGFW->keycodes[67] = RGFW_F1; + _RGFW->keycodes[68] = RGFW_F2; + _RGFW->keycodes[69] = RGFW_F3; + _RGFW->keycodes[70] = RGFW_F4; + _RGFW->keycodes[71] = RGFW_F5; + _RGFW->keycodes[72] = RGFW_F6; + _RGFW->keycodes[73] = RGFW_F7; + _RGFW->keycodes[74] = RGFW_F8; + _RGFW->keycodes[75] = RGFW_F9; + _RGFW->keycodes[76] = RGFW_F10; + _RGFW->keycodes[95] = RGFW_F11; + _RGFW->keycodes[96] = RGFW_F12; + _RGFW->keycodes[111] = RGFW_up; + _RGFW->keycodes[116] = RGFW_down; + _RGFW->keycodes[113] = RGFW_left; + _RGFW->keycodes[114] = RGFW_right; + _RGFW->keycodes[118] = RGFW_insert; + _RGFW->keycodes[115] = RGFW_end; + _RGFW->keycodes[112] = RGFW_pageUp; + _RGFW->keycodes[117] = RGFW_pageDown; + _RGFW->keycodes[9] = RGFW_escape; + _RGFW->keycodes[110] = RGFW_home; + _RGFW->keycodes[78] = RGFW_scrollLock; + _RGFW->keycodes[107] = RGFW_printScreen; + _RGFW->keycodes[128] = RGFW_pause; + _RGFW->keycodes[191] = RGFW_F13; + _RGFW->keycodes[192] = RGFW_F14; + _RGFW->keycodes[193] = RGFW_F15; + _RGFW->keycodes[194] = RGFW_F16; + _RGFW->keycodes[195] = RGFW_F17; + _RGFW->keycodes[196] = RGFW_F18; + _RGFW->keycodes[197] = RGFW_F19; + _RGFW->keycodes[198] = RGFW_F20; + _RGFW->keycodes[199] = RGFW_F21; + _RGFW->keycodes[200] = RGFW_F22; + _RGFW->keycodes[201] = RGFW_F23; + _RGFW->keycodes[202] = RGFW_F24; + _RGFW->keycodes[203] = RGFW_F25; + _RGFW->keycodes[142] = RGFW_kpEqual; + _RGFW->keycodes[161] = RGFW_world1; /* non-US key #1 */ + _RGFW->keycodes[162] = RGFW_world2; /* non-US key #2 */ +} + +i32 RGFW_initPlatform(void) { + #if defined(_POSIX_MONOTONIC_CLOCK) + struct timespec ts; + RGFW_MEMSET(&ts, 0, sizeof(struct timespec)); + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + _RGFW->clock = CLOCK_MONOTONIC; + #else + _RGFW->clock = CLOCK_REALTIME; + #endif + +#ifdef RGFW_WAYLAND + RGFW_load_Wayland(); + i32 ret = RGFW_initPlatform_Wayland(); + if (ret == 0) { + return 0; + } else { + #ifdef RGFW_X11 + RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningWayland, "Falling back to X11"); + RGFW_useWayland(0); + #else + return ret; + #endif + } +#endif +#ifdef RGFW_X11 + RGFW_load_X11(); + return RGFW_initPlatform_X11(); +#else + return 0; +#endif +} + + +void RGFW_deinitPlatform(void) { + if (_RGFW->eventWait_forceStop[0] || _RGFW->eventWait_forceStop[1]){ + close(_RGFW->eventWait_forceStop[0]); + close(_RGFW->eventWait_forceStop[1]); + } +#ifdef RGFW_WAYLAND + if (_RGFW->useWaylandBool) { + RGFW_deinitPlatform_Wayland(); + return; + } +#endif +#ifdef RGFW_X11 + RGFW_deinitPlatform_X11(); +#endif +} + +RGFWDEF size_t RGFW_unix_stringlen(const char* name); +size_t RGFW_unix_stringlen(const char* name) { + size_t i = 0; + while (name[i]) { i++; } + return i; +} + +#endif /* end of wayland or X11 defines */ + + +/* + + +Start of Linux / Unix defines + + +*/ + +#ifdef RGFW_X11 +#ifdef RGFW_WAYLAND +#define RGFW_FUNC(func) func##_X11 +#else +#define RGFW_FUNC(func) func +#endif + +#include +#include + +#include /* for data limits (mainly used in drag and drop functions) */ +#include + +void RGFW_setXInstName(const char* name) { _RGFW->instName = name; } +#if !defined(RGFW_NO_X11_CURSOR) && defined(RGFW_X11) + #include +#endif + +#include +#include +#include + +#include /* for converting keycode to string */ +#include /* for hiding */ +#include +#include +#include + +#ifdef RGFW_OPENGL + #ifndef __gl_h_ + #define __gl_h_ + #define RGFW_gl_ndef + #define GLubyte unsigned char + #define GLenum unsigned int + #define GLint int + #define GLuint unsigned int + #define GLsizei int + #define GLfloat float + #define GLvoid void + #define GLbitfield unsigned int + #define GLintptr ptrdiff_t + #define GLsizeiptr ptrdiff_t + #define GLboolean unsigned char + #endif + + #include /* GLX defs, xlib.h, gl.h */ + #ifndef GLX_MESA_swap_control + #define GLX_MESA_swap_control + #endif + + #ifdef RGFW_gl_ndef + #undef __gl_h_ + #undef GLubyte + #undef GLenum + #undef GLint + #undef GLuint + #undef GLsizei + #undef GLfloat + #undef GLvoid + #undef GLbitfield + #undef GLintptr + #undef GLsizeiptr + #undef GLboolean + #endif + typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); +#endif + +/* atoms needed for drag and drop */ +#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 + +#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 + +RGFWDEF RGFW_bool RGFW_waitForShowEvent_X11(RGFW_window* win); +RGFW_bool RGFW_waitForShowEvent_X11(RGFW_window* win) { + XEvent dummy; + while (!XCheckTypedWindowEvent(_RGFW->display, win->src.window, VisibilityNotify, &dummy)) { + RGFW_waitForEvent(100); + } + + return RGFW_TRUE; +} + +RGFWDEF void RGFW_x11_icCallback(XIC ic, char* clientData, char* callData); +void RGFW_x11_icCallback(XIC ic, char* clientData, char* callData) { + RGFW_UNUSED(ic); RGFW_UNUSED(callData); + RGFW_window* win = (RGFW_window*)(void*)clientData; + win->src.ic = NULL; +} + +RGFWDEF void RGFW_x11_imCallback(XIM im, char* clientData, char* callData); +void RGFW_x11_imCallback(XIM im, char* clientData, char* callData) { + RGFW_UNUSED(im); RGFW_UNUSED(clientData); RGFW_UNUSED(callData); + _RGFW->im = NULL; +} + +RGFWDEF void RGFW_x11_imInitCallback(Display* display, XPointer clientData, XPointer callData); +void RGFW_x11_imInitCallback(Display* display, XPointer clientData, XPointer callData) { + RGFW_UNUSED(display); RGFW_UNUSED(clientData); RGFW_UNUSED(callData); + + if (_RGFW->im) { + return; + } + + _RGFW->im = XOpenIM(_RGFW->display, 0, NULL, NULL); + if (_RGFW->im == NULL) { + return; + } + + RGFW_bool found = RGFW_FALSE; + XIMStyles* styles = NULL; + + if (XGetIMValues(_RGFW->im, XNQueryInputStyle, &styles, NULL) != NULL) { + found = RGFW_FALSE; + } else { + for (unsigned int i = 0; i < styles->count_styles; i++) { + if (styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing)) { + found = RGFW_TRUE; + break; + } + } + + XFree(styles); + } + + if (found == RGFW_FALSE) { + XCloseIM(_RGFW->im); + _RGFW->im = NULL; + } + + XIMCallback callback; + callback.callback = (XIMProc) RGFW_x11_imCallback; + callback.client_data = NULL; + XSetIMValues(_RGFW->im, XNDestroyCallback, &callback, NULL); +} + +void* RGFW_getDisplay_X11(void) { return _RGFW->display; } +u64 RGFW_window_getWindow_X11(RGFW_window* win) { return (u64)win->src.window; } + +RGFWDEF RGFW_format RGFW_XImage_getFormat(XImage* image); +RGFW_format RGFW_XImage_getFormat(XImage* image) { + switch (image->bits_per_pixel) { + case 24: + if (image->red_mask == 0xFF0000 && image->green_mask == 0x00FF00 && image->blue_mask == 0x0000FF) + return RGFW_formatRGB8; + if (image->red_mask == 0x0000FF && image->green_mask == 0x00FF00 && image->blue_mask == 0xFF0000) + return RGFW_formatBGR8; + break; + case 32: + if (image->red_mask == 0x00FF0000 && image->green_mask == 0x0000FF00 && image->blue_mask == 0x000000FF) + return RGFW_formatBGRA8; + if (image->red_mask == 0x000000FF && image->green_mask == 0x0000FF00 && image->blue_mask == 0x00FF0000) + return RGFW_formatRGBA8; + if (image->red_mask == 0x0000FF00 && image->green_mask == 0x00FF0000 && image->blue_mask == 0xFF000000) + return RGFW_formatABGR8; + if (image->red_mask == 0x00FF0000 && image->green_mask == 0x0000FF00 && image->blue_mask == 0x000000FF) + return RGFW_formatARGB8; /* ambiguous without alpha */ + break; + } + return RGFW_formatARGB8; +} + + +RGFW_bool RGFW_window_createSurfacePtr(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) { + RGFW_ASSERT(surface != NULL); + surface->data = data; + surface->w = w; + surface->h = h; + surface->format = format; + + XWindowAttributes attrs; + if (XGetWindowAttributes(_RGFW->display, win->src.window, &attrs) == 0) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, "Failed to get window attributes."); + return RGFW_FALSE; + } + + surface->native.bitmap = XCreateImage(_RGFW->display, attrs.visual, (u32)attrs.depth, + ZPixmap, 0, NULL, (u32)surface->w, (u32)surface->h, 32, 0); + + surface->native.buffer = (u8*)RGFW_ALLOC((size_t)(w * h * 4)); + surface->native.format = RGFW_XImage_getFormat(surface->native.bitmap); + + if (surface->native.bitmap == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, "Failed to create XImage."); + return RGFW_FALSE; + } + + surface->native.format = RGFW_formatBGRA8; + return RGFW_TRUE; +} + +RGFW_format RGFW_FUNC(RGFW_nativeFormat)(void) { return RGFW_formatBGRA8; } + +RGFW_bool RGFW_FUNC(RGFW_createSurfacePtr) (u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) { + return RGFW_window_createSurfacePtr(_RGFW->root, data, w, h, format, surface); +} + +void RGFW_FUNC(RGFW_window_blitSurface) (RGFW_window* win, RGFW_surface* surface) { + RGFW_ASSERT(surface != NULL); + surface->native.bitmap->data = (char*)surface->native.buffer; + RGFW_copyImageData((u8*)surface->native.buffer, surface->w, RGFW_MIN(win->h, surface->h), surface->native.format, surface->data, surface->format, surface->convertFunc); + + XPutImage(_RGFW->display, win->src.window, win->src.gc, surface->native.bitmap, 0, 0, 0, 0, (u32)RGFW_MIN(win->w, surface->w), (u32)RGFW_MIN(win->h, surface->h)); + surface->native.bitmap->data = NULL; + return; +} + +void RGFW_FUNC(RGFW_surface_freePtr) (RGFW_surface* surface) { + RGFW_ASSERT(surface != NULL); + RGFW_FREE(surface->native.buffer); + XDestroyImage(surface->native.bitmap); + return; +} + +#define RGFW_LOAD_ATOM(name) \ + static Atom name = 0; \ + if (name == 0) name = XInternAtom(_RGFW->display, #name, False); + +void RGFW_FUNC(RGFW_window_setBorder) (RGFW_window* win, RGFW_bool border) { + RGFW_setBit(&win->internal.flags, RGFW_windowNoBorder, !border); + 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(_RGFW->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); + } +} + +void RGFW_FUNC(RGFW_window_setRawMouseModePlatform) (RGFW_window* win, RGFW_bool state) { + RGFW_UNUSED(win); RGFW_UNUSED(state); +} + +void RGFW_FUNC(RGFW_window_captureMousePlatform) (RGFW_window* win, RGFW_bool state) { + if (state) { + unsigned int event_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask; + XGrabPointer(_RGFW->display, win->src.window, True, event_mask, GrabModeAsync, GrabModeAsync, win->src.window, None, CurrentTime); + } else { + XUngrabPointer(_RGFW->display, CurrentTime); + } +} + +#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) RGFW_MEMCPY(&name##SRC, &ptr, sizeof(PFN_##name)); \ +} + +RGFWDEF void RGFW_window_getVisual(XVisualInfo* visual, RGFW_bool transparent); +void RGFW_window_getVisual(XVisualInfo* visual, RGFW_bool transparent) { + visual->visual = DefaultVisual(_RGFW->display, DefaultScreen(_RGFW->display)); + visual->depth = DefaultDepth(_RGFW->display, DefaultScreen(_RGFW->display)); + if (transparent) { + XMatchVisualInfo(_RGFW->display, DefaultScreen(_RGFW->display), 32, TrueColor, visual); /*!< for RGBA backgrounds */ + if (visual->depth != 32) + RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, "Failed to load a 32-bit depth."); + } +} + +RGFWDEF int RGFW_XErrorHandler(Display* display, XErrorEvent* ev); +int RGFW_XErrorHandler(Display* display, XErrorEvent* ev) { + char errorText[512]; + XGetErrorText(display, ev->error_code, errorText, sizeof(errorText)); + + char buf[1024]; + RGFW_SNPRINTF(buf, sizeof(buf), "[X Error] %s\n Error code: %d\n Request code: %d\n Minor code: %d\n Serial: %lu\n", + errorText, + ev->error_code, ev->request_code, ev->minor_code, ev->serial); + + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errX11, buf); + _RGFW->x11Error = ev; + return 0; +} + +void RGFW_XCreateWindow (XVisualInfo visual, const char* name, RGFW_windowFlags flags, RGFW_window* win) { + i64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask | + LeaveWindowMask | EnterWindowMask | ExposureMask | VisibilityChangeMask | PropertyChangeMask; + + /* make X window attrubutes */ + XSetWindowAttributes swa; + RGFW_MEMSET(&swa, 0, sizeof(swa)); + + win->src.parent = DefaultRootWindow(_RGFW->display); + + Colormap cmap; + swa.colormap = cmap = XCreateColormap(_RGFW->display, + win->src.parent, + visual.visual, AllocNone); + swa.event_mask = event_mask; + swa.background_pixmap = None; + + /* create the window */ + win->src.window = XCreateWindow(_RGFW->display, win->src.parent, win->x, win->y, (u32)win->w, (u32)win->h, + 0, visual.depth, InputOutput, visual.visual, + CWBorderPixel | CWColormap | CWEventMask, &swa); + + win->src.flashEnd = 0; + + XFreeColors(_RGFW->display, cmap, NULL, 0, 0); + + XSaveContext(_RGFW->display, win->src.window, _RGFW->context, (XPointer)win); + + win->src.gc = XCreateGC(_RGFW->display, win->src.window, 0, NULL); + + if (_RGFW->im) { + XIMCallback callback; + callback.callback = (XIMProc) RGFW_x11_icCallback; + callback.client_data = (XPointer) win; + + win->src.ic = XCreateIC(_RGFW->im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, win->src.window, XNFocusWindow, win->src.window, XNDestroyCallback, &callback, 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(_RGFW->display, win->src.window, &hint); + + XWMHints hints; + hints.flags = StateHint; + hints.initial_state = NormalState; + + XSetWMHints(_RGFW->display, win->src.window, &hints); + + if (flags & RGFW_windowScaleToMonitor) + RGFW_window_scaleToMonitor(win); + + XSelectInput(_RGFW->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 */ + RGFW_LOAD_ATOM(WM_DELETE_WINDOW); + XSetWMProtocols(_RGFW->display, (Drawable) win->src.window, &WM_DELETE_WINDOW, 1); + /* set the background */ + RGFW_window_setName(win, name); + + XMoveWindow(_RGFW->display, (Drawable) win->src.window, win->x, win->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->internal.flags |= RGFW_windowAllowDND; + + /* actions */ + Atom XdndAware = XInternAtom(_RGFW->display, "XdndAware", False); + const u8 version = 5; + + XChangeProperty(_RGFW->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(_RGFW->display, win->src.window, protcols, 2); + + XSyncValue initial_value; + XSyncIntToValue(&initial_value, 0); + win->src.counter = XSyncCreateCounter(_RGFW->display, initial_value); + + XChangeProperty(_RGFW->display, win->src.window, _NET_WM_SYNC_REQUEST_COUNTER, XA_CARDINAL, 32, PropModeReplace, (u8*)&win->src.counter, 1); +#endif + + win->src.x = win->x; + win->src.y = win->y; + win->src.w = win->w; + win->src.h = win->h; + + XSetWindowBackground(_RGFW->display, win->src.window, None); + XClearWindow(_RGFW->display, win->src.window); + + /* stupid hack to make resizing the window less bad */ + XSetWindowBackgroundPixmap(_RGFW->display, win->src.window, None); +} + +RGFW_window* RGFW_FUNC(RGFW_createWindowPlatform) (const char* name, RGFW_windowFlags flags, RGFW_window* win) { + if ((flags & RGFW_windowOpenGL) || (flags & RGFW_windowEGL)) { + win->src.window = 0; + return win; + } + + XVisualInfo visual; + RGFW_window_getVisual(&visual, RGFW_BOOL(win->internal.flags & RGFW_windowTransparent)); + RGFW_XCreateWindow(visual, name, flags, win); + return win; /*return newly created window */ +} + +RGFW_bool RGFW_FUNC(RGFW_getGlobalMouse) (i32* fX, i32* fY) { + RGFW_init(); + i32 x, y; + u32 z; + Window window1, window2; + XQueryPointer(_RGFW->display, XDefaultRootWindow(_RGFW->display), &window1, &window2, fX, fY, &x, &y, &z); + return RGFW_TRUE; +} + +RGFWDEF void RGFW_XHandleClipboardSelection(XEvent* event); +void RGFW_XHandleClipboardSelection(XEvent* event) { RGFW_UNUSED(event); + RGFW_LOAD_ATOM(ATOM_PAIR); + RGFW_LOAD_ATOM(MULTIPLE); + RGFW_LOAD_ATOM(TARGETS); + RGFW_LOAD_ATOM(SAVE_TARGETS); + RGFW_LOAD_ATOM(UTF8_STRING); + + const XSelectionRequestEvent* request = &event->xselectionrequest; + Atom formats[2] = {0}; + formats[0] = UTF8_STRING; + formats[1] = XA_STRING; + const int formatCount = sizeof(formats) / sizeof(formats[0]); + + if (request->target == TARGETS) { + Atom targets[4] = {0}; + targets[0] = TARGETS; + targets[1] = MULTIPLE; + targets[2] = UTF8_STRING; + targets[3] = 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] == UTF8_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); + XFlush(_RGFW->display); +} + +i32 RGFW_XHandleClipboardSelectionHelper(void); + +RGFW_key RGFW_FUNC(RGFW_physicalToMappedKey) (RGFW_key key) { + KeyCode keycode = (KeyCode)RGFW_rgfwToApiKey(key); + KeySym sym = XkbKeycodeToKeysym(_RGFW->display, keycode, 0, 0); + + if (sym < 256) { + return (RGFW_key)sym; + } + + switch (sym) { + case XK_F1: return RGFW_F1; + case XK_F2: return RGFW_F2; + case XK_F3: return RGFW_F3; + case XK_F4: return RGFW_F4; + case XK_F5: return RGFW_F5; + case XK_F6: return RGFW_F6; + case XK_F7: return RGFW_F7; + case XK_F8: return RGFW_F8; + case XK_F9: return RGFW_F9; + case XK_F10: return RGFW_F10; + case XK_F11: return RGFW_F11; + case XK_F12: return RGFW_F12; + case XK_F13: return RGFW_F13; + case XK_F14: return RGFW_F14; + case XK_F15: return RGFW_F15; + case XK_F16: return RGFW_F16; + case XK_F17: return RGFW_F17; + case XK_F18: return RGFW_F18; + case XK_F19: return RGFW_F19; + case XK_F20: return RGFW_F20; + case XK_F21: return RGFW_F21; + case XK_F22: return RGFW_F22; + case XK_F23: return RGFW_F23; + case XK_F24: return RGFW_F24; + case XK_F25: return RGFW_F25; + case XK_Shift_L: return RGFW_shiftL; + case XK_Shift_R: return RGFW_shiftR; + case XK_Control_L: return RGFW_controlL; + case XK_Control_R: return RGFW_controlR; + case XK_Alt_L: return RGFW_altL; + case XK_Alt_R: return RGFW_altR; + case XK_Super_L: return RGFW_superL; + case XK_Super_R: return RGFW_superR; + case XK_Caps_Lock: return RGFW_capsLock; + case XK_Num_Lock: return RGFW_numLock; + case XK_Scroll_Lock:return RGFW_scrollLock; + case XK_Up: return RGFW_up; + case XK_Down: return RGFW_down; + case XK_Left: return RGFW_left; + case XK_Right: return RGFW_right; + case XK_Home: return RGFW_home; + case XK_End: return RGFW_end; + case XK_Page_Up: return RGFW_pageUp; + case XK_Page_Down: return RGFW_pageDown; + case XK_Insert: return RGFW_insert; + case XK_Menu: return RGFW_menu; + case XK_KP_Add: return RGFW_kpPlus; + case XK_KP_Subtract: return RGFW_kpMinus; + case XK_KP_Multiply: return RGFW_kpMultiply; + case XK_KP_Divide: return RGFW_kpSlash; + case XK_KP_Equal: return RGFW_kpEqual; + case XK_KP_Enter: return RGFW_kpReturn; + case XK_KP_Decimal: return RGFW_kpPeriod; + case XK_KP_0: return RGFW_kp0; + case XK_KP_1: return RGFW_kp1; + case XK_KP_2: return RGFW_kp2; + case XK_KP_3: return RGFW_kp3; + case XK_KP_4: return RGFW_kp4; + case XK_KP_5: return RGFW_kp5; + case XK_KP_6: return RGFW_kp6; + case XK_KP_7: return RGFW_kp7; + case XK_KP_8: return RGFW_kp8; + case XK_KP_9: return RGFW_kp9; + case XK_Print: return RGFW_printScreen; + case XK_Pause: return RGFW_pause; + default: break; + } + + return RGFW_keyNULL; +} + +RGFWDEF void RGFW_XHandleEvent(void); +void RGFW_XHandleEvent(void) { + 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); + RGFW_LOAD_ATOM(WM_STATE); + RGFW_LOAD_ATOM(_NET_WM_STATE); + + /* xdnd data */ + static Window source = 0; + static long version = 0; + static i32 format = 0; + + static float deltaX = 0.0f; + static float deltaY = 0.0f; + + XEvent reply = { ClientMessage }; + XEvent E; + + XNextEvent(_RGFW->display, &E); + + if (E.type != GenericEvent) { + deltaX = 0.0f; + deltaY = 0.0f; + } + +#ifndef RGFW_NO_XRANDR + if (E.type == _RGFW->xrandrEventBase + RRNotify) { + RGFW_pollMonitors(); + return; + } +#endif + + switch (E.type) { + case SelectionRequest: + RGFW_XHandleClipboardSelection(&E); + return; + case GenericEvent: { + XGetEventData(_RGFW->display, &E.xcookie); + switch (E.xcookie.evtype) { + case XI_RawMotion: { + XIRawEvent* raw = (XIRawEvent *)E.xcookie.data; + if (raw->valuators.mask_len == 0) { + XFreeEventData(_RGFW->display, &E.xcookie); + return; + } + + i32 index = 0; + if (XIMaskIsSet(raw->valuators.mask, 0) != 0) { + deltaX += (float)raw->raw_values[index]; + index += 1; + } + + if (XIMaskIsSet(raw->valuators.mask, 1) != 0) + deltaY += (float)raw->raw_values[index]; + + _RGFW->vectorX = (float)deltaX; + _RGFW->vectorY = (float)deltaY; + } + default: break; + } + + XFreeEventData(_RGFW->display, &E.xcookie); + return; + } + } + + RGFW_window* win = NULL; + if (XFindContext(_RGFW->display, E.xany.window, _RGFW->context, (XPointer*) &win) != 0) { + return; + } + + if (win->src.flashEnd) { + if ((win->src.flashEnd <= RGFW_linux_getTimeNS()) || RGFW_window_isInFocus(win)) { + RGFW_window_flash(win, RGFW_flashCancel); + } + } + + + /* + Repeated key presses are sent as a release followed by another press at the same time. + We want to convert that into a single key press event with the repeat flag set + */ + + RGFW_bool keyRepeat = RGFW_FALSE; + + if (E.type == KeyRelease && XEventsQueued(_RGFW->display, QueuedAfterReading)) { + XEvent NE; + XPeekEvent(_RGFW->display, &NE); + if (NE.type == KeyPress && E.xkey.time == NE.xkey.time && E.xkey.keycode == NE.xkey.keycode) { + /* Use the next KeyPress event */ + XNextEvent(_RGFW->display, &E); + keyRepeat = RGFW_TRUE; + } + } + + switch (E.type) { + case KeyPress: { + if (!(win->internal.enabledEvents & RGFW_keyPressedFlag)) return; + RGFW_key value = (u8)RGFW_apiKeyToRGFW(E.xkey.keycode); + + XkbStateRec state; + XkbGetState(_RGFW->display, XkbUseCoreKbd, &state); + RGFW_updateKeyMods(win, (state.locked_mods & LockMask), (state.locked_mods & Mod2Mask), (state.locked_mods & Mod3Mask)); + + if (win->src.ic && XFilterEvent(&E, None) == False) { + char buffer[100]; + char* chars = buffer; + + Status status; + size_t count = (size_t)Xutf8LookupString(win->src.ic, &E.xkey, buffer, sizeof(buffer) - 1, NULL, &status); + + if (status == XBufferOverflow) { + chars = (char*)RGFW_ALLOC(count + 1); + count = (size_t)Xutf8LookupString(win->src.ic, &E.xkey, chars, (int)count, NULL, &status); + } + + if (status == XLookupChars || status == XLookupBoth) { + chars[count] = '\0'; + for (size_t index = 0; index < count; + RGFW_keyCharCallback(win, RGFW_decodeUTF8(&chars[index], &index)) + ); + } + + if (chars != buffer) + RGFW_FREE(chars); + } else { + 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)E.xkey.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; + + RGFW_keyCharCallback(win, (u8)sym); + } + + RGFW_keyCallback(win, value, win->internal.mod, keyRepeat, RGFW_TRUE); + break; + } + case KeyRelease: { + if (!(win->internal.enabledEvents & RGFW_keyReleasedFlag)) return; + + RGFW_key value = (u8)RGFW_apiKeyToRGFW(E.xkey.keycode); + + XkbStateRec state; + XkbGetState(_RGFW->display, XkbUseCoreKbd, &state); + RGFW_updateKeyMods(win, (state.locked_mods & LockMask), (state.locked_mods & Mod2Mask), (state.locked_mods & Mod3Mask)); + + RGFW_keyCallback(win, value, win->internal.mod, keyRepeat, RGFW_FALSE); + break; + } + case ButtonPress: { + RGFW_bool scroll = RGFW_FALSE; + if (E.xbutton.button >= Button4 && E.xbutton.button <= 7) { + scroll = RGFW_TRUE; + } + + float scrollX = 0.0f; + float scrollY = 0.0f; + RGFW_mouseButton value = 0; + + switch (E.xbutton.button) { + case Button1: value = RGFW_mouseLeft; break; + case Button2: value = RGFW_mouseMiddle; break; + case Button3: value = RGFW_mouseRight; break; + case Button4: scrollY = 1.0; break; + case Button5: scrollY = -1.0; break; + case 6: scrollX = 1.0f; break; + case 7: scrollX = -1.0f; break; + default: + value = (u8)E.xbutton.button - Button1 - 4; + break; + } + + if (scroll) { + RGFW_mouseScrollCallback(win, scrollX, scrollY); + break; + } + + RGFW_mouseButtonCallback(win, value, RGFW_TRUE); + break; + } + case ButtonRelease: { + if (E.xbutton.button >= Button4 && E.xbutton.button <= 7) break; + + RGFW_mouseButton value = 0; + switch(E.xbutton.button) { + case Button1: value = RGFW_mouseLeft; break; + case Button2: value = RGFW_mouseMiddle; break; + case Button3: value = RGFW_mouseRight; break; + default: + value = (u8)E.xbutton.button - Button1 - 4; + break; + } + + RGFW_mouseButtonCallback(win, value, RGFW_FALSE); + break; + } + case MotionNotify: + RGFW_mousePosCallback(win, E.xmotion.x, E.xmotion.y, _RGFW->vectorX, _RGFW->vectorY); + break; + + case Expose: { + RGFW_windowRefreshCallback(win); + +#ifdef RGFW_ADVANCED_SMOOTH_RESIZE + XSyncValue value; + XSyncIntToValue(&value, (i32)win->src.counter_value); + XSyncSetCounter(_RGFW->display, win->src.counter, value); +#endif + break; + } + + case PropertyNotify: + if (E.xproperty.state != PropertyNewValue) break; + + if (E.xproperty.atom == WM_STATE) { + if (RGFW_window_isMinimized(win) && !(win->internal.flags & RGFW_windowMinimized)) { + RGFW_windowMinimizedCallback(win); + break; + } + } else if (E.xproperty.atom == _NET_WM_STATE) { + if (RGFW_window_isMaximized(win) && !(win->internal.flags & RGFW_windowMaximize)) { + RGFW_windowMaximizedCallback(win, win->x, win->y, win->w, win->h); + break; + } + } + + RGFW_window_checkMode(win); + break; + case MapNotify: case UnmapNotify: RGFW_window_checkMode(win); break; + case ClientMessage: { + RGFW_LOAD_ATOM(WM_DELETE_WINDOW); + /* if the client closed the window */ + if (E.xclient.data.l[0] == (long)WM_DELETE_WINDOW) { + 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(_RGFW->display, win->src.counter, value); + break; + } +#endif + if ((win->internal.flags & RGFW_windowAllowDND) == 0) + return; + + i32 dragX = 0; + i32 dragY = 0; + + 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( + _RGFW->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; + } + + Atom XtextPlain = XInternAtom(_RGFW->display, "text/plain", False); + Atom XtextUriList = XInternAtom(_RGFW->display, "text/uri-list", False); + + 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( + _RGFW->display, XDefaultRootWindow(_RGFW->display), win->src.window, + xabs, yabs, &xpos, &ypos, &dummy + ); + + dragX = xpos; + dragY = 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(_RGFW->display, source, False, NoEventMask, &reply); + XFlush(_RGFW->display); + break; + } + if (E.xclient.message_type != XdndDrop) + break; + + if (version > 5) + break; + + if (format) { + Time time = (version >= 1) + ? (Time)E.xclient.data.l[2] + : CurrentTime; + + XConvertSelection( + _RGFW->display, XdndSelection, (Atom)format, + XdndSelection, win->src.window, time + ); + } else if (version >= 2) { + XEvent new_reply = { ClientMessage }; + + XSendEvent(_RGFW->display, source, False, NoEventMask, &new_reply); + XFlush(_RGFW->display); + } + + RGFW_dataDragCallback(win, dragX, dragY); + } break; + case SelectionNotify: { + /* this is only for checking for xdnd drops */ + if (!(win->internal.enabledEvents & RGFW_dataDropFlag) || E.xselection.property != XdndSelection || !(win->internal.flags & RGFW_windowAllowDND)) + return; + char* data; + unsigned long result; + + Atom actualType; + i32 actualFormat; + unsigned long bytesAfter; + + XGetWindowProperty(_RGFW->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; + + size_t count = 0; + char** files = _RGFW->files; + + while ((line = (char*)RGFW_strtok(data, "\r\n"))) { + 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; + } + + count++; + + size_t len = RGFW_unix_stringlen(line); + char* path = (char*)RGFW_ALLOC(len + 1); + + size_t index = 0; + while (*line) { + if (line[0] == '%' && line[1] && line[2]) { + char digits[3] = {0}; + digits[0] = line[1]; + digits[1] = line[2]; + digits[2] = '\0'; + path[index] = (char) RGFW_STRTOL(digits, NULL, 16); + line += 2; + } else { + if (index >= len) { + break; + } + + path[index] = *line; + } + + index++; + line++; + } + + path[len] = '\0'; + size_t cnt = RGFW_MIN(len + 1, RGFW_MAX_PATH); + if (cnt == RGFW_MAX_PATH) { + path[cnt] = '\0'; + } + + RGFW_MEMCPY(files[count - 1], path, cnt); + RGFW_FREE(path); + } + + RGFW_dataDropCallback(win, files, count); + if (data) + XFree(data); + + if (version >= 2) { + 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(_RGFW->display, source, False, NoEventMask, &new_reply); + XFlush(_RGFW->display); + } + break; + } + case FocusIn: + if (win->src.ic) XSetICFocus(win->src.ic); + RGFW_focusCallback(win, 1); + break; + case FocusOut: + if (win->src.ic) XUnsetICFocus(win->src.ic); + RGFW_focusCallback(win, 0); + break; + case EnterNotify: { + RGFW_mouseNotifyCallback(win, E.xcrossing.x, E.xcrossing.y, RGFW_TRUE); + break; + } + + case LeaveNotify: { + RGFW_mouseNotifyCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, RGFW_FALSE); + break; + } + case ReparentNotify: + win->src.parent = E.xreparent.parent; + break; + case ConfigureNotify: { + /* detect resize */ + if (E.xconfigure.width != win->src.w || E.xconfigure.height != win->src.h) { + RGFW_window_checkMode(win); + win->src.w = E.xconfigure.width; + win->src.h = E.xconfigure.height; + RGFW_windowResizedCallback(win, E.xconfigure.width, E.xconfigure.height); + } + + i32 x = E.xconfigure.x; + i32 y = E.xconfigure.y; + + /* + if the event came from the server and we're not a direct child of the root window then + we're using local coords which need to be translated into screen coords + */ + Window root = DefaultRootWindow(_RGFW->display); + if (E.xany.send_event == 0 && win->src.parent != root) { + Window dummy = 0; + XTranslateCoordinates(_RGFW->display, win->src.parent, root, x, y, &x, &y, &dummy); + } + + /* detect move */ + if (E.xconfigure.x != win->src.x || E.xconfigure.y != win->src.y) { + win->src.x = E.xconfigure.x; + win->src.y = E.xconfigure.y; + RGFW_windowMovedCallback(win, E.xconfigure.x, E.xconfigure.y); + } + return; + } + default: + break; + } + + XFlush(_RGFW->display); +} + +void RGFW_FUNC(RGFW_pollEvents) (void) { + RGFW_resetPrevState(); + + XPending(_RGFW->display); + /* if there is no unread queued events, get a new one */ + while (QLength(_RGFW->display)) { + RGFW_XHandleEvent(); + } +} + +void RGFW_FUNC(RGFW_window_move) (RGFW_window* win, i32 x, i32 y) { + RGFW_ASSERT(win != NULL); + win->x = x; + win->y = y; + + XMoveWindow(_RGFW->display, win->src.window, x, y); + return; +} + + +void RGFW_FUNC(RGFW_window_resize) (RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + win->w = (i32)w; + win->h = (i32)h; + + XResizeWindow(_RGFW->display, win->src.window, (u32)w, (u32)h); + + if ((win->internal.flags & RGFW_windowNoResize)) { + XSizeHints sh; + sh.flags = (1L << 4) | (1L << 5); + sh.min_width = sh.max_width = (i32)w; + sh.min_height = sh.max_height = (i32)h; + + XSetWMSizeHints(_RGFW->display, (Drawable) win->src.window, &sh, XA_WM_NORMAL_HINTS); + } + return; +} + +void RGFW_FUNC(RGFW_window_setAspectRatio) (RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + + + if (w == 0 && h == 0) + return; + XSizeHints hints; + long flags; + + XGetWMNormalHints(_RGFW->display, win->src.window, &hints, &flags); + + hints.flags |= PAspect; + + hints.min_aspect.x = hints.max_aspect.x = (i32)w; + hints.min_aspect.y = hints.max_aspect.y = (i32)h; + + XSetWMNormalHints(_RGFW->display, win->src.window, &hints); + return; +} + +void RGFW_FUNC(RGFW_window_setMinSize) (RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + + long flags; + XSizeHints hints; + RGFW_MEMSET(&hints, 0, sizeof(XSizeHints)); + + XGetWMNormalHints(_RGFW->display, win->src.window, &hints, &flags); + + hints.flags |= PMinSize; + + hints.min_width = (i32)w; + hints.min_height = (i32)h; + + XSetWMNormalHints(_RGFW->display, win->src.window, &hints); + return; +} + +void RGFW_FUNC(RGFW_window_setMaxSize) (RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + + long flags; + XSizeHints hints; + RGFW_MEMSET(&hints, 0, sizeof(XSizeHints)); + + XGetWMNormalHints(_RGFW->display, win->src.window, &hints, &flags); + + hints.flags |= PMaxSize; + + hints.max_width = (i32)w; + hints.max_height = (i32)h; + + XSetWMNormalHints(_RGFW->display, win->src.window, &hints); + return; +} + +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(_RGFW->display, DefaultRootWindow(_RGFW->display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); +} + +void RGFW_FUNC(RGFW_window_maximize) (RGFW_window* win) { + win->internal.oldX = win->x; + win->internal.oldY = win->y; + win->internal.oldW = win->w; + win->internal.oldH = win->h; + + RGFW_toggleXMaximized(win, 1); + return; +} + +void RGFW_FUNC(RGFW_window_focus) (RGFW_window* win) { + RGFW_ASSERT(win); + + XWindowAttributes attr; + XGetWindowAttributes(_RGFW->display, win->src.window, &attr); + if (attr.map_state != IsViewable) return; + + XSetInputFocus(_RGFW->display, win->src.window, RevertToPointerRoot, CurrentTime); + XFlush(_RGFW->display); +} + +void RGFW_FUNC(RGFW_window_raise) (RGFW_window* win) { + RGFW_ASSERT(win); + XMapRaised(_RGFW->display, win->src.window); + RGFW_window_setFullscreen(win, RGFW_window_isFullscreen(win)); +} + +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(_RGFW->display, DefaultRootWindow(_RGFW->display), False, SubstructureNotifyMask | SubstructureRedirectMask, &xev); +} + +void RGFW_FUNC(RGFW_window_setFullscreen)(RGFW_window* win, RGFW_bool fullscreen) { + RGFW_ASSERT(win != NULL); + + if (fullscreen) { + win->internal.flags |= RGFW_windowFullscreen; + win->internal.oldX = win->x; + win->internal.oldY = win->y; + win->internal.oldW = win->w; + win->internal.oldH = win->h; + } + else win->internal.flags &= ~(u32)RGFW_windowFullscreen; + + XRaiseWindow(_RGFW->display, win->src.window); + + RGFW_LOAD_ATOM(_NET_WM_STATE_FULLSCREEN); + RGFW_window_setXAtom(win, _NET_WM_STATE_FULLSCREEN, fullscreen); + + if (!(win->internal.flags & RGFW_windowTransparent)) { + const unsigned char value = fullscreen; + RGFW_LOAD_ATOM(_NET_WM_BYPASS_COMPOSITOR); + XChangeProperty( + _RGFW->display, win->src.window, + _NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32, + PropModeReplace, &value, 1); + } +} + +void RGFW_FUNC(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_FUNC(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(_RGFW->display, win->src.window, + NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &value, 1); +} + +void RGFW_FUNC(RGFW_window_minimize)(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + if (RGFW_window_isMaximized(win)) return; + + win->internal.oldX = win->x; + win->internal.oldY = win->y; + win->internal.oldW = win->w; + win->internal.oldH = win->h; + XIconifyWindow(_RGFW->display, win->src.window, DefaultScreen(_RGFW->display)); + XFlush(_RGFW->display); +} + +void RGFW_FUNC(RGFW_window_restore)(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_toggleXMaximized(win, RGFW_FALSE); + RGFW_window_move(win, win->internal.oldX, win->internal.oldY); + RGFW_window_resize(win, win->internal.oldW, win->internal.oldH); + + RGFW_window_show(win); + XFlush(_RGFW->display); +} + +RGFW_bool RGFW_FUNC(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(_RGFW->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_FUNC(RGFW_window_setName)(RGFW_window* win, const char* name) { + RGFW_ASSERT(win != NULL); + if (name == NULL) name = "\0"; + + Xutf8SetWMProperties(_RGFW->display, win->src.window, name, name, NULL, 0, NULL, NULL, NULL); + XStoreName(_RGFW->display, win->src.window, name); + + RGFW_LOAD_ATOM(_NET_WM_NAME); RGFW_LOAD_ATOM(UTF8_STRING); + + XChangeProperty( + _RGFW->display, win->src.window, _NET_WM_NAME, UTF8_STRING, + 8, PropModeReplace, (u8*)name, (int)RGFW_unix_stringlen(name) + ); +} + +#ifndef RGFW_NO_PASSTHROUGH +void RGFW_FUNC(RGFW_window_setMousePassthrough) (RGFW_window* win, RGFW_bool passthrough) { + RGFW_ASSERT(win != NULL); + if (passthrough) { + Region region = XCreateRegion(); + XShapeCombineRegion(_RGFW->display, win->src.window, ShapeInput, 0, 0, region, ShapeSet); + XDestroyRegion(region); + + return; + } + + XShapeCombineMask(_RGFW->display, win->src.window, ShapeInput, 0, 0, None, ShapeSet); +} +#endif /* RGFW_NO_PASSTHROUGH */ + +RGFW_bool RGFW_FUNC(RGFW_window_setIconEx) (RGFW_window* win, u8* data_src, i32 w, i32 h, RGFW_format format, RGFW_icon type) { + Atom _NET_WM_ICON = XInternAtom(_RGFW->display, "_NET_WM_ICON", False); + RGFW_ASSERT(win != NULL); + if (data_src == NULL) { + RGFW_bool res = (RGFW_bool)XChangeProperty( + _RGFW->display, win->src.window, _NET_WM_ICON, XA_CARDINAL, 32, + PropModeReplace, (u8*)NULL, 0 + ); + return res; + } + + i32 count = (i32)(2 + (w * h)); + + unsigned long* data = (unsigned long*) RGFW_ALLOC((u32)count * sizeof(unsigned long)); + RGFW_ASSERT(data != NULL); + + RGFW_MEMSET(data, 0, (u32)count * sizeof(unsigned long)); + data[0] = (unsigned long)w; + data[1] = (unsigned long)h; + + RGFW_copyImageData64((u8*)&data[2], w, h, RGFW_formatBGRA8, data_src, format, RGFW_TRUE, NULL); + RGFW_bool res = RGFW_TRUE; + if (type & RGFW_iconTaskbar) { + res = (RGFW_bool)XChangeProperty( + _RGFW->display, win->src.window, _NET_WM_ICON, XA_CARDINAL, 32, + PropModeReplace, (u8*)data, count + ); + } + + RGFW_copyImageData64((u8*)&data[2], w, h, RGFW_formatBGRA8, data_src, format, RGFW_FALSE, NULL); + + if (type & RGFW_iconWindow) { + XWMHints wm_hints; + wm_hints.flags = IconPixmapHint; + + i32 depth = DefaultDepth(_RGFW->display, DefaultScreen(_RGFW->display)); + XImage *image = XCreateImage(_RGFW->display, DefaultVisual(_RGFW->display, DefaultScreen(_RGFW->display)), + (u32)depth, ZPixmap, 0, (char *)&data[2], (u32)w, (u32)h, 32, 0); + + wm_hints.icon_pixmap = XCreatePixmap(_RGFW->display, win->src.window, (u32)w, (u32)h, (u32)depth); + XPutImage(_RGFW->display, wm_hints.icon_pixmap, DefaultGC(_RGFW->display, DefaultScreen(_RGFW->display)), image, 0, 0, 0, 0, (u32)w, (u32)h); + image->data = NULL; + XDestroyImage(image); + + XSetWMHints(_RGFW->display, win->src.window, &wm_hints); + } + + RGFW_FREE(data); + XFlush(_RGFW->display); + return RGFW_BOOL(res); +} + +RGFW_mouse* RGFW_FUNC(RGFW_loadMouse) (u8* data, i32 w, i32 h, RGFW_format format) { + RGFW_ASSERT(data); +#ifndef RGFW_NO_X11_CURSOR + RGFW_init(); + XcursorImage* native = XcursorImageCreate((i32)w, (i32)h); + native->xhot = 0; + native->yhot = 0; + RGFW_MEMSET(native->pixels, 0, (u32)(w * h * 4)); + RGFW_copyImageData((u8*)native->pixels, w, h, RGFW_formatBGRA8, data, format, NULL); + + Cursor cursor = XcursorImageLoadCursor(_RGFW->display, native); + XcursorImageDestroy(native); + + return (void*)cursor; +#else + RGFW_UNUSED(data); RGFW_UNUSED(w); RGFW_UNUSED(h); RGFW_UNUSED(format); + return NULL; +#endif +} + +void RGFW_FUNC(RGFW_window_setMouse)(RGFW_window* win, RGFW_mouse* mouse) { + RGFW_ASSERT(win && mouse); + XDefineCursor(_RGFW->display, win->src.window, (Cursor)mouse); +} + +void RGFW_FUNC(RGFW_freeMouse)(RGFW_mouse* mouse) { + RGFW_ASSERT(mouse); + XFreeCursor(_RGFW->display, (Cursor)mouse); +} + +void RGFW_FUNC(RGFW_window_moveMouse)(RGFW_window* win, i32 x, i32 y) { + RGFW_ASSERT(win != NULL); + + XEvent event; + XQueryPointer(_RGFW->display, DefaultRootWindow(_RGFW->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->internal.lastMouseX = x - win->x; + win->internal.lastMouseY = y - win->y; + if (event.xbutton.x == x && event.xbutton.y == y) + return; + + XWarpPointer(_RGFW->display, None, win->src.window, 0, 0, 0, 0, (int) x - win->x, (int) y - win->y); +} + +RGFW_bool RGFW_FUNC(RGFW_window_setMouseDefault) (RGFW_window* win) { + return RGFW_window_setMouseStandard(win, RGFW_mouseArrow); +} + +RGFW_bool RGFW_FUNC(RGFW_window_setMouseStandard) (RGFW_window* win, u8 mouse) { + RGFW_ASSERT(win != NULL); + + u32 mouseIcon = 0; + + switch (mouse) { + case RGFW_mouseNormal: mouseIcon = XC_left_ptr; break; + case RGFW_mouseArrow: mouseIcon = XC_left_ptr; break; + case RGFW_mouseIbeam: mouseIcon = XC_xterm; break; + case RGFW_mouseWait: mouseIcon = XC_watch; break; + case RGFW_mouseCrosshair: mouseIcon = XC_tcross; break; + case RGFW_mouseProgress: mouseIcon = XC_watch; break; + case RGFW_mouseResizeNWSE: mouseIcon = XC_top_left_corner; break; + case RGFW_mouseResizeNESW: mouseIcon = XC_top_right_corner; break; + case RGFW_mouseResizeEW: mouseIcon = XC_sb_h_double_arrow; break; + case RGFW_mouseResizeNS: mouseIcon = XC_sb_v_double_arrow; break; + case RGFW_mouseResizeNW: mouseIcon = XC_top_left_corner; break; + case RGFW_mouseResizeN: mouseIcon = XC_top_side; break; + case RGFW_mouseResizeNE: mouseIcon = XC_top_right_corner; break; + case RGFW_mouseResizeE: mouseIcon = XC_right_side; break; + case RGFW_mouseResizeSE: mouseIcon = XC_bottom_right_corner; break; + case RGFW_mouseResizeS: mouseIcon = XC_bottom_side; break; + case RGFW_mouseResizeSW: mouseIcon = XC_bottom_left_corner; break; + case RGFW_mouseResizeW: mouseIcon = XC_left_side; break; + case RGFW_mouseResizeAll: mouseIcon = XC_fleur; break; + case RGFW_mouseNotAllowed: mouseIcon = XC_pirate; break; + case RGFW_mousePointingHand: mouseIcon = XC_hand2; break; + default: return RGFW_FALSE; + } + + Cursor cursor = XCreateFontCursor(_RGFW->display, mouseIcon); + XDefineCursor(_RGFW->display, win->src.window, (Cursor) cursor); + XFreeCursor(_RGFW->display, (Cursor) cursor); + return RGFW_TRUE; +} + +void RGFW_FUNC(RGFW_window_hide)(RGFW_window* win) { + win->internal.flags |= (u32)RGFW_windowHide; + XUnmapWindow(_RGFW->display, win->src.window); + + XFlush(_RGFW->display); +} + +void RGFW_FUNC(RGFW_window_show) (RGFW_window* win) { + win->internal.flags &= ~(u32)RGFW_windowHide; + if (win->internal.flags & RGFW_windowFocusOnShow) RGFW_window_focus(win); + + if (RGFW_window_isHidden(win) == RGFW_FALSE) { + return; + } + + XMapWindow(_RGFW->display, win->src.window); + RGFW_window_move(win, win->x, win->y); + + RGFW_waitForShowEvent_X11(win); + RGFW_window_setFullscreen(win, RGFW_window_isFullscreen(win)); + return; +} + +void RGFW_FUNC(RGFW_window_flash) (RGFW_window* win, RGFW_flashRequest request) { + if (RGFW_window_isInFocus(win) && request) { + return; + } + + XWMHints* wmhints = XGetWMHints(_RGFW->display, win->src.window); + if (wmhints == NULL) return; + + if (request) { + wmhints->flags |= XUrgencyHint; + if (request == RGFW_flashBriefly) + win->src.flashEnd = RGFW_linux_getTimeNS() + (u64)1e+9; + if (request == RGFW_flashUntilFocused) + win->src.flashEnd = (u64)-1; + } else { + win->src.flashEnd = 0; + wmhints->flags &= ~XUrgencyHint; + } + + XSetWMHints(_RGFW->display, win->src.window, wmhints); + XFree(wmhints); +} + +RGFW_ssize_t RGFW_FUNC(RGFW_readClipboardPtr)(char* str, size_t strCapacity) { + RGFW_init(); + RGFW_LOAD_ATOM(XSEL_DATA); RGFW_LOAD_ATOM(UTF8_STRING); RGFW_LOAD_ATOM(CLIPBOARD); + if (XGetSelectionOwner(_RGFW->display, CLIPBOARD) == _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; + int format; + unsigned long N, sizeN; + char* data; + Atom target; + + XConvertSelection(_RGFW->display, CLIPBOARD, UTF8_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 != CLIPBOARD || 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 == UTF8_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; +} + +i32 RGFW_XHandleClipboardSelectionHelper(void) { + 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; +} + +void RGFW_FUNC(RGFW_writeClipboard)(const char* text, u32 textLen) { + RGFW_LOAD_ATOM(SAVE_TARGETS); RGFW_LOAD_ATOM(CLIPBOARD); + RGFW_init(); + + /* request ownership of the clipboard section and request to convert it, this means its our job to convert it */ + XSetSelectionOwner(_RGFW->display, CLIPBOARD, _RGFW->helperWindow, CurrentTime); + if (XGetSelectionOwner(_RGFW->display, CLIPBOARD) != _RGFW->helperWindow) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errClipboard, "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 - 1); + _RGFW->clipboard[textLen - 1] = '\0'; + _RGFW->clipboard_len = textLen; + return; +} + +RGFW_bool RGFW_FUNC(RGFW_window_isHidden)(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + XWindowAttributes windowAttributes; + XGetWindowAttributes(_RGFW->display, win->src.window, &windowAttributes); + + return (windowAttributes.map_state != IsViewable); +} + +RGFW_bool RGFW_FUNC(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(_RGFW->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(_RGFW->display, win->src.window, &windowAttributes); + return windowAttributes.map_state != IsViewable; +} + +RGFW_bool RGFW_FUNC(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(_RGFW->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; +} + +RGFWDEF void RGFW_XGetSystemContentDPI(float* dpi); +void RGFW_XGetSystemContentDPI(float* dpi) { + if (dpi == NULL) return; + float dpiOutput = 96.0f; + + #ifndef RGFW_NO_XRANDR + char* rms = XResourceManagerString(_RGFW->display); + if (rms == NULL) return; + + XrmDatabase db = XrmGetStringDatabase(rms); + if (db == NULL) return; + + XrmValue value; + char* type = NULL; + + if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value) && type && RGFW_STRNCMP(type, "String", 7) == 0) + dpiOutput = (float)RGFW_ATOF(value.addr); + XrmDestroyDatabase(db); + #endif + + if (dpi) *dpi = dpiOutput; +} + +RGFWDEF XRRModeInfo* RGFW_XGetMode(XRRCrtcInfo* ci, XRRScreenResources* res, RRMode mode, RGFW_monitorMode* foundMode); +XRRModeInfo* RGFW_XGetMode(XRRCrtcInfo* ci, XRRScreenResources* res, RRMode mode, RGFW_monitorMode* foundMode) { + XRRModeInfo* mi = None; + for (i32 j = 0; j < res->nmode; j++) { + if (res->modes[j].id == mode) + mi = &res->modes[j]; + } + + if (mi == None) return NULL; + + if ((mi->modeFlags & RR_Interlace) != 0) return NULL; + + foundMode->w = (i32)mi->width; + foundMode->h = (i32)mi->height; + if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) { + foundMode->w = (i32)mi->height; + foundMode->h = (i32)mi->width; + } else { + foundMode->w = (i32)mi->width; + foundMode->h = (i32)mi->height; + } + + RGFW_splitBPP((u32)DefaultDepth(_RGFW->display, DefaultScreen(_RGFW->display)), foundMode); + + foundMode->src = (void*)mode; + + foundMode->refreshRate = 0; + if (mi->hTotal == 0 || mi->vTotal == 0) + return mi; + + u32 vTotal = mi->vTotal; + + if (mi->modeFlags & RR_DoubleScan) { + vTotal *= 2; + } + + if (mi->modeFlags & RR_Interlace) { + vTotal /= 2; + } + + i32 numerator = (i32)mi->dotClock; + i32 denominator = (i32)(mi->hTotal * vTotal); + float refreshRate = 0; + + if (denominator <= 0) { + denominator = 1; + } + + refreshRate = ((float)numerator / (float)denominator); + + foundMode->refreshRate = RGFW_ROUNDF((refreshRate * 100)) / 100.0f; + return mi; +} + +void RGFW_FUNC(RGFW_pollMonitors) (void) { + RGFW_init(); + + Window root = XDefaultRootWindow(_RGFW->display); + XRRScreenResources* res = XRRGetScreenResourcesCurrent(_RGFW->display, root); + if (res == 0) { + return; + } + + RROutput primary = XRRGetOutputPrimary(_RGFW->display, root); + + for (RGFW_monitorNode* node = _RGFW->monitors.list.head; node; node = node->next) { + node->disconnected = RGFW_TRUE; + } + + for (i32 i = 0; i < res->noutput; i++) { + RGFW_monitorNode* node = NULL; + for (node = _RGFW->monitors.list.head; node; node = node->next) { + if (node->rrOutput == res->outputs[i]) { + break; + } + } + + if (node) { + node->disconnected = RGFW_FALSE; + if (node->rrOutput == primary) { + _RGFW->monitors.primary = node; + } + continue; + } + + RGFW_monitor monitor; + + XRROutputInfo* info = XRRGetOutputInfo(_RGFW->display, res, res->outputs[i]); + if (info == NULL || info->connection != RR_Connected || info->crtc == None) { + continue; + } + + XRRCrtcInfo* ci = XRRGetCrtcInfo(_RGFW->display, res, info->crtc); + + if (ci == NULL) { + continue; + } + + float physW = (float)info->mm_width / 25.4f; + float physH = (float)info->mm_height / 25.4f; + + RGFW_STRNCPY(monitor.name, info->name, sizeof(monitor.name) - 1); + monitor.name[sizeof(monitor.name) - 1] = '\0'; + + if (physW > 0.0f && physH > 0.0f) { + monitor.physW = physW; + monitor.physH = physH; + } else { + monitor.physW = (float) ((float)ci->width / 96.f); + monitor.physH = (float) ((float)ci->height / 96.f); + } + + monitor.x = ci->x; + monitor.y = ci->y; + + float dpi = 96.0f; + RGFW_XGetSystemContentDPI(&dpi); + + monitor.scaleX = dpi / 96.0f; + monitor.scaleY = dpi / 96.0f; + + monitor.pixelRatio = dpi >= 192.0f ? 2.0f : 1.0f; + + XRRModeInfo* mi = RGFW_XGetMode(ci, res, ci->mode, &monitor.mode); + + if (mi == NULL) { + break; + } + + XRRFreeCrtcInfo(ci); + + node = RGFW_monitors_add(&monitor); + if (node == NULL) break; + + node->rrOutput = res->outputs[i]; + node->crtc = info->crtc; + + if (node->rrOutput == primary) { + _RGFW->monitors.primary = node; + } + + XRRFreeOutputInfo(info); + info = NULL; + + RGFW_monitorCallback(_RGFW->root, &node->mon, RGFW_TRUE); + } + + XRRFreeScreenResources(res); + + RGFW_monitors_refresh(); +} + +RGFW_bool RGFW_FUNC(RGFW_monitor_getWorkarea) (RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height) { + RGFW_LOAD_ATOM(_NET_WORKAREA); + RGFW_LOAD_ATOM(_NET_CURRENT_DESKTOP); + + Window root = DefaultRootWindow(_RGFW->display); + + i32 areaX = monitor->x; + i32 areaY = monitor->y; + i32 areaW = monitor->mode.w; + i32 areaH = monitor->mode.h; + + if (_NET_WORKAREA && _NET_CURRENT_DESKTOP) { + Atom* extents = NULL; + Atom* desktop = NULL; + + Atom actualType = 0; + int actualFormat = 0; + unsigned long extentCount = 0, bytesAfter = 0; + XGetWindowProperty(_RGFW->display, root, _NET_WORKAREA, 0, LONG_MAX, False, XA_CARDINAL, &actualType, &actualFormat, &extentCount, &bytesAfter, (u8**) &extents); + + unsigned long count; + XGetWindowProperty(_RGFW->display, root, _NET_CURRENT_DESKTOP, 0, LONG_MAX, False, XA_CARDINAL, &actualType, &actualFormat, &count, &bytesAfter, (u8**) &desktop); + + if (count) { + if (extentCount >= 4 && *desktop < extentCount / 4) { + i32 globalX = (i32)extents[*desktop * 4 + 0]; + i32 globalY = (i32)extents[*desktop * 4 + 1]; + i32 globalW = (i32)extents[*desktop * 4 + 2]; + i32 globalH = (i32)extents[*desktop * 4 + 3]; + + if (areaX < globalX) { + areaW -= globalX - areaX; + areaX = globalX; + } + + if (areaY < globalY) { + areaH -= globalY - areaY; + areaY = globalY; + } + + if (areaX + areaW > globalX + globalW) + areaW = globalX - areaX + globalW; + if (areaY + areaH > globalY + globalH) + areaH = globalY - areaY + globalH; + } + } + + if (extents) + XFree(extents); + if (desktop) + XFree(desktop); + } + + if (x) *x = areaX; + if (y) *y = areaY; + if (width) *width = areaW; + if (height) *height = areaH; + + return RGFW_TRUE; +} + +size_t RGFW_FUNC(RGFW_monitor_getModesPtr) (RGFW_monitor* monitor, RGFW_monitorMode** modes) { + size_t count = 0; + + XRRScreenResources* res = XRRGetScreenResourcesCurrent(_RGFW->display, DefaultRootWindow(_RGFW->display)); + if (res == NULL) return 0; + + XRRCrtcInfo* ci = XRRGetCrtcInfo(_RGFW->display, res, monitor->node->crtc); + XRROutputInfo* oi = XRRGetOutputInfo(_RGFW->display, res, monitor->node->rrOutput); + count = (size_t)oi->nmode; + + int i; + for (i = 0; modes && i < oi->nmode; i++) { + XRRModeInfo* mi = RGFW_XGetMode(ci, res, oi->modes[i], &((*modes)[i])); + RGFW_UNUSED(mi); + } + + XRRFreeOutputInfo(oi); + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(res); + + return count; +} + +size_t RGFW_FUNC(RGFW_monitor_getGammaRampPtr) (RGFW_monitor* monitor, RGFW_gammaRamp* ramp) { + RGFW_UNUSED(monitor); RGFW_UNUSED(ramp); +#ifndef RGFW_NO_XRANDR + size_t size = (size_t)XRRGetCrtcGammaSize(_RGFW->display, monitor->node->crtc); + XRRCrtcGamma* gamma = XRRGetCrtcGamma(_RGFW->display, monitor->node->crtc); + + if (ramp) { + RGFW_MEMCPY(ramp->red, gamma->red, size * sizeof(unsigned short)); + RGFW_MEMCPY(ramp->green, gamma->green, size * sizeof(unsigned short)); + RGFW_MEMCPY(ramp->blue, gamma->blue, size * sizeof(unsigned short)); + } + + XRRFreeGamma(gamma); + return size; +#endif + + return 0; +} + +RGFW_bool RGFW_FUNC(RGFW_monitor_setGammaRamp) (RGFW_monitor* monitor, RGFW_gammaRamp* ramp) { + RGFW_UNUSED(monitor); RGFW_UNUSED(ramp); + +#ifndef RGFW_NO_XRANDR + size_t size = (size_t)XRRGetCrtcGammaSize(_RGFW->display, monitor->node->crtc); + if (size != ramp->count) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errX11, "X11: Gamma ramp size must match current ramp size"); + return RGFW_FALSE; + } + + XRRCrtcGamma* gamma = XRRAllocGamma((int)ramp->count); + + memcpy(gamma->red, ramp->red, ramp->count * sizeof(unsigned short)); + memcpy(gamma->green, ramp->green, ramp->count * sizeof(unsigned short)); + memcpy(gamma->blue, ramp->blue, ramp->count * sizeof(unsigned short)); + + XRRSetCrtcGamma(_RGFW->display, monitor->node->crtc, gamma); + XRRFreeGamma(gamma); + + return RGFW_TRUE; +#endif + return RGFW_FALSE; +} + +RGFW_bool RGFW_FUNC(RGFW_monitor_setMode)(RGFW_monitor* mon, RGFW_monitorMode* mode) { + RGFW_bool out = RGFW_FALSE; + + XRRScreenResources* res = XRRGetScreenResourcesCurrent(_RGFW->display, DefaultRootWindow(_RGFW->display)); + XRRCrtcInfo* ci = XRRGetCrtcInfo(_RGFW->display, res, mon->node->crtc); + + if (XRRSetCrtcConfig(_RGFW->display, res, mon->node->crtc, CurrentTime, ci->x, ci->y, (RRMode)mode->src, ci->rotation, ci->outputs, ci->noutput) == True) { + out = RGFW_TRUE; + } + + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(res); + return out; +} + +RGFW_bool RGFW_FUNC(RGFW_monitor_requestMode)(RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request) { + #ifndef RGFW_NO_XRANDR + RGFW_init(); + + RGFW_bool output = RGFW_FALSE; + + XRRScreenResources* res = XRRGetScreenResourcesCurrent(_RGFW->display, DefaultRootWindow(_RGFW->display)); + if (res == NULL) return RGFW_FALSE; + + XRRCrtcInfo* ci = XRRGetCrtcInfo(_RGFW->display, res, mon->node->crtc); + XRROutputInfo* oi = XRRGetOutputInfo(_RGFW->display, res, mon->node->rrOutput); + + RRMode native = None; + + int i; + for (i = 0; i < oi->nmode; i++) { + RGFW_monitorMode foundMode; + XRRModeInfo* mi = RGFW_XGetMode(ci, res, oi->modes[i], &foundMode); + if (mi == NULL) { + continue; + } + + if (RGFW_monitorModeCompare(mode, &foundMode, request)) { + native = mi->id; + output = RGFW_TRUE; + mon->mode = foundMode; + break; + } + } + + if (native) { + XRRSetCrtcConfig(_RGFW->display, res, mon->node->crtc, CurrentTime, ci->x, ci->y, native, ci->rotation, ci->outputs, ci->noutput); + } + + XRRFreeOutputInfo(oi); + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(res); + return output; +#endif + return RGFW_FALSE; +} + +RGFW_monitor* RGFW_FUNC(RGFW_window_getMonitor) (RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + XWindowAttributes attrs; + if (!XGetWindowAttributes(_RGFW->display, win->src.window, &attrs)) { + return NULL; + } + + for (RGFW_monitorNode* node = _RGFW->monitors.list.head; node; node = node->next) { + if ((attrs.x < node->mon.x + node->mon.mode.w) && (attrs.x + attrs.width > node->mon.x) && (attrs.y < node->mon.y + node->mon.mode.h) && (attrs.y + attrs.height > node->mon.y)) + return &node->mon; + } + + + return &_RGFW->monitors.list.head->mon; +} + +#ifdef RGFW_OPENGL +RGFW_bool RGFW_FUNC(RGFW_window_createContextPtr_OpenGL) (RGFW_window* win, RGFW_glContext* context, RGFW_glHints* hints) { + /* for checking extensions later */ + const char sRGBARBstr[] = "GLX_ARB_framebuffer_sRGB"; + const char sRGBEXTstr[] = "GLX_EXT_framebuffer_sRGB"; + const char noErorrStr[] = "GLX_ARB_create_context_no_error"; + const char flushStr[] = "GLX_ARB_context_flush_control"; + const char robustStr[] = "GLX_ARB_create_context_robustness"; + + /* basic RGFW int */ + win->src.ctx.native = context; + win->src.gfxType = RGFW_gfxNativeOpenGL; + + /* This is required so that way the user can create their own OpenGL context after RGFW_createWindow is used */ + RGFW_bool showWindow = RGFW_FALSE; + if (win->src.window) { + showWindow = (RGFW_window_isMinimized(win) == RGFW_FALSE); + RGFW_window_closePlatform(win); + } + + RGFW_bool transparent = (win->internal.flags & RGFW_windowTransparent); + + /* start by creating a GLX config / X11 Viusal */ + XVisualInfo visual; + GLXFBConfig bestFbc; + + i32 visual_attribs[40]; + RGFW_attribStack stack; + RGFW_attribStack_init(&stack, visual_attribs, 40); + RGFW_attribStack_pushAttribs(&stack, GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR); + RGFW_attribStack_pushAttribs(&stack, GLX_X_RENDERABLE, 1); + RGFW_attribStack_pushAttribs(&stack, GLX_RENDER_TYPE, GLX_RGBA_BIT); + RGFW_attribStack_pushAttribs(&stack, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT); + RGFW_attribStack_pushAttribs(&stack, GLX_DOUBLEBUFFER, 1); + RGFW_attribStack_pushAttribs(&stack, GLX_ALPHA_SIZE, hints->alpha); + RGFW_attribStack_pushAttribs(&stack, GLX_DEPTH_SIZE, hints->depth); + RGFW_attribStack_pushAttribs(&stack, GLX_STENCIL_SIZE, hints->stencil); + RGFW_attribStack_pushAttribs(&stack, GLX_STEREO, hints->stereo); + RGFW_attribStack_pushAttribs(&stack, GLX_AUX_BUFFERS, hints->auxBuffers); + RGFW_attribStack_pushAttribs(&stack, GLX_RED_SIZE, hints->red); + RGFW_attribStack_pushAttribs(&stack, GLX_GREEN_SIZE, hints->green); + RGFW_attribStack_pushAttribs(&stack, GLX_BLUE_SIZE, hints->blue); + RGFW_attribStack_pushAttribs(&stack, GLX_ACCUM_RED_SIZE, hints->accumRed); + RGFW_attribStack_pushAttribs(&stack, GLX_ACCUM_GREEN_SIZE, hints->accumGreen); + RGFW_attribStack_pushAttribs(&stack, GLX_ACCUM_BLUE_SIZE, hints->accumBlue); + RGFW_attribStack_pushAttribs(&stack, GLX_ACCUM_ALPHA_SIZE, hints->accumAlpha); + + if (hints->sRGB) { + if (RGFW_extensionSupportedPlatform_OpenGL(sRGBARBstr, sizeof(sRGBARBstr))) + RGFW_attribStack_pushAttribs(&stack, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, hints->sRGB); + if (RGFW_extensionSupportedPlatform_OpenGL(sRGBEXTstr, sizeof(sRGBEXTstr))) + RGFW_attribStack_pushAttribs(&stack, GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, hints->sRGB); + } + + RGFW_attribStack_pushAttribs(&stack, 0, 0); + + /* find the configs */ + i32 fbcount; + GLXFBConfig* fbc = glXChooseFBConfig(_RGFW->display, DefaultScreen(_RGFW->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, "Failed to find any valid GLX visual configs."); + return 0; + } + + /* search through all found configs to find the best match */ + i32 i; + for (i = 0; i < fbcount; i++) { + XVisualInfo* vi = glXGetVisualFromFBConfig(_RGFW->display, fbc[i]); + if (vi == NULL) + continue; + + i32 samp_buf, samples; + glXGetFBConfigAttrib(_RGFW->display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf); + glXGetFBConfigAttrib(_RGFW->display, fbc[i], GLX_SAMPLES, &samples); + + if (best_fbc == -1) best_fbc = i; + if ((!(transparent) || vi->depth == 32) && best_depth == 0) { + best_fbc = i; + best_depth = vi->depth; + } + if ((!(transparent) || vi->depth == 32) && samples <= hints->samples && 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, "Failed to get a valid GLX visual."); + return 0; + } + + /* we found a config */ + bestFbc = fbc[best_fbc]; + XVisualInfo* vi = glXGetVisualFromFBConfig(_RGFW->display, bestFbc); + if (vi->depth != 32 && transparent) + RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, "Failed to to find a matching visual with a 32-bit depth."); + + if (best_samples < hints->samples) + RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, "Failed to load a matching sample count."); + + XFree(fbc); + visual = *vi; + XFree(vi); + + /* use the visual to create a new window */ + RGFW_XCreateWindow(visual, "", win->internal.flags, win); + + if (showWindow) { + RGFW_window_show(win); + } + + /* create the actual OpenGL context */ + i32 context_attribs[40]; + RGFW_attribStack_init(&stack, context_attribs, 40); + + i32 mask = 0; + switch (hints->profile) { + case RGFW_glES: mask |= GLX_CONTEXT_ES_PROFILE_BIT_EXT; break; + case RGFW_glForwardCompatibility: mask |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; break; + case RGFW_glCompatibility: mask |= GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; break; + case RGFW_glCore: mask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB; break; + default: mask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB; break; + } + + RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_PROFILE_MASK_ARB, mask); + + if (hints->minor || hints->major) { + RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_MAJOR_VERSION_ARB, hints->major); + RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_MINOR_VERSION_ARB, hints->minor); + } + + + if (RGFW_extensionSupportedPlatform_OpenGL(flushStr, sizeof(flushStr))) { + if (hints->releaseBehavior == RGFW_glReleaseFlush) { + RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); + } else if (hints->releaseBehavior == RGFW_glReleaseNone) { + RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); + } + } + + i32 flags = 0; + if (hints->debug) flags |= GLX_CONTEXT_DEBUG_BIT_ARB; + if (hints->robustness && RGFW_extensionSupportedPlatform_OpenGL(robustStr, sizeof(robustStr))) flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB; + if (flags) { + RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_FLAGS_ARB, flags); + } + + if (RGFW_extensionSupportedPlatform_OpenGL(noErorrStr, sizeof(noErorrStr))) { + RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_OPENGL_NO_ERROR_ARB, hints->noError); + } + + RGFW_attribStack_pushAttribs(&stack, 0, 0); + + /* create the context */ + glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; + char str[] = "glXCreateContextAttribsARB"; + glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)glXGetProcAddressARB((u8*) str); + + GLXContext ctx = NULL; + if (hints->share) { + ctx = hints->share->ctx; + } + + if (glXCreateContextAttribsARB == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to load proc address 'glXCreateContextAttribsARB', loading a generic OpenGL context."); + win->src.ctx.native->ctx = glXCreateContext(_RGFW->display, &visual, ctx, True); + } else { + _RGFW->x11Error = NULL; + win->src.ctx.native->ctx = glXCreateContextAttribsARB(_RGFW->display, bestFbc, ctx, True, context_attribs); + if (_RGFW->x11Error || win->src.ctx.native->ctx == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to create an OpenGL context with AttribsARB, loading a generic OpenGL context."); + win->src.ctx.native->ctx = glXCreateContext(_RGFW->display, &visual, ctx, True); + } + } + + #ifndef RGFW_NO_GLXWINDOW + win->src.ctx.native->window = glXCreateWindow(_RGFW->display, bestFbc, win->src.window, NULL); + #else + win->src.ctx.native->window = win->src.window; + #endif + + glXMakeCurrent(_RGFW->display, (Drawable)win->src.ctx.native->window, (GLXContext)win->src.ctx.native->ctx); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context initalized."); + + RGFW_window_swapInterval_OpenGL(win, 0); + + return RGFW_TRUE; +} + +void RGFW_FUNC(RGFW_window_deleteContextPtr_OpenGL) (RGFW_window* win, RGFW_glContext* ctx) { + #ifndef RGFW_NO_GLXWINDOW + if (win->src.ctx.native->window != win->src.window) { + glXDestroyWindow(_RGFW->display, win->src.ctx.native->window); + } + #endif + + glXDestroyContext(_RGFW->display, ctx->ctx); + win->src.ctx.native = NULL; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context freed."); +} + +RGFW_bool RGFW_FUNC(RGFW_extensionSupportedPlatform_OpenGL)(const char * extension, size_t len) { + RGFW_init(); + const char* extensions = glXQueryExtensionsString(_RGFW->display, XDefaultScreen(_RGFW->display)); + return (extensions != NULL) && RGFW_extensionSupportedStr(extensions, extension, len); +} + +RGFW_proc RGFW_FUNC(RGFW_getProcAddress_OpenGL)(const char* procname) { return glXGetProcAddress((u8*) procname); } + +void RGFW_FUNC(RGFW_window_makeCurrentContext_OpenGL) (RGFW_window* win) { if (win) RGFW_ASSERT(win->src.ctx.native); + if (win == NULL) + glXMakeCurrent(NULL, (Drawable)NULL, (GLXContext) NULL); + else + glXMakeCurrent(_RGFW->display, (Drawable)win->src.ctx.native->window, (GLXContext) win->src.ctx.native->ctx); + return; +} +void* RGFW_FUNC(RGFW_getCurrentContext_OpenGL) (void) { return glXGetCurrentContext(); } +void RGFW_FUNC(RGFW_window_swapBuffers_OpenGL) (RGFW_window* win) { RGFW_ASSERT(win->src.ctx.native); glXSwapBuffers(_RGFW->display, win->src.ctx.native->window); } + +void RGFW_FUNC(RGFW_window_swapInterval_OpenGL) (RGFW_window* win, i32 swapInterval) { + RGFW_ASSERT(win != NULL); + /* cached pfn to avoid calling glXGetProcAddress more than once */ + static PFNGLXSWAPINTERVALEXTPROC pfn = NULL; + static int (*pfn2)(int) = NULL; + + if (pfn == NULL) { + u8 str[] = "glXSwapIntervalEXT"; + pfn = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress(str); + if (pfn == NULL) { + pfn = (PFNGLXSWAPINTERVALEXTPROC)1; + const char* array[] = {"GLX_MESA_swap_control", "GLX_SGI_swap_control"}; + + size_t i; + for (i = 0; i < sizeof(array) / sizeof(char*) && pfn2 == NULL; i++) { + pfn2 = (int(*)(int))glXGetProcAddress((u8*)array[i]); + } + + if (pfn2 != NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to load swap interval function, fallingback to the native swapinterval function"); + } else { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to load swap interval function"); + } + } + } + + if (pfn != (PFNGLXSWAPINTERVALEXTPROC)1) { + pfn(_RGFW->display, win->src.ctx.native->window, swapInterval); + } + else if (pfn2 != NULL) { + pfn2(swapInterval); + } +} +#endif /* RGFW_OPENGL */ + +i32 RGFW_initPlatform_X11(void) { + #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); + _RGFW->context = XUniqueContext(); + + 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); + + u8 RGFW_blk[] = { 0, 0, 0, 0 }; + _RGFW->hiddenMouse = RGFW_loadMouse(RGFW_blk, 1, 1, RGFW_formatRGBA8); + _RGFW->clipboard = NULL; + + XkbComponentNamesRec rec; + XkbDescPtr desc = XkbGetMap(_RGFW->display, 0, XkbUseCoreKbd); + XkbDescPtr evdesc; + XSetErrorHandler(RGFW_XErrorHandler); + u8 old[256]; + + XkbGetNames(_RGFW->display, XkbKeyNamesMask, desc); + + RGFW_MEMSET(&rec, 0, sizeof(rec)); + char evdev[] = "evdev"; + rec.keycodes = evdev; + evdesc = XkbGetKeyboardByName(_RGFW->display, XkbUseCoreKbd, &rec, XkbGBN_KeyNamesMask, XkbGBN_KeyNamesMask, False); + /* memo: RGFW_keycodes[x11 keycode] = rgfw keycode */ + if(evdesc != NULL && desc != NULL) { + int i, j; + for(i = 0; i < (int)sizeof(old); i++){ + old[i] = _RGFW->keycodes[i]; + _RGFW->keycodes[i] = 0; + } + for(i = evdesc->min_key_code; i <= evdesc->max_key_code; i++){ + for(j = desc->min_key_code; j <= desc->max_key_code; j++){ + if(RGFW_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); + } + + XSetLocaleModifiers(""); + XRegisterIMInstantiateCallback(_RGFW->display, NULL, NULL, NULL, RGFW_x11_imInitCallback, NULL); + + unsigned char mask[XIMaskLen(XI_RawMotion)]; + RGFW_MEMSET(mask, 0, sizeof(mask)); + XISetMask(mask, XI_RawMotion); + + XIEventMask em; + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; + + XISelectEvents(_RGFW->display, XDefaultRootWindow(_RGFW->display), &em, 1); + +#ifndef RGFW_NO_XRANDR + i32 errorBase; + if (XRRQueryExtension(_RGFW->display, &_RGFW->xrandrEventBase, &errorBase)) { + XRRSelectInput(_RGFW->display, RootWindow(_RGFW->display, DefaultScreen(_RGFW->display)), RROutputChangeNotifyMask); + } +#endif + + return 0; +} + +void RGFW_deinitPlatform_X11(void) { + #define RGFW_FREE_LIBRARY(x) if (x != NULL) dlclose(x); x = NULL; + /* to save the clipboard on the x server after the window is closed */ + RGFW_LOAD_ATOM(CLIPBOARD_MANAGER); RGFW_LOAD_ATOM(CLIPBOARD); + RGFW_LOAD_ATOM(SAVE_TARGETS); + if (XGetSelectionOwner(_RGFW->display, CLIPBOARD) == _RGFW->helperWindow) { + XConvertSelection(_RGFW->display, CLIPBOARD_MANAGER, SAVE_TARGETS, None, _RGFW->helperWindow, CurrentTime); + while (RGFW_XHandleClipboardSelectionHelper()); + } + + XUnregisterIMInstantiateCallback(_RGFW->display, NULL, NULL, NULL, RGFW_x11_imInitCallback, NULL); + + if (_RGFW->im) { + XCloseIM(_RGFW->im); + _RGFW->im = NULL; + } + + if (_RGFW->clipboard) { + RGFW_FREE(_RGFW->clipboard); + _RGFW->clipboard = NULL; + } + + if (_RGFW->hiddenMouse) { + RGFW_freeMouse(_RGFW->hiddenMouse); + _RGFW->hiddenMouse = NULL; + } + + 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) + 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 +} + +void RGFW_FUNC(RGFW_window_closePlatform)(RGFW_window* win) { + if (win->src.ic) { + XDestroyIC(win->src.ic); + win->src.ic = NULL; + } + + XFreeGC(_RGFW->display, win->src.gc); + XDeleteContext(_RGFW->display, win->src.window, _RGFW->context); + XDestroyWindow(_RGFW->display, (Drawable) win->src.window); /*!< close the window */ + return; +} + +#ifdef RGFW_WEBGPU +WGPUSurface RGFW_FUNC(RGFW_window_createSurface_WebGPU) (RGFW_window* window, WGPUInstance instance) { + WGPUSurfaceDescriptor surfaceDesc = {0}; + WGPUSurfaceSourceXlibWindow fromXlib = {0}; + fromXlib.chain.sType = WGPUSType_SurfaceSourceXlibWindow; + fromXlib.display = _RGFW->display; + fromXlib.window = window->src.window; + + surfaceDesc.nextInChain = (WGPUChainedStruct*)&fromXlib.chain; + return wgpuInstanceCreateSurface(instance, &surfaceDesc); +} +#endif + +#endif +/* + End of X11 linux / wayland / unix defines +*/ + +/* + + Start of Wayland defayland +*/ + +#ifdef RGFW_WAYLAND +#ifdef RGFW_X11 +#undef RGFW_FUNC /* remove previous define */ +#define RGFW_FUNC(func) func##_Wayland +#else +#define RGFW_FUNC(func) func +#endif + +/* +Wayland TODO: (out of date) +- fix RGFW_keyPressed lock state + + RGFW_windowMoved, the window was moved (by the user) + RGFW_windowRefresh The window content needs to be refreshed + + RGFW_dataDrop a file has been dropped into the window + RGFW_dataDrag + +- 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 +#include +#include + +struct wl_display* RGFW_getDisplay_Wayland(void) { return _RGFW->wl_display; } +struct wl_surface* RGFW_window_getWindow_Wayland(RGFW_window* win) { return win->src.surface; } + + +/* wayland global garbage (wayland bad, X11 is fine (ish) (not really)) */ +#include "xdg-shell.h" +#include "xdg-toplevel-icon-v1.h" +#include "xdg-decoration-unstable-v1.h" +#include "relative-pointer-unstable-v1.h" +#include "pointer-constraints-unstable-v1.h" +#include "xdg-output-unstable-v1.h" +#include "pointer-warp-v1.h" + +void RGFW_toggleWaylandMaximized(RGFW_window* win, RGFW_bool maximized); + +static void RGFW_wl_setOpaque(RGFW_window* win) { + struct wl_region* wl_region = wl_compositor_create_region(_RGFW->compositor); + + if (!wl_region) return; /* return if no region was created */ + + wl_region_add(wl_region, 0, 0, win->w, win->h); + wl_surface_set_opaque_region(win->src.surface, wl_region); + wl_region_destroy(wl_region); + +} + +static void RGFW_wl_xdg_wm_base_ping_handler(void* data, struct xdg_wm_base* wm_base, + u32 serial) { + RGFW_UNUSED(data); + xdg_wm_base_pong(wm_base, serial); +} +static void RGFW_wl_xdg_surface_configure_handler(void* data, struct xdg_surface* xdg_surface, + u32 serial) { + + xdg_surface_ack_configure(xdg_surface, serial); + + RGFW_window* win = (RGFW_window*)data; + + if (win == NULL) { + win = _RGFW->kbOwner; + if (win == NULL) + return; + } + + /* useful for libdecor */ + if (win->src.activated != win->src.pending_activated) { + win->src.activated = win->src.pending_activated; + } + + if (win->src.maximized != win->src.pending_maximized) { + RGFW_toggleWaylandMaximized(win, win->src.pending_maximized); + + RGFW_window_checkMode(win); + } + + + if (win->src.resizing) { + + RGFW_windowResizedCallback(win, win->w, win->h); + RGFW_window_resize(win, win->w, win->h); + if (!(win->internal.flags & RGFW_windowTransparent)) { + RGFW_wl_setOpaque(win); + } + } + + win->src.configured = RGFW_TRUE; +} + +static void RGFW_wl_xdg_toplevel_configure_handler(void* data, struct xdg_toplevel* toplevel, + i32 width, i32 height, struct wl_array* states) { + + RGFW_UNUSED(toplevel); + RGFW_window* win = (RGFW_window*)data; + + + win->src.pending_activated = RGFW_FALSE; + win->src.pending_maximized = RGFW_FALSE; + win->src.resizing = RGFW_FALSE; + + + enum xdg_toplevel_state* state; + wl_array_for_each(state, states) { + switch (*state) { + case XDG_TOPLEVEL_STATE_ACTIVATED: + win->src.pending_activated = RGFW_TRUE; + break; + case XDG_TOPLEVEL_STATE_MAXIMIZED: + win->src.pending_maximized = RGFW_TRUE; + break; + default: + break; + } + + } + /* if width and height are not zero and are not the same as the window */ + /* the window is resizing so update the values */ + if ((width && height) && (win->w != width || win->h != height)) { + win->src.resizing = RGFW_TRUE; + win->src.w = win->w = width; + win->src.h = win->h = height; + } +} + +static void RGFW_wl_xdg_toplevel_close_handler(void* data, struct xdg_toplevel *toplevel) { + RGFW_UNUSED(toplevel); + RGFW_window* win = (RGFW_window*)data; + + if (!win->internal.shouldClose) { + RGFW_windowQuitCallback(win); + } +} + +static void RGFW_wl_xdg_decoration_configure_handler(void* data, + struct zxdg_toplevel_decoration_v1* zxdg_toplevel_decoration_v1, u32 mode) { + RGFW_window* win = (RGFW_window*)data; RGFW_UNUSED(zxdg_toplevel_decoration_v1); + + /* this is expected to run once */ + /* set the decoration mode set by earlier request */ + if (mode != win->src.decoration_mode) { + win->src.decoration_mode = mode; + } +} + +static void RGFW_wl_shm_format_handler(void* data, struct wl_shm *shm, u32 format) { + RGFW_UNUSED(data); RGFW_UNUSED(shm); RGFW_UNUSED(format); +} + +static void RGFW_wl_relative_pointer_motion(void *data, struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1, + u32 time_hi, u32 time_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel) { + + RGFW_UNUSED(zwp_relative_pointer_v1); RGFW_UNUSED(time_hi); RGFW_UNUSED(time_lo); + RGFW_UNUSED(dx_unaccel); RGFW_UNUSED(dy_unaccel); + + RGFW_info* RGFW = (RGFW_info*)data; + + RGFW_ASSERT(RGFW->mouseOwner != NULL); + RGFW_window* win = RGFW->mouseOwner; + + RGFW_ASSERT(win); + + float vecX = (float)wl_fixed_to_double(dx); + float vecY = (float)wl_fixed_to_double(dy); + RGFW_mousePosCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, vecX, vecY); +} + +static void RGFW_wl_pointer_locked(void *data, struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1) { + RGFW_UNUSED(zwp_locked_pointer_v1); + RGFW_info* RGFW = (RGFW_info*)data; + wl_pointer_set_cursor(RGFW->wl_pointer, RGFW->mouse_enter_serial, NULL, 0, 0); /* draw no cursor */ +} + +static void RGFW_wl_pointer_enter(void* data, struct wl_pointer* pointer, u32 serial, + struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { + RGFW_info* RGFW = (RGFW_info*)data; + RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); + + /* save when the pointer is locked or using default cursor */ + RGFW->mouse_enter_serial = serial; + win->internal.mouseInside = RGFW_TRUE; + RGFW->windowState.mouseEnter = RGFW_TRUE; + + RGFW->mouseOwner = win; + + /* set the cursor */ + if (win->src.using_custom_cursor) { + wl_pointer_set_cursor(pointer, serial, win->src.custom_cursor_surface, 0, 0); + } + else { + RGFW_window_setMouseDefault(win); + } + + i32 x = (i32)wl_fixed_to_double(surface_x); + i32 y = (i32)wl_fixed_to_double(surface_y); + RGFW_mouseNotifyCallback(win, x, y, RGFW_TRUE); +} + +static void RGFW_wl_pointer_leave(void* data, struct wl_pointer *pointer, u32 serial, struct wl_surface *surface) { + RGFW_UNUSED(pointer); RGFW_UNUSED(serial); + RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); + RGFW_info* RGFW = (RGFW_info*)data; + if (RGFW->mouseOwner == win) + RGFW->mouseOwner = NULL; + + RGFW_mouseNotifyCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, RGFW_FALSE); +} + +static void RGFW_wl_pointer_motion(void* data, struct wl_pointer *pointer, u32 time, wl_fixed_t x, wl_fixed_t y) { + RGFW_UNUSED(pointer); RGFW_UNUSED(time); + + RGFW_info* RGFW = (RGFW_info*)data; + RGFW_ASSERT(RGFW->mouseOwner != NULL); + + RGFW_window* win = RGFW->mouseOwner; + + i32 convertedX = (i32)wl_fixed_to_double(x); + i32 convertedY = (i32)wl_fixed_to_double(y); + float newVecX = (float)(convertedX - win->internal.lastMouseX); + float newVecY = (float)(convertedY - win->internal.lastMouseY); + + RGFW_mousePosCallback(win, convertedX, convertedY, newVecX, newVecY); +} + +static void RGFW_wl_pointer_button(void* data, struct wl_pointer *pointer, u32 serial, u32 time, u32 button, u32 state) { + RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(serial); + RGFW_info* RGFW = (RGFW_info*)data; + + RGFW_ASSERT(RGFW->mouseOwner != NULL); + RGFW_window* win = RGFW->mouseOwner; + + u32 b = (button - 0x110); + + /* flip right and middle button codes */ + if (b == 1) b = 2; + else if (b == 2) b = 1; + + RGFW_mouseButtonCallback(win, (u8)b, RGFW_BOOL(state)); +} + +static void RGFW_wl_pointer_axis(void* data, struct wl_pointer *pointer, u32 time, u32 axis, wl_fixed_t value) { + RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(axis); + + RGFW_info* RGFW = (RGFW_info*)data; + RGFW_ASSERT(RGFW->mouseOwner != NULL); + RGFW_window* win = RGFW->mouseOwner; + + float scrollX = 0.0; + float scrollY = 0.0; + + if (!(win->internal.enabledEvents & (RGFW_BIT(RGFW_mouseScroll)))) return; + + if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) + scrollX = (float)(-wl_fixed_to_double(value) / 10.0); + else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) + scrollY = (float)(-wl_fixed_to_double(value) / 10.0); + + RGFW_mouseScrollCallback(win, scrollX, scrollY); +} + + +static void RGFW_doNothing(void) { } + +static void RGFW_wl_keyboard_keymap(void* data, struct wl_keyboard *keyboard, u32 format, i32 fd, u32 size) { + RGFW_UNUSED(keyboard); RGFW_UNUSED(format); + RGFW_info* RGFW = (RGFW_info*)data; + + char *keymap_string = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0); + xkb_keymap_unref(RGFW->keymap); + RGFW->keymap = xkb_keymap_new_from_string(RGFW->xkb_context, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + + munmap(keymap_string, size); + close(fd); + xkb_state_unref(RGFW->xkb_state); + RGFW->xkb_state = xkb_state_new(RGFW->keymap); + + const char* locale = getenv("LC_ALL"); + if (!locale) + locale = getenv("LC_CTYPE"); + if (!locale) + locale = getenv("LANG"); + if (!locale) + locale = "C"; + + struct xkb_compose_table* composeTable = xkb_compose_table_new_from_locale(RGFW->xkb_context, locale, XKB_COMPOSE_COMPILE_NO_FLAGS); + if (composeTable) { + RGFW->composeState = xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS); + xkb_compose_table_unref(composeTable); + } +} + +static void RGFW_wl_keyboard_enter(void* data, struct wl_keyboard *keyboard, u32 serial, struct wl_surface *surface, struct wl_array *keys) { + RGFW_UNUSED(keyboard); RGFW_UNUSED(keys); + + RGFW_info* RGFW = (RGFW_info*)data; + RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); + RGFW->kbOwner = win; + + + // this is to prevent race conditions + if (RGFW->data_device != NULL && win->src.data_source != NULL) { + wl_data_device_set_selection(RGFW->data_device, win->src.data_source, serial); + } + /* is set when RGFW_window_minimize is called; if the minimize button is */ + /* pressed this flag is not set since there is no event to listen for */ + if (win->src.minimized == RGFW_TRUE) win->src.minimized = RGFW_FALSE; + + RGFW_focusCallback(win, RGFW_TRUE); +} + +static void RGFW_wl_keyboard_leave(void* data, struct wl_keyboard *keyboard, u32 serial, struct wl_surface *surface) { + RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); + + RGFW_info* RGFW = (RGFW_info*)data; + RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); + if (RGFW->kbOwner == win) + RGFW->kbOwner = NULL; + + RGFW_focusCallback(win, RGFW_FALSE); +} + +static xkb_keysym_t RGFW_wl_composeSymbol(RGFW_info* RGFW, xkb_keysym_t sym) { + if (sym == XKB_KEY_NoSymbol || !RGFW->composeState) + return sym; + if (xkb_compose_state_feed(RGFW->composeState, sym) != XKB_COMPOSE_FEED_ACCEPTED) + return sym; + switch (xkb_compose_state_get_status(RGFW->composeState)) { + case XKB_COMPOSE_COMPOSED: + return xkb_compose_state_get_one_sym(RGFW->composeState); + case XKB_COMPOSE_COMPOSING: + case XKB_COMPOSE_CANCELLED: + return XKB_KEY_NoSymbol; + case XKB_COMPOSE_NOTHING: + default: + return sym; + } +} + +static void RGFW_wl_keyboard_key(void* data, struct wl_keyboard *keyboard, u32 serial, u32 time, u32 key, u32 state) { + RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time); + + RGFW_info* RGFW = (RGFW_info*)data; + if (RGFW->kbOwner == NULL) return; + + RGFW_window *RGFW_key_win = RGFW->kbOwner; + RGFW_key RGFWkey = RGFW_apiKeyToRGFW(key + 8); + + RGFW_updateKeyMods(RGFW_key_win, RGFW_BOOL(xkb_keymap_mod_get_index(RGFW->keymap, "Lock")), RGFW_BOOL(xkb_keymap_mod_get_index(RGFW->keymap, "Mod2")), RGFW_BOOL(xkb_keymap_mod_get_index(RGFW->keymap, "ScrollLock"))); + RGFW_keyCallback(RGFW_key_win, (u8)RGFWkey, RGFW_key_win->internal.mod, RGFW_window_isKeyDown(RGFW_key_win, (u8)RGFWkey), RGFW_BOOL(state)); + + const xkb_keysym_t* keysyms; + if (xkb_state_key_get_syms(RGFW->xkb_state, key + 8, &keysyms) == 1) { + xkb_keysym_t keysym = RGFW_wl_composeSymbol(RGFW, keysyms[0]); + u32 codepoint = xkb_keysym_to_utf32(keysym); + if (codepoint != 0) { + RGFW_keyCharCallback(RGFW_key_win, codepoint); + } + } +} + +static void RGFW_wl_keyboard_modifiers(void* data, struct wl_keyboard *keyboard, u32 serial, u32 mods_depressed, u32 mods_latched, u32 mods_locked, u32 group) { + RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time); + RGFW_info* RGFW = (RGFW_info*)data; + xkb_state_update_mask(RGFW->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); +} + +static void RGFW_wl_seat_capabilities(void* data, struct wl_seat *seat, u32 capabilities) { + RGFW_info* RGFW = (RGFW_info*)data; + static struct wl_pointer_listener pointer_listener; + RGFW_MEMSET(&pointer_listener, 0, sizeof(pointer_listener)); + pointer_listener.enter = &RGFW_wl_pointer_enter; + pointer_listener.leave = &RGFW_wl_pointer_leave; + pointer_listener.motion = &RGFW_wl_pointer_motion; + pointer_listener.button = &RGFW_wl_pointer_button; + pointer_listener.axis = &RGFW_wl_pointer_axis; + + static struct wl_keyboard_listener keyboard_listener; + RGFW_MEMSET(&keyboard_listener, 0, sizeof(keyboard_listener)); + keyboard_listener.keymap = &RGFW_wl_keyboard_keymap; + keyboard_listener.enter = &RGFW_wl_keyboard_enter; + keyboard_listener.leave = &RGFW_wl_keyboard_leave; + keyboard_listener.key = &RGFW_wl_keyboard_key; + keyboard_listener.modifiers = &RGFW_wl_keyboard_modifiers; + + if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !RGFW->wl_pointer) { + RGFW->wl_pointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(RGFW->wl_pointer, &pointer_listener, RGFW); + } + if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && !RGFW->wl_keyboard) { + RGFW->wl_keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(RGFW->wl_keyboard, &keyboard_listener, RGFW); + } + + if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && RGFW->wl_pointer) { + wl_pointer_destroy(RGFW->wl_pointer); + } + if (!(capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && RGFW->wl_keyboard) { + wl_keyboard_destroy(RGFW->wl_keyboard); + } +} + +static void RGFW_wl_output_set_geometry(void *data, struct wl_output *wl_output, + i32 x, i32 y, i32 physical_width, i32 physical_height, + i32 subpixel, const char *make, const char *model, i32 transform) { + + RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon; + monitor->x = x; + monitor->y = y; + + monitor->physW = (float)physical_width / 25.4f; + monitor->physH = (float)physical_height / 25.4f; + + RGFW_UNUSED(wl_output); + RGFW_UNUSED(subpixel); + RGFW_UNUSED(make); + RGFW_UNUSED(model); + RGFW_UNUSED(transform); +} + +static void RGFW_wl_output_handle_mode(void *data, struct wl_output *wl_output, u32 flags, + i32 width, i32 height, i32 refresh) { + + RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon; + + RGFW_monitorMode mode; + mode.w = width; + mode.h = height; + mode.refreshRate = (float)refresh / 1000.0f; + mode.src = wl_output; + + monitor->node->modeCount += 1; + + RGFW_monitorMode* modes = (RGFW_monitorMode*)RGFW_ALLOC(monitor->node->modeCount * sizeof(RGFW_monitorMode)); + + if (monitor->node->modeCount > 1) { + RGFW_monitor_getModesPtr(monitor, &modes); + RGFW_FREE(monitor->node->modes); + } + + modes[monitor->node->modeCount - 1] = mode; + monitor->node->modes = modes; + + if (flags & WL_OUTPUT_MODE_CURRENT) { + monitor->mode = mode; + } else { + } +} + +static void RGFW_wl_output_set_scale(void *data, struct wl_output *wl_output, i32 factor) { + RGFW_UNUSED(wl_output); + RGFW_monitor* mon = &((RGFW_monitorNode*)data)->mon; + + mon->scaleX = (float)factor; + mon->scaleY = (float)factor; +} + +static void RGFW_wl_output_set_name(void *data, struct wl_output *wl_output, const char *name) { + RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon; + + RGFW_STRNCPY(monitor->name, name, sizeof(monitor->name) - 1); + monitor->name[sizeof(monitor->name) - 1] = '\0'; + + RGFW_UNUSED(wl_output); + +} + +static void RGFW_xdg_output_logical_pos(void *data, struct zxdg_output_v1 *zxdg_output_v1, i32 x, i32 y) { + RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon; + monitor->x = x; + monitor->y = y; + RGFW_UNUSED(zxdg_output_v1); +} + +static void RGFW_xdg_output_logical_size(void *data, struct zxdg_output_v1 *zxdg_output_v1, i32 width, i32 height) { + RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon; + + float mon_float_width = (float) monitor->mode.w; + float mon_float_height = (float) monitor->mode.h; + + float scaleX = (mon_float_width / (float) width); + float scaleY = (mon_float_height / (float) height); + RGFW_UNUSED(scaleY); + + float dpi = scaleX * 96.0f; + + monitor->pixelRatio = dpi >= 192.0f ? 2.0f : 1.0f; + + /* under xwayland the monitor changes w & h when compositor scales it */ + monitor->mode.w = width; + monitor->mode.h = height; + RGFW_UNUSED(zxdg_output_v1); +} + + +static void RGFW_wl_output_handle_done(void* data, struct wl_output* output) { + RGFW_UNUSED(output); + + RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon; + + if (monitor->physW <= 0 || monitor->physH <= 0) { + monitor->physW = (i32) ((float)monitor->mode.w / 96.0f); + monitor->physH = (i32) ((float)monitor->mode.h / 96.0f); + } + + if (((RGFW_monitorNode*)data)->disconnected == RGFW_FALSE) { + return; + } + + ((RGFW_monitorNode*)data)->disconnected = RGFW_TRUE; + + RGFW_monitorCallback(_RGFW->root, monitor, RGFW_TRUE); +} + +static void RGFW_wl_create_outputs(struct wl_registry *const registry, u32 id) { + struct wl_output *output = wl_registry_bind(registry, id, &wl_output_interface, wl_display_get_version(_RGFW->wl_display) < 4 ? 3 : 4); + RGFW_monitorNode* node; + RGFW_monitor mon; + + if (!output) return; + + char RGFW_mon_default_name[10]; + + RGFW_SNPRINTF(RGFW_mon_default_name, sizeof(RGFW_mon_default_name), "monitor-%li", _RGFW->monitors.count); + RGFW_STRNCPY(mon.name, RGFW_mon_default_name, sizeof(mon.name) - 1); + mon.name[sizeof(mon.name) - 1] = '\0'; + + /* set in case compositor does not send one */ + /* or no xdg_output support */ + mon.scaleY = mon.scaleX = mon.pixelRatio = 1.0f; + + node = RGFW_monitors_add(&mon); + if (node == NULL) return; + + node->modeCount = 0; + node->disconnected = RGFW_TRUE; + node->id = id; + node->output = output; + + static const struct wl_output_listener wl_output_listener = { + .geometry = RGFW_wl_output_set_geometry, + .mode = RGFW_wl_output_handle_mode, + .done = RGFW_wl_output_handle_done, + .scale = RGFW_wl_output_set_scale, + .name = RGFW_wl_output_set_name, + .description = (void (*)(void *, struct wl_output *, const char *))&RGFW_doNothing + }; + + /* the wl_output will have a reference to the node */ + wl_output_set_user_data(output, node); + + /* pass the monitor so we can access it in the callback functions */ + wl_output_add_listener(output, &wl_output_listener, node); + + if (!_RGFW->xdg_output_manager) + return; /* compositor does not support it */ + + static const struct zxdg_output_v1_listener xdg_output_listener = { + .name = (void (*)(void *,struct zxdg_output_v1 *, const char *))&RGFW_doNothing, + .done = (void (*)(void *,struct zxdg_output_v1 *))&RGFW_doNothing, + .description = (void (*)(void *,struct zxdg_output_v1 *, const char *))&RGFW_doNothing, + .logical_position = RGFW_xdg_output_logical_pos, + .logical_size = RGFW_xdg_output_logical_size + }; + + node->xdg_output = zxdg_output_manager_v1_get_xdg_output(_RGFW->xdg_output_manager, node->output); + zxdg_output_v1_add_listener(node->xdg_output, &xdg_output_listener, node); +} + +static void RGFW_wl_surface_enter(void *data, struct wl_surface *wl_surface, struct wl_output *output) { + RGFW_UNUSED(wl_surface); + + RGFW_window* win = (RGFW_window*)data; + RGFW_monitorNode* node = wl_output_get_user_data(output); + if (node == NULL) return; + + win->src.active_monitor = node; + + if (win->internal.flags & RGFW_windowScaleToMonitor) + RGFW_window_scaleToMonitor(win); +} + +static void RGFW_wl_data_source_send(void *data, struct wl_data_source *wl_data_source, const char *mime_type, i32 fd) { + RGFW_UNUSED(data); RGFW_UNUSED(wl_data_source); + + // a client can accept our clipboard + if (RGFW_STRNCMP(mime_type, "text/plain;charset=utf-8", 25) == 0) { + // do not write \0 + write(fd, _RGFW->clipboard, _RGFW->clipboard_len - 1); + } + + close(fd); +} + +static void RGFW_wl_data_source_cancelled(void *data, struct wl_data_source *wl_data_source) { + + RGFW_info* RGFW = (RGFW_info*)data; + + if (RGFW->kbOwner->src.data_source == wl_data_source) { + RGFW->kbOwner->src.data_source = NULL; + } + + wl_data_source_destroy(wl_data_source); + +} + +static void RGFW_wl_data_device_data_offer(void *data, struct wl_data_device *wl_data_device, struct wl_data_offer *wl_data_offer) { + + RGFW_UNUSED(data); RGFW_UNUSED(wl_data_device); + static const struct wl_data_offer_listener wl_data_offer_listener = { + .offer = (void (*)(void *data, struct wl_data_offer *wl_data_offer, const char *))RGFW_doNothing, + .source_actions = (void (*)(void *data, struct wl_data_offer *wl_data_offer, u32 dnd_action))RGFW_doNothing, + .action = (void (*)(void *data, struct wl_data_offer *wl_data_offer, u32 dnd_action))RGFW_doNothing + }; + wl_data_offer_add_listener(wl_data_offer, &wl_data_offer_listener, NULL); +} + +static void RGFW_wl_data_device_selection(void *data, struct wl_data_device *wl_data_device, struct wl_data_offer *wl_data_offer) { + RGFW_UNUSED(data); RGFW_UNUSED(wl_data_device); + /* Clipboard is empty */ + if (wl_data_offer == NULL) { + return; + } + + int pfds[2]; + pipe(pfds); + + wl_data_offer_receive(wl_data_offer, "text/plain;charset=utf-8", pfds[1]); + close(pfds[1]); + + wl_display_roundtrip(_RGFW->wl_display); + + char buf[1024]; + + ssize_t n = read(pfds[0], buf, sizeof(buf)); + + _RGFW->clipboard = (char*)RGFW_ALLOC((size_t)n); + RGFW_ASSERT(_RGFW->clipboard != NULL); + RGFW_STRNCPY(_RGFW->clipboard, buf, (size_t)n); + + _RGFW->clipboard_len = (size_t)n + 1; + + close(pfds[0]); + + wl_data_offer_destroy(wl_data_offer); + +} + +static void RGFW_wl_global_registry_handler(void* data, struct wl_registry *registry, u32 id, const char *interface, u32 version) { + + static struct wl_seat_listener seat_listener = {&RGFW_wl_seat_capabilities, (void (*)(void *, struct wl_seat *, const char *))&RGFW_doNothing}; + static const struct wl_shm_listener shm_listener = { .format = RGFW_wl_shm_format_handler }; + + RGFW_info* RGFW = (RGFW_info*)data; + RGFW_UNUSED(version); + + if (RGFW_STRNCMP(interface, "wl_compositor", 16) == 0) { + RGFW->compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 4); + } else if (RGFW_STRNCMP(interface, "xdg_wm_base", 12) == 0) { + RGFW->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) { + RGFW->decoration_manager = wl_registry_bind(registry, id, &zxdg_decoration_manager_v1_interface, 1); + } else if (RGFW_STRNCMP(interface, zwp_pointer_constraints_v1_interface.name, 255) == 0) { + RGFW->constraint_manager = wl_registry_bind(registry, id, &zwp_pointer_constraints_v1_interface, 1); + } else if (RGFW_STRNCMP(interface, zwp_relative_pointer_manager_v1_interface.name, 255) == 0) { + RGFW->relative_pointer_manager = wl_registry_bind(registry, id, &zwp_relative_pointer_manager_v1_interface, 1); + } else if (RGFW_STRNCMP(interface, xdg_toplevel_icon_manager_v1_interface.name, 255) == 0) { + RGFW->icon_manager = wl_registry_bind(registry, id, &xdg_toplevel_icon_manager_v1_interface, 1); + } else if (RGFW_STRNCMP(interface, "wl_shm", 7) == 0) { + RGFW->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); + wl_shm_add_listener(RGFW->shm, &shm_listener, RGFW); + } else if (RGFW_STRNCMP(interface,"wl_seat", 8) == 0) { + RGFW->seat = wl_registry_bind(registry, id, &wl_seat_interface, 1); + wl_seat_add_listener(RGFW->seat, &seat_listener, RGFW); + } else if (RGFW_STRNCMP(interface, zxdg_output_manager_v1_interface.name, 255) == 0) { + RGFW->xdg_output_manager = wl_registry_bind(registry, id, &zxdg_output_manager_v1_interface, 1); + } else if (RGFW_STRNCMP(interface,"wl_output", 10) == 0) { + RGFW_wl_create_outputs(registry, id); + } else if (RGFW_STRNCMP(interface, wp_pointer_warp_v1_interface.name, 255) == 0) { + RGFW->wp_pointer_warp = wl_registry_bind(registry, id, &wp_pointer_warp_v1_interface, 1); + } else if (RGFW_STRNCMP(interface,"wl_data_device_manager", 23) == 0) { + RGFW->data_device_manager = wl_registry_bind(registry, id, &wl_data_device_manager_interface, 1); + } +} + +static void RGFW_wl_global_registry_remove(void* data, struct wl_registry *registry, u32 id) { + RGFW_UNUSED(data); RGFW_UNUSED(registry); + RGFW_info* RGFW = (RGFW_info*)data; + RGFW_monitorNode* prev = RGFW->monitors.list.head; + RGFW_monitorNode* node = NULL; + if (prev == NULL) return; + + if (prev->id != id) { + /* find the first node that has a matching id */ + while(prev->next != NULL && prev->next->id != id) { + prev = prev->next; + } + + if (prev->next == NULL) return; + node = prev->next; + } else { + node = prev; + } + + if (node->output) { + wl_output_destroy(node->output); + } + + if (node->xdg_output) { + zxdg_output_v1_destroy(node->xdg_output); + } + + if (node->modeCount) { + RGFW_FREE(node->modes); + node->modeCount = 0; + } + + RGFW_monitorCallback(_RGFW->root, &node->mon, RGFW_FALSE); + RGFW_monitors_remove(node, prev); +} + +static void RGFW_wl_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; + } +} + +static int RGFW_wl_anonymous_shm_open(void) { + char name[] = "/RGFW-wayland-XXXXXX"; + int retries = 100; + + do { + RGFW_wl_randname(name + RGFW_unix_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; +} + +static int RGFW_wl_create_shm_file(off_t size) { + int fd = RGFW_wl_anonymous_shm_open(); + if (fd < 0) { + return fd; + } + + if (ftruncate(fd, size) < 0) { + close(fd); + return -1; + } + + return fd; +} + +i32 RGFW_initPlatform_Wayland(void) { + _RGFW->wl_display = wl_display_connect(NULL); + if (_RGFW->wl_display == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errWayland, "Failed to load Wayland display"); + return -1; + } + + _RGFW->compositor = NULL; + static const struct wl_registry_listener registry_listener = { + .global = RGFW_wl_global_registry_handler, + .global_remove = RGFW_wl_global_registry_remove, + }; + + _RGFW->registry = wl_display_get_registry(_RGFW->wl_display); + wl_registry_add_listener(_RGFW->registry, ®istry_listener, _RGFW); + + wl_display_roundtrip(_RGFW->wl_display); /* bind to globals */ + + if (_RGFW->compositor == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errWayland, "Can't find compositor."); + return 1; + } + + if (_RGFW->wl_cursor_theme == NULL) { + _RGFW->wl_cursor_theme = wl_cursor_theme_load(NULL, 24, _RGFW->shm); + _RGFW->cursor_surface = wl_compositor_create_surface(_RGFW->compositor); + } + + u8 RGFW_blk[] = { 0, 0, 0, 0 }; + _RGFW->hiddenMouse = RGFW_loadMouse(RGFW_blk, 1, 1, RGFW_formatRGBA8); + + static const struct xdg_wm_base_listener xdg_wm_base_listener = { + .ping = RGFW_wl_xdg_wm_base_ping_handler, + }; + + xdg_wm_base_add_listener(_RGFW->xdg_wm_base, &xdg_wm_base_listener, NULL); + + _RGFW->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + + static const struct wl_data_device_listener wl_data_device_listener = { + .data_offer = RGFW_wl_data_device_data_offer, + .enter = (void (*)(void *, struct wl_data_device *, u32, struct wl_surface*, wl_fixed_t, wl_fixed_t, struct wl_data_offer *))&RGFW_doNothing, + .leave = (void (*)(void *, struct wl_data_device *))&RGFW_doNothing, + .motion = (void (*)(void *, struct wl_data_device *, u32, wl_fixed_t, wl_fixed_t))&RGFW_doNothing, + .drop = (void (*)(void *, struct wl_data_device *))&RGFW_doNothing, + .selection = RGFW_wl_data_device_selection + }; + + if (_RGFW->seat && _RGFW->data_device_manager) { + _RGFW->data_device = wl_data_device_manager_get_data_device(_RGFW->data_device_manager, _RGFW->seat); + wl_data_device_add_listener(_RGFW->data_device, &wl_data_device_listener, NULL); + } + + return 0; +} + +void RGFW_deinitPlatform_Wayland(void) { + if (_RGFW->clipboard) { + RGFW_FREE(_RGFW->clipboard); + _RGFW->clipboard = NULL; + } + + if (_RGFW->wl_pointer) { + wl_pointer_destroy(_RGFW->wl_pointer); + } + if (_RGFW->wl_keyboard) { + wl_keyboard_destroy(_RGFW->wl_keyboard); + } + + wl_registry_destroy(_RGFW->registry); + if (_RGFW->decoration_manager != NULL) + zxdg_decoration_manager_v1_destroy(_RGFW->decoration_manager); + if (_RGFW->relative_pointer_manager != NULL) { + zwp_relative_pointer_manager_v1_destroy(_RGFW->relative_pointer_manager); + } + + if (_RGFW->relative_pointer) { + zwp_relative_pointer_v1_destroy(_RGFW->relative_pointer); + } + + if (_RGFW->constraint_manager != NULL) { + zwp_pointer_constraints_v1_destroy(_RGFW->constraint_manager); + } + + if (_RGFW->xdg_output_manager != NULL) + if (_RGFW->icon_manager != NULL) { + xdg_toplevel_icon_manager_v1_destroy(_RGFW->icon_manager); + } + + if (_RGFW->xdg_output_manager) { + zxdg_output_manager_v1_destroy(_RGFW->xdg_output_manager); + } + + if (_RGFW->data_device_manager) { + wl_data_device_manager_destroy(_RGFW->data_device_manager); + } + + if (_RGFW->data_device) { + wl_data_device_destroy(_RGFW->data_device); + } + + if (_RGFW->wl_cursor_theme != NULL) { + wl_cursor_theme_destroy(_RGFW->wl_cursor_theme); + } + + if (_RGFW->wp_pointer_warp != NULL) { + wp_pointer_warp_v1_destroy(_RGFW->wp_pointer_warp); + } + + RGFW_freeMouse(_RGFW->hiddenMouse); + + RGFW_monitorNode* node = _RGFW->monitors.list.head; + + while (node != NULL) { + if (node->output) { + wl_output_destroy(node->output); + } + + if (node->xdg_output) { + zxdg_output_v1_destroy(node->xdg_output); + } + + _RGFW->monitors.count -= 1; + node = node->next; + + } + + wl_surface_destroy(_RGFW->cursor_surface); + wl_shm_destroy(_RGFW->shm); + wl_seat_release(_RGFW->seat); + xdg_wm_base_destroy(_RGFW->xdg_wm_base); + wl_compositor_destroy(_RGFW->compositor); + wl_display_disconnect(_RGFW->wl_display); +} + +RGFW_format RGFW_FUNC(RGFW_nativeFormat)(void) { return RGFW_formatBGRA8; } + +RGFW_bool RGFW_FUNC(RGFW_createSurfacePtr) (u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) { + RGFW_ASSERT(surface != NULL); + surface->data = data; + surface->w = w; + surface->h = h; + surface->format = format; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoBuffer, "Creating a 4 channel buffer"); + + u32 size = (u32)(surface->w * surface->h * 4); + int fd = RGFW_wl_create_shm_file(size); + if (fd < 0) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, "Failed to create a buffer."); + return RGFW_FALSE; + } + + surface->native.pool = wl_shm_create_pool(_RGFW->shm, fd, (i32)size); + + surface->native.buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (surface->native.buffer == MAP_FAILED) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, "mmap failed."); + return RGFW_FALSE; + } + + surface->native.fd = fd; + surface->native.format = RGFW_formatBGRA8; + return RGFW_TRUE; +} + +void RGFW_FUNC(RGFW_window_blitSurface) (RGFW_window* win, RGFW_surface* surface) { + RGFW_ASSERT(surface != NULL); + + surface->native.wl_buffer = wl_shm_pool_create_buffer(surface->native.pool, 0, RGFW_MIN(win->w, surface->w), RGFW_MIN(win->h, surface->h), (i32)surface->w * 4, WL_SHM_FORMAT_ARGB8888); + RGFW_copyImageData(surface->native.buffer, surface->w, RGFW_MIN(win->h, surface->h), surface->native.format, surface->data, surface->format, surface->convertFunc); + + wl_surface_attach(win->src.surface, surface->native.wl_buffer, 0, 0); + wl_surface_damage(win->src.surface, 0, 0, RGFW_MIN(win->w, surface->w), RGFW_MIN(win->h, surface->h)); + wl_surface_commit(win->src.surface); + + wl_buffer_destroy(surface->native.wl_buffer); +} + +void RGFW_FUNC(RGFW_surface_freePtr) (RGFW_surface* surface) { + RGFW_ASSERT(surface != NULL); + + wl_shm_pool_destroy(surface->native.pool); + close(surface->native.fd); + + munmap(surface->native.buffer, (size_t)(surface->w * surface->h * 4)); +} + +void RGFW_FUNC(RGFW_window_setBorder) (RGFW_window* win, RGFW_bool border) { + RGFW_setBit(&win->internal.flags, RGFW_windowNoBorder, !border); + + /* for now just toggle between SSD & CSD depending on the bool */ + if (_RGFW->decoration_manager != NULL) { + zxdg_toplevel_decoration_v1_set_mode(win->src.decoration, (border ? ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE : ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE)); + } +} + +void RGFW_FUNC(RGFW_window_setRawMouseModePlatform) (RGFW_window* win, RGFW_bool state) { + RGFW_ASSERT(win); + if (_RGFW->relative_pointer_manager == NULL) return; + + if (state == RGFW_FALSE) { + if (_RGFW->relative_pointer != NULL) + zwp_relative_pointer_v1_destroy(_RGFW->relative_pointer); + _RGFW->relative_pointer = NULL; + return; + } + + if (_RGFW->relative_pointer != NULL) return; + + _RGFW->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(_RGFW->relative_pointer_manager, _RGFW->wl_pointer); + + static const struct zwp_relative_pointer_v1_listener relative_motion_listener = { + .relative_motion = RGFW_wl_relative_pointer_motion + }; + + zwp_relative_pointer_v1_add_listener(_RGFW->relative_pointer, &relative_motion_listener, _RGFW); +} + +void RGFW_FUNC(RGFW_window_captureMousePlatform) (RGFW_window* win, RGFW_bool state) { + RGFW_ASSERT(win); + + /* compositor has no support or window already is locked do nothing */ + if (_RGFW->constraint_manager == NULL) return; + + if (state == RGFW_FALSE) { + if (win->src.locked_pointer != NULL) + zwp_locked_pointer_v1_destroy(win->src.locked_pointer); + win->src.locked_pointer = NULL; + return; + } + + + if (win->src.locked_pointer != NULL) return; + win->src.locked_pointer = zwp_pointer_constraints_v1_lock_pointer(_RGFW->constraint_manager, win->src.surface, _RGFW->wl_pointer, NULL, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); + + static const struct zwp_locked_pointer_v1_listener locked_listener = { + .locked = RGFW_wl_pointer_locked, + .unlocked = (void (*)(void *, struct zwp_locked_pointer_v1 *))RGFW_doNothing + }; + + zwp_locked_pointer_v1_add_listener(win->src.locked_pointer, &locked_listener, _RGFW); +} + +RGFW_window* RGFW_FUNC(RGFW_createWindowPlatform) (const char* name, RGFW_windowFlags flags, RGFW_window* win) { + RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningWayland, "RGFW Wayland support is experimental"); + + static const struct xdg_surface_listener xdg_surface_listener = { + .configure = RGFW_wl_xdg_surface_configure_handler, + }; + + static const struct wl_surface_listener wl_surface_listener = { + .enter = RGFW_wl_surface_enter, + .leave = (void (*)(void *, struct wl_surface *, struct wl_output *))&RGFW_doNothing, + .preferred_buffer_scale = (void (*)(void *, struct wl_surface *, i32))&RGFW_doNothing, + .preferred_buffer_transform = (void (*)(void *, struct wl_surface *, u32))&RGFW_doNothing + }; + + win->src.surface = wl_compositor_create_surface(_RGFW->compositor); + wl_surface_add_listener(win->src.surface, &wl_surface_listener, win); + + /* create a surface for a custom cursor */ + win->src.custom_cursor_surface = wl_compositor_create_surface(_RGFW->compositor); + + win->src.xdg_surface = xdg_wm_base_get_xdg_surface(_RGFW->xdg_wm_base, win->src.surface); + xdg_surface_add_listener(win->src.xdg_surface, &xdg_surface_listener, win); + + xdg_wm_base_set_user_data(_RGFW->xdg_wm_base, win); + + win->src.xdg_toplevel = xdg_surface_get_toplevel(win->src.xdg_surface); + + if (_RGFW->className == NULL) + _RGFW->className = (char*)name; + + xdg_toplevel_set_app_id(win->src.xdg_toplevel, name); + + xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->w, win->h); + + if (!(win->internal.flags & RGFW_windowTransparent)) { /* no transparency */ + RGFW_wl_setOpaque(win); + } + + static const struct xdg_toplevel_listener xdg_toplevel_listener = { + .configure = RGFW_wl_xdg_toplevel_configure_handler, + .close = RGFW_wl_xdg_toplevel_close_handler, + }; + + xdg_toplevel_add_listener(win->src.xdg_toplevel, &xdg_toplevel_listener, win); + + /* compositor supports both SSD & CSD + So choose accordingly + */ + if (_RGFW->decoration_manager) { + u32 decoration_mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE; + win->src.decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( + _RGFW->decoration_manager, win->src.xdg_toplevel); + + static const struct zxdg_toplevel_decoration_v1_listener xdg_decoration_listener = { + .configure = RGFW_wl_xdg_decoration_configure_handler + }; + + zxdg_toplevel_decoration_v1_add_listener(win->src.decoration, &xdg_decoration_listener, win); + + /* we want no decorations */ + if ((flags & RGFW_windowNoBorder)) { + decoration_mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; + } + + zxdg_toplevel_decoration_v1_set_mode(win->src.decoration, decoration_mode); + + /* no xdg_decoration support */ + } else if (!(flags & RGFW_windowNoBorder)) { + /* TODO, some fallback */ + #ifdef RGFW_LIBDECOR + static struct libdecor_interface interface = { + .error = NULL, + }; + + static struct libdecor_frame_interface frameInterface = {0}; /*= { + RGFW_wl_handle_configure, + RGFW_wl_handle_close, + RGFW_wl_handle_commit, + RGFW_wl_handle_dismiss_popup, + };*/ + + win->src.decorContext = libdecor_new(_RGFW->wl_display, &interface); + if (win->src.decorContext) { + struct libdecor_frame *frame = libdecor_decorate(win->src.decorContext, win->src.surface, &frameInterface, win); + if (!frame) { + libdecor_unref(win->src.decorContext); + win->src.decorContext = NULL; + } else { + libdecor_frame_set_app_id(frame, "my-libdecor-app"); + libdecor_frame_set_title(frame, "My Libdecor Window"); + } + } + #endif + } + + if (_RGFW->icon_manager != NULL) { + /* set the default wayland icon */ + xdg_toplevel_icon_manager_v1_set_icon(_RGFW->icon_manager, win->src.xdg_toplevel, NULL); + } + + wl_surface_commit(win->src.surface); + + while (win->src.configured == RGFW_FALSE) { + wl_display_dispatch(_RGFW->wl_display); + } + + RGFW_UNUSED(name); + + return win; +} + +RGFW_bool RGFW_FUNC(RGFW_getGlobalMouse) (i32* x, i32* y) { + RGFW_init(); + if (x) *x = 0; + if (y) *y = 0; + return RGFW_FALSE; +} + +RGFW_key RGFW_FUNC(RGFW_physicalToMappedKey)(RGFW_key key) { + u32 keycode = RGFW_rgfwToApiKey(key); + xkb_keycode_t kc = keycode + 8; + xkb_keysym_t sym = xkb_state_key_get_one_sym(_RGFW->xkb_state, kc); + if (sym < 256) { + return (RGFW_key)sym; + } + + switch (sym) { + case XKB_KEY_F1: return RGFW_F1; + case XKB_KEY_F2: return RGFW_F2; + case XKB_KEY_F3: return RGFW_F3; + case XKB_KEY_F4: return RGFW_F4; + case XKB_KEY_F5: return RGFW_F5; + case XKB_KEY_F6: return RGFW_F6; + case XKB_KEY_F7: return RGFW_F7; + case XKB_KEY_F8: return RGFW_F8; + case XKB_KEY_F9: return RGFW_F9; + case XKB_KEY_F10: return RGFW_F10; + case XKB_KEY_F11: return RGFW_F11; + case XKB_KEY_F12: return RGFW_F12; + case XKB_KEY_F13: return RGFW_F13; + case XKB_KEY_F14: return RGFW_F14; + case XKB_KEY_F15: return RGFW_F15; + case XKB_KEY_F16: return RGFW_F16; + case XKB_KEY_F17: return RGFW_F17; + case XKB_KEY_F18: return RGFW_F18; + case XKB_KEY_F19: return RGFW_F19; + case XKB_KEY_F20: return RGFW_F20; + case XKB_KEY_F21: return RGFW_F21; + case XKB_KEY_F22: return RGFW_F22; + case XKB_KEY_F23: return RGFW_F23; + case XKB_KEY_F24: return RGFW_F24; + case XKB_KEY_F25: return RGFW_F25; + case XKB_KEY_Shift_L: return RGFW_shiftL; + case XKB_KEY_Shift_R: return RGFW_shiftR; + case XKB_KEY_Control_L: return RGFW_controlL; + case XKB_KEY_Control_R: return RGFW_controlR; + case XKB_KEY_Alt_L: return RGFW_altL; + case XKB_KEY_Alt_R: return RGFW_altR; + case XKB_KEY_Super_L: return RGFW_superL; + case XKB_KEY_Super_R: return RGFW_superR; + case XKB_KEY_Caps_Lock: return RGFW_capsLock; + case XKB_KEY_Num_Lock: return RGFW_numLock; + case XKB_KEY_Scroll_Lock:return RGFW_scrollLock; + case XKB_KEY_Up: return RGFW_up; + case XKB_KEY_Down: return RGFW_down; + case XKB_KEY_Left: return RGFW_left; + case XKB_KEY_Right: return RGFW_right; + case XKB_KEY_Home: return RGFW_home; + case XKB_KEY_End: return RGFW_end; + case XKB_KEY_Page_Up: return RGFW_pageUp; + case XKB_KEY_Page_Down: return RGFW_pageDown; + case XKB_KEY_Insert: return RGFW_insert; + case XKB_KEY_Menu: return RGFW_menu; + case XKB_KEY_KP_Add: return RGFW_kpPlus; + case XKB_KEY_KP_Subtract: return RGFW_kpMinus; + case XKB_KEY_KP_Multiply: return RGFW_kpMultiply; + case XKB_KEY_KP_Divide: return RGFW_kpSlash; + case XKB_KEY_KP_Equal: return RGFW_kpEqual; + case XKB_KEY_KP_Enter: return RGFW_kpReturn; + case XKB_KEY_KP_Decimal: return RGFW_kpPeriod; + case XKB_KEY_KP_0: return RGFW_kp0; + case XKB_KEY_KP_1: return RGFW_kp1; + case XKB_KEY_KP_2: return RGFW_kp2; + case XKB_KEY_KP_3: return RGFW_kp3; + case XKB_KEY_KP_4: return RGFW_kp4; + case XKB_KEY_KP_5: return RGFW_kp5; + case XKB_KEY_KP_6: return RGFW_kp6; + case XKB_KEY_KP_7: return RGFW_kp7; + case XKB_KEY_KP_8: return RGFW_kp8; + case XKB_KEY_KP_9: return RGFW_kp9; + case XKB_KEY_Print: return RGFW_printScreen; + case XKB_KEY_Pause: return RGFW_pause; + default: break; + } + + return RGFW_keyNULL; +} + +void RGFW_FUNC(RGFW_pollEvents) (void) { + RGFW_resetPrevState(); + + /* send buffered requests to compositor */ + while (wl_display_flush(_RGFW->wl_display) == -1) { + /* compositor not responding to new requests */ + /* so let's dispatch some events so the compositor responds */ + if (errno == EAGAIN) { + if (wl_display_dispatch_pending(_RGFW->wl_display) == -1) { + return; + } + } else { + return; + } + } + + /* read the events; if empty this reads from the */ + /* wayland file descriptor */ + if (wl_display_dispatch(_RGFW->wl_display) == -1) { + return; + } + +} + +void RGFW_FUNC(RGFW_window_move) (RGFW_window* win, i32 x, i32 y) { + RGFW_ASSERT(win != NULL); + win->x = x; + win->y = y; +} + + +void RGFW_FUNC(RGFW_window_resize) (RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + win->w = w; + win->h = h; + if (_RGFW->compositor) { + xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->w, win->h); + #ifdef RGFW_OPENGL + if (win->src.ctx.egl) + wl_egl_window_resize(win->src.ctx.egl->eglWindow, (i32)w, (i32)h, 0, 0); + #endif + } +} + +void RGFW_FUNC(RGFW_window_setAspectRatio) (RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + + if (w == 0 && h == 0) + return; + xdg_toplevel_set_max_size(win->src.xdg_toplevel, (i32)w, (i32)h); +} + +void RGFW_FUNC(RGFW_window_setMinSize) (RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + xdg_toplevel_set_min_size(win->src.xdg_toplevel, w, h); +} + +void RGFW_FUNC(RGFW_window_setMaxSize) (RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + xdg_toplevel_set_max_size(win->src.xdg_toplevel, w, h); +} + +void RGFW_toggleWaylandMaximized(RGFW_window* win, RGFW_bool maximized) { + win->src.maximized = maximized; + if (maximized) { + xdg_toplevel_set_maximized(win->src.xdg_toplevel); + } else { + xdg_toplevel_unset_maximized(win->src.xdg_toplevel); + } +} + +void RGFW_FUNC(RGFW_window_maximize) (RGFW_window* win) { + win->internal.oldX = win->x; + win->internal.oldY = win->y; + win->internal.oldW = win->w; + win->internal.oldH = win->h; + RGFW_toggleWaylandMaximized(win, 1); + return; +} + +void RGFW_FUNC(RGFW_window_focus)(RGFW_window* win) { + RGFW_ASSERT(win); +} + +void RGFW_FUNC(RGFW_window_raise)(RGFW_window* win) { + RGFW_ASSERT(win); +} + +void RGFW_FUNC(RGFW_window_setFullscreen)(RGFW_window* win, RGFW_bool fullscreen) { + RGFW_ASSERT(win != NULL); + if (fullscreen) { + + win->internal.flags |= RGFW_windowFullscreen; + win->internal.oldX = win->x; + win->internal.oldY = win->y; + win->internal.oldW = win->w; + win->internal.oldH = win->h; + xdg_toplevel_set_fullscreen(win->src.xdg_toplevel, NULL); /* let the compositor decide */ + } else { + win->internal.flags &= ~(u32)RGFW_windowFullscreen; + xdg_toplevel_unset_fullscreen(win->src.xdg_toplevel); + } + +} + +void RGFW_FUNC(RGFW_window_setFloating) (RGFW_window* win, RGFW_bool floating) { + RGFW_ASSERT(win != NULL); + RGFW_UNUSED(floating); +} + +void RGFW_FUNC(RGFW_window_setOpacity) (RGFW_window* win, u8 opacity) { + RGFW_ASSERT(win != NULL); + RGFW_UNUSED(opacity); +} + +void RGFW_FUNC(RGFW_window_minimize)(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + if (RGFW_window_isMaximized(win)) return; + win->internal.oldX = win->x; + win->internal.oldY = win->y; + win->internal.oldW = win->w; + win->internal.oldH = win->h; + win->src.minimized = RGFW_TRUE; + xdg_toplevel_set_minimized(win->src.xdg_toplevel); +} + +void RGFW_FUNC(RGFW_window_restore)(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_toggleWaylandMaximized(win, RGFW_FALSE); + + RGFW_window_move(win, win->internal.oldX, win->internal.oldY); + RGFW_window_resize(win, win->internal.oldW, win->internal.oldH); + + RGFW_window_show(win); + RGFW_window_move(win, win->internal.oldX, win->internal.oldY); + RGFW_window_resize(win, win->internal.oldW, win->internal.oldH); + + RGFW_window_show(win); +} + +RGFW_bool RGFW_FUNC(RGFW_window_isFloating)(RGFW_window* win) { + return (!RGFW_window_isFullscreen(win) && !RGFW_window_isMaximized(win)); +} + +void RGFW_FUNC(RGFW_window_setName) (RGFW_window* win, const char* name) { + RGFW_ASSERT(win != NULL); + if (name == NULL) name = "\0"; + + if (_RGFW->compositor) + xdg_toplevel_set_title(win->src.xdg_toplevel, name); +} + +#ifndef RGFW_NO_PASSTHROUGH +void RGFW_FUNC(RGFW_window_setMousePassthrough) (RGFW_window* win, RGFW_bool passthrough) { + RGFW_ASSERT(win != NULL); + RGFW_UNUSED(passthrough); +} +#endif /* RGFW_NO_PASSTHROUGH */ + +RGFW_bool RGFW_FUNC(RGFW_window_setIconEx) (RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_icon type) { + RGFW_ASSERT(win != NULL); + RGFW_UNUSED(type); + + if (_RGFW->icon_manager == NULL || w != h) return RGFW_FALSE; + + if (win->src.icon) { + xdg_toplevel_icon_v1_destroy(win->src.icon); + win->src.icon= NULL; + } + + RGFW_surface* surface = RGFW_createSurface(data, w, h, format); + + if (surface == NULL) return RGFW_FALSE; + + RGFW_copyImageData(surface->native.buffer, RGFW_MIN(w, surface->w), RGFW_MIN(h, surface->h), surface->native.format, surface->data, surface->format, NULL); + + win->src.icon = xdg_toplevel_icon_manager_v1_create_icon(_RGFW->icon_manager); + xdg_toplevel_icon_v1_add_buffer(win->src.icon, surface->native.wl_buffer, 1); + xdg_toplevel_icon_manager_v1_set_icon(_RGFW->icon_manager, win->src.xdg_toplevel, win->src.icon); + + RGFW_surface_free(surface); + return RGFW_TRUE; +} + +RGFW_mouse* RGFW_FUNC(RGFW_loadMouse)(u8* data, i32 w, i32 h, RGFW_format format) { + + RGFW_surface *mouse_surface = RGFW_createSurface(data, w, h, format); + + if (mouse_surface == NULL) return NULL; + + RGFW_copyImageData(mouse_surface->native.buffer, RGFW_MIN(w, mouse_surface->w), RGFW_MIN(h, mouse_surface->h), mouse_surface->native.format, mouse_surface->data, mouse_surface->format, NULL); + + return (void*) mouse_surface; +} + +void RGFW_FUNC(RGFW_window_setMouse)(RGFW_window* win, RGFW_mouse* mouse) { + RGFW_ASSERT(win); RGFW_ASSERT(mouse); + RGFW_surface *mouse_surface = (RGFW_surface*)mouse; + + win->src.using_custom_cursor = RGFW_TRUE; + + struct wl_buffer *mouse_buffer = mouse_surface->native.wl_buffer; + + wl_surface_attach(win->src.custom_cursor_surface, mouse_buffer, 0, 0); + wl_surface_damage(win->src.custom_cursor_surface, 0, 0, mouse_surface->w, mouse_surface->h); + wl_surface_commit(win->src.custom_cursor_surface); + +} + +void RGFW_FUNC(RGFW_freeMouse)(RGFW_mouse* mouse) { + if (mouse != NULL) { + RGFW_surface_free((RGFW_surface*)mouse); + } +} + +void RGFW_FUNC(RGFW_window_moveMouse)(RGFW_window* win, i32 x, i32 y) { + if (_RGFW->wp_pointer_warp != NULL) { + wp_pointer_warp_v1_warp_pointer(_RGFW->wp_pointer_warp, win->src.surface, _RGFW->wl_pointer, wl_fixed_from_int(x), wl_fixed_from_int(y), _RGFW->mouse_enter_serial); + } +} + +RGFW_bool RGFW_FUNC(RGFW_window_setMouseDefault)(RGFW_window* win) { + return RGFW_window_setMouseStandard(win, RGFW_mouseArrow); +} + +RGFW_bool RGFW_FUNC(RGFW_window_setMouseStandard)(RGFW_window* win, u8 mouse) { + RGFW_ASSERT(win != NULL); + + char* cursorName = NULL; + switch (mouse) { + case RGFW_mouseNormal: cursorName = (char*)"left_ptr"; break; + case RGFW_mouseArrow: cursorName = (char*)"left_ptr"; break; + case RGFW_mouseIbeam: cursorName = (char*)"xterm"; break; + case RGFW_mouseCrosshair: cursorName = (char*)"crosshair"; break; + case RGFW_mousePointingHand: cursorName = (char*)"hand2"; break; + case RGFW_mouseResizeEW: cursorName = (char*)"sb_h_double_arrow"; break; + case RGFW_mouseResizeNS: cursorName = (char*)"sb_v_double_arrow"; break; + case RGFW_mouseResizeNWSE: cursorName = (char*)"top_left_corner"; break; /* or fd_double_arrow */ + case RGFW_mouseResizeNESW: cursorName = (char*)"top_right_corner"; break; /* or bd_double_arrow */ + case RGFW_mouseResizeNW: cursorName = (char*)"top_left_corner"; break; + case RGFW_mouseResizeN: cursorName = (char*)"top_side"; break; + case RGFW_mouseResizeNE: cursorName = (char*)"top_right_corner"; break; + case RGFW_mouseResizeE: cursorName = (char*)"right_side"; break; + case RGFW_mouseResizeSE: cursorName = (char*)"bottom_right_corner"; break; + case RGFW_mouseResizeS: cursorName = (char*)"bottom_side"; break; + case RGFW_mouseResizeSW: cursorName = (char*)"bottom_left_corner"; break; + case RGFW_mouseResizeW: cursorName = (char*)"left_side"; break; + case RGFW_mouseResizeAll: cursorName = (char*)"fleur"; break; + case RGFW_mouseNotAllowed: cursorName = (char*)"not-allowed"; break; + case RGFW_mouseWait: cursorName = (char*)"watch"; break; + case RGFW_mouseProgress: cursorName = (char*)"watch"; break; + default: return RGFW_FALSE; + } + + win->src.using_custom_cursor = RGFW_FALSE; + + struct wl_cursor* wlcursor = wl_cursor_theme_get_cursor(_RGFW->wl_cursor_theme, cursorName); + if (wlcursor == NULL) + return RGFW_FALSE; + struct wl_cursor_image* cursor_image = wlcursor->images[0]; + struct wl_buffer* cursor_buffer = wl_cursor_image_get_buffer(cursor_image); + wl_pointer_set_cursor(_RGFW->wl_pointer, _RGFW->mouse_enter_serial, _RGFW->cursor_surface, (i32)cursor_image->hotspot_x, (i32)cursor_image->hotspot_y); + wl_surface_attach(_RGFW->cursor_surface, cursor_buffer, 0, 0); + wl_surface_damage(_RGFW->cursor_surface, 0, 0, (i32)cursor_image->width, (i32)cursor_image->height); + wl_surface_commit(_RGFW->cursor_surface); + return RGFW_TRUE; +} + +void RGFW_FUNC(RGFW_window_hide) (RGFW_window* win) { + wl_surface_attach(win->src.surface, NULL, 0, 0); + wl_surface_commit(win->src.surface); + win->internal.flags |= RGFW_windowHide; +} + +void RGFW_FUNC(RGFW_window_show) (RGFW_window* win) { + win->internal.flags &= ~(u32)RGFW_windowHide; + if (win->internal.flags & RGFW_windowFocusOnShow) RGFW_window_focus(win); + /* wl_surface_attach(win->src.surface, win->x, win->y, win->w, win->h, 0, 0); */ + wl_surface_commit(win->src.surface); +} + +void RGFW_FUNC(RGFW_window_flash) (RGFW_window* win, RGFW_flashRequest request) { + if (RGFW_window_isInFocus(win) && request) { + return; + } +} + +RGFW_ssize_t RGFW_FUNC(RGFW_readClipboardPtr) (char* str, size_t strCapacity) { + RGFW_UNUSED(strCapacity); + + 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; +} + +void RGFW_FUNC(RGFW_writeClipboard) (const char* text, u32 textLen) { + + // compositor does not support wl_data_device_manager + // clients cannot read rgfw's clipboard + if (_RGFW->data_device_manager == NULL) return; + // clear the clipboard + if (_RGFW->clipboard) + RGFW_FREE(_RGFW->clipboard); + + // set the contents + _RGFW->clipboard = (char*)RGFW_ALLOC(textLen); + RGFW_ASSERT(_RGFW->clipboard != NULL); + RGFW_STRNCPY(_RGFW->clipboard, text, textLen - 1); + _RGFW->clipboard[textLen - 1] = '\0'; + _RGFW->clipboard_len = textLen; + + // means we already wrote to the clipboard + // so destroy it to create a new one + RGFW_window* win = _RGFW->kbOwner; + + if (win->src.data_source != NULL) { + wl_data_source_destroy(win->src.data_source); + win->src.data_source = NULL; + } + + // advertise to other clients that we offer text + win->src.data_source = wl_data_device_manager_create_data_source(_RGFW->data_device_manager); + + // basic error checking + if (win->src.data_source == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errClipboard, "Could not create clipboard data source"); + return; + } + wl_data_source_offer(win->src.data_source , "text/plain;charset=utf-8"); + + // needed RGFW_doNothing because wayland will call the functions + // if not set they are random data that lead to a crash + static const struct wl_data_source_listener data_source_listener = { + .target = (void (*)(void *, struct wl_data_source *, const char *))&RGFW_doNothing, + .action = (void (*)(void *, struct wl_data_source *, u32))&RGFW_doNothing, + .dnd_drop_performed = (void (*)(void *, struct wl_data_source *))&RGFW_doNothing, + .dnd_finished = (void (*)(void *, struct wl_data_source *))&RGFW_doNothing, + .send = RGFW_wl_data_source_send, + .cancelled = RGFW_wl_data_source_cancelled + }; + + wl_data_source_add_listener(win->src.data_source, &data_source_listener, _RGFW); + +} + +RGFW_bool RGFW_FUNC(RGFW_window_isHidden) (RGFW_window* win) { + RGFW_ASSERT(win != NULL); + return RGFW_FALSE; +} + +RGFW_bool RGFW_FUNC(RGFW_window_isMinimized) (RGFW_window* win) { + RGFW_ASSERT(win != NULL); + return win->src.minimized; +} + +RGFW_bool RGFW_FUNC(RGFW_window_isMaximized) (RGFW_window* win) { + RGFW_ASSERT(win != NULL); + return win->src.maximized; +} + +void RGFW_FUNC(RGFW_pollMonitors) (void) { + _RGFW->monitors.primary = _RGFW->monitors.list.head; +} + + +RGFW_bool RGFW_FUNC(RGFW_monitor_getWorkarea) (RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height) { + /* NOTE: Wayland has no way to get the actual workarea as far as I'm aware :( */ + if (x) *x = monitor->x; + if (y) *y = monitor->y; + if (width) *width = monitor->mode.w; + if (height) *height = monitor->mode.h; + return RGFW_TRUE; +} + +size_t RGFW_FUNC(RGFW_monitor_getModesPtr) (RGFW_monitor* monitor, RGFW_monitorMode** modes) { + if (modes) { + RGFW_MEMCPY((*modes), monitor->node->modes, monitor->node->modeCount * sizeof(RGFW_monitorMode)); + } + + return monitor->node->modeCount; +} + +size_t RGFW_FUNC(RGFW_monitor_getGammaRampPtr) (RGFW_monitor* monitor, RGFW_gammaRamp* ramp) { + RGFW_UNUSED(monitor); RGFW_UNUSED(ramp); + return 0; +} + +RGFW_bool RGFW_FUNC(RGFW_monitor_setGammaRamp) (RGFW_monitor* monitor, RGFW_gammaRamp* ramp) { + RGFW_UNUSED(monitor); RGFW_UNUSED(ramp); + return RGFW_FALSE; +} + +RGFW_bool RGFW_FUNC(RGFW_monitor_requestMode) (RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request) { + for (size_t i = 0; i < mon->node->modeCount; i++) { + if (RGFW_monitorModeCompare(mode, &mon->node->modes[i], request) == RGFW_FALSE) { + continue; + } + + RGFW_monitor_setMode(mon, &mon->node->modes[i]); + return RGFW_TRUE; + } + + return RGFW_FALSE; +} + +RGFW_bool RGFW_FUNC(RGFW_monitor_setMode) (RGFW_monitor* mon, RGFW_monitorMode* mode) { + RGFW_UNUSED(mon); RGFW_UNUSED(mode); + return RGFW_FALSE; +} + +RGFW_monitor* RGFW_FUNC(RGFW_window_getMonitor) (RGFW_window* win) { + RGFW_ASSERT(win); + if (win->src.active_monitor == NULL) { + /* TODO: fix race condition [probably a problem with wayland] */ + return RGFW_getPrimaryMonitor(); + } + + return &win->src.active_monitor->mon; +} + +#ifdef RGFW_OPENGL +RGFW_bool RGFW_FUNC(RGFW_extensionSupportedPlatform_OpenGL) (const char * extension, size_t len) { return RGFW_extensionSupportedPlatform_EGL(extension, len); } +RGFW_proc RGFW_FUNC(RGFW_getProcAddress_OpenGL) (const char* procname) { return RGFW_getProcAddress_EGL(procname); } + + +RGFW_bool RGFW_FUNC(RGFW_window_createContextPtr_OpenGL)(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) { + RGFW_bool out = RGFW_window_createContextPtr_EGL(win, &ctx->egl, hints); + win->src.gfxType = RGFW_gfxNativeOpenGL; + + RGFW_window_swapInterval_OpenGL(win, 0); + return out; +} +void RGFW_FUNC(RGFW_window_deleteContextPtr_OpenGL) (RGFW_window* win, RGFW_glContext* ctx) { RGFW_window_deleteContextPtr_EGL(win, &ctx->egl); win->src.ctx.native = NULL; } + +void RGFW_FUNC(RGFW_window_makeCurrentContext_OpenGL) (RGFW_window* win) { RGFW_window_makeCurrentContext_EGL(win); } +void* RGFW_FUNC(RGFW_getCurrentContext_OpenGL) (void) { return RGFW_getCurrentContext_EGL(); } +void RGFW_FUNC(RGFW_window_swapBuffers_OpenGL) (RGFW_window* win) { RGFW_window_swapBuffers_EGL(win); } +void RGFW_FUNC(RGFW_window_swapInterval_OpenGL) (RGFW_window* win, i32 swapInterval) { RGFW_window_swapInterval_EGL(win, swapInterval); } +#endif /* RGFW_OPENGL */ + +void RGFW_FUNC(RGFW_window_closePlatform)(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, "a window was freed"); + #ifdef RGFW_LIBDECOR + if (win->src.decorContext) + libdecor_unref(win->src.decorContext); + #endif + + if (win->src.decoration) { + zxdg_toplevel_decoration_v1_destroy(win->src.decoration); + } + + if (win->src.xdg_toplevel) { + xdg_toplevel_destroy(win->src.xdg_toplevel); + } + + wl_surface_destroy(win->src.custom_cursor_surface); + + if (win->src.locked_pointer) { + zwp_locked_pointer_v1_destroy(win->src.locked_pointer); + } + + if (win->src.icon) { + xdg_toplevel_icon_v1_destroy(win->src.icon); + } + + xdg_surface_destroy(win->src.xdg_surface); + wl_surface_destroy(win->src.surface); +} + +#ifdef RGFW_WEBGPU +WGPUSurface RGFW_FUNC(RGFW_window_createSurface_WebGPU) (RGFW_window* window, WGPUInstance instance) { + WGPUSurfaceDescriptor surfaceDesc = {0}; + WGPUSurfaceSourceWaylandSurface fromWl = {0}; + fromWl.chain.sType = WGPUSType_SurfaceSourceWaylandSurface; + fromWl.display = _RGFW->wl_display; + fromWl.surface = window->src.surface; + + surfaceDesc.nextInChain = (WGPUChainedStruct*)&fromWl.chain; + return wgpuInstanceCreateSurface(instance, &surfaceDesc); +} +#endif + + + +#endif /* RGFW_WAYLAND */ +/* + End of Wayland defines +*/ + +/* + + Start of Windows defines + + +*/ + +#ifdef RGFW_WINDOWS +#ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN +#endif + +#ifndef OEMRESOURCE + #define OEMRESOURCE +#endif + +#include + +#ifndef OCR_NORMAL +#define OCR_NORMAL 32512 +#define OCR_IBEAM 32513 +#define OCR_WAIT 32514 +#define OCR_CROSS 32515 +#define OCR_UP 32516 +#define OCR_SIZENWSE 32642 +#define OCR_SIZENESW 32643 +#define OCR_SIZEWE 32644 +#define OCR_SIZENS 32645 +#define OCR_SIZEALL 32646 +#define OCR_NO 32648 +#define OCR_HAND 32649 +#define OCR_APPSTARTING 32650 +#endif + +#include +#include +#include +#include +#include +#include + +#ifndef WM_DPICHANGED +#define WM_DPICHANGED 0x02E0 +#endif + +RGFWDEF DWORD RGFW_winapi_window_getStyle(RGFW_window* win, RGFW_windowFlags flags); +DWORD RGFW_winapi_window_getStyle(RGFW_window* win, RGFW_windowFlags flags) { + RGFW_UNUSED(win); + DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + + if ((flags & RGFW_windowFullscreen)) { + style |= WS_POPUP; + } else { + style |= WS_SYSMENU | WS_MINIMIZEBOX; + + if (!(flags & RGFW_windowNoBorder)) { + style |= WS_CAPTION; + + if (!(flags & RGFW_windowNoResize)) + style |= WS_MAXIMIZEBOX | WS_THICKFRAME; + } + else + style |= WS_POPUP; + } + + return style; +} + +RGFWDEF DWORD RGFW_winapi_window_getExStyle(RGFW_window* win, RGFW_windowFlags flags); +DWORD RGFW_winapi_window_getExStyle(RGFW_window* win, RGFW_windowFlags flags) { + DWORD style = WS_EX_APPWINDOW; + if (flags & RGFW_windowFullscreen || (flags & RGFW_windowFloating || RGFW_window_isFloating(win))) { + style |= WS_EX_TOPMOST; + } + + return style; +} + +RGFW_bool RGFW_createUTF8FromWideStringWin32(const WCHAR* source, char* out, size_t max); + +#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 + +/* these two wgl functions need to be preloaded */ +typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList); +PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL; + +HMODULE RGFW_wgl_dll = NULL; + +#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 + +void* RGFW_window_getHWND(RGFW_window* win) { return win->src.window; } +void* RGFW_window_getHDC(RGFW_window* win) { return win->src.hdc; } + +#ifdef RGFW_OPENGL +RGFWDEF void RGFW_win32_loadOpenGLFuncs(HWND dummyWin); + +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; +#ifndef _DWMAPI_H_ +typedef struct { DWORD dwFlags; int fEnable; HRGN hRgnBlur; int fTransitionOnMaximized;} DWM_BLURBEHIND; +#endif +typedef HRESULT (WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND, const DWM_BLURBEHIND*); +PFN_DwmEnableBlurBehindWindow DwmEnableBlurBehindWindowSRC = NULL; + +typedef HRESULT (WINAPI * PFN_DwmSetWindowAttribute)(HWND, DWORD, LPCVOID, DWORD); +PFN_DwmSetWindowAttribute DwmSetWindowAttributeSRC = NULL; +#endif +void RGFW_win32_makeWindowTransparent(RGFW_window* win); +void RGFW_win32_makeWindowTransparent(RGFW_window* win) { + if (!(win->internal.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); + } +} + +RGFWDEF RGFW_bool RGFW_win32_getDarkModeState(void); +RGFW_bool RGFW_win32_getDarkModeState(void) { + u32 lightMode = 1; + DWORD len = sizeof(lightMode); + + RegGetValueW( + HKEY_CURRENT_USER, + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", + L"AppsUseLightTheme", RRF_RT_REG_DWORD, NULL, &lightMode, &len + ); + + return (lightMode == 0); +} + +RGFWDEF void RGFW_win32_makeWindowDarkMode(RGFW_window* win, RGFW_bool state); +void RGFW_win32_makeWindowDarkMode(RGFW_window* win, RGFW_bool state) { + BOOL value = (state == RGFW_TRUE) ? TRUE : FALSE; + DwmSetWindowAttributeSRC(win->src.window, 20 /* DWMWA_USE_IMMERSIVE_DARK_MODE */, &value, sizeof(value)); +} + +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); + + static BYTE keyboardState[256]; + GetKeyboardState(keyboardState); + + RECT frame; + ZeroMemory(&frame, sizeof(frame)); + DWORD style = RGFW_winapi_window_getStyle(win, win->internal.flags); + DWORD exStyle = RGFW_winapi_window_getExStyle(win, win->internal.flags); + AdjustWindowRectEx(&frame, style, FALSE, exStyle); + + switch (message) { + case WM_DISPLAYCHANGE: + RGFW_pollMonitors(); + break; + case WM_CLOSE: + case WM_QUIT: + RGFW_windowQuitCallback(win); + return 0; + case WM_ACTIVATE: { + RGFW_bool inFocus = RGFW_BOOL(LOWORD(wParam) != WA_INACTIVE); + RGFW_focusCallback(win, inFocus); + + return DefWindowProcW(hWnd, message, wParam, lParam); + } + case WM_MOVE: + if (win->internal.captureMouse) { + RGFW_window_captureMousePlatform(win, RGFW_TRUE); + } + + RGFW_windowMovedCallback(win, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + return DefWindowProcW(hWnd, message, wParam, lParam); + case WM_SIZE: { + if (win->internal.captureMouse) { + RGFW_window_captureMousePlatform(win, RGFW_TRUE); + } + + RGFW_windowResizedCallback(win, LOWORD(lParam), HIWORD(lParam)); + RGFW_window_checkMode(win); + return DefWindowProcW(hWnd, message, wParam, lParam); + } + case WM_MOUSEACTIVATE: { + if (HIWORD(lParam) == WM_LBUTTONDOWN) { + if (LOWORD(lParam) != HTCLIENT) + win->src.actionFrame = RGFW_TRUE; + } + + break; + } + case WM_CAPTURECHANGED: { + if (lParam == 0 && win->src.actionFrame) { + RGFW_window_captureMousePlatform(win, win->internal.captureMouse); + win->src.actionFrame = RGFW_FALSE; + } + + break; + } + #ifndef RGFW_NO_DPI + case WM_DPICHANGED: { + const float scaleX = HIWORD(wParam) / (float) 96; + const float scaleY = LOWORD(wParam) / (float) 96; + + RGFW_scaleUpdatedCallback(win, scaleX, scaleY); + return DefWindowProcW(hWnd, message, wParam, lParam); + } + #endif + case WM_SIZING: { + if (win->src.aspectRatioW == 0 && win->src.aspectRatioH == 0) { + break; + } + + RECT* area = (RECT*)lParam; + i32 edge = (i32)wParam; + + double ratio = (double)win->src.aspectRatioW / (double) win->src.aspectRatioH; + + if (edge == WMSZ_LEFT || edge == WMSZ_BOTTOMLEFT || edge == WMSZ_RIGHT || edge == WMSZ_BOTTOMRIGHT) { + area->bottom = area->top + (frame.bottom - frame.top) + (i32) (((area->right - area->left) - (frame.right - frame.left)) / ratio); + } else if (edge == WMSZ_TOPLEFT || edge == WMSZ_TOPRIGHT) { + area->top = area->bottom - (frame.bottom - frame.top) - (i32) (((area->right - area->left) - (frame.right - frame.left)) / ratio); + } else if (edge == WMSZ_TOP || edge == WMSZ_BOTTOM) { + area->right = area->left + (frame.right - frame.left) + (i32) (((area->bottom - area->top) - (frame.bottom - frame.top)) * ratio); + } + + return TRUE; + } + case WM_GETMINMAXINFO: { + MINMAXINFO* mmi = (MINMAXINFO*) lParam; + RGFW_bool resize = ((win->src.minSizeW == win->src.maxSizeW) && (win->src.minSizeH == win->src.maxSizeH)); + RGFW_setBit(&win->internal.flags, RGFW_windowNoResize, resize); + + mmi->ptMinTrackSize.x = (LONG)(win->src.minSizeW + (frame.right - frame.left)); + mmi->ptMinTrackSize.y = (LONG)(win->src.minSizeH + (frame.bottom - frame.top)); + if (win->src.maxSizeW == 0 && win->src.maxSizeH == 0) + return DefWindowProcW(hWnd, message, wParam, lParam); + + mmi->ptMaxTrackSize.x = (LONG)(win->src.maxSizeW + (frame.right - frame.left)); + mmi->ptMaxTrackSize.y = (LONG)(win->src.maxSizeH + (frame.bottom - frame.top)); + return DefWindowProcW(hWnd, message, wParam, lParam); + } + case WM_PAINT: { + PAINTSTRUCT ps; + BeginPaint(hWnd, &ps); + 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 + + case WM_ENTERSIZEMOVE: { + if (win->src.actionFrame) + RGFW_window_captureMousePlatform(win, win->internal.captureMouse); + + #ifdef RGFW_ADVANCED_SMOOTH_RESIZE + SetTimer(win->src.window, 1, USER_TIMER_MINIMUM, NULL); break; + #endif + break; + } + case WM_EXITSIZEMOVE: { + if (win->src.actionFrame) + RGFW_window_captureMousePlatform(win, win->internal.captureMouse); + + #ifdef RGFW_ADVANCED_SMOOTH_RESIZE + KillTimer(win->src.window, 1); break; + #endif + break; + } + case WM_TIMER: + RGFW_windowRefreshCallback(win); + break; + + 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, (u32)(point.x)|((u32)(point.y) << 16)); + break; + } + case WM_MOUSELEAVE: + RGFW_mouseNotifyCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, RGFW_FALSE); + break; + + case WM_CHAR: + case WM_SYSCHAR: { + if (wParam >= 0xd800 && wParam <= 0xdbff) + win->src.highSurrogate = (WCHAR) wParam; + else { + u32 codepoint = 0; + + if (wParam >= 0xdc00 && wParam <= 0xdfff) { + if (win->src.highSurrogate) { + codepoint += (u32)((win->src.highSurrogate - 0xd800) << 10); + codepoint += (u32)((WCHAR) wParam - 0xdc00); + codepoint += 0x10000; + } + } + else + codepoint = (WCHAR) wParam; + + win->src.highSurrogate = 0; + RGFW_keyCharCallback(win, (u32)codepoint); + } + + return 0; + } + + case WM_UNICHAR: { + if (wParam == UNICODE_NOCHAR) { + return TRUE; + } + + RGFW_keyCharCallback(win, (u32)wParam); + return 0; + } + case WM_SYSKEYUP: case WM_KEYUP: { + if (!(win->internal.enabledEvents & RGFW_keyReleasedFlag)) return DefWindowProcW(hWnd, message, wParam, lParam); + i32 scancode = (HIWORD(lParam) & (KF_EXTENDED | 0xff)); + if (scancode == 0) + scancode = (i32)MapVirtualKeyW((UINT)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; + } + + RGFW_key value = (u8)RGFW_apiKeyToRGFW((u32) scancode); + + if (wParam == VK_CONTROL) { + if (HIWORD(lParam) & KF_EXTENDED) + value = RGFW_controlR; + else value = RGFW_controlL; + } + + RGFW_bool repeat = ((lParam & 0x40000000) != 0) || RGFW_window_isKeyDown(win, value); + + RGFW_updateKeyMods(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001), (GetKeyState(VK_SCROLL) & 0x0001)); + RGFW_keyCallback(win, value, win->internal.mod, repeat, RGFW_FALSE); + break; + } + case WM_SYSKEYDOWN: case WM_KEYDOWN: { + if (!(win->internal.enabledEvents & RGFW_keyPressedFlag)) return DefWindowProcW(hWnd, message, wParam, lParam); + i32 scancode = (HIWORD(lParam) & (KF_EXTENDED | 0xff)); + if (scancode == 0) + scancode = (i32)MapVirtualKeyW((u32)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; + } + + RGFW_key value = (u8)RGFW_apiKeyToRGFW((u32) scancode); + if (wParam == VK_CONTROL) { + if (HIWORD(lParam) & KF_EXTENDED) + value = RGFW_controlR; + else value = RGFW_controlL; + } + + RGFW_bool repeat = ((lParam & 0x40000000) != 0) || RGFW_window_isKeyDown(win, value); + + RGFW_updateKeyMods(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001), (GetKeyState(VK_SCROLL) & 0x0001)); + RGFW_keyCallback(win, value, win->internal.mod, repeat, 1); + break; + } + case WM_MOUSEMOVE: { + if (win->internal.mouseInside == RGFW_FALSE) { + RGFW_mouseNotifyCallback(win, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), RGFW_TRUE); + } + + if ((win->internal.rawMouse) || _RGFW->rawMouse) { + return DefWindowProcW(hWnd, message, wParam, lParam); + } + + RGFW_mousePosCallback(win, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), _RGFW->vectorX, _RGFW->vectorY); + break; + } + case WM_INPUT: { + if (!(win->internal.rawMouse || _RGFW->rawMouse)) return DefWindowProcW(hWnd, message, wParam, lParam); + unsigned size = sizeof(RAWINPUT); + static RAWINPUT raw; + + GetRawInputData((HRAWINPUT)lParam, RID_INPUT, &raw, &size, sizeof(RAWINPUTHEADER)); + + if (raw.header.dwType != RIM_TYPEMOUSE || (raw.data.mouse.lLastX == 0 && raw.data.mouse.lLastY == 0) ) + break; + + float vecX = 0.0f; + float vecY = 0.0f; + + 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); + + vecX = (float)(pos.x - win->internal.lastMouseX); + vecY = (float)(pos.y - win->internal.lastMouseY); + } else { + vecX = (float)(raw.data.mouse.lLastX); + vecY = (float)(raw.data.mouse.lLastY); + } + + RGFW_mousePosCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, vecX, vecY); + break; + } + case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: case WM_XBUTTONDOWN: { + RGFW_mouseButton value = 0; + if (message == WM_XBUTTONDOWN) + value = RGFW_mouseMisc1 + (GET_XBUTTON_WPARAM(wParam) == XBUTTON2); + else value = (message == WM_LBUTTONDOWN) ? (u8)RGFW_mouseLeft : + (message == WM_RBUTTONDOWN) ? (u8)RGFW_mouseRight : (u8)RGFW_mouseMiddle; + + RGFW_mouseButtonCallback(win, value, 1); + break; + } + case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: case WM_XBUTTONUP: { + RGFW_mouseButton value = 0; + if (message == WM_XBUTTONUP) + value = RGFW_mouseMisc1 + (GET_XBUTTON_WPARAM(wParam) == XBUTTON2); + else value = (message == WM_LBUTTONUP) ? (u8)RGFW_mouseLeft : + (message == WM_RBUTTONUP) ? (u8)RGFW_mouseRight : (u8)RGFW_mouseMiddle; + + RGFW_mouseButtonCallback(win, value, 0); + break; + } + case WM_MOUSEWHEEL: { + float scrollY = (float)((i16) HIWORD(wParam) / (double) WHEEL_DELTA); + RGFW_mouseScrollCallback(win, 0.0f, scrollY); + break; + } + case 0x020E: {/* WM_MOUSEHWHEEL */ + float scrollX = -(float)((i16) HIWORD(wParam) / (double) WHEEL_DELTA); + RGFW_mouseScrollCallback(win, scrollX, 0.0f); + break; + } + case WM_DROPFILES: { + HDROP drop = (HDROP) wParam; + POINT pt; + + /* Move the mouse to the position of the drop */ + DragQueryPoint(drop, &pt); + RGFW_dataDragCallback(win, pt.x, pt.y); + + if (!(win->internal.enabledEvents & RGFW_dataDrop)) return DefWindowProcW(hWnd, message, wParam, lParam); + char** files = _RGFW->files; + size_t count = DragQueryFileW(drop, 0xffffffff, NULL, 0); + + u32 i; + for (i = 0; i < count; 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); + + RGFW_createUTF8FromWideStringWin32(buffer, files[i], RGFW_MAX_PATH); + + files[i][RGFW_MAX_PATH - 1] = '\0'; + } + + DragFinish(drop); + + RGFW_dataDropCallback(win, files, count); + 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)); \ + RGFW_ASSERT(name##SRC != NULL); \ + } + +RGFW_format RGFW_nativeFormat(void) { return RGFW_formatBGRA8; } + +RGFW_bool RGFW_createSurfacePtr(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) { + RGFW_ASSERT(surface != NULL); + surface->data = data; + surface->w = w; + surface->h = h; + surface->format = format; + + BITMAPV5HEADER bi; + ZeroMemory(&bi, sizeof(bi)); + bi.bV5Size = sizeof(bi); + bi.bV5Width = (i32)w; + bi.bV5Height = -((LONG) h); + bi.bV5Planes = 1; + bi.bV5BitCount = (format >= RGFW_formatRGBA8) ? 32 : 24; + bi.bV5Compression = BI_RGB; + + surface->native.bitmap = CreateDIBSection(_RGFW->root->src.hdc, + (BITMAPINFO*) &bi, DIB_RGB_COLORS, + (void**) &surface->native.bitmapBits, + NULL, (DWORD) 0); + + surface->native.format = (format >= RGFW_formatRGBA8) ? RGFW_formatBGRA8 : RGFW_formatBGR8; + + if (surface->native.bitmap == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, "Failed to create DIB section."); + return RGFW_FALSE; + } + + surface->native.hdcMem = CreateCompatibleDC(_RGFW->root->src.hdc); + SelectObject(surface->native.hdcMem, surface->native.bitmap); + + return RGFW_TRUE; +} + +void RGFW_surface_freePtr(RGFW_surface* surface) { + RGFW_ASSERT(surface != NULL); + + DeleteDC(surface->native.hdcMem); + DeleteObject(surface->native.bitmap); +} + +void RGFW_window_blitSurface(RGFW_window* win, RGFW_surface* surface) { + RGFW_copyImageData(surface->native.bitmapBits, surface->w, RGFW_MIN(win->h, surface->h), surface->native.format, surface->data, surface->format, surface->convertFunc); + BitBlt(win->src.hdc, 0, 0, RGFW_MIN(win->w, surface->w), RGFW_MIN(win->h, surface->h), surface->native.hdcMem, 0, 0, SRCCOPY); +} + +void RGFW_window_setRawMouseModePlatform(RGFW_window* win, RGFW_bool state) { + RGFW_UNUSED(win); + RAWINPUTDEVICE id = { 0x01, 0x02, 0, win->src.window }; + id.dwFlags = (state == RGFW_TRUE) ? 0 : RIDEV_REMOVE; + + RegisterRawInputDevices(&id, 1, sizeof(id)); +} + +void RGFW_window_captureMousePlatform(RGFW_window* win, RGFW_bool state) { + if (state == RGFW_FALSE) { + ClipCursor(NULL); + return; + } + + RECT clipRect; + GetClientRect(win->src.window, &clipRect); + ClientToScreen(win->src.window, (POINT*) &clipRect.left); + ClientToScreen(win->src.window, (POINT*) &clipRect.right); + ClipCursor(&clipRect); +} + +#define RGFW_LOAD_LIBRARY(x, lib) if (x == NULL) { x = LoadLibraryA(lib); RGFW_ASSERT(x != NULL); } + +#ifdef RGFW_DIRECTX +int RGFW_window_createSwapChain_DirectX(RGFW_window* win, IDXGIFactory* pFactory, IUnknown* pDevice, IDXGISwapChain** swapchain) { + RGFW_ASSERT(win && pFactory && pDevice && swapchain); + + static DXGI_SWAP_CHAIN_DESC swapChainDesc; + RGFW_MEMSET(&swapChainDesc, 0, sizeof(swapChainDesc)); + swapChainDesc.BufferCount = 2; + swapChainDesc.BufferDesc.Width = win->w; + swapChainDesc.BufferDesc.Height = win->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, "Failed to create DirectX swap chain!"); + return -2; + } + + return 0; +} +#endif + +/* we're doing it with magic numbers because some keys are missing */ +void RGFW_initKeycodesPlatform(void) { + _RGFW->keycodes[0x00B] = RGFW_0; + _RGFW->keycodes[0x002] = RGFW_1; + _RGFW->keycodes[0x003] = RGFW_2; + _RGFW->keycodes[0x004] = RGFW_3; + _RGFW->keycodes[0x005] = RGFW_4; + _RGFW->keycodes[0x006] = RGFW_5; + _RGFW->keycodes[0x007] = RGFW_6; + _RGFW->keycodes[0x008] = RGFW_7; + _RGFW->keycodes[0x009] = RGFW_8; + _RGFW->keycodes[0x00A] = RGFW_9; + _RGFW->keycodes[0x01E] = RGFW_a; + _RGFW->keycodes[0x030] = RGFW_b; + _RGFW->keycodes[0x02E] = RGFW_c; + _RGFW->keycodes[0x020] = RGFW_d; + _RGFW->keycodes[0x012] = RGFW_e; + _RGFW->keycodes[0x021] = RGFW_f; + _RGFW->keycodes[0x022] = RGFW_g; + _RGFW->keycodes[0x023] = RGFW_h; + _RGFW->keycodes[0x017] = RGFW_i; + _RGFW->keycodes[0x024] = RGFW_j; + _RGFW->keycodes[0x025] = RGFW_k; + _RGFW->keycodes[0x026] = RGFW_l; + _RGFW->keycodes[0x032] = RGFW_m; + _RGFW->keycodes[0x031] = RGFW_n; + _RGFW->keycodes[0x018] = RGFW_o; + _RGFW->keycodes[0x019] = RGFW_p; + _RGFW->keycodes[0x010] = RGFW_q; + _RGFW->keycodes[0x013] = RGFW_r; + _RGFW->keycodes[0x01F] = RGFW_s; + _RGFW->keycodes[0x014] = RGFW_t; + _RGFW->keycodes[0x016] = RGFW_u; + _RGFW->keycodes[0x02F] = RGFW_v; + _RGFW->keycodes[0x011] = RGFW_w; + _RGFW->keycodes[0x02D] = RGFW_x; + _RGFW->keycodes[0x015] = RGFW_y; + _RGFW->keycodes[0x02C] = RGFW_z; + _RGFW->keycodes[0x028] = RGFW_apostrophe; + _RGFW->keycodes[0x02B] = RGFW_backSlash; + _RGFW->keycodes[0x033] = RGFW_comma; + _RGFW->keycodes[0x00D] = RGFW_equals; + _RGFW->keycodes[0x029] = RGFW_backtick; + _RGFW->keycodes[0x01A] = RGFW_bracket; + _RGFW->keycodes[0x00C] = RGFW_minus; + _RGFW->keycodes[0x034] = RGFW_period; + _RGFW->keycodes[0x01B] = RGFW_closeBracket; + _RGFW->keycodes[0x027] = RGFW_semicolon; + _RGFW->keycodes[0x035] = RGFW_slash; + _RGFW->keycodes[0x056] = RGFW_world2; + _RGFW->keycodes[0x00E] = RGFW_backSpace; + _RGFW->keycodes[0x153] = RGFW_delete; + _RGFW->keycodes[0x14F] = RGFW_end; + _RGFW->keycodes[0x01C] = RGFW_enter; + _RGFW->keycodes[0x001] = RGFW_escape; + _RGFW->keycodes[0x147] = RGFW_home; + _RGFW->keycodes[0x152] = RGFW_insert; + _RGFW->keycodes[0x15D] = RGFW_menu; + _RGFW->keycodes[0x151] = RGFW_pageDown; + _RGFW->keycodes[0x149] = RGFW_pageUp; + _RGFW->keycodes[0x045] = RGFW_pause; + _RGFW->keycodes[0x039] = RGFW_space; + _RGFW->keycodes[0x00F] = RGFW_tab; + _RGFW->keycodes[0x03A] = RGFW_capsLock; + _RGFW->keycodes[0x145] = RGFW_numLock; + _RGFW->keycodes[0x046] = RGFW_scrollLock; + _RGFW->keycodes[0x03B] = RGFW_F1; + _RGFW->keycodes[0x03C] = RGFW_F2; + _RGFW->keycodes[0x03D] = RGFW_F3; + _RGFW->keycodes[0x03E] = RGFW_F4; + _RGFW->keycodes[0x03F] = RGFW_F5; + _RGFW->keycodes[0x040] = RGFW_F6; + _RGFW->keycodes[0x041] = RGFW_F7; + _RGFW->keycodes[0x042] = RGFW_F8; + _RGFW->keycodes[0x043] = RGFW_F9; + _RGFW->keycodes[0x044] = RGFW_F10; + _RGFW->keycodes[0x057] = RGFW_F11; + _RGFW->keycodes[0x058] = RGFW_F12; + _RGFW->keycodes[0x064] = RGFW_F13; + _RGFW->keycodes[0x065] = RGFW_F14; + _RGFW->keycodes[0x066] = RGFW_F15; + _RGFW->keycodes[0x067] = RGFW_F16; + _RGFW->keycodes[0x068] = RGFW_F17; + _RGFW->keycodes[0x069] = RGFW_F18; + _RGFW->keycodes[0x06A] = RGFW_F19; + _RGFW->keycodes[0x06B] = RGFW_F20; + _RGFW->keycodes[0x06C] = RGFW_F21; + _RGFW->keycodes[0x06D] = RGFW_F22; + _RGFW->keycodes[0x06E] = RGFW_F23; + _RGFW->keycodes[0x076] = RGFW_F24; + _RGFW->keycodes[0x038] = RGFW_altL; + _RGFW->keycodes[0x01D] = RGFW_controlL; + _RGFW->keycodes[0x02A] = RGFW_shiftL; + _RGFW->keycodes[0x15B] = RGFW_superL; + _RGFW->keycodes[0x137] = RGFW_printScreen; + _RGFW->keycodes[0x138] = RGFW_altR; + _RGFW->keycodes[0x11D] = RGFW_controlR; + _RGFW->keycodes[0x036] = RGFW_shiftR; + _RGFW->keycodes[0x15C] = RGFW_superR; + _RGFW->keycodes[0x150] = RGFW_down; + _RGFW->keycodes[0x14B] = RGFW_left; + _RGFW->keycodes[0x14D] = RGFW_right; + _RGFW->keycodes[0x148] = RGFW_up; + _RGFW->keycodes[0x052] = RGFW_kp0; + _RGFW->keycodes[0x04F] = RGFW_kp1; + _RGFW->keycodes[0x050] = RGFW_kp2; + _RGFW->keycodes[0x051] = RGFW_kp3; + _RGFW->keycodes[0x04B] = RGFW_kp4; + _RGFW->keycodes[0x04C] = RGFW_kp5; + _RGFW->keycodes[0x04D] = RGFW_kp6; + _RGFW->keycodes[0x047] = RGFW_kp7; + _RGFW->keycodes[0x048] = RGFW_kp8; + _RGFW->keycodes[0x049] = RGFW_kp9; + _RGFW->keycodes[0x04E] = RGFW_kpPlus; + _RGFW->keycodes[0x053] = RGFW_kpPeriod; + _RGFW->keycodes[0x135] = RGFW_kpSlash; + _RGFW->keycodes[0x11C] = RGFW_kpReturn; + _RGFW->keycodes[0x059] = RGFW_kpEqual; + _RGFW->keycodes[0x037] = RGFW_kpMultiply; + _RGFW->keycodes[0x04A] = RGFW_kpMinus; +} + + +i32 RGFW_initPlatform(void) { +#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); + RGFW_PROC_DEF(RGFW_dwm_dll, DwmSetWindowAttribute); + #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, 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, 1, 1, RGFW_formatRGBA8); + return 1; +} + +RGFW_window* RGFW_createWindowPlatform(const char* name, RGFW_windowFlags flags, RGFW_window* win) { + if (name[0] == 0) name = (char*) " "; + win->src.hIconSmall = win->src.hIconBig = NULL; + win->src.maxSizeW = 0; + win->src.maxSizeH = 0; + win->src.minSizeW = 0; + win->src.minSizeH = 0; + win->src.aspectRatioW = 0; + win->src.aspectRatioH = 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; + + 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->x, win->y, win->w, win->h, 0, 0, inh, 0); + + GetWindowRect(dummyWin, &windowRect); + GetClientRect(dummyWin, &clientRect); + +#ifdef RGFW_OPENGL + RGFW_win32_loadOpenGLFuncs(dummyWin); +#endif + + DestroyWindow(dummyWin); + + RECT rect = { 0, 0, win->w, win->h}; + DWORD style = RGFW_winapi_window_getStyle(win, flags); + DWORD exStyle = RGFW_winapi_window_getExStyle(win, flags); + AdjustWindowRectEx(&rect, style, FALSE, exStyle); + + win->src.window = CreateWindowW(Class.lpszClassName, (wchar_t*)wide_name, window_style, win->x + rect.left, win->y + rect.top, rect.right - rect.left, rect.bottom - rect.top, 0, 0, inh, 0); + SetPropW(win->src.window, L"RGFW", win); + RGFW_window_resize(win, win->w, win->h); /* so WM_GETMINMAXINFO gets called again */ + + if (flags & RGFW_windowAllowDND) { + win->internal.flags |= RGFW_windowAllowDND; + RGFW_window_setDND(win, 1); + } + win->src.hdc = GetDC(win->src.window); + + RGFW_win32_makeWindowDarkMode(win, RGFW_win32_getDarkModeState()); + RGFW_win32_makeWindowTransparent(win); + return win; +} + +void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { + RGFW_setBit(&win->internal.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 { + if (win->internal.flags & RGFW_windowNoResize) style &= ~WS_MAXIMIZEBOX; + 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->internal.flags, RGFW_windowAllowDND, allow); + DragAcceptFiles(win->src.window, allow); +} + +RGFW_bool RGFW_getGlobalMouse(i32* x, i32* y) { + POINT p; + GetCursorPos(&p); + if (x) *x = p.x; + if (y) *y = p.y; + return RGFW_TRUE; +} + +void RGFW_window_setAspectRatio(RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + win->src.aspectRatioW = w; + win->src.aspectRatioH = h; +} + +void RGFW_window_setMinSize(RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + win->src.minSizeW = w; + win->src.minSizeH = h; +} + +void RGFW_window_setMaxSize(RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + win->src.maxSizeW = w; + win->src.maxSizeH = h; +} + +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->x, win->y, win->w, win->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); + + RECT rect = { 0, 0, win->internal.oldW, win->internal.oldH}; + DWORD style = RGFW_winapi_window_getStyle(win, win->internal.flags); + DWORD exStyle = RGFW_winapi_window_getExStyle(win, win->internal.flags); + AdjustWindowRectEx(&rect, style, FALSE, exStyle); + SetWindowPos(win->src.window, HWND_TOP, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER); + + + win->internal.flags &= ~(u32)RGFW_windowFullscreen; + win->x = win->internal.oldX; + win->y = win->internal.oldY; + win->w = win->internal.oldW; + win->h = win->internal.oldH; + return; + } + + win->internal.oldX = win->x; + win->internal.oldY = win->y; + win->internal.oldW = win->w; + win->internal.oldH = win->h; + win->internal.flags |= RGFW_windowFullscreen; + + RGFW_monitor* mon = RGFW_window_getMonitor(win); + + RGFW_window_setBorder(win, 0); + RGFW_monitor_scaleToWindow(mon, win); + + SetWindowPos(win->src.window, HWND_TOPMOST, (i32)mon->x, (i32)mon->y, (i32)mon->mode.w, (i32)mon->mode.h, SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW); + + win->x = mon->x; + win->y = mon->y; + win->w = mon->mode.w; + win->h = mon->mode.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; +} + +void RGFW_stopCheckEvents(void) { + PostMessageW(_RGFW->root->src.window, WM_NULL, 0, 0); +} + +void RGFW_waitForEvent(i32 waitMS) { + MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD)waitMS, QS_ALLINPUT); +} + +RGFW_key RGFW_physicalToMappedKey(RGFW_key key) { + UINT vsc = RGFW_rgfwToApiKey(key); + BYTE keyboardState[256] = {0}; + + if (!GetKeyboardState(keyboardState)) + return key; + + UINT vk = MapVirtualKeyW(vsc, MAPVK_VSC_TO_VK); + HKL layout = GetKeyboardLayout(0); + + wchar_t charBuffer[4] = {0}; + int result = ToUnicodeEx(vk, vsc, keyboardState, charBuffer, 1, 0, layout); + + if (result == 1 && charBuffer[0] < 256) { + return (RGFW_key)charBuffer[0]; + } + + switch (vk) { + case VK_F1: return RGFW_F1; + case VK_F2: return RGFW_F2; + case VK_F3: return RGFW_F3; + case VK_F4: return RGFW_F4; + case VK_F5: return RGFW_F5; + case VK_F6: return RGFW_F6; + case VK_F7: return RGFW_F7; + case VK_F8: return RGFW_F8; + case VK_F9: return RGFW_F9; + case VK_F10: return RGFW_F10; + case VK_F11: return RGFW_F11; + case VK_F12: return RGFW_F12; + case VK_F13: return RGFW_F13; + case VK_F14: return RGFW_F14; + case VK_F15: return RGFW_F15; + case VK_F16: return RGFW_F16; + case VK_F17: return RGFW_F17; + case VK_F18: return RGFW_F18; + case VK_F19: return RGFW_F19; + case VK_F20: return RGFW_F20; + case VK_F21: return RGFW_F21; + case VK_F22: return RGFW_F22; + case VK_F23: return RGFW_F23; + case VK_F24: return RGFW_F24; + case VK_LSHIFT: return RGFW_shiftL; + case VK_RSHIFT: return RGFW_shiftR; + case VK_LCONTROL: return RGFW_controlL; + case VK_RCONTROL: return RGFW_controlR; + case VK_LMENU: return RGFW_altL; + case VK_RMENU: return RGFW_altR; + case VK_LWIN: return RGFW_superL; + case VK_RWIN: return RGFW_superR; + case VK_CAPITAL: return RGFW_capsLock; + case VK_NUMLOCK: return RGFW_numLock; + case VK_SCROLL: return RGFW_scrollLock; + case VK_UP: return RGFW_up; + case VK_DOWN: return RGFW_down; + case VK_LEFT: return RGFW_left; + case VK_RIGHT: return RGFW_right; + case VK_HOME: return RGFW_home; + case VK_END: return RGFW_end; + case VK_PRIOR: return RGFW_pageUp; + case VK_NEXT: return RGFW_pageDown; + case VK_INSERT: return RGFW_insert; + case VK_APPS: return RGFW_menu; + case VK_ADD: return RGFW_kpPlus; + case VK_SUBTRACT: return RGFW_kpMinus; + case VK_MULTIPLY: return RGFW_kpMultiply; + case VK_DIVIDE: return RGFW_kpSlash; + case VK_RETURN: return RGFW_kpReturn; + case VK_DECIMAL: return RGFW_kpPeriod; + case VK_NUMPAD0: return RGFW_kp0; + case VK_NUMPAD1: return RGFW_kp1; + case VK_NUMPAD2: return RGFW_kp2; + case VK_NUMPAD3: return RGFW_kp3; + case VK_NUMPAD4: return RGFW_kp4; + case VK_NUMPAD5: return RGFW_kp5; + case VK_NUMPAD6: return RGFW_kp6; + case VK_NUMPAD7: return RGFW_kp7; + case VK_NUMPAD8: return RGFW_kp8; + case VK_NUMPAD9: return RGFW_kp9; + case VK_SNAPSHOT: return RGFW_printScreen; + case VK_PAUSE: return RGFW_pause; + default: return RGFW_keyNULL; + } + + return RGFW_keyNULL; +} + +void RGFW_pollEvents(void) { + RGFW_resetPrevState(); + MSG msg; + while (PeekMessageA(&msg, NULL, 0u, 0u, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessageA(&msg); + } +} + +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); +} + +RGFW_bool RGFW_monitor_getWorkarea(RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height) { + MONITORINFOEX mi; + mi.cbSize = sizeof(MONITORINFOEX); + GetMonitorInfoA(monitor->node->hMonitor, (LPMONITORINFO)&mi); + + if (x) *x = mi.rcWork.left; + if (y) *y = mi.rcWork.top; + if (width) *width = mi.rcWork.right - mi.rcWork.left; + if (height) *height = mi.rcWork.bottom - mi.rcWork.top; + + return RGFW_TRUE; +} + +size_t RGFW_monitor_getGammaRampPtr(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) { + WORD values[3][256]; + + HDC dc = CreateDCW(L"DISPLAY", monitor->node->adapterName, NULL, NULL); + GetDeviceGammaRamp(dc, values); + DeleteDC(dc); + + if (ramp) { + memcpy(ramp->red, values[0], sizeof(values[0])); + memcpy(ramp->green, values[1], sizeof(values[1])); + memcpy(ramp->blue, values[2], sizeof(values[2])); + } + + return sizeof(values[0]) / sizeof(WORD); +} + +RGFW_bool RGFW_monitor_setGammaRamp(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) { + WORD values[3][256]; + if (ramp->count != 256) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errX11, "Win32: Gamma ramp size must be 256"); + return RGFW_FALSE; + } + + memcpy(values[0], ramp->red, sizeof(values[0])); + memcpy(values[1], ramp->green, sizeof(values[1])); + memcpy(values[2], ramp->blue, sizeof(values[2])); + + HDC dc = CreateDCW(L"DISPLAY", monitor->node->adapterName, NULL, NULL); + SetDeviceGammaRamp(dc, values); + DeleteDC(dc); + return RGFW_TRUE; +} + +BOOL CALLBACK RGFW_win32_getMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData); +BOOL CALLBACK RGFW_win32_getMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { + RGFW_UNUSED(hMonitor); + RGFW_UNUSED(hdcMonitor); + RGFW_UNUSED(lprcMonitor); + RGFW_UNUSED(dwData); + + MONITORINFOEXW mi; + ZeroMemory(&mi, sizeof(mi)); + mi.cbSize = sizeof(mi); + + if (GetMonitorInfoW(hMonitor, (MONITORINFO*) &mi)) { + RGFW_monitorNode* node = (RGFW_monitorNode*)dwData; + if (wcscmp(mi.szDevice, node->adapterName) == 0) { + node->hMonitor = hMonitor; + } + } + + return TRUE; +} + +RGFWDEF void RGFW_win32_getMode(DEVMODEW* dm, RGFW_monitorMode* mode); +void RGFW_win32_getMode(DEVMODEW* dm, RGFW_monitorMode* mode) { + mode->w = (i32)dm->dmPelsWidth; + mode->h = (i32)dm->dmPelsHeight; + RGFW_splitBPP(dm->dmBitsPerPel, mode); + + switch (dm->dmDisplayFrequency) { + case 119: + case 59: + case 29: + mode->refreshRate = ((float)(dm->dmDisplayFrequency + 1) * 1000.0f) / 1001.f; + break; + default: + mode->refreshRate = (float)dm->dmDisplayFrequency; + break; + } +} + +size_t RGFW_monitor_getModesPtr(RGFW_monitor* monitor, RGFW_monitorMode** modes){ + size_t count = 0; + DWORD modeIndex = 0; + + for (;;) { + DEVMODEW dm; + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + + if (!EnumDisplaySettingsW(monitor->node->adapterName, modeIndex, &dm)) + break; + + modeIndex++; + + if (dm.dmBitsPerPel < 15) + continue; + + if (modes) { + RGFW_monitorMode mode; + RGFW_win32_getMode(&dm, &mode); + + size_t i; + for (i = 0; i < count; i++) { + if (RGFW_monitorModeCompare(&(*modes)[i], &mode, RGFW_monitorAll) == RGFW_TRUE) { + break; + } + } + + if (i < count) { + continue; + } + + (*modes)[count] = mode; + } + + count += 1; + } + + return count; +} + +RGFWDEF void RGFW_win32_createMonitor(DISPLAY_DEVICEW* adapter, DISPLAY_DEVICEW* dd); +void RGFW_win32_createMonitor(DISPLAY_DEVICEW* adapter, DISPLAY_DEVICEW* dd) { + DEVMODEW dm; + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + + if (!EnumDisplaySettingsW(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &dm)) { + return; + } + + RGFW_monitorNode* node = RGFW_monitors_add(NULL); + + wcscpy(node->adapterName, adapter->DeviceName); + wcscpy(node->deviceName, dd->DeviceName); + + RGFW_createUTF8FromWideStringWin32(dd->DeviceString, node->mon.name, sizeof(node->mon.name)); + node->mon.name[sizeof(node->mon.name) - 1] = '\0'; + + RECT rect; + rect.left = (LONG)dm.dmPosition.x; + rect.top = (LONG)dm.dmPosition.y; + rect.right = (LONG)((LONG)dm.dmPosition.x + (LONG)dm.dmPelsWidth); + rect.bottom = (LONG)((long)dm.dmPosition.y + (LONG)dm.dmPelsHeight); + EnumDisplayMonitors(NULL, &rect, RGFW_win32_getMonitorHandle, (LPARAM)node); + + RGFW_win32_getMode(&dm, &node->mon.mode); + + MONITORINFOEXW monitorInfo; + monitorInfo.cbSize = sizeof(MONITORINFOEXW); + GetMonitorInfoW(node->hMonitor, (LPMONITORINFO)&monitorInfo); + + node->mon.x = monitorInfo.rcMonitor.left; + node->mon.y = monitorInfo.rcMonitor.top; + + HDC hdc = CreateDCW(monitorInfo.szDevice, NULL, NULL, NULL); + /* get pixels per inch */ + float dpiX = (float)GetDeviceCaps(hdc, LOGPIXELSX); + float dpiY = (float)GetDeviceCaps(hdc, LOGPIXELSX); + + node->mon.scaleX = dpiX / 96.0f; + node->mon.scaleY = dpiY / 96.0f; + node->mon.pixelRatio = dpiX >= 192.0f ? 2.0f : 1.0f; + + node->mon.physW = (float)GetDeviceCaps(hdc, HORZSIZE) / 25.4f; + node->mon.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(node->hMonitor, MDT_EFFECTIVE_DPI, &x, &y); + node->mon.scaleX = (float) (x) / (float) 96.0f; + node->mon.scaleY = (float) (y) / (float) 96.0f; + node->mon.pixelRatio = dpiX >= 192.0f ? 2.0f : 1.0f; + } +#endif + + if (monitorInfo.dwFlags & MONITORINFOF_PRIMARY) { + _RGFW->monitors.primary = node; + } + + RGFW_monitorCallback(_RGFW->root, &node->mon, RGFW_TRUE); +} + +void RGFW_pollMonitors(void) { + for (RGFW_monitorNode* node = _RGFW->monitors.list.head; node; node = node->next) { + node->disconnected = RGFW_TRUE; + } + + /* loop through display adapters (GPU) */ + DISPLAY_DEVICEW adapter; + DWORD adapterNum; + for (adapterNum = 0; ; adapterNum++) { + ZeroMemory(&adapter, sizeof(adapter)); + adapter.cb = sizeof(adapter); + + if (!EnumDisplayDevicesW(NULL, adapterNum, &adapter, 0)) + break; + + if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE)) + continue; + + DISPLAY_DEVICEW dd; + dd.cb = sizeof(dd); + + /* loop through display devices (monitors) */ + DWORD deviceNum; + for (deviceNum = 0; ; deviceNum++) { + ZeroMemory(&dd, sizeof(dd)); + dd.cb = sizeof(dd); + + if (!EnumDisplayDevicesW(adapter.DeviceName, deviceNum, &dd, 0)) + break; + + if (!(dd.StateFlags & DISPLAY_DEVICE_ACTIVE)) + continue; + + RGFW_monitorNode* node; + for (node = _RGFW->monitors.list.head; node; node = node->next) { + if (node->disconnected == RGFW_TRUE && wcscmp(node->deviceName, dd.DeviceName) == 0) { + node->disconnected = RGFW_FALSE; + EnumDisplayMonitors(NULL, NULL, RGFW_win32_getMonitorHandle, (LPARAM) &node->mon); + break; + } + } + + if (node) { + continue; + } + + RGFW_win32_createMonitor(&adapter, &dd); + } + + /* if there are no display devices, just use the monitor directly (hack borrowed from GLFW (I'm not giving it back)) */ + if (deviceNum == 0) { + RGFW_monitorNode* node; + for (node = _RGFW->monitors.list.head; node; node = node->next) { + if (node->disconnected == RGFW_TRUE && wcscmp(node->adapterName, adapter.DeviceName) == 0) { + node->disconnected = RGFW_FALSE; + break; + } + } + + if (node) { + continue; + } + + RGFW_win32_createMonitor(&adapter, NULL); + } + } + + RGFW_monitors_refresh(); +} + +RGFW_monitor* RGFW_window_getMonitor(RGFW_window* win) { + HMONITOR src = MonitorFromWindow(win->src.window, MONITOR_DEFAULTTOPRIMARY); + RGFW_monitorNode* node = _RGFW->monitors.list.head; + + for (node = _RGFW->monitors.list.head; node; node = node->next) { + if (node->hMonitor == src) { + return &node->mon; + } + } + + return RGFW_getPrimaryMonitor(); +} + +RGFW_bool RGFW_monitor_setMode(RGFW_monitor* mon, RGFW_monitorMode* mode) { + DEVMODEW dm; + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + + dm.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT; + dm.dmPelsWidth = (u32)mode->w; + dm.dmPelsHeight = (u32)mode->h; + + dm.dmFields |= DM_DISPLAYFREQUENCY; + dm.dmDisplayFrequency = (DWORD)mode->refreshRate; + + dm.dmFields |= DM_BITSPERPEL; + dm.dmBitsPerPel = (DWORD)(mode->red + mode->green + mode->blue); + + if (ChangeDisplaySettingsExW(mon->node->adapterName, &dm, NULL, CDS_TEST, NULL) == DISP_CHANGE_SUCCESSFUL) { + if (ChangeDisplaySettingsExW(mon->node->adapterName, &dm, NULL, CDS_UPDATEREGISTRY, NULL) == DISP_CHANGE_SUCCESSFUL) + return RGFW_TRUE; + return RGFW_FALSE; + } else return RGFW_FALSE; +} + +RGFW_bool RGFW_monitor_requestMode(RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request) { + HMONITOR src = mon->node->hMonitor; + + MONITORINFOEX monitorInfo; + monitorInfo.cbSize = sizeof(MONITORINFOEX); + GetMonitorInfoA(src, (LPMONITORINFO)&monitorInfo); + + DEVMODEW dm; + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + + if (EnumDisplaySettingsW(mon->node->adapterName, ENUM_CURRENT_SETTINGS, &dm)) { + if (request & RGFW_monitorScale) { + dm.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT; + dm.dmPelsWidth = (u32)mode->w; + dm.dmPelsHeight = (u32)mode->h; + } + + if (request & RGFW_monitorRefresh) { + dm.dmFields |= DM_DISPLAYFREQUENCY; + dm.dmDisplayFrequency = (DWORD)mode->refreshRate; + } + + if (request & RGFW_monitorRGB) { + dm.dmFields |= DM_BITSPERPEL; + dm.dmBitsPerPel = (DWORD)(mode->red + mode->green + mode->blue); + } + + if (ChangeDisplaySettingsExW(mon->node->adapterName, &dm, NULL, CDS_TEST, NULL) == DISP_CHANGE_SUCCESSFUL) { + if (ChangeDisplaySettingsExW(mon->node->adapterName, &dm, NULL, CDS_UPDATEREGISTRY, NULL) == DISP_CHANGE_SUCCESSFUL) { + RGFW_win32_getMode(&dm, &mon->mode); + return RGFW_TRUE; + } + return RGFW_FALSE; + } else return RGFW_FALSE; + } + + return RGFW_FALSE; +} + +HICON RGFW_loadHandleImage(u8* data, i32 w, i32 h, RGFW_format format, BOOL icon); +HICON RGFW_loadHandleImage(u8* data, i32 w, i32 h, RGFW_format format, BOOL icon) { + BITMAPV5HEADER bi; + ZeroMemory(&bi, sizeof(bi)); + bi.bV5Size = sizeof(bi); + bi.bV5Width = (i32)w; + bi.bV5Height = -((LONG) h); + bi.bV5Planes = 1; + bi.bV5BitCount = (WORD)32; + 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); + + RGFW_copyImageData(target, w, h, RGFW_formatBGRA8, data, format, NULL); + ReleaseDC(NULL, dc); + + HBITMAP mask = CreateBitmap((i32)w, (i32)h, 1, 1, NULL); + + ICONINFO ii; + ZeroMemory(&ii, sizeof(ii)); + ii.fIcon = icon; + ii.xHotspot = (u32)w / 2; + ii.yHotspot = (u32)h / 2; + ii.hbmMask = mask; + ii.hbmColor = color; + + HICON handle = CreateIconIndirect(&ii); + + DeleteObject(color); + DeleteObject(mask); + + return handle; +} +RGFW_mouse* RGFW_loadMouse(u8* data, i32 w, i32 h, RGFW_format format) { + HCURSOR cursor = (HCURSOR) RGFW_loadHandleImage(data, w, h, format, 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); + + u32 mouseIcon = 0; + + switch (mouse) { + case RGFW_mouseNormal: mouseIcon = OCR_NORMAL; break; + case RGFW_mouseArrow: mouseIcon = OCR_NORMAL; break; + case RGFW_mouseIbeam: mouseIcon = OCR_IBEAM; break; + case RGFW_mouseWait: mouseIcon = OCR_WAIT; break; + case RGFW_mouseCrosshair: mouseIcon = OCR_CROSS; break; + case RGFW_mouseProgress: mouseIcon = OCR_APPSTARTING; break; + case RGFW_mouseResizeNWSE: mouseIcon = OCR_SIZENWSE; break; + case RGFW_mouseResizeNESW: mouseIcon = OCR_SIZENESW; break; + case RGFW_mouseResizeEW: mouseIcon = OCR_SIZEWE; break; + case RGFW_mouseResizeNS: mouseIcon = OCR_SIZENS; break; + case RGFW_mouseResizeAll: mouseIcon = OCR_SIZEALL; break; + case RGFW_mouseNotAllowed: mouseIcon = OCR_NO; break; + case RGFW_mousePointingHand: mouseIcon = OCR_HAND; break; + case RGFW_mouseResizeNW: mouseIcon = OCR_SIZENWSE; break; + case RGFW_mouseResizeN: mouseIcon = OCR_SIZENS; break; + case RGFW_mouseResizeNE: mouseIcon = OCR_SIZENESW; break; + case RGFW_mouseResizeE: mouseIcon = OCR_SIZEWE; break; + case RGFW_mouseResizeSE: mouseIcon = OCR_SIZENWSE; break; + case RGFW_mouseResizeS: mouseIcon = OCR_SIZENS; break; + case RGFW_mouseResizeSW: mouseIcon = OCR_SIZENESW; break; + case RGFW_mouseResizeW: mouseIcon = OCR_SIZEWE; break; + default: return RGFW_FALSE; + } + + char* icon = MAKEINTRESOURCEA(mouseIcon); + + 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->internal.flags & RGFW_windowFocusOnShow) RGFW_window_focus(win); + ShowWindow(win->src.window, SW_RESTORE); +} + +void RGFW_window_flash(RGFW_window* win, RGFW_flashRequest request) { + if (RGFW_window_isInFocus(win) && request) { + return; + } + + FLASHWINFO desc; + RGFW_MEMSET(&desc, 0, sizeof(desc)); + + desc.cbSize = sizeof(desc); + desc.hwnd = win->src.window; + + switch (request) { + case RGFW_flashCancel: + desc.dwFlags = FLASHW_STOP; + break; + case RGFW_flashBriefly: + desc.dwFlags = FLASHW_TRAY; + desc.uCount = 1; + break; + case RGFW_flashUntilFocused: + desc.dwFlags = (FLASHW_TRAY | FLASHW_TIMERNOFG); + break; + default: break; + } + + FlashWindowEx(&desc); +} + +#define RGFW_FREE_LIBRARY(x) if (x != NULL) FreeLibrary(x); x = NULL; +void RGFW_deinitPlatform(void) { + #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_freeMouse(_RGFW->hiddenMouse); +} + + +void RGFW_window_closePlatform(RGFW_window* 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); +} + +void RGFW_window_move(RGFW_window* win, i32 x, i32 y) { + RGFW_ASSERT(win != NULL); + + win->x = x; + win->y = y; + SetWindowPos(win->src.window, HWND_TOP, win->x, win->y, 0, 0, SWP_NOSIZE); +} + +void RGFW_window_resize(RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + + win->w = w; + win->h = h; + + RECT rect = { 0, 0, w, h}; + DWORD style = RGFW_winapi_window_getStyle(win, win->internal.flags); + DWORD exStyle = RGFW_winapi_window_getExStyle(win, win->internal.flags); + AdjustWindowRectEx(&rect, style, FALSE, exStyle); + + SetWindowPos(win->src.window, HWND_TOP, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER); +} + +void RGFW_window_setName(RGFW_window* win, const char* name) { + RGFW_ASSERT(win != NULL); + if (name == NULL) name = "\0"; + + 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* data, i32 w, i32 h, RGFW_format format, RGFW_icon type) { + RGFW_ASSERT(win != NULL); + #ifndef RGFW_WIN95 + if (win->src.hIconSmall && (type & RGFW_iconWindow)) DestroyIcon(win->src.hIconSmall); + if (win->src.hIconBig && (type & RGFW_iconTaskbar)) DestroyIcon(win->src.hIconBig); + + if (data == 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(data, w, h, format, TRUE); + SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)win->src.hIconSmall); + } + if (type & RGFW_iconTaskbar) { + win->src.hIconBig = RGFW_loadHandleImage(data, w, h, format, TRUE); + SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)win->src.hIconBig); + } + return RGFW_TRUE; + #else + RGFW_UNUSED(img); + RGFW_UNUSED(type); + 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 - 1] = '\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, i32 x, i32 y) { + RGFW_ASSERT(win != NULL); + win->internal.lastMouseX = x - win->x; + win->internal.lastMouseY = y - win->y; + SetCursorPos(x, y); +} + +#ifdef RGFW_OPENGL +RGFW_bool RGFW_extensionSupportedPlatform_OpenGL(const char * extension, size_t len) { + const char* extensions = NULL; + + RGFW_proc proc = RGFW_getProcAddress_OpenGL("wglGetExtensionsStringARB"); + RGFW_proc proc2 = RGFW_getProcAddress_OpenGL("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_OpenGL(const char* procname) { + RGFW_proc proc = (RGFW_proc)wglGetProcAddress(procname); + if (proc) + return proc; + + return (RGFW_proc) GetProcAddress(RGFW_wgl_dll, procname); +} + +void RGFW_win32_loadOpenGLFuncs(HWND dummyWin) { + 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); + + HGLRC cur = wglGetCurrentContext(); + 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, "Failed to load swap interval function"); + } + + wglMakeCurrent(dummy_dc, cur); + wglDeleteContext(dummy_context); + ReleaseDC(dummyWin, dummy_dc); +} + +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_FULL_ACCELERATION_ARB 0x2027 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_TYPE_RGBA_ARB 0x202b +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_ALPHA_BITS_ARB 0x201b +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_STEREO_ARB 0x2012 +#define WGL_AUX_BUFFERS_ARB 0x2024 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_ACCUM_RED_BITS_ARB 0x201e +#define WGL_ACCUM_GREEN_BITS_ARB 0x201f +#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 +#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 +#define WGL_COLORSPACE_SRGB_EXT 0x3089 +#define WGL_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0x0000 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_ACCESS_READ_WRITE_NV 0x00000001 +#define WGL_COVERAGE_SAMPLES_NV 0x2042 +#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20A9 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 + +RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) { + const char flushControl[] = "WGL_ARB_context_flush_control"; + const char noError[] = "WGL_ARB_create_context_no_error"; + const char robustness[] = "WGL_ARB_create_context_robustness"; + + win->src.ctx.native = ctx; + win->src.gfxType = RGFW_gfxNativeOpenGL; + + 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)hints->stencil; + pfd.cAuxBuffers = (BYTE)hints->auxBuffers; + if (hints->stereo) 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 (hints->renderer == RGFW_glSoftware) + 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[50]; + RGFW_attribStack stack; + RGFW_attribStack_init(&stack, pixel_format_attribs, 50); + + RGFW_attribStack_pushAttribs(&stack, WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB); + RGFW_attribStack_pushAttribs(&stack, WGL_DRAW_TO_WINDOW_ARB, 1); + RGFW_attribStack_pushAttribs(&stack, WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB); + RGFW_attribStack_pushAttribs(&stack, WGL_SUPPORT_OPENGL_ARB, 1); + RGFW_attribStack_pushAttribs(&stack, WGL_COLOR_BITS_ARB, 32); + RGFW_attribStack_pushAttribs(&stack, WGL_DOUBLE_BUFFER_ARB, 1); + RGFW_attribStack_pushAttribs(&stack, WGL_ALPHA_BITS_ARB, hints->alpha); + RGFW_attribStack_pushAttribs(&stack, WGL_DEPTH_BITS_ARB, hints->depth); + RGFW_attribStack_pushAttribs(&stack, WGL_STENCIL_BITS_ARB, hints->stencil); + RGFW_attribStack_pushAttribs(&stack, WGL_STEREO_ARB, hints->stereo); + RGFW_attribStack_pushAttribs(&stack, WGL_AUX_BUFFERS_ARB, hints->auxBuffers); + RGFW_attribStack_pushAttribs(&stack, WGL_RED_BITS_ARB, hints->red); + RGFW_attribStack_pushAttribs(&stack, WGL_GREEN_BITS_ARB, hints->blue); + RGFW_attribStack_pushAttribs(&stack, WGL_BLUE_BITS_ARB, hints->green); + RGFW_attribStack_pushAttribs(&stack, WGL_ACCUM_RED_BITS_ARB, hints->accumRed); + RGFW_attribStack_pushAttribs(&stack, WGL_ACCUM_GREEN_BITS_ARB, hints->accumGreen); + RGFW_attribStack_pushAttribs(&stack, WGL_ACCUM_BLUE_BITS_ARB, hints->accumBlue); + RGFW_attribStack_pushAttribs(&stack, WGL_ACCUM_ALPHA_BITS_ARB, hints->accumAlpha); + + if(hints->sRGB) { + if (hints->profile != RGFW_glES) + RGFW_attribStack_pushAttribs(&stack, WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, 1); + else + RGFW_attribStack_pushAttribs(&stack, WGL_COLORSPACE_SRGB_EXT, hints->sRGB); + } + + RGFW_attribStack_pushAttribs(&stack, WGL_COVERAGE_SAMPLES_NV, hints->samples); + + RGFW_attribStack_pushAttribs(&stack, 0, 0); + + 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, "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, "Failed to set the WGL pixel format"); + + if (wglCreateContextAttribsARB != NULL) { + /* create OpenGL/WGL context for the specified version */ + i32 attribs[40]; + RGFW_attribStack stack; + RGFW_attribStack_init(&stack, attribs, 50); + + + i32 mask = 0; + switch (hints->profile) { + case RGFW_glES: mask |= WGL_CONTEXT_ES_PROFILE_BIT_EXT; break; + case RGFW_glCompatibility: mask |= WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; break; + case RGFW_glForwardCompatibility: mask |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; break; + case RGFW_glCore: mask |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB; break; + default: mask |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB; break; + } + + RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_PROFILE_MASK_ARB, mask); + + if (hints->minor || hints->major) { + RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_MAJOR_VERSION_ARB, hints->major); + RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_MINOR_VERSION_ARB, hints->minor); + } + + if (RGFW_extensionSupportedPlatform_OpenGL(noError, sizeof(noError))) + RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_OPENGL_NO_ERROR_ARB, hints->noError); + + if (RGFW_extensionSupportedPlatform_OpenGL(flushControl, sizeof(flushControl))) { + if (hints->releaseBehavior == RGFW_glReleaseFlush) { + RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); /* WGL_CONTEXT_RELEASE_BEHAVIOR_ARB */ + } else if (hints->releaseBehavior == RGFW_glReleaseNone) { + RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); + } + } + + i32 flags = 0; + if (hints->debug) flags |= WGL_CONTEXT_DEBUG_BIT_ARB; + if (hints->robustness && RGFW_extensionSupportedPlatform_OpenGL(robustness, sizeof(robustness))) flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB; + if (flags) { + RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_FLAGS_ARB, flags); + } + + + RGFW_attribStack_pushAttribs(&stack, 0, 0); + + win->src.ctx.native->ctx = (HGLRC)wglCreateContextAttribsARB(win->src.hdc, NULL, attribs); + } + + if (wglCreateContextAttribsARB == NULL || win->src.ctx.native->ctx == NULL) { /* fall back to a default context (probably OpenGL 2 or something) */ + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to create an accelerated OpenGL Context."); + win->src.ctx.native->ctx = wglCreateContext(win->src.hdc); + } + + ReleaseDC(win->src.window, win->src.hdc); + win->src.hdc = GetDC(win->src.window); + + if (hints->share) { + wglShareLists((HGLRC)RGFW_getCurrentContext_OpenGL(), hints->share->ctx); + } + + wglMakeCurrent(win->src.hdc, win->src.ctx.native->ctx); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context initalized."); + return RGFW_TRUE; +} + +void RGFW_window_deleteContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx) { + wglDeleteContext((HGLRC) ctx->ctx); /*!< delete OpenGL context */ + win->src.ctx.native->ctx = NULL; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context freed."); +} + +void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win) { + if (win == NULL) + wglMakeCurrent(NULL, NULL); + else + wglMakeCurrent(win->src.hdc, (HGLRC) win->src.ctx.native->ctx); +} +void* RGFW_getCurrentContext_OpenGL(void) { + return wglGetCurrentContext(); +} +void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) { + RGFW_ASSERT(win->src.ctx.native); + SwapBuffers(win->src.hdc); +} + +void RGFW_window_swapInterval_OpenGL(RGFW_window* win, i32 swapInterval) { + RGFW_ASSERT(win != NULL); + if (wglSwapIntervalEXT == NULL || wglSwapIntervalEXT(swapInterval) == FALSE) + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to set swap interval"); +} +#endif + +RGFW_bool RGFW_createUTF8FromWideStringWin32(const WCHAR* source, char* output, size_t max) { + i32 size = 0; + if (source == NULL) { + return RGFW_FALSE; + } + size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL); + if (!size) { + return RGFW_FALSE; + } + + if (size > (i32)max) + size = (i32)max; + + if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, output, size, NULL, NULL)) { + return RGFW_FALSE; + } + + output[size] = 0; + return RGFW_TRUE; +} + +#ifdef RGFW_WEBGPU +WGPUSurface RGFW_window_createSurface_WebGPU(RGFW_window* window, WGPUInstance instance) { + WGPUSurfaceDescriptor surfaceDesc = {0}; + WGPUSurfaceSourceWindowsHWND fromHwnd = {0}; + fromHwnd.chain.sType = WGPUSType_SurfaceSourceWindowsHWND; + fromHwnd.hwnd = window->src.window; + + fromHwnd.hinstance = GetModuleHandle(NULL); + + surfaceDesc.nextInChain = (WGPUChainedStruct*)&fromHwnd.chain; + return wgpuInstanceCreateSurface(instance, &surfaceDesc); +} +#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 TISInputSourceRef (*PFN_TISCopyCurrentKeyboardLayoutInputSource)(void); +PFN_TISCopyCurrentKeyboardLayoutInputSource TISCopyCurrentKeyboardLayoutInputSourceSrc; +#define TISCopyCurrentKeyboardLayoutInputSource TISCopyCurrentKeyboardLayoutInputSourceSrc + +typedef CFDataRef (*PFN_TISGetInputSourceProperty)(TISInputSourceRef, CFStringRef); +PFN_TISGetInputSourceProperty TISGetInputSourcePropertySrc; +#define TISGetInputSourceProperty TISGetInputSourcePropertySrc + +typedef u8 (*PFN_LMGetKbdType)(void); +PFN_LMGetKbdType LMGetKbdTypeSrc; +#define LMGetKbdType LMGetKbdTypeSrc + +CFStringRef kTISPropertyUnicodeKeyLayoutDataSrc; + +#ifndef __OBJC__ +typedef CGRect NSRect; +typedef CGPoint NSPoint; +typedef CGSize NSSize; + +typedef const char* NSPasteboardType; +typedef unsigned long NSUInteger; +typedef long NSInteger; +typedef NSInteger NSModalResponse; + +typedef enum NSRequestUserAttentionType { + NSCriticalRequest = 0, + NSInformationalRequest = 10 +} NSRequestUserAttentionType; + +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 +}; + +#define NSPasteboardTypeString "public.utf8-plain-text" + +typedef RGFW_ENUM(i32, NSDragOperation) { + NSDragOperationNone = 0, + NSDragOperationCopy = 1, + NSDragOperationLink = 2, + NSDragOperationGeneric = 4, + NSDragOperationPrivate = 8, + NSDragOperationMove = 16, + NSDragOperationDelete = 32, + NSDragOperationEvery = (int)ULONG_MAX +}; + +typedef RGFW_ENUM(NSInteger, NSOpenGLContextParameter) { + 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 */ + 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, +}; + +#define NSPasteboardTypeURL "public.url" +#define NSPasteboardTypeFileURL "public.file-url" +#define NSTrackingMouseEnteredAndExited 0x01 +#define NSTrackingMouseMoved 0x02 +#define NSTrackingCursorUpdate 0x04 +#define NSTrackingActiveWhenFirstResponder 0x10 +#define NSTrackingActiveInKeyWindow 0x20 +#define NSTrackingActiveInActiveApp 0x40 +#define NSTrackingActiveAlways 0x80 +#define NSTrackingAssumeInside 0x100 +#define NSTrackingInVisibleRect 0x200 +#define NSTrackingEnabledDuringMouseDrag 0x400 +enum { + NSOpenGLPFAAllRenderers = 1, /* choose from all available renderers */ + NSOpenGLPFATripleBuffer = 3, /* choose a triple buffered pixel format */ + NSOpenGLPFADoubleBuffer = 5, /* choose a double buffered pixel format */ + NSOpenGLPFAAuxBuffers = 7, /* number of aux buffers */ + NSOpenGLPFAColorSize = 8, /* number of color buffer bits */ + NSOpenGLPFAAlphaSize = 11, /* number of alpha component bits */ + NSOpenGLPFADepthSize = 12, /* number of depth buffer bits */ + NSOpenGLPFAStencilSize = 13, /* number of stencil buffer bits */ + NSOpenGLPFAAccumSize = 14, /* number of accum buffer bits */ + NSOpenGLPFAMinimumPolicy = 51, /* never choose smaller buffers than requested */ + NSOpenGLPFAMaximumPolicy = 52, /* choose largest buffers of type requested */ + NSOpenGLPFASampleBuffers = 55, /* number of multi sample buffers */ + NSOpenGLPFASamples = 56, /* number of samples per multi sample buffer */ + NSOpenGLPFAAuxDepthStencil = 57, /* each aux buffer has its own depth stencil */ + NSOpenGLPFAColorFloat = 58, /* color buffers store floating point pixels */ + NSOpenGLPFAMultisample = 59, /* choose multisampling */ + NSOpenGLPFASupersample = 60, /* choose supersampling */ + NSOpenGLPFASampleAlpha = 61, /* request alpha filtering */ + NSOpenGLPFARendererID = 70, /* request renderer by ID */ + NSOpenGLPFANoRecovery = 72, /* disable all failure recovery systems */ + NSOpenGLPFAAccelerated = 73, /* choose a hardware accelerated renderer */ + NSOpenGLPFAClosestPolicy = 74, /* choose the closest color buffer to request */ + NSOpenGLPFABackingStore = 76, /* back buffer contents are valid after swap */ + NSOpenGLPFAScreenMask = 84, /* bit mask of supported physical screens */ + NSOpenGLPFAAllowOfflineRenderers = 96, /* allow use of offline renderers */ + NSOpenGLPFAAcceleratedCompute = 97, /* choose a hardware accelerated compute device */ + NSOpenGLPFAOpenGLProfile = 99, /* specify an OpenGL Profile to use */ + NSOpenGLProfileVersionLegacy = 0x1000, /* The requested profile is a legacy (pre-OpenGL 3.0) profile. */ + NSOpenGLProfileVersion3_2Core = 0x3200, /* The 3.2 Profile of OpenGL */ + NSOpenGLProfileVersion4_1Core = 0x3200, /* The 4.1 profile of OpenGL */ + NSOpenGLPFAVirtualScreenCount = 128, /* number of virtual screens in this format */ + NSOpenGLPFAStereo = 6, + NSOpenGLPFAOffScreen = 53, + NSOpenGLPFAFullScreen = 54, + NSOpenGLPFASingleRenderer = 71, + NSOpenGLPFARobust = 75, + NSOpenGLPFAMPSafe = 78, + NSOpenGLPFAWindow = 80, + NSOpenGLPFAMultiScreen = 81, + NSOpenGLPFACompliant = 83, + NSOpenGLPFAPixelBuffer = 90, + NSOpenGLPFARemotePixelBuffer = 91, +}; + +typedef RGFW_ENUM(u32, NSEventType) { /* various types of events */ + NSEventTypeApplicationDefined = 15, +}; +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; + +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 = (1 << 8), + NSBitmapFormatThirtyTwoBitLittleEndian = (1 << 9), + NSBitmapFormatSixteenBitBigEndian = (1 << 10), + NSBitmapFormatThirtyTwoBitBigEndian = (1 << 11) +}; + +#else +#import +#include +#endif /* notdef __OBJC__ */ + +#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) + +#define NSRelease(obj) objc_msgSend_void((id)obj, sel_registerName("release")) +RGFWDEF 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); +} + +RGFWDEF float RGFW_cocoaYTransform(float y); +float RGFW_cocoaYTransform(float y) { return (float)(CGDisplayBounds(CGMainDisplayID()).size.height - (double)y - (double)1.0f); } + +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")); +} + +unsigned char* NSBitmapImageRep_bitmapData(id imageRep); +unsigned char* NSBitmapImageRep_bitmapData(id imageRep) { + return ((unsigned char* (*)(id, SEL))objc_msgSend) ((id)imageRep, sel_registerName("bitmapData")); +} + +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) { + Class 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 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((const char*)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) { + return ((id (*)(id, SEL, void*, NSUInteger))objc_msgSend) (NSAlloc(objc_getClass("NSArray")), sel_registerName("initWithObjects:count:"), 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")) + +/* + End of cocoa wrapper +*/ + +static id RGFW__osxCustomInitWithRGFWWindow(id self, SEL _cmd, RGFW_window* win) { + RGFW_UNUSED(_cmd); + struct objc_super s = { self, class_getSuperclass(object_getClass(self)) }; + self = ((id (*)(struct objc_super*, SEL))objc_msgSendSuper)(&s, sel_registerName("init")); + + if (self != nil) { + object_setInstanceVariable(self, "RGFW_window", win); + object_setInstanceVariable(self, "trackingArea", nil); + + object_setInstanceVariable( + self, "markedText", + ((id (*)(id, SEL))objc_msgSend)( + ((id (*)(Class, SEL))objc_msgSend)(objc_getClass("NSMutableAttributedString"), sel_registerName("alloc")), + sel_registerName("init") + ) + ); + + ((void (*)(id, SEL))objc_msgSend)(self, sel_registerName("updateTrackingAreas")); + + ((void (*)(id, SEL, id))objc_msgSend)( + self, sel_registerName("registerForDraggedTypes:"), + ((id (*)(Class, SEL, id))objc_msgSend)( + objc_getClass("NSArray"), + sel_registerName("arrayWithObject:"), + ((id (*)(Class, SEL, const char*))objc_msgSend)( + objc_getClass("NSString"), + sel_registerName("stringWithUTF8String:"), + "public.url" + ) + ) + ); + } + + return self; +} + +static u32 RGFW_OnClose(id self) { + RGFW_window* win = NULL; + object_getInstanceVariable(self, (const char*)"RGFW_window", (void**)&win); + if (win == NULL) return true; + + RGFW_windowQuitCallback(win); + return false; +} + +/* NOTE(EimaMei): Fixes the constant clicking when the app is running under a terminal. */ +static bool RGFW__osxAcceptsFirstResponder(void) { return true; } +static bool RGFW__osxPerformKeyEquivalent(id event) { RGFW_UNUSED(event); return true; } + +static NSDragOperation RGFW__osxDraggingEntered(id self, SEL sel, id sender) { + RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); + + return NSDragOperationCopy; +} +static NSDragOperation RGFW__osxDraggingUpdated(id self, SEL sel, id sender) { + RGFW_UNUSED(sel); + + RGFW_window* win = NULL; + + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) + return 0; + if (!(win->internal.enabledEvents & RGFW_dataDragFlag)) return NSDragOperationCopy; + + NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation")); + RGFW_dataDragCallback(win, (i32) p.x, (i32) (win->h - p.y)); + return NSDragOperationCopy; +} +static bool RGFW__osxPrepareForDragOperation(id self) { + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL || (!(win->internal.enabledEvents & RGFW_dataDropFlag))) + return true; + + if (!(win->internal.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; } + +static bool RGFW__osxPerformDragOperation(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 || (!(win->internal.enabledEvents & RGFW_dataDropFlag))) + 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, "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; + + char** files = (char**)(void*)_RGFW->files; + + u32 i; + for (i = 0; i < (u32)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_STRNCPY(files[i], filePath, RGFW_MAX_PATH - 1); + files[i][RGFW_MAX_PATH - 1] = '\0'; + } + + RGFW_dataDropCallback(win, files, (size_t)count); + + return false; +} + +#ifndef RGFW_NO_IOKIT +#include + +float RGFW_osx_getFallbackRefreshRate(CGDirectDisplayID displayID); +float RGFW_osx_getFallbackRefreshRate(CGDirectDisplayID displayID) { + float refreshRate = 0; + io_iterator_t it; + io_service_t service; + CFNumberRef indexRef, clockRef, countRef; + u32 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) { + u32 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 = (float)((double)clock / (double) count); + CFRelease(countRef); + } + } + CFRelease(clockRef); + } + } + + IOObjectRelease(it); + return refreshRate; +} +#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); +} + +static void RGFW__osxDidChangeScreenParameters(id self, SEL _cmd, id notification) { + RGFW_UNUSED(self); RGFW_UNUSED(_cmd); RGFW_UNUSED(notification); + RGFW_pollMonitors(); +} + +static 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; + + RGFW_windowRestoredCallback(win, win->x, win->y, win->w, win->h); + +} +static 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; + + RGFW_windowMinimizedCallback(win); + +} + +static 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; + + RGFW_focusCallback(win, RGFW_TRUE); +} + +static 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; + + RGFW_focusCallback(win, RGFW_FALSE); +} + +static void RGFW__osxDidWindowResize(id self, SEL _cmd, id notification) { + RGFW_UNUSED(_cmd); RGFW_UNUSED(notification); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + NSRect frame; + if (win->src.view) frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame")); + else return; + + if (frame.size.width == 0 || frame.size.height == 0) return; + win->w = (i32)frame.size.width; + win->h = (i32)frame.size.height; + + RGFW_monitor* mon = RGFW_window_getMonitor(win); + if (mon == NULL) return; + + if ((i32)mon->mode.w == win->w && (i32)mon->mode.h - 102 <= win->h) { + RGFW_windowMaximizedCallback(win, 0, 0, win->w, win->h); + } else if (win->internal.flags & RGFW_windowMaximize) { + RGFW_windowRestoredCallback(win, win->x, win->y, win->w, win->h); + } + + RGFW_windowResizedCallback(win, win->w, win->h); +} + +static 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")); + NSRect content = ((NSRect(*)(id, SEL, NSRect))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("contentRectForFrameRect:"), frame); + + float y = RGFW_cocoaYTransform((float)(content.origin.y + content.size.height - 1)); + + RGFW_windowMovedCallback(win, (i32)content.origin.x, (i32)y); +} + +static 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); + if (mon == NULL) return; + + RGFW_scaleUpdatedCallback(win, mon->scaleX, mon->scaleY); +} + +static BOOL RGFW__osxWantsUpdateLayer(id self, SEL _cmd) { RGFW_UNUSED(self); RGFW_UNUSED(_cmd); return YES; } + +static void RGFW__osxUpdateLayer(id self, SEL _cmd) { + RGFW_UNUSED(self); RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + RGFW_windowRefreshCallback(win); +} + +static 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_windowRefreshCallback(win); +} + +static void RGFW__osxMouseEntered(id self, SEL _cmd, id event) { + RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + NSPoint p = ((NSPoint(*)(id, SEL))objc_msgSend)(event, sel_registerName("locationInWindow")); + RGFW_mouseNotifyCallback(win, (i32)p.x, (i32)(win->h - p.y), 1); +} + +static void RGFW__osxMouseExited(id self, SEL _cmd, id event) { + RGFW_UNUSED(_cmd); RGFW_UNUSED(event); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + RGFW_mouseNotifyCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, RGFW_FALSE); +} + +static void RGFW__osxKeyDown(id self, SEL _cmd, id event) { + RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL || !(win->internal.enabledEvents & RGFW_keyPressedFlag)) return; + + u32 key = (u16)((u32(*)(id, SEL))objc_msgSend)(event, sel_registerName("keyCode")); + + RGFW_key value = (u8)RGFW_apiKeyToRGFW(key); + RGFW_bool repeat = RGFW_window_isKeyPressed(win, value); + + RGFW_keyCallback(win, value, win->internal.mod, repeat, 1); + + id nsstring = ((id(*)(id, SEL))objc_msgSend)(event, sel_registerName("charactersIgnoringModifiers")); + const char* string = NSString_to_char(nsstring); + size_t count = (size_t)((int (*)(id, SEL))objc_msgSend)(nsstring, sel_registerName("length")); + + for (size_t index = 0; index < count; + RGFW_keyCharCallback(win, RGFW_decodeUTF8(&string[index], &index)) + ); +} + +static void RGFW__osxKeyUp(id self, SEL _cmd, id event) { + RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL || !(win->internal.enabledEvents & RGFW_keyReleasedFlag)) return; + + u32 key = (u16)((u32(*)(id, SEL))objc_msgSend)(event, sel_registerName("keyCode")); + + RGFW_key value = (u8)RGFW_apiKeyToRGFW(key); + RGFW_bool repeat = RGFW_window_isKeyDown(win, (u8)value); + + RGFW_keyCallback(win, value, win->internal.mod, repeat, 0); +} + +static void RGFW__osxFlagsChanged(id self, SEL _cmd, id event) { + RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + RGFW_key value = 0; + RGFW_bool pressed = RGFW_FALSE; + + u32 flags = (u32)((u32(*)(id, SEL))objc_msgSend)(event, sel_registerName("modifierFlags")); + RGFW_updateKeyModsEx(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 = _RGFW->keyboard[i + RGFW_capsLock].current; + + for (i = 0; i < 5; i++) { + u32 shift = (1 << (i + 16)); + RGFW_key key = i + RGFW_capsLock; + if ((flags & shift) && !RGFW_window_isKeyDown(win, (u8)key)) { + pressed = RGFW_TRUE; + value = (u8)key; + break; + } + if (!(flags & shift) && RGFW_window_isKeyDown(win, (u8)key)) { + pressed = RGFW_FALSE; + value = (u8)key; + break; + } + } + + RGFW_bool repeat = RGFW_window_isKeyDown(win, (u8)value); + RGFW_keyCallback(win, value, win->internal.mod, repeat, pressed); + + if (value != RGFW_capsLock) { + RGFW_keyCallback(win, value + 4, win->internal.mod, repeat, pressed); + } + +} + +static void RGFW__osxMouseMoved(id self, SEL _cmd, id event) { + RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + NSPoint p = ((NSPoint(*)(id, SEL))objc_msgSend)(event, sel_registerName("locationInWindow")); + + CGFloat vecX = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("deltaX")); + CGFloat vecY = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("deltaY")); + + RGFW_mousePosCallback(win, (i32)p.x, (i32)(win->h - p.y), (float)vecX, (float)vecY); +} + +static void RGFW__osxMouseDown(id self, SEL _cmd, id event) { + RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + u32 buttonNumber = (u32)((u32(*)(id, SEL))objc_msgSend)(event, sel_registerName("buttonNumber")); + + RGFW_mouseButton value = 0; + switch (buttonNumber) { + case 0: value = RGFW_mouseLeft; break; + case 1: value = RGFW_mouseRight; break; + case 2: value = RGFW_mouseMiddle; break; + default: value = (u8)buttonNumber; + } + + RGFW_mouseButtonCallback(win, value, 1); +} + +static void RGFW__osxMouseUp(id self, SEL _cmd, id event) { + RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + u32 buttonNumber = (u32)((u32(*)(id, SEL))objc_msgSend)(event, sel_registerName("buttonNumber")); + + RGFW_mouseButton value = 0; + switch (buttonNumber) { + case 0: value = RGFW_mouseLeft; break; + case 1: value = RGFW_mouseRight; break; + case 2: value = RGFW_mouseMiddle; break; + default: value = (u8)buttonNumber; + } + + RGFW_mouseButtonCallback(win, value, 0); +} + +static void RGFW__osxScrollWheel(id self, SEL _cmd, id event) { + RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + float deltaX = (float)((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("deltaX")); + float deltaY = (float)((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("deltaY")); + + RGFW_mouseScrollCallback(win, deltaX, deltaY); +} + +RGFW_format RGFW_nativeFormat(void) { return RGFW_formatRGBA8; } + +RGFW_bool RGFW_createSurfacePtr(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) { + surface->data = data; + surface->w = w; + surface->h = h; + surface->format = format; + surface->native.format = RGFW_formatRGBA8; + + surface->native.buffer = (u8*)RGFW_ALLOC((size_t)(w * h * 4)); + return RGFW_TRUE; +} + +void RGFW_surface_freePtr(RGFW_surface* surface) { + RGFW_FREE(surface->native.buffer); +} + +void RGFW_window_blitSurface(RGFW_window* win, RGFW_surface* surface) { + id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); + pool = objc_msgSend_id(pool, sel_registerName("init")); + + int minW = RGFW_MIN(win->w, surface->w); + int minH = RGFW_MIN(win->h, surface->h); + + RGFW_monitor* mon = RGFW_window_getMonitor(win); + if (mon == NULL) return; + + minW = (i32)((float)minW * mon->pixelRatio); + minH = (i32)((float)minH * mon->pixelRatio); + + surface->native.rep = (void*)NSBitmapImageRep_initWithBitmapData(&surface->native.buffer, minW, minH, 8, 4, true, false, "NSDeviceRGBColorSpace", 1 << 1, (u32)surface->w * 4, 32); + + id image = ((id (*)(Class, SEL))objc_msgSend)(objc_getClass("NSImage"), sel_getUid("alloc")); + NSSize size = (NSSize){(double)minW, (double)minH}; + image = ((id (*)(id, SEL, NSSize))objc_msgSend)((id)image, sel_getUid("initWithSize:"), size); + + RGFW_copyImageData(NSBitmapImageRep_bitmapData((id)surface->native.rep), surface->w, minH, RGFW_formatRGBA8, surface->data, surface->native.format, surface->convertFunc); + ((void (*)(id, SEL, id))objc_msgSend)((id)image, sel_getUid("addRepresentation:"), (id)surface->native.rep); + + id layer = ((id (*)(id, SEL))objc_msgSend)((id)win->src.view, sel_getUid("layer")); + ((void (*)(id, SEL, id))objc_msgSend)(layer, sel_getUid("setContents:"), (id)image); + + NSRelease(image); + NSRelease(surface->native.rep); + + objc_msgSend_bool_void(pool, sel_registerName("drain")); +} + +void* RGFW_window_getView_OSX(RGFW_window* win) { return win->src.view; } + +void RGFW_window_setLayer_OSX(RGFW_window* win, void* layer) { + objc_msgSend_void_id((id)win->src.view, sel_registerName("setLayer:"), (id)layer); +} + +void* RGFW_getLayer_OSX(void) { + return objc_msgSend_class((id)objc_getClass("CAMetalLayer"), (SEL)sel_registerName("layer")); +} +void* RGFW_window_getWindow_OSX(RGFW_window* win) { return win->src.window; } + +void RGFW_initKeycodesPlatform(void) { + _RGFW->keycodes[0x1D] = RGFW_0; + _RGFW->keycodes[0x12] = RGFW_1; + _RGFW->keycodes[0x13] = RGFW_2; + _RGFW->keycodes[0x14] = RGFW_3; + _RGFW->keycodes[0x15] = RGFW_4; + _RGFW->keycodes[0x17] = RGFW_5; + _RGFW->keycodes[0x16] = RGFW_6; + _RGFW->keycodes[0x1A] = RGFW_7; + _RGFW->keycodes[0x1C] = RGFW_8; + _RGFW->keycodes[0x19] = RGFW_9; + _RGFW->keycodes[0x00] = RGFW_a; + _RGFW->keycodes[0x0B] = RGFW_b; + _RGFW->keycodes[0x08] = RGFW_c; + _RGFW->keycodes[0x02] = RGFW_d; + _RGFW->keycodes[0x0E] = RGFW_e; + _RGFW->keycodes[0x03] = RGFW_f; + _RGFW->keycodes[0x05] = RGFW_g; + _RGFW->keycodes[0x04] = RGFW_h; + _RGFW->keycodes[0x22] = RGFW_i; + _RGFW->keycodes[0x26] = RGFW_j; + _RGFW->keycodes[0x28] = RGFW_k; + _RGFW->keycodes[0x25] = RGFW_l; + _RGFW->keycodes[0x2E] = RGFW_m; + _RGFW->keycodes[0x2D] = RGFW_n; + _RGFW->keycodes[0x1F] = RGFW_o; + _RGFW->keycodes[0x23] = RGFW_p; + _RGFW->keycodes[0x0C] = RGFW_q; + _RGFW->keycodes[0x0F] = RGFW_r; + _RGFW->keycodes[0x01] = RGFW_s; + _RGFW->keycodes[0x11] = RGFW_t; + _RGFW->keycodes[0x20] = RGFW_u; + _RGFW->keycodes[0x09] = RGFW_v; + _RGFW->keycodes[0x0D] = RGFW_w; + _RGFW->keycodes[0x07] = RGFW_x; + _RGFW->keycodes[0x10] = RGFW_y; + _RGFW->keycodes[0x06] = RGFW_z; + _RGFW->keycodes[0x27] = RGFW_apostrophe; + _RGFW->keycodes[0x2A] = RGFW_backSlash; + _RGFW->keycodes[0x2B] = RGFW_comma; + _RGFW->keycodes[0x18] = RGFW_equals; + _RGFW->keycodes[0x32] = RGFW_backtick; + _RGFW->keycodes[0x21] = RGFW_bracket; + _RGFW->keycodes[0x1B] = RGFW_minus; + _RGFW->keycodes[0x2F] = RGFW_period; + _RGFW->keycodes[0x1E] = RGFW_closeBracket; + _RGFW->keycodes[0x29] = RGFW_semicolon; + _RGFW->keycodes[0x2C] = RGFW_slash; + _RGFW->keycodes[0x0A] = RGFW_world1; + _RGFW->keycodes[0x33] = RGFW_backSpace; + _RGFW->keycodes[0x39] = RGFW_capsLock; + _RGFW->keycodes[0x75] = RGFW_delete; + _RGFW->keycodes[0x7D] = RGFW_down; + _RGFW->keycodes[0x77] = RGFW_end; + _RGFW->keycodes[0x24] = RGFW_enter; + _RGFW->keycodes[0x35] = RGFW_escape; + _RGFW->keycodes[0x7A] = RGFW_F1; + _RGFW->keycodes[0x78] = RGFW_F2; + _RGFW->keycodes[0x63] = RGFW_F3; + _RGFW->keycodes[0x76] = RGFW_F4; + _RGFW->keycodes[0x60] = RGFW_F5; + _RGFW->keycodes[0x61] = RGFW_F6; + _RGFW->keycodes[0x62] = RGFW_F7; + _RGFW->keycodes[0x64] = RGFW_F8; + _RGFW->keycodes[0x65] = RGFW_F9; + _RGFW->keycodes[0x6D] = RGFW_F10; + _RGFW->keycodes[0x67] = RGFW_F11; + _RGFW->keycodes[0x6F] = RGFW_F12; + _RGFW->keycodes[0x69] = RGFW_printScreen; + _RGFW->keycodes[0x6B] = RGFW_F14; + _RGFW->keycodes[0x71] = RGFW_F15; + _RGFW->keycodes[0x6A] = RGFW_F16; + _RGFW->keycodes[0x40] = RGFW_F17; + _RGFW->keycodes[0x4F] = RGFW_F18; + _RGFW->keycodes[0x50] = RGFW_F19; + _RGFW->keycodes[0x5A] = RGFW_F20; + _RGFW->keycodes[0x73] = RGFW_home; + _RGFW->keycodes[0x72] = RGFW_insert; + _RGFW->keycodes[0x7B] = RGFW_left; + _RGFW->keycodes[0x3A] = RGFW_altL; + _RGFW->keycodes[0x3B] = RGFW_controlL; + _RGFW->keycodes[0x38] = RGFW_shiftL; + _RGFW->keycodes[0x37] = RGFW_superL; + _RGFW->keycodes[0x6E] = RGFW_menu; + _RGFW->keycodes[0x47] = RGFW_numLock; + _RGFW->keycodes[0x79] = RGFW_pageDown; + _RGFW->keycodes[0x74] = RGFW_pageUp; + _RGFW->keycodes[0x7C] = RGFW_right; + _RGFW->keycodes[0x3D] = RGFW_altR; + _RGFW->keycodes[0x3E] = RGFW_controlR; + _RGFW->keycodes[0x3C] = RGFW_shiftR; + _RGFW->keycodes[0x36] = RGFW_superR; + _RGFW->keycodes[0x31] = RGFW_space; + _RGFW->keycodes[0x30] = RGFW_tab; + _RGFW->keycodes[0x7E] = RGFW_up; + _RGFW->keycodes[0x52] = RGFW_kp0; + _RGFW->keycodes[0x53] = RGFW_kp1; + _RGFW->keycodes[0x54] = RGFW_kp2; + _RGFW->keycodes[0x55] = RGFW_kp3; + _RGFW->keycodes[0x56] = RGFW_kp4; + _RGFW->keycodes[0x57] = RGFW_kp5; + _RGFW->keycodes[0x58] = RGFW_kp6; + _RGFW->keycodes[0x59] = RGFW_kp7; + _RGFW->keycodes[0x5B] = RGFW_kp8; + _RGFW->keycodes[0x5C] = RGFW_kp9; + _RGFW->keycodes[0x45] = RGFW_kpSlash; + _RGFW->keycodes[0x41] = RGFW_kpPeriod; + _RGFW->keycodes[0x4B] = RGFW_kpSlash; + _RGFW->keycodes[0x4C] = RGFW_kpReturn; + _RGFW->keycodes[0x51] = RGFW_kpEqual; + _RGFW->keycodes[0x43] = RGFW_kpMultiply; + _RGFW->keycodes[0x4E] = RGFW_kpMinus; +} + +i32 RGFW_initPlatform(void) { + _RGFW->tisBundle = (void*)CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIToolbox")); + + TISGetInputSourcePropertySrc = (PFN_TISGetInputSourceProperty)CFBundleGetFunctionPointerForName((CFBundleRef)_RGFW->tisBundle, CFSTR("TISGetInputSourceProperty")); + TISCopyCurrentKeyboardLayoutInputSourceSrc = (PFN_TISCopyCurrentKeyboardLayoutInputSource)CFBundleGetFunctionPointerForName((CFBundleRef)_RGFW->tisBundle, CFSTR("TISCopyCurrentKeyboardLayoutInputSource")); + LMGetKbdTypeSrc = (PFN_LMGetKbdType)CFBundleGetFunctionPointerForName((CFBundleRef)_RGFW->tisBundle, CFSTR("LMGetKbdType")); + + CFStringRef* cfStr = (CFStringRef*)CFBundleGetDataPointerForName((CFBundleRef)_RGFW->tisBundle, CFSTR("kTISPropertyUnicodeKeyLayoutData"));; + if (cfStr) kTISPropertyUnicodeKeyLayoutDataSrc = *cfStr; + + class_addMethod(objc_getClass("NSObject"), sel_registerName("windowShouldClose:"), (IMP)(void*)RGFW_OnClose, 0); + + /* NOTE(EimaMei): Fixes the 'Boop' sfx from constantly playing each time you click a key. Only a problem when running in the terminal. */ + class_addMethod(objc_getClass("NSWindowClass"), sel_registerName("acceptsFirstResponder:"), (IMP)(void*)RGFW__osxAcceptsFirstResponder, 0); + class_addMethod(objc_getClass("NSWindowClass"), sel_registerName("performKeyEquivalent:"), (IMP)(void*)RGFW__osxPerformKeyEquivalent, 0); + + _RGFW->NSApp = objc_msgSend_id(objc_getClass("NSApplication"), sel_registerName("sharedApplication")); + + NSRetain(_RGFW->NSApp); + + _RGFW->customNSAppDelegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "RGFWNSAppDelegate", 0); + class_addMethod((Class)_RGFW->customNSAppDelegateClass, sel_registerName("applicationDidChangeScreenParameters:"), (IMP)RGFW__osxDidChangeScreenParameters, "v@:@"); + objc_registerClassPair((Class)_RGFW->customNSAppDelegateClass); + _RGFW->customNSAppDelegate = objc_msgSend_id(NSAlloc(_RGFW->customNSAppDelegateClass), sel_registerName("init")); + + objc_msgSend_void_id(_RGFW->NSApp, sel_registerName("setDelegate:"), _RGFW->customNSAppDelegate); + + ((void (*)(id, SEL, NSUInteger))objc_msgSend) ((id)_RGFW->NSApp, sel_registerName("setActivationPolicy:"), NSApplicationActivationPolicyRegular); + + _RGFW->customViewClasses[0] = objc_allocateClassPair(objc_getClass("NSView"), "RGFWCustomView", 0); + _RGFW->customViewClasses[1] = objc_allocateClassPair(objc_getClass("NSOpenGLView"), "RGFWOpenGLCustomView", 0); + for (size_t i = 0; i < 2; i++) { + class_addIvar((Class)_RGFW->customViewClasses[i], "RGFW_window", sizeof(RGFW_window*), sizeof(RGFW_window*), "L"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("drawRect:"), (IMP)RGFW__osxDrawRect, "v@:{CGRect=ffff}"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("viewDidChangeBackingProperties"), (IMP)RGFW__osxViewDidChangeBackingProperties, "v@:"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseDown:"), (IMP)RGFW__osxMouseDown, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("rightMouseDown:"), (IMP)RGFW__osxMouseDown, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("otherMouseDown:"), (IMP)RGFW__osxMouseDown, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseUp:"), (IMP)RGFW__osxMouseUp, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("rightMouseUp:"), (IMP)RGFW__osxMouseUp, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("otherMouseUp:"), (IMP)RGFW__osxMouseUp, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("scrollWheel:"), (IMP)RGFW__osxScrollWheel, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseDragged:"), (IMP)RGFW__osxMouseMoved, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("rightMouseDragged:"), (IMP)RGFW__osxMouseMoved, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("otherMouseDragged:"), (IMP)RGFW__osxMouseMoved, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("keyDown:"), (IMP)RGFW__osxKeyDown, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("keyUp:"), (IMP)RGFW__osxKeyUp, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseMoved:"), (IMP)RGFW__osxMouseMoved, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseEntered:"), (IMP)RGFW__osxMouseEntered, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseExited:"), (IMP)RGFW__osxMouseExited, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("flagsChanged:"), (IMP)RGFW__osxFlagsChanged, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_getUid("acceptsFirstResponder"), (IMP)RGFW__osxAcceptsFirstResponder, "B@:"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("initWithRGFWWindow:"), (IMP)RGFW__osxCustomInitWithRGFWWindow, "@@:{CGRect={CGPoint=dd}{CGSize=dd}}"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("wantsUpdateLayer"), (IMP)RGFW__osxWantsUpdateLayer, "B@:"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("updateLayer"), (IMP)RGFW__osxUpdateLayer, "v@:"); + objc_registerClassPair((Class)_RGFW->customViewClasses[i]); + } + + _RGFW->customWindowDelegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "RGFWWindowDelegate", 0); + class_addIvar((Class)_RGFW->customWindowDelegateClass, "RGFW_window", sizeof(RGFW_window*), sizeof(RGFW_window*), "L"); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidResize:"), (IMP)RGFW__osxDidWindowResize, "v@:@"); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidMove:"), (IMP) RGFW__osxWindowMove, ""); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidMiniaturize:"), (IMP) RGFW__osxWindowMiniaturize, ""); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidDeminiaturize:"), (IMP) RGFW__osxWindowDeminiaturize, ""); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidBecomeKey:"), (IMP) RGFW__osxWindowBecameKey, ""); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidResignKey:"), (IMP) RGFW__osxWindowResignKey, ""); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("draggingEntered:"), (IMP)RGFW__osxDraggingEntered, "l@:@"); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("draggingUpdated:"), (IMP)RGFW__osxDraggingUpdated, "l@:@"); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("draggingExited:"), (IMP)RGFW__osxDraggingEnded, "v@:@"); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("draggingEnded:"), (IMP)RGFW__osxDraggingEnded, "v@:@"); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("prepareForDragOperation:"), (IMP)RGFW__osxPrepareForDragOperation, "B@:@"); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("performDragOperation:"), (IMP)RGFW__osxPerformDragOperation, "B@:@"); + objc_registerClassPair((Class)_RGFW->customWindowDelegateClass); + return 0; +} + +void RGFW_osx_initView(RGFW_window* win) { + NSRect contentRect; + contentRect.origin.x = 0; + contentRect.origin.y = 0; + contentRect.size.width = (double)win->w; + contentRect.size.height = (double)win->h; + ((void(*)(id, SEL, CGRect))objc_msgSend)((id)win->src.view, sel_registerName("setFrame:"), contentRect); + + + if (RGFW_COCOA_FRAME_NAME) + objc_msgSend_ptr(win->src.view, sel_registerName("setFrameAutosaveName:"), RGFW_COCOA_FRAME_NAME); + + object_setInstanceVariable((id)win->src.view, "RGFW_window", win); + objc_msgSend_void_id((id)win->src.window, sel_registerName("setContentView:"), win->src.view); + objc_msgSend_void_bool(win->src.view, sel_registerName("setWantsLayer:"), true); + objc_msgSend_int((id)win->src.view, sel_registerName("setLayerContentsPlacement:"), 4); + + id trackingArea = objc_msgSend_id(objc_getClass("NSTrackingArea"), sel_registerName("alloc")); + trackingArea = ((id (*)(id, SEL, NSRect, NSUInteger, id, id))objc_msgSend)( + trackingArea, + sel_registerName("initWithRect:options:owner:userInfo:"), + contentRect, + NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect, + (id)win->src.view, + nil + ); + + ((void (*)(id, SEL, id))objc_msgSend)((id)win->src.view, sel_registerName("addTrackingArea:"), trackingArea); + ((void (*)(id, SEL))objc_msgSend)(trackingArea, sel_registerName("release")); +} + +RGFW_window* RGFW_createWindowPlatform(const char* name, RGFW_windowFlags flags, RGFW_window* win) { + /* 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 = (double)win->x; + windowRect.origin.y = (double)RGFW_cocoaYTransform((float)(win->y + win->h - 1)); + windowRect.size.width = (double)win->w; + windowRect.size.height = (double)win->h; + NSBackingStoreType macArgs = (NSBackingStoreType)(NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSBackingStoreBuffered | NSWindowStyleMaskTitled); + + if (!(flags & RGFW_windowNoResize)) + macArgs = (NSBackingStoreType)(macArgs | (NSBackingStoreType)NSWindowStyleMaskResizable); + if (!(flags & RGFW_windowNoBorder)) + macArgs = (NSBackingStoreType)(macArgs | (NSBackingStoreType)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, (NSWindowStyleMask)macArgs, macArgs, false); + } + + id str = NSString_stringWithUTF8String(name); + objc_msgSend_void_id((id)win->src.window, sel_registerName("setTitle:"), str); + + win->src.delegate = (void*)objc_msgSend_id(NSAlloc((Class)_RGFW->customWindowDelegateClass), sel_registerName("init")); + object_setInstanceVariable((id)win->src.delegate, "RGFW_window", win); + + objc_msgSend_void_id((id)win->src.window, sel_registerName("setDelegate:"), (id)win->src.delegate); + + if (flags & RGFW_windowAllowDND) { + win->internal.flags |= RGFW_windowAllowDND; + + NSPasteboardType types[] = {NSPasteboardTypeURL, NSPasteboardTypeFileURL, NSPasteboardTypeString}; + NSregisterForDraggedTypes((id)win->src.window, types, 3); + } + + objc_msgSend_void_bool((id)win->src.window, sel_registerName("setAcceptsMouseMovedEvents:"), true); + + 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)); + } + + /* Show the window */ + objc_msgSend_void_bool((id)_RGFW->NSApp, sel_registerName("activateIgnoringOtherApps:"), true); + + if (_RGFW->root == NULL) { + objc_msgSend_void(win->src.window, sel_registerName("makeMainWindow")); + } + + objc_msgSend_void(win->src.window, sel_registerName("makeKeyWindow")); + + NSRetain(win->src.window); + + win->src.view = ((id(*)(id, SEL, RGFW_window*))objc_msgSend) (NSAlloc((Class)_RGFW->customViewClasses[0]), sel_registerName("initWithRGFWWindow:"), win); + 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")); + double offset = 0; + + RGFW_setBit(&win->internal.flags, RGFW_windowNoBorder, !border); + NSBackingStoreType storeType = (NSBackingStoreType)(NSWindowStyleMaskBorderless | NSWindowStyleMaskFullSizeContentView); + if (border) + storeType = (NSBackingStoreType)(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable); + if (!(win->internal.flags & RGFW_windowNoResize)) { + storeType = (NSBackingStoreType)(storeType | (NSBackingStoreType)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 = (double)(frame.size.height - content.size.height); + } + + RGFW_window_resize(win, win->w, win->h + (i32)offset); + win->h -= (i32)offset; +} + +RGFW_bool RGFW_getGlobalMouse(i32* x, i32* y) { + RGFW_ASSERT(_RGFW->root != NULL); + + CGEventRef e = CGEventCreate(NULL); + CGPoint point = CGEventGetLocation(e); + CFRelease(e); + + if (x) *x = (i32)point.x; + if (y) *y = (i32)point.y; + return RGFW_TRUE; +} + +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) + ((id)_RGFW->NSApp, sel_registerName("postEvent:atStart:"), e, 1); + + objc_msgSend_bool_void(eventPool, sel_registerName("drain")); +} + +void RGFW_waitForEvent(i32 waitMS) { + 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) + ((id)_RGFW->NSApp, eventFunc, + ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true); + + if (e) { + ((void (*)(id, SEL, id, bool))objc_msgSend) + ((id)_RGFW->NSApp, sel_registerName("postEvent:atStart:"), e, 1); + } + + objc_msgSend_bool_void(eventPool, sel_registerName("drain")); +} + +RGFW_key RGFW_physicalToMappedKey(RGFW_key key) { + u16 keycode = (u16)RGFW_rgfwToApiKey(key); + TISInputSourceRef source = TISCopyCurrentKeyboardLayoutInputSource(); + if (source == NULL) + return key; + + CFDataRef layoutData = TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutDataSrc); + + if (layoutData == NULL) { + CFRelease(source); + return key; + } + + UCKeyboardLayout *layout = (UCKeyboardLayout*)(void*)CFDataGetBytePtr(layoutData); + + UInt32 deadKeyState = 0; + UniChar chars[4]; + UniCharCount len = 0; + u32 type = LMGetKbdType(); + OSStatus status = UCKeyTranslate(layout, keycode, kUCKeyActionDown, 0, type, kUCKeyTranslateNoDeadKeysBit, &deadKeyState, 4, &len, chars ); + + CFRelease(source); + + if (status == noErr && len == 1 && chars[0] < 256) { + return (RGFW_key)chars[0]; + } + + return key; +} + +void RGFW_pollEvents(void) { + RGFW_resetPrevState(); + + 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:"); + + while (1) { + void* date = NULL; + id e = (id) ((id(*)(id, SEL, NSEventMask, void*, id, bool))objc_msgSend) + ((id)_RGFW->NSApp, eventFunc, ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true); + + if (e == NULL) { + break; + } + + objc_msgSend_void_id((id)_RGFW->NSApp, sel_registerName("sendEvent:"), e); + } + + objc_msgSend_bool_void(eventPool, sel_registerName("drain")); +} + + +void RGFW_window_move(RGFW_window* win, i32 x, i32 y) { + RGFW_ASSERT(win != NULL); + + NSRect content = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame")); + + win->x = x; + win->y = (i32)RGFW_cocoaYTransform((float)y + (float)content.size.height - 1.0f); + + ((void(*)(id,SEL,NSPoint))objc_msgSend)((id)win->src.window, sel_registerName("setFrameOrigin:"), (NSPoint){(double)x, (double)y}); +} + +void RGFW_window_resize(RGFW_window* win, i32 w, i32 h) { + 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->w = w; + win->h = h; + + + ((void(*)(id, SEL, CGRect))objc_msgSend)((id)win->src.view, sel_registerName("setFrame:"), (NSRect){{0, 0}, {(double)win->w, (double)win->h}}); + ((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend) + ((id)win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{(double)win->x, (double)win->y}, {(double)win->w, (double)win->h + (double)offset}}, true, true); +} + +void RGFW_window_focus(RGFW_window* win) { + RGFW_ASSERT(win); + objc_msgSend_void_bool((id)_RGFW->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->internal.flags & RGFW_windowFullscreen)) return; + if (!fullscreen && !(win->internal.flags & RGFW_windowFullscreen)) return; + + if (fullscreen) { + win->internal.oldX = win->x; + win->internal.oldY = win->y; + win->internal.oldW = win->w; + win->internal.oldH = win->h; + + win->internal.flags |= RGFW_windowFullscreen; + + RGFW_monitor* mon = RGFW_window_getMonitor(win); + RGFW_monitor_scaleToWindow(mon, win); + + RGFW_window_setBorder(win, RGFW_FALSE); + + if (mon != NULL) { + win->x = mon->x; + win->y = mon->y; + win->w = mon->mode.w; + win->h = mon->mode.h; + RGFW_window_resize(win, mon->mode.w, mon->mode.h); + RGFW_window_move(win, mon->x, mon->y); + } + + ((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:"), 25); + } + + objc_msgSend_void_SEL(win->src.window, sel_registerName("toggleFullScreen:"), NULL); + + if (!fullscreen) { + win->x = win->internal.oldX; + win->y = win->internal.oldY; + win->w = win->internal.oldW; + win->h = win->internal.oldH; + win->internal.flags &= ~(u32)RGFW_windowFullscreen; + + RGFW_window_resize(win, win->w, win->h); + RGFW_window_move(win, win->x, win->y); + } +} + +void RGFW_window_maximize(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + if (RGFW_window_isMaximized(win)) return; + + win->internal.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); + if (name == NULL) name = "\0"; + + 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, i32 w, i32 h) { + if (w == 0 && h == 0) { w = 1; h = 1; }; + + ((void (*)(id, SEL, NSSize))objc_msgSend) + ((id)win->src.window, sel_registerName("setContentAspectRatio:"), (NSSize){(CGFloat)w, (CGFloat)h}); +} + +void RGFW_window_setMinSize(RGFW_window* win, i32 w, i32 h) { + ((void (*)(id, SEL, NSSize))objc_msgSend) ((id)win->src.window, sel_registerName("setMinSize:"), (NSSize){(CGFloat)w, (CGFloat)h}); +} + +void RGFW_window_setMaxSize(RGFW_window* win, i32 w, i32 h) { + if (w == 0 && h == 0) { + RGFW_monitor* mon = RGFW_window_getMonitor(win); + if (mon != NULL) { + w = mon->mode.w; + h = mon->mode.h; + } + } + + ((void (*)(id, SEL, NSSize))objc_msgSend) + ((id)win->src.window, sel_registerName("setMaxSize:"), (NSSize){(CGFloat)w, (CGFloat)h}); +} + +RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_icon type) { + RGFW_ASSERT(win != NULL); + RGFW_UNUSED(type); + + id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); + pool = objc_msgSend_id(pool, sel_registerName("init")); + + if (data == NULL) { + objc_msgSend_void_id((id)_RGFW->NSApp, sel_registerName("setApplicationIconImage:"), NULL); + objc_msgSend_bool_void(pool, sel_registerName("drain")); + return RGFW_TRUE; + } + + id representation = NSBitmapImageRep_initWithBitmapData(NULL, w, h, 8, (NSInteger)4, true, false, "NSCalibratedRGBColorSpace", 1 << 1, w * 4, 32); + RGFW_copyImageData(NSBitmapImageRep_bitmapData(representation), w, h, RGFW_formatRGBA8, data, format, NULL); + + id dock_image = ((id(*)(id, SEL, NSSize))objc_msgSend) (NSAlloc((id)objc_getClass("NSImage")), sel_registerName("initWithSize:"), ((NSSize){(CGFloat)w, (CGFloat)h})); + + objc_msgSend_void_id(dock_image, sel_registerName("addRepresentation:"), representation); + + objc_msgSend_void_id((id)_RGFW->NSApp, sel_registerName("setApplicationIconImage:"), dock_image); + + NSRelease(dock_image); + NSRelease(representation); + + objc_msgSend_bool_void(pool, sel_registerName("drain")); + + return RGFW_TRUE; +} + +id NSCursor_arrowStr(const char* str); +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* data, i32 w, i32 h, RGFW_format format) { + id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); + pool = objc_msgSend_id(pool, sel_registerName("init")); + + if (data == NULL) { + objc_msgSend_void(NSCursor_arrowStr("arrowCursor"), sel_registerName("set")); + + objc_msgSend_bool_void(pool, sel_registerName("drain")); + return NULL; + } + + id representation = (id)NSBitmapImageRep_initWithBitmapData(NULL, w, h, 8, (NSInteger)4, true, false, "NSCalibratedRGBColorSpace", 1 << 1, w * 4, 32); + RGFW_copyImageData(NSBitmapImageRep_bitmapData(representation), w, h, RGFW_formatRGBA8, data, format, NULL); + + id cursor_image = ((id(*)(id, SEL, NSSize))objc_msgSend) (NSAlloc((id)objc_getClass("NSImage")), sel_registerName("initWithSize:"), ((NSSize){(CGFloat)w, (CGFloat)h})); + + objc_msgSend_void_id(cursor_image, sel_registerName("addRepresentation:"), representation); + + 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}); + + NSRelease(cursor_image); + NSRelease(representation); + + objc_msgSend_bool_void(pool, sel_registerName("drain")); + + 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 stdMouse) { + const char* cursorSelectorStr; + switch (stdMouse) { + case RGFW_mouseNormal: cursorSelectorStr = "arrowCursor"; break; + case RGFW_mouseArrow: cursorSelectorStr = "arrowCursor"; break; + case RGFW_mouseIbeam: cursorSelectorStr = "IBeamCursor"; break; + case RGFW_mouseCrosshair: cursorSelectorStr = "crosshairCursor"; break; + case RGFW_mousePointingHand: cursorSelectorStr = "pointingHandCursor"; break; + case RGFW_mouseResizeEW: cursorSelectorStr = "resizeLeftRightCursor"; break; + case RGFW_mouseResizeE: cursorSelectorStr = "resizeLeftRightCursor"; break; + case RGFW_mouseResizeW: cursorSelectorStr = "resizeLeftRightCursor"; break; + case RGFW_mouseResizeNS: cursorSelectorStr = "resizeUpDownCursor"; break; + case RGFW_mouseResizeN: cursorSelectorStr = "resizeUpDownCursor"; break; + case RGFW_mouseResizeS: cursorSelectorStr = "resizeUpDownCursor"; break; + case RGFW_mouseResizeNWSE: cursorSelectorStr = "_windowResizeNorthWestSouthEastCursor"; break; + case RGFW_mouseResizeNW: cursorSelectorStr = "_windowResizeNorthWestSouthEastCursor"; break; + case RGFW_mouseResizeSE: cursorSelectorStr = "_windowResizeNorthWestSouthEastCursor"; break; + case RGFW_mouseResizeNESW: cursorSelectorStr = "_windowResizeNorthEastSouthWestCursor"; break; + case RGFW_mouseResizeNE: cursorSelectorStr = "_windowResizeNorthEastSouthWestCursor"; break; + case RGFW_mouseResizeSW: cursorSelectorStr = "_windowResizeNorthEastSouthWestCursor"; break; + case RGFW_mouseResizeAll: cursorSelectorStr = "openHandCursor"; break; + case RGFW_mouseNotAllowed: cursorSelectorStr = "operationNotAllowedCursor"; break; + case RGFW_mouseWait: cursorSelectorStr = "arrowCursor"; break; + case RGFW_mouseProgress: cursorSelectorStr = "arrowCursor"; break; + default: + return RGFW_FALSE; + } + + id mouse = NSCursor_arrowStr(cursorSelectorStr); + + 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_window_setRawMouseModePlatform(RGFW_window* win, RGFW_bool state) { + RGFW_UNUSED(win); RGFW_UNUSED(state); +} + +void RGFW_window_captureMousePlatform(RGFW_window* win, RGFW_bool state) { + RGFW_UNUSED(win); + CGAssociateMouseAndMouseCursorPosition(!(state == RGFW_TRUE)); +} + +void RGFW_window_moveMouse(RGFW_window* win, i32 x, i32 y) { + RGFW_UNUSED(win); + + win->internal.lastMouseX = x - win->x; + win->internal.lastMouseY = y - win->y; + CGWarpMouseCursorPosition((CGPoint){(CGFloat)x, (CGFloat)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->internal.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); +} + +void RGFW_window_flash(RGFW_window* win, RGFW_flashRequest request) { + if (RGFW_window_isInFocus(win) && request) { + return; + } + + id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); + pool = objc_msgSend_id(pool, sel_registerName("init")); + + if (_RGFW->flash) { + ((void (*)(id, SEL, NSInteger))objc_msgSend) ((id)_RGFW->NSApp, sel_registerName("cancelUserAttentionRequest:"), _RGFW->flash); + } + + switch (request) { + case RGFW_flashBriefly: + _RGFW->flash = ((NSInteger (*)(id, SEL, NSInteger))objc_msgSend) ((id)_RGFW->NSApp, sel_registerName("requestUserAttention:"), NSInformationalRequest); + break; + case RGFW_flashUntilFocused: + _RGFW->flash = ((NSInteger (*)(id, SEL, NSInteger))objc_msgSend) ((id)_RGFW->NSApp, sel_registerName("requestUserAttention:"), NSCriticalRequest); + break; + default: break; + } + + objc_msgSend_bool_void(pool, sel_registerName("drain")); +} + +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; +} + +RGFWDEF id RGFW_getNSScreenForDisplayUInt(u32 uintNum); +id RGFW_getNSScreenForDisplayUInt(u32 uintNum) { + 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 (CGDisplayUnitNumber((CGDirectDisplayID)objc_msgSend_uint(screenNumber, sel_registerName("unsignedIntValue"))) == uintNum) { + return screen; + } + } + + return NULL; +} + +float RGFW_osx_getRefreshRate(CGDirectDisplayID display, CGDisplayModeRef mode); +float RGFW_osx_getRefreshRate(CGDirectDisplayID display, CGDisplayModeRef mode) { + if (mode) { + float refreshRate = (float)CGDisplayModeGetRefreshRate(mode); + if (refreshRate != 0) return refreshRate; + } + +#ifndef RGFW_NO_IOKIT + float res = RGFW_osx_getFallbackRefreshRate(display); + if (res != 0) return res; +#else + RGFW_UNUSED(display); +#endif + return 60; +} + +void RGFW_pollMonitors(void) { + static CGDirectDisplayID displays[RGFW_MAX_MONITORS]; + u32 count; + + if (CGGetActiveDisplayList(RGFW_MAX_MONITORS, displays, &count) != kCGErrorSuccess) { + return; + } + + if (count > RGFW_MAX_MONITORS) count = RGFW_MAX_MONITORS; + + for (RGFW_monitorNode* node = _RGFW->monitors.list.head; node; node = node->next) { + node->disconnected = RGFW_TRUE; + } + + CGDirectDisplayID primary = CGMainDisplayID(); + + u32 i; + for (i = 0; i < count; i++) { + RGFW_monitor monitor; + + u32 uintNum = CGDisplayUnitNumber(displays[i]); + id screen = RGFW_getNSScreenForDisplayUInt(uintNum); + + RGFW_monitorNode* node; + for (node = _RGFW->monitors.list.head; node; node = node->next) { + if (node->uintNum == uintNum) break; + } + + if (node) { + node->screen = (void*)screen; + node->display = displays[i]; + node->disconnected = RGFW_FALSE; + if (displays[i] == primary) { + _RGFW->monitors.primary = node; + } + continue; + } + + const char name[] = "MacOS\0"; + RGFW_MEMCPY(monitor.name, name, 6); + + CGRect bounds = CGDisplayBounds(displays[i]); + monitor.x = (i32)bounds.origin.x; + monitor.y = (i32)RGFW_cocoaYTransform((float)(bounds.origin.y + bounds.size.height - 1)); + + CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displays[i]); + monitor.mode.w = (i32)CGDisplayModeGetWidth(mode); + monitor.mode.h = (i32)CGDisplayModeGetHeight(mode); + monitor.mode.src = (void*)mode; + monitor.mode.red = 8; monitor.mode.green = 8; monitor.mode.blue = 8; + + monitor.mode.refreshRate = RGFW_osx_getRefreshRate(displays[i], mode); + CFRelease(mode); + + CGSize screenSizeMM = CGDisplayScreenSize(displays[i]); + monitor.physW = (float)screenSizeMM.width / 25.4f; + monitor.physH = (float)screenSizeMM.height / 25.4f; + + float ppi_width = ((float)monitor.mode.w / monitor.physW); + float ppi_height = ((float)monitor.mode.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 = ((((float) (ppi_width) / dpi) * 10.0f)) / 10.0f; + monitor.scaleY = ((((float) (ppi_height) / dpi) * 10.0f)) / 10.0f; + + node = RGFW_monitors_add(&monitor); + + node->screen = (void*)screen; + node->uintNum = uintNum; + node->display = displays[i]; + + if (displays[i] == primary) { + _RGFW->monitors.primary = node; + } + + RGFW_monitorCallback(_RGFW->root, &node->mon, RGFW_TRUE); + } + + RGFW_monitors_refresh(); +} + +RGFW_bool RGFW_monitor_getWorkarea(RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height) { + NSRect frameRect = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)monitor->node->screen, sel_registerName("visibleFrame")); + + if (x) *x = (i32)frameRect.origin.x; + if (y) *y = (i32)RGFW_cocoaYTransform((float)(frameRect.origin.y + frameRect.size.height - (double)1.0f)); + if (width) *width = (i32)frameRect.size.width; + if (height) *height = (i32)frameRect.size.height; + + return RGFW_TRUE; +} + +size_t RGFW_monitor_getGammaRampPtr(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) { + id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); + pool = objc_msgSend_id(pool, sel_registerName("init")); + + u32 size = CGDisplayGammaTableCapacity(monitor->node->display); + CGGammaValue* values = (CGGammaValue*)RGFW_ALLOC(size * 3 * sizeof(CGGammaValue)); + + CGGetDisplayTransferByTable(monitor->node->display, size, values, values + size, values + size * 2, &size); + + for (u32 i = 0; ramp && i < size; i++) { + ramp->red[i] = (u16) (values[i] * 65535); + ramp->green[i] = (u16) (values[i + size] * 65535); + ramp->blue[i] = (u16) (values[i + size * 2] * 65535); + } + + RGFW_FREE(values); + + objc_msgSend_bool_void(pool, sel_registerName("drain")); + return size; +} + +RGFW_bool RGFW_monitor_setGammaRamp(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) { + id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); + pool = objc_msgSend_id(pool, sel_registerName("init")); + + CGGammaValue* values = (CGGammaValue*)RGFW_ALLOC(ramp->count * 3 * sizeof(CGGammaValue)); + + for (u32 i = 0; i < ramp->count; i++) { + values[i] = ramp->red[i] / 65535.f; + values[i + ramp->count] = ramp->green[i] / 65535.f; + values[i + ramp->count * 2] = ramp->blue[i] / 65535.f; + } + + CGSetDisplayTransferByTable(monitor->node->display, (u32)ramp->count, values, values + ramp->count, values + ramp->count * 2); + + RGFW_FREE(values); + + objc_msgSend_bool_void(pool, sel_registerName("drain")); + + return RGFW_TRUE; +} + +size_t RGFW_monitor_getModesPtr(RGFW_monitor* mon, RGFW_monitorMode** modes) { + CGDirectDisplayID display = mon->node->display; + CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, NULL); + + if (allModes == NULL) { + return RGFW_FALSE; + } + + size_t count = (size_t)CFArrayGetCount(allModes); + + CFIndex i; + for (i = 0; i < (CFIndex)count && modes; i++) { + CGDisplayModeRef cmode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i); + + RGFW_monitorMode foundMode; + foundMode.w = (i32)CGDisplayModeGetWidth(cmode); + foundMode.h = (i32)CGDisplayModeGetHeight(cmode); + foundMode.refreshRate = RGFW_osx_getRefreshRate(display, cmode); + foundMode.red = 8; foundMode.green = 8; foundMode.blue = 8; + foundMode.src = (void*)cmode; + (*modes)[i] = foundMode; + } + + CFRelease(allModes); + return count; +} + +RGFW_bool RGFW_monitor_setMode(RGFW_monitor* mon, RGFW_monitorMode* mode) { + if (CGDisplaySetDisplayMode(mon->node->display, (CGDisplayModeRef)mode->src, NULL) == kCGErrorSuccess) { + return RGFW_TRUE; + } + + return RGFW_FALSE; +} + +RGFW_bool RGFW_monitor_requestMode(RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request) { + CGDirectDisplayID display = mon->node->display; + CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, NULL); + + if (allModes == NULL) { + return RGFW_FALSE; + } + + CGDisplayModeRef native = NULL; + + CFIndex i; + for (i = 0; i < CFArrayGetCount(allModes); i++) { + CGDisplayModeRef cmode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i); + + RGFW_monitorMode foundMode; + foundMode.w = (i32)CGDisplayModeGetWidth(cmode); + foundMode.h = (i32)CGDisplayModeGetHeight(cmode); + foundMode.refreshRate = RGFW_osx_getRefreshRate(display, cmode); + foundMode.red = 8; foundMode.green = 8; foundMode.blue = 8; + foundMode.src = (void*)cmode; + + if (RGFW_monitorModeCompare(mode, &foundMode, request)) { + native = cmode; + mon->mode = foundMode; + break; + } + } + + CFRelease(allModes); + + if (native) { + if (CGDisplaySetDisplayMode(display, native, NULL) == kCGErrorSuccess) { + return RGFW_TRUE; + } + } + + return RGFW_FALSE; +} + +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")); + + RGFW_monitorNode* node = _RGFW->monitors.list.head; + for (node = _RGFW->monitors.list.head; node; node = node->next) { + if (node->display == display && (id)node->screen == screen) { + break; + } + } + + return &node->mon; +} + +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((const char*)NSPasteboardTypeString)); +} + +#ifdef RGFW_OPENGL +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); +} + + +/* MacOS OpenGL API spares us yet again (there are no extensions) */ +RGFW_bool RGFW_extensionSupportedPlatform_OpenGL(const char * extension, size_t len) { RGFW_UNUSED(extension); RGFW_UNUSED(len); return RGFW_FALSE; } + +RGFW_proc RGFW_getProcAddress_OpenGL(const char* procname) { + static CFBundleRef RGFWnsglFramework = NULL; + 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; +} + +RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) { + win->src.ctx.native = ctx; + win->src.gfxType = RGFW_gfxNativeOpenGL; + + i32 attribs[40]; + size_t render_type_index = 0; + { + RGFW_attribStack stack; + RGFW_attribStack_init(&stack, attribs, 40); + + i32 colorBits = (i32)(hints->red + hints->green + hints->blue + hints->alpha) / 4; + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAColorSize, colorBits); + + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAAlphaSize, hints->alpha); + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFADepthSize, hints->depth); + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAStencilSize, hints->stencil); + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAAuxBuffers, hints->auxBuffers); + RGFW_attribStack_pushAttrib(&stack, NSOpenGLPFAClosestPolicy); + if (hints->samples) { + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFASampleBuffers, 1); + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFASamples, hints->samples); + } else RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFASampleBuffers, 0); + + if (hints->doubleBuffer) + RGFW_attribStack_pushAttrib(&stack, NSOpenGLPFADoubleBuffer); + + #ifdef RGFW_COCOA_GRAPHICS_SWITCHING + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAAllowOfflineRenderers, kCGLPFASupportsAutomaticGraphicsSwitching); + #endif + #if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 + if (hints->stereo) RGFW_attribStack_pushAttrib(&stack, NSOpenGLPFAStereo); + #endif + + /* 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? */ + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAOpenGLProfile, + (hints->major >= 4) ? NSOpenGLProfileVersion4_1Core : (hints->major >= 3) ? + NSOpenGLProfileVersion3_2Core : NSOpenGLProfileVersionLegacy); + + if (hints->major <= 2) { + i32 accumSize = (i32)(hints->accumRed + hints->accumGreen + hints->accumBlue + hints->accumAlpha) / 4; + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAAccumSize, accumSize); + } + + if (hints->renderer == RGFW_glSoftware) { + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFARendererID, kCGLRendererGenericFloatID); + } else { + RGFW_attribStack_pushAttrib(&stack, NSOpenGLPFAAccelerated); + } + render_type_index = stack.count - 1; + + RGFW_attribStack_pushAttribs(&stack, 0, 0); + } + + void* format = (void*) ((id(*)(id, SEL, const u32*))objc_msgSend) (NSAlloc((id)objc_getClass("NSOpenGLPixelFormat")), sel_registerName("initWithAttributes:"), (u32*)attribs); + if (format == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to load pixel format for OpenGL"); + + assert(render_type_index + 3 < (sizeof(attribs) / sizeof(attribs[0]))); + attribs[render_type_index] = NSOpenGLPFARendererID; + attribs[render_type_index + 1] = kCGLRendererGenericFloatID; + attribs[render_type_index + 3] = 0; + + format = (void*) ((id(*)(id, SEL, const u32*))objc_msgSend) (NSAlloc((id)objc_getClass("NSOpenGLPixelFormat")), sel_registerName("initWithAttributes:"), (u32*)attribs); + if (format == NULL) + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "and loading software rendering OpenGL failed"); + else + RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, "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) */ + + if (win->src.view) + NSRelease(win->src.view); + win->src.view = (id) ((id(*)(id, SEL, NSRect, u32*))objc_msgSend) (NSAlloc(_RGFW->customViewClasses[1]), + sel_registerName("initWithFrame:pixelFormat:"), (NSRect){{0, 0}, {(double)win->w, (double)win->h}}, (u32*)format); + + id share = NULL; + if (hints->share) { + share = (id)hints->share->ctx; + } + + win->src.ctx.native->ctx = ((id (*)(id, SEL, id, id))objc_msgSend)(NSAlloc(objc_getClass("NSOpenGLContext")), + sel_registerName("initWithFormat:shareContext:"), + (id)format, share); + + win->src.ctx.native->format = format; + + objc_msgSend_void_id(win->src.view, sel_registerName("setOpenGLContext:"), win->src.ctx.native->ctx); + if (win->internal.flags & RGFW_windowTransparent) { + i32 opacity = 0; + #define NSOpenGLCPSurfaceOpacity 236 + NSOpenGLContext_setValues((id)win->src.ctx.native->ctx, &opacity, (NSOpenGLContextParameter)NSOpenGLCPSurfaceOpacity); + + } + + objc_msgSend_void(win->src.ctx.native->ctx, sel_registerName("makeCurrentContext")); + + objc_msgSend_void_id((id)win->src.window, sel_registerName("setContentView:"), win->src.view); + objc_msgSend_void_bool(win->src.view, sel_registerName("setWantsLayer:"), true); + objc_msgSend_int((id)win->src.view, sel_registerName("setLayerContentsPlacement:"), 4); + + RGFW_window_swapInterval_OpenGL(win, 0); + + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context initalized."); + return RGFW_TRUE; +} + +void RGFW_window_deleteContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx) { + objc_msgSend_void(ctx->format, sel_registerName("release")); + win->src.ctx.native->format = NULL; + + objc_msgSend_void(ctx->ctx, sel_registerName("release")); + win->src.ctx.native->ctx = NULL; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context freed."); +} + +void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win) { + if (win) RGFW_ASSERT(win->src.ctx.native); + if (win != NULL) + objc_msgSend_void(win->src.ctx.native->ctx, sel_registerName("makeCurrentContext")); + else + objc_msgSend_id(objc_getClass("NSOpenGLContext"), sel_registerName("clearCurrentContext")); +} +void* RGFW_getCurrentContext_OpenGL(void) { + return objc_msgSend_id(objc_getClass("NSOpenGLContext"), sel_registerName("currentContext")); +} + +void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) { + RGFW_ASSERT(win && win->src.ctx.native); + objc_msgSend_void(win->src.ctx.native->ctx, sel_registerName("flushBuffer")); +} +void RGFW_window_swapInterval_OpenGL(RGFW_window* win, i32 swapInterval) { + RGFW_ASSERT(win != NULL && win->src.ctx.native != NULL); + NSOpenGLContext_setValues((id)win->src.ctx.native->ctx, &swapInterval, (NSOpenGLContextParameter)222); +} +#endif + +void RGFW_deinitPlatform(void) { + objc_msgSend_void_id(_RGFW->NSApp, sel_registerName("setDelegate:"), NULL); + + objc_msgSend_void_id(_RGFW->NSApp, sel_registerName("stop:"), NULL); + NSRelease(_RGFW->NSApp); + _RGFW->NSApp = NULL; + + NSRelease(_RGFW->customNSAppDelegate); + + _RGFW->customNSAppDelegate = NULL; + + objc_disposeClassPair((Class)_RGFW->customViewClasses[0]); + objc_disposeClassPair((Class)_RGFW->customViewClasses[1]); + objc_disposeClassPair((Class)_RGFW->customWindowDelegateClass); + objc_disposeClassPair((Class)_RGFW->customNSAppDelegateClass); +} + +void RGFW_window_closePlatform(RGFW_window* win) { + objc_msgSend_void_id((id)win->src.window, sel_registerName("setDelegate:"), NULL); + NSRelease((id)win->src.delegate); + NSRelease(win->src.view); + + objc_msgSend_id(win->src.window, sel_registerName("close")); + NSRelease(win->src.window); +} + +#ifdef RGFW_VULKAN +VkResult RGFW_window_createSurface_Vulkan(RGFW_window* win, VkInstance instance, VkSurfaceKHR* surface) { + RGFW_ASSERT(win != NULL); RGFW_ASSERT(instance); + RGFW_ASSERT(surface != NULL); + + *surface = VK_NULL_HANDLE; + id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); + pool = objc_msgSend_id(pool, sel_registerName("init")); + + id nsView = (id)win->src.view; + if (!nsView) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errMetal, "NSView is NULL for macOS window"); + return -1; + } + + + id layer = ((id (*)(id, SEL))objc_msgSend)(nsView, sel_registerName("layer")); + + void* metalLayer = RGFW_getLayer_OSX(); + if (metalLayer == NULL) { + return -1; + } + ((void (*)(id, SEL, id))objc_msgSend)((id)nsView, sel_registerName("setLayer:"), metalLayer); + ((void (*)(id, SEL, BOOL))objc_msgSend)(nsView, sel_registerName("setWantsLayer:"), YES); + + VkResult result; +/* + VkMetalSurfaceCreateInfoEXT macos; + macos.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; + macos.slayer = metalLayer; + RGFW_MEMSET(&macos, 0, sizeof(macos)); + result = vkCreateMacOSSurfaceMVK(instance, &macos, NULL, surface); +*/ + + VkMacOSSurfaceCreateInfoMVK macos; + RGFW_MEMSET(&macos, 0, sizeof(macos)); + macos.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; + macos.pView = nsView; + + result = vkCreateMacOSSurfaceMVK(instance, &macos, NULL, surface); + + objc_msgSend_bool_void(pool, sel_registerName("drain")); + + return result; +} +#endif + +#ifdef RGFW_WEBGPU +WGPUSurface RGFW_window_createSurface_WebGPU(RGFW_window* window, WGPUInstance instance) { + WGPUSurfaceDescriptor surfaceDesc = {0}; + id* nsView = (id*)window->src.view; + if (!nsView) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errMetal, "NSView is NULL for macOS window"); + return NULL; + } + + ((void (*)(id, SEL, BOOL))objc_msgSend)(nsView, sel_registerName("setWantsLayer:"), YES); + id layer = ((id (*)(id, SEL))objc_msgSend)(nsView, sel_registerName("layer")); + + void* metalLayer = RGFW_getLayer_OSX(); + if (metalLayer == NULL) { + return NULL; + } + ((void (*)(id, SEL, id))objc_msgSend)((id)nsView, sel_registerName("setLayer:"), metalLayer); + layer = metalLayer; + + WGPUSurfaceSourceMetalLayer fromMetal = {0}; + fromMetal.chain.sType = WGPUSType_SurfaceSourceMetalLayer; +#ifdef __OBJC__ + fromMetal.layer = (__bridge CAMetalLayer*)layer; /* Use __bridge for ARC compatibility if mixing C/Obj-C */ +#else + fromMetal.layer = layer; +#endif + + surfaceDesc.nextInChain = (WGPUChainedStruct*)&fromMetal.chain; + return wgpuInstanceCreateSurface(instance, &surfaceDesc); +} +#endif + +#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_windowResizedCallback(_RGFW->root, 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); + + if (!(_RGFW->root->internal.enabledEvents & RGFW_windowResizedFlag)) return EM_TRUE; + + static u8 fullscreen = RGFW_FALSE; + static i32 originalW, originalH; + + if (fullscreen == RGFW_FALSE) { + originalW = _RGFW->root->w; + originalH = _RGFW->root->h; + } + + fullscreen = !fullscreen; + _RGFW->root->w = E->screenWidth; + _RGFW->root->h = E->screenHeight; + + EM_ASM("Module.canvas.focus();"); + + if (fullscreen == RGFW_FALSE) { + _RGFW->root->w = originalW; + _RGFW->root->h = originalH; + } else { + #if __EMSCRIPTEN_major__ >= 1 && __EMSCRIPTEN_minor__ >= 29 && __EMSCRIPTEN_tiny__ >= 0 + EmscriptenFullscreenStrategy FSStrat = {0}; + FSStrat.scaleMode = 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->w, _RGFW->root->h); + RGFW_windowResizedCallback(_RGFW->root, _RGFW->root->w, _RGFW->root->h); + 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_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_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_mousePosCallback(_RGFW->root, E->targetX, E->targetY, 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_mouseButtonCallback(_RGFW->root, button, 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_mouseButtonCallback(_RGFW->root, button, 0); + return EM_TRUE; +} + +EM_BOOL Emscripten_on_wheel(int eventType, const EmscriptenWheelEvent* E, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + + RGFW_mouseScrollCallback(_RGFW->root, E->deltaX, E->deltaY); + + return EM_TRUE; +} + +EM_BOOL Emscripten_on_touchstart(int eventType, const EmscriptenTouchEvent* E, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + + if (!(_RGFW->root->internal.enabledEvents & RGFW_mouseButtonPressedFlag)) return EM_TRUE; + + size_t i; + for (i = 0; i < (size_t)E->numTouches; i++) { + RGFW_mousePosCallback(_RGFW->root, E->touches[i].targetX, E->touches[i].targetY, 0, 0); + RGFW_mouseButtonCallback(_RGFW->root, RGFW_mouseLeft, 1); + } + + return EM_TRUE; +} + +EM_BOOL Emscripten_on_touchmove(int eventType, const EmscriptenTouchEvent* E, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + + if (!(_RGFW->root->internal.enabledEvents & RGFW_mousePosChangedFlag)) return EM_TRUE; + + size_t i; + for (i = 0; i < (size_t)E->numTouches; i++) { + RGFW_mousePosCallback(_RGFW->root, E->touches[i].targetX, E->touches[i].targetY, 0, 0); + } + return EM_TRUE; +} + +EM_BOOL Emscripten_on_touchend(int eventType, const EmscriptenTouchEvent* E, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + + if (!(_RGFW->root->internal.enabledEvents & RGFW_mouseButtonReleasedFlag)) return EM_TRUE; + + size_t i; + for (i = 0; i < (size_t)E->numTouches; i++) { + RGFW_mousePosCallback(_RGFW->root, E->touches[i].targetX, E->touches[i].targetY, 0, 0); + RGFW_mouseButtonCallback(_RGFW->root, RGFW_mouseLeft, 0); + } + return EM_TRUE; +} + +EM_BOOL Emscripten_on_touchcancel(int eventType, const EmscriptenTouchEvent* E, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); return EM_TRUE; } + +RGFW_key RGFW_WASMPhysicalToRGFW(u32 hash); + +void EMSCRIPTEN_KEEPALIVE RGFW_handleKeyEvent(char* code, u32 codepoint, RGFW_bool press) { + const char* iCode = code; + + u32 hash = 0; + while(*iCode) hash = ((hash ^ 0x7E057D79U) << 3) ^ (unsigned int)*iCode++; + + u32 physicalKey = RGFW_WASMPhysicalToRGFW(hash); + + RGFW_keyCallback(_RGFW->root, physicalKey, _RGFW->root->internal.mod, RGFW_window_isKeyDown(_RGFW->root, (u8)physicalKey), press); + if (press) { +; RGFW_keyCharCallback(_RGFW->root, codepoint); + } +} + +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_updateKeyModsEx(_RGFW->root, capital, numlock, control, alt, shift, super, scroll); +} + +void EMSCRIPTEN_KEEPALIVE Emscripten_onDrop(size_t count) { + RGFW_dataDropCallback(_RGFW->root, _RGFW->files, count); +} + +void RGFW_stopCheckEvents(void) { + _RGFW->stopCheckEvents_bool = RGFW_TRUE; +} + +RGFW_bool RGFW_createSurfacePtr(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) { + surface->data = data; + surface->w = w; + surface->h = h; + surface->format = format; + return RGFW_TRUE; +} + +void RGFW_window_blitSurface(RGFW_window* win, RGFW_surface* surface) { + /* TODO: Needs fixing. */ + RGFW_copyImageData(surface->data, surface->w, RGFW_MIN(win->h, surface->h), RGFW_formatRGBA8, surface->data, surface->format, surface->convertFunc); + 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); + }, surface->data, surface->w, surface->h, RGFW_MIN(win->h, surface->w), RGFW_MIN(win->h, surface->h)); +} + +void RGFW_surface_freePtr(RGFW_surface* surface) { } + +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_STRNCPY((char*)_RGFW->files[index], file, RGFW_MAX_PATH - 1); + _RGFW->files[index][RGFW_MAX_PATH - 1] = '\0'; +} + +#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_initKeycodesPlatform(void) { + _RGFW->keycodes[DOM_VK_BACK_QUOTE] = RGFW_backtick; + _RGFW->keycodes[DOM_VK_0] = RGFW_0; + _RGFW->keycodes[DOM_VK_1] = RGFW_1; + _RGFW->keycodes[DOM_VK_2] = RGFW_2; + _RGFW->keycodes[DOM_VK_3] = RGFW_3; + _RGFW->keycodes[DOM_VK_4] = RGFW_4; + _RGFW->keycodes[DOM_VK_5] = RGFW_5; + _RGFW->keycodes[DOM_VK_6] = RGFW_6; + _RGFW->keycodes[DOM_VK_7] = RGFW_7; + _RGFW->keycodes[DOM_VK_8] = RGFW_8; + _RGFW->keycodes[DOM_VK_9] = RGFW_9; + _RGFW->keycodes[DOM_VK_SPACE] = RGFW_space; + _RGFW->keycodes[DOM_VK_A] = RGFW_a; + _RGFW->keycodes[DOM_VK_B] = RGFW_b; + _RGFW->keycodes[DOM_VK_C] = RGFW_c; + _RGFW->keycodes[DOM_VK_D] = RGFW_d; + _RGFW->keycodes[DOM_VK_E] = RGFW_e; + _RGFW->keycodes[DOM_VK_F] = RGFW_f; + _RGFW->keycodes[DOM_VK_G] = RGFW_g; + _RGFW->keycodes[DOM_VK_H] = RGFW_h; + _RGFW->keycodes[DOM_VK_I] = RGFW_i; + _RGFW->keycodes[DOM_VK_J] = RGFW_j; + _RGFW->keycodes[DOM_VK_K] = RGFW_k; + _RGFW->keycodes[DOM_VK_L] = RGFW_l; + _RGFW->keycodes[DOM_VK_M] = RGFW_m; + _RGFW->keycodes[DOM_VK_N] = RGFW_n; + _RGFW->keycodes[DOM_VK_O] = RGFW_o; + _RGFW->keycodes[DOM_VK_P] = RGFW_p; + _RGFW->keycodes[DOM_VK_Q] = RGFW_q; + _RGFW->keycodes[DOM_VK_R] = RGFW_r; + _RGFW->keycodes[DOM_VK_S] = RGFW_s; + _RGFW->keycodes[DOM_VK_T] = RGFW_t; + _RGFW->keycodes[DOM_VK_U] = RGFW_u; + _RGFW->keycodes[DOM_VK_V] = RGFW_v; + _RGFW->keycodes[DOM_VK_W] = RGFW_w; + _RGFW->keycodes[DOM_VK_X] = RGFW_x; + _RGFW->keycodes[DOM_VK_Y] = RGFW_y; + _RGFW->keycodes[DOM_VK_Z] = RGFW_z; + _RGFW->keycodes[DOM_VK_PERIOD] = RGFW_period; + _RGFW->keycodes[DOM_VK_COMMA] = RGFW_comma; + _RGFW->keycodes[DOM_VK_SLASH] = RGFW_slash; + _RGFW->keycodes[DOM_VK_OPEN_BRACKET] = RGFW_bracket; + _RGFW->keycodes[DOM_VK_CLOSE_BRACKET] = RGFW_closeBracket; + _RGFW->keycodes[DOM_VK_SEMICOLON] = RGFW_semicolon; + _RGFW->keycodes[DOM_VK_QUOTE] = RGFW_apostrophe; + _RGFW->keycodes[DOM_VK_BACK_SLASH] = RGFW_backSlash; + _RGFW->keycodes[DOM_VK_RETURN] = RGFW_return; + _RGFW->keycodes[DOM_VK_DELETE] = RGFW_delete; + _RGFW->keycodes[DOM_VK_NUM_LOCK] = RGFW_numLock; + _RGFW->keycodes[DOM_VK_DIVIDE] = RGFW_kpSlash; + _RGFW->keycodes[DOM_VK_MULTIPLY] = RGFW_kpMultiply; + _RGFW->keycodes[DOM_VK_SUBTRACT] = RGFW_kpMinus; + _RGFW->keycodes[DOM_VK_NUMPAD1] = RGFW_kp1; + _RGFW->keycodes[DOM_VK_NUMPAD2] = RGFW_kp2; + _RGFW->keycodes[DOM_VK_NUMPAD3] = RGFW_kp3; + _RGFW->keycodes[DOM_VK_NUMPAD4] = RGFW_kp4; + _RGFW->keycodes[DOM_VK_NUMPAD5] = RGFW_kp5; + _RGFW->keycodes[DOM_VK_NUMPAD6] = RGFW_kp6; + _RGFW->keycodes[DOM_VK_NUMPAD9] = RGFW_kp9; + _RGFW->keycodes[DOM_VK_NUMPAD0] = RGFW_kp0; + _RGFW->keycodes[DOM_VK_DECIMAL] = RGFW_kpPeriod; + _RGFW->keycodes[DOM_VK_RETURN] = RGFW_kpReturn; + _RGFW->keycodes[DOM_VK_HYPHEN_MINUS] = RGFW_minus; + _RGFW->keycodes[DOM_VK_EQUALS] = RGFW_equals; + _RGFW->keycodes[DOM_VK_BACK_SPACE] = RGFW_backSpace; + _RGFW->keycodes[DOM_VK_TAB] = RGFW_tab; + _RGFW->keycodes[DOM_VK_CAPS_LOCK] = RGFW_capsLock; + _RGFW->keycodes[DOM_VK_SHIFT] = RGFW_shiftL; + _RGFW->keycodes[DOM_VK_CONTROL] = RGFW_controlL; + _RGFW->keycodes[DOM_VK_ALT] = RGFW_altL; + _RGFW->keycodes[DOM_VK_META] = RGFW_superL; + _RGFW->keycodes[DOM_VK_F1] = RGFW_F1; + _RGFW->keycodes[DOM_VK_F2] = RGFW_F2; + _RGFW->keycodes[DOM_VK_F3] = RGFW_F3; + _RGFW->keycodes[DOM_VK_F4] = RGFW_F4; + _RGFW->keycodes[DOM_VK_F5] = RGFW_F5; + _RGFW->keycodes[DOM_VK_F6] = RGFW_F6; + _RGFW->keycodes[DOM_VK_F7] = RGFW_F7; + _RGFW->keycodes[DOM_VK_F8] = RGFW_F8; + _RGFW->keycodes[DOM_VK_F9] = RGFW_F9; + _RGFW->keycodes[DOM_VK_F10] = RGFW_F10; + _RGFW->keycodes[DOM_VK_F11] = RGFW_F11; + _RGFW->keycodes[DOM_VK_F12] = RGFW_F12; + _RGFW->keycodes[DOM_VK_UP] = RGFW_up; + _RGFW->keycodes[DOM_VK_DOWN] = RGFW_down; + _RGFW->keycodes[DOM_VK_LEFT] = RGFW_left; + _RGFW->keycodes[DOM_VK_RIGHT] = RGFW_right; + _RGFW->keycodes[DOM_VK_INSERT] = RGFW_insert; + _RGFW->keycodes[DOM_VK_END] = RGFW_end; + _RGFW->keycodes[DOM_VK_PAGE_UP] = RGFW_pageUp; + _RGFW->keycodes[DOM_VK_PAGE_DOWN] = RGFW_pageDown; + _RGFW->keycodes[DOM_VK_ESCAPE] = RGFW_escape; + _RGFW->keycodes[DOM_VK_HOME] = RGFW_home; + _RGFW->keycodes[DOM_VK_SCROLL_LOCK] = RGFW_scrollLock; + _RGFW->keycodes[DOM_VK_PRINTSCREEN] = RGFW_printScreen; + _RGFW->keycodes[DOM_VK_PAUSE] = RGFW_pause; + _RGFW->keycodes[DOM_VK_F13] = RGFW_F13; + _RGFW->keycodes[DOM_VK_F14] = RGFW_F14; + _RGFW->keycodes[DOM_VK_F15] = RGFW_F15; + _RGFW->keycodes[DOM_VK_F16] = RGFW_F16; + _RGFW->keycodes[DOM_VK_F17] = RGFW_F17; + _RGFW->keycodes[DOM_VK_F18] = RGFW_F18; + _RGFW->keycodes[DOM_VK_F19] = RGFW_F19; + _RGFW->keycodes[DOM_VK_F20] = RGFW_F20; + _RGFW->keycodes[DOM_VK_F21] = RGFW_F21; + _RGFW->keycodes[DOM_VK_F22] = RGFW_F22; + _RGFW->keycodes[DOM_VK_F23] = RGFW_F23; + _RGFW->keycodes[DOM_VK_F24] = RGFW_F24; +} + +i32 RGFW_initPlatform(void) { return 0; } + +RGFW_window* RGFW_createWindowPlatform(const char* name, RGFW_windowFlags flags, RGFW_window* win) { + emscripten_set_canvas_element_size("#canvas", win->w, win->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); + + if (flags & RGFW_windowAllowDND) { + win->internal.flags |= RGFW_windowAllowDND; + } + + EM_ASM({ + window.addEventListener("keydown", + (event) => { + 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")); + + var codepoint = event.key.charCodeAt(0); + if(codepoint < 0x7f && event.key.length > 1) { + codepoint = 0; + } + + Module._RGFW_handleKeyEvent(code, codepoint, 1); + _free(code); + }, + true); + window.addEventListener("keyup", + (event) => { + 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(code, 0, 0); + _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; + + Module._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); + }); + + return win; +} + +RGFW_key RGFW_physicalToMappedKey(RGFW_key key) { + return key; +} + +void RGFW_pollEvents(void) { + RGFW_resetPrevState(); + emscripten_sleep(0); +} + +void RGFW_window_resize(RGFW_window* win, i32 w, i32 h) { + RGFW_UNUSED(win); + emscripten_set_canvas_element_size("#canvas", w, h); +} + +/* NOTE: I don't know if this is possible */ +void RGFW_window_moveMouse(RGFW_window* win, i32 x, i32 y) { RGFW_UNUSED(win); RGFW_UNUSED(x); RGFW_UNUSED(y); } +/* this one might be possible but it looks iffy */ +RGFW_mouse* RGFW_loadMouse(u8* data, i32 w, i32 h, RGFW_format format) { RGFW_UNUSED(data); RGFW_UNUSED(w); RGFW_UNUSED(h); RGFW_UNUSED(format); 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) { + RGFW_UNUSED(win); + char* cursorName = NULL; + + switch (mouse) { + case RGFW_mouseNormal: cursorName = (char*)"default"; break; + case RGFW_mouseArrow: cursorName = (char*)"default"; break; + case RGFW_mouseIbeam: cursorName = (char*)"text"; break; + case RGFW_mouseCrosshair: cursorName = (char*)"crosshair"; break; + case RGFW_mousePointingHand: cursorName = (char*)"pointer"; break; + case RGFW_mouseResizeEW: cursorName = (char*)"ew-resize"; break; + case RGFW_mouseResizeNS: cursorName = (char*)"ns-resize"; break; + case RGFW_mouseResizeNWSE: cursorName = (char*)"nwse-resize"; break; + case RGFW_mouseResizeNESW: cursorName = (char*)"nesw-resize"; break; + case RGFW_mouseResizeNW: cursorName = (char*)"nw-resize"; break; + case RGFW_mouseResizeN: cursorName = (char*)"n-resize"; break; + case RGFW_mouseResizeNE: cursorName = (char*)"ne-resize"; break; + case RGFW_mouseResizeE: cursorName = (char*)"e-resize"; break; + case RGFW_mouseResizeSE: cursorName = (char*)"se-resize"; break; + case RGFW_mouseResizeS: cursorName = (char*)"s-resize"; break; + case RGFW_mouseResizeSW: cursorName = (char*)"sw-resize"; break; + case RGFW_mouseResizeW: cursorName = (char*)"w-resize"; break; + case RGFW_mouseResizeAll: cursorName = (char*)"move"; break; + case RGFW_mouseNotAllowed: cursorName = (char*)"not-allowed"; break; + case RGFW_mouseWait: cursorName = (char*)"wait"; break; + case RGFW_mouseProgress: cursorName = (char*)"progress"; break; + default: return RGFW_FALSE; + } + + EM_ASM( { document.getElementById("canvas").style.cursor = UTF8ToString($0); }, cursorName); + 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_bool RGFW_getGlobalMouse(i32* x, i32* y) { + if(x) *x = EM_ASM_INT({ + return window.mouseX || 0; + }); + if (y) *y = EM_ASM_INT({ + return window.mouseY || 0; + }); + return RGFW_TRUE; +} + +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; +} + +#ifdef RGFW_OPENGL +RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) { + win->src.ctx.native = ctx; + win->src.gfxType = RGFW_gfxNativeOpenGL; + + EmscriptenWebGLContextAttributes attrs; + attrs.alpha = hints->alpha; + attrs.depth = hints->depth; + attrs.stencil = hints->stencil; + attrs.antialias = hints->samples; + attrs.premultipliedAlpha = EM_TRUE; + attrs.preserveDrawingBuffer = EM_FALSE; + + if (hints->doubleBuffer == 0) + attrs.renderViaOffscreenBackBuffer = 0; + else + attrs.renderViaOffscreenBackBuffer = hints->auxBuffers; + + attrs.failIfMajorPerformanceCaveat = EM_FALSE; + attrs.majorVersion = (hints->major == 0) ? 1 : hints->major; + attrs.minorVersion = hints->minor; + + attrs.enableExtensionsByDefault = EM_TRUE; + attrs.explicitSwapControl = EM_TRUE; + + emscripten_webgl_init_context_attributes(&attrs); + win->src.ctx.native->ctx = emscripten_webgl_create_context("#canvas", &attrs); + emscripten_webgl_make_context_current(win->src.ctx.native->ctx); + + #ifdef LEGACY_GL_EMULATION + EM_ASM("Module.useWebGL = true; GLImmediate.init();"); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context initalized."); + #endif + + RGFW_window_swapInterval_OpenGL(win, 0); + + return RGFW_TRUE; +} + +void RGFW_window_deleteContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx) { + emscripten_webgl_destroy_context(ctx->ctx); + win->src.ctx.native->ctx = 0; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context freed."); +} + +void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win) { + if (win) RGFW_ASSERT(win->src.ctx.native); + if (win == NULL) + emscripten_webgl_make_context_current(0); + else + emscripten_webgl_make_context_current(win->src.ctx.native->ctx); +} + +void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) { + RGFW_ASSERT(win && win->src.ctx.native); + emscripten_webgl_commit_frame(); +} +void* RGFW_getCurrentContext_OpenGL(void) { return (void*)emscripten_webgl_get_current_context(); } + +RGFW_bool RGFW_extensionSupportedPlatform_OpenGL(const char* extension, size_t len) { + 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); + return RGFW_FALSE; +} + +RGFW_proc RGFW_getProcAddress_OpenGL(const char* procname) { + return (RGFW_proc)emscripten_webgl_get_proc_address(procname); + return NULL; +} + +#endif + +void RGFW_window_swapInterval_OpenGL(RGFW_window* win, i32 swapInterval) { RGFW_UNUSED(win); RGFW_UNUSED(swapInterval); } + +void RGFW_deinitPlatform(void) { } + +void RGFW_window_closePlatform(RGFW_window* win) { } + +int RGFW_innerWidth(void) { return EM_ASM_INT({ return window.innerWidth; }); } +int RGFW_innerHeight(void) { return EM_ASM_INT({ return window.innerHeight; }); } + +void RGFW_window_setRawMouseModePlatform(RGFW_window* win, RGFW_bool state) { + RGFW_UNUSED(win); RGFW_UNUSED(state); +} + +void RGFW_window_captureMousePlatform(RGFW_window* win, RGFW_bool state) { + RGFW_UNUSED(win); + if (state) { + emscripten_request_pointerlock("#canvas", 1); + } else { + emscripten_exit_pointerlock(); + } +} + +void RGFW_window_setName(RGFW_window* win, const char* name) { + RGFW_UNUSED(win); + if (name == NULL) name = "\0"; + + emscripten_set_window_title(name); +} + +void RGFW_window_maximize(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + RGFW_monitor* mon = RGFW_window_getMonitor(win); + if (mon != NULL) { + RGFW_window_resize(win, mon->mode.w, mon->mode.h); + } + + RGFW_window_move(win, 0, 0); +} + +void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) { + RGFW_ASSERT(win != NULL); + if (fullscreen) { + win->internal.flags |= RGFW_windowFullscreen; + EM_ASM( Module.requestFullscreen(false, true); ); + return; + } + win->internal.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); +} + +#ifdef RGFW_WEBGPU +WGPUSurface RGFW_window_createSurface_WebGPU(RGFW_window* window, WGPUInstance instance) { + WGPUSurfaceDescriptor surfaceDesc = {0}; + WGPUEmscriptenSurfaceSourceCanvasHTMLSelector canvasDesc = {0}; + canvasDesc.chain.sType = WGPUSType_EmscriptenSurfaceSourceCanvasHTMLSelector; + canvasDesc.selector = (WGPUStringView){.data = "#canvas", .length = 7}; + + surfaceDesc.nextInChain = &canvasDesc.chain; + return wgpuInstanceCreateSurface(instance, &surfaceDesc); +} +#endif + +RGFW_key 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_kpMultiply; + 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_kpMultiply; /* 0x0047 */ + case 0xC925FCD0U /* Numpad8 */: return RGFW_kp8; /* 0x0048 */ + case 0xC925FCD1U /* Numpad9 */: return RGFW_kp9; /* 0x0049 */ + case 0x5EA3E8A4U /* NumpadSubtract */: return RGFW_minus; /* 0x004A */ + case 0xC925FCDCU /* Numpad4 */: return RGFW_kp4; /* 0x004B */ + case 0xC925FCDDU /* Numpad5 */: return RGFW_kp5; /* 0x004C */ + case 0xC925FCDEU /* Numpad6 */: return RGFW_kp6; /* 0x004D */ + case 0xC925FCD9U /* Numpad1 */: return RGFW_kp1; /* 0x004F */ + case 0xC925FCDAU /* Numpad2 */: return RGFW_kp2; /* 0x0050 */ + case 0xC925FCDBU /* Numpad3 */: return RGFW_kp3; /* 0x0051 */ + case 0xC925FCD8U /* Numpad0 */: return RGFW_kp0; /* 0x0052 */ + case 0x95852DACU /* NumpadDecimal */: return RGFW_period; /* 0x0053 */ + case 0x7B8E57B1U /* F11 */: return RGFW_F11; /* 0x0057 */ + case 0x7B8E57B2U /* F12 */: return RGFW_F12; /* 0x0058 */ + case 0x7B8E57B3U /* F13 */: return DOM_PK_F13; /* 0x0064 */ + case 0x7B8E57B4U /* F14 */: return DOM_PK_F14; /* 0x0065 */ + case 0x7B8E57B5U /* F15 */: return DOM_PK_F15; /* 0x0066 */ + case 0x7B8E57B6U /* F16 */: return DOM_PK_F16; /* 0x0067 */ + case 0x7B8E57B7U /* F17 */: return DOM_PK_F17; /* 0x0068 */ + case 0x7B8E57B8U /* F18 */: return DOM_PK_F18; /* 0x0069 */ + case 0x7B8E57B9U /* F19 */: return DOM_PK_F19; /* 0x006A */ + case 0x7B8E57A8U /* F20 */: return DOM_PK_F20; /* 0x006B */ + case 0x7B8E57A9U /* F21 */: return DOM_PK_F21; /* 0x006C */ + case 0x7B8E57AAU /* F22 */: return DOM_PK_F22; /* 0x006D */ + case 0x7B8E57ABU /* F23 */: return DOM_PK_F23; /* 0x006E */ + case 0x7393FBACU /* NumpadEqual */: return RGFW_kpReturn; + 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 */ + case 0x380B9C8CU /* NumpadAdd */: return DOM_PK_NUMPAD_ADD; /* 0x004E */ + default: return DOM_PK_UNKNOWN; + } + + return 0; +} + +/* 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_bool RGFW_monitor_getWorkarea(RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height) { RGFW_UNUSED(monitor); RGFW_UNUSED(x); RGFW_UNUSED(width); RGFW_UNUSED(height); return RGFW_FALSE; } +size_t RGFW_monitor_getGammaRampPtr(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) { RGFW_UNUSED(monitor); RGFW_UNUSED(ramp); return 0; } +RGFW_bool RGFW_monitor_setGammaRamp(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) { RGFW_UNUSED(monitor); RGFW_UNUSED(ramp); return RGFW_FALSE; } +size_t RGFW_monitor_getModesPtr(RGFW_monitor* mon, RGFW_monitorMode** modes) { RGFW_UNUSED(mon); RGFW_UNUSED(modes); return 0; } +RGFW_bool RGFW_monitor_setMode(RGFW_monitor* mon, RGFW_monitorMode* mode) { RGFW_UNUSED(mon); RGFW_UNUSED(mode); return RGFW_FALSE; } +void RGFW_pollMonitors(void) { } +void RGFW_window_move(RGFW_window* win, i32 x, i32 y) { RGFW_UNUSED(win); RGFW_UNUSED(x); RGFW_UNUSED(y); } +void RGFW_window_setAspectRatio(RGFW_window* win, i32 w, i32 h) { RGFW_UNUSED(win); RGFW_UNUSED(w); RGFW_UNUSED(h); } +void RGFW_window_setMinSize(RGFW_window* win, i32 w, i32 h) { RGFW_UNUSED(win); RGFW_UNUSED(w); RGFW_UNUSED(h); } +void RGFW_window_setMaxSize(RGFW_window* win, i32 w, i32 h) { RGFW_UNUSED(win); RGFW_UNUSED(w); RGFW_UNUSED(h); } +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* data, i32 w, i32 h, RGFW_format format, RGFW_icon type) { RGFW_UNUSED(win); RGFW_UNUSED(data); RGFW_UNUSED(w); RGFW_UNUSED(h); RGFW_UNUSED(format); 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); } +void RGFW_window_flash(RGFW_window* win, RGFW_flashRequest request) { RGFW_UNUSED(win); RGFW_UNUSED(request); } +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 NULL; } +void RGFW_waitForEvent(i32 waitMS) { RGFW_UNUSED(waitMS); } +#endif + +/* end of web asm defines */ + +/* + * RGFW function pointer backend, made to allow you to compile for Wayland but fallback to X11 +*/ +#ifdef RGFW_DYNAMIC +typedef RGFW_window* (*RGFW_createWindowPlatform_ptr)(const char* name, RGFW_windowFlags flags, RGFW_window* win); +typedef RGFW_bool (*RGFW_getMouse_ptr)(i32* x, i32* y); +typedef RGFW_key (*RGFW_physicalToMappedKey_ptr)(RGFW_key key); +typedef void (*RGFW_pollEvents_ptr)(void); +typedef void (*RGFW_pollMonitors_ptr)(void); +typedef void (*RGFW_window_move_ptr)(RGFW_window* win, i32 x, i32 y); +typedef void (*RGFW_window_resize_ptr)(RGFW_window* win, i32 w, i32 h); +typedef void (*RGFW_window_setAspectRatio_ptr)(RGFW_window* win, i32 w, i32 h); +typedef void (*RGFW_window_setMinSize_ptr)(RGFW_window* win, i32 w, i32 h); +typedef void (*RGFW_window_setMaxSize_ptr)(RGFW_window* win, i32 w, i32 h); +typedef void (*RGFW_window_maximize_ptr)(RGFW_window* win); +typedef void (*RGFW_window_focus_ptr)(RGFW_window* win); +typedef void (*RGFW_window_raise_ptr)(RGFW_window* win); +typedef void (*RGFW_window_setFullscreen_ptr)(RGFW_window* win, RGFW_bool fullscreen); +typedef void (*RGFW_window_setFloating_ptr)(RGFW_window* win, RGFW_bool floating); +typedef void (*RGFW_window_setOpacity_ptr)(RGFW_window* win, u8 opacity); +typedef void (*RGFW_window_minimize_ptr)(RGFW_window* win); +typedef void (*RGFW_window_restore_ptr)(RGFW_window* win); +typedef RGFW_bool (*RGFW_window_isFloating_ptr)(RGFW_window* win); +typedef void (*RGFW_window_setName_ptr)(RGFW_window* win, const char* name); +typedef void (*RGFW_window_setMousePassthrough_ptr)(RGFW_window* win, RGFW_bool passthrough); +typedef RGFW_bool (*RGFW_window_setIconEx_ptr)(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, u8 type); +typedef RGFW_mouse* (*RGFW_loadMouse_ptr)(u8* data, i32 w, i32 h, RGFW_format format); +typedef void (*RGFW_window_setMouse_ptr)(RGFW_window* win, RGFW_mouse* mouse); +typedef void (*RGFW_window_moveMouse_ptr)(RGFW_window* win, i32 x, i32 y); +typedef RGFW_bool (*RGFW_window_setMouseDefault_ptr)(RGFW_window* win); +typedef RGFW_bool (*RGFW_window_setMouseStandard_ptr)(RGFW_window* win, u8 mouse); +typedef void (*RGFW_window_hide_ptr)(RGFW_window* win); +typedef void (*RGFW_window_show_ptr)(RGFW_window* win); +typedef void (*RGFW_window_flash_ptr)(RGFW_window* win, RGFW_flashRequest request); +typedef RGFW_ssize_t (*RGFW_readClipboardPtr_ptr)(char* str, size_t strCapacity); +typedef void (*RGFW_writeClipboard_ptr)(const char* text, u32 textLen); +typedef RGFW_bool (*RGFW_window_isHidden_ptr)(RGFW_window* win); +typedef RGFW_bool (*RGFW_window_isMinimized_ptr)(RGFW_window* win); +typedef RGFW_bool (*RGFW_window_isMaximized_ptr)(RGFW_window* win); +typedef RGFW_bool (*RGFW_monitor_requestMode_ptr)(RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request); +typedef RGFW_bool (*RGFW_monitor_getWorkarea_ptr)(RGFW_monitor* mon, i32* x, i32* y, i32* w, i32* h); +typedef size_t (*RGFW_monitor_getModesPtr_ptr)(RGFW_monitor* mon, RGFW_monitorMode** modes); +typedef size_t (*RGFW_monitor_getGammaRampPtr_ptr) (RGFW_monitor* monitor, RGFW_gammaRamp* ramp); +typedef RGFW_bool (*RGFW_monitor_setGammaRamp_ptr) (RGFW_monitor* monitor, RGFW_gammaRamp* ramp); +typedef RGFW_bool (*RGFW_monitor_setMode_ptr)(RGFW_monitor* mon, RGFW_monitorMode* mode); +typedef RGFW_monitor* (*RGFW_window_getMonitor_ptr)(RGFW_window* win); +typedef void (*RGFW_window_closePlatform_ptr)(RGFW_window* win); +typedef RGFW_format (*RGFW_nativeFormat_ptr)(void); +typedef RGFW_bool (*RGFW_createSurfacePtr_ptr)(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface); +typedef void (*RGFW_window_blitSurface_ptr)(RGFW_window* win, RGFW_surface* surface); +typedef void (*RGFW_surface_freePtr_ptr)(RGFW_surface* surface); +typedef void (*RGFW_freeMouse_ptr)(RGFW_mouse* mouse); +typedef void (*RGFW_window_setBorder_ptr)(RGFW_window* win, RGFW_bool border); +typedef void (*RGFW_window_captureMousePlatform_ptr)(RGFW_window* win, RGFW_bool state); +typedef void (*RGFW_window_setRawMouseModePlatform_ptr)(RGFW_window* win, RGFW_bool state); +#ifdef RGFW_OPENGL +typedef void (*RGFW_window_makeCurrentContext_OpenGL_ptr)(RGFW_window* win); +typedef void* (*RGFW_getCurrentContext_OpenGL_ptr)(void); +typedef void (*RGFW_window_swapBuffers_OpenGL_ptr)(RGFW_window* win); +typedef void (*RGFW_window_swapInterval_OpenGL_ptr)(RGFW_window* win, i32 swapInterval); +typedef RGFW_bool (*RGFW_extensionSupportedPlatform_OpenGL_ptr)(const char* extension, size_t len); +typedef RGFW_proc (*RGFW_getProcAddress_OpenGL_ptr)(const char* procname); +typedef RGFW_bool (*RGFW_window_createContextPtr_OpenGL_ptr)(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints); +typedef void (*RGFW_window_deleteContextPtr_OpenGL_ptr)(RGFW_window* win, RGFW_glContext* ctx); +#endif +#ifdef RGFW_WEBGPU +typedef WGPUSurface (*RGFW_window_createSurface_WebGPU_ptr)(RGFW_window* window, WGPUInstance instance); +#endif + +/* Structure to hold all function pointers */ +typedef struct RGFW_FunctionPointers { + RGFW_nativeFormat_ptr nativeFormat; + RGFW_createSurfacePtr_ptr createSurfacePtr; + RGFW_window_blitSurface_ptr window_blitSurface; + RGFW_surface_freePtr_ptr surface_freePtr; + RGFW_freeMouse_ptr freeMouse; + RGFW_window_setBorder_ptr window_setBorder; + RGFW_window_captureMousePlatform_ptr window_captureMousePlatform; + RGFW_window_setRawMouseModePlatform_ptr window_setRawMouseModePlatform; + RGFW_createWindowPlatform_ptr createWindowPlatform; + RGFW_getMouse_ptr getGlobalMouse; + RGFW_physicalToMappedKey_ptr physicalToMappedKey; + RGFW_pollEvents_ptr pollEvents; + RGFW_pollMonitors_ptr pollMonitors; + RGFW_window_move_ptr window_move; + RGFW_window_resize_ptr window_resize; + RGFW_window_setAspectRatio_ptr window_setAspectRatio; + RGFW_window_setMinSize_ptr window_setMinSize; + RGFW_window_setMaxSize_ptr window_setMaxSize; + RGFW_window_maximize_ptr window_maximize; + RGFW_window_focus_ptr window_focus; + RGFW_window_raise_ptr window_raise; + RGFW_window_setFullscreen_ptr window_setFullscreen; + RGFW_window_setFloating_ptr window_setFloating; + RGFW_window_setOpacity_ptr window_setOpacity; + RGFW_window_minimize_ptr window_minimize; + RGFW_window_restore_ptr window_restore; + RGFW_window_isFloating_ptr window_isFloating; + RGFW_window_setName_ptr window_setName; + RGFW_window_setMousePassthrough_ptr window_setMousePassthrough; + RGFW_window_setIconEx_ptr window_setIconEx; + RGFW_loadMouse_ptr loadMouse; + RGFW_window_setMouse_ptr window_setMouse; + RGFW_window_moveMouse_ptr window_moveMouse; + RGFW_window_setMouseDefault_ptr window_setMouseDefault; + RGFW_window_setMouseStandard_ptr window_setMouseStandard; + RGFW_window_hide_ptr window_hide; + RGFW_window_show_ptr window_show; + RGFW_window_flash_ptr window_flash; + RGFW_readClipboardPtr_ptr readClipboardPtr; + RGFW_writeClipboard_ptr writeClipboard; + RGFW_window_isHidden_ptr window_isHidden; + RGFW_window_isMinimized_ptr window_isMinimized; + RGFW_window_isMaximized_ptr window_isMaximized; + RGFW_monitor_requestMode_ptr monitor_requestMode; + RGFW_monitor_getWorkarea_ptr monitor_getWorkarea; + RGFW_monitor_getModesPtr_ptr monitor_getModesPtr; + RGFW_monitor_getGammaRampPtr_ptr monitor_getGammaRampPtr; + RGFW_monitor_setGammaRamp_ptr monitor_setGammaRamp; + RGFW_monitor_setMode_ptr monitor_setMode; + RGFW_window_getMonitor_ptr window_getMonitor; + RGFW_window_closePlatform_ptr window_closePlatform; +#ifdef RGFW_OPENGL + RGFW_extensionSupportedPlatform_OpenGL_ptr extensionSupportedPlatform_OpenGL; + RGFW_getProcAddress_OpenGL_ptr getProcAddress_OpenGL; + RGFW_window_createContextPtr_OpenGL_ptr window_createContextPtr_OpenGL; + RGFW_window_deleteContextPtr_OpenGL_ptr window_deleteContextPtr_OpenGL; + RGFW_window_makeCurrentContext_OpenGL_ptr window_makeCurrentContext_OpenGL; + RGFW_getCurrentContext_OpenGL_ptr getCurrentContext_OpenGL; + RGFW_window_swapBuffers_OpenGL_ptr window_swapBuffers_OpenGL; + RGFW_window_swapInterval_OpenGL_ptr window_swapInterval_OpenGL; +#endif +#ifdef RGFW_WEBGPU + RGFW_window_createSurface_WebGPU_ptr window_createSurface_WebGPU; +#endif +} RGFW_functionPointers; + +RGFW_functionPointers RGFW_api; + +RGFW_format RGFW_nativeFormat(void) { return RGFW_api.nativeFormat(); } +RGFW_bool RGFW_createSurfacePtr(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) { return RGFW_api.createSurfacePtr(data, w, h, format, surface); } +void RGFW_surface_freePtr(RGFW_surface* surface) { RGFW_api.surface_freePtr(surface); } +void RGFW_freeMouse(RGFW_mouse* mouse) { RGFW_api.freeMouse(mouse); } +void RGFW_window_blitSurface(RGFW_window* win, RGFW_surface* surface) { RGFW_api.window_blitSurface(win, surface); } +void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { RGFW_api.window_setBorder(win, border); } +void RGFW_window_captureMousePlatform(RGFW_window* win, RGFW_bool state) { RGFW_api.window_captureMousePlatform(win, state); } +void RGFW_window_setRawMouseModePlatform(RGFW_window* win, RGFW_bool state) { RGFW_api.window_setRawMouseModePlatform(win, state); } +RGFW_window* RGFW_createWindowPlatform(const char* name, RGFW_windowFlags flags, RGFW_window* win) { RGFW_init(); return RGFW_api.createWindowPlatform(name, flags, win); } +RGFW_bool RGFW_getGlobalMouse(i32* x, i32* y) { return RGFW_api.getGlobalMouse(x, y); } +RGFW_key RGFW_physicalToMappedKey(RGFW_key key) { return RGFW_api.physicalToMappedKey(key); } +void RGFW_pollEvents(void) { RGFW_api.pollEvents(); } +void RGFW_pollMonitors(void) { RGFW_api.pollMonitors(); } +void RGFW_window_move(RGFW_window* win, i32 x, i32 y) { RGFW_api.window_move(win, x, y); } +void RGFW_window_resize(RGFW_window* win, i32 w, i32 h) { RGFW_api.window_resize(win, w, h); } +void RGFW_window_setAspectRatio(RGFW_window* win, i32 w, i32 h) { RGFW_api.window_setAspectRatio(win, w, h); } +void RGFW_window_setMinSize(RGFW_window* win, i32 w, i32 h) { RGFW_api.window_setMinSize(win, w, h); } +void RGFW_window_setMaxSize(RGFW_window* win, i32 w, i32 h) { RGFW_api.window_setMaxSize(win, w, h); } +void RGFW_window_maximize(RGFW_window* win) { RGFW_api.window_maximize(win); } +void RGFW_window_focus(RGFW_window* win) { RGFW_api.window_focus(win); } +void RGFW_window_raise(RGFW_window* win) { RGFW_api.window_raise(win); } +void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) { RGFW_api.window_setFullscreen(win, fullscreen); } +void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) { RGFW_api.window_setFloating(win, floating); } +void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) { RGFW_api.window_setOpacity(win, opacity); } +void RGFW_window_minimize(RGFW_window* win) { RGFW_api.window_minimize(win); } +void RGFW_window_restore(RGFW_window* win) { RGFW_api.window_restore(win); } +RGFW_bool RGFW_window_isFloating(RGFW_window* win) { return RGFW_api.window_isFloating(win); } +void RGFW_window_setName(RGFW_window* win, const char* name) { RGFW_api.window_setName(win, name); } + +#ifndef RGFW_NO_PASSTHROUGH +void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) { RGFW_api.window_setMousePassthrough(win, passthrough); } +#endif + +RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, u8 type) { return RGFW_api.window_setIconEx(win, data, w, h, format, type); } +RGFW_mouse* RGFW_loadMouse(u8* data, i32 w, i32 h, RGFW_format format) { return RGFW_api.loadMouse(data, w, h, format); } +void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) { RGFW_api.window_setMouse(win, mouse); } +void RGFW_window_moveMouse(RGFW_window* win, i32 x, i32 y) { RGFW_api.window_moveMouse(win, x, y); } +RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) { return RGFW_api.window_setMouseDefault(win); } +RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { return RGFW_api.window_setMouseStandard(win, mouse); } +void RGFW_window_hide(RGFW_window* win) { RGFW_api.window_hide(win); } +void RGFW_window_show(RGFW_window* win) { RGFW_api.window_show(win); } +void RGFW_window_flash(RGFW_window* win, RGFW_flashRequest request) { RGFW_api.window_flash(win, request); } +RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { return RGFW_api.readClipboardPtr(str, strCapacity); } +void RGFW_writeClipboard(const char* text, u32 textLen) { RGFW_api.writeClipboard(text, textLen); } +RGFW_bool RGFW_window_isHidden(RGFW_window* win) { return RGFW_api.window_isHidden(win); } +RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { return RGFW_api.window_isMinimized(win); } +RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { return RGFW_api.window_isMaximized(win); } +RGFW_bool RGFW_monitor_requestMode(RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request) { return RGFW_api.monitor_requestMode(mon, mode, request); } +RGFW_bool RGFW_monitor_getWorkarea(RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height) { return RGFW_api.monitor_getWorkarea(monitor, x, y, width, height); } +size_t RGFW_monitor_getGammaRampPtr(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) { return RGFW_api.monitor_getGammaRampPtr(monitor, ramp); } +RGFW_bool RGFW_monitor_setGammaRamp(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) { return RGFW_api.monitor_setGammaRamp(monitor, ramp); } +size_t RGFW_monitor_getModesPtr(RGFW_monitor* mon, RGFW_monitorMode** modes) { return RGFW_api.monitor_getModesPtr(mon, modes); } +RGFW_bool RGFW_monitor_setMode(RGFW_monitor* mon, RGFW_monitorMode* mode) { return RGFW_api.monitor_setMode(mon, mode); } +RGFW_monitor* RGFW_window_getMonitor(RGFW_window* win) { return RGFW_api.window_getMonitor(win); } +void RGFW_window_closePlatform(RGFW_window* win) { RGFW_api.window_closePlatform(win); } + +#ifdef RGFW_OPENGL +RGFW_bool RGFW_extensionSupportedPlatform_OpenGL(const char* extension, size_t len) { return RGFW_api.extensionSupportedPlatform_OpenGL(extension, len); } +RGFW_proc RGFW_getProcAddress_OpenGL(const char* procname) { return RGFW_api.getProcAddress_OpenGL(procname); } +RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) { return RGFW_api.window_createContextPtr_OpenGL(win, ctx, hints); } +void RGFW_window_deleteContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx) { RGFW_api.window_deleteContextPtr_OpenGL(win, ctx); } +void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win) { RGFW_api.window_makeCurrentContext_OpenGL(win); } +void* RGFW_getCurrentContext_OpenGL(void) { return RGFW_api.getCurrentContext_OpenGL(); } +void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) { RGFW_api.window_swapBuffers_OpenGL(win); } +void RGFW_window_swapInterval_OpenGL(RGFW_window* win, i32 swapInterval) { RGFW_api.window_swapInterval_OpenGL(win, swapInterval); } +#endif + +#ifdef RGFW_WEBGPU +WGPUSurface RGFW_window_createSurface_WebGPU(RGFW_window* window, WGPUInstance instance) { return RGFW_api.window_createSurface_WebGPU(window, instance); } +#endif +#endif /* RGFW_DYNAMIC */ + +/* + * start of X11 AND wayland defines + * this allows a single executable to support x11 AND wayland + * falling back to x11 if wayland fails to initalize +*/ +#if defined(RGFW_WAYLAND) && defined(RGFW_X11) +void RGFW_load_X11(void) { + RGFW_api.nativeFormat = RGFW_nativeFormat_X11; + RGFW_api.createSurfacePtr = RGFW_createSurfacePtr_X11; + RGFW_api.window_blitSurface = RGFW_window_blitSurface_X11; + RGFW_api.surface_freePtr = RGFW_surface_freePtr_X11; + RGFW_api.freeMouse = RGFW_freeMouse_X11; + RGFW_api.window_setBorder = RGFW_window_setBorder_X11; + RGFW_api.window_captureMousePlatform = RGFW_window_captureMousePlatform_X11; + RGFW_api.window_setRawMouseModePlatform = RGFW_window_setRawMouseModePlatform_X11; + RGFW_api.createWindowPlatform = RGFW_createWindowPlatform_X11; + RGFW_api.getGlobalMouse = RGFW_getGlobalMouse_X11; + RGFW_api.physicalToMappedKey = RGFW_physicalToMappedKey_X11; + RGFW_api.pollEvents = RGFW_pollEvents_X11; + RGFW_api.pollMonitors = RGFW_pollMonitors_X11; + RGFW_api.window_move = RGFW_window_move_X11; + RGFW_api.window_resize = RGFW_window_resize_X11; + RGFW_api.window_setAspectRatio = RGFW_window_setAspectRatio_X11; + RGFW_api.window_setMinSize = RGFW_window_setMinSize_X11; + RGFW_api.window_setMaxSize = RGFW_window_setMaxSize_X11; + RGFW_api.window_maximize = RGFW_window_maximize_X11; + RGFW_api.window_focus = RGFW_window_focus_X11; + RGFW_api.window_raise = RGFW_window_raise_X11; + RGFW_api.window_setFullscreen = RGFW_window_setFullscreen_X11; + RGFW_api.window_setFloating = RGFW_window_setFloating_X11; + RGFW_api.window_setOpacity = RGFW_window_setOpacity_X11; + RGFW_api.window_minimize = RGFW_window_minimize_X11; + RGFW_api.window_restore = RGFW_window_restore_X11; + RGFW_api.window_isFloating = RGFW_window_isFloating_X11; + RGFW_api.window_setName = RGFW_window_setName_X11; +#ifndef RGFW_NO_PASSTHROUGH + RGFW_api.window_setMousePassthrough = RGFW_window_setMousePassthrough_X11; +#endif + RGFW_api.window_setIconEx = RGFW_window_setIconEx_X11; + RGFW_api.loadMouse = RGFW_loadMouse_X11; + RGFW_api.window_setMouse = RGFW_window_setMouse_X11; + RGFW_api.window_moveMouse = RGFW_window_moveMouse_X11; + RGFW_api.window_setMouseDefault = RGFW_window_setMouseDefault_X11; + RGFW_api.window_setMouseStandard = RGFW_window_setMouseStandard_X11; + RGFW_api.window_hide = RGFW_window_hide_X11; + RGFW_api.window_show = RGFW_window_show_X11; + RGFW_api.window_flash = RGFW_window_flash_X11; + RGFW_api.readClipboardPtr = RGFW_readClipboardPtr_X11; + RGFW_api.writeClipboard = RGFW_writeClipboard_X11; + RGFW_api.window_isHidden = RGFW_window_isHidden_X11; + RGFW_api.window_isMinimized = RGFW_window_isMinimized_X11; + RGFW_api.window_isMaximized = RGFW_window_isMaximized_X11; + RGFW_api.monitor_requestMode = RGFW_monitor_requestMode_X11; + RGFW_api.monitor_getModesPtr = RGFW_monitor_getModesPtr_X11; + RGFW_api.monitor_setGammaRamp = RGFW_monitor_setGammaRamp_X11; + RGFW_api.monitor_getGammaRampPtr = RGFW_monitor_getGammaRampPtr_X11; + RGFW_api.monitor_setMode = RGFW_monitor_setMode_X11; + RGFW_api.window_getMonitor = RGFW_window_getMonitor_X11; + RGFW_api.window_closePlatform = RGFW_window_closePlatform_X11; +#ifdef RGFW_OPENGL + RGFW_api.extensionSupportedPlatform_OpenGL = RGFW_extensionSupportedPlatform_OpenGL_X11; + RGFW_api.getProcAddress_OpenGL = RGFW_getProcAddress_OpenGL_X11; + RGFW_api.window_createContextPtr_OpenGL = RGFW_window_createContextPtr_OpenGL_X11; + RGFW_api.window_deleteContextPtr_OpenGL = RGFW_window_deleteContextPtr_OpenGL_X11; + RGFW_api.window_makeCurrentContext_OpenGL = RGFW_window_makeCurrentContext_OpenGL_X11; + RGFW_api.getCurrentContext_OpenGL = RGFW_getCurrentContext_OpenGL_X11; + RGFW_api.window_swapBuffers_OpenGL = RGFW_window_swapBuffers_OpenGL_X11; + RGFW_api.window_swapInterval_OpenGL = RGFW_window_swapInterval_OpenGL_X11; +#endif +#ifdef RGFW_WEBGPU + RGFW_api.window_createSurface_WebGPU = RGFW_window_createSurface_WebGPU_X11; +#endif +} + +void RGFW_load_Wayland(void) { + RGFW_api.nativeFormat = RGFW_nativeFormat_Wayland; + RGFW_api.createSurfacePtr = RGFW_createSurfacePtr_Wayland; + RGFW_api.window_blitSurface = RGFW_window_blitSurface_Wayland; + RGFW_api.surface_freePtr = RGFW_surface_freePtr_Wayland; + RGFW_api.freeMouse = RGFW_freeMouse_Wayland; + RGFW_api.window_setBorder = RGFW_window_setBorder_Wayland; + RGFW_api.window_captureMousePlatform = RGFW_window_captureMousePlatform_Wayland; + RGFW_api.window_setRawMouseModePlatform = RGFW_window_setRawMouseModePlatform_Wayland; + RGFW_api.createWindowPlatform = RGFW_createWindowPlatform_Wayland; + RGFW_api.getGlobalMouse = RGFW_getGlobalMouse_Wayland; + RGFW_api.physicalToMappedKey = RGFW_physicalToMappedKey_Wayland; + RGFW_api.pollEvents = RGFW_pollEvents_Wayland; + RGFW_api.pollMonitors = RGFW_pollMonitors_Wayland; + RGFW_api.window_move = RGFW_window_move_Wayland; + RGFW_api.window_resize = RGFW_window_resize_Wayland; + RGFW_api.window_setAspectRatio = RGFW_window_setAspectRatio_Wayland; + RGFW_api.window_setMinSize = RGFW_window_setMinSize_Wayland; + RGFW_api.window_setMaxSize = RGFW_window_setMaxSize_Wayland; + RGFW_api.window_maximize = RGFW_window_maximize_Wayland; + RGFW_api.window_focus = RGFW_window_focus_Wayland; + RGFW_api.window_raise = RGFW_window_raise_Wayland; + RGFW_api.window_setFullscreen = RGFW_window_setFullscreen_Wayland; + RGFW_api.window_setFloating = RGFW_window_setFloating_Wayland; + RGFW_api.window_setOpacity = RGFW_window_setOpacity_Wayland; + RGFW_api.window_minimize = RGFW_window_minimize_Wayland; + RGFW_api.window_restore = RGFW_window_restore_Wayland; + RGFW_api.window_isFloating = RGFW_window_isFloating_Wayland; + RGFW_api.window_setName = RGFW_window_setName_Wayland; +#ifndef RGFW_NO_PASSTHROUGH + RGFW_api.window_setMousePassthrough = RGFW_window_setMousePassthrough_Wayland; +#endif + RGFW_api.window_setIconEx = RGFW_window_setIconEx_Wayland; + RGFW_api.loadMouse = RGFW_loadMouse_Wayland; + RGFW_api.window_setMouse = RGFW_window_setMouse_Wayland; + RGFW_api.window_moveMouse = RGFW_window_moveMouse_Wayland; + RGFW_api.window_setMouseDefault = RGFW_window_setMouseDefault_Wayland; + RGFW_api.window_setMouseStandard = RGFW_window_setMouseStandard_Wayland; + RGFW_api.window_hide = RGFW_window_hide_Wayland; + RGFW_api.window_show = RGFW_window_show_Wayland; + RGFW_api.window_flash = RGFW_window_flash_X11; + RGFW_api.readClipboardPtr = RGFW_readClipboardPtr_Wayland; + RGFW_api.writeClipboard = RGFW_writeClipboard_Wayland; + RGFW_api.window_isHidden = RGFW_window_isHidden_Wayland; + RGFW_api.window_isMinimized = RGFW_window_isMinimized_Wayland; + RGFW_api.window_isMaximized = RGFW_window_isMaximized_Wayland; + RGFW_api.monitor_requestMode = RGFW_monitor_requestMode_Wayland; + RGFW_api.monitor_getModesPtr = RGFW_monitor_getModesPtr_Wayland; + RGFW_api.monitor_setGammaRamp = RGFW_monitor_setGammaRamp_Wayland; + RGFW_api.monitor_getGammaRampPtr = RGFW_monitor_getGammaRampPtr_Wayland; + RGFW_api.monitor_setMode = RGFW_monitor_setMode_Wayland; + RGFW_api.window_getMonitor = RGFW_window_getMonitor_Wayland; + RGFW_api.window_closePlatform = RGFW_window_closePlatform_Wayland; +#ifdef RGFW_OPENGL + RGFW_api.extensionSupportedPlatform_OpenGL = RGFW_extensionSupportedPlatform_OpenGL_Wayland; + RGFW_api.getProcAddress_OpenGL = RGFW_getProcAddress_OpenGL_Wayland; + RGFW_api.window_createContextPtr_OpenGL = RGFW_window_createContextPtr_OpenGL_Wayland; + RGFW_api.window_deleteContextPtr_OpenGL = RGFW_window_deleteContextPtr_OpenGL_Wayland; + RGFW_api.window_makeCurrentContext_OpenGL = RGFW_window_makeCurrentContext_OpenGL_Wayland; + RGFW_api.getCurrentContext_OpenGL = RGFW_getCurrentContext_OpenGL_Wayland; + RGFW_api.window_swapBuffers_OpenGL = RGFW_window_swapBuffers_OpenGL_Wayland; + RGFW_api.window_swapInterval_OpenGL = RGFW_window_swapInterval_OpenGL_Wayland; +#endif +#ifdef RGFW_WEBGPU + RGFW_api.window_createSurface_WebGPU = RGFW_window_createSurface_WebGPU_Wayland; +#endif +} +#endif /* wayland AND x11 */ +/* end of X11 AND wayland defines */ + +#endif /* RGFW_IMPLEMENTATION */ + +#if defined(__cplusplus) && !defined(__EMSCRIPTEN__) +} +#endif + +#if _MSC_VER + #pragma warning( pop ) +#endif diff --git a/src/external/RGFW/deps/minigamepad.h b/src/external/RGFW/deps/minigamepad.h new file mode 100644 index 000000000..dfadcbf40 --- /dev/null +++ b/src/external/RGFW/deps/minigamepad.h @@ -0,0 +1,5242 @@ +/* +* +* minigamepad - alpha + +* Copyright (C) 2025 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 MG_IMPLEMENTATION is in exactly one header or you use -D MG_IMPLEMENTATION) + #define MG_IMPLEMENTATION - makes it so source code is included with header +*/ + +/* + #define MG_IMPLEMENTATION - (required) makes it so the source code is included + #define MG_ALLOC x - choose the default allocation function (defaults to standard malloc) + #define MG_FREE x - choose the default deallocation function (defaults to standard free) + + #define MG_EXPORT - use when building minigamepad + #define MG_IMPORT - use when linking with minigamepad (not as a single-header) + + #define MG_USE_INT - force the use c-types rather than stdint.h (for systems that might not have stdint.h (msvc)) + #define mg_bool x - choose what type to use for bool, by default u32 is used + + #define MG_MAX_GAMEPADS x - set the max number of gamepads (4 by default) +*/ + + + +/* + Credits : + GLFW and SDL - used as a resource + https:///github.com/MysteriousJ/Joystick-Input-Examples - good resoruce for learning how to use gamepad code + + contributors : (feel free to put yourself here if you contribute) +*/ + +#if _MSC_VER + #if _MSC_VER < 600 + #define MG_C89 + #endif +#else + #if defined(__STDC__) && !defined(__STDC_VERSION__) + #define MG_C89 + #endif +#endif + +#ifndef MG_UNUSED + #define MG_UNUSED(x) (void)(x) +#endif + +#ifndef MG_ALLOC + #include + #define MG_ALLOC malloc + #define MG_FREE free +#endif + +#ifndef MG_ASSERT + #include + #define MG_ASSERT assert +#endif + +#if !defined(MG_MEMCPY) || !defined(MG_STRNCMP) || !defined(MG_STRNCPY) || !defined(MG_MEMSET) || !defined(MG_STRCSPN) || !defined(MG_STRSPN) + #include +#endif + +#ifndef MG_MEMSET + #define MG_MEMSET(ptr, value, num) memset(ptr, value, num) +#endif + +#ifndef MG_MEMCPY + #define MG_MEMCPY(dist, src, len) memcpy(dist, src, len) +#endif + +#ifndef MG_STRNCMP + #define MG_STRNCMP(s1, s2, max) strncmp(s1, s2, max) +#endif + +#ifndef MG_STRNCPY + #define MG_STRNCPY(dist, src, len) strncpy(dist, src, len) +#endif + +#ifndef MG_STRCSPN + #define MG_STRCSPN(str1, str2) strcspn(str1, str2) +#endif + +#ifndef MG_STRSPN + #define MG_STRSPN(str1, str2) strspn(str1, str2) +#endif + +#if !defined(MG_SPRINTF) || !defined(MG_FPRINTF) + #include +#endif + + +#if !defined(MG_SPRINTF) + #define MG_SPRINTF sprintf +#endif + +#if !defined(MG_FPRINTF) + #define MG_FPRINTF fprintf +#endif + +#ifndef MG_FABS +#include +#define MG_FABS(x) fabs(x) +#endif + +#if defined(MG_EXPORT) || defined(MG_IMPORT) + #if defined(_WIN32) + #if defined(__TINYC__) && (defined(MG_EXPORT) || defined(MG_IMPORT)) + #define __declspec(x) __attribute__((x)) + #endif + + #if defined(MG_EXPORT) + #define MG_API __declspec(dllexport) + #else + #define MG_API __declspec(dllimport) + #endif + #else + #if defined(MG_EXPORT) + #define MG_API __attribute__((visibility("default"))) + #endif + #endif + #ifndef MG_API + #define MG_API + #endif +#endif + +#ifndef MG_API + #ifdef MG_C89 + #define MG_API + #else + #define MG_API inline + #endif +#endif + +#ifndef MG_ENUM + #define MG_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 MG_HEADER + +#define MG_HEADER + +#ifdef __EMSCRIPTEN__ + #define MG_WASM +#elif defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) + #define MG_WINDOWS +#elif defined(__linux__) + #define MG_LINUX +#elif defined(__APPLE__) + #define MG_MACOS +#endif + +#ifndef MG_INT_DEFINED + #ifdef MG_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; + + + typedef signed long mg_ssize_t; + typedef unsigned long mg_size_t; + #else /* use stdint standard types instead of c "standard" types */ + #include + #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; + +#ifdef __linux__ + #include + typedef ssize_t mg_ssize_t; +#endif + typedef size_t mg_size_t; + #endif + #define MG_INT_DEFINED +#endif + +#ifndef MG_BOOL_DEFINED + #define MG_BOOL_DEFINED + typedef u8 mg_bool; +#endif + +#define MG_BOOL(x) (mg_bool)((x) ? MG_TRUE : MG_FALSE) /* force an value to be 0 or 1 */ +#define MG_TRUE (mg_bool)1 +#define MG_FALSE (mg_bool)0 + +#ifndef MG_MAX_GAMEPADS + #define MG_MAX_GAMEPADS 4 +#endif + +#ifndef MG_MAX_EVENTS + #define MG_MAX_EVENTS 32 +#endif + +/**! + * @brief global stucture for the source API data of all the gamepads +*/ +typedef struct mg_gamepads_src mg_gamepads_src; + +/**! + * @brief global stucture for the data of all the gamepads +*/ +typedef struct mg_gamepads mg_gamepads; + +/**! + * @brief the source API data of the gamepad object +*/ +typedef struct mg_gamepad_src mg_gamepad_src; + +/**! + * @brief data stucture for gamepad objects +*/ +typedef struct mg_gamepad mg_gamepad; + +/**! + * @brief source gamepad list object +*/ +typedef struct mg_gamepad_list { + mg_gamepad* head; /* head node of the list */ + mg_gamepad* cur; /* current/tail node of the list */ + mg_size_t count; /* number of nodes */ +} mg_gamepad_list; + +/**! + * @brief abstract constants for gamepad buttons +*/ +typedef MG_ENUM(i8, mg_button) { + MG_BUTTON_UNKNOWN = -1, + MG_BUTTON_SOUTH, /**< Bottom face button (e.g. Xbox A button) */ + MG_BUTTON_EAST, /**< Right face button (e.g. Xbox B button) */ + MG_BUTTON_WEST, /**< Left face button (e.g. Xbox X button) */ + MG_BUTTON_NORTH, /**< Top face button (e.g. Xbox Y button) */ + MG_BUTTON_BACK, /**< back (or select) button on the gamepad */ + MG_BUTTON_GUIDE, /**< guide button on the controller (e.g. the Xbox button or ps button) */ + MG_BUTTON_START, /**< start button on the gamepad */ + MG_BUTTON_LEFT_STICK, /**< left stick button (L3) */ + MG_BUTTON_RIGHT_STICK, /**< left shoulder button (R4) */ + MG_BUTTON_LEFT_SHOULDER, /**< left shoulder button (L1) */ + MG_BUTTON_RIGHT_SHOULDER, /**< left shoulder button (R1) */ + MG_BUTTON_DPAD_LEFT, /**< dpad left button */ + MG_BUTTON_DPAD_RIGHT, /**< dpad right button */ + MG_BUTTON_DPAD_UP, /**< dpad up button */ + MG_BUTTON_DPAD_DOWN, /**< dpad down button */ + MG_BUTTON_LEFT_TRIGGER, /**< left trigger button (L2) */ + MG_BUTTON_RIGHT_TRIGGER, /**< right trigger button (R2) */ + /* extras */ + MG_BUTTON_MISC1, /**< Additional button (e.g. Xbox Series X share button, PS5 microphone button, Nintendo Switch Pro capture button, Amazon Luna microphone button, Google Stadia capture button) */ + MG_BUTTON_RIGHT_PADDLE1, /**< Upper or primary paddle, under your right hand (e.g. Xbox Elite paddle P1) */ + MG_BUTTON_LEFT_PADDLE1, /**< Upper or primary paddle, under your left hand (e.g. Xbox Elite paddle P3) */ + MG_BUTTON_RIGHT_PADDLE2, /**< Lower or secondary paddle, under your right hand (e.g. Xbox Elite paddle P2) */ + MG_BUTTON_LEFT_PADDLE2, /**< Lower or secondary paddle, under your left hand (e.g. Xbox Elite paddle P4) */ + MG_BUTTON_TOUCHPAD, /**< PS4/PS5 touchpad button */ + MG_BUTTON_MISC2, /**< Additional button */ + MG_BUTTON_MISC3, /**< Additional button */ + MG_BUTTON_MISC4, /**< Additional button */ + MG_BUTTON_MISC5, /**< Additional button */ + MG_BUTTON_MISC6, /**< Additional button */ + MG_BUTTON_COUNT +}; + +/**! + * @brief abstract constants for gamepad axes +*/ +typedef MG_ENUM(i8, mg_axis) { + MG_AXIS_UNKNOWN = -1, /**< */ + MG_AXIS_LEFT_X, /**< X axis of the left stick */ + MG_AXIS_LEFT_Y, /**< Y axis of the left stick */ + MG_AXIS_RIGHT_X, /**< X axis of the right stick */ + MG_AXIS_RIGHT_Y, /**< Y axis of the left stick */ + MG_AXIS_LEFT_TRIGGER, /**< Axis of the left triggle (0 - 1) */ + MG_AXIS_RIGHT_TRIGGER, /**< Axis of the right triggle (0 - 1) */ + MG_AXIS_HAT_DPAD_LEFT_RIGHT, /**< d-pad left-right hat value */ + MG_AXIS_HAT_DPAD_LEFT = MG_AXIS_HAT_DPAD_LEFT_RIGHT, + MG_AXIS_HAT_DPAD_RIGHT = MG_AXIS_HAT_DPAD_LEFT_RIGHT, + MG_AXIS_HAT_DPAD_UP_DOWN, /**< d-pad up-down hat value */ + MG_AXIS_HAT_DPAD_UP = MG_AXIS_HAT_DPAD_UP_DOWN, /**< */ + MG_AXIS_HAT_DPAD_DOWN = MG_AXIS_HAT_DPAD_UP_DOWN, +/* extras */ + MG_AXIS_THROTTLE, + MG_AXIS_RUDDER, + MG_AXIS_WHEEL, + MG_AXIS_GAS, + MG_AXIS_BRAKE, + MG_AXIS_HAT1X, + MG_AXIS_HAT1Y, + MG_AXIS_HAT2X, + MG_AXIS_HAT2Y, + MG_AXIS_HAT3X, + MG_AXIS_HAT3Y, + MG_AXIS_PRESSURE, + MG_AXIS_DISTANCE, + MG_AXIS_TILT_X, + MG_AXIS_TILT_Y, + MG_AXIS_TOOL_WIDTH, + MG_AXIS_VOLUME, + MG_AXIS_PROFILE, + MG_AXIS_MISC, + + MG_AXIS_COUNT +}; + +/**! + * @brief the type of event in the queue +*/ +typedef MG_ENUM(u8, mg_event_type) { + MG_EVENT_NONE = 0, /**< no/null event */ + MG_EVENT_GAMEPAD_CONNECT, /**< a new gamepad connected */ + MG_EVENT_GAMEPAD_DISCONNECT, /**< a gamepad was disconnected */ + MG_EVENT_BUTTON_PRESS, /**< gamepad button was pressed */ + MG_EVENT_BUTTON_RELEASE, /**< gamepad button was released */ + MG_EVENT_AXIS_MOVE /**< gamepad axis was moved/changed */ +}; + +/**! + * @brief button (press or release) event type +*/ +typedef struct mg_button_state { + mg_bool supported; /* if the button is supported or not by the gamepad */ + mg_bool current; /* the current state of the button */ + mg_bool prev; /* the previous state of the button */ +} mg_button_state; + +/**! + * @brief axis motion event type +*/ +typedef struct mg_axis_state { + mg_bool supported; /* if the axis is supported or not by the gamepad */ + float value; /* the current value of the axis */ + float deadzone; /* deadzones of the axis */ +} mg_axis_state; + +/**! + * @brief event structure for processing event data +*/ +typedef struct mg_event { + mg_event_type type; /* the type of event */ + mg_button button; /* button value */ + mg_axis axis; /* axis value */ + mg_gamepad* gamepad; /* which gamepad */ +} mg_event; + +/**! + * @brief stucture for all event data for collecting events +*/ +typedef struct mg_events { + mg_event queue[MG_MAX_EVENTS]; /* event queue array for collecting the frame's events */ + size_t len; /* the number of events in the array */ +} mg_events; + +/* callbacks */ +/**! + * @brief type for the connection callback function +*/ +typedef void (*mg_gamepad_connection_func)(mg_gamepad* gamepad, mg_bool connected); + +/**! + * @brief type for the button state callback function +*/ +typedef void (*mg_gamepad_button_func)(mg_gamepad* gamepad, mg_button button, mg_bool pressed); + +/**! + * @brief type for the axis move callback function +*/ +typedef void (*mg_gamepad_axis_func)(mg_gamepad* gamepad, mg_axis); + +/**! + * @brief init gamepads, api and internal data + * @param pointer to a pre-allocated gamepads object +*/ +MG_API void mg_gamepads_init(mg_gamepads* gamepads); + +/**! + * @brief enable or disable the event queue [disabled by default and enabled by default when mg_gamepads_check_event is called] + * @param gamepads object to modify +*/ +MG_API void mg_gamepads_set_queue_events(mg_gamepads* gamepads, mg_bool queue_events); + +/**! + * @brief poll information on the gamepads + * @param gamepads object that needs to be updated + * @return returns a boolean value if there was an event or not to process +*/ +MG_API mg_bool mg_gamepads_poll(mg_gamepads* gamepads); + +/**! + * @brief check and pop top event in the gamepad's event queue, does not poll for events + * @param gamepads object that needs to be updated + * @param pointer to a event object to fill the event with [can be NULL] + * @return returns a boolean value if there was an event or not to process +*/ +MG_API mg_bool mg_gamepads_check_queued_event(mg_gamepads* gamepads, mg_event* event); + +/**! + * @brief poll for events if they weren't already polled then check and pop the top event in the event queue + * @param gamepads object that needs to be updated + * @param pointer to a event object to fill the event with [can be NULL] + * @return returns a boolean value if there was an event or not to process +*/ +MG_API mg_bool mg_gamepads_check_event(mg_gamepads* gamepads, mg_event* event); + +/**! + * @brief free internal gamepads data + * @param gamepads object +*/ +MG_API void mg_gamepads_free(mg_gamepads* gamepads); + +/**! + * @brief returns if a button of a gamepad was pressed or not + * @param gamepad object + * @param button to check + * @return boolean value button for if the button was pressed +*/ + +MG_API mg_bool mg_gamepad_button_is_pressed(mg_gamepad* gamepad, mg_button button); + + +/* add a new mapping */ +/**! + * @brief returns if a button of a gamepad was released down or not + * @param the gamepad object + * @param button to check + * @return the boolean value for if the button was released +*/ + +MG_API mg_bool mg_gamepad_button_is_released(mg_gamepad* gamepad, mg_button button); + +/* add a new mapping */ +/**! + * @brief returns if a button of a gamepad is down or not + * @param the gamepad object + * @param the button to check + * @return the boolean state of the button +*/ + +MG_API mg_bool mg_gamepad_button_is_down(mg_gamepad* gamepad, mg_button button); + +/* add a new mapping */ +/**! + * @brief returns the axis value of an axis of a gamepad + * @param the source gamepad object + * @param the axis to check + * @return the current floating point value of the axis +*/ + +MG_API float mg_gamepad_axis_value(mg_gamepad* gamepad, mg_axis axis); + +/* add a new mapping */ +/**! + * @brief set the function object for gamepad connection events + * @param the function object to use + * @return the original function object in use +*/ + +MG_API mg_gamepad_connection_func mg_set_gamepad_connected_callback(mg_gamepad_connection_func func); + +/**! + * @brief set the function object for gamepad release events + * @param the function object to use + * @return the original function object in use +*/ +MG_API mg_gamepad_button_func mg_set_gamepad_release_callback(mg_gamepad_button_func func); + +/**! + * @brief set the function object for gamepad axis movement aevents + * @param the function object to use + * @return the current function object in use +*/ +MG_API mg_gamepad_axis_func mg_set_gamepad_axis_callback(mg_gamepad_axis_func func); + +/**! + * @brief set the gamepad disconnected callback function object + * @param the function object to use + * @return the original value of the disconnected callback +*/ + +MG_API mg_gamepad_connection_func mg_set_gamepad_disconnected_callback(mg_gamepad_connection_func func); + +/**! + * @brief set the gamepad press callbqack function to use + * @param the function object to use + * @return the original value of the gamepad press callback +*/ +MG_API mg_gamepad_button_func mg_set_gamepad_press_callback(mg_gamepad_button_func func); + +/* add a new mapping */ +/**! + * @brief update the mappings the gamepads object uses with a new mapping + * @param the new mapping string + * @return returns a boolean value based on success +*/ +MG_API mg_bool mg_update_gamepad_mappings(mg_gamepads* gamepads, const char* string); + +/**! + * @brief fetch the string name of a button (for testing) + * @param the button enum value + * @return constant c-string that represents the button's name +*/ +MG_API const char* mg_button_get_name(mg_button button); + +/**! + * @brief fetch the string name of a axis (for testing) + * @param the axis enum value + * @return constant c-string that represents the axis's name +*/ +MG_API const char* mg_axis_get_name(mg_axis axis); + +#endif /* MG_HEADER */ + +#if (defined(MG_NATIVE) || defined(MG_IMPLEMENTATION)) && !defined(MG_NATIVE_HEADER) +#define MG_NATIVE_HEADER + +#ifdef MG_LINUX + +struct mg_input_absinfo { + i32 value; + i32 minimum; + i32 maximum; + i32 fuzz; + i32 flat; + i32 resolution; +}; + +struct mg_gamepad_src { + int fd; + u8 keyMap[512]; + u8 absMap[64]; + struct mg_input_absinfo absInfo[64]; + char full_path[256]; +}; + +#elif defined(MG_WINDOWS) + +struct mg_gamepad_src { + void* device; + u32 xinput_index; +}; + +#elif defined(MG_MACOS) + +struct mg_gamepad_src { + void* device; + void* events; +}; + +#elif defined(MG_WASM) + +struct mg_gamepad_src { + int index; +}; + +#endif + +struct mg_mapping; + +struct mg_gamepad { + char name[128]; + char guid[33]; + + mg_button_state buttons[MG_BUTTON_COUNT]; + mg_axis_state axes[MG_AXIS_COUNT]; + + mg_bool connected; + mg_size_t index; /* index in gamepad array */ + + struct mg_mapping* mapping; + struct mg_gamepad* prev; + struct mg_gamepad* next; + mg_gamepad_src src; +}; + +#ifdef MG_LINUX +#include +#include + +struct mg_gamepads_src { + int inotify, watch; +}; +#elif defined(MG_WINDOWS) +struct mg_gamepads_src { + void* dinput; + void* ginput; + void* dummy_win; +}; +#elif defined(MG_MACOS) +struct mg_gamepads_src { + void* hidManager; +}; +#elif defined(MG_WASM) +struct mg_gamepads_src { + int TODO; +}; +#endif + +struct mg_gamepads { + mg_gamepad gamepads[MG_MAX_GAMEPADS]; + + mg_gamepad_list list; + mg_gamepad_list free_list; + + mg_events events; + + mg_bool queue_events; + mg_bool polled_events; + + mg_gamepads_src src; +}; + +#endif /* MG_NATIVE */ + +#ifdef MG_IMPLEMENTATION + +/* gamepads->src.global API */ +/* find a valid unused gamepad or return NULL */ +MG_API mg_gamepad* mg_gamepad_find(mg_gamepads* gamepads); +MG_API void mg_gamepad_release(mg_gamepads* gamepads, mg_gamepad* gamepad); +MG_API void mg_list_swap_gamepad(mg_gamepad_list* from, mg_gamepad_list* to, mg_gamepad* gamepad); + +/* gamepads->src.platform-specific API */ +MG_API void mg_gamepads_init_platform(mg_gamepads* gamepads); +/* updates gamepad structure by checking if any new gamepads are connected and adding them */ +MG_API mg_bool mg_gamepads_poll_platform(mg_gamepads* gamepads, mg_events* events); +MG_API void mg_gamepads_free_platform(mg_gamepads* gamepads); +MG_API mg_bool mg_gamepad_update_platform(mg_gamepad* gamepad, mg_events* events); +MG_API void mg_gamepad_release_platform(mg_gamepad* gamepad); +MG_API mg_button mg_get_gamepad_button_platform(u32 button); +MG_API mg_axis mg_get_gamepad_axis_platform(u32 axis); + +/* gamepads->src.mappings API */ +MG_API struct mg_mapping* mg_gamepad_find_valid_mapping(mg_gamepad* gamepad); +MG_API mg_button mg_get_gamepad_button(mg_gamepad* gamepad, u8 button); +MG_API mg_axis mg_get_gamepad_axis(mg_gamepad* gamepad, u8 axis); +MG_API void mg_mappings_init(void); +/* public/global API implementation */ + +mg_bool mg_gamepad_button_is_pressed(mg_gamepad* gamepad, mg_button button) { + return gamepad->buttons[button].current; +} + +mg_bool mg_gamepad_button_is_released(mg_gamepad* gamepad, mg_button button) { + return gamepad->buttons[button].prev && !gamepad->buttons[button].current; +} + +mg_bool mg_gamepad_button_is_down(mg_gamepad* gamepad, mg_button button) { + return gamepad->buttons[button].prev && gamepad->buttons[button].current; +} + +float mg_gamepad_axis_value(mg_gamepad* gamepad, mg_axis axis) { + return gamepad->axes[axis].value; +} + +static mg_gamepad_connection_func mg_gamepad_connected_callback = NULL; + +mg_gamepad_connection_func mg_set_gamepad_connected_callback(mg_gamepad_connection_func func) { + mg_gamepad_connection_func prev = mg_gamepad_connected_callback; + mg_gamepad_connected_callback = func; + return prev; +} + +static mg_gamepad_connection_func mg_gamepad_disconnected_callback = NULL; + +mg_gamepad_connection_func mg_set_gamepad_disconnected_callback(mg_gamepad_connection_func func) { + mg_gamepad_connection_func prev = mg_gamepad_disconnected_callback; + mg_gamepad_disconnected_callback = func; + return prev; +} + +static mg_gamepad_button_func mg_gamepad_press_callback = NULL; + +mg_gamepad_button_func mg_set_gamepad_press_callback(mg_gamepad_button_func func) { + mg_gamepad_button_func prev = mg_gamepad_press_callback; + mg_gamepad_press_callback = func; + return prev; +} + +static mg_gamepad_button_func mg_gamepad_release_callback = NULL; +#define mg_release_callback(gamepad, button) if (mg_gamepad_release_callback) mg_gamepad_release_callback(gamepad, button, MG_FALSE) + +mg_gamepad_button_func mg_set_gamepad_release_callback(mg_gamepad_button_func func) { + mg_gamepad_button_func prev = mg_gamepad_release_callback; + mg_gamepad_release_callback = func; + return prev; +} + +static mg_gamepad_axis_func mg_gamepad_axis_callback = NULL; +mg_gamepad_axis_func mg_set_gamepad_axis_callback(mg_gamepad_axis_func func) { + mg_gamepad_axis_func prev = mg_gamepad_axis_callback; + mg_gamepad_axis_callback = func; + return prev; +} + +void mg_handle_event(mg_events* events, mg_event_type type, mg_button btn, mg_axis axis, mg_bool state, float value, mg_gamepad* gamepad) { + mg_event event; + event.gamepad = gamepad; + event.axis = axis; + event.button = btn; + + switch (type) { + case MG_EVENT_GAMEPAD_CONNECT: + case MG_EVENT_GAMEPAD_DISCONNECT: + if (state) { + type = MG_EVENT_GAMEPAD_CONNECT; + if (mg_gamepad_connected_callback) mg_gamepad_connected_callback(gamepad, state); + } else { + type = MG_EVENT_GAMEPAD_DISCONNECT; + if (mg_gamepad_disconnected_callback) mg_gamepad_disconnected_callback(gamepad, state); + } + break; + case MG_EVENT_BUTTON_PRESS: + case MG_EVENT_BUTTON_RELEASE: + if (state == gamepad->buttons[btn].current) { + return; + } + + gamepad->buttons[btn].prev = gamepad->buttons[btn].current; + gamepad->buttons[btn].current = state; + if (state) { + type = MG_EVENT_BUTTON_PRESS; + if (mg_gamepad_press_callback) mg_gamepad_press_callback(gamepad, btn, state); + } + else { + type = MG_EVENT_BUTTON_RELEASE; + if (mg_gamepad_release_callback) mg_gamepad_release_callback(gamepad, btn, state); + } + break; + case MG_EVENT_AXIS_MOVE: + if (value == gamepad->axes[axis].value) { + return; + } + + gamepad->axes[axis].value = value; + if (mg_gamepad_axis_callback) mg_gamepad_axis_callback(gamepad, axis); + break; + default: break; + } + + /* push event */ + if (events == NULL || events->len >= MG_MAX_EVENTS) { + return; + } + + event.type = type; + events->len += 1; + events->queue[MG_MAX_EVENTS - events->len] = event; +} + +#define mg_handle_connection_event(events, state, gamepad) mg_handle_event(events, MG_EVENT_GAMEPAD_CONNECT, 0, 0, state, 0, gamepad) +#define mg_handle_button_event(events, btn, state, gamepad) mg_handle_event(events, MG_EVENT_BUTTON_PRESS, btn, 0, state, 0, gamepad) +#define mg_handle_axis_event(events, axis, value, gamepad) mg_handle_event(events, MG_EVENT_AXIS_MOVE, 0, axis, 0, value, gamepad) + +void mg_gamepads_init(mg_gamepads* gamepads) { + MG_ASSERT(gamepads != NULL); + + gamepads->polled_events = MG_FALSE; + gamepads->queue_events = MG_FALSE; + + mg_mappings_init(); + MG_MEMSET(gamepads, 0, sizeof(mg_gamepads)); + + /* create free list */ + gamepads->free_list.head = &gamepads->gamepads[0]; + gamepads->free_list.cur = gamepads->free_list.head; + + { + mg_size_t i; + for (i = 0; i < MG_MAX_GAMEPADS; i++) { + gamepads->free_list.cur->prev = NULL; + gamepads->free_list.cur->next = NULL; + + if (i) { + gamepads->free_list.cur->prev = &gamepads->gamepads[i - 1]; + } + + if (i != MG_MAX_GAMEPADS - 1) { + gamepads->free_list.cur->next = &gamepads->gamepads[i + 1]; + gamepads->free_list.cur = gamepads->free_list.cur->next; + } + } + } + + mg_gamepads_init_platform(gamepads); +} + +void mg_gamepads_set_queue_events(mg_gamepads* gamepads, mg_bool queue_events) { + gamepads->polled_events = queue_events; +} + +mg_bool mg_gamepads_poll(mg_gamepads* gamepads) { + mg_gamepad* cur; + mg_bool out = MG_FALSE; + + mg_events* events = (gamepads->queue_events) ? &gamepads->events : NULL; + + if (mg_gamepads_poll_platform(gamepads, events)) { + out = MG_TRUE; + } + + for (cur = gamepads->list.head; cur != NULL; cur = cur->next) { + if (mg_gamepad_update_platform(cur, events)) { + out = MG_TRUE; + } + } + + return out; +} + +mg_bool mg_events_pop(mg_events* events, mg_event* ev) { + MG_ASSERT(events->len <= MG_MAX_EVENTS); + + if (events->len == 0) { + return MG_FALSE; + } + + if (ev) { + *ev = events->queue[MG_MAX_EVENTS - events->len]; + } + + events->len -= 1; + return MG_TRUE; +} + +mg_bool mg_gamepads_check_event(mg_gamepads* gamepads, mg_event* event) { + if (gamepads->events.len == 0 && gamepads->polled_events == MG_FALSE) { + gamepads->queue_events = MG_TRUE; + mg_gamepads_poll(gamepads); + gamepads->polled_events = MG_TRUE; + } + + if (mg_gamepads_check_queued_event(gamepads, event) == MG_FALSE) { + gamepads->polled_events = MG_FALSE; + return MG_FALSE; + } + + return MG_TRUE; +} + +mg_bool mg_gamepads_check_queued_event(mg_gamepads* gamepads, mg_event* event) { + MG_ASSERT(gamepads != NULL); + gamepads->polled_events = MG_TRUE; + + /* check queued events */ + return mg_events_pop(&gamepads->events, event); +} + +void mg_gamepads_free(mg_gamepads* gamepads) { + mg_gamepad* cur; + MG_ASSERT(gamepads != NULL); + + mg_gamepads_free_platform(gamepads); + + for (cur = gamepads->list.cur; cur != NULL; cur = cur->prev) { + mg_gamepad_release(gamepads, cur); + } + MG_MEMSET(gamepads, 0, sizeof(mg_gamepads)); +} + +void mg_list_swap_gamepad(mg_gamepad_list* from, mg_gamepad_list* to, mg_gamepad* gamepad) { + if (gamepad->prev != NULL) { + gamepad->prev->next = gamepad->next; + } + + if (gamepad->next != NULL) { + gamepad->next->prev = gamepad->prev; + } + + if (from->cur == gamepad) { + from->cur = gamepad->prev; + } + + if (from->head == gamepad) { + from->head = gamepad->next; + } + + MG_MEMSET(gamepad, 0, sizeof(mg_gamepad)); + + if (to->head == NULL) { + to->head = gamepad; + to->cur = NULL; + } else if (to->cur) { + to->cur->next = gamepad; + } + + gamepad->prev = to->cur; + to->cur = gamepad; +} + +mg_gamepad* mg_gamepad_find(mg_gamepads* gamepads) { + mg_gamepad* gamepad = gamepads->free_list.cur; + if (gamepad == NULL) + return NULL; + + mg_list_swap_gamepad(&gamepads->free_list, &gamepads->list, gamepad); + gamepad->index = (mg_size_t)(gamepad - gamepads->gamepads); + return gamepad; +} + + +void mg_gamepad_release(mg_gamepads* gamepads, mg_gamepad* gamepad) { + mg_gamepad_release_platform(gamepad); + mg_list_swap_gamepad(&gamepads->list, &gamepads->free_list, gamepad); +} + +/* + * start of linux + */ + +#if defined(MG_LINUX) + +#include +#include +#include +#include +#include +#include +#include +#include + +mg_gamepad* mg_linux_setup_gamepad(mg_gamepads* gamepads, const char* full_path) { + int fd = 0; + struct input_id id = {0}; + char evBits[(EV_CNT + 7) / 8] = {0}; + char keyBits[(KEY_CNT + 7) / 8] = {0}; + char absBits[(ABS_CNT + 7) / 8] = {0}; + u32 i, btn, axis; + mg_size_t buttonCount = 0, axisCount = 0; + + mg_gamepad* gamepad = mg_gamepad_find(gamepads); + if (gamepad == NULL) { + return NULL; + } + + MG_STRNCPY(gamepad->src.full_path, full_path, 256); + + gamepad->src.fd = open(full_path, O_RDWR); + + fd = gamepad->src.fd; + if (fd <= 0) { + mg_gamepad_release(gamepads, gamepad); + return NULL; + } + + if (ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits) < 0 || + ioctl(fd, EVIOCGID, &id) < 0 || + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits) < 0 || + ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits) < 0 + ) { + mg_gamepad_release(gamepads, gamepad); + return NULL; + } + + #define isBitSet(bit, arr) (arr[(bit) / 8] & (1 << ((bit) % 8))) + if (!isBitSet(EV_ABS, evBits)) { + mg_gamepad_release(gamepads, gamepad); + return NULL; + } + + memset(gamepad->buttons, 0, sizeof(gamepad->buttons)); + memset(gamepad->buttons, 0, sizeof(gamepad->axes)); + + /* go through any buttons a gamepad would have */ + for (i = BTN_MISC; i < KEY_CNT; i++) { + if (!isBitSet(i, keyBits)) + continue; + + gamepad->src.keyMap[i - BTN_MISC] = (u8)buttonCount; + buttonCount++; + } + + /* go through any axes a gamepad would have */ + for (i = 0; i < ABS_CNT; i++) { + if (!isBitSet(i, absBits)) + continue; + + if (ioctl(fd, EVIOCGABS(i), (struct input_absinfo*)&gamepad->src.absInfo[i]) < 0) + continue; + + gamepad->src.absMap[i] = (u8)axisCount; + axisCount++; + } + + if ((axisCount == 0 && buttonCount == 0) || buttonCount > MG_BUTTON_COUNT + 10) { + mg_gamepad_release(gamepads, gamepad); + return NULL; + } + + /* Generate a joystick GUID that matches the SDL 2.0.5+ one (sourced from GLFW) */ + if (ioctl(gamepad->src.fd, EVIOCGNAME(sizeof(gamepad->name)), gamepad->name) < 0) + MG_STRNCPY(gamepad->name, "Unknown", sizeof(gamepad->name)); + + if (id.vendor && id.product && id.version) { + MG_SPRINTF(gamepad->guid, "%02x%02x0000%02x%02x0000%02x%02x0000%02x%02x0000", + id.bustype & 0xff, id.bustype >> 8, + id.vendor & 0xff, id.vendor >> 8, + id.product & 0xff, id.product >> 8, + id.version & 0xff, id.version >> 8); + } else { + const char* name = (const char*)gamepad->name; + MG_SPRINTF(gamepad->guid, "%02x%02x0000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00", + id.bustype & 0xff, id.bustype >> 8, + name[0], name[1], name[2], name[3], + name[4], name[5], name[6], name[7], + name[8], name[9], name[10]); + } + + gamepad->mapping = mg_gamepad_find_valid_mapping(gamepad); + + for (btn = BTN_MISC; btn < KEY_CNT; btn++) { + mg_button key = mg_get_gamepad_button(gamepad, gamepad->src.keyMap[btn - BTN_MISC]); + if (key == MG_BUTTON_UNKNOWN) + key = mg_get_gamepad_button_platform(btn); + if (key == MG_BUTTON_UNKNOWN) + continue; + + if (gamepad->buttons[key].supported) + continue; + + if (!isBitSet(btn, keyBits)) { + gamepad->buttons[key].supported = MG_FALSE; + continue; + } + + gamepad->buttons[key].supported = MG_TRUE; + gamepad->buttons[key].current = 0; + } + + for (axis = 0; axis < ABS_CNT; axis++) { + mg_axis key; + float deadzone = 0; + if (!isBitSet(axis, absBits)) { + continue; + } + + switch (axis) { + case ABS_HAT0X: + gamepad->buttons[MG_BUTTON_DPAD_LEFT].supported = MG_TRUE; + gamepad->buttons[MG_BUTTON_DPAD_LEFT].current = 0; + gamepad->buttons[MG_BUTTON_DPAD_RIGHT].supported = MG_TRUE; + gamepad->buttons[MG_BUTTON_DPAD_RIGHT].current = 0; + deadzone = 0; + break; + case ABS_HAT0Y: + gamepad->buttons[MG_BUTTON_DPAD_UP].supported = MG_TRUE; + gamepad->buttons[MG_BUTTON_DPAD_UP].current = 0; + gamepad->buttons[MG_BUTTON_DPAD_DOWN].supported = MG_TRUE; + gamepad->buttons[MG_BUTTON_DPAD_DOWN].current = 0; + deadzone = 0; + break; + case ABS_HAT1X: + case ABS_HAT1Y: + case ABS_HAT2X: + case ABS_HAT2Y: + case ABS_HAT3X: + case ABS_HAT3Y: + deadzone = 0; + break; + case ABS_Z: + gamepad->buttons[MG_BUTTON_LEFT_TRIGGER].supported = MG_TRUE; + gamepad->buttons[MG_BUTTON_LEFT_TRIGGER].current = 0; + + gamepad->axes[MG_AXIS_LEFT_TRIGGER].supported = MG_TRUE; + gamepad->axes[MG_AXIS_LEFT_TRIGGER].value = 0; + deadzone = 0; + break; + case ABS_RZ: + gamepad->buttons[MG_BUTTON_RIGHT_TRIGGER].supported = MG_TRUE; + gamepad->buttons[MG_BUTTON_RIGHT_TRIGGER].current = 0; + + gamepad->axes[MG_AXIS_RIGHT_TRIGGER].supported = MG_TRUE; + gamepad->axes[MG_AXIS_RIGHT_TRIGGER].value = 0; + deadzone = 0; + break; + default: + deadzone = 0.15f; + break; + } + + key = mg_get_gamepad_axis(gamepad, gamepad->src.absMap[axis]); + if (key == MG_AXIS_UNKNOWN) { + key = mg_get_gamepad_axis_platform(axis); + } + if (key == MG_AXIS_UNKNOWN) + continue; + + gamepad->axes[key].supported = MG_TRUE; + gamepad->axes[key].value = 0; + gamepad->axes[key].deadzone = deadzone; + } + + #undef isBitSet + + { + i32 flags = fcntl(gamepad->src.fd, F_GETFL, 0); + fcntl(gamepad->src.fd, F_SETFL, flags | O_NONBLOCK); + } + gamepad->connected = MG_TRUE; + + return gamepad; +} + +void mg_gamepad_release_platform(mg_gamepad* gamepad) { + close(gamepad->src.fd); +} + + +void mg_gamepads_init_platform(mg_gamepads* gamepads) { + struct dirent *dp; + DIR *dfd; + char full_path[256]; + + gamepads->src.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (gamepads->src.inotify > 0) { + /* HACK: Register for IN_ATTRIB to get notified when udev is done + This works well in practice but the MG_TRUE way is libudev */ + + gamepads->src.watch = inotify_add_watch(gamepads->src.inotify, "/dev/input/", IN_CREATE | IN_ATTRIB | IN_DELETE); + } + + /* open the directory where all the devices are gonna be */ + if ((dfd = opendir("/dev/input/")) == NULL) { + MG_FPRINTF(stderr, "Can't open /dev/input/\n"); + return; + } + + /* for each file found: */ + while ((dp = readdir(dfd)) != NULL) { + /* get the full path of it (size of path + size of file name) */ + const char path[] = "/dev/input/"; + mg_gamepad* gamepad; + + MG_STRNCPY(full_path, path, sizeof(full_path)); + MG_STRNCPY(&full_path[sizeof(path) - 1], dp->d_name, sizeof(full_path) - sizeof(path)); + full_path[255] = '\0'; + gamepad = (mg_gamepad*)mg_linux_setup_gamepad(gamepads, (const char*)full_path); + if (gamepad) { + mg_handle_connection_event(&gamepads->events, MG_TRUE, gamepad); + } + } + + closedir(dfd); +} + +mg_bool mg_gamepads_poll_platform(mg_gamepads* gamepads, mg_events* events) { + mg_ssize_t offset = 0; + char buffer[16384]; + char full_path[256]; + const char path[] = "/dev/input/"; + mg_ssize_t size; + + if (gamepads->src.inotify <= 0) + return MG_FALSE; + + size = read(gamepads->src.inotify, buffer, sizeof(buffer)); + + while (size > offset) { + const struct inotify_event* e = (struct inotify_event*) (buffer + offset); + + offset += (mg_ssize_t)sizeof(struct inotify_event) + e->len; + + if (strncmp(e->name, "event", 5) != 0) { + continue; + } + + MG_STRNCPY(full_path, path, sizeof(full_path)); + MG_STRNCPY(&full_path[sizeof(path) - 1], e->name, sizeof(full_path) - sizeof(path)); + full_path[255] = '\0'; + + if (e->mask & (IN_CREATE | IN_ATTRIB)) { + mg_gamepad* gamepad = mg_linux_setup_gamepad(gamepads, full_path); + if (gamepad) { + mg_handle_connection_event(events, MG_TRUE, gamepad); + return MG_TRUE; + } + } + + else if (e->mask & IN_DELETE) { + mg_gamepad* cur; + for (cur = gamepads->list.head; cur != NULL; cur = cur->next) { + if (MG_STRNCMP(cur->src.full_path, full_path, sizeof(cur->src.full_path)) == 0) { + mg_handle_connection_event(events, MG_FALSE, cur); + mg_gamepad_release(gamepads, cur); + return MG_TRUE; + } + } + } + } + + return MG_FALSE; +} + +void mg_gamepads_free_platform(mg_gamepads* gamepads) { + if (gamepads->src.inotify > 0) { + if (gamepads->src.watch > 0) + inotify_rm_watch(gamepads->src.inotify, gamepads->src.watch); + + close(gamepads->src.inotify); + } + + gamepads->src.inotify = 0; + gamepads->src.watch = 0; +} + +mg_bool mg_gamepad_update_platform(mg_gamepad* gamepad, mg_events* events) { + mg_bool event_handled = MG_FALSE; + struct input_event ev; + + i8 i; + if (gamepad->connected == MG_FALSE) return MG_FALSE; + + for (i = 0; i < 2; i++) { + mg_button button = MG_BUTTON_LEFT_TRIGGER + i; + mg_axis axis = MG_AXIS_LEFT_TRIGGER + i; + mg_handle_button_event(events, button, MG_BOOL(gamepad->axes[axis].value >= 0.98f), gamepad); + } + + for (i = 0; i < 2; i++) { + mg_button button = MG_BUTTON_DPAD_LEFT + (mg_button)(i * 2); + mg_button button2 = MG_BUTTON_DPAD_LEFT + 1 + (mg_button)(i * 2); + mg_axis axis = (mg_axis)(MG_AXIS_HAT_DPAD_LEFT + i); + + mg_handle_button_event(events, button, gamepad->axes[axis].value < 0, gamepad); + mg_handle_button_event(events, button2, gamepad->axes[axis].value > 0, gamepad); + } + + + MG_MEMSET(&ev, 0, sizeof(ev)); + while (read(gamepad->src.fd, &ev, sizeof(ev)) > 0) { + if (ev.type != EV_KEY && ev.type != EV_ABS) continue; + + switch (ev.type) { + case EV_KEY: { + mg_button btn = mg_get_gamepad_button(gamepad, (u8)(gamepad->src.keyMap[ev.code - BTN_MISC])); + if (btn == MG_BUTTON_UNKNOWN) { + btn = mg_get_gamepad_button_platform(ev.code); + } + + if (btn == MG_BUTTON_UNKNOWN) { + break; + } + + mg_handle_button_event(events, btn, MG_BOOL(ev.value), gamepad); + event_handled = MG_TRUE; + break; + } + case EV_ABS: { + float deadzone, event_val; + + mg_axis axis = mg_get_gamepad_axis(gamepad, gamepad->src.absMap[ev.code]); + const struct mg_input_absinfo info = gamepad->src.absInfo[ev.code]; + float normalized = (float)ev.value; + const float range = (float)(info.maximum - info.minimum); + + if (axis == MG_AXIS_UNKNOWN) + axis = mg_get_gamepad_axis_platform(ev.code); + if (axis == MG_AXIS_UNKNOWN) { + break; + } + + if (range) { + /* Normalize to 0.0 -> 1.0 */ + normalized = (normalized - (float)info.minimum) / range; + /* Normalize to -1.0 -> 1.0 */ + normalized = normalized * 2.0f - 1.0f; + } + + deadzone = gamepad->axes[axis].deadzone; + event_val = normalized; + if (MG_FABS(event_val) < deadzone) { + event_val = 0; + } + + mg_handle_axis_event(events, axis, event_val, gamepad); + event_handled = MG_TRUE; + break; + } + default: + break; + } + } + + return event_handled; +} + +mg_button mg_get_gamepad_button_platform(u32 button) { + switch (button) { + case BTN_WEST: + return MG_BUTTON_WEST; + case BTN_A: + return MG_BUTTON_SOUTH; + case BTN_NORTH: + return MG_BUTTON_NORTH; + case BTN_EAST: + return MG_BUTTON_EAST; + case BTN_BACK: + return MG_BUTTON_BACK; + case BTN_MODE: + return MG_BUTTON_GUIDE; + case BTN_START: + return MG_BUTTON_START; + case BTN_THUMBL: + return MG_BUTTON_LEFT_STICK; + case BTN_THUMBR: + return MG_BUTTON_RIGHT_STICK; + case BTN_TL: + return MG_BUTTON_LEFT_SHOULDER; + case BTN_DPAD_UP: + return MG_BUTTON_DPAD_UP; + case BTN_DPAD_DOWN: + return MG_BUTTON_DPAD_DOWN; + case BTN_DPAD_LEFT: + return MG_BUTTON_DPAD_LEFT; + case BTN_DPAD_RIGHT: + return MG_BUTTON_DPAD_RIGHT; + case BTN_TR: + return MG_BUTTON_RIGHT_SHOULDER; + case BTN_TOUCH: + return MG_BUTTON_TOUCHPAD; + case BTN_TRIGGER_HAPPY4: + return MG_BUTTON_RIGHT_PADDLE1; + case BTN_TRIGGER_HAPPY6: + return MG_BUTTON_RIGHT_PADDLE2; + case BTN_TRIGGER_HAPPY7: + return MG_BUTTON_LEFT_PADDLE1; + case BTN_TRIGGER_HAPPY8: + return MG_BUTTON_LEFT_PADDLE2; + + case BTN_SELECT: + return MG_BUTTON_MISC1; + case BTN_TRIGGER_HAPPY2: + return MG_BUTTON_MISC2; + case BTN_TRIGGER_HAPPY3: + return MG_BUTTON_MISC3; + case BTN_TRIGGER_HAPPY9: + return MG_BUTTON_MISC5; + case BTN_TRIGGER_HAPPY10: + return MG_BUTTON_MISC6; + + case BTN_TRIGGER: return MG_BUTTON_WEST; + case BTN_THUMB: return MG_BUTTON_SOUTH; + case BTN_THUMB2: return MG_BUTTON_EAST; + case BTN_TOP: return MG_BUTTON_NORTH; + case BTN_TOP2: return MG_BUTTON_START; + case BTN_PINKIE: return MG_BUTTON_LEFT_SHOULDER; + case BTN_BASE: return MG_BUTTON_RIGHT_SHOULDER; + case BTN_BASE2: return MG_BUTTON_BACK; + + case BTN_BASE3: return MG_BUTTON_BACK; + case BTN_BASE4: return MG_BUTTON_START; + case BTN_BASE5: return MG_BUTTON_START; + case BTN_BASE6: return MG_BUTTON_RIGHT_STICK; + default: + return MG_BUTTON_UNKNOWN; + } + return MG_BUTTON_UNKNOWN; +} + +mg_axis mg_get_gamepad_axis_platform(u32 axis) { + switch (axis) { + case ABS_X: + return MG_AXIS_LEFT_X; + case ABS_Y: + return MG_AXIS_LEFT_Y; + case ABS_Z: + return MG_AXIS_LEFT_TRIGGER; + case ABS_RX: + return MG_AXIS_RIGHT_X; + case ABS_RY: + return MG_AXIS_RIGHT_Y; + case ABS_RZ: + return MG_AXIS_RIGHT_TRIGGER; + case ABS_THROTTLE: + return MG_AXIS_THROTTLE; + case ABS_RUDDER: + return MG_AXIS_RUDDER; + case ABS_WHEEL: + return MG_AXIS_WHEEL; + case ABS_GAS: + return MG_AXIS_GAS; + case ABS_BRAKE: + return MG_AXIS_BRAKE; + case ABS_HAT0X: + return MG_AXIS_HAT_DPAD_LEFT_RIGHT; + case ABS_HAT0Y: + return MG_AXIS_HAT_DPAD_UP_DOWN; + case ABS_HAT1X: + return MG_AXIS_HAT1X; + case ABS_HAT1Y: + return MG_AXIS_HAT1Y; + case ABS_HAT2X: + return MG_AXIS_HAT2X; + case ABS_HAT2Y: + return MG_AXIS_HAT2Y; + case ABS_HAT3X: + return MG_AXIS_HAT3X; + case ABS_HAT3Y: + return MG_AXIS_HAT3Y; + case ABS_PRESSURE: + return MG_AXIS_PRESSURE; + case ABS_DISTANCE: + return MG_AXIS_DISTANCE; + case ABS_TILT_X: + return MG_AXIS_TILT_X; + case ABS_TILT_Y: + return MG_AXIS_TILT_Y; + case ABS_TOOL_WIDTH: + return MG_AXIS_TOOL_WIDTH; + case ABS_VOLUME: + return MG_AXIS_VOLUME; + case ABS_PROFILE: + return MG_AXIS_PROFILE; + case ABS_MISC: + return MG_AXIS_MISC; + default: + return MG_AXIS_UNKNOWN; + } + + return MG_AXIS_UNKNOWN; +} +#endif /* MG_LINUX */ + +/* + * start of windows + */ + +#if defined(MG_WINDOWS) + +#ifdef __cplusplus +#define MG_REF_GUID(g) *(g) +#else +#define MG_REF_GUID(g) (g) +#endif + +#include +#include + +#ifndef DIDFT_OPTIONAL +#define DIDFT_OPTIONAL 0x80000000 +#endif + +typedef void (*mg_proc)(void); /* function pointer equivalent of void* */ + +MG_API void mg_xinput_fetch_gamepads(mg_gamepads* gamepads, mg_events* events); + +mg_gamepad* mg_xinput_list[XUSER_MAX_COUNT]; +typedef DWORD (* PFN_XInputGetState)(DWORD,XINPUT_STATE*); +typedef DWORD (* PFN_XInputGetCapabilities)(DWORD,DWORD,XINPUT_CAPABILITIES*); +typedef DWORD (* PFN_XInputGetKeystroke)(DWORD, DWORD, PXINPUT_KEYSTROKE); +typedef HRESULT (WINAPI * PFN_DirectInput8Create)(HINSTANCE,DWORD,REFIID,LPVOID*,LPUNKNOWN); +typedef HRESULT (WINAPI * PFN_GameInputCreate)(void* gameinput); + +HINSTANCE mg_gameinput_dll = NULL; +HINSTANCE mg_xinput_dll = NULL; +HINSTANCE mg_dinput_dll = NULL; + +PFN_GameInputCreate GameInputCreateSrc = NULL; + +PFN_XInputGetState XInputGetStateSrc = NULL; +PFN_XInputGetKeystroke XInputGetKeystrokeSrc = NULL; +PFN_XInputGetCapabilities XInputGetCapabilitiesSrc = NULL; +PFN_DirectInput8Create DInput8CreateSrc = NULL; + +const GUID MG_IID_IDirectInput8W = + {0xbf798031,0x483a,0x4da2,{0xaa,0x99,0x5d,0x64,0xed,0x36,0x97,0x00}}; +const GUID MG_GUID_XAxis = + {0xa36d02e0,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +const GUID MG_GUID_YAxis = + {0xa36d02e1,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +const GUID MG_GUID_ZAxis = + {0xa36d02e2,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +const GUID MG_GUID_RxAxis = + {0xa36d02f4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +const GUID MG_GUID_RyAxis = + {0xa36d02f5,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +const GUID MG_GUID_RzAxis = + {0xa36d02e3,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +const GUID MG_GUID_Slider = + {0xa36d02e4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +const GUID MG_GUID_POV = + {0xa36d02f2,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; + +static DIOBJECTDATAFORMAT mg_objectDataFormats[] = { + { &MG_GUID_XAxis,DIJOFS_X,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &MG_GUID_YAxis,DIJOFS_Y,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &MG_GUID_ZAxis,DIJOFS_Z,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &MG_GUID_RxAxis,DIJOFS_RX,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &MG_GUID_RyAxis,DIJOFS_RY,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &MG_GUID_RzAxis,DIJOFS_RZ,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &MG_GUID_Slider,DIJOFS_SLIDER(0),DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &MG_GUID_Slider,DIJOFS_SLIDER(1),DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &MG_GUID_POV,DIJOFS_POV(0),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { &MG_GUID_POV,DIJOFS_POV(1),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { &MG_GUID_POV,DIJOFS_POV(2),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { &MG_GUID_POV,DIJOFS_POV(3),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(0),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(1),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(2),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(3),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(4),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(5),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(6),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(7),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(8),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(9),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(10),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(11),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(12),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(13),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(14),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(15),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(16),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(17),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(18),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(19),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(20),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(21),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(22),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(23),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(24),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(25),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(26),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(27),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(28),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(29),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(30),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(31),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, +}; + +const DIDATAFORMAT mg_dataFormat = { + sizeof(DIDATAFORMAT), + sizeof(DIOBJECTDATAFORMAT), + DIDFT_ABSAXIS, + sizeof(DIJOYSTATE), + sizeof(mg_objectDataFormats) / sizeof(DIOBJECTDATAFORMAT), + mg_objectDataFormats +}; + +mg_bool mg_supportsXInput(const GUID* guid) { + RAWINPUTDEVICELIST* list; + unsigned int count = 0; + mg_size_t i; + + if (mg_xinput_dll == NULL) { + return MG_FALSE; + } + + if (GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST)) != 0) + return MG_FALSE; + + list = (RAWINPUTDEVICELIST*)malloc(count * sizeof(RAWINPUTDEVICELIST)); + MG_MEMSET(list, 0, count * sizeof(RAWINPUTDEVICELIST)); + + if ((int)GetRawInputDeviceList(list, &count, sizeof(RAWINPUTDEVICELIST)) == -1) { + free(list); + return MG_FALSE; + } + + for (i = 0; i < count; i++) { + RID_DEVICE_INFO rdi = {0}; + char name[256]; + UINT size = sizeof(rdi); + + rdi.cbSize = sizeof(rdi); + + if (list[i].dwType != RIM_TYPEHID) + continue; + + if ((int)GetRawInputDeviceInfoA(list[i].hDevice, RIDI_DEVICEINFO, &rdi, &size) == -1) { + continue; + } + + if (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) != (LONG) guid->Data1) + continue; + + MG_MEMSET(name, 0, sizeof(name)); + size = sizeof(name); + + if ((int)GetRawInputDeviceInfoA(list[i].hDevice, RIDI_DEVICENAME, name, &size) == -1) { + break; + } + + name[sizeof(name) - 1] = '\0'; + if (strstr((char*)name, "IG_")) { + free(list); + return MG_TRUE; + } + } + + free(list); + return MG_FALSE; +} + +#include + +BOOL CALLBACK DirectInputEnumDevicesCallback(LPCDIDEVICEINSTANCE inst, LPVOID userData) { + mg_gamepads* gamepads = (mg_gamepads*)userData; + mg_gamepad* gamepad; + u32 i; + DIDEVCAPS caps; + DIPROPDWORD dipd; + /* avoid clones */ + + if (mg_supportsXInput(&inst->guidProduct)) { + return DIENUM_CONTINUE; + } + + gamepad = mg_gamepad_find(gamepads); + if (gamepad == NULL) return FALSE; + + gamepad->src.device = NULL; + + if (FAILED(IDirectInput8_CreateDevice((IDirectInput8*)gamepads->src.dinput, MG_REF_GUID(&inst->guidInstance), (IDirectInputDevice8**)&gamepad->src.device, NULL))) { + mg_gamepad_release(gamepads, gamepad); + return DIENUM_CONTINUE; + } + + + if (FAILED(IDirectInputDevice8_SetDataFormat((IDirectInputDevice8*)gamepad->src.device, &mg_dataFormat ))) { + mg_gamepad_release(gamepads, gamepad); + return DIENUM_CONTINUE; + } + + MG_MEMSET(&caps, 0, sizeof(caps)); + caps.dwSize = sizeof(DIDEVCAPS); + + IDirectInputDevice8_GetCapabilities((IDirectInputDevice8*)gamepad->src.device, &caps); + + MG_MEMSET(&dipd, 0, sizeof(dipd)); + dipd.diph.dwSize = sizeof(dipd); + dipd.diph.dwHeaderSize = sizeof(dipd.diph); + dipd.diph.dwHow = DIPH_DEVICE; + dipd.dwData = DIPROPAXISMODE_ABS; + + if (FAILED(IDirectInputDevice8_SetProperty((IDirectInputDevice8*)gamepad->src.device, DIPROP_AXISMODE, &dipd.diph))) { + mg_gamepad_release(gamepads, gamepad); + return DIENUM_CONTINUE; + } + + if (!WideCharToMultiByte(CP_UTF8, 0, (const wchar_t*)inst->tszInstanceName, -1, gamepad->name, sizeof(gamepad->name), NULL, NULL)) { + mg_gamepad_release(gamepads, gamepad); + return DIENUM_STOP; + } + + if (memcmp(&inst->guidProduct.Data4[2], "PIDVID", 6) == 0) { + MG_SPRINTF(gamepad->guid, "03000000%02x%02x0000%02x%02x000000000000", + (uint8_t) inst->guidProduct.Data1, + (uint8_t) (inst->guidProduct.Data1 >> 8), + (uint8_t) (inst->guidProduct.Data1 >> 16), + (uint8_t) (inst->guidProduct.Data1 >> 24)); + } else { + MG_SPRINTF(gamepad->guid, "05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00", + gamepad->name[0], gamepad->name[1], gamepad->name[2], gamepad->name[3], + gamepad->name[4], gamepad->name[5], gamepad->name[6], gamepad->name[7], + gamepad->name[8], gamepad->name[9], gamepad->name[10]); + } + + + gamepad->mapping = mg_gamepad_find_valid_mapping(gamepad); + +{ + HRESULT result; + DIJOYSTATE state; + MG_MEMSET(&state, 0, sizeof(state)); + + IDirectInputDevice8_Acquire((IDirectInputDevice8*)gamepad->src.device); + IDirectInputDevice8_Poll((IDirectInputDevice8*)gamepad->src.device); + + result = IDirectInputDevice8_GetDeviceState((IDirectInputDevice8*)gamepad->src.device, sizeof(state), &state); + if (FAILED(result)) { + mg_gamepad_release(gamepads, gamepad); + return DIENUM_CONTINUE; + } +} + + for (i = 0; i < caps.dwButtons; i++) { + mg_button key = mg_get_gamepad_button(gamepad, (u8)i); + if (key == MG_BUTTON_UNKNOWN) { + key = mg_get_gamepad_button_platform(i); + if (key == MG_BUTTON_UNKNOWN) continue; + } + + if (gamepad->buttons[key].supported) + continue; + + gamepad->buttons[key].supported = MG_TRUE; + gamepad->buttons[key].current = 0; + } + + for (i = 0; i < caps.dwAxes; i++) { + mg_axis key = mg_get_gamepad_axis(gamepad, (u8)i); + if (key == MG_AXIS_UNKNOWN) { + key = mg_get_gamepad_axis_platform(i); + if (key == MG_AXIS_UNKNOWN) continue; + } + + if (gamepad->axes[key].supported) + continue; + + gamepad->axes[key].supported = MG_TRUE; + gamepad->axes[key].value = 0; + } + + if (caps.dwPOVs) { + const mg_axis povs[2] = { + MG_AXIS_HAT_DPAD_UP_DOWN, + MG_AXIS_HAT_DPAD_LEFT_RIGHT, + }; + + for (i = 0; i < 2; i++) { + mg_axis key = povs[i]; + gamepad->axes[key].supported = MG_TRUE; + gamepad->axes[key].value = 0; + } + } + + gamepad->connected = MG_TRUE; + mg_handle_connection_event(&gamepads->events, MG_TRUE, gamepad); + + return DIENUM_CONTINUE; +} + +#include + +static LRESULT CALLBACK mg_winproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { + mg_gamepads* gamepads = (mg_gamepads*)GetPropW(hWnd, L"gamepads"); + if (gamepads == NULL) return DefWindowProcW(hWnd, message, wParam, lParam); + + switch (message) { + case WM_DEVICECHANGE: { + if (gamepads->src.dinput) { + IDirectInput8_EnumDevices((IDirectInput8*)gamepads->src.dinput, + DI8DEVCLASS_GAMECTRL, + DirectInputEnumDevicesCallback, + (void*)gamepads, + DIEDFL_ATTACHEDONLY); + } + + mg_xinput_fetch_gamepads(gamepads, &gamepads->events); + break; + } + } + + return DefWindowProcW(hWnd, message, wParam, lParam); +} + + +void mg_gamepads_init_platform(mg_gamepads* gamepads) { + HINSTANCE hInstance = GetModuleHandle(0); + WNDCLASSW Class; + + if (mg_gameinput_dll == NULL) { + /* load gameinput dll and functions (if it's available) */ + static const char* names[] = {"gameinput.lib", "gameinput.dll"}; + + uint32_t i; + for (i = 0; i < sizeof(names) / sizeof(const char*); i++) { + mg_gameinput_dll = LoadLibraryA(names[i]); + if (mg_gameinput_dll) { + GameInputCreateSrc = (PFN_GameInputCreate)(mg_proc)GetProcAddress(mg_gameinput_dll, "GameInputCreate"); + } + } + + + if (mg_gameinput_dll && GameInputCreateSrc) { + /*HRESULT hr = GameInputCreateSrc(&gamepads->src.ginput); + MG_UNUSED(hr); TODO */ + } + } + + /* init global gamepads->src.data */ + if (mg_xinput_dll == NULL) { + /* load xinput dll and functions (if it's available) */ + static const char* names[] = {"xinput0_4.dll", "xinput9_1_0.dll", "xinput1_2.dll", "xinput1_1.dll"}; + + uint32_t i; + for (i = 0; i < sizeof(names) / sizeof(const char*) && (XInputGetStateSrc == NULL || XInputGetKeystrokeSrc != NULL); i++) { + mg_xinput_dll = LoadLibraryA(names[i]); + if (mg_xinput_dll) { + XInputGetStateSrc = (PFN_XInputGetState)(mg_proc)GetProcAddress(mg_xinput_dll, "XInputGetState"); + XInputGetKeystrokeSrc = (PFN_XInputGetKeystroke)(mg_proc)GetProcAddress(mg_xinput_dll, "XInputGetKeystroke"); + XInputGetCapabilitiesSrc = (PFN_XInputGetCapabilities)(mg_proc)GetProcAddress(mg_xinput_dll, "XInputGetCapabilities"); + } + } + + if (mg_xinput_dll) { + mg_xinput_fetch_gamepads(gamepads, &gamepads->events); + } + } + + if (mg_dinput_dll == NULL && DInput8CreateSrc == NULL) { + /* load directinput dll and functions */ + mg_dinput_dll = LoadLibraryA("dinput8.dll"); + if (mg_dinput_dll) + DInput8CreateSrc = (PFN_DirectInput8Create)(mg_proc)GetProcAddress(mg_dinput_dll, "DirectInput8Create"); + } + + if (DInput8CreateSrc) { + if (FAILED(DInput8CreateSrc(hInstance, + DIRECTINPUT_VERSION, + MG_REF_GUID(&MG_IID_IDirectInput8W), + (void**) &gamepads->src.dinput, + NULL)) || + FAILED(IDirectInput8_EnumDevices((IDirectInput8*)gamepads->src.dinput, + DI8DEVCLASS_GAMECTRL, + DirectInputEnumDevicesCallback, + (void*)gamepads, + DIEDFL_ALLDEVICES))) { + mg_dinput_dll = NULL; + } + } + + MG_MEMSET(&Class, 0, sizeof(Class)); + + Class.hInstance = hInstance; + Class.lpfnWndProc = mg_winproc; + Class.cbClsExtra = sizeof(mg_gamepads*); + Class.lpszClassName = L"minigamepadclass"; + + RegisterClassW(&Class); + + gamepads->src.dummy_win = CreateWindowW(Class.lpszClassName, (wchar_t*)"", 0, 0, 0, 0, 0, 0, 0, hInstance, 0); + + SetPropW((HWND)gamepads->src.dummy_win, L"gamepads", gamepads); +} + +#ifndef XINPUT_DEVSUBTYPE_FLIGHT_STICK + /* MINGW PLEASE FIX THIS */ + #define XINPUT_DEVSUBTYPE_FLIGHT_STICK 0x04 +#endif + +static const char* mg_xinput_gamepad_name(const XINPUT_CAPABILITIES xic) { + switch (xic.SubType) { + case XINPUT_DEVSUBTYPE_WHEEL: + return "XInput Wheel"; + case XINPUT_DEVSUBTYPE_ARCADE_STICK: + return "XInput Arcade Stick"; + case XINPUT_DEVSUBTYPE_FLIGHT_STICK: + return "XInput Flight Stick"; + case XINPUT_DEVSUBTYPE_DANCE_PAD: + return "XInput Dance Pad"; + case XINPUT_DEVSUBTYPE_GUITAR: + return "XInput Guitar"; + case XINPUT_DEVSUBTYPE_DRUM_KIT: + return "XInput Drum Kit"; + case XINPUT_DEVSUBTYPE_GAMEPAD: { + if (xic.Flags & XINPUT_CAPS_WIRELESS) + return "Wireless Xbox Controller"; + else + return "Xbox Controller"; + } + } + + return "Unknown XInput Device"; +} + +void mg_xinput_fetch_gamepads(mg_gamepads* gamepads, mg_events* events) { + DWORD dwResult, i; + if (mg_xinput_dll == NULL) return; + + for (i = 0; i < XUSER_MAX_COUNT; i++) { + mg_gamepad* gamepad = mg_xinput_list[i]; + XINPUT_STATE state; + mg_button button; + mg_axis axis; + char* name; + XINPUT_CAPABILITIES xic; + + MG_MEMSET(&state, 0, sizeof(state)); + + dwResult = XInputGetStateSrc(i, &state); + + if ((dwResult == ERROR_SUCCESS && gamepad) || + (dwResult != ERROR_SUCCESS && gamepad == NULL) + ) + continue; + + if (dwResult != ERROR_SUCCESS) { + gamepad->src.xinput_index = 0; + mg_xinput_list[i] = NULL; + mg_handle_connection_event(events, MG_FALSE, gamepad); + mg_gamepad_release(gamepads, gamepad); + + continue; + } + + if (XInputGetCapabilitiesSrc(i, 0, &xic) != ERROR_SUCCESS) + continue; + + gamepad = mg_gamepad_find(gamepads); + + gamepad->src.xinput_index = i + 1; + MG_SPRINTF(gamepad->guid, "78696e707574%02x000000000000000000", xic.SubType & 0xff); + + for (button = 0; button < MG_BUTTON_MISC1; button++) { + if (button == MG_BUTTON_GUIDE) continue; + + gamepad->buttons[button].supported = MG_TRUE; + gamepad->buttons[button].current = MG_FALSE; + gamepad->buttons[button].prev = MG_FALSE; + } + + for (axis = 0; axis < MG_AXIS_HAT_DPAD_LEFT_RIGHT; axis++) { + gamepad->axes[axis].value = 0; + gamepad->axes[axis].supported = MG_TRUE; + } + + name = (char*)mg_xinput_gamepad_name(xic); + MG_STRNCPY(gamepad->name, name, sizeof(gamepad->name)); + + gamepad->connected = MG_TRUE; + mg_xinput_list[i] = gamepad; + mg_handle_connection_event(events, MG_TRUE, gamepad); + } +} + +mg_bool mg_gamepads_poll_platform(mg_gamepads* gamepads, mg_events* events) { + mg_bool out = MG_FALSE; + MSG msg; + + MG_UNUSED(gamepads); MG_UNUSED(events); + + while (PeekMessageA(&msg, NULL, 0u, 0u, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessageA(&msg); + } + + return out; +} + +void mg_gamepads_free_platform(mg_gamepads* gamepads) { + DestroyWindow((HWND)gamepads->src.dummy_win); + + if (mg_xinput_dll) { + FreeLibrary(mg_xinput_dll); + mg_xinput_dll = NULL; + } + + if (mg_dinput_dll) { + if (gamepads->src.dinput) + IDirectInput8_Release((IDirectInput8*)gamepads->src.dinput); + + FreeLibrary(mg_dinput_dll); + } +} + + +const u32 mg_xinput_map[MG_BUTTON_MISC1] = { + XINPUT_GAMEPAD_A, + XINPUT_GAMEPAD_B, + XINPUT_GAMEPAD_X, + XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_BACK, + 0, + XINPUT_GAMEPAD_START, + XINPUT_GAMEPAD_LEFT_THUMB, + XINPUT_GAMEPAD_RIGHT_THUMB, + XINPUT_GAMEPAD_LEFT_SHOULDER, + XINPUT_GAMEPAD_RIGHT_SHOULDER, + XINPUT_GAMEPAD_DPAD_LEFT, + XINPUT_GAMEPAD_DPAD_RIGHT, + XINPUT_GAMEPAD_DPAD_UP, + XINPUT_GAMEPAD_DPAD_DOWN, + 0, 0 +}; + +mg_bool mg_gamepad_update_platform(mg_gamepad* gamepad, mg_events* events) { + + if (gamepad->connected == MG_FALSE) { +/* mg_gamepad_release(gamepad); */ + return MG_FALSE; + } + + if (gamepad->src.xinput_index) { + DWORD dwResult; + XINPUT_STATE state; + DWORD i = gamepad->src.xinput_index - 1; + mg_button button; + + if (gamepad != mg_xinput_list[i]) { + gamepad->connected = MG_FALSE; + return MG_TRUE; + } + + MG_MEMSET(&state, 0, sizeof(state)); + dwResult = XInputGetStateSrc(i, &state); + if (dwResult != ERROR_SUCCESS) { + gamepad->connected = MG_FALSE; + mg_handle_connection_event(events, MG_FALSE, gamepad); + return MG_TRUE; + } + + for (button = 0; button < MG_BUTTON_MISC1; button++) { + u32 btn = mg_xinput_map[button]; + mg_bool btn_state; + + if (btn == 0) continue; + btn_state = MG_BOOL((state.Gamepad.wButtons & btn)); + + mg_handle_button_event(events, button, btn_state, gamepad); + } + + mg_handle_button_event(events, MG_BUTTON_LEFT_TRIGGER, MG_BOOL(gamepad->axes[MG_AXIS_LEFT_TRIGGER].value > 0), gamepad); + mg_handle_button_event(events, MG_BUTTON_RIGHT_TRIGGER, MG_BOOL(gamepad->axes[MG_AXIS_RIGHT_TRIGGER].value > 0), gamepad); + + mg_handle_axis_event(events, MG_AXIS_LEFT_TRIGGER, (state.Gamepad.bLeftTrigger / 127.5f - 1.0f), gamepad); + mg_handle_axis_event(events, MG_AXIS_RIGHT_TRIGGER, (state.Gamepad.bRightTrigger / 127.5f - 1.0f), gamepad); + + mg_handle_axis_event(events, MG_AXIS_LEFT_X, (state.Gamepad.sThumbLX + 0.5f) / 32767.5f, gamepad); + mg_handle_axis_event(events, MG_AXIS_LEFT_Y, -(state.Gamepad.sThumbLY + 0.5f) / 32767.5f, gamepad); + + mg_handle_axis_event(events, MG_AXIS_RIGHT_X, (state.Gamepad.sThumbRX + 0.5f) / 32767.5f, gamepad); + mg_handle_axis_event(events, MG_AXIS_RIGHT_Y, -(state.Gamepad.sThumbRY + 0.5f) / 32767.5f, gamepad); + } + + if (gamepad->src.device) { + u32 i; + DIDEVCAPS caps; + DIJOYSTATE state; + HRESULT result; + LONG axes_state[6]; + + MG_MEMSET(&caps, 0, sizeof(caps)); + caps.dwSize = sizeof(DIDEVCAPS); + + IDirectInputDevice8_GetCapabilities((IDirectInputDevice8*)gamepad->src.device, &caps); + IDirectInputDevice8_Poll((IDirectInputDevice8*)gamepad->src.device); + result = IDirectInputDevice8_GetDeviceState((IDirectInputDevice8*)gamepad->src.device, sizeof(state), &state); + if (result == DIERR_NOTACQUIRED || result == DIERR_INPUTLOST) { + IDirectInputDevice8_Acquire((IDirectInputDevice8*)gamepad->src.device); + IDirectInputDevice8_Poll((IDirectInputDevice8*)gamepad->src.device); + + result = IDirectInputDevice8_GetDeviceState((IDirectInputDevice8*)gamepad->src.device, sizeof(state), &state); + } + + if (FAILED(result)) { + gamepad->connected = MG_FALSE; + mg_handle_connection_event(events, MG_FALSE, gamepad); + return MG_FALSE; + } + + for (i = 0; i < caps.dwButtons; i++) { + mg_button key = mg_get_gamepad_button(gamepad, (u8)i); + if (key == MG_BUTTON_UNKNOWN) { + key = mg_get_gamepad_button_platform(i); + if (key == MG_BUTTON_UNKNOWN) continue; + } + + mg_handle_button_event(events, key, MG_BOOL(state.rgbButtons[i]), gamepad); + } + + memcpy(&axes_state, &state, sizeof(axes_state)); + + for (i = 0; i < 6; i++) { + float value; + mg_axis key = mg_get_gamepad_axis(gamepad, (u8)i); + if (key == MG_AXIS_UNKNOWN) { + key = mg_get_gamepad_axis_platform(i); + if (key == MG_AXIS_UNKNOWN) continue; + } + + /* NOTE: this doesn't work with triggers because triggers are combined into one input + * TODO: fix this (or just use xinput) + * */ + value = (((float)axes_state[i] + 0.5f) / 32767.5f) - 1.0f; + mg_handle_axis_event(events, key, value, gamepad); + } + + if (caps.dwPOVs) { + DWORD pov = state.rgdwPOV[0]; + + if (pov != 0xFFFF) { + float angle = (float)pov / (45.0f * DI_DEGREES); + i32 x = 0, y = 0; + + switch ((u32)angle) { + case 0: /* up */ x = 0; y = -1; break; + case 1: /* up-right */ x = 1; y = -1; break; + case 2: /* right */ x = 1; y = 0; break; + case 3: /* down-right */ x = 1; y = 1; break; + case 4: /* down */ x = 0; y = 1; break; + case 5: /* down-left */ x = -1; y = 1; break; + case 6: /* left */ x = -1; y = 0; break; + case 7: /* up-left */ x = -1; y = -1; break; + } + + mg_handle_button_event(events, MG_BUTTON_DPAD_LEFT, x < 0, gamepad); + mg_handle_button_event(events, MG_BUTTON_DPAD_RIGHT, x > 0, gamepad); + mg_handle_button_event(events, MG_BUTTON_DPAD_UP, y < 0, gamepad); + mg_handle_button_event(events, MG_BUTTON_DPAD_DOWN, y > 0, gamepad); + } else { + mg_handle_axis_event(events, MG_AXIS_HAT_DPAD_LEFT_RIGHT, (float)0.0f, gamepad); + mg_handle_axis_event(events, MG_AXIS_HAT_DPAD_UP_DOWN, (float)0.0f, gamepad); + } + } + } + + return MG_FALSE; +} + +void mg_gamepad_release_platform(mg_gamepad* gamepad) { + if (gamepad->src.device) { + IDirectInputDevice8_Release((IDirectInputDevice8*)gamepad->src.device); + } + + if (gamepad->src.xinput_index) { + mg_xinput_list[gamepad->src.xinput_index - 1] = NULL; + } +} + +mg_button mg_get_gamepad_button_platform(u32 button) { + /* TODO */ + switch (button) { + default: break; + } + return MG_BUTTON_UNKNOWN; +} + +mg_axis mg_get_gamepad_axis_platform(u32 axis) { + /* TODO */ + switch (axis) { + case 2: return MG_AXIS_LEFT_TRIGGER; + case 5: return MG_AXIS_RIGHT_TRIGGER; + default: break; + } + + return MG_AXIS_UNKNOWN; +} +#endif /* MG_WINDOWS */ + +/* + * start of macos + */ + +#if defined(MG_MACOS) +#include +#include + +void mg_osx_input_value_changed_callback(void *context, IOReturn result, void *sender, IOHIDValueRef value) { + mg_gamepad* gamepad = (mg_gamepad*)context; + + IOHIDElementRef element = IOHIDValueGetElement(value); + + IOHIDDeviceRef device = IOHIDElementGetDevice(element); + + uint32_t usagePage = IOHIDElementGetUsagePage(element); + uint32_t usage = IOHIDElementGetUsage(element); + + CFIndex intValue = IOHIDValueGetIntegerValue(value); + MG_UNUSED(result); MG_UNUSED(sender); + + if ((IOHIDDeviceRef)gamepad->src.device != device) + return; + + switch (usagePage) { + case kHIDPage_Button: { + mg_button btn = mg_get_gamepad_button(gamepad, (u8)usage); + if (btn == 0) + btn = mg_get_gamepad_button_platform(usage); + if (btn == 0) + break; + + mg_handle_button_event((mg_events*)gamepad->src.events, btn, MG_BOOL(intValue), gamepad); + break; + } + case kHIDPage_GenericDesktop: { + CFIndex logicalMin = IOHIDElementGetLogicalMin(element); + CFIndex logicalMax = IOHIDElementGetLogicalMax(element); + mg_axis btn = mg_get_gamepad_axis(gamepad, (u8)usage); + if (btn == 0) + btn = mg_get_gamepad_axis_platform(usage); + if (btn == 0) + break; + + if (logicalMax <= logicalMin) return; + if (intValue < logicalMin) intValue = logicalMin; + if (intValue > logicalMax) intValue = logicalMax; + + mg_handle_axis_event((mg_events*)gamepad->src.events, btn, (-1.0f + ((intValue - logicalMin) * 2.0f) / (float)(logicalMax - logicalMin)), gamepad); + } + } +} + + +void mg_osx_device_added_callback(void* context, IOReturn result, void *sender, IOHIDDeviceRef device) { + mg_gamepad* gamepad; + mg_gamepads* gamepads = (mg_gamepads*)context; + + CFIndex i; + CFArrayRef elements; + CFTypeRef property; + u32 vendor = 0, product = 0, version = 0; + CFStringRef deviceName; + CFTypeRef usageRef = (CFTypeRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDPrimaryUsageKey)); + int usage = 0; + if (usageRef) + CFNumberGetValue((CFNumberRef)usageRef, (CFNumberType)kCFNumberIntType, (void*)&usage); + + MG_UNUSED(context); MG_UNUSED(result); MG_UNUSED(sender); + if (usage != kHIDUsage_GD_Joystick && usage != kHIDUsage_GD_GamePad && usage != kHIDUsage_GD_MultiAxisController) { + return; + } + + elements = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone); + if (elements == NULL) + return; + + gamepad = mg_gamepad_find(gamepads); + if (gamepad == NULL) { + return; + } + + IOHIDDeviceRegisterInputValueCallback(device, mg_osx_input_value_changed_callback, gamepad); + + deviceName = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); + if (deviceName) + CFStringGetCString(deviceName, gamepad->name, sizeof(gamepad->name), kCFStringEncodingUTF8); + else + MG_STRNCPY(gamepad->name, "Unknown", sizeof(gamepad->name)); + + property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)); + if (property) + CFNumberGetValue((CFNumberRef)property, (CFNumberType)kCFNumberSInt32Type, (void*)&vendor); + + property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey)); + if (property) + CFNumberGetValue((CFNumberRef)property, kCFNumberSInt32Type, (void*)&product); + + property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVersionNumberKey)); + if (property) + CFNumberGetValue((CFNumberRef)property, (CFNumberType)kCFNumberSInt32Type, (void*)&version); + + if (vendor && product) { + MG_SPRINTF(gamepad->guid, "03000000%02x%02x0000%02x%02x0000%02x%02x0000", + (u8) vendor, (u8) (vendor >> 8), + (u8) product, (u8) (product >> 8), + (u8) version, (u8) (version >> 8)); + } + else { + MG_SPRINTF(gamepad->guid, "05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00", + gamepad->name[0], gamepad->name[1], gamepad->name[2], gamepad->name[3], + gamepad->name[4], gamepad->name[5], gamepad->name[6], gamepad->name[7], + gamepad->name[8], gamepad->name[9], gamepad->name[10]); + } + + + gamepad->mapping = mg_gamepad_find_valid_mapping(gamepad); + gamepad->connected = MG_TRUE; + + for (i = 0; i < CFArrayGetCount(elements); i++) { + u32 elm_usage = 0, page = 0; + IOHIDElementType type; + IOHIDElementRef native = (IOHIDElementRef) + CFArrayGetValueAtIndex(elements, i); + if (CFGetTypeID(native) != IOHIDElementGetTypeID()) + continue; + + type = IOHIDElementGetType(native); + if ((type != kIOHIDElementTypeInput_Axis) && + (type != kIOHIDElementTypeInput_Button) && + (type != kIOHIDElementTypeInput_Misc)) + { + continue; + } + + + elm_usage = IOHIDElementGetUsage(native); + page = IOHIDElementGetUsagePage(native); + + switch (page) { + case kHIDPage_Button: { + mg_button btn = mg_get_gamepad_button(gamepad, (u8)elm_usage); + if (btn == 0) + btn = mg_get_gamepad_button_platform(elm_usage); + if (btn == 0) + break; + + gamepad->buttons[btn].prev = 0; + gamepad->buttons[btn].current = 0; + gamepad->buttons[btn].supported = MG_TRUE; + break; + } + case kHIDPage_GenericDesktop: { + mg_axis btn = mg_get_gamepad_axis(gamepad, (u8)elm_usage); + if (btn == 0) + btn = mg_get_gamepad_axis_platform(elm_usage); + if (btn == 0) + break; + + gamepad->axes[btn].value = 0.0f; + gamepad->axes[btn].supported = MG_TRUE; + break; + } + } + } + + gamepad->src.events = (void*)&gamepads->events; + mg_handle_connection_event(&gamepads->events, MG_TRUE, gamepad); + CFRelease(elements); +} + +void mg_osx_device_removed_callback(void *context, IOReturn result, void *sender, IOHIDDeviceRef device) { + mg_gamepads* gamepads = (mg_gamepads*)context; + mg_gamepad* cur = NULL; + + CFNumberRef usageRef = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDPrimaryUsageKey)); + int usage = 0; + if (usageRef) + CFNumberGetValue((CFNumberRef)usageRef, (CFNumberType)kCFNumberIntType, (void*)&usage); + + MG_UNUSED(context); MG_UNUSED(result); MG_UNUSED(sender); MG_UNUSED(device); + if (usage != kHIDUsage_GD_Joystick && usage != kHIDUsage_GD_GamePad && usage != kHIDUsage_GD_MultiAxisController) { + return; + } + + for (cur = gamepads->list.head; cur; cur = cur->next) { + if ((IOHIDDeviceRef)cur->src.device == device) { + mg_handle_connection_event(&gamepads->events, MG_FALSE, cur); + mg_gamepad_release(gamepads, cur); + return; + } + } +} + +void mg_gamepads_init_platform(mg_gamepads* gamepads) { + const int filter[] = {kHIDPage_GenericDesktop}; + + CFMutableDictionaryRef matchingDictionary; + gamepads->src.hidManager = (void*)IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + + matchingDictionary = CFDictionaryCreateMutable( + kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks + ); + if (!matchingDictionary) { + CFRelease((IOHIDManagerRef)gamepads->src.hidManager); + return; + } + + CFDictionarySetValue( + matchingDictionary, + CFSTR(kIOHIDDeviceUsagePageKey), + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, filter) + ); + + IOHIDManagerSetDeviceMatching((IOHIDManagerRef)gamepads->src.hidManager, matchingDictionary); + CFRelease(matchingDictionary); + + IOHIDManagerRegisterDeviceMatchingCallback((IOHIDManagerRef)gamepads->src.hidManager, mg_osx_device_added_callback, gamepads); + IOHIDManagerRegisterDeviceRemovalCallback((IOHIDManagerRef)gamepads->src.hidManager, mg_osx_device_removed_callback, gamepads); + + IOHIDManagerScheduleWithRunLoop((IOHIDManagerRef)gamepads->src.hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + + IOHIDManagerOpen((IOHIDManagerRef)gamepads->src.hidManager, kIOHIDOptionsTypeNone); + + /* Execute the run loop once in order to register any initially-attached joysticks */ + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false); + +} + +mg_bool mg_gamepads_poll_platform(mg_gamepads* gamepads, mg_events* events) { + MG_UNUSED(gamepads); MG_UNUSED(events); + return MG_FALSE; +} + +void mg_gamepads_free_platform(mg_gamepads* gamepads) { + CFRelease((IOHIDManagerRef)gamepads->src.hidManager); +} + +mg_bool mg_gamepad_update_platform(mg_gamepad* gamepad, mg_events* events) { + gamepad->src.events = (void*)events; + return MG_FALSE; +} + + +void mg_gamepad_release_platform(mg_gamepad* gamepads) { + MG_UNUSED(gamepads); +} + +mg_button mg_get_gamepad_button_platform(u32 button) { + switch (button) { + case kHIDUsage_GD_DPadUp: return MG_BUTTON_DPAD_UP; + case kHIDUsage_GD_DPadRight: return MG_BUTTON_DPAD_RIGHT; + case kHIDUsage_GD_DPadDown: return MG_BUTTON_DPAD_DOWN; + case kHIDUsage_GD_DPadLeft: return MG_BUTTON_DPAD_LEFT; + case kHIDUsage_GD_SystemMainMenu: return MG_BUTTON_GUIDE; + case kHIDUsage_GD_Select: return MG_BUTTON_BACK; + case kHIDUsage_GD_Start: return MG_BUTTON_START; + case kHIDUsage_Button_1: return MG_BUTTON_SOUTH; + case kHIDUsage_Button_2: return MG_BUTTON_WEST; + case kHIDUsage_Button_3: return MG_BUTTON_EAST; + case kHIDUsage_Button_4: return MG_BUTTON_NORTH; + case kHIDUsage_Button_5: return MG_BUTTON_LEFT_SHOULDER; + case kHIDUsage_Button_6: return MG_BUTTON_RIGHT_SHOULDER; + case kHIDUsage_Button_7: return MG_BUTTON_LEFT_TRIGGER; + case kHIDUsage_Button_8: return MG_BUTTON_RIGHT_TRIGGER; + case kHIDUsage_Button_9: return MG_BUTTON_LEFT_STICK; + case kHIDUsage_Button_10: return MG_BUTTON_RIGHT_STICK; + default: break; + } + + return MG_BUTTON_UNKNOWN; +} + +mg_axis mg_get_gamepad_axis_platform(u32 axis) { + switch (axis) { + case kHIDUsage_GD_X: return MG_AXIS_LEFT_X; + case kHIDUsage_GD_Y: return MG_AXIS_LEFT_Y; + case kHIDUsage_GD_Z: return MG_AXIS_LEFT_TRIGGER; + case kHIDUsage_GD_Rx: return MG_AXIS_RIGHT_X; + case kHIDUsage_GD_Ry: return MG_AXIS_RIGHT_Y; + case kHIDUsage_GD_Rz: return MG_AXIS_RIGHT_TRIGGER; + default: break; + } + return MG_AXIS_UNKNOWN; +} +#endif /* MG_MACOS */ + +/* + * start of wasm + */ + +#if defined(MG_WASM) +#include + +mg_gamepad* mg_wasm_gamepads[MG_MAX_GAMEPADS]; + + +EM_BOOL mg_emscripten_on_gamepad(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) { + mg_gamepad* gamepad; + mg_gamepads* gamepads = (mg_gamepads*)userData; + if (gamepads == NULL) { + return 0; + } + + MG_UNUSED(eventType); + + gamepad = mg_wasm_gamepads[gamepadEvent->index]; + + if (gamepadEvent->connected) { + mg_button button; + mg_axis axis; + + if (gamepad) { + mg_gamepad_release(gamepads, mg_wasm_gamepads[gamepadEvent->index]); + } + + gamepad = mg_gamepad_find(gamepads); + if (gamepad == NULL) { + return 0; + } + + for (button = 0; button < MG_BUTTON_MISC1; button++) { + gamepad->buttons[button].supported = MG_TRUE; + gamepad->buttons[button].current = MG_FALSE; + gamepad->buttons[button].prev = MG_FALSE; + } + + for (axis = 0; axis < MG_AXIS_HAT_DPAD_LEFT_RIGHT; axis++) { + gamepad->axes[axis].value = 0; + gamepad->axes[axis].supported = MG_TRUE; + } + + gamepad->connected = MG_TRUE; + mg_wasm_gamepads[gamepadEvent->index] = gamepad; + } else { + mg_gamepad_release(gamepads, mg_wasm_gamepads[gamepadEvent->index]); + mg_wasm_gamepads[gamepadEvent->index] = NULL; + } + + return 1; /* The event was consumed by the callback handler */ +} + +void mg_gamepads_init_platform(mg_gamepads* gamepads) { + emscripten_set_gamepadconnected_callback(gamepads, 1, mg_emscripten_on_gamepad); + emscripten_set_gamepaddisconnected_callback(gamepads, 1, mg_emscripten_on_gamepad); + + emscripten_sample_gamepad_data(); +} + +mg_bool mg_gamepads_poll_platform(mg_gamepads* gamepads, mg_events* events) { + MG_UNUSED(gamepads); MG_UNUSED(events); + return MG_FALSE; +} + +void mg_gamepads_free_platform(mg_gamepads* gamepads) { + MG_UNUSED(gamepads); +} + +mg_bool mg_gamepad_update_platform(mg_gamepad* gamepad, mg_events* events) { + int i = gamepad->src.index; + int j; + EmscriptenGamepadEvent gamepadState; + + emscripten_sample_gamepad_data(); + + if (emscripten_get_gamepad_status(i, &gamepadState) != EMSCRIPTEN_RESULT_SUCCESS) + return MG_FALSE; + + for (j = 0; j < gamepadState.numButtons; j++) { + mg_button map[] = { + MG_BUTTON_SOUTH, MG_BUTTON_EAST, MG_BUTTON_WEST, MG_BUTTON_NORTH, + MG_BUTTON_LEFT_SHOULDER, MG_BUTTON_RIGHT_SHOULDER, MG_BUTTON_LEFT_TRIGGER, MG_BUTTON_RIGHT_TRIGGER, + MG_BUTTON_BACK, MG_BUTTON_START, + MG_BUTTON_LEFT_STICK, MG_BUTTON_RIGHT_STICK, + MG_BUTTON_DPAD_UP, MG_BUTTON_DPAD_DOWN, MG_BUTTON_DPAD_LEFT, MG_BUTTON_DPAD_RIGHT, + MG_BUTTON_GUIDE + }; + mg_button btn = MG_BUTTON_UNKNOWN; + if (j < (int)(sizeof(map) / sizeof(mg_button))) + btn = map[j]; + if (btn == MG_BUTTON_UNKNOWN) + continue; + + mg_handle_button_event(events, btn, MG_BOOL(gamepadState.digitalButton[j]), gamepad); + } + + for (j = 0; j < gamepadState.numAxes; j += 2) { + mg_axis map[] = { + MG_AXIS_LEFT_X, MG_AXIS_LEFT_Y, + MG_AXIS_RIGHT_X, MG_AXIS_RIGHT_Y, + MG_AXIS_LEFT_TRIGGER, MG_AXIS_LEFT_TRIGGER + }; + mg_axis btn = MG_AXIS_UNKNOWN; + if (j < (int)(sizeof(map) / sizeof(mg_axis))) + btn = map[j]; + if (btn == MG_AXIS_UNKNOWN) + continue; + + mg_handle_axis_event(events, btn, (float)gamepadState.axis[j], gamepad); + } + + return MG_FALSE; +} + +void mg_gamepad_release_platform(mg_gamepad* gamepad) { + mg_wasm_gamepads[gamepad->src.index] = NULL; +} + +mg_button mg_get_gamepad_button_platform(u32 button) { + /* TODO */ + MG_UNUSED(button); + return MG_BUTTON_UNKNOWN; +} + +mg_axis mg_get_gamepad_axis_platform(u32 axis) { + /* TODO */ + MG_UNUSED(axis); + return MG_AXIS_UNKNOWN; +} + +#endif + +extern const char* sdl_db[]; + +typedef struct mg_element { + unsigned char type; + unsigned char index; + signed char axisScale; + signed char axisOffset; +} mg_element; + +typedef struct mg_mapping { + char name[128]; + char guid[33]; + mg_element buttons[16]; + mg_element axes[6]; + + mg_button rButtons[256]; + mg_axis rAxes[MG_AXIS_COUNT]; +} mg_mapping; + +#define MG_JOYSTICK_AXIS 1 +#define MG_JOYSTICK_BUTTON 2 +#define MG_JOYSTICK_HATBIT 3 + +typedef struct mappings_data { + mg_mapping mappings[1300]; + int mappingCount; + int mappingMax; +} mappings_data; + +mappings_data mappings; + +mg_button mg_get_gamepad_button(mg_gamepad* gamepad, u8 btn) { + if (gamepad->mapping == NULL) { + return MG_BUTTON_UNKNOWN; + } + + return gamepad->mapping->rButtons[btn]; +} + +mg_axis mg_get_gamepad_axis(mg_gamepad* gamepad, u8 axis) { + if (gamepad->mapping == NULL) { + return MG_AXIS_UNKNOWN; + } + + if (axis >= MG_AXIS_COUNT) { + return MG_AXIS_UNKNOWN; + } + + return gamepad->mapping->rAxes[axis]; +} + +void updateGamepadGUID(char* guid) { +#ifdef __APPLE__ + if ((strncmp(&guid[4], "000000000000", 12) == 0) && + (strncmp(&guid[20], "000000000000", 12) == 0)) + { + char original[33]; + MG_STRNCPY(original, guid, sizeof(original) - 1); + MG_SPRINTF(guid,"03000000%.4s0000%.4s000000000000", + original, original + 16); + } +#elif defined(_WIN32) + if (strncmp(&guid[20], "504944564944", 12) == 0) { + char original[33]; + MG_STRNCPY(original, guid, sizeof(original) - 1); + MG_SPRINTF(guid, "03000000%.4s0000%.4s000000000000", + original, original + 4); + } +#else + (void)(guid); /* prevent unused warning */ +#endif +} + +MG_API mg_mapping* findMapping(const char* guid); +mg_mapping* findMapping(const char* guid) { + int i; + for (i = 0; i < mappings.mappingCount; i++) { + if (strncmp(mappings.mappings[i].guid, guid, sizeof(mappings.mappings[i].guid)) == 0) { + return &mappings.mappings[i]; + } + } + + return NULL; +} + +MG_API mg_mapping* findMappingPermisive(const char* guid); +mg_mapping* findMappingPermisive(const char* guid) { + int i; + for (i = 0; i < mappings.mappingCount; i++) { + if (strncmp(mappings.mappings[i].guid, guid, sizeof(mappings.mappings[i].guid) - 8) == 0) { + return &mappings.mappings[i]; + } + } + + return NULL; +} + +mg_mapping* mg_gamepad_find_valid_mapping(mg_gamepad* js) { + mg_mapping* mapping = findMapping(js->guid); + if (mapping == NULL) { + mapping = findMappingPermisive(js->guid); + if (mapping == NULL) + return NULL; + } + + return mapping; +} + +typedef struct mg_field { + const char* name; + mg_size_t len; + u8 val; + mg_element* element; +} mg_field; + + +MG_API mg_bool parseMapping(mg_mapping* mapping, const char* string); +mg_bool parseMapping(mg_mapping* mapping, const char* string) { + const char* substr = string; + mg_size_t i, length, len; + mg_field fields[] = { + { "platform", 8, 0, NULL }, + { "a", 1, MG_BUTTON_SOUTH , NULL}, + { "b", 1, MG_BUTTON_EAST , NULL}, + { "x", 1, MG_BUTTON_WEST , NULL}, + { "y", 1, MG_BUTTON_NORTH , NULL}, + { "back", 4, MG_BUTTON_BACK , NULL}, + { "start", 5, MG_BUTTON_START , NULL}, + { "guide", 5, MG_BUTTON_GUIDE , NULL}, + { "leftshoulder", 12, MG_BUTTON_LEFT_SHOULDER , NULL}, + { "rightshoulder", 13, MG_BUTTON_RIGHT_SHOULDER , NULL}, + { "leftstick", 9, MG_BUTTON_LEFT_STICK , NULL}, + { "rightstick", 10, MG_BUTTON_RIGHT_STICK , NULL}, + { "dpup", 4, MG_BUTTON_DPAD_UP , NULL}, + { "dpright", 7, MG_BUTTON_DPAD_RIGHT , NULL}, + { "dpdown", 6, MG_BUTTON_DPAD_DOWN , NULL}, + { "dpleft", 6, MG_BUTTON_DPAD_LEFT , NULL}, + { "lefttrigger", 11, MG_BUTTON_LEFT_TRIGGER , NULL}, + { "righttrigger", 12, MG_BUTTON_RIGHT_TRIGGER , NULL }, + + { "lefttrigger", 11, MG_AXIS_LEFT_TRIGGER, NULL}, + { "righttrigger", 12, MG_AXIS_RIGHT_TRIGGER, NULL }, + { "leftx", 5, MG_AXIS_LEFT_X, NULL }, + { "lefty", 5, MG_AXIS_LEFT_Y, NULL } , + { "rightx", 6, MG_AXIS_RIGHT_X, NULL}, + { "righty", 6, MG_AXIS_RIGHT_Y, NULL } + }; + + len = (sizeof(fields) / sizeof(mg_field)); + + for (i = 1; i < len - 8; i++) { + fields[i].element = &mapping->buttons[fields[i].val]; + } + + for (i = len - 8; i < len; i++) { + fields[i].element = &mapping->axes[fields[i].val]; + } + + length = MG_STRCSPN(substr, ","); + if (length != 32 || substr[length] != ',') { + return MG_FALSE; + } + + MG_MEMCPY(mapping->guid, substr, length); + substr += length + 1; + + length = MG_STRCSPN(substr, ","); + if (length >= sizeof(mapping->name) || substr[length] != ',') { + return MG_FALSE; + } + + MG_MEMCPY(mapping->name, substr, length); + substr += length + 1; + + while (substr[0]) { + /* TODO: Implement output modifiers */ + char mod = 0;; + if (substr[0] == '+' || substr[0] == '-') { + mod = substr[0]; + substr++; + } + + for (i = 0; i < sizeof(fields) / sizeof(fields[0]); i++) { + int8_t minimum = -1; + int8_t maximum = 1; + mg_element* e; + + switch (mod) { + case '+': + minimum = 0; + break; + case '-': + maximum = 0; + break; + default: break; + } + + length = fields[i].len; + if (strncmp(substr, fields[i].name, length) != 0 || substr[length] != ':') + continue; + substr += length + 1; + + if (fields[i].element == NULL) { + #if defined(_WIN32) + const char name[] = "Windows"; + #elif defined(__APPLE__) + const char name[] = "Mac OS X"; + #elif defined(EMSCRIPTEN) + const char name[] = "Web"; + #elif defined(__linux__) + const char name[] = "Linux"; + #else + const char name[] = ""; + #endif + if (strncmp(substr, name, sizeof(name)) != 0) + return MG_FALSE; + + break; + } + + e = fields[i].element; + if (e >= mapping->axes && e <= &mapping->axes[6] && substr[0] == 'b') { + continue; + } + + switch (substr[0]) { + case '+': + minimum = 0; + substr += 1; + break; + case '-': + maximum = 0; + substr += 1; + break; + default: break; + } + + switch (substr[0]) { + case 'a': + + e->type = MG_JOYSTICK_AXIS; + e->axisScale = (signed char)(2 / (maximum - minimum)); + e->axisOffset = (signed char)(-(maximum + minimum)); + + if (substr[0] == '~') { + e->axisScale = -e->axisScale; + e->axisOffset = -e->axisOffset; + } + e->index = (uint8_t) strtoul(&substr[1], (char**) &substr, 10); + break; + case 'b': + e->type = MG_JOYSTICK_BUTTON; + e->index = (uint8_t) strtoul(&substr[1], (char**) &substr, 10); + break; + case 'h': { + const unsigned long hat = strtoul(&substr[1], (char**) &substr, 10); + const unsigned long bit = strtoul(&substr[1], (char**) &substr, 10); + + e->type = MG_JOYSTICK_HATBIT; + e->index = (uint8_t) ((hat << 4) | bit); + + break; + } + default: break; + } + + break; + } + + substr += MG_STRCSPN(substr, ","); + substr += MG_STRSPN(substr, ","); + } + + for (i = 0; i < 32; i++) { + if (mapping->guid[i] >= 'A' && mapping->guid[i] <= 'F') + mapping->guid[i] += 'a' - 'A'; + } + + for (i = 0; i < 255; i++) { + mg_size_t y; + mapping->rButtons[i] = MG_BUTTON_UNKNOWN; + for (y = 0; y < 16; y++) { + mg_element e = mapping->buttons[y]; + if (e.index == i) { + mapping->rButtons[i] = (mg_button)y; + break; + } + } + } + + for (i = 0; i < MG_AXIS_COUNT; i++) { + mg_size_t y; + mapping->rAxes[i] = MG_AXIS_UNKNOWN; + for (y = 0; y < 6; y++) { + mg_element e = mapping->axes[y]; + if (e.index == i) { + mapping->rAxes[i] = (mg_axis)y; + break; + } + } + } + + updateGamepadGUID(mapping->guid); + return MG_TRUE; +} + +mg_bool mg_update_gamepad_mappings(mg_gamepads* gamepads, const char* string) { + const char* substr = string; + mg_gamepad* cur; + mg_mapping mapping; + mg_size_t length; + char line[1024]; + + if (mappings.mappingCount >= mappings.mappingMax) { + return MG_FALSE; + } + + while (*substr) { + if (!(*substr >= '0' && *substr <= '9') && + !(*substr >= 'a' && *substr <= 'f') && + !(*substr >= 'A' && *substr <= 'F')) { + substr += MG_STRCSPN(substr, "\r\n"); + substr += MG_STRSPN(substr, "\r\n"); + break; + } + + length = MG_STRCSPN(substr, "\r\n"); + if (length < sizeof(line)) { + MG_MEMSET(&mapping, 0, sizeof(mapping)); + MG_MEMCPY(line, substr, length); + line[length] = '\0'; + + if (parseMapping(&mapping, line)) { + mg_mapping* previous = findMapping(mapping.guid); + if (previous) + *previous = mapping; + else { + mappings.mappingCount++; + /*mappings.mappings = + realloc(mappings.mappings, + sizeof(mg_mapping) * (mg_size_t)mappings.mappingCount);*/ + mappings.mappings[mappings.mappingCount - 1] = mapping; + } + } + } + + substr += length; + } + + for (cur = gamepads->list.head; cur; cur = cur->next) { + cur->mapping = mg_gamepad_find_valid_mapping(cur); + } + + return MG_TRUE; +} + +const char * sdl_db[] = { +#ifdef _WIN32 +"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,", +"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,", +"03000000fa2d00000100000000000000,3dRudder Foot Motion Controller,leftx:a0,lefty:a1,rightx:a5,righty:a2,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000c82d000012ab000000000000,8BitDo NES30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"030000006f0e00001401000000000000,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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000a30c00002800000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"030000000b0400003365000000000000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"0300000011040000c600000000000000,FC801,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000b50700001302000000000000,Firestorm D3,a:b0,b:b2,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,x:b1,y:b3,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000242e00006a38000000000000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000fd0500000030000000000000,Interact GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000c0160000990a000000000000,Mega Drive Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,righttrigger:b2,start:b3,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000000f00000100000000000000,NES Controller,a:b1,b:b0,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,", +"03000000921200004346000000000000,NES Controller,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"030000008a2400006682000000000000,R1 Mobile Controller,a:b3,b:b1,back:b7,leftx:a0,lefty:a1,start:b6,x:b4,y:b0,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000830500000960000000000000,Revenger,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b3,x:b4,y:b5,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000050b0000e318000000000000,ROG Chakram,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,", +"03000000050b0000e518000000000000,ROG Chakram,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,", +"03000000050b00005819000000000000,ROG Chakram Core,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,", +"03000000050b0000181a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,", +"03000000050b00001a1a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,", +"03000000050b00001c1a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000730700000401000000000000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,", +"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,", +"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,", +"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,", +"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,", +"03000000a30c00002500000000000000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000380700001722000000000000,Speedlink Competition Pro,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,x:b2,y:b3,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000591c00002400000000000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,", +"03000000591c00002600000000000000,THEGamepad,a:b2,b:b1,back:b6,leftx:a0,lefty:a1,start:b7,x:b3,y:b0,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000571d00002100000000000000,Tomee NES Controller Adapter,a:b1,b:b0,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,start:b3,", +"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,", +"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,", +"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,", +"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,", +"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,", +"030000004f04000087b6000000000000,TWCS Throttle,dpdown:b8,dpleft:b9,dpright:b7,dpup:b6,leftstick:b5,lefttrigger:-a5,leftx:a0,lefty:a1,righttrigger:+a5,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000bd12000012d0000000000000,USB Controller,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000e0ff00000201000000000000,Xiaomi Black Shark (L),back:b0,dpdown:b11,dpleft:b9,dpright:b10,dpup:b8,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,", +"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,", +"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,", +"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,", +"xinput,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,", +"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,", +"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,", +"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,", +"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,", +"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," +#elif defined(__APPLE__) +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000c82d00001330000000020000,8BitDo Ultimate 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,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000a30c00002800000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000fd0500000030000010010000,Interact GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,", +"03000000a30c00002500000006020000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000591c00002400000021000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,", +"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,", +"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,", +"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,", +"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,", +"03000000571d00002100000021000000,Tomee NES Controller Adapter,a:b1,b:b0,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,start:b3,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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," +#elif defined(TARGET_OS_IPHONE) +"05000000ac0500000100000000006d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS,", +"05000000ac050000010000004f066d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS,", +"05000000ac05000001000000cf076d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS,", +"05000000ac05000001000000df076d01,*,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,", +"05000000ac05000001000000ff076d01,*,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:iOS,", +"05000000ac0500000200000000006d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,platform:iOS,", +"05000000ac050000020000004f066d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,platform:iOS,", +"05000000ac05000004000000a8986d04,8BitDo Micro gamepad,a:b1,b:b0,back:b4,dpdown:b7,dpleft:b8,dpright:b9,dpup:b10,guide:b2,leftshoulder:b11,lefttrigger:b12,rightshoulder:b13,righttrigger:b14,start:b3,x:b6,y:b5,platform:iOS,", +"05000000ac050000040000003b8a6d04,8BitDo SN30 Pro+,a:b1,b:b0,back:b4,dpdown:b7,dpleft:b8,dpright:b9,dpup:b10,guide:b2,leftshoulder:b11,leftstick:b12,lefttrigger:b13,leftx:a0,lefty:a1~,rightshoulder:b14,rightstick:b15,righttrigger:b16,rightx:a2,righty:a3~,start:b3,x:b6,y:b5,platform:iOS,", +"050000008a35000003010000ff070000,Backbone One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:iOS,", +"050000008a35000004010000ff070000,Backbone One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:iOS,", +"4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS,", +"4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS,", +"050000007e050000062000000f060000,Nintendo Switch Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b2,leftshoulder:b4,rightshoulder:b5,x:b1,y:b3,platform:iOS,", +"050000007e050000062000004f060000,Nintendo Switch Joy-Con (L),+leftx:h0.1,+lefty:h0.2,-leftx:h0.4,-lefty:h0.8,dpdown:b2,dpleft:b0,dpright:b3,dpup:b1,leftshoulder:b4,misc1:b6,rightshoulder:b5,platform:iOS,", +"050000007e05000008200000df070000,Nintendo Switch Joy-Con (L/R),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,", +"050000007e0500000e200000df070000,Nintendo Switch Joy-Con (L/R),a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:iOS,", +"050000007e050000072000004f060000,Nintendo Switch Joy-Con (R),+rightx:h0.4,+righty:h0.8,-rightx:h0.1,-righty:h0.2,a:b1,b:b0,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b3,y:b2,platform:iOS,", +"050000007e05000009200000df870000,Nintendo Switch Pro Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b10,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:iOS,", +"050000007e05000009200000ff870000,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,misc1:b11,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,", +"050000004c050000cc090000df070000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,", +"050000004c050000cc090000df870001,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,", +"050000004c050000cc090000ff070000,PS4 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:iOS,", +"050000004c050000cc090000ff870001,PS4 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,touchpad:b11,x:b2,y:b3,platform:iOS,", +"050000004c050000cc090000ff876d01,PS4 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,misc1:b11,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,", +"050000004c050000e60c0000df870000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,touchpad:b10,x:b2,y:b3,platform:iOS,", +"050000004c050000e60c0000ff870000,PS5 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,touchpad:b11,x:b2,y:b3,platform:iOS,", +"05000000ac0500000300000000006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS,", +"05000000ac0500000300000043006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS,", +"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS,", +"05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS,", +"050000005e040000050b0000df070001,Xbox Elite Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b10,paddle2:b12,paddle3:b11,paddle4:b13,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,", +"050000005e040000050b0000ff070001,Xbox Elite 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,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,", +"050000005e040000e0020000df070000,Xbox One Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,", +"050000005e040000e0020000ff070000,Xbox One 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:iOS,", +"050000005e040000130b0000df870001,Xbox Series Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b10,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,", +"050000005e040000130b0000ff870001,Xbox Series 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,misc1:b11,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS," +#elif defined(__linux__) + "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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000c82d00000631000014010000,8BitDo Ultimate 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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000a30c00002800000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000af1e00002400000010010000,Clockwork Pi DevTerm,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b9,x:b3,y:b0,", +"030000000b0400003365000000010000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03005036852100000201000010010000,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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000242e00006a38000010010000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000d80400008200000003000000,IMS PCU0,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,", +"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,", +"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,", +"03000000fd0500000030000000010000,InterAct GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", + "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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"050000005a1d00000218000003000000,Nokia GC 5000,a:b9,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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,", +"050000004c0500006802000000800000,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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"030000009b2800000300000001010000,Raphnet 4nes4snes,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,", +"030000009b2800004200000001010000,Raphnet Dual NES Adapter,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"0300000081170000990a000001010000,Retronic Adapter,a:b0,leftx:a0,lefty:a1,", +"0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000a30c00002500000011010000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000591c00002400000010010000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,", +"03000000591c00002600000010010000,THEGamepad,a:b2,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b0,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"03000000790000001100000000010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", + "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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"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,", +"xinput,XInput 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,", +"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,", +"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," +#elif defined(__ANDROID_API__) +"38653964633230666463343334313533,8BitDo Adapter,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"36666264316630653965636634386234,8BitDo Adapter 2,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"38426974446f20417263616465205374,8BitDo Arcade Stick,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b5,leftshoulder:b9,lefttrigger:a4,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"61393962646434393836356631636132,8BitDo Arcade Stick,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,", +"64323139346131306233636562663738,8BitDo Arcade Stick,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,", +"64643565386136613265663236636564,8BitDo Arcade Stick,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,", +"33313433353539306634656436353432,8BitDo Dogbone,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"38426974446f20446f67626f6e65204d,8BitDo Dogbone,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,", +"34343439373236623466343934376233,8BitDo FC30 Pro,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b28,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b29,righttrigger:b7,start:b5,x:b30,y:b2,", +"38426974446f204e4743204d6f646b69,8BitDo GameCube,a:b0,b:b2,back:b4,dpdown:b12,dpleft:b13,dpright:b14,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,paddle1:b18,paddle2:b17,rightshoulder:b15,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b1,y:b3,", +"38426974446f2038426974446f204c69,8BitDo Lite,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"30643332373663313263316637356631,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"38426974446f204c6974652032000000,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"62656331626461363634633735353032,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"38393936616436383062666232653338,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"38426974446f204c6974652053450000,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"39356430616562366466646636643435,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"05000000c82d000006500000ffff3f00,8BitDo M30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b17,leftshoulder:b9,lefttrigger:a5,rightshoulder:b10,righttrigger:a4,start:b6,x:b3,y:b2,", +"05000000c82d000051060000ffff3f00,8BitDo M30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b17,leftshoulder:b9,lefttrigger:a4,rightshoulder:b10,righttrigger:a5,start:b6,x:b3,y:b2,", +"32323161363037623637326438643634,8BitDo M30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"33656266353630643966653238646264,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,start:b10,x:b19,y:b2,", +"38426974446f204d3330204d6f646b69,8BitDo M30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"39366630663062373237616566353437,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,start:b6,x:b2,y:b3,", +"64653533313537373934323436343563,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,start:b6,x:b2,y:b3,", +"66356438346136366337386437653934,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,start:b18,x:b19,y:b2,", +"66393064393162303732356665666366,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,", +"38426974446f204d6963726f2067616d,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,", +"61653365323561356263373333643266,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,", +"62613137616239666338343866326336,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,", +"33663431326134333366393233616633,8BitDo N30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,", +"38426974446f204e3330204d6f646b69,8BitDo N30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,", +"05000000c82d000015900000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"05000000c82d000065280000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,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,", +"38323035343766666239373834336637,8BitDo N64,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,", +"38426974446f204e3634204d6f646b69,8BitDo N64,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,", +"32363135613966656338666638666237,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"35363534633333373639386466346631,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"38426974446f204e454f47454f204750,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"39383963623932353561633733306334,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"050000000220000000900000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,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:b3,y:b2,", +"050000002038000009000000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,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:b3,y:b2,", +"38313433643131656262306631373166,8BitDo P30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"38326536643339353865323063616339,8BitDo P30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"38426974446f2050333020636c617373,8BitDo P30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"35376664343164386333616535333434,8BitDo Pro 2,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,start:b10,x:b19,y:b2,", +"38426974446f2038426974446f205072,8BitDo Pro 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"38426974446f2050726f203200000000,8BitDo Pro 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"61333362366131643730353063616330,8BitDo Pro 2,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,", +"62373739366537363166326238653463,8BitDo Pro 2,a:b1,b:b0,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,x:b3,y:b2,", +"38386464613034326435626130396565,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"38426974446f2038426974446f205265,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"66303230343038613365623964393766,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"38426974446f20533330204d6f646b69,8BitDo S30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"66316462353561376330346462316137,8BitDo S30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"05000000c82d000000600000ffff3f00,8BitDo SF30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"05000000c82d000000610000ffff3f00,8BitDo SF30 Pro,a:b1,b:b0,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:b3,y:b2,", +"38426974646f20534633302050726f00,8BitDo SF30 Pro,a:b1,b:b0,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b17,", +"61623334636338643233383735326439,8BitDo SFC30,a:b0,b:b1,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b31,start:b5,x:b30,y:b2,", +"05000000c82d000012900000ffff3f00,8BitDo SN30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,", +"05000000c82d000062280000ffff3f00,8BitDo SN30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,", +"38316230613931613964356666353839,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"38426974446f20534e3330204d6f646b,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"65323563303231646531383162646335,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"35383531346263653330306238353131,8BitDo SN30 PP,a:b0,b:b1,back:b15,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,", +"05000000c82d000001600000ffff3f00,8BitDo SN30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"05000000c82d000002600000ffff0f00,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"36653638656632326235346264663661,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,", +"38303232393133383836366330346462,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,", +"38346630346135363335366265656666,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"38426974446f20534e33302050726f2b,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"536f6e7920436f6d707574657220456e,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b15,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,", +"66306331643531333230306437353936,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"050000002028000009000000ffff3f00,8BitDo SNES30,a:b1,b:b0,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:b3,y:b2,", +"050000003512000020ab000000780f00,8BitDo SNES30,a:b21,b:b20,back:b30,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b26,rightshoulder:b27,start:b31,x:b24,y:b23,", +"33666663316164653937326237613331,8BitDo Zero,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,", +"38426974646f205a65726f2047616d65,8BitDo Zero,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,", +"05000000c82d000018900000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,", +"05000000c82d000030320000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,", +"33663434393362303033616630346337,8BitDo Zero 2,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,x:b19,y:b2,", +"34656330626361666438323266633963,8BitDo Zero 2,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b20,start:b10,x:b19,y:b2,", +"63396666386564393334393236386630,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,", +"63633435623263373466343461646430,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,", +"32333634613735616163326165323731,Amazon Luna Controller,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,x:b2,y:b3,", +"4c696e757820342e31392e3137322077,Anbernic Handheld,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a4,start:b6,x:b2,y:b3,", +"417374726f2063697479206d696e6920,Astro City Mini,a:b23,b:b22,back:b29,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b25,righttrigger:b26,start:b30,x:b24,y:b21,", +"35643263313264386134376362363435,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,start:b6,", +"32353831643566306563643065356239,Atari VCS Modern Controller,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,", +"4f64696e20436f6e74726f6c6c657200,AYN Odin,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b14,dpright:b13,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:+a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"32303165626138343962363666346165,Brook Mars PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,", +"38383337343564366131323064613561,Brook Mars PS4 Controller,a:b1,b:b19,back:b17,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,", +"34313430343161653665353737323365,Elecom JC-W01U,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,", +"4875694a6961204a432d573031550000,Elecom JC-W01U,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,", +"30363230653635633863366338623265,Evo VR,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,x:b2,y:b3,", +"05000000b404000011240000dfff3f00,Flydigi Vader 2,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"05000000bc20000000550000ffff3f00,GameSir G3w,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"34323662653333636330306631326233,Google Nexus,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,", +"35383633353935396534393230616564,Google Stadia Controller,a:b0,b:b1,back:b15,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,", +"476f6f676c65204c4c43205374616469,Google Stadia Controller,a:b0,b:b1,back:b15,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,", +"5374616469614e3848532d6532633400,Google Stadia Controller,a:b0,b:b1,back:b15,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,", +"05000000d6020000e5890000dfff3f00,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,", +"05000000d6020000e5890000dfff3f80,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a3,rightx:a4,righty:a5,start:b6,x:b2,y:b3,", +"66633030656131663837396562323935,Hori Battle,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,", +"35623466343433653739346434636330,Hori Fighting Commander 3 Pro,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,", +"484f524920434f2e2c4c54442e203130,Hori Fighting Commander 3 Pro,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b20,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b9,rightx:a2,righty:a3,start:b18,x:b0,y:b2,", +"484f524920434f2e2c4c544420205041,Hori Gem Pad 3,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b0,y:b2,", +"65656436646661313232656661616130,Hori PC Engine Mini Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,start:b18,", +"31303433326562636431653534636633,Hori Real Arcade Pro 3,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,", +"32656664353964393561366362333636,Hori Switch Split Pad Pro,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,", +"30306539356238653637313730656134,HORIPAD Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,", +"48797065726b696e2050616400000000,Hyperkin Admiral N64 Controller,+rightx:b6,+righty:b7,-rightx:b17,-righty:b5,a:b1,b:b0,leftshoulder:b3,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,", +"62333331353131353034386136626636,Hyperkin Admiral N64 Controller,+rightx:b6,+righty:b7,-rightx:b17,-righty:b5,a:b1,b:b0,leftshoulder:b3,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,", +"31306635363562663834633739396333,Hyperkin N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,", +"5368616e57616e202020202048797065,Hyperkin N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,", +"0500000083050000602000000ffe0000,iBuffalo SNES Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b15,rightshoulder:b16,start:b10,x:b2,y:b3,", +"5553422c322d6178697320382d627574,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,rightshoulder:b18,start:b10,x:b3,y:b2,", +"64306137363261396266353433303531,InterAct GoPad,a:b24,b:b25,leftshoulder:b23,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,x:b21,y:b22,", +"532e542e442e20496e74657261637420,InterAct HammerHead FX,a:b23,b:b24,back:b30,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,leftstick:b22,lefttrigger:b28,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b25,righttrigger:b29,rightx:a2,righty:a3,start:b31,x:b20,y:b21,", +"65346535636333663931613264643164,Joy-Con,a:b21,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b23,y:b24,", +"33346566643039343630376565326335,Joy-Con (L),a:b0,b:b1,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b17,x:b19,y:b2,", +"35313531613435623366313835326238,Joy-Con (L),a:b0,b:b1,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b17,x:b19,y:b2,", +"4a6f792d436f6e20284c290000000000,Joy-Con (L),a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b17,x:b19,y:b2,", +"38383665633039363066383334653465,Joy-Con (R),a:b0,b:b1,back:b5,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b18,x:b19,y:b2,", +"39363561613936303237333537383931,Joy-Con (R),a:b0,b:b1,back:b5,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b18,x:b19,y:b2,", +"39373064396565646338333134303131,Joy-Con (R),a:b1,b:b2,back:b5,leftstick:b8,leftx:a1~,lefty:a0,start:b6,x:b0,y:b3,", +"4a6f792d436f6e202852290000000000,Joy-Con (R),a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,", +"39656136363638323036303865326464,JYS Aapter,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,", +"63316564383539663166353034616434,JYS Adapter,a:b1,b:b3,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b0,y:b2,", +"64623163333561643339623235373232,Logitech F310,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,", +"35623364393661626231343866613337,Logitech F710,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"4c6f6769746563682047616d65706164,Logitech F710,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,", +"64396331333230326333313330336533,Logitech F710,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,", +"39653365373864633935383236363438,Logitech G Cloud,a:b0,b:b1,back:b15,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,", +"416d617a6f6e2047616d6520436f6e74,Luna Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,", +"4c756e612047616d6570616400000000,Luna Controller,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,", +"30363066623539323534363639323363,Magic NS,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,", +"31353762393935386662336365626334,Magic NS,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,", +"39623565346366623931666633323530,Magic NS,a:b1,b:b3,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b0,y:b2,", +"6d6179666c617368206c696d69746564,Mayflash GameCube Adapter,a:b22,b:b21,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a5,righty:a2,start:b30,x:b23,y:b24,", +"436f6e74726f6c6c6572000000000000,Mayflash N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,", +"65666330633838383061313633326461,Mayflash N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,", +"37316565396364386635383230353365,Mayflash Saturn Adapter,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,", +"4875694a696120205553422047616d65,Mayflash Saturn Adapter,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,", +"535a4d792d706f776572204c54442043,Mayflash Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b31,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,", +"30653962643666303631376438373532,Mayflash Wii DolphinBar,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b0,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,", +"39346131396233376535393665363161,Mayflash Wii U Pro Adapter,a:b22,b:b23,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,leftstick:b31,lefttrigger:b27,rightshoulder:b26,rightstick:b0,righttrigger:b28,rightx:a0,righty:a1,start:b30,x:b21,y:b24,", +"31323564663862633234646330373138,Mega Drive,a:b23,b:b22,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b25,righttrigger:b26,start:b30,x:b24,y:b21,", +"37333564393261653735306132613061,Mega Drive,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,", +"64363363336633363736393038313464,Mega Drive,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,start:b9,x:b2,y:b3,", +"33323763323132376537376266393366,Microsoft Dual Strike,a:b24,b:b23,back:b25,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b29,rightshoulder:b78,rightx:a0,righty:a1~,start:b26,x:b22,y:b21,", +"30306461613834333439303734316539,Microsoft SideWinder Pro,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b20,lefttrigger:b9,rightshoulder:b19,righttrigger:b10,start:b17,x:b2,y:b3,", +"32386235353630393033393135613831,Microsoft Xbox Series Controller,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,", +"4d4f42415041442050726f2d48440000,Mobapad Chitu HD,a:b0,b:b1,back:b15,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,", +"4d4f435554452d303533582d4d35312d,Mocute 053X,a:b0,b:b1,back:b15,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,", +"33343361376163623438613466616531,Mocute M053,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"39306635663061636563316166303966,Mocute M053,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"7573622067616d657061642020202020,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,", +"050000007e05000009200000ffff0f00,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b17,y:b2,", +"31316661666466633938376335383661,Nintendo Switch Pro Controller,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,misc1:b5,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,start:b6,x:b3,y:b2,", +"34323437396534643531326161633738,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,misc1:b5,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"50726f20436f6e74726f6c6c65720000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b2,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b10,rightx:a2,righty:a3,start:b18,y:b3,", +"36326533353166323965623661303933,NSO N64 Controller,+rightx:b17,+righty:b10,-rightx:b2,-righty:b19,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,righttrigger:b15,start:b18,", +"4e363420436f6e74726f6c6c65720000,NSO N64 Controller,+rightx:b17,+righty:b10,-rightx:b2,-righty:b19,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,righttrigger:b15,start:b18,", +"534e455320436f6e74726f6c6c657200,NSO SNES Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,", +"64623863346133633561626136366634,NSO SNES Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,", +"050000005509000003720000cf7f3f00,NVIDIA Controller,a:b0,b:b1,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,", +"050000005509000010720000ffff3f00,NVIDIA Controller,a:b0,b:b1,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,", +"050000005509000014720000df7f3f00,NVIDIA Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,", +"050000005509000014720000df7f3f80,NVIDIA Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a3,rightx:a4,righty:a5,start:b6,x:b2,y:b3,", +"37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,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,", +"4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,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,", +"61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,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,", +"39383335313438623439373538343266,OUYA Controller,a:b0,b:b2,dpdown:b18,dpleft:b15,dpright:b16,dpup:b17,leftshoulder:b3,leftstick:b9,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,x:b1,y:b19,", +"4f5559412047616d6520436f6e74726f,OUYA Controller,a:b0,b:b2,dpdown:b18,dpleft:b15,dpright:b6,dpup:b17,leftshoulder:b3,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b19,", +"506572666f726d616e63652044657369,PDP PS3 Rock Candy Controller,a:b1,b:b17,back:h0.2,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b0,y:b2,", +"61653962353232366130326530363061,Pokken,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,rightshoulder:b20,righttrigger:b10,start:b18,x:b0,y:b2,", +"32666633663735353234363064386132,PS2,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a3,righty:a2,start:b30,x:b24,y:b21,", +"050000004c05000068020000dfff3f00,PS3 Controller,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,", +"536f6e7920504c415953544154494f4e,PS3 Controller,a:b0,b:b1,back:b15,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,", +"61363034663839376638653463633865,PS3 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"66366539656564653432353139356536,PS3 Controller,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,", +"66383132326164626636313737373037,PS3 Controller,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,", +"050000004c050000c405000000783f00,PS4 Controller,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,", +"050000004c050000c4050000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,", +"050000004c050000c4050000fffe3f80,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a3,rightx:a4,righty:a5,start:b16,x:b0,y:b2,", +"050000004c050000c4050000ffff3f00,PS4 Controller,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,", +"050000004c050000cc090000fffe3f00,PS4 Controller,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,", +"050000004c050000cc090000ffff3f00,PS4 Controller,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,", +"30303839663330346632363232623138,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,", +"31326235383662333266633463653332,PS4 Controller,a:b1,b:b16,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b17,x:b0,y:b2,", +"31373231336561636235613666323035,PS4 Controller,a:b0,b:b1,back:b15,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,", +"31663838336334393132303338353963,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"34613139376634626133336530386430,PS4 Controller,a:b0,b:b1,back:b15,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,", +"35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,", +"37626233336235343937333961353732,PS4 Controller,a:b0,b:b1,back:b15,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,", +"37626464343430636562316661643863,PS4 Controller,a:b0,b:b1,back:b15,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,", +"38393161636261653636653532386639,PS4 Controller,a:b0,b:b1,back:b15,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,", +"63313733393535663339656564343962,PS4 Controller,a:b0,b:b1,back:b15,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,", +"63393662363836383439353064663939,PS4 Controller,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,", +"65366465656364636137653363376531,PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,", +"66613532303965383534396638613230,PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,", +"050000004c050000e60c0000fffe3f00,PS5 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,", +"050000004c050000e60c0000fffe3f80,PS5 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a3,rightx:a4,righty:a5,start:b16,x:b2,y:b17,", +"050000004c050000e60c0000ffff3f00,PS5 Controller,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,", +"32346465346533616263386539323932,PS5 Controller,a:b0,b:b1,back:b15,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,", +"32633532643734376632656664383733,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,", +"37363764353731323963323639666565,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,", +"61303162353165316365336436343139,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,", +"64336263393933626535303339616332,Qanba 4RAF,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b20,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b9,rightx:a2,righty:a3,start:b18,x:b19,y:b2,", +"36626666353861663864336130363137,Razer Junglecat,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,", +"05000000f8270000bf0b0000ffff3f00,Razer Kishi,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,", +"62653861643333663663383332396665,Razer Kishi,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"050000003215000005070000ffff3f00,Razer Raiju Mobile,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,", +"050000003215000007070000ffff3f00,Razer Raiju Mobile,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,", +"050000003215000000090000bf7f3f00,Razer Serval,a:b0,b:b1,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,x:b2,y:b3,", +"5a6869587520526574726f2042697420,Retro Bit Saturn Controller,a:b21,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b26,rightshoulder:b27,righttrigger:b28,start:b30,x:b23,y:b24,", +"32417865732031314b6579732047616d,Retro Bit SNES Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,", +"36313938306539326233393732613361,Retro Bit SNES Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,", +"526574726f466c616720576972656420,Retro Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,rightshoulder:b18,start:b10,x:b2,y:b3,", +"61343739353764363165343237303336,Retro Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,lefttrigger:b18,leftx:a0,lefty:a1,start:b10,x:b2,y:b3,", +"526574726f696420506f636b65742043,Retroid Pocket,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"582d426f7820436f6e74726f6c6c6572,Retroid Pocket,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"64633735616665613536653363336132,Retroid Pocket,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,paddle1:b19,paddle2:b20,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"38653130373365613538333235303036,Retroid Pocket 2,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"64363363336633363736393038313463,Retrolink,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,start:b6,", +"37393234373533633333323633646531,RetroUSB N64 RetroPort,+rightx:b17,+righty:b15,-rightx:b18,-righty:b6,a:b10,b:b9,dpdown:b19,dpleft:b1,dpright:b0,dpup:b2,leftshoulder:b7,lefttrigger:b20,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,", +"5365616c6965436f6d707574696e6720,RetroUSB N64 RetroPort,+rightx:b17,+righty:b15,-rightx:b18,-righty:b6,a:b10,b:b9,dpdown:b19,dpleft:b1,dpright:b0,dpup:b2,leftshoulder:b7,lefttrigger:b20,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,", +"526574726f5553422e636f6d20534e45,RetroUSB SNES RetroPort,a:b1,b:b20,back:b19,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b2,x:b0,y:b3,", +"64643037633038386238303966376137,RetroUSB SNES RetroPort,a:b1,b:b20,back:b19,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b2,x:b0,y:b3,", +"37656564346533643138636436356230,Rock Candy Switch Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,", +"33373336396634316434323337666361,RumblePad 2,a:b22,b:b23,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b24,", +"36363537303435333566386638366333,Samsung EIGP20,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"53616d73756e672047616d6520506164,Samsung EIGP20,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"66386565396238363534313863353065,Sanwa PlayOnline Mobile,a:b21,b:b22,back:b23,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b24,", +"32383165316333383766336338373261,Saturn,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:a4,righttrigger:a5,x:b2,y:b3,", +"38613865396530353338373763623431,Saturn,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,lefttrigger:b10,rightshoulder:b20,righttrigger:b19,start:b17,x:b2,y:b3,", +"61316232336262373631343137633631,Saturn,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:a4,righttrigger:a5,x:b2,y:b3,", +"30353835333338613130373363646337,SG H510,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,", +"66386262366536653765333235343634,SG H510,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,x:b2,y:b3,", +"66633132393363353531373465633064,SG H510,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,", +"62653761636366393366613135366338,SN30 PP,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", +"38376662666661636265313264613039,SNES,a:b0,b:b1,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b20,start:b10,x:b19,y:b2,", +"5346432f555342205061640000000000,SNES Adapter,a:b0,b:b1,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b20,start:b10,x:b19,y:b2,", +"5553422047616d657061642000000000,SNES Controller,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,", +"62653335326261303663356263626339,Sony PlayStation Classic Controller,a:b19,b:b1,back:b17,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,lefttrigger:b3,rightshoulder:b10,righttrigger:b20,start:b18,x:b2,y:b0,", +"536f6e7920496e746572616374697665,Sony PlayStation Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"576972656c65737320436f6e74726f6c,Sony PlayStation Controller,a:b0,b:b1,back:b15,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,", +"63303964303462366136616266653561,Sony PSP,a:b21,b:b22,back:b27,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b28,x:b23,y:b24,", +"63376637643462343766333462383235,Sony Vita,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a3,righty:a4,start:b18,x:b0,y:b2,", +"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,", +"05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,", +"0500000011010000201400000f7e0f00,SteelSeries Nimbus,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,x:b19,y:b2,", +"35306436396437373135383665646464,SteelSeries Nimbus Plus,a:b0,b:b1,leftshoulder:b3,leftstick:b17,lefttrigger:b9,leftx:a0,rightshoulder:b20,rightstick:b18,righttrigger:b10,rightx:a2,x:b19,y:b2,", +"33313930373536613937326534303931,Taito Egret II Mini Control Panel,a:b25,b:b23,back:b27,guide:b30,leftx:a0,lefty:a1,rightshoulder:b21,righttrigger:b22,start:b28,x:b29,y:b24,", +"54475a20436f6e74726f6c6c65720000,TGZ Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"62363434353532386238336663643836,TGZ Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"37323236633763666465316365313236,THEC64 Joystick,a:b21,b:b22,back:b27,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b27,x:b23,y:b24,", +"38346162326232346533316164363336,THEGamepad,a:b23,b:b22,back:b27,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b28,x:b24,y:b21,", +"050000004f0400000ed00000fffe3f00,Thrustmaster eSwap Pro Controller,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,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"5477696e20555342204a6f7973746963,Twin Joystick,a:b22,b:b21,back:b28,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,", +"30623739343039643830333266346439,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b24,paddle2:b23,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"31643365666432386133346639383937,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b24,paddle2:b23,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"30386438313564306161393537333663,Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,", +"33333034646336346339646538643633,Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,", +"050000005e0400008e02000000783f00,Xbox 360 Controller,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,", +"30396232393162346330326334636566,Xbox 360 Controller,a:b0,b:b1,back:b4,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,", +"38313038323730383864666463383533,Xbox 360 Controller,a:b0,b:b1,back:b4,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,", +"58626f782033363020576972656c6573,Xbox 360 Controller,a:b0,b:b1,back:b15,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,", +"65353331386662343338643939643636,Xbox 360 Controller,a:b0,b:b1,back:b4,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,", +"65613532386633373963616462363038,Xbox 360 Controller,a:b0,b:b1,back:b4,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,", +"47656e6572696320582d426f78207061,Xbox Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"4d6963726f736f667420582d426f7820,Xbox Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"64633436313965656664373634323364,Xbox Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,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,", +"050000005e04000091020000ff073f00,Xbox One Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,", +"050000005e04000091020000ff073f80,Xbox One Controller,a:b0,b:b1,back:b4,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,", +"050000005e040000e00200000ffe3f00,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,", +"050000005e040000e00200000ffe3f80,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a2,righty:a3,start:b10,x:b17,y:b2,", +"050000005e040000e0020000ffff3f00,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b4,leftshoulder:b3,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,", +"050000005e040000e0020000ffff3f80,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b4,leftshoulder:b3,leftstick:b8,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b10,x:b17,y:b2,", +"050000005e040000fd020000ffff3f00,Xbox One Controller,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,", +"33356661323266333733373865656366,Xbox One Controller,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,", +"34356136633366613530316338376136,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,", +"35623965373264386238353433656138,Xbox One Controller,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,", +"36616131643361333337396261666433,Xbox One Controller,a:b0,b:b1,back:b15,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,", +"58626f7820576972656c65737320436f,Xbox One Controller,a:b0,b:b1,back:b15,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,", +"65316262316265373335666131623538,Xbox One Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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,", +"050000005e040000000b000000783f00,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,", +"050000005e040000000b000000783f80,Xbox One Elite 2 Controller,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,", +"050000005e040000050b0000ffff3f00,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a6,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", +"050000005e040000e002000000783f00,Xbox One S Controller,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,", +"050000005e040000ea02000000783f00,Xbox One S Controller,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,", +"050000005e040000fd020000ff7f3f00,Xbox One S Controller,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,", +"050000005e040000120b000000783f00,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,", +"050000005e040000120b000000783f80,Xbox Series Controller,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,", +"050000005e040000130b0000ffff3f00,Xbox Series Controller,a:b0,b:b1,back:b15,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,", +"65633038363832353634653836396239,Xbox Series Controller,a:b0,b:b1,back:b15,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,", +"050000001727000044310000ffff3f00,XiaoMi Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a6,rightx:a2,righty:a5,start:b6,x:b2,y:b3," +#elif defined(EMSCRIPTEN) +"\0" /* look into at some point */ +#endif +}; + +void mg_mappings_init(void) { + mg_size_t i; + mappings.mappingCount = 0; + mappings.mappingMax = 1300; + MG_MEMSET(mappings.mappings, 0, sizeof(mappings.mappings)); + + for (i = 0; i < (sizeof(sdl_db) / sizeof(char*)); i++) { + if (parseMapping(&mappings.mappings[mappings.mappingCount], sdl_db[i])) + mappings.mappingCount++; + } +} + +const char* mg_button_get_name(mg_button btn) { + switch (btn) { + case MG_BUTTON_COUNT: + case MG_BUTTON_UNKNOWN: + return "Unknown Button"; + case MG_BUTTON_SOUTH: + return "South Button"; + case MG_BUTTON_WEST: + return "West Button"; + case MG_BUTTON_NORTH: + return "North Button"; + case MG_BUTTON_EAST: + return "East Button"; + case MG_BUTTON_BACK: + return "Back Button"; + case MG_BUTTON_GUIDE: + return "Guide Button"; + case MG_BUTTON_START: + return "Start Button"; + case MG_BUTTON_LEFT_STICK: + return "Left Stick Button"; + case MG_BUTTON_RIGHT_STICK: + return "Right Stick Button"; + case MG_BUTTON_DPAD_UP: + return "D-pad Up"; + case MG_BUTTON_DPAD_DOWN: + return "D-pad Down"; + case MG_BUTTON_DPAD_LEFT: + return "D-pad Left"; + case MG_BUTTON_DPAD_RIGHT: + return "D-pad Right"; + case MG_BUTTON_LEFT_SHOULDER: + return "Left Shoulder Button"; + case MG_BUTTON_RIGHT_SHOULDER: + return "Right Shoulder Button"; + case MG_BUTTON_LEFT_TRIGGER: + return "Left Trigger Button"; + case MG_BUTTON_RIGHT_TRIGGER: + return "Right Trigger Button"; + case MG_BUTTON_MISC1: + return "Misc Button 1"; + case MG_BUTTON_RIGHT_PADDLE1: + return "Paddle 1 Right"; + case MG_BUTTON_LEFT_PADDLE1: + return "Paddle 1 Left"; + case MG_BUTTON_RIGHT_PADDLE2: + return "Paddle 2 Right"; + case MG_BUTTON_LEFT_PADDLE2: + return "Paddle 2 Left"; + case MG_BUTTON_TOUCHPAD: + return "Touchpad"; + case MG_BUTTON_MISC2: + return "Misc Button 2"; + case MG_BUTTON_MISC3: + return "Misc Button 3"; + case MG_BUTTON_MISC4: + return "Misc Button 4"; + case MG_BUTTON_MISC5: + return "Misc Button 5"; + case MG_BUTTON_MISC6: + return "Misc Button 6"; + default: return NULL; + } + return NULL; +} + +const char* mg_axis_get_name(mg_axis axis) { + switch (axis) { + case MG_AXIS_COUNT: + case MG_AXIS_UNKNOWN: + return "Unknown Axis"; + case MG_AXIS_LEFT_X: + return "X Axis"; + case MG_AXIS_LEFT_Y: + return "Y Axis"; + case MG_AXIS_LEFT_TRIGGER: + return "Z Axis"; + case MG_AXIS_RIGHT_X: + return "RX Axis"; + case MG_AXIS_RIGHT_Y: + return "RY Axis"; + case MG_AXIS_RIGHT_TRIGGER: + return "RZ Axis"; + case MG_AXIS_THROTTLE: + return "Throttle"; + case MG_AXIS_RUDDER: + return "Rudder"; + case MG_AXIS_WHEEL: + return "Wheel"; + case MG_AXIS_GAS: + return "Gas"; + case MG_AXIS_BRAKE: + return "Brake"; + case MG_AXIS_HAT_DPAD_LEFT_RIGHT: + return "Hat D-Pad Left-Right Axis"; + case MG_AXIS_HAT_DPAD_UP_DOWN: + return "Hat D-Pad Up-Down Axis"; + case MG_AXIS_HAT1X: + return "Hat 1 X Axis"; + case MG_AXIS_HAT1Y: + return "Hat 1 Y Axis"; + case MG_AXIS_HAT2X: + return "Hat 2 X Axis"; + case MG_AXIS_HAT2Y: + return "Hat 2 Y Axis"; + case MG_AXIS_HAT3X: + return "Hat 3 X Axis"; + case MG_AXIS_HAT3Y: + return "Hat 3 Y Axis"; + case MG_AXIS_PRESSURE: + return "Pressure Axis"; + case MG_AXIS_DISTANCE: + return "Distance Axis"; + case MG_AXIS_TILT_X: + return "Tilt X Axis"; + case MG_AXIS_TILT_Y: + return "Tilt Y Axis"; + case MG_AXIS_TOOL_WIDTH: + return "Tool Width Axis"; + case MG_AXIS_VOLUME: + return "Volume Axis"; + case MG_AXIS_PROFILE: + return "Profile Axis"; + case MG_AXIS_MISC: + return "Misc Axis"; + default: break; + } + return NULL; +} + +#endif /* MG_IMPLEMENTATION */ + +#if defined(__cplusplus) && !defined(__EMSCRIPTEN__) +} +#endif diff --git a/src/external/RGFW/deps/wayland/pointer-constraints-unstable-v1.xml b/src/external/RGFW/deps/wayland/pointer-constraints-unstable-v1.xml new file mode 100644 index 000000000..83b0a1c2c --- /dev/null +++ b/src/external/RGFW/deps/wayland/pointer-constraints-unstable-v1.xml @@ -0,0 +1,334 @@ + + + + + Copyright © 2014 Jonas Ådahl + Copyright © 2015 Red Hat Inc. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + This protocol specifies a set of interfaces used for adding constraints to + the motion of a pointer. Possible constraints include confining pointer + motions to a given region, or locking it to its current position. + + In order to constrain the pointer, a client must first bind the global + interface "wp_pointer_constraints" which, if a compositor supports pointer + constraints, is exposed by the registry. Using the bound global object, the + client uses the request that corresponds to the type of constraint it wants + to make. See wp_pointer_constraints for more details. + + Warning! The protocol described in this file is experimental and backward + incompatible changes may be made. Backward compatible changes may be added + together with the corresponding interface version bump. Backward + incompatible changes are done by bumping the version number in the protocol + and interface names and resetting the interface version. Once the protocol + is to be declared stable, the 'z' prefix and the version number in the + protocol and interface names are removed and the interface version number is + reset. + + + + + The global interface exposing pointer constraining functionality. It + exposes two requests: lock_pointer for locking the pointer to its + position, and confine_pointer for locking the pointer to a region. + + The lock_pointer and confine_pointer requests create the objects + wp_locked_pointer and wp_confined_pointer respectively, and the client can + use these objects to interact with the lock. + + For any surface, only one lock or confinement may be active across all + wl_pointer objects of the same seat. If a lock or confinement is requested + when another lock or confinement is active or requested on the same surface + and with any of the wl_pointer objects of the same seat, an + 'already_constrained' error will be raised. + + + + + These errors can be emitted in response to wp_pointer_constraints + requests. + + + + + + + These values represent different lifetime semantics. They are passed + as arguments to the factory requests to specify how the constraint + lifetimes should be managed. + + + + A oneshot pointer constraint will never reactivate once it has been + deactivated. See the corresponding deactivation event + (wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) for + details. + + + + + A persistent pointer constraint may again reactivate once it has + been deactivated. See the corresponding deactivation event + (wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) for + details. + + + + + + + Used by the client to notify the server that it will no longer use this + pointer constraints object. + + + + + + The lock_pointer request lets the client request to disable movements of + the virtual pointer (i.e. the cursor), effectively locking the pointer + to a position. This request may not take effect immediately; in the + future, when the compositor deems implementation-specific constraints + are satisfied, the pointer lock will be activated and the compositor + sends a locked event. + + The protocol provides no guarantee that the constraints are ever + satisfied, and does not require the compositor to send an error if the + constraints cannot ever be satisfied. It is thus possible to request a + lock that will never activate. + + There may not be another pointer constraint of any kind requested or + active on the surface for any of the wl_pointer objects of the seat of + the passed pointer when requesting a lock. If there is, an error will be + raised. See general pointer lock documentation for more details. + + The intersection of the region passed with this request and the input + region of the surface is used to determine where the pointer must be + in order for the lock to activate. It is up to the compositor whether to + warp the pointer or require some kind of user interaction for the lock + to activate. If the region is null the surface input region is used. + + A surface may receive pointer focus without the lock being activated. + + The request creates a new object wp_locked_pointer which is used to + interact with the lock as well as receive updates about its state. See + the the description of wp_locked_pointer for further information. + + Note that while a pointer is locked, the wl_pointer objects of the + corresponding seat will not emit any wl_pointer.motion events, but + relative motion events will still be emitted via wp_relative_pointer + objects of the same seat. wl_pointer.axis and wl_pointer.button events + are unaffected. + + + + + + + + + + + The confine_pointer request lets the client request to confine the + pointer cursor to a given region. This request may not take effect + immediately; in the future, when the compositor deems implementation- + specific constraints are satisfied, the pointer confinement will be + activated and the compositor sends a confined event. + + The intersection of the region passed with this request and the input + region of the surface is used to determine where the pointer must be + in order for the confinement to activate. It is up to the compositor + whether to warp the pointer or require some kind of user interaction for + the confinement to activate. If the region is null the surface input + region is used. + + The request will create a new object wp_confined_pointer which is used + to interact with the confinement as well as receive updates about its + state. See the the description of wp_confined_pointer for further + information. + + + + + + + + + + + + The wp_locked_pointer interface represents a locked pointer state. + + While the lock of this object is active, the wl_pointer objects of the + associated seat will not emit any wl_pointer.motion events. + + This object will send the event 'locked' when the lock is activated. + Whenever the lock is activated, it is guaranteed that the locked surface + will already have received pointer focus and that the pointer will be + within the region passed to the request creating this object. + + To unlock the pointer, send the destroy request. This will also destroy + the wp_locked_pointer object. + + If the compositor decides to unlock the pointer the unlocked event is + sent. See wp_locked_pointer.unlock for details. + + When unlocking, the compositor may warp the cursor position to the set + cursor position hint. If it does, it will not result in any relative + motion events emitted via wp_relative_pointer. + + If the surface the lock was requested on is destroyed and the lock is not + yet activated, the wp_locked_pointer object is now defunct and must be + destroyed. + + + + + Destroy the locked pointer object. If applicable, the compositor will + unlock the pointer. + + + + + + Set the cursor position hint relative to the top left corner of the + surface. + + If the client is drawing its own cursor, it should update the position + hint to the position of its own cursor. A compositor may use this + information to warp the pointer upon unlock in order to avoid pointer + jumps. + + The cursor position hint is double-buffered state, see + wl_surface.commit. + + + + + + + + Set a new region used to lock the pointer. + + The new lock region is double-buffered, see wl_surface.commit. + + For details about the lock region, see wp_locked_pointer. + + + + + + + Notification that the pointer lock of the seat's pointer is activated. + + + + + + Notification that the pointer lock of the seat's pointer is no longer + active. If this is a oneshot pointer lock (see + wp_pointer_constraints.lifetime) this object is now defunct and should + be destroyed. If this is a persistent pointer lock (see + wp_pointer_constraints.lifetime) this pointer lock may again + reactivate in the future. + + + + + + + The wp_confined_pointer interface represents a confined pointer state. + + This object will send the event 'confined' when the confinement is + activated. Whenever the confinement is activated, it is guaranteed that + the surface the pointer is confined to will already have received pointer + focus and that the pointer will be within the region passed to the request + creating this object. It is up to the compositor to decide whether this + requires some user interaction and if the pointer will warp to within the + passed region if outside. + + To unconfine the pointer, send the destroy request. This will also destroy + the wp_confined_pointer object. + + If the compositor decides to unconfine the pointer the unconfined event is + sent. The wp_confined_pointer object is at this point defunct and should + be destroyed. + + + + + Destroy the confined pointer object. If applicable, the compositor will + unconfine the pointer. + + + + + + Set a new region used to confine the pointer. + + The new confine region is double-buffered, see wl_surface.commit. + + If the confinement is active when the new confinement region is applied + and the pointer ends up outside of newly applied region, the pointer may + warped to a position within the new confinement region. If warped, a + wl_pointer.motion event will be emitted, but no + wp_relative_pointer.relative_motion event. + + The compositor may also, instead of using the new region, unconfine the + pointer. + + For details about the confine region, see wp_confined_pointer. + + + + + + + Notification that the pointer confinement of the seat's pointer is + activated. + + + + + + Notification that the pointer confinement of the seat's pointer is no + longer active. If this is a oneshot pointer confinement (see + wp_pointer_constraints.lifetime) this object is now defunct and should + be destroyed. If this is a persistent pointer confinement (see + wp_pointer_constraints.lifetime) this pointer confinement may again + reactivate in the future. + + + + + diff --git a/src/external/RGFW/deps/wayland/pointer-warp-v1.xml b/src/external/RGFW/deps/wayland/pointer-warp-v1.xml new file mode 100644 index 000000000..158dad83c --- /dev/null +++ b/src/external/RGFW/deps/wayland/pointer-warp-v1.xml @@ -0,0 +1,72 @@ + + + + Copyright © 2024 Neal Gompa + Copyright © 2024 Xaver Hugl + Copyright © 2024 Matthias Klumpp + Copyright © 2024 Vlad Zahorodnii + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + + This global interface allows applications to request the pointer to be + moved to a position relative to a wl_surface. + + Note that if the desired behavior is to constrain the pointer to an area + or lock it to a position, this protocol does not provide a reliable way + to do that. The pointer constraint and pointer lock protocols should be + used for those use cases instead. + + Warning! The protocol described in this file is currently in the testing + phase. Backward compatible changes may be added together with the + corresponding interface version bump. Backward incompatible changes can + only be done by creating a new major version of the extension. + + + + + Destroy the pointer warp manager. + + + + + + Request the compositor to move the pointer to a surface-local position. + Whether or not the compositor honors the request is implementation defined, + but it should + - honor it if the surface has pointer focus, including + when it has an implicit pointer grab + - reject it if the enter serial is incorrect + - reject it if the requested position is outside of the surface + + Note that the enter serial is valid for any surface of the client, + and does not have to be from the surface the pointer is warped to. + + + + + + + + + + diff --git a/src/external/RGFW/deps/wayland/relative-pointer-unstable-v1.xml b/src/external/RGFW/deps/wayland/relative-pointer-unstable-v1.xml new file mode 100644 index 000000000..ca6f81d12 --- /dev/null +++ b/src/external/RGFW/deps/wayland/relative-pointer-unstable-v1.xml @@ -0,0 +1,136 @@ + + + + + Copyright © 2014 Jonas Ådahl + Copyright © 2015 Red Hat Inc. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + This protocol specifies a set of interfaces used for making clients able to + receive relative pointer events not obstructed by barriers (such as the + monitor edge or other pointer barriers). + + To start receiving relative pointer events, a client must first bind the + global interface "wp_relative_pointer_manager" which, if a compositor + supports relative pointer motion events, is exposed by the registry. After + having created the relative pointer manager proxy object, the client uses + it to create the actual relative pointer object using the + "get_relative_pointer" request given a wl_pointer. The relative pointer + motion events will then, when applicable, be transmitted via the proxy of + the newly created relative pointer object. See the documentation of the + relative pointer interface for more details. + + Warning! The protocol described in this file is experimental and backward + incompatible changes may be made. Backward compatible changes may be added + together with the corresponding interface version bump. Backward + incompatible changes are done by bumping the version number in the protocol + and interface names and resetting the interface version. Once the protocol + is to be declared stable, the 'z' prefix and the version number in the + protocol and interface names are removed and the interface version number is + reset. + + + + + A global interface used for getting the relative pointer object for a + given pointer. + + + + + Used by the client to notify the server that it will no longer use this + relative pointer manager object. + + + + + + Create a relative pointer interface given a wl_pointer object. See the + wp_relative_pointer interface for more details. + + + + + + + + + A wp_relative_pointer object is an extension to the wl_pointer interface + used for emitting relative pointer events. It shares the same focus as + wl_pointer objects of the same seat and will only emit events when it has + focus. + + + + + + + + + Relative x/y pointer motion from the pointer of the seat associated with + this object. + + A relative motion is in the same dimension as regular wl_pointer motion + events, except they do not represent an absolute position. For example, + moving a pointer from (x, y) to (x', y') would have the equivalent + relative motion (x' - x, y' - y). If a pointer motion caused the + absolute pointer position to be clipped by for example the edge of the + monitor, the relative motion is unaffected by the clipping and will + represent the unclipped motion. + + This event also contains non-accelerated motion deltas. The + non-accelerated delta is, when applicable, the regular pointer motion + delta as it was before having applied motion acceleration and other + transformations such as normalization. + + Note that the non-accelerated delta does not represent 'raw' events as + they were read from some device. Pointer motion acceleration is device- + and configuration-specific and non-accelerated deltas and accelerated + deltas may have the same value on some devices. + + Relative motions are not coupled to wl_pointer.motion events, and can be + sent in combination with such events, but also independently. There may + also be scenarios where wl_pointer.motion is sent, but there is no + relative motion. The order of an absolute and relative motion event + originating from the same physical motion is not guaranteed. + + If the client needs button events or focus state, it can receive them + from a wl_pointer object of the same seat that the wp_relative_pointer + object is associated with. + + + + + + + + + + + diff --git a/src/external/RGFW/deps/wayland/xdg-decoration-unstable-v1.xml b/src/external/RGFW/deps/wayland/xdg-decoration-unstable-v1.xml new file mode 100644 index 000000000..82ca247c7 --- /dev/null +++ b/src/external/RGFW/deps/wayland/xdg-decoration-unstable-v1.xml @@ -0,0 +1,160 @@ + + + + Copyright © 2018 Simon Ser + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + + This interface allows a compositor to announce support for server-side + decorations. + + A window decoration is a set of window controls as deemed appropriate by + the party managing them, such as user interface components used to move, + resize and change a window's state. + + A client can use this protocol to request being decorated by a supporting + compositor. + + If compositor and client do not negotiate the use of a server-side + decoration using this protocol, clients continue to self-decorate as they + see fit. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + Destroy the decoration manager. This doesn't destroy objects created + with the manager. + + + + + + Create a new decoration object associated with the given toplevel. + + Creating an xdg_toplevel_decoration from an xdg_toplevel which has a + buffer attached or committed is a client error, and any attempts by a + client to attach or manipulate a buffer prior to the first + xdg_toplevel_decoration.configure event must also be treated as + errors. + + + + + + + + + The decoration object allows the compositor to toggle server-side window + decorations for a toplevel surface. The client can request to switch to + another mode. + + The xdg_toplevel_decoration object must be destroyed before its + xdg_toplevel. + + + + + + + + + + + + Switch back to a mode without any server-side decorations at the next + commit. + + + + + + These values describe window decoration modes. + + + + + + + + Set the toplevel surface decoration mode. This informs the compositor + that the client prefers the provided decoration mode. + + After requesting a decoration mode, the compositor will respond by + emitting an xdg_surface.configure event. The client should then update + its content, drawing it without decorations if the received mode is + server-side decorations. The client must also acknowledge the configure + when committing the new content (see xdg_surface.ack_configure). + + The compositor can decide not to use the client's mode and enforce a + different mode instead. + + Clients whose decoration mode depend on the xdg_toplevel state may send + a set_mode request in response to an xdg_surface.configure event and wait + for the next xdg_surface.configure event to prevent unwanted state. + Such clients are responsible for preventing configure loops and must + make sure not to send multiple successive set_mode requests with the + same decoration mode. + + If an invalid mode is supplied by the client, the invalid_mode protocol + error is raised by the compositor. + + + + + + + Unset the toplevel surface decoration mode. This informs the compositor + that the client doesn't prefer a particular decoration mode. + + This request has the same semantics as set_mode. + + + + + + The configure event configures the effective decoration mode. The + configured state should not be applied immediately. Clients must send an + ack_configure in response to this event. See xdg_surface.configure and + xdg_surface.ack_configure for details. + + A configure event can be sent at any time. The specified mode must be + obeyed by the client. + + + + + diff --git a/src/external/RGFW/deps/wayland/xdg-output-unstable-v1.xml b/src/external/RGFW/deps/wayland/xdg-output-unstable-v1.xml new file mode 100644 index 000000000..a7306e419 --- /dev/null +++ b/src/external/RGFW/deps/wayland/xdg-output-unstable-v1.xml @@ -0,0 +1,222 @@ + + + + + Copyright © 2017 Red Hat Inc. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + This protocol aims at describing outputs in a way which is more in line + with the concept of an output on desktop oriented systems. + + Some information are more specific to the concept of an output for + a desktop oriented system and may not make sense in other applications, + such as IVI systems for example. + + Typically, the global compositor space on a desktop system is made of + a contiguous or overlapping set of rectangular regions. + + The logical_position and logical_size events defined in this protocol + might provide information identical to their counterparts already + available from wl_output, in which case the information provided by this + protocol should be preferred to their equivalent in wl_output. The goal is + to move the desktop specific concepts (such as output location within the + global compositor space, etc.) out of the core wl_output protocol. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible + changes may be added together with the corresponding interface + version bump. + Backward incompatible changes are done by bumping the version + number in the protocol and interface names and resetting the + interface version. Once the protocol is to be declared stable, + the 'z' prefix and the version number in the protocol and + interface names are removed and the interface version number is + reset. + + + + + A global factory interface for xdg_output objects. + + + + + Using this request a client can tell the server that it is not + going to use the xdg_output_manager object anymore. + + Any objects already created through this instance are not affected. + + + + + + This creates a new xdg_output object for the given wl_output. + + + + + + + + + An xdg_output describes part of the compositor geometry. + + This typically corresponds to a monitor that displays part of the + compositor space. + + For objects version 3 onwards, after all xdg_output properties have been + sent (when the object is created and when properties are updated), a + wl_output.done event is sent. This allows changes to the output + properties to be seen as atomic, even if they happen via multiple events. + + + + + Using this request a client can tell the server that it is not + going to use the xdg_output object anymore. + + + + + + The position event describes the location of the wl_output within + the global compositor space. + + The logical_position event is sent after creating an xdg_output + (see xdg_output_manager.get_xdg_output) and whenever the location + of the output changes within the global compositor space. + + + + + + + + The logical_size event describes the size of the output in the + global compositor space. + + Most regular Wayland clients should not pay attention to the + logical size and would rather rely on xdg_shell interfaces. + + Some clients such as Xwayland, however, need this to configure + their surfaces in the global compositor space as the compositor + may apply a different scale from what is advertised by the output + scaling property (to achieve fractional scaling, for example). + + For example, for a wl_output mode 3840×2160 and a scale factor 2: + + - A compositor not scaling the monitor viewport in its compositing space + will advertise a logical size of 3840×2160, + + - A compositor scaling the monitor viewport with scale factor 2 will + advertise a logical size of 1920×1080, + + - A compositor scaling the monitor viewport using a fractional scale of + 1.5 will advertise a logical size of 2560×1440. + + For example, for a wl_output mode 1920×1080 and a 90 degree rotation, + the compositor will advertise a logical size of 1080x1920. + + The logical_size event is sent after creating an xdg_output + (see xdg_output_manager.get_xdg_output) and whenever the logical + size of the output changes, either as a result of a change in the + applied scale or because of a change in the corresponding output + mode(see wl_output.mode) or transform (see wl_output.transform). + + + + + + + + This event is sent after all other properties of an xdg_output + have been sent. + + This allows changes to the xdg_output properties to be seen as + atomic, even if they happen via multiple events. + + For objects version 3 onwards, this event is deprecated. Compositors + are not required to send it anymore and must send wl_output.done + instead. + + + + + + + + Many compositors will assign names to their outputs, show them to the + user, allow them to be configured by name, etc. The client may wish to + know this name as well to offer the user similar behaviors. + + The naming convention is compositor defined, but limited to + alphanumeric characters and dashes (-). Each name is unique among all + wl_output globals, but if a wl_output global is destroyed the same name + may be reused later. The names will also remain consistent across + sessions with the same hardware and software configuration. + + Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do + not assume that the name is a reflection of an underlying DRM + connector, X11 connection, etc. + + The name event is sent after creating an xdg_output (see + xdg_output_manager.get_xdg_output). This event is only sent once per + xdg_output, and the name does not change over the lifetime of the + wl_output global. + + This event is deprecated, instead clients should use wl_output.name. + Compositors must still support this event. + + + + + + + Many compositors can produce human-readable descriptions of their + outputs. The client may wish to know this description as well, to + communicate the user for various purposes. + + The description is a UTF-8 string with no convention defined for its + contents. Examples might include 'Foocorp 11" Display' or 'Virtual X11 + output via :1'. + + The description event is sent after creating an xdg_output (see + xdg_output_manager.get_xdg_output) and whenever the description + changes. The description is optional, and may not be sent at all. + + For objects of version 2 and lower, this event is only sent once per + xdg_output, and the description does not change over the lifetime of + the wl_output global. + + This event is deprecated, instead clients should use + wl_output.description. Compositors must still support this event. + + + + + + diff --git a/src/external/RGFW/deps/wayland/xdg-shell.xml b/src/external/RGFW/deps/wayland/xdg-shell.xml new file mode 100644 index 000000000..39ecf8acc --- /dev/null +++ b/src/external/RGFW/deps/wayland/xdg-shell.xml @@ -0,0 +1,1418 @@ + + + + + Copyright © 2008-2013 Kristian Høgsberg + Copyright © 2013 Rafael Antognolli + Copyright © 2013 Jasper St. Pierre + Copyright © 2010-2013 Intel Corporation + Copyright © 2015-2017 Samsung Electronics Co., Ltd + Copyright © 2015-2017 Red Hat Inc. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + + The xdg_wm_base interface is exposed as a global object enabling clients + to turn their wl_surfaces into windows in a desktop environment. It + defines the basic functionality needed for clients and the compositor to + create windows that can be dragged, resized, maximized, etc, as well as + creating transient windows such as popup menus. + + + + + + + + + + + + + + + Destroy this xdg_wm_base object. + + Destroying a bound xdg_wm_base object while there are surfaces + still alive created by this xdg_wm_base object instance is illegal + and will result in a defunct_surfaces error. + + + + + + Create a positioner object. A positioner object is used to position + surfaces relative to some parent surface. See the interface description + and xdg_surface.get_popup for details. + + + + + + + This creates an xdg_surface for the given surface. While xdg_surface + itself is not a role, the corresponding surface may only be assigned + a role extending xdg_surface, such as xdg_toplevel or xdg_popup. It is + illegal to create an xdg_surface for a wl_surface which already has an + assigned role and this will result in a role error. + + This creates an xdg_surface for the given surface. An xdg_surface is + used as basis to define a role to a given surface, such as xdg_toplevel + or xdg_popup. It also manages functionality shared between xdg_surface + based surface roles. + + See the documentation of xdg_surface for more details about what an + xdg_surface is and how it is used. + + + + + + + + A client must respond to a ping event with a pong request or + the client may be deemed unresponsive. See xdg_wm_base.ping + and xdg_wm_base.error.unresponsive. + + + + + + + The ping event asks the client if it's still alive. Pass the + serial specified in the event back to the compositor by sending + a "pong" request back with the specified serial. See xdg_wm_base.pong. + + Compositors can use this to determine if the client is still + alive. It's unspecified what will happen if the client doesn't + respond to the ping request, or in what timeframe. Clients should + try to respond in a reasonable amount of time. The “unresponsive” + error is provided for compositors that wish to disconnect unresponsive + clients. + + A compositor is free to ping in any way it wants, but a client must + always respond to any xdg_wm_base object it created. + + + + + + + + The xdg_positioner provides a collection of rules for the placement of a + child surface relative to a parent surface. Rules can be defined to ensure + the child surface remains within the visible area's borders, and to + specify how the child surface changes its position, such as sliding along + an axis, or flipping around a rectangle. These positioner-created rules are + constrained by the requirement that a child surface must intersect with or + be at least partially adjacent to its parent surface. + + See the various requests for details about possible rules. + + At the time of the request, the compositor makes a copy of the rules + specified by the xdg_positioner. Thus, after the request is complete the + xdg_positioner object can be destroyed or reused; further changes to the + object will have no effect on previous usages. + + For an xdg_positioner object to be considered complete, it must have a + non-zero size set by set_size, and a non-zero anchor rectangle set by + set_anchor_rect. Passing an incomplete xdg_positioner object when + positioning a surface raises an invalid_positioner error. + + + + + + + + + Notify the compositor that the xdg_positioner will no longer be used. + + + + + + Set the size of the surface that is to be positioned with the positioner + object. The size is in surface-local coordinates and corresponds to the + window geometry. See xdg_surface.set_window_geometry. + + If a zero or negative size is set the invalid_input error is raised. + + + + + + + + Specify the anchor rectangle within the parent surface that the child + surface will be placed relative to. The rectangle is relative to the + window geometry as defined by xdg_surface.set_window_geometry of the + parent surface. + + When the xdg_positioner object is used to position a child surface, the + anchor rectangle may not extend outside the window geometry of the + positioned child's parent surface. + + If a negative size is set the invalid_input error is raised. + + + + + + + + + + + + + + + + + + + + + + Defines the anchor point for the anchor rectangle. The specified anchor + is used derive an anchor point that the child surface will be + positioned relative to. If a corner anchor is set (e.g. 'top_left' or + 'bottom_right'), the anchor point will be at the specified corner; + otherwise, the derived anchor point will be centered on the specified + edge, or in the center of the anchor rectangle if no edge is specified. + + + + + + + + + + + + + + + + + + + Defines in what direction a surface should be positioned, relative to + the anchor point of the parent surface. If a corner gravity is + specified (e.g. 'bottom_right' or 'top_left'), then the child surface + will be placed towards the specified gravity; otherwise, the child + surface will be centered over the anchor point on any axis that had no + gravity specified. If the gravity is not in the ‘gravity’ enum, an + invalid_input error is raised. + + + + + + + The constraint adjustment value define ways the compositor will adjust + the position of the surface, if the unadjusted position would result + in the surface being partly constrained. + + Whether a surface is considered 'constrained' is left to the compositor + to determine. For example, the surface may be partly outside the + compositor's defined 'work area', thus necessitating the child surface's + position be adjusted until it is entirely inside the work area. + + The adjustments can be combined, according to a defined precedence: 1) + Flip, 2) Slide, 3) Resize. + + + + Don't alter the surface position even if it is constrained on some + axis, for example partially outside the edge of an output. + + + + + Slide the surface along the x axis until it is no longer constrained. + + First try to slide towards the direction of the gravity on the x axis + until either the edge in the opposite direction of the gravity is + unconstrained or the edge in the direction of the gravity is + constrained. + + Then try to slide towards the opposite direction of the gravity on the + x axis until either the edge in the direction of the gravity is + unconstrained or the edge in the opposite direction of the gravity is + constrained. + + + + + Slide the surface along the y axis until it is no longer constrained. + + First try to slide towards the direction of the gravity on the y axis + until either the edge in the opposite direction of the gravity is + unconstrained or the edge in the direction of the gravity is + constrained. + + Then try to slide towards the opposite direction of the gravity on the + y axis until either the edge in the direction of the gravity is + unconstrained or the edge in the opposite direction of the gravity is + constrained. + + + + + Invert the anchor and gravity on the x axis if the surface is + constrained on the x axis. For example, if the left edge of the + surface is constrained, the gravity is 'left' and the anchor is + 'left', change the gravity to 'right' and the anchor to 'right'. + + If the adjusted position also ends up being constrained, the resulting + position of the flip_x adjustment will be the one before the + adjustment. + + + + + Invert the anchor and gravity on the y axis if the surface is + constrained on the y axis. For example, if the bottom edge of the + surface is constrained, the gravity is 'bottom' and the anchor is + 'bottom', change the gravity to 'top' and the anchor to 'top'. + + The adjusted position is calculated given the original anchor + rectangle and offset, but with the new flipped anchor and gravity + values. + + If the adjusted position also ends up being constrained, the resulting + position of the flip_y adjustment will be the one before the + adjustment. + + + + + Resize the surface horizontally so that it is completely + unconstrained. + + + + + Resize the surface vertically so that it is completely unconstrained. + + + + + + + Specify how the window should be positioned if the originally intended + position caused the surface to be constrained, meaning at least + partially outside positioning boundaries set by the compositor. The + adjustment is set by constructing a bitmask describing the adjustment to + be made when the surface is constrained on that axis. + + If no bit for one axis is set, the compositor will assume that the child + surface should not change its position on that axis when constrained. + + If more than one bit for one axis is set, the order of how adjustments + are applied is specified in the corresponding adjustment descriptions. + + The default adjustment is none. + + + + + + + Specify the surface position offset relative to the position of the + anchor on the anchor rectangle and the anchor on the surface. For + example if the anchor of the anchor rectangle is at (x, y), the surface + has the gravity bottom|right, and the offset is (ox, oy), the calculated + surface position will be (x + ox, y + oy). The offset position of the + surface is the one used for constraint testing. See + set_constraint_adjustment. + + An example use case is placing a popup menu on top of a user interface + element, while aligning the user interface element of the parent surface + with some user interface element placed somewhere in the popup surface. + + + + + + + + + + When set reactive, the surface is reconstrained if the conditions used + for constraining changed, e.g. the parent window moved. + + If the conditions changed and the popup was reconstrained, an + xdg_popup.configure event is sent with updated geometry, followed by an + xdg_surface.configure event. + + + + + + Set the parent window geometry the compositor should use when + positioning the popup. The compositor may use this information to + determine the future state the popup should be constrained using. If + this doesn't match the dimension of the parent the popup is eventually + positioned against, the behavior is undefined. + + The arguments are given in the surface-local coordinate space. + + + + + + + + Set the serial of an xdg_surface.configure event this positioner will be + used in response to. The compositor may use this information together + with set_parent_size to determine what future state the popup should be + constrained using. + + + + + + + + An interface that may be implemented by a wl_surface, for + implementations that provide a desktop-style user interface. + + It provides a base set of functionality required to construct user + interface elements requiring management by the compositor, such as + toplevel windows, menus, etc. The types of functionality are split into + xdg_surface roles. + + Creating an xdg_surface does not set the role for a wl_surface. In order + to map an xdg_surface, the client must create a role-specific object + using, e.g., get_toplevel, get_popup. The wl_surface for any given + xdg_surface can have at most one role, and may not be assigned any role + not based on xdg_surface. + + A role must be assigned before any other requests are made to the + xdg_surface object. + + The client must call wl_surface.commit on the corresponding wl_surface + for the xdg_surface state to take effect. + + Creating an xdg_surface from a wl_surface which has a buffer attached or + committed is a client error, and any attempts by a client to attach or + manipulate a buffer prior to the first xdg_surface.configure call must + also be treated as errors. + + After creating a role-specific object and setting it up (e.g. by sending + the title, app ID, size constraints, parent, etc), the client must + perform an initial commit without any buffer attached. The compositor + will reply with initial wl_surface state such as + wl_surface.preferred_buffer_scale followed by an xdg_surface.configure + event. The client must acknowledge it and is then allowed to attach a + buffer to map the surface. + + Mapping an xdg_surface-based role surface is defined as making it + possible for the surface to be shown by the compositor. Note that + a mapped surface is not guaranteed to be visible once it is mapped. + + For an xdg_surface to be mapped by the compositor, the following + conditions must be met: + (1) the client has assigned an xdg_surface-based role to the surface + (2) the client has set and committed the xdg_surface state and the + role-dependent state to the surface + (3) the client has committed a buffer to the surface + + A newly-unmapped surface is considered to have met condition (1) out + of the 3 required conditions for mapping a surface if its role surface + has not been destroyed, i.e. the client must perform the initial commit + again before attaching a buffer. + + + + + + + + + + + + + + Destroy the xdg_surface object. An xdg_surface must only be destroyed + after its role object has been destroyed, otherwise + a defunct_role_object error is raised. + + + + + + This creates an xdg_toplevel object for the given xdg_surface and gives + the associated wl_surface the xdg_toplevel role. + + See the documentation of xdg_toplevel for more details about what an + xdg_toplevel is and how it is used. + + + + + + + This creates an xdg_popup object for the given xdg_surface and gives + the associated wl_surface the xdg_popup role. + + If null is passed as a parent, a parent surface must be specified using + some other protocol, before committing the initial state. + + See the documentation of xdg_popup for more details about what an + xdg_popup is and how it is used. + + + + + + + + + The window geometry of a surface is its "visible bounds" from the + user's perspective. Client-side decorations often have invisible + portions like drop-shadows which should be ignored for the + purposes of aligning, placing and constraining windows. Note that + in some situations, compositors may clip rendering to the window + geometry, so the client should avoid putting functional elements + outside of it. + + The window geometry is double-buffered state, see wl_surface.commit. + + When maintaining a position, the compositor should treat the (x, y) + coordinate of the window geometry as the top left corner of the window. + A client changing the (x, y) window geometry coordinate should in + general not alter the position of the window. + + Once the window geometry of the surface is set, it is not possible to + unset it, and it will remain the same until set_window_geometry is + called again, even if a new subsurface or buffer is attached. + + If never set, the value is the full bounds of the surface, + including any subsurfaces. This updates dynamically on every + commit. This unset is meant for extremely simple clients. + + The arguments are given in the surface-local coordinate space of + the wl_surface associated with this xdg_surface, and may extend outside + of the wl_surface itself to mark parts of the subsurface tree as part of + the window geometry. + + When applied, the effective window geometry will be the set window + geometry clamped to the bounding rectangle of the combined + geometry of the surface of the xdg_surface and the associated + subsurfaces. + + The effective geometry will not be recalculated unless a new call to + set_window_geometry is done and the new pending surface state is + subsequently applied. + + The width and height of the effective window geometry must be + greater than zero. Setting an invalid size will raise an + invalid_size error. + + + + + + + + + + When a configure event is received, if a client commits the + surface in response to the configure event, then the client + must make an ack_configure request sometime before the commit + request, passing along the serial of the configure event. + + For instance, for toplevel surfaces the compositor might use this + information to move a surface to the top left only when the client has + drawn itself for the maximized or fullscreen state. + + If the client receives multiple configure events before it + can respond to one, it only has to ack the last configure event. + Acking a configure event that was never sent raises an invalid_serial + error. + + A client is not required to commit immediately after sending + an ack_configure request - it may even ack_configure several times + before its next surface commit. + + A client may send multiple ack_configure requests before committing, but + only the last request sent before a commit indicates which configure + event the client really is responding to. + + Sending an ack_configure request consumes the serial number sent with + the request, as well as serial numbers sent by all configure events + sent on this xdg_surface prior to the configure event referenced by + the committed serial. + + It is an error to issue multiple ack_configure requests referencing a + serial from the same configure event, or to issue an ack_configure + request referencing a serial from a configure event issued before the + event identified by the last ack_configure request for the same + xdg_surface. Doing so will raise an invalid_serial error. + + + + + + + The configure event marks the end of a configure sequence. A configure + sequence is a set of one or more events configuring the state of the + xdg_surface, including the final xdg_surface.configure event. + + Where applicable, xdg_surface surface roles will during a configure + sequence extend this event as a latched state sent as events before the + xdg_surface.configure event. Such events should be considered to make up + a set of atomically applied configuration states, where the + xdg_surface.configure commits the accumulated state. + + Clients should arrange their surface for the new states, and then send + an ack_configure request with the serial sent in this configure event at + some point before committing the new surface. + + If the client receives multiple configure events before it can respond + to one, it is free to discard all but the last event it received. + + + + + + + + + This interface defines an xdg_surface role which allows a surface to, + among other things, set window-like properties such as maximize, + fullscreen, and minimize, set application-specific metadata like title and + id, and well as trigger user interactive operations such as interactive + resize and move. + + A xdg_toplevel by default is responsible for providing the full intended + visual representation of the toplevel, which depending on the window + state, may mean things like a title bar, window controls and drop shadow. + + Unmapping an xdg_toplevel means that the surface cannot be shown + by the compositor until it is explicitly mapped again. + All active operations (e.g., move, resize) are canceled and all + attributes (e.g. title, state, stacking, ...) are discarded for + an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to + the state it had right after xdg_surface.get_toplevel. The client + can re-map the toplevel by performing a commit without any buffer + attached, waiting for a configure event and handling it as usual (see + xdg_surface description). + + Attaching a null buffer to a toplevel unmaps the surface. + + + + + This request destroys the role surface and unmaps the surface; + see "Unmapping" behavior in interface section for details. + + + + + + + + + + + + Set the "parent" of this surface. This surface should be stacked + above the parent surface and all other ancestor surfaces. + + Parent surfaces should be set on dialogs, toolboxes, or other + "auxiliary" surfaces, so that the parent is raised when the dialog + is raised. + + Setting a null parent for a child surface unsets its parent. Setting + a null parent for a surface which currently has no parent is a no-op. + + Only mapped surfaces can have child surfaces. Setting a parent which + is not mapped is equivalent to setting a null parent. If a surface + becomes unmapped, its children's parent is set to the parent of + the now-unmapped surface. If the now-unmapped surface has no parent, + its children's parent is unset. If the now-unmapped surface becomes + mapped again, its parent-child relationship is not restored. + + The parent toplevel must not be one of the child toplevel's + descendants, and the parent must be different from the child toplevel, + otherwise the invalid_parent protocol error is raised. + + + + + + + Set a short title for the surface. + + This string may be used to identify the surface in a task bar, + window list, or other user interface elements provided by the + compositor. + + The string must be encoded in UTF-8. + + + + + + + Set an application identifier for the surface. + + The app ID identifies the general class of applications to which + the surface belongs. The compositor can use this to group multiple + surfaces together, or to determine how to launch a new application. + + For D-Bus activatable applications, the app ID is used as the D-Bus + service name. + + The compositor shell will try to group application surfaces together + by their app ID. As a best practice, it is suggested to select app + ID's that match the basename of the application's .desktop file. + For example, "org.freedesktop.FooViewer" where the .desktop file is + "org.freedesktop.FooViewer.desktop". + + Like other properties, a set_app_id request can be sent after the + xdg_toplevel has been mapped to update the property. + + See the desktop-entry specification [0] for more details on + application identifiers and how they relate to well-known D-Bus + names and .desktop files. + + [0] https://standards.freedesktop.org/desktop-entry-spec/ + + + + + + + Clients implementing client-side decorations might want to show + a context menu when right-clicking on the decorations, giving the + user a menu that they can use to maximize or minimize the window. + + This request asks the compositor to pop up such a window menu at + the given position, relative to the local surface coordinates of + the parent surface. There are no guarantees as to what menu items + the window menu contains, or even if a window menu will be drawn + at all. + + This request must be used in response to some sort of user action + like a button press, key press, or touch down event. + + + + + + + + + + Start an interactive, user-driven move of the surface. + + This request must be used in response to some sort of user action + like a button press, key press, or touch down event. The passed + serial is used to determine the type of interactive move (touch, + pointer, etc). + + The server may ignore move requests depending on the state of + the surface (e.g. fullscreen or maximized), or if the passed serial + is no longer valid. + + If triggered, the surface will lose the focus of the device + (wl_pointer, wl_touch, etc) used for the move. It is up to the + compositor to visually indicate that the move is taking place, such as + updating a pointer cursor, during the move. There is no guarantee + that the device focus will return when the move is completed. + + + + + + + + These values are used to indicate which edge of a surface + is being dragged in a resize operation. + + + + + + + + + + + + + + + Start a user-driven, interactive resize of the surface. + + This request must be used in response to some sort of user action + like a button press, key press, or touch down event. The passed + serial is used to determine the type of interactive resize (touch, + pointer, etc). + + The server may ignore resize requests depending on the state of + the surface (e.g. fullscreen or maximized). + + If triggered, the client will receive configure events with the + "resize" state enum value and the expected sizes. See the "resize" + enum value for more details about what is required. The client + must also acknowledge configure events using "ack_configure". After + the resize is completed, the client will receive another "configure" + event without the resize state. + + If triggered, the surface also will lose the focus of the device + (wl_pointer, wl_touch, etc) used for the resize. It is up to the + compositor to visually indicate that the resize is taking place, + such as updating a pointer cursor, during the resize. There is no + guarantee that the device focus will return when the resize is + completed. + + The edges parameter specifies how the surface should be resized, and + is one of the values of the resize_edge enum. Values not matching + a variant of the enum will cause the invalid_resize_edge protocol error. + The compositor may use this information to update the surface position + for example when dragging the top left corner. The compositor may also + use this information to adapt its behavior, e.g. choose an appropriate + cursor image. + + + + + + + + + The different state values used on the surface. This is designed for + state values like maximized, fullscreen. It is paired with the + configure event to ensure that both the client and the compositor + setting the state can be synchronized. + + States set in this way are double-buffered, see wl_surface.commit. + + + + The surface is maximized. The window geometry specified in the configure + event must be obeyed by the client, or the xdg_wm_base.invalid_surface_state + error is raised. + + The client should draw without shadow or other + decoration outside of the window geometry. + + + + + The surface is fullscreen. The window geometry specified in the + configure event is a maximum; the client cannot resize beyond it. For + a surface to cover the whole fullscreened area, the geometry + dimensions must be obeyed by the client. For more details, see + xdg_toplevel.set_fullscreen. + + + + + The surface is being resized. The window geometry specified in the + configure event is a maximum; the client cannot resize beyond it. + Clients that have aspect ratio or cell sizing configuration can use + a smaller size, however. + + + + + Client window decorations should be painted as if the window is + active. Do not assume this means that the window actually has + keyboard or pointer focus. + + + + + The window is currently in a tiled layout and the left edge is + considered to be adjacent to another part of the tiling grid. + + The client should draw without shadow or other decoration outside of + the window geometry on the left edge. + + + + + The window is currently in a tiled layout and the right edge is + considered to be adjacent to another part of the tiling grid. + + The client should draw without shadow or other decoration outside of + the window geometry on the right edge. + + + + + The window is currently in a tiled layout and the top edge is + considered to be adjacent to another part of the tiling grid. + + The client should draw without shadow or other decoration outside of + the window geometry on the top edge. + + + + + The window is currently in a tiled layout and the bottom edge is + considered to be adjacent to another part of the tiling grid. + + The client should draw without shadow or other decoration outside of + the window geometry on the bottom edge. + + + + + The surface is currently not ordinarily being repainted; for + example because its content is occluded by another window, or its + outputs are switched off due to screen locking. + + + + + The left edge of the window is currently constrained, meaning it + shouldn't attempt to resize from that edge. It can for example mean + it's tiled next to a monitor edge on the constrained side of the + window. + + + + + The right edge of the window is currently constrained, meaning it + shouldn't attempt to resize from that edge. It can for example mean + it's tiled next to a monitor edge on the constrained side of the + window. + + + + + The top edge of the window is currently constrained, meaning it + shouldn't attempt to resize from that edge. It can for example mean + it's tiled next to a monitor edge on the constrained side of the + window. + + + + + The bottom edge of the window is currently constrained, meaning it + shouldn't attempt to resize from that edge. It can for example mean + it's tiled next to a monitor edge on the constrained side of the + window. + + + + + + + Set a maximum size for the window. + + The client can specify a maximum size so that the compositor does + not try to configure the window beyond this size. + + The width and height arguments are in window geometry coordinates. + See xdg_surface.set_window_geometry. + + Values set in this way are double-buffered, see wl_surface.commit. + + The compositor can use this information to allow or disallow + different states like maximize or fullscreen and draw accurate + animations. + + Similarly, a tiling window manager may use this information to + place and resize client windows in a more effective way. + + The client should not rely on the compositor to obey the maximum + size. The compositor may decide to ignore the values set by the + client and request a larger size. + + If never set, or a value of zero in the request, means that the + client has no expected maximum size in the given dimension. + As a result, a client wishing to reset the maximum size + to an unspecified state can use zero for width and height in the + request. + + Requesting a maximum size to be smaller than the minimum size of + a surface is illegal and will result in an invalid_size error. + + The width and height must be greater than or equal to zero. Using + strictly negative values for width or height will result in a + invalid_size error. + + + + + + + + Set a minimum size for the window. + + The client can specify a minimum size so that the compositor does + not try to configure the window below this size. + + The width and height arguments are in window geometry coordinates. + See xdg_surface.set_window_geometry. + + Values set in this way are double-buffered, see wl_surface.commit. + + The compositor can use this information to allow or disallow + different states like maximize or fullscreen and draw accurate + animations. + + Similarly, a tiling window manager may use this information to + place and resize client windows in a more effective way. + + The client should not rely on the compositor to obey the minimum + size. The compositor may decide to ignore the values set by the + client and request a smaller size. + + If never set, or a value of zero in the request, means that the + client has no expected minimum size in the given dimension. + As a result, a client wishing to reset the minimum size + to an unspecified state can use zero for width and height in the + request. + + Requesting a minimum size to be larger than the maximum size of + a surface is illegal and will result in an invalid_size error. + + The width and height must be greater than or equal to zero. Using + strictly negative values for width and height will result in a + invalid_size error. + + + + + + + + Maximize the surface. + + After requesting that the surface should be maximized, the compositor + will respond by emitting a configure event. Whether this configure + actually sets the window maximized is subject to compositor policies. + The client must then update its content, drawing in the configured + state. The client must also acknowledge the configure when committing + the new content (see ack_configure). + + It is up to the compositor to decide how and where to maximize the + surface, for example which output and what region of the screen should + be used. + + If the surface was already maximized, the compositor will still emit + a configure event with the "maximized" state. + + If the surface is in a fullscreen state, this request has no direct + effect. It may alter the state the surface is returned to when + unmaximized unless overridden by the compositor. + + + + + + Unmaximize the surface. + + After requesting that the surface should be unmaximized, the compositor + will respond by emitting a configure event. Whether this actually + un-maximizes the window is subject to compositor policies. + If available and applicable, the compositor will include the window + geometry dimensions the window had prior to being maximized in the + configure event. The client must then update its content, drawing it in + the configured state. The client must also acknowledge the configure + when committing the new content (see ack_configure). + + It is up to the compositor to position the surface after it was + unmaximized; usually the position the surface had before maximizing, if + applicable. + + If the surface was already not maximized, the compositor will still + emit a configure event without the "maximized" state. + + If the surface is in a fullscreen state, this request has no direct + effect. It may alter the state the surface is returned to when + unmaximized unless overridden by the compositor. + + + + + + Make the surface fullscreen. + + After requesting that the surface should be fullscreened, the + compositor will respond by emitting a configure event. Whether the + client is actually put into a fullscreen state is subject to compositor + policies. The client must also acknowledge the configure when + committing the new content (see ack_configure). + + The output passed by the request indicates the client's preference as + to which display it should be set fullscreen on. If this value is NULL, + it's up to the compositor to choose which display will be used to map + this surface. + + If the surface doesn't cover the whole output, the compositor will + position the surface in the center of the output and compensate with + with border fill covering the rest of the output. The content of the + border fill is undefined, but should be assumed to be in some way that + attempts to blend into the surrounding area (e.g. solid black). + + If the fullscreened surface is not opaque, the compositor must make + sure that other screen content not part of the same surface tree (made + up of subsurfaces, popups or similarly coupled surfaces) are not + visible below the fullscreened surface. + + + + + + + Make the surface no longer fullscreen. + + After requesting that the surface should be unfullscreened, the + compositor will respond by emitting a configure event. + Whether this actually removes the fullscreen state of the client is + subject to compositor policies. + + Making a surface unfullscreen sets states for the surface based on the following: + * the state(s) it may have had before becoming fullscreen + * any state(s) decided by the compositor + * any state(s) requested by the client while the surface was fullscreen + + The compositor may include the previous window geometry dimensions in + the configure event, if applicable. + + The client must also acknowledge the configure when committing the new + content (see ack_configure). + + + + + + Request that the compositor minimize your surface. There is no + way to know if the surface is currently minimized, nor is there + any way to unset minimization on this surface. + + If you are looking to throttle redrawing when minimized, please + instead use the wl_surface.frame event for this, as this will + also work with live previews on windows in Alt-Tab, Expose or + similar compositor features. + + + + + + This configure event asks the client to resize its toplevel surface or + to change its state. The configured state should not be applied + immediately. See xdg_surface.configure for details. + + The width and height arguments specify a hint to the window + about how its surface should be resized in window geometry + coordinates. See set_window_geometry. + + If the width or height arguments are zero, it means the client + should decide its own window dimension. This may happen when the + compositor needs to configure the state of the surface but doesn't + have any information about any previous or expected dimension. + + The states listed in the event specify how the width/height + arguments should be interpreted, and possibly how it should be + drawn. + + Clients must send an ack_configure in response to this event. See + xdg_surface.configure and xdg_surface.ack_configure for details. + + + + + + + + + The close event is sent by the compositor when the user + wants the surface to be closed. This should be equivalent to + the user clicking the close button in client-side decorations, + if your application has any. + + This is only a request that the user intends to close the + window. The client may choose to ignore this request, or show + a dialog to ask the user to save their data, etc. + + + + + + + + The configure_bounds event may be sent prior to a xdg_toplevel.configure + event to communicate the bounds a window geometry size is recommended + to constrain to. + + The passed width and height are in surface coordinate space. If width + and height are 0, it means bounds is unknown and equivalent to as if no + configure_bounds event was ever sent for this surface. + + The bounds can for example correspond to the size of a monitor excluding + any panels or other shell components, so that a surface isn't created in + a way that it cannot fit. + + The bounds may change at any point, and in such a case, a new + xdg_toplevel.configure_bounds will be sent, followed by + xdg_toplevel.configure and xdg_surface.configure. + + + + + + + + + + + + + + + + + This event advertises the capabilities supported by the compositor. If + a capability isn't supported, clients should hide or disable the UI + elements that expose this functionality. For instance, if the + compositor doesn't advertise support for minimized toplevels, a button + triggering the set_minimized request should not be displayed. + + The compositor will ignore requests it doesn't support. For instance, + a compositor which doesn't advertise support for minimized will ignore + set_minimized requests. + + Compositors must send this event once before the first + xdg_surface.configure event. When the capabilities change, compositors + must send this event again and then send an xdg_surface.configure + event. + + The configured state should not be applied immediately. See + xdg_surface.configure for details. + + The capabilities are sent as an array of 32-bit unsigned integers in + native endianness. + + + + + + + + A popup surface is a short-lived, temporary surface. It can be used to + implement for example menus, popovers, tooltips and other similar user + interface concepts. + + A popup can be made to take an explicit grab. See xdg_popup.grab for + details. + + When the popup is dismissed, a popup_done event will be sent out, and at + the same time the surface will be unmapped. See the xdg_popup.popup_done + event for details. + + Explicitly destroying the xdg_popup object will also dismiss the popup and + unmap the surface. Clients that want to dismiss the popup when another + surface of their own is clicked should dismiss the popup using the destroy + request. + + A newly created xdg_popup will be stacked on top of all previously created + xdg_popup surfaces associated with the same xdg_toplevel. + + The parent of an xdg_popup must be mapped (see the xdg_surface + description) before the xdg_popup itself. + + The client must call wl_surface.commit on the corresponding wl_surface + for the xdg_popup state to take effect. + + + + + + + + + This destroys the popup. Explicitly destroying the xdg_popup + object will also dismiss the popup, and unmap the surface. + + If this xdg_popup is not the "topmost" popup, the + xdg_wm_base.not_the_topmost_popup protocol error will be sent. + + + + + + This request makes the created popup take an explicit grab. An explicit + grab will be dismissed when the user dismisses the popup, or when the + client destroys the xdg_popup. This can be done by the user clicking + outside the surface, using the keyboard, or even locking the screen + through closing the lid or a timeout. + + If the compositor denies the grab, the popup will be immediately + dismissed. + + This request must be used in response to some sort of user action like a + button press, key press, or touch down event. The serial number of the + event should be passed as 'serial'. + + The parent of a grabbing popup must either be an xdg_toplevel surface or + another xdg_popup with an explicit grab. If the parent is another + xdg_popup it means that the popups are nested, with this popup now being + the topmost popup. + + Nested popups must be destroyed in the reverse order they were created + in, e.g. the only popup you are allowed to destroy at all times is the + topmost one. + + When compositors choose to dismiss a popup, they may dismiss every + nested grabbing popup as well. When a compositor dismisses popups, it + will follow the same dismissing order as required from the client. + + If the topmost grabbing popup is destroyed, the grab will be returned to + the parent of the popup, if that parent previously had an explicit grab. + + If the parent is a grabbing popup which has already been dismissed, this + popup will be immediately dismissed. If the parent is a popup that did + not take an explicit grab, an error will be raised. + + During a popup grab, the client owning the grab will receive pointer + and touch events for all their surfaces as normal (similar to an + "owner-events" grab in X11 parlance), while the top most grabbing popup + will always have keyboard focus. + + + + + + + + This event asks the popup surface to configure itself given the + configuration. The configured state should not be applied immediately. + See xdg_surface.configure for details. + + The x and y arguments represent the position the popup was placed at + given the xdg_positioner rule, relative to the upper left corner of the + window geometry of the parent surface. + + For version 2 or older, the configure event for an xdg_popup is only + ever sent once for the initial configuration. Starting with version 3, + it may be sent again if the popup is setup with an xdg_positioner with + set_reactive requested, or in response to xdg_popup.reposition requests. + + + + + + + + + + The popup_done event is sent out when a popup is dismissed by the + compositor. The client should destroy the xdg_popup object at this + point. + + + + + + + + Reposition an already-mapped popup. The popup will be placed given the + details in the passed xdg_positioner object, and a + xdg_popup.repositioned followed by xdg_popup.configure and + xdg_surface.configure will be emitted in response. Any parameters set + by the previous positioner will be discarded. + + The passed token will be sent in the corresponding + xdg_popup.repositioned event. The new popup position will not take + effect until the corresponding configure event is acknowledged by the + client. See xdg_popup.repositioned for details. The token itself is + opaque, and has no other special meaning. + + If multiple reposition requests are sent, the compositor may skip all + but the last one. + + If the popup is repositioned in response to a configure event for its + parent, the client should send an xdg_positioner.set_parent_configure + and possibly an xdg_positioner.set_parent_size request to allow the + compositor to properly constrain the popup. + + If the popup is repositioned together with a parent that is being + resized, but not in response to a configure event, the client should + send an xdg_positioner.set_parent_size request. + + + + + + + + The repositioned event is sent as part of a popup configuration + sequence, together with xdg_popup.configure and lastly + xdg_surface.configure to notify the completion of a reposition request. + + The repositioned event is to notify about the completion of a + xdg_popup.reposition request. The token argument is the token passed + in the xdg_popup.reposition request. + + Immediately after this event is emitted, xdg_popup.configure and + xdg_surface.configure will be sent with the updated size and position, + as well as a new configure serial. + + The client should optionally update the content of the popup, but must + acknowledge the new popup configuration for the new position to take + effect. See xdg_surface.ack_configure for details. + + + + + + diff --git a/src/external/RGFW/deps/wayland/xdg-toplevel-icon-v1.xml b/src/external/RGFW/deps/wayland/xdg-toplevel-icon-v1.xml new file mode 100644 index 000000000..fc409fef7 --- /dev/null +++ b/src/external/RGFW/deps/wayland/xdg-toplevel-icon-v1.xml @@ -0,0 +1,205 @@ + + + + + Copyright © 2023-2024 Matthias Klumpp + Copyright © 2024 David Edmundson + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + This protocol allows clients to set icons for their toplevel surfaces + either via the XDG icon stock (using an icon name), or from pixel data. + + A toplevel icon represents the individual toplevel (unlike the application + or launcher icon, which represents the application as a whole), and may be + shown in window switchers, window overviews and taskbars that list + individual windows. + + This document adheres to RFC 2119 when using words like "must", + "should", "may", etc. + + Warning! The protocol described in this file is currently in the testing + phase. Backward compatible changes may be added together with the + corresponding interface version bump. Backward incompatible changes can + only be done by creating a new major version of the extension. + + + + + This interface allows clients to create toplevel window icons and set + them on toplevel windows to be displayed to the user. + + + + + Destroy the toplevel icon manager. + This does not destroy objects created with the manager. + + + + + + Creates a new icon object. This icon can then be attached to a + xdg_toplevel via the 'set_icon' request. + + + + + + + This request assigns the icon 'icon' to 'toplevel', or clears the + toplevel icon if 'icon' was null. + This state is double-buffered and is applied on the next + wl_surface.commit of the toplevel. + + After making this call, the xdg_toplevel_icon_v1 provided as 'icon' + can be destroyed by the client without 'toplevel' losing its icon. + The xdg_toplevel_icon_v1 is immutable from this point, and any + future attempts to change it must raise the + 'xdg_toplevel_icon_v1.immutable' protocol error. + + The compositor must set the toplevel icon from either the pixel data + the icon provides, or by loading a stock icon using the icon name. + See the description of 'xdg_toplevel_icon_v1' for details. + + If 'icon' is set to null, the icon of the respective toplevel is reset + to its default icon (usually the icon of the application, derived from + its desktop-entry file, or a placeholder icon). + If this request is passed an icon with no pixel buffers or icon name + assigned, the icon must be reset just like if 'icon' was null. + + + + + + + + This event indicates an icon size the compositor prefers to be + available if the client has scalable icons and can render to any size. + + When the 'xdg_toplevel_icon_manager_v1' object is created, the + compositor may send one or more 'icon_size' events to describe the list + of preferred icon sizes. If the compositor has no size preference, it + may not send any 'icon_size' event, and it is up to the client to + decide a suitable icon size. + + A sequence of 'icon_size' events must be finished with a 'done' event. + If the compositor has no size preferences, it must still send the + 'done' event, without any preceding 'icon_size' events. + + + + + + + This event is sent after all 'icon_size' events have been sent. + + + + + + + This interface defines a toplevel icon. + An icon can have a name, and multiple buffers. + In order to be applied, the icon must have either a name, or at least + one buffer assigned. Applying an empty icon (with no buffer or name) to + a toplevel should reset its icon to the default icon. + + It is up to compositor policy whether to prefer using a buffer or loading + an icon via its name. See 'set_name' and 'add_buffer' for details. + + + + + + + + + + + Destroys the 'xdg_toplevel_icon_v1' object. + The icon must still remain set on every toplevel it was assigned to, + until the toplevel icon is reset explicitly. + + + + + + This request assigns an icon name to this icon. + Any previously set name is overridden. + + The compositor must resolve 'icon_name' according to the lookup rules + described in the XDG icon theme specification[1] using the + environment's current icon theme. + + If the compositor does not support icon names or cannot resolve + 'icon_name' according to the XDG icon theme specification it must + fall back to using pixel buffer data instead. + + If this request is made after the icon has been assigned to a toplevel + via 'set_icon', a 'immutable' error must be raised. + + [1]: https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html + + + + + + + This request adds pixel data supplied as wl_buffer to the icon. + + The client should add pixel data for all icon sizes and scales that + it can provide, or which are explicitly requested by the compositor + via 'icon_size' events on xdg_toplevel_icon_manager_v1. + + The wl_buffer supplying pixel data as 'buffer' must be backed by wl_shm + and must be a square (width and height being equal). + If any of these buffer requirements are not fulfilled, a 'invalid_buffer' + error must be raised. + + If this icon instance already has a buffer of the same size and scale + from a previous 'add_buffer' request, data from the last request + overrides the preexisting pixel data. + + The wl_buffer must be kept alive for as long as the xdg_toplevel_icon + it is associated with is not destroyed, otherwise a 'no_buffer' error + is raised. The buffer contents must not be modified after it was + assigned to the icon. As a result, the region of the wl_shm_pool's + backing storage used for the wl_buffer must not be modified after this + request is sent. The wl_buffer.release event is unused. + + If this request is made after the icon has been assigned to a toplevel + via 'set_icon', a 'immutable' error must be raised. + + + + + + diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c old mode 100644 new mode 100755 index dbc5e0132..e04097e98 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -45,8 +45,29 @@ * **********************************************************************************************/ +#if defined(_WIN32) || defined(_WIN64) + #define BI_ALPHABITFIELDS 4 + #define LoadImage LoadImageA + + // Temporarily rename conflicting symbols + #define CloseWindow CloseWindowWin32 + #define Rectangle RectangleWin32 + #define ShowCursor ShowCursorWin32 + + #define WIN32_LEAN_AND_MEAN + #include + + // Restore for raylib/RGFW + #undef CloseWindow + #undef Rectangle + #undef ShowCursor + #undef LoadImage + + #include "../external/fix_win32_compatibility.h" +#endif + #if defined(PLATFORM_WEB_RGFW) -#define RGFW_NO_GL_HEADER + #define RGFW_NO_GL_HEADER #endif #if defined(GRAPHICS_API_OPENGL_ES2) && !defined(PLATFORM_WEB_RGFW) @@ -55,15 +76,13 @@ void ShowCursor(void); void CloseWindow(void); - -#if defined(__linux__) - #define _INPUT_EVENT_CODES_H -#endif +double get_time_seconds(void); #if defined(__unix__) || defined(__linux__) #define _XTYPEDEF_FONT #endif +#define RGFW_OPENGL #define RGFW_IMPLEMENTATION #if defined(_WIN32) || defined(_WIN64) @@ -73,8 +92,6 @@ void CloseWindow(void); #define ShowCursor __imp_ShowCursor #define _APISETSTRING_ - #undef MAX_PATH - #if defined(__cplusplus) extern "C" { #endif @@ -88,13 +105,51 @@ extern "C" { #if defined(__APPLE__) #define Point NSPOINT #define Size NSSIZE + + #ifdef GetColor + #undef GetColor + #endif + #define GetColor GetColor_osx + #ifdef EventType + #undef EventType + #endif + #define EventType EventType_osx #endif +#if defined(__APPLE__) + // older macs (unsupported?) are missing these + #include + #ifndef kHIDUsage_Button_5 + #define kHIDUsage_Button_5 0x05 + #endif + #ifndef kHIDUsage_Button_6 + #define kHIDUsage_Button_6 0x06 + #endif + #ifndef kHIDUsage_Button_7 + #define kHIDUsage_Button_7 0x07 + #endif + #ifndef kHIDUsage_Button_8 + #define kHIDUsage_Button_8 0x08 + #endif + #ifndef kHIDUsage_Button_9 + #define kHIDUsage_Button_9 0x09 + #endif + #ifndef kHIDUsage_Button_10 + #define kHIDUsage_Button_10 0x0A + #endif +#endif + +// minigamepad used for gamepad support +#define MG_MAX_GAMEPADS MAX_GAMEPADS // copy raylibs define into minigamepad +#define MG_IMPLEMENTATION +#include "../external/RGFW/deps/minigamepad.h" + #define RGFW_ALLOC RL_MALLOC #define RGFW_FREE RL_FREE #define RGFW_CALLOC RL_CALLOC +#define RGFW_INT_DEFINED 1 // to avoid issues with minigamepad+RGFW definitions -#include "../external/RGFW.h" +#include "../external/RGFW/RGFW.h" #if defined(_WIN32) || defined(_WIN64) #undef DrawText @@ -102,7 +157,9 @@ extern "C" { #undef CloseWindow #undef Rectangle - #undef MAX_PATH + #ifdef MAX_PATH + #undef MAX_PATH + #endif #define MAX_PATH 1025 #endif @@ -112,14 +169,15 @@ extern "C" { #endif #include -#include // Required for: strcmp() //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- typedef struct { + double startTime; RGFW_window *window; // Native display device (physical screen connection) - RGFW_monitor mon; + RGFW_monitor *monitor; + mg_gamepads minigamepad; } PlatformData; //---------------------------------------------------------------------------------- @@ -129,30 +187,125 @@ extern CoreData CORE; // Global CORE state context static PlatformData platform = { 0 }; // Platform specific -static bool RGFW_disableCursor = false; +#if defined(__linux__) // prevent collision of raylibs KEY_ and linux/input.h KEY_ + #undef KEY_NULL + // Alphanumeric keys + #undef KEY_APOSTROPHE + #undef KEY_COMMA + #undef KEY_MINUS + #undef KEY_PERIOD + #undef KEY_SLASH + #undef KEY_ZERO + #undef KEY_ONE + #undef KEY_TWO + #undef KEY_THREE + #undef KEY_FOUR + #undef KEY_FIVE + #undef KEY_SIX + #undef KEY_SEVEN + #undef KEY_EIGHT + #undef KEY_NINE + #undef KEY_SEMICOLON + #undef KEY_EQUAL + #undef KEY_A + #undef KEY_B + #undef KEY_C + #undef KEY_D + #undef KEY_E + #undef KEY_F + #undef KEY_G + #undef KEY_H + #undef KEY_I + #undef KEY_J + #undef KEY_K + #undef KEY_L + #undef KEY_M + #undef KEY_N + #undef KEY_O + #undef KEY_P + #undef KEY_Q + #undef KEY_R + #undef KEY_S + #undef KEY_T + #undef KEY_U + #undef KEY_V + #undef KEY_W + #undef KEY_X + #undef KEY_Y + #undef KEY_Z + #undef KEY_LEFT_BRACKET + #undef KEY_BACKSLASH + #undef KEY_RIGHT_BRACKET + #undef KEY_GRAVE + // Function keys + #undef KEY_SPACE + #undef KEY_ESCAPE + #undef KEY_ENTER + #undef KEY_TAB + #undef KEY_BACKSPACE + #undef KEY_INSERT + #undef KEY_DELETE + #undef KEY_RIGHT + #undef KEY_LEFT + #undef KEY_DOWN + #undef KEY_UP + #undef KEY_PAGE_UP + #undef KEY_PAGE_DOWN + #undef KEY_HOME + #undef KEY_END + #undef KEY_CAPS_LOCK + #undef KEY_SCROLL_LOCK + #undef KEY_NUM_LOCK + #undef KEY_PRINT_SCREEN + #undef KEY_PAUSE + #undef KEY_F1 + #undef KEY_F2 + #undef KEY_F3 + #undef KEY_F4 + #undef KEY_F5 + #undef KEY_F6 + #undef KEY_F7 + #undef KEY_F8 + #undef KEY_F9 + #undef KEY_F10 + #undef KEY_F11 + #undef KEY_F12 + #undef KEY_LEFT_SHIFT + #undef KEY_LEFT_CONTROL + #undef KEY_LEFT_ALT + #undef KEY_LEFT_SUPER + #undef KEY_RIGHT_SHIFT + #undef KEY_RIGHT_CONTROL + #undef KEY_RIGHT_ALT + #undef KEY_RIGHT_SUPER + #undef KEY_KB_MENU + // Keypad keys + #undef KEY_KP_0 + #undef KEY_KP_1 + #undef KEY_KP_2 + #undef KEY_KP_3 + #undef KEY_KP_4 + #undef KEY_KP_5 + #undef KEY_KP_6 + #undef KEY_KP_7 + #undef KEY_KP_8 + #undef KEY_KP_9 + #undef KEY_KP_DECIMAL + #undef KEY_KP_DIVIDE + #undef KEY_KP_MULTIPLY + #undef KEY_KP_SUBTRACT + #undef KEY_KP_ADD + #undef KEY_KP_ENTER + #undef KEY_KP_EQUAL +#endif -static const unsigned short keyMappingRGFW[] = { +static const unsigned short RGFW_keyConvertTable[] = { [RGFW_keyNULL] = KEY_NULL, - [RGFW_return] = KEY_ENTER, [RGFW_apostrophe] = KEY_APOSTROPHE, [RGFW_comma] = KEY_COMMA, [RGFW_minus] = KEY_MINUS, [RGFW_period] = KEY_PERIOD, [RGFW_slash] = KEY_SLASH, - [RGFW_escape] = KEY_ESCAPE, - [RGFW_F1] = KEY_F1, - [RGFW_F2] = KEY_F2, - [RGFW_F3] = KEY_F3, - [RGFW_F4] = KEY_F4, - [RGFW_F5] = KEY_F5, - [RGFW_F6] = KEY_F6, - [RGFW_F7] = KEY_F7, - [RGFW_F8] = KEY_F8, - [RGFW_F9] = KEY_F9, - [RGFW_F10] = KEY_F10, - [RGFW_F11] = KEY_F11, - [RGFW_F12] = KEY_F12, - [RGFW_backtick] = KEY_GRAVE, [RGFW_0] = KEY_ZERO, [RGFW_1] = KEY_ONE, [RGFW_2] = KEY_TWO, @@ -163,22 +316,8 @@ static const unsigned short keyMappingRGFW[] = { [RGFW_7] = KEY_SEVEN, [RGFW_8] = KEY_EIGHT, [RGFW_9] = KEY_NINE, + [RGFW_semicolon] = KEY_SEMICOLON, [RGFW_equals] = KEY_EQUAL, - [RGFW_backSpace] = KEY_BACKSPACE, - [RGFW_tab] = KEY_TAB, - [RGFW_capsLock] = KEY_CAPS_LOCK, - [RGFW_shiftL] = KEY_LEFT_SHIFT, - [RGFW_controlL] = KEY_LEFT_CONTROL, - [RGFW_altL] = KEY_LEFT_ALT, - [RGFW_superL] = KEY_LEFT_SUPER, - #ifndef RGFW_MACOS - [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, - [RGFW_a] = KEY_A, [RGFW_b] = KEY_B, [RGFW_c] = KEY_C, @@ -208,54 +347,104 @@ static const unsigned short keyMappingRGFW[] = { [RGFW_bracket] = KEY_LEFT_BRACKET, [RGFW_backSlash] = KEY_BACKSLASH, [RGFW_closeBracket] = KEY_RIGHT_BRACKET, - [RGFW_semicolon] = KEY_SEMICOLON, + [RGFW_backtick] = KEY_GRAVE, + [RGFW_space] = KEY_SPACE, + [RGFW_escape] = KEY_ESCAPE, + [RGFW_return] = KEY_ENTER, + [RGFW_tab] = KEY_TAB, + [RGFW_backSpace] = KEY_BACKSPACE, [RGFW_insert] = KEY_INSERT, - [RGFW_home] = KEY_HOME, - [RGFW_pageUp] = KEY_PAGE_UP, [RGFW_delete] = KEY_DELETE, - [RGFW_end] = KEY_END, - [RGFW_pageDown] = KEY_PAGE_DOWN, [RGFW_right] = KEY_RIGHT, [RGFW_left] = KEY_LEFT, [RGFW_down] = KEY_DOWN, [RGFW_up] = KEY_UP, - [RGFW_numLock] = KEY_NUM_LOCK, - [RGFW_KP_Slash] = KEY_KP_DIVIDE, - [RGFW_multiply] = KEY_KP_MULTIPLY, - [RGFW_KP_Minus] = KEY_KP_SUBTRACT, - [RGFW_KP_Return] = KEY_KP_ENTER, - [RGFW_KP_1] = KEY_KP_1, - [RGFW_KP_2] = KEY_KP_2, - [RGFW_KP_3] = KEY_KP_3, - [RGFW_KP_4] = KEY_KP_4, - [RGFW_KP_5] = KEY_KP_5, - [RGFW_KP_6] = KEY_KP_6, - [RGFW_KP_7] = KEY_KP_7, - [RGFW_KP_8] = KEY_KP_8, - [RGFW_KP_9] = KEY_KP_9, - [RGFW_KP_0] = KEY_KP_0, - [RGFW_KP_Period] = KEY_KP_DECIMAL, + [RGFW_pageUp] = KEY_PAGE_UP, + [RGFW_pageDown] = KEY_PAGE_DOWN, + [RGFW_home] = KEY_HOME, + [RGFW_end] = KEY_END, + [RGFW_capsLock] = KEY_CAPS_LOCK, [RGFW_scrollLock] = KEY_SCROLL_LOCK, + [RGFW_numLock] = KEY_NUM_LOCK, + [RGFW_printScreen] = KEY_PRINT_SCREEN, + [RGFW_pause] = KEY_PAUSE, + [RGFW_F1] = KEY_F1, + [RGFW_F2] = KEY_F2, + [RGFW_F3] = KEY_F3, + [RGFW_F4] = KEY_F4, + [RGFW_F5] = KEY_F5, + [RGFW_F6] = KEY_F6, + [RGFW_F7] = KEY_F7, + [RGFW_F8] = KEY_F8, + [RGFW_F9] = KEY_F9, + [RGFW_F10] = KEY_F10, + [RGFW_F11] = KEY_F11, + [RGFW_F12] = KEY_F12, + [RGFW_shiftL] = KEY_LEFT_SHIFT, + [RGFW_controlL] = KEY_LEFT_CONTROL, + [RGFW_altL] = KEY_LEFT_ALT, + [RGFW_superL] = KEY_LEFT_SUPER, + // #ifndef RGFW_MACOS + [RGFW_shiftR] = KEY_RIGHT_SHIFT, + [RGFW_controlR] = KEY_RIGHT_CONTROL, + [RGFW_altR] = KEY_RIGHT_ALT, + [RGFW_superR] = KEY_RIGHT_SUPER, + // #endif + [RGFW_menu] = KEY_KB_MENU, + [RGFW_kp0] = KEY_KP_0, + [RGFW_kp1] = KEY_KP_1, + [RGFW_kp2] = KEY_KP_2, + [RGFW_kp3] = KEY_KP_3, + [RGFW_kp4] = KEY_KP_4, + [RGFW_kp5] = KEY_KP_5, + [RGFW_kp6] = KEY_KP_6, + [RGFW_kp7] = KEY_KP_7, + [RGFW_kp8] = KEY_KP_8, + [RGFW_kp9] = KEY_KP_9, + [RGFW_kpPeriod] = KEY_KP_DECIMAL, + [RGFW_kpSlash] = KEY_KP_DIVIDE, + [RGFW_kpMultiply] = KEY_KP_MULTIPLY, + [RGFW_kpMinus] = KEY_KP_SUBTRACT, + [RGFW_kpPlus] = KEY_KP_ADD, + [RGFW_kpReturn] = KEY_KP_ENTER, + [RGFW_kpEqual] = KEY_KP_EQUAL, }; -static int RGFW_gpConvTable[18] = { - [RGFW_gamepadY] = GAMEPAD_BUTTON_RIGHT_FACE_UP, - [RGFW_gamepadB] = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, - [RGFW_gamepadA] = GAMEPAD_BUTTON_RIGHT_FACE_DOWN, - [RGFW_gamepadX] = GAMEPAD_BUTTON_RIGHT_FACE_LEFT, - [RGFW_gamepadL1] = GAMEPAD_BUTTON_LEFT_TRIGGER_1, - [RGFW_gamepadR1] = GAMEPAD_BUTTON_RIGHT_TRIGGER_1, - [RGFW_gamepadL2] = GAMEPAD_BUTTON_LEFT_TRIGGER_2, - [RGFW_gamepadR2] = GAMEPAD_BUTTON_RIGHT_TRIGGER_2, - [RGFW_gamepadSelect] = GAMEPAD_BUTTON_MIDDLE_LEFT, - [RGFW_gamepadHome] = GAMEPAD_BUTTON_MIDDLE, - [RGFW_gamepadStart] = GAMEPAD_BUTTON_MIDDLE_RIGHT, - [RGFW_gamepadUp] = GAMEPAD_BUTTON_LEFT_FACE_UP, - [RGFW_gamepadRight] = GAMEPAD_BUTTON_LEFT_FACE_RIGHT, - [RGFW_gamepadDown] = GAMEPAD_BUTTON_LEFT_FACE_DOWN, - [RGFW_gamepadLeft] = GAMEPAD_BUTTON_LEFT_FACE_LEFT, - [RGFW_gamepadL3] = GAMEPAD_BUTTON_LEFT_THUMB, - [RGFW_gamepadR3] = GAMEPAD_BUTTON_RIGHT_THUMB, +static int mg_buttonConvertTable[] = { + [MG_BUTTON_NORTH] = GAMEPAD_BUTTON_RIGHT_FACE_UP, + [MG_BUTTON_EAST] = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, + [MG_BUTTON_SOUTH] = GAMEPAD_BUTTON_RIGHT_FACE_DOWN, + [MG_BUTTON_WEST] = GAMEPAD_BUTTON_RIGHT_FACE_LEFT, + [MG_BUTTON_LEFT_SHOULDER] = GAMEPAD_BUTTON_LEFT_TRIGGER_1, + [MG_BUTTON_RIGHT_SHOULDER] = GAMEPAD_BUTTON_RIGHT_TRIGGER_1, + [MG_BUTTON_LEFT_TRIGGER] = GAMEPAD_BUTTON_LEFT_TRIGGER_2, + [MG_BUTTON_RIGHT_TRIGGER] = GAMEPAD_BUTTON_RIGHT_TRIGGER_2, + [MG_BUTTON_BACK] = GAMEPAD_BUTTON_MIDDLE_LEFT, + [MG_BUTTON_GUIDE] = GAMEPAD_BUTTON_MIDDLE, + [MG_BUTTON_START] = GAMEPAD_BUTTON_MIDDLE_RIGHT, + [MG_BUTTON_DPAD_UP] = GAMEPAD_BUTTON_LEFT_FACE_UP, + [MG_BUTTON_DPAD_RIGHT] = GAMEPAD_BUTTON_LEFT_FACE_RIGHT, + [MG_BUTTON_DPAD_DOWN] = GAMEPAD_BUTTON_LEFT_FACE_DOWN, + [MG_BUTTON_DPAD_LEFT] = GAMEPAD_BUTTON_LEFT_FACE_LEFT, + [MG_BUTTON_LEFT_STICK] = GAMEPAD_BUTTON_LEFT_THUMB, + [MG_BUTTON_RIGHT_STICK] = GAMEPAD_BUTTON_RIGHT_THUMB, +}; + +static int mg_axisConvertTable[] = { + [MG_AXIS_LEFT_X] = GAMEPAD_AXIS_LEFT_X, + [MG_AXIS_LEFT_Y] = GAMEPAD_AXIS_LEFT_Y, + [MG_AXIS_RIGHT_X] = GAMEPAD_AXIS_RIGHT_X, + [MG_AXIS_RIGHT_Y] = GAMEPAD_AXIS_RIGHT_Y, + [MG_AXIS_LEFT_TRIGGER] = GAMEPAD_AXIS_LEFT_TRIGGER, + [MG_AXIS_RIGHT_TRIGGER] = GAMEPAD_AXIS_RIGHT_TRIGGER, + + /* unsupported in raylib */ + [MG_AXIS_HAT_DPAD_LEFT_RIGHT] = -1, + [MG_AXIS_HAT_DPAD_LEFT] = -1, + [MG_AXIS_HAT_DPAD_RIGHT] = -1, + [MG_AXIS_HAT_DPAD_UP_DOWN] = -1, + [MG_AXIS_HAT_DPAD_UP] = -1, + [MG_AXIS_HAT_DPAD_DOWN] = -1, }; //---------------------------------------------------------------------------------- @@ -289,61 +478,67 @@ void ToggleFullscreen(void) { if (!FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) { + FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE); // Store previous window position (in case we exit fullscreen) - CORE.Window.previousPosition = CORE.Window.position; + Vector2 currentPosition = GetWindowPosition(); + CORE.Window.previousPosition.x = currentPosition.x; + CORE.Window.previousPosition.y = currentPosition.y; CORE.Window.previousScreen = CORE.Window.screen; - platform.mon = RGFW_window_getMonitor(platform.window); - FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE); - - RGFW_monitor_scaleToWindow(platform.mon, platform.window); + RGFW_monitor* currentMonitor = RGFW_window_getMonitor(platform.window); + RGFW_monitor_scaleToWindow(currentMonitor, platform.window); RGFW_window_setFullscreen(platform.window, 1); } else { FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); - if (platform.mon.mode.area.w) - { - RGFW_monitor monitor = RGFW_window_getMonitor(platform.window); - RGFW_monitor_requestMode(monitor, platform.mon.mode, RGFW_monitorScale); - - platform.mon.mode.area.w = 0; - } - // we update the window position right away CORE.Window.position = CORE.Window.previousPosition; RGFW_window_setFullscreen(platform.window, 0); - RGFW_window_move(platform.window, RGFW_POINT(CORE.Window.position.x, CORE.Window.position.y)); - RGFW_window_resize(platform.window, RGFW_AREA(CORE.Window.previousScreen.width, CORE.Window.previousScreen.height)); + RGFW_window_move(platform.window, CORE.Window.position.x, CORE.Window.position.y); + RGFW_window_resize(platform.window, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); } // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) // NOTE: V-Sync can be enabled by graphic driver configuration - if (FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT)) RGFW_window_swapInterval(platform.window, 1); + if (FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT)) RGFW_window_swapInterval_OpenGL(platform.window, 1); } // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) ToggleFullscreen(); - - if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE)) + if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) { - CORE.Window.previousPosition = CORE.Window.position; + ToggleFullscreen(); + + // it seems like returning here is a more desireable outcome + return; + } + + if (!FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE)) + { + FLAG_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE); + + Vector2 currentPosition = GetWindowPosition(); + CORE.Window.previousPosition.x = (int)currentPosition.x; + CORE.Window.previousPosition.y = (int)currentPosition.y; CORE.Window.previousScreen = CORE.Window.screen; + RGFW_monitor *currentMonitor = RGFW_window_getMonitor(platform.window); RGFW_window_setBorder(platform.window, 0); - - RGFW_monitor mon = RGFW_window_getMonitor(platform.window); - RGFW_window_resize(platform.window, mon.mode.area); + RGFW_window_move(platform.window, 0, 0); + RGFW_window_resize(platform.window, currentMonitor->mode.w, currentMonitor->mode.h); } else { + FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE); RGFW_window_setBorder(platform.window, 1); - + CORE.Window.position = CORE.Window.previousPosition; - RGFW_window_resize(platform.window, RGFW_AREA(CORE.Window.previousScreen.width, CORE.Window.previousScreen.height)); + + RGFW_window_resize(platform.window, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); + RGFW_window_move(platform.window, CORE.Window.position.x, CORE.Window.position.y); } } @@ -376,7 +571,7 @@ void SetWindowState(unsigned int flags) if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT)) { - RGFW_window_swapInterval(platform.window, 1); + RGFW_window_swapInterval_OpenGL(platform.window, 1); } if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE)) { @@ -384,8 +579,8 @@ void SetWindowState(unsigned int flags) } if (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)) { - RGFW_window_setMaxSize(platform.window, RGFW_AREA(0, 0)); - RGFW_window_setMinSize(platform.window, RGFW_AREA(0, 0)); + RGFW_window_setMaxSize(platform.window, 0, 0); + RGFW_window_setMinSize(platform.window, 0, 0); } if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED)) { @@ -406,8 +601,8 @@ void SetWindowState(unsigned int flags) if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED)) { FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); - FLAG_CLEAR(platform.window->_flags, RGFW_windowFocusOnShow); - RGFW_window_setFlags(platform.window, platform.window->_flags); + FLAG_CLEAR(platform.window->internal.flags, RGFW_windowFocusOnShow); + RGFW_window_setFlags(platform.window, platform.window->internal.flags); } if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST)) { @@ -435,7 +630,9 @@ void SetWindowState(unsigned int flags) } if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT)) { - RGFW_setGLHint(RGFW_glSamples, 4); + RGFW_glHints* hints = RGFW_getGlobalHints_OpenGL(); + hints->samples = 4; + RGFW_setGlobalHints_OpenGL(hints); } if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT)) { @@ -450,7 +647,7 @@ void ClearWindowState(unsigned int flags) if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT)) { - RGFW_window_swapInterval(platform.window, 0); + RGFW_window_swapInterval_OpenGL(platform.window, 0); } if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE)) { @@ -458,8 +655,8 @@ void ClearWindowState(unsigned int flags) } if (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)) { - RGFW_window_setMaxSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h)); - RGFW_window_setMinSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h)); + RGFW_window_setMaxSize(platform.window, platform.window->w, platform.window->h); + RGFW_window_setMinSize(platform.window, platform.window->w, platform.window->h); } if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED)) { @@ -485,7 +682,7 @@ void ClearWindowState(unsigned int flags) } if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED)) { - RGFW_window_setFlags(platform.window, platform.window->_flags | RGFW_windowFocusOnShow); + RGFW_window_setFlags(platform.window, platform.window->internal.flags | RGFW_windowFocusOnShow); } if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST)) { @@ -509,7 +706,9 @@ void ClearWindowState(unsigned int flags) } if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT)) { - RGFW_setGLHint(RGFW_glSamples, 0); + RGFW_glHints* hints = RGFW_getGlobalHints_OpenGL(); + hints->samples = 0; + RGFW_setGlobalHints_OpenGL(hints); } if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT)) { @@ -525,7 +724,7 @@ void SetWindowIcon(Image image) TRACELOG(LOG_WARNING, "RGFW: Window icon image must be in R8G8B8A8 pixel format"); return; } - RGFW_window_setIcon(platform.window, (u8 *)image.data, RGFW_AREA(image.width, image.height), 4); + RGFW_window_setIcon(platform.window, (u8 *)image.data, image.width, image.height, 4); } // Set icon for window @@ -533,7 +732,7 @@ void SetWindowIcons(Image *images, int count) { if ((images == NULL) || (count <= 0)) { - RGFW_window_setIcon(platform.window, NULL, RGFW_AREA(0, 0), 0); + RGFW_window_setIcon(platform.window, NULL, 0, 0, 0); } else { @@ -551,8 +750,8 @@ void SetWindowIcons(Image *images, int count) if ((smallIcon == NULL) || ((images[i].width < smallIcon->width) && (images[i].height > smallIcon->height))) smallIcon = &images[i]; } - if (smallIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)smallIcon->data, RGFW_AREA(smallIcon->width, smallIcon->height), 4, RGFW_iconWindow); - if (bigIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)bigIcon->data, RGFW_AREA(bigIcon->width, bigIcon->height), 4, RGFW_iconTaskbar); + if (smallIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)smallIcon->data, smallIcon->width, smallIcon->height, 4, RGFW_iconWindow); + if (bigIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)bigIcon->data, bigIcon->width, bigIcon->height, 4, RGFW_iconTaskbar); } } @@ -566,7 +765,7 @@ void SetWindowTitle(const char *title) // Set window position on screen (windowed mode) void SetWindowPosition(int x, int y) { - RGFW_window_move(platform.window, RGFW_POINT(x, y)); + RGFW_window_move(platform.window, x, y); } // Set monitor for the current window @@ -578,7 +777,7 @@ void SetWindowMonitor(int monitor) // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMinSize(int width, int height) { - RGFW_window_setMinSize(platform.window, RGFW_AREA(width, height)); + RGFW_window_setMinSize(platform.window, width, height); CORE.Window.screenMin.width = width; CORE.Window.screenMin.height = height; } @@ -586,7 +785,7 @@ void SetWindowMinSize(int width, int height) // Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMaxSize(int width, int height) { - RGFW_window_setMaxSize(platform.window, RGFW_AREA(width, height)); + RGFW_window_setMaxSize(platform.window, width, height); CORE.Window.screenMax.width = width; CORE.Window.screenMax.height = height; } @@ -594,10 +793,36 @@ void SetWindowMaxSize(int width, int height) // Set window dimensions void SetWindowSize(int width, int height) { - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) + { + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; - RGFW_window_resize(platform.window, RGFW_AREA(width, height)); + Vector2 scaleDpi = GetWindowScaleDPI(); + + #if defined(__APPLE__) + RGFW_monitor* currentMonitor = RGFW_window_getMonitor(platform.window); + CORE.Window.screenScale = MatrixScale(currentMonitor->pixelRatio, currentMonitor->pixelRatio, 1.0f); + + CORE.Window.render.width = CORE.Window.screen.width * currentMonitor->pixelRatio; + CORE.Window.render.height = CORE.Window.screen.height * currentMonitor->pixelRatio; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + #else + SetMouseScale(1.0f/scaleDpi.x, 1.0f/scaleDpi.y); + CORE.Window.screenScale = MatrixScale(scaleDpi.x, scaleDpi.y, 1.0f); + #endif + + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + } + else + { + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + } + + RGFW_window_resize(platform.window, CORE.Window.screen.width, CORE.Window.screen.height); } // Set window opacity, value opacity is between 0.0 and 1.0 @@ -616,8 +841,11 @@ void SetWindowFocused(void) void *GetWindowHandle(void) { if (platform.window == NULL) return NULL; -#ifdef RGFW_WASM - return (void *)platform.window->src.ctx; + +#if defined(RGFW_WASM) + return (void *)&platform.window->src.ctx; +#elif defined(RGFW_WAYLAND) + return (void *)platform.window->src.surface; #else return (void *)platform.window->src.window; #endif @@ -626,19 +854,8 @@ void *GetWindowHandle(void) // Get number of monitors int GetMonitorCount(void) { - #define MAX_MONITORS_SUPPORTED 6 - - int count = MAX_MONITORS_SUPPORTED; - RGFW_monitor *mons = RGFW_getMonitors(NULL); - - for (int i = 0; i < 6; i++) - { - if (!mons[i].x && !mons[i].y && !mons[i].mode.area.w && mons[i].mode.area.h) - { - count = i; - break; - } - } + size_t count = 0; + RGFW_getMonitors(&count); return count; } @@ -646,15 +863,15 @@ int GetMonitorCount(void) // Get current monitor where window is placed int GetCurrentMonitor(void) { - RGFW_monitor *mons = RGFW_getMonitors(NULL); - RGFW_monitor mon = { 0 }; + RGFW_monitor **mons = RGFW_getMonitors(NULL); + RGFW_monitor *mon = NULL; if (platform.window) mon = RGFW_window_getMonitor(platform.window); else mon = RGFW_getPrimaryMonitor(); for (int i = 0; i < 6; i++) { - if ((mons[i].x == mon.x) && (mons[i].y == mon.y)) return i; + if ((mons[i]->x == mon->x) && (mons[i]->y == mon->y)) return i; } return 0; @@ -663,81 +880,109 @@ int GetCurrentMonitor(void) // Get selected monitor position Vector2 GetMonitorPosition(int monitor) { - RGFW_monitor *mons = RGFW_getMonitors(NULL); + RGFW_monitor **mons = RGFW_getMonitors(NULL); - return (Vector2){ (float)mons[monitor].x, (float)mons[monitor].y }; + return (Vector2){ (float)mons[monitor]->x, (float)mons[monitor]->y }; } // Get selected monitor width (currently used by monitor) int GetMonitorWidth(int monitor) { - RGFW_monitor *mons = RGFW_getMonitors(NULL); + RGFW_monitor **mons = RGFW_getMonitors(NULL); - return mons[monitor].mode.area.w; + return mons[monitor]->mode.w; } // Get selected monitor height (currently used by monitor) int GetMonitorHeight(int monitor) { - RGFW_monitor *mons = RGFW_getMonitors(NULL); + RGFW_monitor **mons = RGFW_getMonitors(NULL); - return mons[monitor].mode.area.h; + return mons[monitor]->mode.h; } // Get selected monitor physical width in millimetres int GetMonitorPhysicalWidth(int monitor) { - RGFW_monitor *mons = RGFW_getMonitors(NULL); + RGFW_monitor **mons = RGFW_getMonitors(NULL); - return mons[monitor].physW; + return mons[monitor]->physW; } // Get selected monitor physical height in millimetres int GetMonitorPhysicalHeight(int monitor) { - RGFW_monitor *mons = RGFW_getMonitors(NULL); + RGFW_monitor **mons = RGFW_getMonitors(NULL); - return (int)mons[monitor].physH; + return (int)mons[monitor]->physH; } // Get selected monitor refresh rate int GetMonitorRefreshRate(int monitor) { - RGFW_monitor *mons = RGFW_getMonitors(NULL); + RGFW_monitor **mons = RGFW_getMonitors(NULL); - return (int)mons[monitor].mode.refreshRate; + return (int)mons[monitor]->mode.refreshRate; } // Get the human-readable, UTF-8 encoded name of the selected monitor const char *GetMonitorName(int monitor) { - RGFW_monitor *mons = RGFW_getMonitors(NULL); + RGFW_monitor **mons = RGFW_getMonitors(NULL); - return mons[monitor].name; + return mons[monitor]->name; } // Get window position XY on monitor Vector2 GetWindowPosition(void) { - if (platform.window == NULL) return (Vector2){ 0.0f, 0.0f }; - return (Vector2){ (float)platform.window->r.x, (float)platform.window->r.y }; + if (platform.window == NULL) + { + return (Vector2){ 0.0f, 0.0f }; + } + + if (RGFW_window_getPosition(platform.window, &platform.window->x, &platform.window->y)) { + return (Vector2){ (float)platform.window->x, (float)platform.window->y }; + } + + return (Vector2){ 0.0f, 0.0f }; } // Get window scale DPI factor for current monitor Vector2 GetWindowScaleDPI(void) { - RGFW_monitor monitor = { 0 }; + RGFW_monitor *monitor = NULL; if (platform.window) monitor = RGFW_window_getMonitor(platform.window); else monitor = RGFW_getPrimaryMonitor(); - return (Vector2){ monitor.scaleX, monitor.scaleX }; + #if defined(__APPLE__) + // apple does < 1.0f scaling, example: 0.66f, 0.5f + // we want to convert this to be consistent + return (Vector2){ 1.0f / monitor->scaleX, 1.0f / monitor->scaleX }; + #else + // linux and windows do >= 1.0f scaling, example: 1.0f, 1.25f, 2.0f + return (Vector2){ monitor->scaleX, monitor->scaleX }; + #endif +} + +// Not part of raylib. Mac has a different pixel ratio for retina displays +// and we want to be able to handle it +float GetMonitorPixelRatio(void) +{ + RGFW_monitor *monitor = NULL; + + if (platform.window) monitor = RGFW_window_getMonitor(platform.window); + else monitor = RGFW_getPrimaryMonitor(); + + return monitor->pixelRatio; } // Set clipboard text content void SetClipboardText(const char *text) { - RGFW_writeClipboard(text, strlen(text)); + // add 1 for null terminator + RGFW_writeClipboard(text, strlen(text)+1); } // Get clipboard text content @@ -761,11 +1006,11 @@ const char *GetClipboardText(void) Image GetClipboardImage(void) { Image image = { 0 }; - unsigned long long int dataSize = 0; - void *fileData = NULL; - #if defined(SUPPORT_CLIPBOARD_IMAGE) #if defined(_WIN32) + unsigned long long int dataSize = 0; // moved into _WIN32 scope until other platforms gain support + void *fileData = NULL; // moved into _WIN32 scope until other platforms gain support + int width = 0; int height = 0; fileData = (void *)Win32GetClipboardImageData(&width, &height, &dataSize); @@ -797,8 +1042,7 @@ void HideCursor(void) // Enables cursor (unlock cursor) void EnableCursor(void) { - RGFW_disableCursor = false; - RGFW_window_mouseUnhold(platform.window); + RGFW_window_captureMouse(platform.window, false); // Set cursor position in the middle SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); @@ -810,8 +1054,7 @@ void EnableCursor(void) // Disables cursor (lock cursor) void DisableCursor(void) { - RGFW_disableCursor = true; - RGFW_window_mouseHold(platform.window, RGFW_AREA(0, 0)); + RGFW_window_captureMouse(platform.window, true); HideCursor(); CORE.Input.Mouse.cursorLocked = true; @@ -820,7 +1063,7 @@ void DisableCursor(void) // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { - RGFW_window_swapBuffers(platform.window); + RGFW_window_swapBuffers_OpenGL(platform.window); } //---------------------------------------------------------------------------------- @@ -830,7 +1073,7 @@ void SwapScreenBuffer(void) // Get elapsed time measure in seconds since InitTimer() double GetTime(void) { - return RGFW_getTime(); + return get_time_seconds() - platform.startTime; } // Open URL with default system browser (if available) @@ -865,8 +1108,7 @@ void OpenURL(const char *url) // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { - TRACELOG(LOG_WARNING, "SetGamepadMappings() unsupported on target platform"); - return 0; + return mg_update_gamepad_mappings(&platform.minigamepad, mappings); } // Set gamepad vibration @@ -878,7 +1120,7 @@ void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float d // Set mouse position XY void SetMousePosition(int x, int y) { - RGFW_window_moveMouse(platform.window, RGFW_POINT(x, y)); + RGFW_window_moveMouse(platform.window, x, y); CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; } @@ -917,7 +1159,7 @@ void PollInputEvents(void) // Register previous mouse position // Reset last gamepad button/axis registered state - for (int i = 0; (i < 4) && (i < MAX_GAMEPADS); i++) + for (int i = 0; i < MAX_GAMEPADS; i++) { // Check if gamepad is available if (CORE.Input.Gamepad.ready[i]) @@ -955,7 +1197,7 @@ void PollInputEvents(void) CORE.Window.resizedLastFrame = false; CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; - if (FLAG_IS_SET(platform.window->_flags, RGFW_HOLD_MOUSE)) + if (RGFW_window_isCaptured(platform.window)) { CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f }; CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f }; @@ -967,26 +1209,23 @@ void PollInputEvents(void) if ((CORE.Window.eventWaiting) || (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN))) { - RGFW_window_eventWait(platform.window, -1); // Wait for input events: keyboard/mouse/window events (callbacks) -> Update keys state CORE.Time.previous = GetTime(); } - while (RGFW_window_checkEvent(platform.window)) + RGFW_event rgfw_event; + while (RGFW_window_checkEvent(platform.window, &rgfw_event)) { - RGFW_event *event = &platform.window->event; // All input events can be processed after polling - - switch (event->type) + switch (rgfw_event.type) { case RGFW_mouseEnter: CORE.Input.Mouse.cursorOnScreen = true; break; case RGFW_mouseLeave: CORE.Input.Mouse.cursorOnScreen = false; break; case RGFW_quit: - event->type = 0; - CORE.Window.shouldClose = true; + RGFW_window_setShouldClose(platform.window, true); return; - case RGFW_DND: // Dropped file + case RGFW_dataDrop: // Dropped file { - for (int i = 0; i < event->droppedFilesCount; i++) + for (int i = 0; i < rgfw_event.drop.count; i++) { if (CORE.Window.dropFileCount == 0) { @@ -996,14 +1235,14 @@ void PollInputEvents(void) CORE.Window.dropFilepaths = (char **)RL_CALLOC(1024, sizeof(char *)); CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); - strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event->droppedFiles[i]); + strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], rgfw_event.drop.files[i]); CORE.Window.dropFileCount++; } else if (CORE.Window.dropFileCount < 1024) { CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); - strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event->droppedFiles[i]); + strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], rgfw_event.drop.files[i]); CORE.Window.dropFileCount++; } @@ -1014,22 +1253,53 @@ void PollInputEvents(void) // Window events are also polled (Minimized, maximized, close...) case RGFW_windowResized: { - SetupViewport(platform.window->r.w, platform.window->r.h); + #if defined(__APPLE__) + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) + { + RGFW_monitor* currentMonitor = RGFW_window_getMonitor(platform.window); + SetupViewport(platform.window->w * currentMonitor->pixelRatio, platform.window->h * currentMonitor->pixelRatio); + CORE.Window.screenScale = MatrixScale(currentMonitor->pixelRatio, currentMonitor->pixelRatio, 1.0f); - // if we are doing automatic DPI scaling, then the "screen" size is divided by the window scale - if (IsWindowState(FLAG_WINDOW_HIGHDPI)) - { - CORE.Window.screen.width = (int)(platform.window->r.w/GetWindowScaleDPI().x); - CORE.Window.screen.height = (int)(platform.window->r.h/GetWindowScaleDPI().y); - } - else - { - CORE.Window.screen.width = platform.window->r.w; - CORE.Window.screen.height = platform.window->r.h; - } + CORE.Window.screen.width = platform.window->w; + CORE.Window.screen.height = platform.window->h; + CORE.Window.render.width = CORE.Window.screen.width * currentMonitor->pixelRatio; + CORE.Window.render.height = CORE.Window.screen.height * currentMonitor->pixelRatio; + } + else + { + SetupViewport(platform.window->w, platform.window->h); + CORE.Window.screen.width = platform.window->w; + CORE.Window.screen.height = platform.window->h; + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + } - CORE.Window.currentFbo.width = platform.window->r.w; - CORE.Window.currentFbo.height = platform.window->r.h; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + #elif defined(PLATFORM_WEB_RGFW) + // do nothing for web + return; + #else + SetupViewport(platform.window->w, platform.window->h); + // if we are doing automatic DPI scaling, then the "screen" size is divided by the window scale + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) + { + Vector2 scaleDpi = GetWindowScaleDPI(); + CORE.Window.screen.width = (int)(platform.window->w/scaleDpi.x); + CORE.Window.screen.height = (int)(platform.window->h/scaleDpi.y); + CORE.Window.screenScale = MatrixScale(scaleDpi.x, scaleDpi.y, 1.0f); + // mouse scale doesnt seem needed + // SetMouseScale(1.0f/scaleDpi.x, 1.0f/scaleDpi.y); + } + else + { + CORE.Window.screen.width = platform.window->w; + CORE.Window.screen.height = platform.window->h; + } + + CORE.Window.currentFbo.width = CORE.Window.screen.width; + CORE.Window.currentFbo.height = CORE.Window.screen.height; + #endif CORE.Window.resizedLastFrame = true; } break; case RGFW_windowMaximized: @@ -1049,14 +1319,14 @@ void PollInputEvents(void) } break; case RGFW_windowMoved: { - CORE.Window.position.x = platform.window->r.x; - CORE.Window.position.y = platform.window->r.x; + CORE.Window.position.x = platform.window->x; + CORE.Window.position.y = platform.window->x; } break; // Keyboard events case RGFW_keyPressed: { - KeyboardKey key = ConvertScancodeToKey(event->key); + KeyboardKey key = ConvertScancodeToKey(rgfw_event.key.value); if (key != KEY_NULL) { // If key was up, add it to the key pressed queue @@ -1067,36 +1337,37 @@ void PollInputEvents(void) } CORE.Input.Keyboard.currentKeyState[key] = 1; + + if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey]) RGFW_window_setShouldClose(platform.window, true); } + } break; + case RGFW_keyReleased: + { + KeyboardKey key = ConvertScancodeToKey(rgfw_event.key.value); + if (key != KEY_NULL) CORE.Input.Keyboard.currentKeyState[key] = 0; + } break; - if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey]) CORE.Window.shouldClose = true; - + case RGFW_keyChar: + { // NOTE: event.text.text data comes an UTF-8 text sequence but we register codepoints (int) // Check if there is space available in the queue if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) { // Add character (codepoint) to the queue - CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = event->keyChar; + CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = rgfw_event.keyChar.value; CORE.Input.Keyboard.charPressedQueueCount++; } } break; - case RGFW_keyReleased: - { - KeyboardKey key = ConvertScancodeToKey(event->key); - if (key != KEY_NULL) CORE.Input.Keyboard.currentKeyState[key] = 0; - } break; // Check mouse events + case RGFW_mouseScroll: + { + CORE.Input.Mouse.currentWheelMove.x += rgfw_event.scroll.x; + CORE.Input.Mouse.currentWheelMove.y += rgfw_event.scroll.y; + } break; case RGFW_mouseButtonPressed: { - if ((event->button == RGFW_mouseScrollUp) || (event->button == RGFW_mouseScrollDown)) - { - CORE.Input.Mouse.currentWheelMove.y = event->scroll; - break; - } - else CORE.Input.Mouse.currentWheelMove.y = 0; - - int btn = event->button; + int btn = rgfw_event.button.value; if (btn == RGFW_mouseLeft) btn = 1; else if (btn == RGFW_mouseRight) btn = 2; else if (btn == RGFW_mouseMiddle) btn = 3; @@ -1108,14 +1379,7 @@ void PollInputEvents(void) } break; case RGFW_mouseButtonReleased: { - if ((event->button == RGFW_mouseScrollUp) || (event->button == RGFW_mouseScrollDown)) - { - CORE.Input.Mouse.currentWheelMove.y = event->scroll; - break; - } - else CORE.Input.Mouse.currentWheelMove.y = 0; - - int btn = event->button; + int btn = rgfw_event.button.value; if (btn == RGFW_mouseLeft) btn = 1; else if (btn == RGFW_mouseRight) btn = 2; else if (btn == RGFW_mouseMiddle) btn = 3; @@ -1127,83 +1391,20 @@ void PollInputEvents(void) } break; case RGFW_mousePosChanged: { - if (FLAG_IS_SET(platform.window->_flags, RGFW_HOLD_MOUSE)) + if (RGFW_window_isCaptured(platform.window)) { - CORE.Input.Mouse.currentPosition.x += (float)event->vector.x; - CORE.Input.Mouse.currentPosition.y += (float)event->vector.y; + CORE.Input.Mouse.currentPosition.x += (float)rgfw_event.mouse.vecX; + CORE.Input.Mouse.currentPosition.y += (float)rgfw_event.mouse.vecY; } else { - CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; - CORE.Input.Mouse.currentPosition.x = (float)event->point.x; - CORE.Input.Mouse.currentPosition.y = (float)event->point.y; + CORE.Input.Mouse.currentPosition.x = (float)rgfw_event.mouse.x; + CORE.Input.Mouse.currentPosition.y = (float)rgfw_event.mouse.y; } CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; touchAction = 2; } break; - case RGFW_gamepadConnected: - { - CORE.Input.Gamepad.ready[platform.window->event.gamepad] = true; - CORE.Input.Gamepad.axisCount[platform.window->event.gamepad] = platform.window->event.axisesCount; - CORE.Input.Gamepad.axisState[platform.window->event.gamepad][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f; - CORE.Input.Gamepad.axisState[platform.window->event.gamepad][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f; - - strcpy(CORE.Input.Gamepad.name[platform.window->event.gamepad], RGFW_getGamepadName(platform.window, platform.window->event.gamepad)); - } break; - case RGFW_gamepadDisconnected: - { - CORE.Input.Gamepad.ready[platform.window->event.gamepad] = false; - } break; - case RGFW_gamepadButtonPressed: - { - int button = RGFW_gpConvTable[event->button]; - - if (button >= 0) - { - CORE.Input.Gamepad.currentButtonState[event->gamepad][button] = 1; - CORE.Input.Gamepad.lastButtonPressed = button; - } - } break; - case RGFW_gamepadButtonReleased: - { - int button = RGFW_gpConvTable[event->button]; - - CORE.Input.Gamepad.currentButtonState[event->gamepad][button] = 0; - if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0; - } break; - case RGFW_gamepadAxisMove: - { - int axis = -1; - float value = 0; - - switch(event->whichAxis) - { - case 0: - { - CORE.Input.Gamepad.axisState[event->gamepad][GAMEPAD_AXIS_LEFT_X] = event->axis[0].x/100.0f; - CORE.Input.Gamepad.axisState[event->gamepad][GAMEPAD_AXIS_LEFT_Y] = event->axis[0].y/100.0f; - } break; - case 1: - { - CORE.Input.Gamepad.axisState[event->gamepad][GAMEPAD_AXIS_RIGHT_X] = event->axis[1].x/100.0f; - CORE.Input.Gamepad.axisState[event->gamepad][GAMEPAD_AXIS_RIGHT_Y] = event->axis[1].y/100.0f; - } break; - case 2: axis = GAMEPAD_AXIS_LEFT_TRIGGER; - case 3: - { - if (axis == -1) axis = GAMEPAD_AXIS_RIGHT_TRIGGER; - - int button = (axis == GAMEPAD_AXIS_LEFT_TRIGGER)? GAMEPAD_BUTTON_LEFT_TRIGGER_2 : GAMEPAD_BUTTON_RIGHT_TRIGGER_2; - int pressed = (value > 0.1f); - CORE.Input.Gamepad.currentButtonState[event->gamepad][button] = pressed; - - if (pressed) CORE.Input.Gamepad.lastButtonPressed = button; - else if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0; - } - default: break; - } - } break; default: break; } @@ -1238,6 +1439,76 @@ void PollInputEvents(void) #endif } //----------------------------------------------------------------------------- + + mg_event gamepad_event; + while (mg_gamepads_check_event(&platform.minigamepad, &gamepad_event)) { + int gamepadIndex = gamepad_event.gamepad->index; + switch (gamepad_event.type) { + case MG_EVENT_BUTTON_PRESS: + { + int button = mg_buttonConvertTable[gamepad_event.button]; + if (button >= 0) + { + CORE.Input.Gamepad.currentButtonState[gamepadIndex][button] = 1; + CORE.Input.Gamepad.lastButtonPressed = button; + } + } break; + case MG_EVENT_BUTTON_RELEASE: + { + int button = mg_buttonConvertTable[gamepad_event.button]; + if (button >= 0) + { + CORE.Input.Gamepad.currentButtonState[gamepadIndex][button] = 0; + if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0; + } + } break; + case MG_EVENT_AXIS_MOVE: + { + int axis = mg_axisConvertTable[gamepad_event.axis]; + + switch (axis) { + case GAMEPAD_AXIS_LEFT_X: + case GAMEPAD_AXIS_LEFT_Y: + case GAMEPAD_AXIS_RIGHT_X: + case GAMEPAD_AXIS_RIGHT_Y: + CORE.Input.Gamepad.axisState[gamepadIndex][axis] = platform.minigamepad.gamepads[gamepadIndex].axes[gamepad_event.axis].value; + break; + case GAMEPAD_AXIS_LEFT_TRIGGER: + case GAMEPAD_AXIS_RIGHT_TRIGGER: + CORE.Input.Gamepad.axisState[gamepadIndex][axis] = platform.minigamepad.gamepads[gamepadIndex].axes[gamepad_event.axis].value; + + /* trigger button press when axis is all the way */ + int button = (axis == GAMEPAD_AXIS_LEFT_TRIGGER) ? GAMEPAD_BUTTON_LEFT_TRIGGER_2 : GAMEPAD_BUTTON_RIGHT_TRIGGER_2; + int pressed = (platform.minigamepad.gamepads[gamepadIndex].axes[gamepad_event.axis].value >= 1.0f); + + CORE.Input.Gamepad.currentButtonState[gamepadIndex][button] = pressed; + if (pressed) CORE.Input.Gamepad.lastButtonPressed = button; + else if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0; + break; + } + } break; + case MG_EVENT_GAMEPAD_CONNECT: + CORE.Input.Gamepad.ready[gamepadIndex] = true; + CORE.Input.Gamepad.axisState[gamepadIndex][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f; + CORE.Input.Gamepad.axisState[gamepadIndex][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f; + + int axisCount = 0; + for (int i = 0; i < MG_AXIS_COUNT; i += 1) { + if (platform.minigamepad.gamepads[gamepadIndex].axes[i].supported) { + axisCount += 1; + } else { + break; + } + } + CORE.Input.Gamepad.axisCount[gamepadIndex] = axisCount; + strcpy(CORE.Input.Gamepad.name[gamepadIndex], platform.minigamepad.gamepads[gamepadIndex].name); + break; + case MG_EVENT_GAMEPAD_DISCONNECT: + CORE.Input.Gamepad.ready[gamepadIndex] = false; + break; + default: break; + } + } } //---------------------------------------------------------------------------------- @@ -1270,46 +1541,51 @@ int InitPlatform(void) // NOTE: Some OpenGL context attributes must be set before window creation // Check selection OpenGL version + + RGFW_glHints* hints = RGFW_getGlobalHints_OpenGL(); if (rlGetVersion() == RL_OPENGL_21) { - RGFW_setGLHint(RGFW_glMajor, 2); - RGFW_setGLHint(RGFW_glMinor, 1); + hints->major = 2; + hints->minor = 1; } else if (rlGetVersion() == RL_OPENGL_33) { - RGFW_setGLHint(RGFW_glMajor, 3); - RGFW_setGLHint(RGFW_glMinor, 3); + hints->major = 3; + hints->minor = 3; } else if (rlGetVersion() == RL_OPENGL_43) { - RGFW_setGLHint(RGFW_glMajor, 4); - RGFW_setGLHint(RGFW_glMinor, 3); + hints->major = 4; + hints->minor = 3; } - if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) RGFW_setGLHint(RGFW_glSamples, 4); + if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) hints->samples = 4; if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) FLAG_SET(flags, RGFW_windowFocusOnShow | RGFW_windowFocus); - platform.window = RGFW_createWindow((CORE.Window.title != 0)? 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) + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) { - // 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; + #if !defined(__APPLE__) + CORE.Window.screen.width = CORE.Window.screen.width * GetWindowScaleDPI().x; + CORE.Window.screen.height = CORE.Window.screen.height * GetWindowScaleDPI().y; + #endif } + RGFW_setGlobalHints_OpenGL(hints); + platform.window = RGFW_createWindow((CORE.Window.title != 0)? CORE.Window.title : " ", 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, flags | RGFW_windowOpenGL); + platform.startTime = get_time_seconds(); + #ifndef PLATFORM_WEB_RGFW - RGFW_area screenSize = RGFW_getScreenSize(); - CORE.Window.display.width = screenSize.w; - CORE.Window.display.height = screenSize.h; + i32 screenSizeWidth; + i32 screenSizeHeight; + RGFW_window_getSize(platform.window, &screenSizeWidth, &screenSizeHeight); + CORE.Window.display.width = screenSizeWidth; + CORE.Window.display.height = screenSizeHeight; #else CORE.Window.display.width = CORE.Window.screen.width; CORE.Window.display.height = CORE.Window.screen.height; #endif - if (FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT)) RGFW_window_swapInterval(platform.window, 1); - RGFW_window_makeCurrent(platform.window); + if (FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT)) RGFW_window_swapInterval_OpenGL(platform.window, 1); // Check surface and context activation if (platform.window != NULL) @@ -1326,16 +1602,42 @@ int InitPlatform(void) TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); return -1; } + + // NOTE: RGFW's exit key is distinct from raylib's exit key and + // must be set to NULL to not interfere + RGFW_window_setExitKey(platform.window, RGFW_keyNULL); + RGFW_window_makeCurrentWindow_OpenGL(platform.window); + //---------------------------------------------------------------------------- // If everything work as expected, we can continue - CORE.Window.position.x = platform.window->r.x; - CORE.Window.position.y = platform.window->r.y; + CORE.Window.position.x = platform.window->x; + CORE.Window.position.y = platform.window->y; CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; CORE.Window.currentFbo.width = CORE.Window.render.width; CORE.Window.currentFbo.height = CORE.Window.render.height; + // adjust scale if using highdpi + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) { + Vector2 scaleDpi = GetWindowScaleDPI(); + + #if defined(__APPLE__) + RGFW_monitor* currentMonitor = RGFW_window_getMonitor(platform.window); + CORE.Window.screenScale = MatrixScale(currentMonitor->pixelRatio, currentMonitor->pixelRatio, 1.0f); + + CORE.Window.render.width = CORE.Window.screen.width * currentMonitor->pixelRatio; + CORE.Window.render.height = CORE.Window.screen.height * currentMonitor->pixelRatio; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + #else + SetMouseScale(1.0f/scaleDpi.x, 1.0f/scaleDpi.y); + CORE.Window.screenScale = MatrixScale(scaleDpi.x, scaleDpi.y, 1.0f); + CORE.Window.screen.width /= scaleDpi.x; + CORE.Window.screen.height /= scaleDpi.y; + #endif + } + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); @@ -1345,7 +1647,7 @@ int InitPlatform(void) // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions //---------------------------------------------------------------------------- - rlLoadExtensions((void *)RGFW_getProcAddress); + rlLoadExtensions((void *)RGFW_getProcAddress_OpenGL); //---------------------------------------------------------------------------- // Initialize timing system @@ -1355,11 +1657,17 @@ int InitPlatform(void) // Initialize storage system //---------------------------------------------------------------------------- + #if defined(__APPLE__) + // mac defaults to the users home folder for some reason + // this is done to help them read relative paths to the binary + ChangeDirectory(GetApplicationDirectory()); + #endif + CORE.Storage.basePath = GetWorkingDirectory(); //---------------------------------------------------------------------------- #if defined(RGFW_WAYLAND) - if (RGFW_useWaylandBool) TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Wayland): Initialized successfully"); + if (RGFW_usingWayland()) TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Wayland): Initialized successfully"); else TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11 (fallback)): Initialized successfully"); #elif defined(RGFW_X11) #if defined(__APPLE__) @@ -1375,6 +1683,8 @@ int InitPlatform(void) TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - MacOS): Initialized successfully"); #endif + mg_gamepads_init(&platform.minigamepad); + return 0; } @@ -1387,8 +1697,47 @@ void ClosePlatform(void) // Keycode mapping static KeyboardKey ConvertScancodeToKey(u32 keycode) { - if (keycode > sizeof(keyMappingRGFW)/sizeof(unsigned short)) return KEY_NULL; + if (keycode > sizeof(RGFW_keyConvertTable)/sizeof(unsigned short)) return KEY_NULL; - return (KeyboardKey)keyMappingRGFW[keycode]; + return (KeyboardKey)RGFW_keyConvertTable[keycode]; } +// Helper functions for Time +double get_time_seconds(void) +{ + double currentTime = 0.0; + + #if defined(_WIN32) + static LARGE_INTEGER freq = { 0 }; + static int freq_init = 0; + LARGE_INTEGER counter; + if (!freq_init) { + QueryPerformanceFrequency(&freq); + freq_init = 1; + } + QueryPerformanceCounter(&counter); + currentTime = (double)counter.QuadPart / (double)freq.QuadPart; + #elif defined(__EMSCRIPTEN__) + currentTime = emscripten_get_now() / 1000.0; + #elif defined(__APPLE__) + static mach_timebase_info_data_t tb; + static int tb_initialized = 0; + + if (!tb_initialized) { + mach_timebase_info(&tb); + tb_initialized = 1; + } + uint64_t ticks = mach_absolute_time(); + + currentTime = (double)ticks * (double)tb.numer / (double)tb.denom / 1e9; + #elif defined(__linux__) + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + currentTime = (double)ts.tv_sec + (double)ts.tv_nsec / 1e9; + #else + // fallback to cstd + currentTime = (double)clock() / (double)CLOCKS_PER_SEC; + #endif + + return currentTime; +} From 7ba604eb69d140dccdfc8de62857b28512c147bd Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Feb 2026 23:08:41 +0100 Subject: [PATCH 062/319] REXM: RENAME: `models_animation_playing` --> `models_loading_iqm` --- examples/Makefile | 2 +- examples/Makefile.Web | 4 ++-- examples/README.md | 2 +- examples/examples_list.txt | 2 +- ...imation_playing.c => models_loading_iqm.c} | 21 ++++++++---------- ...ion_playing.png => models_loading_iqm.png} | Bin ...ing.vcxproj => models_loading_iqm.vcxproj} | 6 ++--- projects/VS2022/raylib.sln | 2 +- 8 files changed, 18 insertions(+), 21 deletions(-) rename examples/models/{models_animation_playing.c => models_loading_iqm.c} (88%) rename examples/models/{models_animation_playing.png => models_loading_iqm.png} (100%) rename projects/VS2022/examples/{models_animation_playing.vcxproj => models_loading_iqm.vcxproj} (99%) diff --git a/examples/Makefile b/examples/Makefile index 40906ebfc..b7c2cee81 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -660,7 +660,7 @@ MODELS = \ models/models_animation_blending \ models/models_animation_bone_blending \ models/models_animation_gpu_skinning \ - models/models_animation_playing \ + models/models_loading_iqm \ models/models_basic_voxel \ models/models_billboard_rendering \ models/models_bone_socket \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index b91f59267..ce5ee4784 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -645,7 +645,7 @@ MODELS = \ models/models_animation_blending \ models/models_animation_bone_blending \ models/models_animation_gpu_skinning \ - models/models_animation_playing \ + models/models_loading_iqm \ models/models_basic_voxel \ models/models_billboard_rendering \ models/models_bone_socket \ @@ -1217,7 +1217,7 @@ models/models_animation_gpu_skinning: models/models_animation_gpu_skinning.c --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_animation_playing: models/models_animation_playing.c +models/models_loading_iqm: models/models_loading_iqm.c $(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 \ diff --git a/examples/README.md b/examples/README.md index 2b8b9dd98..cfd7442ca 100644 --- a/examples/README.md +++ b/examples/README.md @@ -188,7 +188,7 @@ Examples using raylib models functionality, including models loading/generation | example | image | difficulty
level | version
created | last version
updated | original
developer | |-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| -| [models_animation_playing](models/models_animation_playing.c) | models_animation_playing | ⭐⭐☆☆ | 2.5 | 3.5 | [Culacant](https://github.com/culacant) | +| [models_loading_iqm](models/models_loading_iqm.c) | models_loading_iqm | ⭐⭐☆☆ | 2.5 | 3.5 | [Culacant](https://github.com/culacant) | | [models_billboard_rendering](models/models_billboard_rendering.c) | models_billboard_rendering | ⭐⭐⭐☆ | 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_rendering](models/models_cubicmap_rendering.c) | models_cubicmap_rendering | ⭐⭐☆☆ | 1.8 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) | diff --git a/examples/examples_list.txt b/examples/examples_list.txt index 85b997ec4..981636ed8 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -144,7 +144,7 @@ text;text_codepoints_loading;★★★☆;4.2;4.2;2022;2025;"Ramon Santamaria";@ text;text_inline_styling;★★★☆;5.6-dev;5.6-dev;2025;2025;"Wagner Barongello";@SultansOfCode text;text_words_alignment;★☆☆☆;5.6-dev;5.6-dev;2025;2025;"JP Mortiboys";@themushroompirates text;text_strings_management;★★★☆;5.6-dev;5.6-dev;2025;2025;"David Buzatto";@davidbuzatto -models;models_animation_playing;★★☆☆;2.5;3.5;2019;2025;"Culacant";@culacant +models;models_loading_iqm;★★☆☆;2.5;3.5;2019;2025;"Culacant";@culacant models;models_billboard_rendering;★★★☆;1.3;3.5;2015;2025;"Ramon Santamaria";@raysan5 models;models_box_collisions;★☆☆☆;1.3;3.5;2015;2025;"Ramon Santamaria";@raysan5 models;models_cubicmap_rendering;★★☆☆;1.8;3.5;2015;2025;"Ramon Santamaria";@raysan5 diff --git a/examples/models/models_animation_playing.c b/examples/models/models_loading_iqm.c similarity index 88% rename from examples/models/models_animation_playing.c rename to examples/models/models_loading_iqm.c index fd708b7dd..97d97ad40 100644 --- a/examples/models/models_animation_playing.c +++ b/examples/models/models_loading_iqm.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [models] example - animation playing +* raylib [models] example - loading iqm * * Example complexity rating: [★★☆☆] 2/4 * @@ -33,7 +33,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [models] example - animation playing"); + InitWindow(screenWidth, screenHeight, "raylib [models] example - loading iqm"); // Define the camera to look into our 3d world Camera camera = { 0 }; @@ -52,7 +52,7 @@ int main(void) // Load animation data int animsCount = 0; ModelAnimation *anims = LoadModelAnimations("resources/models/iqm/guyanim.iqm", &animsCount); - int animFrameCounter = 0; + float animFrameCounter = 0; DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second @@ -63,15 +63,12 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera, CAMERA_FIRST_PERSON); + UpdateCamera(&camera, CAMERA_ORBITAL); // Play animation when spacebar is held down - if (IsKeyDown(KEY_SPACE)) - { - animFrameCounter++; - UpdateModelAnimation(model, anims[0], animFrameCounter); - if (animFrameCounter >= anims[0].frameCount) animFrameCounter = 0; - } + animFrameCounter += 1.0f; + UpdateModelAnimation(model, anims[0], animFrameCounter); + if (animFrameCounter >= anims[0].keyframeCount) animFrameCounter = 0; //---------------------------------------------------------------------------------- // Draw @@ -84,9 +81,9 @@ int main(void) DrawModelEx(model, position, (Vector3){ 1.0f, 0.0f, 0.0f }, -90.0f, (Vector3){ 1.0f, 1.0f, 1.0f }, WHITE); - for (int i = 0; i < model.boneCount; i++) + for (int i = 0; i < model.skeleton.boneCount; i++) { - DrawCube(anims[0].framePoses[animFrameCounter][i].translation, 0.2f, 0.2f, 0.2f, RED); + //DrawCube(anims[0].keyframePoses[animFrameCounter][i].translation, 0.2f, 0.2f, 0.2f, RED); } DrawGrid(10, 1.0f); // Draw a grid diff --git a/examples/models/models_animation_playing.png b/examples/models/models_loading_iqm.png similarity index 100% rename from examples/models/models_animation_playing.png rename to examples/models/models_loading_iqm.png diff --git a/projects/VS2022/examples/models_animation_playing.vcxproj b/projects/VS2022/examples/models_loading_iqm.vcxproj similarity index 99% rename from projects/VS2022/examples/models_animation_playing.vcxproj rename to projects/VS2022/examples/models_loading_iqm.vcxproj index 292c45fb6..b5364fc37 100644 --- a/projects/VS2022/examples/models_animation_playing.vcxproj +++ b/projects/VS2022/examples/models_loading_iqm.vcxproj @@ -53,9 +53,9 @@ {AFDDE100-2D36-4749-817D-12E54C56312F} Win32Proj - models_animation_playing + models_loading_iqm 10.0 - models_animation_playing + models_loading_iqm @@ -553,7 +553,7 @@ - + diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 873db235b..938f23bf4 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -83,7 +83,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_window_letterbox", "ex EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_world_screen", "examples\core_world_screen.vcxproj", "{79417CE2-FEEB-42F0-BC53-62D5267B19B1}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_playing", "examples\models_animation_playing.vcxproj", "{AFDDE100-2D36-4749-817D-12E54C56312F}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_loading_iqm", "examples\models_loading_iqm.vcxproj", "{AFDDE100-2D36-4749-817D-12E54C56312F}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_billboard_rendering", "examples\models_billboard_rendering.vcxproj", "{B7812167-50FB-4934-996F-DF6FE4CBBFDF}" EndProject From cd17ed1d09700b064160b15dbb5cb4a8ab04c690 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Feb 2026 23:09:23 +0100 Subject: [PATCH 063/319] Update models_animation_blending.c --- examples/models/models_animation_blending.c | 49 +++++++-------------- 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/examples/models/models_animation_blending.c b/examples/models/models_animation_blending.c index b33dbe85d..c9f59d9f1 100644 --- a/examples/models/models_animation_blending.c +++ b/examples/models/models_animation_blending.c @@ -1,6 +1,8 @@ /******************************************************************************************* * * raylib [models] example - animation blending +* +* Example complexity rating: [★★★☆] 3/4 * * Example originally created with raylib 5.5, last time updated with raylib 5.6-dev * @@ -47,31 +49,26 @@ int main(void) camera.fovy = 45.0f; // Camera field-of-view Y camera.projection = CAMERA_PERSPECTIVE; // Camera projection type - // Load gltf model + // Load model Model characterModel = LoadModel("resources/models/gltf/robot.glb"); // Load character model -/* INFO: Uncomment this to use GPU skinning // Load skinning shader Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION), TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION)); + + // Assign skinning shader to all materials shaders + for (int i = 0; i < characterModel.materialCount; i++) characterModel.materials[i].shader = skinningShader; - for (int i = 0; i < characterModel.materialCount; i++) - { - characterModel.materials[i].shader = skinningShader; - } -*/ - - // Load gltf model animations + // Load model animations int animsCount = 0; - unsigned int animIndex0 = 0; - unsigned int animIndex1 = 0; - unsigned int animCurrentFrame = 0; ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/robot.glb", &animsCount); + // Define animation variables + unsigned int animIndex0 = 0; + unsigned int animIndex1 = 0; + float animCurrentFrame = 0; float blendFactor = 0.5f; - DisableCursor(); // Limit cursor to relative movement inside the window - SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -80,7 +77,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera, CAMERA_THIRD_PERSON); + UpdateCamera(&camera, CAMERA_ORBITAL); // Select current animation if (IsKeyPressed(KEY_T)) animIndex0 = (animIndex0 + 1)%animsCount; @@ -93,14 +90,12 @@ int main(void) else if (IsKeyPressed(KEY_J)) blendFactor = clamp(blendFactor + 0.1, 0.0f, 1.0f); // Update animation - animCurrentFrame++; + animCurrentFrame += 0.2f; // Update bones // Note: Same animation frame index is used below. By default it loops both animations - UpdateModelAnimationBonesLerp(characterModel, modelAnimations[animIndex0], animCurrentFrame, modelAnimations[animIndex1], animCurrentFrame, blendFactor); - -// INFO: Comment the following line to use GPU skinning - UpdateModelVertsToCurrentBones(characterModel); + UpdateModelAnimationEx(characterModel, modelAnimations[animIndex0], animCurrentFrame, + modelAnimations[animIndex1], animCurrentFrame, blendFactor); //---------------------------------------------------------------------------------- // Draw @@ -111,18 +106,7 @@ int main(void) BeginMode3D(camera); -/* INFO: Uncomment this to use GPU skinning - // Draw character mesh, pose calculation is done in shader (GPU skinning) - for (int i = 0; i < characterModel.meshCount; i++) - { - DrawMesh(characterModel.meshes[i], characterModel.materials[characterModel.meshMaterial[i]], characterModel.transform); - } -*/ - -// INFO: Comment the following line to use GPU skinning DrawModel(characterModel, (Vector3){0.0f, 0.0f, 0.0f}, 1.0f, WHITE); - - DrawGrid(10, 1.0f); EndMode3D(); @@ -142,8 +126,7 @@ int main(void) UnloadModelAnimations(modelAnimations, animsCount); // Unload model animation UnloadModel(characterModel); // Unload model and meshes/material -// INFO: Uncomment the following line to use GPU skinning - // UnloadShader(skinningShader); // Unload GPU skinning shader + UnloadShader(skinningShader); // Unload GPU skinning shader CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- From 542333b6d32a0bb2371cae9b42e969f3d78e1936 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 23 Feb 2026 01:41:16 +0100 Subject: [PATCH 064/319] Update README.md --- examples/README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/README.md b/examples/README.md index cfd7442ca..a4e65b9f5 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,7 +17,7 @@ You may find it easier to use than other toolchains, especially when it comes to - `zig build [module]` to compile all examples for a module (e.g. `zig build core`) - `zig build [example]` to compile _and run_ a particular example (e.g. `zig build core_basic_window`) -## EXAMPLES COLLECTION [TOTAL: 207] +## EXAMPLES COLLECTION [TOTAL: 208] ### category: core [49] @@ -182,7 +182,7 @@ Examples using raylib text functionality, including sprite fonts loading/generat | [text_words_alignment](text/text_words_alignment.c) | text_words_alignment | ⭐☆☆☆ | 5.6-dev | 5.6-dev | [JP Mortiboys](https://github.com/themushroompirates) | | [text_strings_management](text/text_strings_management.c) | text_strings_management | ⭐⭐⭐☆ | 5.6-dev | 5.6-dev | [David Buzatto](https://github.com/davidbuzatto) | -### category: models [29] +### category: models [30] Examples using raylib models functionality, including models loading/generation and drawing, provided by raylib [models](../src/rmodels.c) module. @@ -215,8 +215,9 @@ Examples using raylib models functionality, including models loading/generation | [models_rotating_cube](models/models_rotating_cube.c) | models_rotating_cube | ⭐☆☆☆ | 5.6-dev | 5.6-dev | [Jopestpe](https://github.com/jopestpe) | | [models_decals](models/models_decals.c) | models_decals | ⭐⭐⭐⭐️ | 5.6-dev | 5.6-dev | [JP Mortiboys](https://github.com/themushroompirates) | | [models_directional_billboard](models/models_directional_billboard.c) | models_directional_billboard | ⭐⭐☆☆ | 5.6-dev | 5.6 | [Robin](https://github.com/RobinsAviary) | -| [models_animation_bone_blending](models/models_animation_bone_blending.c) | models_animation_bone_blending | ⭐⭐⭐⭐️ | 5.5 | 5.5 | [dmitrii-brand](https://github.com/dmitrii-brand) | +| [models_animation_blend_custom](models/models_animation_blend_custom.c) | models_animation_blend_custom | ⭐⭐⭐⭐️ | 5.5 | 5.5 | [dmitrii-brand](https://github.com/dmitrii-brand) | | [models_animation_blending](models/models_animation_blending.c) | models_animation_blending | ☆☆☆☆ | 5.5 | 5.6-dev | [Kirandeep](https://github.com/Kirandeep-Singh-Khehra) | +| [models_animation_timming](models/models_animation_timming.c) | models_animation_timming | ⭐⭐☆☆ | 5.6 | 5.6 | [Ramon Santamaria](https://github.com/raysan5) | ### category: shaders [34] From ae6d34a731a5abde2b5c37f57084954d58941452 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 23 Feb 2026 01:43:53 +0100 Subject: [PATCH 065/319] Renamed some models examples for consistency --- examples/Makefile | 5 +- examples/Makefile.Web | 25 +- examples/examples_list.txt | 3 +- ...ding.c => models_animation_blend_custom.c} | 58 +- .../models/models_animation_blend_custom.png | Bin 0 -> 23976 bytes examples/models/models_animation_blending.c | 2 +- .../models/models_animation_gpu_skinning.c | 60 +- examples/models/models_animation_timming.c | 114 ++++ examples/models/models_animation_timming.png | Bin 0 -> 22154 bytes examples/models/models_bone_socket.c | 18 +- ... => models_animation_blend_custom.vcxproj} | 6 +- .../examples/models_animation_timming.vcxproj | 569 ++++++++++++++++++ projects/VS2022/raylib.sln | 31 +- tools/rexm/reports/examples_issues.md | 1 - tools/rexm/reports/examples_validation.md | 5 +- 15 files changed, 795 insertions(+), 102 deletions(-) rename examples/models/{models_animation_bone_blending.c => models_animation_blend_custom.c} (87%) create mode 100644 examples/models/models_animation_blend_custom.png create mode 100644 examples/models/models_animation_timming.c create mode 100644 examples/models/models_animation_timming.png rename projects/VS2022/examples/{models_animation_bone_blending.vcxproj => models_animation_blend_custom.vcxproj} (99%) create mode 100644 projects/VS2022/examples/models_animation_timming.vcxproj diff --git a/examples/Makefile b/examples/Makefile index b7c2cee81..fcc013876 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -657,10 +657,10 @@ TEXT = \ text/text_writing_anim MODELS = \ + models/models_animation_blend_custom \ models/models_animation_blending \ - models/models_animation_bone_blending \ models/models_animation_gpu_skinning \ - models/models_loading_iqm \ + models/models_animation_timming \ models/models_basic_voxel \ models/models_billboard_rendering \ models/models_bone_socket \ @@ -673,6 +673,7 @@ MODELS = \ models/models_heightmap_rendering \ models/models_loading \ models/models_loading_gltf \ + models/models_loading_iqm \ models/models_loading_m3d \ models/models_loading_vox \ models/models_mesh_generation \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index ce5ee4784..b355bc63e 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -642,10 +642,10 @@ TEXT = \ text/text_writing_anim MODELS = \ + models/models_animation_blend_custom \ models/models_animation_blending \ - models/models_animation_bone_blending \ models/models_animation_gpu_skinning \ - models/models_loading_iqm \ + models/models_animation_timming \ models/models_basic_voxel \ models/models_billboard_rendering \ models/models_bone_socket \ @@ -658,6 +658,7 @@ MODELS = \ models/models_heightmap_rendering \ models/models_loading \ models/models_loading_gltf \ + models/models_loading_iqm \ models/models_loading_m3d \ models/models_loading_vox \ models/models_mesh_generation \ @@ -1199,15 +1200,15 @@ 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_blending: models/models_animation_blending.c +models/models_animation_blend_custom: models/models_animation_blend_custom.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file models/resources/models/gltf/robot.glb@resources/models/gltf/robot.glb \ + --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_animation_bone_blending: models/models_animation_bone_blending.c +models/models_animation_blending: models/models_animation_blending.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/models/gltf/robot.glb@resources/models/gltf/robot.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 @@ -1217,11 +1218,9 @@ models/models_animation_gpu_skinning: models/models_animation_gpu_skinning.c --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_loading_iqm: models/models_loading_iqm.c +models/models_animation_timming: models/models_animation_timming.c $(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 + --preload-file models/resources/models/gltf/robot.glb@resources/models/gltf/robot.glb models/models_basic_voxel: models/models_basic_voxel.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -1276,6 +1275,12 @@ models/models_loading_gltf: models/models_loading_gltf.c $(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_iqm: models/models_loading_iqm.c + $(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_loading_m3d: models/models_loading_m3d.c $(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 diff --git a/examples/examples_list.txt b/examples/examples_list.txt index 981636ed8..042c1aed3 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -171,8 +171,9 @@ models;models_basic_voxel;★★☆☆;5.5;5.5;2025;2025;"Tim Little";@timlittle models;models_rotating_cube;★☆☆☆;5.6-dev;5.6-dev;2025;2025;"Jopestpe";@jopestpe models;models_decals;★★★★;5.6-dev;5.6-dev;2025;2025;"JP Mortiboys";@themushroompirates models;models_directional_billboard;★★☆☆;5.6-dev;5.6;2025;2025;"Robin";@RobinsAviary -models;models_animation_bone_blending;★★★★;5.5;5.5;2026;2026;"dmitrii-brand";@dmitrii-brand +models;models_animation_blend_custom;★★★★;5.5;5.5;2026;2026;"dmitrii-brand";@dmitrii-brand models;models_animation_blending;☆☆☆☆;5.5;5.6-dev;2024;2024;"Kirandeep";@Kirandeep-Singh-Khehra +models;models_animation_timming;★★☆☆;5.6;5.6;2026;2026;"Ramon Santamaria";@raysan5 shaders;shaders_ascii_rendering;★★☆☆;5.5;5.6;2025;2025;"Maicon Santana";@maiconpintoabreu shaders;shaders_basic_lighting;★★★★;3.0;4.2;2019;2025;"Chris Camacho";@chriscamacho shaders;shaders_model_shader;★★☆☆;1.3;3.7;2014;2025;"Ramon Santamaria";@raysan5 diff --git a/examples/models/models_animation_bone_blending.c b/examples/models/models_animation_blend_custom.c similarity index 87% rename from examples/models/models_animation_bone_blending.c rename to examples/models/models_animation_blend_custom.c index 11e05a305..76dba6a69 100644 --- a/examples/models/models_animation_bone_blending.c +++ b/examples/models/models_animation_blend_custom.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [models] example - animation bone blending +* raylib [models] example - animation blend custom * * Example complexity rating: [★★★★] 4/4 * @@ -53,7 +53,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [models] example - animation bone blending"); + InitWindow(screenWidth, screenHeight, "raylib [models] example - animation blend custom"); // Define the camera to look into our 3d world Camera camera = { 0 }; @@ -80,7 +80,7 @@ int main(void) TraceLog(LOG_INFO, "Found %d animations:", animsCount); for (int i = 0; i < animsCount; i++) { - TraceLog(LOG_INFO, " Animation %d: %s (%d frames)", i, modelAnimations[i].name, modelAnimations[i].frameCount); + TraceLog(LOG_INFO, " Animation %d: %s (%d frames)", i, modelAnimations[i].name, modelAnimations[i].keyframeCount); } // Use specific indices: walk/move = 2, attack = 3 @@ -118,8 +118,8 @@ int main(void) ModelAnimation anim1 = modelAnimations[animIndex1]; ModelAnimation anim2 = modelAnimations[animIndex2]; - animCurrentFrame1 = (animCurrentFrame1 + 1)%anim1.frameCount; - animCurrentFrame2 = (animCurrentFrame2 + 1)%anim2.frameCount; + animCurrentFrame1 = (animCurrentFrame1 + 1)%anim1.keyframeCount; + animCurrentFrame2 = (animCurrentFrame2 + 1)%anim2.keyframeCount; // Blend the two animations characterModel.transform = MatrixTranslate(position.x, position.y, position.z); @@ -203,9 +203,9 @@ static void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int f ModelAnimation *anim2, int frame2, float blendFactor, bool upperBodyBlend) { // Validate inputs - if (anim1->boneCount == 0 || anim1->framePoses == NULL || - anim2->boneCount == 0 || anim2->framePoses == NULL || - model->boneCount == 0 || model->bindPose == NULL) + if (anim1->boneCount == 0 || anim1->keyframePoses == NULL || + anim2->boneCount == 0 || anim2->keyframePoses == NULL || + model->skeleton.boneCount == 0 || model->skeleton.bindPose == NULL) { return; } @@ -214,26 +214,13 @@ static void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int f blendFactor = fminf(1.0f, fmaxf(0.0f, blendFactor)); // Ensure frame indices are valid - if (frame1 >= anim1->frameCount) frame1 = anim1->frameCount - 1; - if (frame2 >= anim2->frameCount) frame2 = anim2->frameCount - 1; + if (frame1 >= anim1->keyframeCount) frame1 = anim1->keyframeCount - 1; + if (frame2 >= anim2->keyframeCount) frame2 = anim2->keyframeCount - 1; if (frame1 < 0) frame1 = 0; if (frame2 < 0) frame2 = 0; - // Find first mesh with bones - int firstMeshWithBones = -1; - for (int i = 0; i < model->meshCount; i++) - { - if (model->meshes[i].boneMatrices) - { - firstMeshWithBones = i; - break; - } - } - - if (firstMeshWithBones == -1) return; - // Get bone count (use minimum of all to be safe) - int boneCount = model->boneCount; + int boneCount = model->skeleton.boneCount; if (anim1->boneCount < boneCount) boneCount = anim1->boneCount; if (anim2->boneCount < boneCount) boneCount = anim2->boneCount; @@ -246,7 +233,7 @@ static void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int f // If upper body blending is enabled, use different blend factors for upper vs lower body if (upperBodyBlend) { - const char *boneName = model->bones[boneId].name; + const char *boneName = model->skeleton.bones[boneId].name; bool isUpperBody = IsUpperBodyBone(boneName); // Upper body: use anim2 (attack), Lower body: use anim1 (walk) @@ -256,12 +243,12 @@ static void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int f } // Get transforms from both animations - Transform *bindTransform = &model->bindPose[boneId]; - Transform *anim1Transform = &anim1->framePoses[frame1][boneId]; - Transform *anim2Transform = &anim2->framePoses[frame2][boneId]; + Transform *bindTransform = &model->skeleton.bindPose[boneId]; + Transform *anim1Transform = &anim1->keyframePoses[frame1][boneId]; + Transform *anim2Transform = &anim2->keyframePoses[frame2][boneId]; // Blend the transforms - Transform blended; + Transform blended = { 0 }; blended.translation = Vector3Lerp(anim1Transform->translation, anim2Transform->translation, boneBlendFactor); blended.rotation = QuaternionSlerp(anim1Transform->rotation, anim2Transform->rotation, boneBlendFactor); blended.scale = Vector3Lerp(anim1Transform->scale, anim2Transform->scale, boneBlendFactor); @@ -279,18 +266,7 @@ static void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int f MatrixTranslate(blended.translation.x, blended.translation.y, blended.translation.z)); // Calculate final bone matrix (similar to UpdateModelAnimationBones) - model->meshes[firstMeshWithBones].boneMatrices[boneId] = MatrixMultiply(MatrixInvert(bindMatrix), blendedMatrix); - } - - // Copy bone matrices to remaining meshes - for (int i = firstMeshWithBones + 1; i < model->meshCount; i++) - { - if (model->meshes[i].boneMatrices) - { - memcpy(model->meshes[i].boneMatrices, - model->meshes[firstMeshWithBones].boneMatrices, - model->meshes[i].boneCount*sizeof(model->meshes[i].boneMatrices[0])); - } + model->boneMatrices[boneId] = MatrixMultiply(MatrixInvert(bindMatrix), blendedMatrix); } } diff --git a/examples/models/models_animation_blend_custom.png b/examples/models/models_animation_blend_custom.png new file mode 100644 index 0000000000000000000000000000000000000000..0f6dbca87898af2cbf5bb535fab3ba46c6ca85fe GIT binary patch literal 23976 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 literal 0 HcmV?d00001 diff --git a/examples/models/models_animation_blending.c b/examples/models/models_animation_blending.c index c9f59d9f1..39623cd28 100644 --- a/examples/models/models_animation_blending.c +++ b/examples/models/models_animation_blending.c @@ -2,7 +2,7 @@ * * raylib [models] example - animation blending * -* Example complexity rating: [★★★☆] 3/4 +* Example complexity rating: [☆☆☆☆] 0/4 * * Example originally created with raylib 5.5, last time updated with raylib 5.6-dev * diff --git a/examples/models/models_animation_gpu_skinning.c b/examples/models/models_animation_gpu_skinning.c index 337df931c..0cd890449 100644 --- a/examples/models/models_animation_gpu_skinning.c +++ b/examples/models/models_animation_gpu_skinning.c @@ -8,13 +8,15 @@ * * Example contributed by Daniel Holden (@orangeduck) and reviewed by Ramon Santamaria (@raysan5) * +* WARNING: GPU skinning must be enabled in raylib with a compilation flag, +* if not enabled, CPU skinning will be used instead +* NOTE: Due to limitations in the Apple OpenGL driver, this feature does not work on MacOS +* * 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) 2024-2025 Daniel Holden (@orangeduck) * -* Note: Due to limitations in the Apple OpenGL driver, this feature does not work on MacOS -* ********************************************************************************************/ #include "raylib.h" @@ -42,29 +44,29 @@ int main(void) // Define the camera to look into our 3d world Camera camera = { 0 }; camera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; // Camera position - camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; // Camera looking at point + camera.target = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera looking at point camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) camera.fovy = 45.0f; // Camera field-of-view Y camera.projection = CAMERA_PERSPECTIVE; // Camera projection type // Load 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; - unsigned int animCurrentFrame = 0; - ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/greenman.glb", &animsCount); - + Model model = LoadModel("resources/models/gltf/greenman.glb"); // Load character model Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position - DisableCursor(); // Limit cursor to relative movement inside the window + // Load skinning shader + // WARNING: GPU skinning must be enabled in raylib with a compilation flag, + // if not enabled, CPU skinning will be used instead + Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION), + TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION)); + model.materials[1].shader = skinningShader; + + // Load gltf model animations + int animCount = 0; + ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/greenman.glb", &animCount); + + // Animation playing variables + unsigned int animIndex = 0; // Current animation playing + unsigned int animCurrentFrame = 0; // Current animation frame SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -74,17 +76,15 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera, CAMERA_THIRD_PERSON); + UpdateCamera(&camera, CAMERA_ORBITAL); // Select current animation - if (IsKeyPressed(KEY_T)) animIndex = (animIndex + 1)%animsCount; - else if (IsKeyPressed(KEY_G)) animIndex = (animIndex + animsCount - 1)%animsCount; + if (IsKeyPressed(KEY_RIGHT)) animIndex = (animIndex + 1)%animCount; + else if (IsKeyPressed(KEY_LEFT)) animIndex = (animIndex + animCount - 1)%animCount; // Update model animation - ModelAnimation anim = modelAnimations[animIndex]; - animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount; - characterModel.transform = MatrixTranslate(position.x, position.y, position.z); - UpdateModelAnimationBones(characterModel, anim, animCurrentFrame); + animCurrentFrame = (animCurrentFrame + 1)%anims[animIndex].keyframeCount; + UpdateModelAnimation(model, anims[animIndex], (float)animCurrentFrame); //---------------------------------------------------------------------------------- // Draw @@ -95,14 +95,14 @@ int main(void) BeginMode3D(camera); - // Draw character mesh, pose calculation is done in shader (GPU skinning) - DrawMesh(characterModel.meshes[0], characterModel.materials[1], characterModel.transform); + DrawModel(model, position, 1.0f, WHITE); DrawGrid(10, 1.0f); EndMode3D(); - DrawText("Use the T/G to switch animation", 10, 10, 20, GRAY); + DrawText(TextFormat("Current animation: %s", anims[animIndex].name), 10, 40, 20, MAROON); + DrawText("Use the LEFT/RIGHT keys to switch animation", 10, 10, 20, GRAY); EndDrawing(); //---------------------------------------------------------------------------------- @@ -110,8 +110,8 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - UnloadModelAnimations(modelAnimations, animsCount); // Unload model animation - UnloadModel(characterModel); // Unload model and meshes/material + UnloadModelAnimations(anims, animCount); // Unload model animation + UnloadModel(model); // Unload model and meshes/material UnloadShader(skinningShader); // Unload GPU skinning shader CloseWindow(); // Close window and OpenGL context diff --git a/examples/models/models_animation_timming.c b/examples/models/models_animation_timming.c new file mode 100644 index 000000000..8069537ce --- /dev/null +++ b/examples/models/models_animation_timming.c @@ -0,0 +1,114 @@ +/******************************************************************************************* +* +* raylib [models] example - animation timming +* +* Example complexity rating: [★★☆☆] 2/4 +* +* Example originally created with raylib 5.6, last time updated with raylib 5.6 +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2026 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#define RAYGUI_IMPLEMENTATION +#include "raygui.h" // Required for: UI controls + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [models] example - animation timming"); + + // Define the camera to look into our 3d world + Camera camera = { 0 }; + camera.position = (Vector3){ 6.0f, 6.0f, 6.0f }; // Camera position + camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; // Camera looking at point + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) + camera.fovy = 45.0f; // Camera field-of-view Y + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type + + // Load model + Model model = LoadModel("resources/models/gltf/robot.glb"); + Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model world position + + // Load model animations + int animsCount = 0; + ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/robot.glb", &animsCount); + + // Animation playing variables + unsigned int animIndex = 0; // Current animation playing + float animCurrentFrame = 0.0f; // Current animation frame (supporting interpolated frames) + float animFrameSpeed = 0.1f; // Animation play speed + + 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 + //---------------------------------------------------------------------------------- + UpdateCamera(&camera, CAMERA_ORBITAL); + + // Select current animation + if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) animIndex = (animIndex + 1)%animsCount; + else if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) animIndex = (animIndex + animsCount - 1)%animsCount; + + // Select animation playing speed + if (IsKeyPressed(KEY_RIGHT)) animFrameSpeed += 0.1f; + else if (IsKeyPressed(KEY_LEFT)) animFrameSpeed -= 0.1f; + + // Update model animation + animCurrentFrame += animFrameSpeed; + UpdateModelAnimation(model, modelAnimations[animIndex], animCurrentFrame); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + DrawModel(model, position, 1.0f, WHITE); + + DrawGrid(10, 1.0f); + + EndMode3D(); + + // Draw UI + //GuiDropdownBox((Rectangle){ 10, 20, 240, 30 }, "text", &animIndex, editMode); + + DrawText(TextFormat("FRAME SPEED: x%.1f", animFrameSpeed), 10, 40, 20, RED); + + DrawText("Use the LEFT/RIGHT mouse buttons to switch animation", 10, 10, 20, GRAY); + DrawText(TextFormat("Animation: %s", modelAnimations[animIndex].name), 10, GetScreenHeight() - 20, 10, DARKGRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadModel(model); // Unload model and meshes/material + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + + + diff --git a/examples/models/models_animation_timming.png b/examples/models/models_animation_timming.png new file mode 100644 index 0000000000000000000000000000000000000000..673994b7c4451ad3a8f431a9e92cf21406496fbc GIT binary patch literal 22154 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 literal 0 HcmV?d00001 diff --git a/examples/models/models_bone_socket.c b/examples/models/models_bone_socket.c index 8348aa50d..37b677c4a 100644 --- a/examples/models/models_bone_socket.c +++ b/examples/models/models_bone_socket.c @@ -60,25 +60,25 @@ int main(void) unsigned int animCurrentFrame = 0; ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/greenman.glb", &animsCount); - // indices of bones for sockets + // Indices of bones for sockets int boneSocketIndex[BONE_SOCKETS] = { -1, -1, -1 }; - // search bones for sockets - for (int i = 0; i < characterModel.boneCount; i++) + // Search bones for sockets + for (int i = 0; i < characterModel.skeleton.boneCount; i++) { - if (TextIsEqual(characterModel.bones[i].name, "socket_hat")) + if (TextIsEqual(characterModel.skeleton.bones[i].name, "socket_hat")) { boneSocketIndex[BONE_SOCKET_HAT] = i; continue; } - if (TextIsEqual(characterModel.bones[i].name, "socket_hand_R")) + if (TextIsEqual(characterModel.skeleton.bones[i].name, "socket_hand_R")) { boneSocketIndex[BONE_SOCKET_HAND_R] = i; continue; } - if (TextIsEqual(characterModel.bones[i].name, "socket_hand_L")) + if (TextIsEqual(characterModel.skeleton.bones[i].name, "socket_hand_L")) { boneSocketIndex[BONE_SOCKET_HAND_L] = i; continue; @@ -115,7 +115,7 @@ int main(void) // Update model animation ModelAnimation anim = modelAnimations[animIndex]; - animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount; + animCurrentFrame = (animCurrentFrame + 1)%anim.keyframeCount; UpdateModelAnimation(characterModel, anim, animCurrentFrame); //---------------------------------------------------------------------------------- @@ -137,8 +137,8 @@ int main(void) { if (!showEquip[i]) continue; - Transform *transform = &anim.framePoses[animCurrentFrame][boneSocketIndex[i]]; - Quaternion inRotation = characterModel.bindPose[boneSocketIndex[i]].rotation; + Transform *transform = &anim.keyframePoses[animCurrentFrame][boneSocketIndex[i]]; + Quaternion inRotation = characterModel.skeleton.bindPose[boneSocketIndex[i]].rotation; Quaternion outRotation = transform->rotation; // Calculate socket rotation (angle between bone in initial pose and same bone in current animation frame) diff --git a/projects/VS2022/examples/models_animation_bone_blending.vcxproj b/projects/VS2022/examples/models_animation_blend_custom.vcxproj similarity index 99% rename from projects/VS2022/examples/models_animation_bone_blending.vcxproj rename to projects/VS2022/examples/models_animation_blend_custom.vcxproj index 475ad47f0..32fdf506b 100644 --- a/projects/VS2022/examples/models_animation_bone_blending.vcxproj +++ b/projects/VS2022/examples/models_animation_blend_custom.vcxproj @@ -53,9 +53,9 @@ {AC751FE1-C986-4B6A-92A8-28ED89DEE671} Win32Proj - models_animation_bone_blending + models_animation_blend_custom 10.0 - models_animation_bone_blending + models_animation_blend_custom @@ -553,7 +553,7 @@ - + diff --git a/projects/VS2022/examples/models_animation_timming.vcxproj b/projects/VS2022/examples/models_animation_timming.vcxproj new file mode 100644 index 000000000..a18123bbc --- /dev/null +++ b/projects/VS2022/examples/models_animation_timming.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 + + + + {89D5A0E9-683C-465C-BF85-A880865175C8} + Win32Proj + models_animation_timming + 10.0 + models_animation_timming + + + + 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/raylib.sln b/projects/VS2022/raylib.sln index 938f23bf4..51268e037 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -429,12 +429,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_easings_testbed", "e EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_rlgl_compute", "examples\shaders_rlgl_compute.vcxproj", "{AEA9D0D4-B810-4624-BC75-10A291584ED6}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_bone_blending", "examples\models_animation_bone_blending.vcxproj", "{AC751FE1-C986-4B6A-92A8-28ED89DEE671}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_blend_custom", "examples\models_animation_blend_custom.vcxproj", "{AC751FE1-C986-4B6A-92A8-28ED89DEE671}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_blending", "examples\models_animation_blending.vcxproj", "{BB9C957D-34F1-46AE-B64A-9E0499C1746D}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_window_web", "examples\core_window_web.vcxproj", "{4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_timming", "examples\models_animation_timming.vcxproj", "{89D5A0E9-683C-465C-BF85-A880865175C8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|ARM64 = Debug.DLL|ARM64 @@ -5443,6 +5445,30 @@ Global {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Release|x64.Build.0 = Release|x64 {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Release|x86.ActiveCfg = Release|Win32 {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Release|x86.Build.0 = Release|Win32 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug|ARM64.Build.0 = Debug|ARM64 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug|x64.ActiveCfg = Debug|x64 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug|x64.Build.0 = Debug|x64 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug|x86.ActiveCfg = Debug|Win32 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug|x86.Build.0 = Debug|Win32 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Release|ARM64.ActiveCfg = Release|ARM64 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Release|ARM64.Build.0 = Release|ARM64 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Release|x64.ActiveCfg = Release|x64 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Release|x64.Build.0 = Release|x64 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Release|x86.ActiveCfg = Release|Win32 + {89D5A0E9-683C-465C-BF85-A880865175C8}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -5605,7 +5631,7 @@ Global {C54703BF-D68A-480D-BE27-49B62E45D582} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {9CD8BCAD-F212-4BCC-BA98-899743CE3279} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} {0981CA28-E4A5-4DF1-987F-A41D09131EFC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {6777EC3C-077C-42FC-B4AD-B799CE55CCE4} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} {A61DAD9C-271C-4E95-81AA-DB4CD58564D4} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} @@ -5662,6 +5688,7 @@ Global {AC751FE1-C986-4B6A-92A8-28ED89DEE671} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {BB9C957D-34F1-46AE-B64A-9E0499C1746D} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {89D5A0E9-683C-465C-BF85-A880865175C8} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} diff --git a/tools/rexm/reports/examples_issues.md b/tools/rexm/reports/examples_issues.md index 3fcc136e5..a648295cc 100644 --- a/tools/rexm/reports/examples_issues.md +++ b/tools/rexm/reports/examples_issues.md @@ -20,4 +20,3 @@ Example elements validated: ``` | **EXAMPLE NAME** | [C] | [CAT]| [INFO]|[PNG]|[WPNG]| [RES]| [MK] |[MKWEB]| [VCX]| [SOL]|[RDME]|[JS] | [WOUT]|[WMETA]| |:---------------------------------|:---:|:----:|:-----:|:---:|:----:|:----:|:----:|:-----:|:----:|:----:|:----:|:---:|:-----:|:-----:| -| models_animation_bone_blending | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | diff --git a/tools/rexm/reports/examples_validation.md b/tools/rexm/reports/examples_validation.md index 9c0c02956..e46b93e46 100644 --- a/tools/rexm/reports/examples_validation.md +++ b/tools/rexm/reports/examples_validation.md @@ -155,7 +155,7 @@ Example elements validated: | text_inline_styling | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | text_words_alignment | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | text_strings_management | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| models_animation_playing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_loading_iqm | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_billboard_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_box_collisions | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_cubicmap_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -182,8 +182,9 @@ Example elements validated: | models_rotating_cube | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_decals | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_directional_billboard | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| models_animation_bone_blending | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_animation_blend_custom | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_animation_blending | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| models_animation_timming | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_ascii_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_basic_lighting | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_model_shader | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | From 43d8933404acd7e7c7d02e586f4d619ab0f37ee3 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 23 Feb 2026 01:45:23 +0100 Subject: [PATCH 066/319] Renamed skinning shader variables (new default naming) --- examples/models/resources/shaders/glsl100/skinning.vs | 10 +++++----- examples/models/resources/shaders/glsl120/skinning.vs | 10 +++++----- examples/models/resources/shaders/glsl330/skinning.vs | 11 +++++------ 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/examples/models/resources/shaders/glsl100/skinning.vs b/examples/models/resources/shaders/glsl100/skinning.vs index 627a7dda4..344ad3f95 100644 --- a/examples/models/resources/shaders/glsl100/skinning.vs +++ b/examples/models/resources/shaders/glsl100/skinning.vs @@ -6,7 +6,7 @@ attribute vec3 vertexPosition; attribute vec2 vertexTexCoord; attribute vec4 vertexColor; -attribute vec4 vertexBoneIds; +attribute vec4 vertexBoneIndices; attribute vec4 vertexBoneWeights; // Input uniform values @@ -19,10 +19,10 @@ 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); + int boneIndex0 = int(vertexBoneIndices.x); + int boneIndex1 = int(vertexBoneIndices.y); + int boneIndex2 = int(vertexBoneIndices.z); + int boneIndex3 = int(vertexBoneIndices.w); // WARNING: OpenGL ES 2.0 does not support automatic matrix transposing, neither transpose() function mat4 boneMatrixTransposed0 = mat4( diff --git a/examples/models/resources/shaders/glsl120/skinning.vs b/examples/models/resources/shaders/glsl120/skinning.vs index c08840b54..1d9855074 100644 --- a/examples/models/resources/shaders/glsl120/skinning.vs +++ b/examples/models/resources/shaders/glsl120/skinning.vs @@ -6,7 +6,7 @@ attribute vec3 vertexPosition; attribute vec2 vertexTexCoord; attribute vec4 vertexColor; -attribute vec4 vertexBoneIds; +attribute vec4 vertexBoneIndices; attribute vec4 vertexBoneWeights; // Input uniform values @@ -19,10 +19,10 @@ 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); + int boneIndex0 = int(vertexBoneIndices.x); + int boneIndex1 = int(vertexBoneIndices.y); + int boneIndex2 = int(vertexBoneIndices.z); + int boneIndex3 = int(vertexBoneIndices.w); // WARNING: OpenGL ES 2.0 does not support automatic matrix transposing, neither transpose() function mat4 boneMatrixTransposed0 = mat4( diff --git a/examples/models/resources/shaders/glsl330/skinning.vs b/examples/models/resources/shaders/glsl330/skinning.vs index 43bbca76c..9b4ffbbfa 100644 --- a/examples/models/resources/shaders/glsl330/skinning.vs +++ b/examples/models/resources/shaders/glsl330/skinning.vs @@ -7,7 +7,7 @@ in vec3 vertexPosition; in vec2 vertexTexCoord; in vec4 vertexColor; in vec3 vertexNormal; -in vec4 vertexBoneIds; +in vec4 vertexBoneIndices; in vec4 vertexBoneWeights; // Input uniform values @@ -22,10 +22,10 @@ out vec3 fragNormal; void main() { - int boneIndex0 = int(vertexBoneIds.x); - int boneIndex1 = int(vertexBoneIds.y); - int boneIndex2 = int(vertexBoneIds.z); - int boneIndex3 = int(vertexBoneIds.w); + int boneIndex0 = int(vertexBoneIndices.x); + int boneIndex1 = int(vertexBoneIndices.y); + int boneIndex2 = int(vertexBoneIndices.z); + int boneIndex3 = int(vertexBoneIndices.w); vec4 skinnedPosition = vertexBoneWeights.x*(boneMatrices[boneIndex0]*vec4(vertexPosition, 1.0)) + @@ -42,7 +42,6 @@ void main() fragTexCoord = vertexTexCoord; fragColor = vertexColor; - fragNormal = normalize(vec3(matNormal*skinnedNormal)); gl_Position = mvp*skinnedPosition; From e4cbf6b79c4fbd3b6d52ffdbc80c23104fb43477 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Mon, 23 Feb 2026 14:07:42 -0600 Subject: [PATCH 067/319] make glfw consistent (#5583) --- examples/Makefile | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index fcc013876..47e1fb47e 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -90,6 +90,11 @@ BUILD_MODE ?= RELEASE # Use external GLFW library instead of rglfw module USE_EXTERNAL_GLFW ?= FALSE +# Enable support for X11 by default on Linux when using GLFW +# NOTE: Wayland is disabled by default, only enable if you are sure +GLFW_LINUX_ENABLE_WAYLAND ?= FALSE +GLFW_LINUX_ENABLE_X11 ?= TRUE + # 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 @@ -394,9 +399,11 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) LDLIBS = -lraylib -lGL -lm -lpthread -ldl -lrt # On Wayland, additional libraries requires - ifeq ($(USE_WAYLAND_DISPLAY),TRUE) + ifeq ($(GLFW_LINUX_ENABLE_WAYLAND),TRUE) LDLIBS += -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon - else + endif + + ifeq ($(GLFW_LINUX_ENABLE_X11),TRUE) # On X11, additional libraries required LDLIBS += -lX11 # NOTE: It seems additional libraries are not required any more, latest GLFW just dlopen them From 73cb1d5b645323116ae2252cee29388505f9992c Mon Sep 17 00:00:00 2001 From: FinnDemonCat <94855390+FinnDemonCat@users.noreply.github.com> Date: Mon, 23 Feb 2026 17:08:46 -0300 Subject: [PATCH 068/319] Add Dart binding to BINDINGS.md (#5585) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index 6c405ab66..80deadff9 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -103,6 +103,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers | [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/) | **???** | +| [Target](https://github.com/FinnDemonCat/Target/tree/main/libs/raylib) | **5.5** | [Dart](https://dart.dev/) | Apache-2.0 license | ### Utility Wrapers From 3f36c2d3f57ffa2bb51919d030b9fe134fb4426f Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 24 Feb 2026 00:51:20 +0100 Subject: [PATCH 069/319] Update raygui.h --- examples/models/raygui.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/models/raygui.h b/examples/models/raygui.h index 42c5ae7bc..3bf7638be 100644 --- a/examples/models/raygui.h +++ b/examples/models/raygui.h @@ -646,6 +646,7 @@ typedef enum { // ProgressBar typedef enum { PROGRESS_PADDING = 16, // ProgressBar internal padding + PROGRESS_SIDE, // ProgressBar increment side: 0-left->right, 1-right-left } GuiProgressBarProperty; // ScrollBar @@ -3522,7 +3523,15 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight } // Draw slider internal progress bar (depends on state) - GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED))); + if (GuiGetStyle(PROGRESSBAR, PROGRESS_SIDE) == 0) // Left-->Right + { + GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED))); + } + else // Right-->Left + { + progress.x = bounds.x + bounds.width - progress.width - GuiGetStyle(PROGRESSBAR, BORDER_WIDTH); + GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED))); + } } // Draw left/right text if provided From bee3dc66732f70cab10880e5c82951cc4de7e1a7 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 24 Feb 2026 00:51:39 +0100 Subject: [PATCH 070/319] Update models_animation_blending.vcxproj --- projects/VS2022/examples/models_animation_blending.vcxproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/projects/VS2022/examples/models_animation_blending.vcxproj b/projects/VS2022/examples/models_animation_blending.vcxproj index 3eca7a6f2..44a55845d 100644 --- a/projects/VS2022/examples/models_animation_blending.vcxproj +++ b/projects/VS2022/examples/models_animation_blending.vcxproj @@ -381,6 +381,9 @@ {e89d61ac-55de-4482-afd4-df7242ebc859} + + + From d4dc038e2e77ad8b0fddcfa323ee872cc2eb1d0b Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 24 Feb 2026 01:18:57 +0100 Subject: [PATCH 071/319] WARNING: BREAKING: REDESIGNED: **Animation System** #4606 REVIEWED: Reorganized structures for a clearer distinction between "skeleton", "skin" and "skinning" data ADDED: New structures: `ModelSkeleton`, `ModelAnimPose` (alias `Transform*`) ADDED: Runtime data `currentPose` and `boneMatrices` to `Model` structure ADDED: Support animation frames-blending, for timing control ADDED: Support animations blending, between two animations REVIEWED: All models animation loading functions ADDED: `UpdateModelAnimationEx()` for two animations blending REMOVED: `UpdateModelAnimationBones*()`, simplified API REVIEWED: Shader attributes/uniforms names for animations, for consistency REVIEWED: Multiple tweaks on animations loading for consistency between formats ADDED: example: `models_animation_timing` ADDED: example: `models_animation_blending` REVIEWED: example: `models_animation_gpu_skinning` REVIEWED: example: `models_animation_blend_custom` REVIEWED: All animated models loading examples --- examples/examples_list.txt | 2 +- .../models/models_animation_blend_custom.c | 278 +++--- .../models/models_animation_blend_custom.png | Bin 23976 -> 83827 bytes examples/models/models_animation_blending.c | 207 +++- examples/models/models_animation_blending.png | Bin 26143 -> 28003 bytes .../models/models_animation_gpu_skinning.c | 1 - .../models/models_animation_gpu_skinning.png | Bin 49846 -> 66853 bytes ...on_timming.c => models_animation_timing.c} | 65 +- examples/models/models_animation_timing.png | Bin 0 -> 23489 bytes examples/models/models_animation_timming.png | Bin 22154 -> 0 bytes examples/models/models_loading.c | 21 +- examples/models/models_loading_gltf.c | 37 +- examples/models/models_loading_gltf.png | Bin 26632 -> 22136 bytes examples/models/models_loading_iqm.c | 46 +- examples/models/models_loading_iqm.png | Bin 67020 -> 59771 bytes examples/models/models_loading_m3d.c | 132 +-- examples/models/models_loading_m3d.png | Bin 25881 -> 22930 bytes examples/models/models_loading_vox.c | 7 +- examples/models/models_loading_vox.png | Bin 23252 -> 37121 bytes ...cxproj => models_animation_timing.vcxproj} | 4 +- projects/VS2022/raylib.sln | 2 +- src/config.h | 35 +- src/raylib.h | 59 +- src/rcore.c | 8 +- src/rlgl.h | 60 +- src/rmodels.c | 895 ++++++++++-------- 26 files changed, 1052 insertions(+), 807 deletions(-) rename examples/models/{models_animation_timming.c => models_animation_timing.c} (57%) create mode 100644 examples/models/models_animation_timing.png delete mode 100644 examples/models/models_animation_timming.png rename projects/VS2022/examples/{models_animation_timming.vcxproj => models_animation_timing.vcxproj} (99%) diff --git a/examples/examples_list.txt b/examples/examples_list.txt index 042c1aed3..dc3312893 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -173,7 +173,7 @@ models;models_decals;★★★★;5.6-dev;5.6-dev;2025;2025;"JP Mortiboys";@them models;models_directional_billboard;★★☆☆;5.6-dev;5.6;2025;2025;"Robin";@RobinsAviary models;models_animation_blend_custom;★★★★;5.5;5.5;2026;2026;"dmitrii-brand";@dmitrii-brand models;models_animation_blending;☆☆☆☆;5.5;5.6-dev;2024;2024;"Kirandeep";@Kirandeep-Singh-Khehra -models;models_animation_timming;★★☆☆;5.6;5.6;2026;2026;"Ramon Santamaria";@raysan5 +models;models_animation_timing;★★☆☆;5.6;5.6;2026;2026;"Ramon Santamaria";@raysan5 shaders;shaders_ascii_rendering;★★☆☆;5.5;5.6;2025;2025;"Maicon Santana";@maiconpintoabreu shaders;shaders_basic_lighting;★★★★;3.0;4.2;2019;2025;"Chris Camacho";@chriscamacho shaders;shaders_model_shader;★★☆☆;1.3;3.7;2014;2025;"Ramon Santamaria";@raysan5 diff --git a/examples/models/models_animation_blend_custom.c b/examples/models/models_animation_blend_custom.c index 76dba6a69..f846a125b 100644 --- a/examples/models/models_animation_blend_custom.c +++ b/examples/models/models_animation_blend_custom.c @@ -6,7 +6,7 @@ * * Example originally created with raylib 5.5, last time updated with raylib 5.5 * -* This example demonstrates per-bone animation blending, allowing smooth transitions +* DETAILS: Example demonstrates per-bone animation blending, allowing smooth transitions * between two animations by interpolating bone transforms. This is useful for: * - Blending movement animations (walk/run) with action animations (jump/attack) * - Creating smooth animation transitions @@ -27,8 +27,10 @@ #include "raymath.h" -#include // Required for: memcpy() -#include // Required for: NULL +#include "rlgl.h" // Requried for: rlUpdateVertexBuffer() (CPU-skinning) + +#include // Required for: memcpy() +#include // Required for: NULL #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 @@ -40,8 +42,8 @@ // Module Functions Declaration //------------------------------------------------------------------------------------ static bool IsUpperBodyBone(const char *boneName); -static void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int frame1, - ModelAnimation *anim2, int frame2, float blendFactor, bool upperBodyBlend); +static void UpdateModelAnimationBones(Model *model, ModelAnimation *anim1, int frame1, + ModelAnimation *anim2, int frame2, float blend, bool upperBodyBlend); //------------------------------------------------------------------------------------ // Program main entry point @@ -57,51 +59,40 @@ int main(void) // Define the camera to look into our 3d world Camera camera = { 0 }; - camera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; // Camera position - camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; // Camera looking at point + camera.position = (Vector3){ 4.0f, 4.0f, 4.0f }; // Camera position + camera.target = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera looking at point camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) camera.fovy = 45.0f; // Camera field-of-view Y camera.projection = CAMERA_PERSPECTIVE; // Camera projection type // Load gltf model - Model characterModel = LoadModel("resources/models/gltf/greenman.glb"); + Model model = LoadModel("resources/models/gltf/greenman.glb"); + Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position // Load skinning shader + // WARNING: GPU skinning must be enabled in raylib with a compilation flag, + // if not enabled, CPU skinning will be used instead 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; + model.materials[1].shader = skinningShader; // Load gltf model animations - int animsCount = 0; - ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/greenman.glb", &animsCount); + int animCount = 0; + ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/greenman.glb", &animCount); - // Log all available animations for debugging - TraceLog(LOG_INFO, "Found %d animations:", animsCount); - for (int i = 0; i < animsCount; i++) - { - TraceLog(LOG_INFO, " Animation %d: %s (%d frames)", i, modelAnimations[i].name, modelAnimations[i].keyframeCount); - } - - // Use specific indices: walk/move = 2, attack = 3 - unsigned int animIndex1 = 2; // Walk/Move animation (index 2) - unsigned int animIndex2 = 3; // Attack animation (index 3) + // Use specific animation indices: 2-walk/move, 3-attack + unsigned int animIndex0 = 2; // Walk/Move animation (index 2) + unsigned int animIndex1 = 3; // Attack animation (index 3) + unsigned int animCurrentFrame0 = 0; unsigned int animCurrentFrame1 = 0; - unsigned int animCurrentFrame2 = 0; // Validate indices - if (animIndex1 >= animsCount) animIndex1 = 0; - if (animIndex2 >= animsCount) animIndex2 = (animsCount > 1) ? 1 : 0; - - TraceLog(LOG_INFO, "Using Walk (index %d): %s", animIndex1, modelAnimations[animIndex1].name); - TraceLog(LOG_INFO, "Using Attack (index %d): %s", animIndex2, modelAnimations[animIndex2].name); + if (animIndex0 >= animCount) animIndex0 = 0; + if (animIndex1 >= animCount) animIndex1 = (animCount > 1) ? 1 : 0; - Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position - bool upperBodyBlend = true; // Toggle: true = upper/lower body blending, false = uniform blending (50/50) + bool upperBodyBlend = true; // Toggle: true = upper/lower body blending, false = uniform blending (50/50) - DisableCursor(); // Limit cursor to relative movement inside the window - - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- // Main game loop @@ -109,24 +100,28 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera, CAMERA_THIRD_PERSON); + UpdateCamera(&camera, CAMERA_ORBITAL); // Toggle upper/lower body blending mode (SPACE key) if (IsKeyPressed(KEY_SPACE)) upperBodyBlend = !upperBodyBlend; // Update animation frames - ModelAnimation anim1 = modelAnimations[animIndex1]; - ModelAnimation anim2 = modelAnimations[animIndex2]; + ModelAnimation anim0 = anims[animIndex0]; + ModelAnimation anim1 = anims[animIndex1]; + animCurrentFrame0 = (animCurrentFrame0 + 1)%anim0.keyframeCount; animCurrentFrame1 = (animCurrentFrame1 + 1)%anim1.keyframeCount; - animCurrentFrame2 = (animCurrentFrame2 + 1)%anim2.keyframeCount; // Blend the two animations - characterModel.transform = MatrixTranslate(position.x, position.y, position.z); // When upperBodyBlend is ON: upper body = attack (1.0), lower body = walk (0.0) // When upperBodyBlend is OFF: uniform blend at 0.5 (50% walk, 50% attack) - float blendFactor = upperBodyBlend ? 1.0f : 0.5f; - BlendModelAnimationsBones(&characterModel, &anim1, animCurrentFrame1, &anim2, animCurrentFrame2, blendFactor, upperBodyBlend); + float blendFactor = (upperBodyBlend? 1.0f : 0.5f); + UpdateModelAnimationBones(&model, &anim0, animCurrentFrame0, + &anim1, animCurrentFrame1, blendFactor, upperBodyBlend); + + // raylib provided animation blending function + //UpdateModelAnimationEx(model, anim0, (float)animCurrentFrame0, + // anim1, (float)animCurrentFrame1, blendFactor); //---------------------------------------------------------------------------------- // Draw @@ -137,19 +132,18 @@ int main(void) BeginMode3D(camera); - // Draw character mesh, pose calculation is done in shader (GPU skinning) - DrawMesh(characterModel.meshes[0], characterModel.materials[1], characterModel.transform); + DrawModel(model, position, 1.0f, WHITE); DrawGrid(10, 1.0f); EndMode3D(); // Draw UI - DrawText("BONE BLENDING EXAMPLE", 10, 10, 20, DARKGRAY); - DrawText(TextFormat("Walk (Animation 2): %s", anim1.name), 10, 35, 10, GRAY); - DrawText(TextFormat("Attack (Animation 3): %s", anim2.name), 10, 50, 10, GRAY); - DrawText(TextFormat("Mode: %s", upperBodyBlend ? "Upper/Lower Body Blending" : "Uniform Blending"), 10, 65, 10, GRAY); - DrawText("SPACE - Toggle blending mode", 10, GetScreenHeight() - 20, 10, DARKGRAY); + DrawText(TextFormat("ANIM 0: %s", anim0.name), 10, 10, 20, GRAY); + DrawText(TextFormat("ANIM 1: %s", anim1.name), 10, 40, 20, GRAY); + DrawText(TextFormat("[SPACE] Toggle blending mode: %s", + upperBodyBlend? "Upper/Lower Body Blending" : "Uniform Blending"), + 10, GetScreenHeight() - 30, 20, DARKGRAY); EndDrawing(); //---------------------------------------------------------------------------------- @@ -157,8 +151,8 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - UnloadModelAnimations(modelAnimations, animsCount); // Unload model animation - UnloadModel(characterModel); // Unload model and meshes/material + UnloadModelAnimations(anims, animCount); // Unload model animation + UnloadModel(model); // Unload model and meshes/material UnloadShader(skinningShader); // Unload GPU skinning shader CloseWindow(); // Close window and OpenGL context @@ -199,74 +193,138 @@ static bool IsUpperBodyBone(const char *boneName) } // Blend two animations per-bone with selective upper/lower body blending -static void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int frame1, - ModelAnimation *anim2, int frame2, float blendFactor, bool upperBodyBlend) +static void UpdateModelAnimationBones(Model *model, ModelAnimation *anim0, int frame0, + ModelAnimation *anim1, int frame1, float blend, bool upperBodyBlend) { // Validate inputs - if (anim1->boneCount == 0 || anim1->keyframePoses == NULL || - anim2->boneCount == 0 || anim2->keyframePoses == NULL || - model->skeleton.boneCount == 0 || model->skeleton.bindPose == NULL) + if ((anim0->boneCount != 0) && (anim0->keyframePoses != NULL) && + (anim1->boneCount != 0) && (anim1->keyframePoses != NULL) && + (model->skeleton.boneCount != 0) && (model->skeleton.bindPose != NULL)) { - return; - } - - // Clamp blend factor to [0, 1] - blendFactor = fminf(1.0f, fmaxf(0.0f, blendFactor)); - - // Ensure frame indices are valid - if (frame1 >= anim1->keyframeCount) frame1 = anim1->keyframeCount - 1; - if (frame2 >= anim2->keyframeCount) frame2 = anim2->keyframeCount - 1; - if (frame1 < 0) frame1 = 0; - if (frame2 < 0) frame2 = 0; - - // Get bone count (use minimum of all to be safe) - int boneCount = model->skeleton.boneCount; - if (anim1->boneCount < boneCount) boneCount = anim1->boneCount; - if (anim2->boneCount < boneCount) boneCount = anim2->boneCount; - - // Blend each bone - for (int boneId = 0; boneId < boneCount; boneId++) - { - // Determine blend factor for this bone - float boneBlendFactor = blendFactor; + // Clamp blend factor to [0, 1] + blend = fminf(1.0f, fmaxf(0.0f, blend)); - // If upper body blending is enabled, use different blend factors for upper vs lower body - if (upperBodyBlend) + // Ensure frame indices are valid + if (frame0 >= anim0->keyframeCount) frame0 = anim0->keyframeCount - 1; + if (frame1 >= anim1->keyframeCount) frame1 = anim1->keyframeCount - 1; + if (frame0 < 0) frame0 = 0; + if (frame1 < 0) frame1 = 0; + + // Get bone count (use minimum of all to be safe) + int boneCount = model->skeleton.boneCount; + if (anim0->boneCount < boneCount) boneCount = anim0->boneCount; + if (anim1->boneCount < boneCount) boneCount = anim1->boneCount; + + // Blend each bone + for (int boneIndex = 0; boneIndex < boneCount; boneIndex++) { - const char *boneName = model->skeleton.bones[boneId].name; - bool isUpperBody = IsUpperBodyBone(boneName); + // Determine blend factor for this bone + float boneBlendFactor = blend; - // Upper body: use anim2 (attack), Lower body: use anim1 (walk) - // blendFactor = 0.0 means full anim1 (walk), 1.0 means full anim2 (attack) - if (isUpperBody) boneBlendFactor = blendFactor; // Upper body: blend towards anim2 (attack) - else boneBlendFactor = 1.0f - blendFactor; // Lower body: blend towards anim1 (walk) - invert the blend + // If upper body blending is enabled, use different blend factors for upper vs lower body + if (upperBodyBlend) + { + const char *boneName = model->skeleton.bones[boneIndex].name; + bool isUpperBody = IsUpperBodyBone(boneName); + + // Upper body: use anim1 (attack), Lower body: use anim0 (walk) + // blend = 0.0 means full anim0 (walk), 1.0 means full anim1 (attack) + if (isUpperBody) boneBlendFactor = blend; // Upper body: blend towards anim1 (attack) + else boneBlendFactor = 1.0f - blend; // Lower body: blend towards anim0 (walk) - invert the blend + } + + // Get transforms from both animations + Transform *bindTransform = &model->skeleton.bindPose[boneIndex]; + Transform *animTransform0 = &anim0->keyframePoses[frame0][boneIndex]; + Transform *animTransform1 = &anim1->keyframePoses[frame1][boneIndex]; + + // Blend the transforms + Transform blended = { 0 }; + blended.translation = Vector3Lerp(animTransform0->translation, animTransform1->translation, boneBlendFactor); + blended.rotation = QuaternionSlerp(animTransform0->rotation, animTransform1->rotation, boneBlendFactor); + blended.scale = Vector3Lerp(animTransform0->scale, animTransform1->scale, boneBlendFactor); + + // Convert bind pose to matrix + Matrix bindMatrix = MatrixMultiply(MatrixMultiply( + MatrixScale(bindTransform->scale.x, bindTransform->scale.y, bindTransform->scale.z), + QuaternionToMatrix(bindTransform->rotation)), + MatrixTranslate(bindTransform->translation.x, bindTransform->translation.y, bindTransform->translation.z)); + + // Convert blended transform to matrix + Matrix blendedMatrix = MatrixMultiply(MatrixMultiply( + MatrixScale(blended.scale.x, blended.scale.y, blended.scale.z), + QuaternionToMatrix(blended.rotation)), + MatrixTranslate(blended.translation.x, blended.translation.y, blended.translation.z)); + + // Calculate final bone matrix (similar to UpdateModelAnimationBones) + model->boneMatrices[boneIndex] = MatrixMultiply(MatrixInvert(bindMatrix), blendedMatrix); + } + + // CPU skinning, updates CPU buffers and uploads them to GPU (if available) + // NOTE: Fallback in case GPU skinning is not supported or enabled + for (int m = 0; m < model->meshCount; m++) + { + Mesh mesh = model->meshes[m]; + Vector3 animVertex = { 0 }; + Vector3 animNormal = { 0 }; + const int vertexValuesCount = mesh.vertexCount*3; + + int boneIndex = 0; + int boneCounter = 0; + float boneWeight = 0.0f; + bool bufferUpdateRequired = false; // Flag to check when anim vertex information is updated + + // Skip if missing bone data or missing anim buffers initialization + if ((mesh.boneWeights == NULL) || (mesh.boneIndices == NULL) || + (mesh.animVertices == NULL) || (mesh.animNormals == NULL)) continue; + + for (int vCounter = 0; vCounter < vertexValuesCount; vCounter += 3) + { + mesh.animVertices[vCounter] = 0; + mesh.animVertices[vCounter + 1] = 0; + mesh.animVertices[vCounter + 2] = 0; + if (mesh.animNormals != NULL) + { + mesh.animNormals[vCounter] = 0; + mesh.animNormals[vCounter + 1] = 0; + mesh.animNormals[vCounter + 2] = 0; + } + + // Iterates over 4 bones per vertex + for (int j = 0; j < 4; j++, boneCounter++) + { + boneWeight = mesh.boneWeights[boneCounter]; + boneIndex = mesh.boneIndices[boneCounter]; + + // Early stop when no transformation will be applied + if (boneWeight == 0.0f) continue; + animVertex = (Vector3){ mesh.vertices[vCounter], mesh.vertices[vCounter + 1], mesh.vertices[vCounter + 2] }; + animVertex = Vector3Transform(animVertex, model->boneMatrices[boneIndex]); + mesh.animVertices[vCounter] += animVertex.x*boneWeight; + mesh.animVertices[vCounter + 1] += animVertex.y*boneWeight; + mesh.animVertices[vCounter + 2] += animVertex.z*boneWeight; + bufferUpdateRequired = true; + + // Normals processing + // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals) + if ((mesh.normals != NULL) && (mesh.animNormals != NULL )) + { + animNormal = (Vector3){ mesh.normals[vCounter], mesh.normals[vCounter + 1], mesh.normals[vCounter + 2] }; + animNormal = Vector3Transform(animNormal, MatrixTranspose(MatrixInvert(model->boneMatrices[boneIndex]))); + mesh.animNormals[vCounter] += animNormal.x*boneWeight; + mesh.animNormals[vCounter + 1] += animNormal.y*boneWeight; + mesh.animNormals[vCounter + 2] += animNormal.z*boneWeight; + } + } + } + + if (bufferUpdateRequired) + { + // Update GPU vertex buffers with updated data (position + normals) + rlUpdateVertexBuffer(mesh.vboId[SHADER_LOC_VERTEX_POSITION], mesh.animVertices, mesh.vertexCount*3*sizeof(float), 0); + if (mesh.normals != NULL) rlUpdateVertexBuffer(mesh.vboId[SHADER_LOC_VERTEX_NORMAL], mesh.animNormals, mesh.vertexCount*3*sizeof(float), 0); + } } - - // Get transforms from both animations - Transform *bindTransform = &model->skeleton.bindPose[boneId]; - Transform *anim1Transform = &anim1->keyframePoses[frame1][boneId]; - Transform *anim2Transform = &anim2->keyframePoses[frame2][boneId]; - - // Blend the transforms - Transform blended = { 0 }; - blended.translation = Vector3Lerp(anim1Transform->translation, anim2Transform->translation, boneBlendFactor); - blended.rotation = QuaternionSlerp(anim1Transform->rotation, anim2Transform->rotation, boneBlendFactor); - blended.scale = Vector3Lerp(anim1Transform->scale, anim2Transform->scale, boneBlendFactor); - - // Convert bind pose to matrix - Matrix bindMatrix = MatrixMultiply(MatrixMultiply( - MatrixScale(bindTransform->scale.x, bindTransform->scale.y, bindTransform->scale.z), - QuaternionToMatrix(bindTransform->rotation)), - MatrixTranslate(bindTransform->translation.x, bindTransform->translation.y, bindTransform->translation.z)); - - // Convert blended transform to matrix - Matrix blendedMatrix = MatrixMultiply(MatrixMultiply( - MatrixScale(blended.scale.x, blended.scale.y, blended.scale.z), - QuaternionToMatrix(blended.rotation)), - MatrixTranslate(blended.translation.x, blended.translation.y, blended.translation.z)); - - // Calculate final bone matrix (similar to UpdateModelAnimationBones) - model->boneMatrices[boneId] = MatrixMultiply(MatrixInvert(bindMatrix), blendedMatrix); } } diff --git a/examples/models/models_animation_blend_custom.png b/examples/models/models_animation_blend_custom.png index 0f6dbca87898af2cbf5bb535fab3ba46c6ca85fe..95ea1dad2abe0d5e93ef30d769abe9030ae3bfdd 100644 GIT binary patch literal 83827 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 qscD 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 diff --git a/examples/models/models_animation_blending.c b/examples/models/models_animation_blending.c index 39623cd28..a5d3d4b1d 100644 --- a/examples/models/models_animation_blending.c +++ b/examples/models/models_animation_blending.c @@ -21,7 +21,8 @@ #include "raylib.h" -#define clamp(x,a,b) ((x < a)? a : (x > b)? b : x) +#define RAYGUI_IMPLEMENTATION +#include "raygui.h" // Required for: UI controls #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 @@ -43,31 +44,58 @@ int main(void) // Define the camera to look into our 3d world Camera camera = { 0 }; - camera.position = (Vector3){ 8.0f, 8.0f, 8.0f }; // Camera position + camera.position = (Vector3){ 6.0f, 6.0f, 6.0f }; // Camera position camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; // Camera looking at point camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) camera.fovy = 45.0f; // Camera field-of-view Y camera.projection = CAMERA_PERSPECTIVE; // Camera projection type // Load model - Model characterModel = LoadModel("resources/models/gltf/robot.glb"); // Load character model - + Model model = LoadModel("resources/models/gltf/robot.glb"); // Load character model + Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model world position + // Load skinning shader + // WARNING: It requires SUPPORT_GPU_SKINNING enabled on raylib (disabled by default) Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION), TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION)); // Assign skinning shader to all materials shaders - for (int i = 0; i < characterModel.materialCount; i++) characterModel.materials[i].shader = skinningShader; + //for (int i = 0; i < model.materialCount; i++) model.materials[i].shader = skinningShader; // Load model animations - int animsCount = 0; - ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/robot.glb", &animsCount); + int animCount = 0; + ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/robot.glb", &animCount); - // Define animation variables - unsigned int animIndex0 = 0; - unsigned int animIndex1 = 0; - float animCurrentFrame = 0; - float blendFactor = 0.5f; + // Animation playing variables + // NOTE: Two animations are played with a smooth transition between them + int currentAnimPlaying = 0; // Current animation playing (0 o 1) + int nextAnimToPlay = 1; // Next animation to play (to transition) + bool animTransition = false; // Flag to register anim transition state + + int animIndex0 = 10; // Current animation playing (walking) + float animCurrentFrame0 = 0.0f; // Current animation frame (supporting interpolated frames) + float animFrameSpeed0 = 0.5f; // Current animation play speed + int animIndex1 = 6; // Next animation to play (running) + float animCurrentFrame1 = 0.0f; // Next animation frame (supporting interpolated frames) + float animFrameSpeed1 = 0.5f; // Next animation play speed + + float animBlendFactor = 0.0f; // Blend factor from anim0[frame0] --> anim1[frame1], [0.0f..1.0f] + // NOTE: 0.0f results in full anim0[] and 1.0f in full anim1[] + + float animBlendTime = 2.0f; // Time to blend from one playing animation to another (in seconds) + float animBlendTimeCounter = 0.0f; // Time counter (delta time) + + bool animPause = false; // Pause animation + + // UI required variables + char *animNames[64] = { 0 }; // Pointers to animation names for dropdown box + for (int i = 0; i < animCount; i++) animNames[i] = anims[i].name; + + bool dropdownEditMode0 = false; + bool dropdownEditMode1 = false; + float animFrameProgress0 = 0.0f; + float animFrameProgress1 = 0.0f; + float animBlendProgress = 0.0f; SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -79,23 +107,100 @@ int main(void) //---------------------------------------------------------------------------------- UpdateCamera(&camera, CAMERA_ORBITAL); - // Select current animation - if (IsKeyPressed(KEY_T)) animIndex0 = (animIndex0 + 1)%animsCount; - else if (IsKeyPressed(KEY_G)) animIndex0 = (animIndex0 + animsCount - 1)%animsCount; - if (IsKeyPressed(KEY_Y)) animIndex1 = (animIndex1 + 1)%animsCount; - else if (IsKeyPressed(KEY_H)) animIndex1 = (animIndex1 + animsCount - 1)%animsCount; - - // Select blend factor - if (IsKeyPressed(KEY_U)) blendFactor = clamp(blendFactor - 0.1, 0.0f, 1.0f); - else if (IsKeyPressed(KEY_J)) blendFactor = clamp(blendFactor + 0.1, 0.0f, 1.0f); + if (IsKeyPressed(KEY_P)) animPause = !animPause; - // Update animation - animCurrentFrame += 0.2f; + if (!animPause) + { + // Start transition from anim0[] to anim1[] + if (IsKeyPressed(KEY_SPACE) && !animTransition) + { + if (currentAnimPlaying == 0) + { + // Transition anim0 --> anim1 + nextAnimToPlay = 1; + animCurrentFrame1 = 0.0f; + } + else + { + // Transition anim1 --> anim0 + nextAnimToPlay = 0; + animCurrentFrame0 = 0.0f; + } - // Update bones - // Note: Same animation frame index is used below. By default it loops both animations - UpdateModelAnimationEx(characterModel, modelAnimations[animIndex0], animCurrentFrame, - modelAnimations[animIndex1], animCurrentFrame, blendFactor); + // Set animation transition + animTransition = true; + animBlendTimeCounter = 0.0f; + animBlendFactor = 0.0f; + } + + if (animTransition) + { + // Playing anim0 and anim1 at the same time + animCurrentFrame0 += animFrameSpeed0; + if (animCurrentFrame0 >= anims[animIndex0].keyframeCount) animCurrentFrame0 = 0.0f; + animCurrentFrame1 += animFrameSpeed1; + if (animCurrentFrame1 >= anims[animIndex1].keyframeCount) animCurrentFrame1 = 0.0f; + + // Increment blend factor over time to transition from anim0 --> anim1 over time + // NOTE: Time blending could be other than linear, using some easing + animBlendFactor = animBlendTimeCounter/animBlendTime; + animBlendTimeCounter += GetFrameTime(); + animBlendProgress = animBlendFactor; + + // Update model with animations blending + if (nextAnimToPlay == 1) + { + // Blend anim0 --> anim1 + UpdateModelAnimationEx(model, anims[animIndex0], animCurrentFrame0, + anims[animIndex1], animCurrentFrame1, animBlendFactor); + } + else + { + // Blend anim1 --> anim0 + UpdateModelAnimationEx(model, anims[animIndex1], animCurrentFrame1, + anims[animIndex0], animCurrentFrame0, animBlendFactor); + } + + // Check if transition completed + if (animBlendFactor > 1.0f) + { + // Reset frame states + if (currentAnimPlaying == 0) animCurrentFrame0 = 0.0f; + else if (currentAnimPlaying == 1) animCurrentFrame1 = 0.0f; + currentAnimPlaying = nextAnimToPlay; // Update current animation playing + + animBlendFactor = 0.0f; // Reset blend factor + animTransition = false; // Exit transition mode + animBlendTimeCounter = 0.0f; + } + } + else + { + // Play only one anim, the current one + if (currentAnimPlaying == 0) + { + // Playing anim0 at defined speed + animCurrentFrame0 += animFrameSpeed0; + if (animCurrentFrame0 >= anims[animIndex0].keyframeCount) animCurrentFrame0 = 0.0f; + UpdateModelAnimation(model, anims[animIndex0], animCurrentFrame0); + //UpdateModelAnimationEx(model, anims[animIndex0], animCurrentFrame0, + // anims[animIndex1], animCurrentFrame1, 0.0f); + } + else if (currentAnimPlaying == 1) + { + // Playing anim1 at defined speed + animCurrentFrame1 += animFrameSpeed1; + if (animCurrentFrame1 >= anims[animIndex1].keyframeCount) animCurrentFrame1 = 0.0f; + UpdateModelAnimation(model, anims[animIndex1], animCurrentFrame1); + //UpdateModelAnimationEx(model, anims[animIndex0], animCurrentFrame0, + // anims[animIndex1], animCurrentFrame1, 1.0f); + } + } + } + + // Update progress bars values with current frame for each animation + float animFrameProgress0 = animCurrentFrame0; + float animFrameProgress1 = animCurrentFrame1; //---------------------------------------------------------------------------------- // Draw @@ -106,16 +211,47 @@ int main(void) BeginMode3D(camera); - DrawModel(characterModel, (Vector3){0.0f, 0.0f, 0.0f}, 1.0f, WHITE); + DrawModel(model, position, 1.0f, WHITE); // Draw animated model + DrawGrid(10, 1.0f); EndMode3D(); - DrawText("Use the U/J to adjust blend factor", 10, 10, 20, GRAY); - DrawText("Use the T/G to switch first animation", 10, 30, 20, GRAY); - DrawText("Use the Y/H to switch second animation", 10, 50, 20, GRAY); - DrawText(TextFormat("Animations: %s, %s", modelAnimations[animIndex0].name, modelAnimations[animIndex1].name), 10, 70, 20, BLACK); - DrawText(TextFormat("Blend Factor: %f", blendFactor), 10, 86, 20, BLACK); + if (animTransition) DrawText("ANIM TRANSITION BLENDING!", 170, 50, 30, BLUE); + + // Draw UI elements + //--------------------------------------------------------------------------------------------- + // Draw animation selectors for blending transition + // NOTE: Transition does not start until requested + GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 1); + if (GuiDropdownBox((Rectangle){ 10, 10, 160, 24 }, TextJoin(animNames, animCount, ";"), + &animIndex0, dropdownEditMode0)) dropdownEditMode0 = !dropdownEditMode0; + + // Blending process progress bar + if (nextAnimToPlay == 1) GuiSetStyle(PROGRESSBAR, PROGRESS_SIDE, 0); // Left-->Right + else GuiSetStyle(PROGRESSBAR, PROGRESS_SIDE, 1); // Right-->Left + GuiProgressBar((Rectangle){ 180, 14, 440, 16 }, NULL, NULL, &animBlendProgress, 0.0f, 1.0f); + GuiSetStyle(PROGRESSBAR, PROGRESS_SIDE, 0); // Reset to Left-->Right + + if (GuiDropdownBox((Rectangle){ GetScreenWidth() - 170, 10, 160, 24 }, TextJoin(animNames, animCount, ";"), + &animIndex1, dropdownEditMode1)) dropdownEditMode1 = !dropdownEditMode1; + + // Draw playing timeline with keyframes for anim0[] + GuiProgressBar((Rectangle){ 60, GetScreenHeight() - 60, GetScreenWidth() - 180, 20 }, "ANIM 0", + TextFormat("FRAME: %.2f / %i", animFrameProgress0, anims[animIndex0].keyframeCount), + &animFrameProgress0, 0.0f, (float)anims[animIndex0].keyframeCount); + for (int i = 0; i < anims[animIndex0].keyframeCount; i++) + DrawRectangle(60 + ((float)(GetScreenWidth() - 180)/(float)anims[animIndex0].keyframeCount)*(float)i, + GetScreenHeight() - 60, 1, 20, BLUE); + + // Draw playing timeline with keyframes for anim1[] + GuiProgressBar((Rectangle){ 60, GetScreenHeight() - 30, GetScreenWidth() - 180, 20 }, "ANIM 1", + TextFormat("FRAME: %.2f / %i", animFrameProgress1, anims[animIndex1].keyframeCount), + &animFrameProgress1, 0.0f, (float)anims[animIndex1].keyframeCount); + for (int i = 0; i < anims[animIndex1].keyframeCount; i++) + DrawRectangle(60 + ((float)(GetScreenWidth() - 180)/(float)anims[animIndex1].keyframeCount)*(float)i, + GetScreenHeight() - 30, 1, 20, BLUE); + //--------------------------------------------------------------------------------------------- EndDrawing(); //---------------------------------------------------------------------------------- @@ -123,9 +259,8 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - UnloadModelAnimations(modelAnimations, animsCount); // Unload model animation - UnloadModel(characterModel); // Unload model and meshes/material - + UnloadModelAnimations(anims, animCount); // Unload model animation + UnloadModel(model); // Unload model and meshes/material UnloadShader(skinningShader); // Unload GPU skinning shader CloseWindow(); // Close window and OpenGL context diff --git a/examples/models/models_animation_blending.png b/examples/models/models_animation_blending.png index 0d70c1a88436a01b3689693035840c34133cd936..d1ca4a51bc254686bea1b9e979d2760422071fed 100644 GIT binary patch literal 28003 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 z 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 F{{csB+`Rw* literal 26143 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踶GXh~jYVq$`4YMFi*YwYaw_;8L5!(E+DCTM?N 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 HR>uDTmGoo_ diff --git a/examples/models/models_animation_timming.c b/examples/models/models_animation_timing.c similarity index 57% rename from examples/models/models_animation_timming.c rename to examples/models/models_animation_timing.c index 8069537ce..d845e3a9c 100644 --- a/examples/models/models_animation_timming.c +++ b/examples/models/models_animation_timing.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [models] example - animation timming +* raylib [models] example - animation timing * * Example complexity rating: [★★☆☆] 2/4 * @@ -16,7 +16,7 @@ #include "raylib.h" #define RAYGUI_IMPLEMENTATION -#include "raygui.h" // Required for: UI controls +#include "raygui.h" // Required for: UI controls //------------------------------------------------------------------------------------ // Program main entry point @@ -28,7 +28,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [models] example - animation timming"); + InitWindow(screenWidth, screenHeight, "raylib [models] example - animation timing"); // Define the camera to look into our 3d world Camera camera = { 0 }; @@ -43,13 +43,21 @@ int main(void) Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model world position // Load model animations - int animsCount = 0; - ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/robot.glb", &animsCount); + int animCount = 0; + ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/robot.glb", &animCount); // Animation playing variables - unsigned int animIndex = 0; // Current animation playing + int animIndex = 10; // Current animation playing float animCurrentFrame = 0.0f; // Current animation frame (supporting interpolated frames) - float animFrameSpeed = 0.1f; // Animation play speed + float animFrameSpeed = 0.5f; // Animation play speed + bool animPause = false; // Pause animation + + // UI required variables + char *animNames[64] = { 0 }; + for (int i = 0; i < animCount; i++) animNames[i] = anims[i].name; + + bool dropdownEditMode = false; + float animFrameProgress = 0.0f; SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -61,17 +69,20 @@ int main(void) //---------------------------------------------------------------------------------- UpdateCamera(&camera, CAMERA_ORBITAL); - // Select current animation - if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) animIndex = (animIndex + 1)%animsCount; - else if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) animIndex = (animIndex + animsCount - 1)%animsCount; + if (IsKeyPressed(KEY_P)) animPause = !animPause; - // Select animation playing speed - if (IsKeyPressed(KEY_RIGHT)) animFrameSpeed += 0.1f; - else if (IsKeyPressed(KEY_LEFT)) animFrameSpeed -= 0.1f; + if (!animPause && (animIndex < animCount)) + { + // Update model animation + animCurrentFrame += animFrameSpeed; + if (animCurrentFrame >= anims[animIndex].keyframeCount) animCurrentFrame = 0.0f; + UpdateModelAnimation(model, anims[animIndex], animCurrentFrame); + } - // Update model animation - animCurrentFrame += animFrameSpeed; - UpdateModelAnimation(model, modelAnimations[animIndex], animCurrentFrame); + // NOTE: Animation and playing speed selected through UI + + // Update progressbar value with current frame + animFrameProgress = animCurrentFrame; //---------------------------------------------------------------------------------- // Draw @@ -88,13 +99,22 @@ int main(void) EndMode3D(); - // Draw UI - //GuiDropdownBox((Rectangle){ 10, 20, 240, 30 }, "text", &animIndex, editMode); + // Draw UI, select anim and playing speed + GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 1); + if (GuiDropdownBox((Rectangle){ 10, 10, 140, 24 }, TextJoin(animNames, animCount, ";"), + &animIndex, dropdownEditMode)) dropdownEditMode = !dropdownEditMode; - DrawText(TextFormat("FRAME SPEED: x%.1f", animFrameSpeed), 10, 40, 20, RED); + GuiSlider((Rectangle){ 260, 10, 500, 24 }, "FRAME SPEED: ", TextFormat("x%.1f", animFrameSpeed), + &animFrameSpeed, 0.1f, 2.0f); - DrawText("Use the LEFT/RIGHT mouse buttons to switch animation", 10, 10, 20, GRAY); - DrawText(TextFormat("Animation: %s", modelAnimations[animIndex].name), 10, GetScreenHeight() - 20, 10, DARKGRAY); + // Draw playing timeline with keyframes + GuiLabel((Rectangle){ 10, GetScreenHeight() - 64, GetScreenWidth() - 20, 24 }, + TextFormat("CURRENT FRAME: %.2f / %i", animFrameProgress, anims[animIndex].keyframeCount)); + GuiProgressBar((Rectangle){ 10, GetScreenHeight() - 40, GetScreenWidth() - 20, 24 }, NULL, NULL, + &animFrameProgress, 0.0f, (float)anims[animIndex].keyframeCount); + for (int i = 0; i < anims[animIndex].keyframeCount; i++) + DrawRectangle(10 + ((float)(GetScreenWidth() - 20)/(float)anims[animIndex].keyframeCount)*(float)i, + GetScreenHeight() - 40, 1, 24, BLUE); EndDrawing(); //---------------------------------------------------------------------------------- @@ -102,6 +122,7 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- + UnloadModelAnimations(anims, animCount); // Unload model animation UnloadModel(model); // Unload model and meshes/material CloseWindow(); // Close window and OpenGL context @@ -110,5 +131,3 @@ int main(void) return 0; } - - diff --git a/examples/models/models_animation_timing.png b/examples/models/models_animation_timing.png new file mode 100644 index 0000000000000000000000000000000000000000..4448fec67af1efcdda7c26d444ae2cea3aff867c GIT binary patch literal 23489 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Ν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 B^KJkD literal 0 HcmV?d00001 diff --git a/examples/models/models_animation_timming.png b/examples/models/models_animation_timming.png deleted file mode 100644 index 673994b7c4451ad3a8f431a9e92cf21406496fbc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22154 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 diff --git a/examples/models/models_loading.c b/examples/models/models_loading.c index 650b3126f..0672da047 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) -* - GLTF > Text/binary file format. Includes lot of information and it could -* also reference external files, raylib will try loading mesh and materials data +* if .obj references some .mtl materials file, it will be tried to be loaded +* - GLTF/GLB > Text/binary file formats. Includes lot of information and it could +* also reference external files, mesh and materials data will be tried to be loaded * - IQM > Binary file format. Includes mesh vertex data but also animation data, -* raylib can load .iqm animations +* meshes and animation data can be loaded * - 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: @@ -43,10 +43,10 @@ int main(void) // Define the camera to look into our 3d world Camera camera = { 0 }; camera.position = (Vector3){ 50.0f, 50.0f, 50.0f }; // Camera position - camera.target = (Vector3){ 0.0f, 10.0f, 0.0f }; // Camera looking at point + camera.target = (Vector3){ 0.0f, 12.0f, 0.0f }; // Camera looking at point camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) camera.fovy = 45.0f; // Camera field-of-view Y - camera.projection = CAMERA_PERSPECTIVE; // Camera mode type + camera.projection = CAMERA_PERSPECTIVE; // Camera mode type Model model = LoadModel("resources/models/obj/castle.obj"); // Load model Texture2D texture = LoadTexture("resources/models/obj/castle_diffuse.png"); // Load model texture @@ -61,8 +61,6 @@ int main(void) bool selected = false; // Selected object flag - DisableCursor(); // Limit cursor to relative movement inside the window - SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -71,7 +69,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera, CAMERA_FIRST_PERSON); + UpdateCamera(&camera, CAMERA_ORBITAL); // Load new models/textures on drag&drop if (IsFileDropped()) @@ -93,7 +91,10 @@ int main(void) bounds = GetMeshBoundingBox(model.meshes[0]); - // TODO: Move camera position from target enough distance to visualize model properly + // Move camera position from target enough distance to visualize model properly + camera.position.x = bounds.max.x + 10.0f; + camera.position.y = bounds.max.y + 10.0f; + camera.position.z = bounds.max.z + 10.0f; } else if (IsFileExtension(droppedFiles.paths[0], ".png")) // Texture file formats supported { diff --git a/examples/models/models_loading_gltf.c b/examples/models/models_loading_gltf.c index 7729da13f..f21207ba1 100644 --- a/examples/models/models_loading_gltf.c +++ b/examples/models/models_loading_gltf.c @@ -42,15 +42,17 @@ int main(void) camera.fovy = 45.0f; // Camera field-of-view Y camera.projection = CAMERA_PERSPECTIVE; // Camera projection type - // Load gltf model + // Load model Model model = LoadModel("resources/models/gltf/robot.glb"); - Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position + Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model world position - // Load gltf model animations - int animsCount = 0; - unsigned int animIndex = 0; - unsigned int animCurrentFrame = 0; - ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/robot.glb", &animsCount); + // Load model animations + int animCount = 0; + ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/robot.glb", &animCount); + + // Animation playing variables + unsigned int animIndex = 0; // Current animation playing + unsigned int animCurrentFrame = 0; // Current animation frame SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -63,13 +65,12 @@ int main(void) UpdateCamera(&camera, CAMERA_ORBITAL); // Select current animation - if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) animIndex = (animIndex + 1)%animsCount; - else if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) animIndex = (animIndex + animsCount - 1)%animsCount; + if (IsKeyPressed(KEY_RIGHT)) animIndex = (animIndex + 1)%animCount; + else if (IsKeyPressed(KEY_LEFT)) animIndex = (animIndex + animCount - 1)%animCount; // Update model animation - ModelAnimation anim = modelAnimations[animIndex]; - animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount; - UpdateModelAnimation(model, anim, animCurrentFrame); + animCurrentFrame = (animCurrentFrame + 1)%anims[animIndex].keyframeCount; + UpdateModelAnimation(model, anims[animIndex], (float)animCurrentFrame); //---------------------------------------------------------------------------------- // Draw @@ -79,12 +80,15 @@ int main(void) ClearBackground(RAYWHITE); BeginMode3D(camera); - DrawModel(model, position, 1.0f, WHITE); // Draw animated model + + DrawModel(model, position, 1.0f, WHITE); + DrawGrid(10, 1.0f); + EndMode3D(); - DrawText("Use the LEFT/RIGHT mouse buttons to switch animation", 10, 10, 20, GRAY); - DrawText(TextFormat("Animation: %s", anim.name), 10, GetScreenHeight() - 20, 10, DARKGRAY); + DrawText(TextFormat("Current animation: %s", anims[animIndex].name), 10, 40, 20, MAROON); + DrawText("Use the LEFT/RIGHT keys to switch animation", 10, 10, 20, GRAY); EndDrawing(); //---------------------------------------------------------------------------------- @@ -92,7 +96,8 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - UnloadModel(model); // Unload model and meshes/material + UnloadModelAnimations(anims, animCount); // Unload model animations data + UnloadModel(model); // Unload model CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_loading_gltf.png b/examples/models/models_loading_gltf.png index ce2a85284077b0cce9fbfb8b63f326ca48013be8..9aa3e23501db07da0c1f0f630574d9fdd1420b39 100644 GIT binary patch literal 22136 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%0DXCk@=8pxu^9@OA5hv! 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 diff --git a/examples/models/models_loading_iqm.c b/examples/models/models_loading_iqm.c index 97d97ad40..0eab898a4 100644 --- a/examples/models/models_loading_iqm.c +++ b/examples/models/models_loading_iqm.c @@ -8,17 +8,15 @@ * * Example contributed by Culacant (@culacant) and reviewed by Ramon Santamaria (@raysan5) * +* NOTES: To export an IQM 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 the models is +* set to 0; scaling can be set from the export menu +* * 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) 2019-2025 Culacant (@culacant) and Ramon Santamaria (@raysan5) * -******************************************************************************************** -* -* 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 -* ********************************************************************************************/ #include "raylib.h" @@ -38,7 +36,7 @@ int main(void) // Define the camera to look into our 3d world Camera camera = { 0 }; camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position - camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point + camera.target = (Vector3){ 0.0f, 4.0f, 0.0f }; // Camera looking at point camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) camera.fovy = 45.0f; // Camera field-of-view Y camera.projection = CAMERA_PERSPECTIVE; // Camera mode type @@ -46,15 +44,16 @@ int main(void) Model model = LoadModel("resources/models/iqm/guy.iqm"); // Load the animated model mesh and basic data Texture2D texture = LoadTexture("resources/models/iqm/guytex.png"); // Load model texture and set material SetMaterialTexture(&model.materials[0], MATERIAL_MAP_DIFFUSE, texture); // Set model material map texture - - Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position + Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position // Load animation data - int animsCount = 0; - ModelAnimation *anims = LoadModelAnimations("resources/models/iqm/guyanim.iqm", &animsCount); - float animFrameCounter = 0; + int animCount = 0; + ModelAnimation *anims = LoadModelAnimations("resources/models/iqm/guyanim.iqm", &animCount); + + // Animation playing variables + unsigned int animIndex = 0; // Current animation playing + float animCurrentFrame = 0.0f; // Current animation frame (supporting interpolated frames) - DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -66,9 +65,9 @@ int main(void) UpdateCamera(&camera, CAMERA_ORBITAL); // Play animation when spacebar is held down - animFrameCounter += 1.0f; - UpdateModelAnimation(model, anims[0], animFrameCounter); - if (animFrameCounter >= anims[0].keyframeCount) animFrameCounter = 0; + animCurrentFrame += 1.0f; + UpdateModelAnimation(model, anims[0], animCurrentFrame); + if (animCurrentFrame >= anims[0].keyframeCount) animCurrentFrame = 0; //---------------------------------------------------------------------------------- // Draw @@ -81,16 +80,11 @@ int main(void) DrawModelEx(model, position, (Vector3){ 1.0f, 0.0f, 0.0f }, -90.0f, (Vector3){ 1.0f, 1.0f, 1.0f }, WHITE); - for (int i = 0; i < model.skeleton.boneCount; i++) - { - //DrawCube(anims[0].keyframePoses[animFrameCounter][i].translation, 0.2f, 0.2f, 0.2f, RED); - } - - DrawGrid(10, 1.0f); // Draw a grid + DrawGrid(10, 1.0f); EndMode3D(); - DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, 10, 20, MAROON); + DrawText(TextFormat("Current animation: %s", anims[animIndex].name), 10, 10, 20, MAROON); DrawText("(c) Guy IQM 3D model by @culacant", screenWidth - 200, screenHeight - 20, 10, GRAY); EndDrawing(); @@ -99,9 +93,9 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(texture); // Unload texture - UnloadModelAnimations(anims, animsCount); // Unload model animations data - UnloadModel(model); // Unload model + UnloadTexture(texture); // Unload texture + UnloadModelAnimations(anims, animCount); // Unload model animations data + UnloadModel(model); // Unload model CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_loading_iqm.png b/examples/models/models_loading_iqm.png index 57e39dd3e1a40cb357bd8fcc0f4ad69bb695da21..560cb3580ae3d2542d05386adbe03177461e1c28 100644 GIT binary patch literal 59771 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 a76yiSu3=w}G)%K&00K`}KbLh*2~7aM4-;nq literal 67020 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