/* nuklear - public domain */ #include #include #include #include #include #include #include #include #include #include #include #include /* This demo uses "main callbacks" which are new in SDL3 * Those provide highly portable entry point and event loop for the app * see: https://wiki.libsdl.org/SDL3/README-main-functions * */ #define SDL_MAIN_USE_CALLBACKS #include #include "./camera.c" /* =============================================================== * * CONFIG * * ===============================================================*/ /* optional: sdl3_renderer does not need any of these defines * (but some examples might need them, so be careful) */ #define NK_INCLUDE_STANDARD_VARARGS #define NK_INCLUDE_STANDARD_IO /* note that sdl3_renderer comes with nk_sdl_style_set_debug_font() * so you may wish to use that instead of font baking */ #define NK_INCLUDE_FONT_BAKING #define NK_INCLUDE_DEFAULT_FONT /* note that sdl3_renderer comes with nk_sdl_allocator() * and you probably want to use that allocator instead of the default ones */ /*#define NK_INCLUDE_DEFAULT_ALLOCATOR*/ /* mandatory: sdl3_renderer depends on those defines */ #define NK_INCLUDE_COMMAND_USERDATA #define NK_INCLUDE_VERTEX_BUFFER_OUTPUT /* We can re-use the types provided by SDL which are extremely portable, * so there is no need for Nuklear to detect those on its own */ /*#define NK_INCLUDE_FIXED_TYPES*/ #ifndef NK_INCLUDE_FIXED_TYPES #define NK_INT8 Sint8 #define NK_UINT8 Uint8 #define NK_INT16 Sint16 #define NK_UINT16 Uint16 #define NK_INT32 Sint32 #define NK_UINT32 Uint32 /* SDL guarantees 'uintptr_t' typedef */ #define NK_SIZE_TYPE uintptr_t #define NK_POINTER_TYPE uintptr_t #endif /* We can reuse the `bool` symbol because SDL3 guarantees its existence */ /*#define NK_INCLUDE_STANDARD_BOOL*/ #ifndef NK_INCLUDE_STANDARD_BOOL #define NK_BOOL bool #endif /* We can re-use various portable libc functions provided by SDL */ #define NK_ASSERT(condition) SDL_assert(condition) #define NK_STATIC_ASSERT(exp) SDL_COMPILE_TIME_ASSERT(, exp) #define NK_MEMSET(dst, c, len) SDL_memset(dst, c, len) #define NK_MEMCPY(dst, src, len) SDL_memcpy(dst, src, len) #define NK_VSNPRINTF(s, n, f, a) SDL_vsnprintf(s, n, f, a) #define NK_STRTOD(str, endptr) SDL_strtod(str, endptr) /* SDL3 does not provide "dtoa" (only integer versions) * but we can emulate it with SDL_snprintf */ static char* nk_sdl_dtoa(char *str, double d); #define NK_DTOA(str, d) nk_sdl_dtoa(str, d) /* SDL can also provide us with math functions, but beware that Nuklear's own * implementation can be slightly faster at the cost of some precision */ #define NK_INV_SQRT(f) (1.0f / SDL_sqrtf(f)) #define NK_SIN(f) SDL_sinf(f) #define NK_COS(f) SDL_cosf(f) /* HACK: Nuklear pulls two stb libraries in order to use font baking * those libraries pull in some libc headers internally, creating a linkage dependency, * so you’ll most likely want to use SDL symbols instead */ #define STBTT_ifloor(x) ((int)SDL_floor(x)) #define STBTT_iceil(x) ((int)SDL_ceil(x)) #define STBTT_sqrt(x) SDL_sqrt(x) #define STBTT_pow(x,y) SDL_pow(x,y) #define STBTT_fmod(x,y) SDL_fmod(x,y) #define STBTT_cos(x) SDL_cosf(x) #define STBTT_acos(x) SDL_acos(x) #define STBTT_fabs(x) SDL_fabs(x) #define STBTT_assert(x) SDL_assert(x) #define STBTT_strlen(x) SDL_strlen(x) #define STBTT_memcpy SDL_memcpy #define STBTT_memset SDL_memset #define stbtt_uint8 Uint8 #define stbtt_int8 Sint8 #define stbtt_uint16 Uint16 #define stbtt_int16 Sint16 #define stbtt_uint32 Uint32 #define stbtt_int32 Sint32 #define STBRP_SORT SDL_qsort #define STBRP_ASSERT SDL_assert /* There is no need to define STBTT_malloc/STBTT_free macros * Nuklear will define those to user-provided nk_allocator */ #define NK_IMPLEMENTATION #include "../nuklear/nuklear.h" #define NK_SDL3_RENDERER_IMPLEMENTATION #include "nuklear_sdl3_renderer.h" #define WINDOW_WIDTH 1200 #define WINDOW_HEIGHT 800 /* =============================================================== * * DEMO * * ===============================================================*/ struct nk_sdl_app { SDL_Window* window; SDL_Renderer* renderer; struct nk_context * ctx; struct nk_colorf bg; enum nk_anti_aliasing AA; camera_t* camera; }; static SDL_AppResult nk_sdl_fail() { SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Error: %s", SDL_GetError()); return SDL_APP_FAILURE; } SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) { struct nk_sdl_app* app; struct nk_context* ctx; camera_t * camera; float font_scale; NK_UNUSED(argc); NK_UNUSED(argv); if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) { return nk_sdl_fail(); } app = SDL_malloc(sizeof(*app)); if (app == NULL) { return nk_sdl_fail(); } app->camera = malloc(sizeof(*camera)); app->camera->latest_frame = NULL; get_camera_devices(app->camera); if (!SDL_CreateWindowAndRenderer("Nuklear: SDL3 Renderer", WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_RESIZABLE, &app->window, &app->renderer)) { SDL_free(app); return nk_sdl_fail(); } *appstate = app; if (!SDL_SetRenderVSync(app->renderer, 1)) { SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "SDL_SetRenderVSync failed: %s", SDL_GetError()); } app->bg.r = 0.10f; app->bg.g = 0.18f; app->bg.b = 0.24f; app->bg.a = 1.0f; font_scale = 1; { /* This scaling logic was kept simple for the demo purpose. * On some platforms, this might not be the exact scale * that you want to use. For more information, see: * https://wiki.libsdl.org/SDL3/README-highdpi */ const float scale = SDL_GetWindowDisplayScale(app->window); SDL_SetRenderScale(app->renderer, scale, scale); font_scale = scale; } ctx = nk_sdl_init(app->window, app->renderer, nk_sdl_allocator()); app->ctx = ctx; #if 0 { /* If you don't want to use advanced Nuklear font baking API * you can use simple ASCII debug font provided by SDL * just change the `#if 0` above to `#if 1` */ nk_sdl_style_set_debug_font(ctx); /* Note that since debug font is extremely small (only 8x8 pixels), * scaling it does not make much sense. The font would appear blurry. */ NK_UNUSED(font_scale); /* You may wish to change a few style options, here are few recommendations: */ ctx->style.button.rounding = 0.0f; ctx->style.menu_button.rounding = 0.0f; ctx->style.property.rounding = 0.0f; ctx->style.property.border = 0.0f; ctx->style.option.border = -1.0f; ctx->style.checkbox.border = -1.0f; ctx->style.property.dec_button.border = -2.0f; ctx->style.property.inc_button.border = -2.0f; ctx->style.tab.tab_minimize_button.border = -2.0f; ctx->style.tab.tab_maximize_button.border = -2.0f; ctx->style.tab.node_minimize_button.border = -2.0f; ctx->style.tab.node_maximize_button.border = -2.0f; ctx->style.checkbox.spacing = 5.0f; /* It's better to disable anti-aliasing when using small fonts */ app->AA = NK_ANTI_ALIASING_OFF; } #else { struct nk_font_atlas *atlas; struct nk_font_config config = nk_font_config(0); struct nk_font *font; /* set up the font atlas and add desired font; note that font sizes are * multiplied by font_scale to produce better results at higher DPIs */ atlas = nk_sdl_font_stash_begin(ctx); font = nk_font_atlas_add_default(atlas, 16 * font_scale, &config); /*font = nk_font_atlas_add_from_file(atlas, "../../../extra_font/DroidSans.ttf", 14 * font_scale, &config);*/ /*font = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Roboto-Regular.ttf", 16 * font_scale, &config);*/ /*font = nk_font_atlas_add_from_file(atlas, "../../../extra_font/kenvector_future_thin.ttf", 13 * font_scale, &config);*/ /*font = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyClean.ttf", 12 * font_scale, &config);*/ /*font = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyTiny.ttf", 10 * font_scale, &config);*/ /*font = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Cousine-Regular.ttf", 13 * font_scale, &config);*/ nk_sdl_font_stash_end(ctx); /* this hack makes the font appear to be scaled down to the desired * size and is only necessary when font_scale > 1 */ font->handle.height /= font_scale; /*nk_style_load_all_cursors(ctx, atlas->cursors);*/ nk_style_set_font(ctx, &font->handle); app->AA = NK_ANTI_ALIASING_ON; } #endif nk_input_begin(ctx); return SDL_APP_CONTINUE; } SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event* event) { struct nk_sdl_app* app = (struct nk_sdl_app*)appstate; switch (event->type) { case SDL_EVENT_QUIT: return SDL_APP_SUCCESS; case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: /* You may wish to rescale the renderer and Nuklear during this event. * Without this the UI and Font could appear too small or too big. * This is not handled by the demo in order to keep it simple, * but you may wish to re-bake the Font whenever this happens. */ SDL_Log("Unhandled scale event! Nuklear may appear blurry"); return SDL_APP_CONTINUE; } /* Remember to always rescale the event coordinates, * if your renderer uses custom scale. */ SDL_ConvertEventToRenderCoordinates(app->renderer, event); nk_sdl_handle_event(app->ctx, event); return SDL_APP_CONTINUE; } SDL_AppResult SDL_AppIterate(void *appstate) { struct nk_sdl_app* app = (struct nk_sdl_app*)appstate; struct nk_context* ctx = app->ctx; int wwidth = 0; int wheight = 0; static char text[9][64]; static int text_len[9]; SDL_GetWindowSizeInPixels(app->window, &wwidth, &wheight); nk_input_end(ctx); /* GUI */ if (nk_begin(ctx, "Recent", nk_rect(0, 0, 300, wheight), NK_WINDOW_TITLE|NK_TEXT_CENTERED)) { } nk_end(ctx); /* GUI */ if (nk_begin(ctx, "Main", nk_rect(300, 0, wwidth-200-300, wheight), NK_WINDOW_BORDER)) { nk_layout_row_dynamic(ctx, 30, 2); nk_label(ctx, "Loaf Key", 0); nk_edit_string(ctx, NK_EDIT_SIMPLE, text[0], &text_len[0], 64, nk_filter_default); if(nk_button_label(ctx, "Action!")) { start_camera_stream(app->camera); } if(nk_button_label(ctx, "Cut!")) { stop_camera_stream(app->camera); } nk_spacer(ctx); nk_spacer(ctx); } if (nk_begin(ctx, "Main", nk_rect(300, 0, wwidth-200-300, wheight), NK_WINDOW_BORDER)) { if (app->camera->latest_frame != NULL && app->camera->latest_frame->data_bytes > 0) { const uvc_frame_t * frame = app->camera->latest_frame; printf("Texture WxH: %dx%d, Bytes: %zd\n", frame->width, frame->height, frame->data_bytes); SDL_Texture * texture = SDL_CreateTexture(app->renderer, SDL_PIXELFORMAT_MJPG, SDL_TEXTUREACCESS_STREAMING, frame->width, frame->height); if (texture == NULL) { puts("ERROR Creating Texture!"); const char * err = SDL_GetError(); puts(err); puts("---"); } bool upres = SDL_UpdateTexture(texture, NULL, frame->data, frame->data_bytes); if (!upres) { puts("Failed to add frame to Texture"); const char * err = SDL_GetError(); puts(err); puts("---"); } struct nk_image camera_image; camera_image = nk_image_ptr(texture); struct nk_command_buffer * canvas = nk_window_get_canvas(ctx); struct nk_rect total_space = nk_window_get_content_region(ctx); const struct nk_color grid_color = nk_rgba(255, 255, 255, 255); nk_draw_image(canvas, total_space, &camera_image, grid_color); } } nk_end(ctx); SDL_SetRenderDrawColorFloat(app->renderer, app->bg.r, app->bg.g, app->bg.b, app->bg.a); SDL_RenderClear(app->renderer); nk_sdl_render(ctx, app->AA); nk_sdl_update_TextInput(ctx); /* show if TextInput is active for debug purpose. Feel free to remove this. */ SDL_SetRenderDrawColor(app->renderer, 0xFF, 0xFF, 0xFF, 0xFF); // SDL_RenderDebugTextFormat(app->renderer, 10, 10, "APP WINDOW W:%d, H:%d", wwidth, wheight); SDL_RenderPresent(app->renderer); nk_input_begin(ctx); return SDL_APP_CONTINUE; } void SDL_AppQuit(void* appstate, SDL_AppResult result) { struct nk_sdl_app* app = (struct nk_sdl_app*)appstate; NK_UNUSED(result); if (app) { close_camera(app->camera); nk_input_end(app->ctx); nk_sdl_shutdown(app->ctx); SDL_DestroyRenderer(app->renderer); SDL_DestroyWindow(app->window); SDL_free(app); } } static char* nk_sdl_dtoa(char *str, double d) { NK_ASSERT(str); if (!str) return NULL; (void)SDL_snprintf(str, 99999, "%.17g", d); return str; }