From dab6197d1f2eb4041cecad4b04fa2b4bf67a32b4 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Mon, 27 Jul 2026 20:40:53 -0400 Subject: [PATCH] Harden NULL handling in update and font paths --- README.md | 3 +++ include/raylib-nuklear.h | 40 +++++++++++++++++++++++++++----------- test/raylib-nuklear-test.c | 32 ++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 006e124..280ac1b 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,9 @@ float GetNuklearScaling(struct nk_context * ctx); // Retrieves the sc See the [Nuklear API documenation](https://immediate-mode-ui.github.io/Nuklear/doc/nuklear.html) for more how to use Nuklear. +> [!WARNING] +> *raylib-nuklear* stores its own state (scaling, insert mode, double-click tracking) in the context's user data. Calling `nk_set_user_data()` replaces it, which disables scaling, insert mode and double-click detection. Save `ctx->userdata` beforehand and restore it before `UnloadNuklear()`. + ## Configuration The following macros can be defined before including `raylib-nuklear.h` to tune behavior: diff --git a/include/raylib-nuklear.h b/include/raylib-nuklear.h index 4ea7369..74d0c85 100644 --- a/include/raylib-nuklear.h +++ b/include/raylib-nuklear.h @@ -468,10 +468,19 @@ NK_API Font LoadFontFromNuklear(int size) { // Decode base85 -> compressed binary -> raw TTF int compressed_size = (((int)nk_strlen(nk_proggy_clean_ttf_compressed_data_base85) + 4) / 5) * 4; unsigned char *compressed_data = (unsigned char*)MemAlloc((unsigned int)compressed_size); + if (compressed_data == NULL) { + TraceLog(LOG_ERROR, "NUKLEAR: Failed to allocate the default font data"); + return CLITERAL(Font) {0}; + } nk_decode_85(compressed_data, (const unsigned char*)nk_proggy_clean_ttf_compressed_data_base85); unsigned int ttf_size = nk_decompress_length(compressed_data); unsigned char *ttf_data = (unsigned char*)MemAlloc(ttf_size); + if (ttf_data == NULL) { + TraceLog(LOG_ERROR, "NUKLEAR: Failed to allocate the default font data"); + MemFree(compressed_data); + return CLITERAL(Font) {0}; + } nk_decompress(ttf_data, compressed_data, (unsigned int)compressed_size); MemFree(compressed_data); @@ -962,9 +971,11 @@ nk_raylib_input_keyboard(struct nk_context * ctx) nk_input_key(ctx, NK_KEY_TAB, IsKeyDown(KEY_TAB)); NuklearUserData* userData = (NuklearUserData*)ctx->userdata.ptr; - if (IsKeyPressed(KEY_INSERT)) userData->insert_mode = !userData->insert_mode; - nk_input_key(ctx, NK_KEY_TEXT_INSERT_MODE, userData->insert_mode); - nk_input_key(ctx, NK_KEY_TEXT_REPLACE_MODE, !userData->insert_mode); + if (userData != NULL) { + if (IsKeyPressed(KEY_INSERT)) userData->insert_mode = !userData->insert_mode; + nk_input_key(ctx, NK_KEY_TEXT_INSERT_MODE, userData->insert_mode); + nk_input_key(ctx, NK_KEY_TEXT_REPLACE_MODE, !userData->insert_mode); + } int code; while ((code = GetCharPressed()) != 0) { @@ -1010,15 +1021,17 @@ nk_raylib_input_mouse(struct nk_context * ctx) // Double Click { NuklearUserData* userData = (NuklearUserData*)ctx->userdata.ptr; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { - double now = GetTime(); - userData->double_clicking = now - userData->last_left_press <= RAYLIB_NUKLEAR_DOUBLE_CLICK_THRESHOLD; - userData->last_left_press = now; - } - else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) { - userData->double_clicking = false; + if (userData != NULL) { + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { + double now = GetTime(); + userData->double_clicking = now - userData->last_left_press <= RAYLIB_NUKLEAR_DOUBLE_CLICK_THRESHOLD; + userData->last_left_press = now; + } + else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) { + userData->double_clicking = false; + } + nk_input_button(ctx, NK_BUTTON_DOUBLE, mouseX, mouseY, userData->double_clicking); } - nk_input_button(ctx, NK_BUTTON_DOUBLE, mouseX, mouseY, userData->double_clicking); } // Mouse Wheel @@ -1049,6 +1062,11 @@ UpdateNuklear(struct nk_context * ctx) NK_API void UpdateNuklearEx(struct nk_context * ctx, float deltaTime) { + // Skip updating if it's not set. + if (ctx == NULL) { + return; + } + // Update the time that has changed since last frame. ctx->delta_time_seconds = deltaTime; diff --git a/test/raylib-nuklear-test.c b/test/raylib-nuklear-test.c index 6cf255b..e439be9 100644 --- a/test/raylib-nuklear-test.c +++ b/test/raylib-nuklear-test.c @@ -202,6 +202,38 @@ int main(int argc, char *argv[]) { UnloadNuklear(ctx); } + // A NULL context must not crash any of the public entry points. + { + UpdateNuklear(NULL); + UpdateNuklearEx(NULL, 1.0f / 60.0f); + DrawNuklear(NULL); + SetNuklearScaling(NULL, 2.0f); + AssertEqual(GetNuklearScaling(NULL), 1.0f); + UnloadNuklear(NULL); + } + + // Replacing the context user data with nk_set_user_data() must not crash. + { + ctx = InitNuklear(10); + Assert(ctx); + + nk_handle original = ctx->userdata; + nk_set_user_data(ctx, nk_handle_ptr(NULL)); + + UpdateNuklear(ctx); + UpdateNuklearEx(ctx, 1.0f / 60.0f); + AssertEqual(GetNuklearScaling(ctx), 1.0f); + + BeginDrawing(); + ClearBackground(RAYWHITE); + DrawNuklear(ctx); + EndDrawing(); + + // Restore the internal user data so that it's still freed on unload. + nk_set_user_data(ctx, original); + UnloadNuklear(ctx); + } + // RAYLIB_NUKLEAR_VERSION macros Assert(RAYLIB_NUKLEAR_VERSION_MAJOR >= 1); Assert(RAYLIB_NUKLEAR_VERSION_MINOR >= 0);