|
@@ -0,0 +1,707 @@
|
|
|
|
|
+/* nuklear - public domain */
|
|
|
|
|
+
|
|
|
|
|
+/*
|
|
|
|
|
+ * ==============================================================
|
|
|
|
|
+ *
|
|
|
|
|
+ * API
|
|
|
|
|
+ *
|
|
|
|
|
+ * ===============================================================
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+#ifndef NK_SDL3_RENDERER_H_
|
|
|
|
|
+#define NK_SDL3_RENDERER_H_
|
|
|
|
|
+
|
|
|
|
|
+#if SDL_MAJOR_VERSION < 3
|
|
|
|
|
+ #error "nk_sdl3_renderer requires at least SDL 3.0.0"
|
|
|
|
|
+#endif
|
|
|
|
|
+#ifndef NK_INCLUDE_COMMAND_USERDATA
|
|
|
|
|
+ #error "nk_sdl3_renderer requires the NK_INCLUDE_COMMAND_USERDATA define"
|
|
|
|
|
+#endif
|
|
|
|
|
+#ifndef NK_INCLUDE_VERTEX_BUFFER_OUTPUT
|
|
|
|
|
+ #error "nk_sdl3_renderer requires the NK_INCLUDE_VERTEX_BUFFER_OUTPUT define"
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+/* We have to redefine it because demos do not include any headers
|
|
|
|
|
+ * This is the same default value as the one from "src/nuklear_internal.h" */
|
|
|
|
|
+#ifndef NK_BUFFER_DEFAULT_INITIAL_SIZE
|
|
|
|
|
+ #define NK_BUFFER_DEFAULT_INITIAL_SIZE (4*1024)
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+NK_API struct nk_context* nk_sdl_init(SDL_Window *win, SDL_Renderer *renderer, struct nk_allocator allocator);
|
|
|
|
|
+#ifdef NK_INCLUDE_FONT_BAKING
|
|
|
|
|
+NK_API struct nk_font_atlas* nk_sdl_font_stash_begin(struct nk_context* ctx);
|
|
|
|
|
+NK_API void nk_sdl_font_stash_end(struct nk_context* ctx);
|
|
|
|
|
+#endif
|
|
|
|
|
+NK_API int nk_sdl_handle_event(struct nk_context* ctx, SDL_Event *evt);
|
|
|
|
|
+NK_API void nk_sdl_render(struct nk_context* ctx, enum nk_anti_aliasing);
|
|
|
|
|
+NK_API void nk_sdl_update_TextInput(struct nk_context* ctx);
|
|
|
|
|
+NK_API void nk_sdl_shutdown(struct nk_context* ctx);
|
|
|
|
|
+NK_API nk_handle nk_sdl_get_userdata(struct nk_context* ctx);
|
|
|
|
|
+NK_API void nk_sdl_set_userdata(struct nk_context* ctx, nk_handle userdata);
|
|
|
|
|
+NK_API void nk_sdl_style_set_debug_font(struct nk_context* ctx);
|
|
|
|
|
+NK_API struct nk_allocator nk_sdl_allocator(void);
|
|
|
|
|
+
|
|
|
|
|
+#endif /* NK_SDL3_RENDERER_H_ */
|
|
|
|
|
+
|
|
|
|
|
+/*
|
|
|
|
|
+ * ==============================================================
|
|
|
|
|
+ *
|
|
|
|
|
+ * IMPLEMENTATION
|
|
|
|
|
+ *
|
|
|
|
|
+ * ===============================================================
|
|
|
|
|
+ */
|
|
|
|
|
+#ifdef NK_SDL3_RENDERER_IMPLEMENTATION
|
|
|
|
|
+#ifndef NK_SDL3_RENDERER_IMPLEMENTATION_ONCE
|
|
|
|
|
+#define NK_SDL3_RENDERER_IMPLEMENTATION_ONCE
|
|
|
|
|
+
|
|
|
|
|
+#ifndef NK_SDL_DOUBLE_CLICK_LO
|
|
|
|
|
+#define NK_SDL_DOUBLE_CLICK_LO 0.02
|
|
|
|
|
+#endif
|
|
|
|
|
+#ifndef NK_SDL_DOUBLE_CLICK_HI
|
|
|
|
|
+#define NK_SDL_DOUBLE_CLICK_HI 0.2
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+struct nk_sdl_device {
|
|
|
|
|
+ struct nk_buffer cmds;
|
|
|
|
|
+ struct nk_draw_null_texture tex_null;
|
|
|
|
|
+ SDL_Texture *font_tex;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+struct nk_sdl_vertex {
|
|
|
|
|
+ float position[2];
|
|
|
|
|
+ float uv[2];
|
|
|
|
|
+ float col[4];
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+struct nk_sdl {
|
|
|
|
|
+ SDL_Window *win;
|
|
|
|
|
+ SDL_Renderer *renderer;
|
|
|
|
|
+ struct nk_user_font* debug_font;
|
|
|
|
|
+ struct nk_sdl_device ogl;
|
|
|
|
|
+ struct nk_context ctx;
|
|
|
|
|
+#ifdef NK_INCLUDE_FONT_BAKING
|
|
|
|
|
+ struct nk_font_atlas atlas;
|
|
|
|
|
+#endif
|
|
|
|
|
+ struct nk_allocator allocator;
|
|
|
|
|
+ nk_handle userdata;
|
|
|
|
|
+ Uint64 last_left_click;
|
|
|
|
|
+ Uint64 last_render;
|
|
|
|
|
+ bool insert_toggle;
|
|
|
|
|
+ bool edit_was_active;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+NK_API nk_handle
|
|
|
|
|
+nk_sdl_get_userdata(struct nk_context* ctx) {
|
|
|
|
|
+ struct nk_sdl* sdl;
|
|
|
|
|
+ NK_ASSERT(ctx);
|
|
|
|
|
+ sdl = (struct nk_sdl*)ctx->userdata.ptr;
|
|
|
|
|
+ NK_ASSERT(sdl);
|
|
|
|
|
+ return sdl->userdata;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+NK_API void
|
|
|
|
|
+nk_sdl_set_userdata(struct nk_context* ctx, nk_handle userdata) {
|
|
|
|
|
+ struct nk_sdl* sdl;
|
|
|
|
|
+ NK_ASSERT(ctx);
|
|
|
|
|
+ sdl = (struct nk_sdl*)ctx->userdata.ptr;
|
|
|
|
|
+ NK_ASSERT(sdl);
|
|
|
|
|
+ sdl->userdata = userdata;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+NK_INTERN void *
|
|
|
|
|
+nk_sdl_alloc(nk_handle user, void *old, nk_size size)
|
|
|
|
|
+{
|
|
|
|
|
+ NK_UNUSED(user);
|
|
|
|
|
+ /* FIXME: nk_sdl_alloc should use SDL_realloc here, not SDL_malloc
|
|
|
|
|
+ * but this could cause a double-free due to bug within Nuklear, see:
|
|
|
|
|
+ * https://github.com/Immediate-Mode-UI/Nuklear/issues/768
|
|
|
|
|
+ * */
|
|
|
|
|
+#if 0
|
|
|
|
|
+ return SDL_realloc(old, size);
|
|
|
|
|
+#else
|
|
|
|
|
+ NK_UNUSED(old);
|
|
|
|
|
+ return SDL_malloc(size);
|
|
|
|
|
+#endif
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+NK_INTERN void
|
|
|
|
|
+nk_sdl_free(nk_handle user, void *old)
|
|
|
|
|
+{
|
|
|
|
|
+ NK_UNUSED(user);
|
|
|
|
|
+ SDL_free(old);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+NK_API struct nk_allocator
|
|
|
|
|
+nk_sdl_allocator()
|
|
|
|
|
+{
|
|
|
|
|
+ struct nk_allocator allocator;
|
|
|
|
|
+ allocator.userdata.ptr = 0;
|
|
|
|
|
+ allocator.alloc = nk_sdl_alloc;
|
|
|
|
|
+ allocator.free = nk_sdl_free;
|
|
|
|
|
+ return allocator;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+NK_INTERN void
|
|
|
|
|
+nk_sdl_device_upload_atlas(struct nk_context* ctx, const void *image, int width, int height)
|
|
|
|
|
+{
|
|
|
|
|
+ struct nk_sdl* sdl;
|
|
|
|
|
+ NK_ASSERT(ctx);
|
|
|
|
|
+ NK_ASSERT(image);
|
|
|
|
|
+ NK_ASSERT(width > 0);
|
|
|
|
|
+ NK_ASSERT(height > 0);
|
|
|
|
|
+
|
|
|
|
|
+ sdl = (struct nk_sdl*)ctx->userdata.ptr;
|
|
|
|
|
+ NK_ASSERT(sdl);
|
|
|
|
|
+
|
|
|
|
|
+ /* Clean up if the texture already exists. */
|
|
|
|
|
+ if (sdl->ogl.font_tex != NULL) {
|
|
|
|
|
+ SDL_DestroyTexture(sdl->ogl.font_tex);
|
|
|
|
|
+ sdl->ogl.font_tex = NULL;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ sdl->ogl.font_tex = SDL_CreateTexture(sdl->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, width, height);
|
|
|
|
|
+ NK_ASSERT(sdl->ogl.font_tex);
|
|
|
|
|
+ SDL_UpdateTexture(sdl->ogl.font_tex, NULL, image, 4 * width);
|
|
|
|
|
+ SDL_SetTextureBlendMode(sdl->ogl.font_tex, SDL_BLENDMODE_BLEND);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+NK_API void
|
|
|
|
|
+nk_sdl_update_TextInput(struct nk_context* ctx)
|
|
|
|
|
+{
|
|
|
|
|
+ struct nk_sdl* sdl;
|
|
|
|
|
+ bool active;
|
|
|
|
|
+ NK_ASSERT(ctx);
|
|
|
|
|
+ sdl = (struct nk_sdl*)ctx->userdata.ptr;
|
|
|
|
|
+ NK_ASSERT(sdl);
|
|
|
|
|
+
|
|
|
|
|
+ /* Determine if Nuklear is using any top-level "edit" widget.
|
|
|
|
|
+ * Popups take higher priority because they block any incomming input.
|
|
|
|
|
+ * This will not work, if the widget is not updating context state properly. */
|
|
|
|
|
+ if (!ctx->active)
|
|
|
|
|
+ active = false;
|
|
|
|
|
+ else if (ctx->active->popup.win)
|
|
|
|
|
+ active = ctx->active->popup.win->edit.active;
|
|
|
|
|
+ else
|
|
|
|
|
+ active = ctx->active->edit.active;
|
|
|
|
|
+
|
|
|
|
|
+ /* decide, if TextInputActive should be unchanged/stoped/started
|
|
|
|
|
+ * and change its state accordingly for owned SDL Window */
|
|
|
|
|
+ if (active != sdl->edit_was_active)
|
|
|
|
|
+ {
|
|
|
|
|
+ const bool window_edit_active = SDL_TextInputActive(sdl->win);
|
|
|
|
|
+
|
|
|
|
|
+ /* If you ever hit this check, it means that the demo and your app
|
|
|
|
|
+ * (or something else) are all trying to manage TextInputActive state.
|
|
|
|
|
+ * This can cause subtle bugs where the state won't be what you expect.
|
|
|
|
|
+ * You can safely remove this assert and the demo will keep working,
|
|
|
|
|
+ * but make sure it does not cause any issues for you */
|
|
|
|
|
+ NK_ASSERT(window_edit_active == sdl->edit_was_active && "something else changed TextInputActive state for this Window");
|
|
|
|
|
+
|
|
|
|
|
+ if (!window_edit_active && !sdl->edit_was_active && active)
|
|
|
|
|
+ SDL_StartTextInput(sdl->win);
|
|
|
|
|
+ else if (window_edit_active && sdl->edit_was_active && !active)
|
|
|
|
|
+ SDL_StopTextInput(sdl->win);
|
|
|
|
|
+ sdl->edit_was_active = active;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* FIXME:
|
|
|
|
|
+ * for full SDL3 integration, you also need to find current edit widget
|
|
|
|
|
+ * bounds and the text cursor offset, and pass this data into SDL_SetTextInputArea.
|
|
|
|
|
+ * This is currently not possible to do safely as Nuklear does not support it.
|
|
|
|
|
+ * https://wiki.libsdl.org/SDL3/SDL_SetTextInputArea
|
|
|
|
|
+ * https://github.com/Immediate-Mode-UI/Nuklear/pull/857
|
|
|
|
|
+ */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+NK_API void
|
|
|
|
|
+nk_sdl_render(struct nk_context* ctx, enum nk_anti_aliasing AA)
|
|
|
|
|
+{
|
|
|
|
|
+ /* setup global state */
|
|
|
|
|
+ struct nk_sdl* sdl;
|
|
|
|
|
+ NK_ASSERT(ctx);
|
|
|
|
|
+ sdl = (struct nk_sdl*)ctx->userdata.ptr;
|
|
|
|
|
+ NK_ASSERT(sdl);
|
|
|
|
|
+
|
|
|
|
|
+ { /* setup internal delta time that Nuklear needs for animations */
|
|
|
|
|
+ const Uint64 ticks = SDL_GetTicks();
|
|
|
|
|
+ ctx->delta_time_seconds = (float)(ticks - sdl->last_render) / 1000.0f;
|
|
|
|
|
+ sdl->last_render = ticks;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ {
|
|
|
|
|
+ SDL_Rect saved_clip;
|
|
|
|
|
+ bool clipping_enabled;
|
|
|
|
|
+ int vs = sizeof(struct nk_sdl_vertex);
|
|
|
|
|
+ size_t vp = NK_OFFSETOF(struct nk_sdl_vertex, position);
|
|
|
|
|
+ size_t vt = NK_OFFSETOF(struct nk_sdl_vertex, uv);
|
|
|
|
|
+ size_t vc = NK_OFFSETOF(struct nk_sdl_vertex, col);
|
|
|
|
|
+
|
|
|
|
|
+ /* convert from command queue into draw list and draw to screen */
|
|
|
|
|
+ const struct nk_draw_command *cmd;
|
|
|
|
|
+ const nk_draw_index *offset = NULL;
|
|
|
|
|
+ struct nk_buffer vbuf, ebuf;
|
|
|
|
|
+
|
|
|
|
|
+ /* fill converting configuration */
|
|
|
|
|
+ struct nk_convert_config config;
|
|
|
|
|
+ static const struct nk_draw_vertex_layout_element vertex_layout[] = {
|
|
|
|
|
+ {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_sdl_vertex, position)},
|
|
|
|
|
+ {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_sdl_vertex, uv)},
|
|
|
|
|
+ {NK_VERTEX_COLOR, NK_FORMAT_R32G32B32A32_FLOAT, NK_OFFSETOF(struct nk_sdl_vertex, col)},
|
|
|
|
|
+ {NK_VERTEX_LAYOUT_END}
|
|
|
|
|
+ };
|
|
|
|
|
+ NK_MEMSET(&config, 0, sizeof(config));
|
|
|
|
|
+ config.vertex_layout = vertex_layout;
|
|
|
|
|
+ config.vertex_size = sizeof(struct nk_sdl_vertex);
|
|
|
|
|
+ config.vertex_alignment = NK_ALIGNOF(struct nk_sdl_vertex);
|
|
|
|
|
+ config.tex_null = sdl->ogl.tex_null;
|
|
|
|
|
+ config.circle_segment_count = 22;
|
|
|
|
|
+ config.curve_segment_count = 22;
|
|
|
|
|
+ config.arc_segment_count = 22;
|
|
|
|
|
+ config.global_alpha = 1.0f;
|
|
|
|
|
+ config.shape_AA = AA;
|
|
|
|
|
+ config.line_AA = AA;
|
|
|
|
|
+
|
|
|
|
|
+ /* convert shapes into vertexes */
|
|
|
|
|
+ nk_buffer_init(&vbuf, &sdl->allocator, NK_BUFFER_DEFAULT_INITIAL_SIZE);
|
|
|
|
|
+ nk_buffer_init(&ebuf, &sdl->allocator, NK_BUFFER_DEFAULT_INITIAL_SIZE);
|
|
|
|
|
+ nk_convert(&sdl->ctx, &sdl->ogl.cmds, &vbuf, &ebuf, &config);
|
|
|
|
|
+
|
|
|
|
|
+ /* iterate over and execute each draw command */
|
|
|
|
|
+ offset = (const nk_draw_index*)nk_buffer_memory_const(&ebuf);
|
|
|
|
|
+
|
|
|
|
|
+ clipping_enabled = SDL_RenderClipEnabled(sdl->renderer);
|
|
|
|
|
+ SDL_GetRenderClipRect(sdl->renderer, &saved_clip);
|
|
|
|
|
+
|
|
|
|
|
+ nk_draw_foreach(cmd, &sdl->ctx, &sdl->ogl.cmds)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!cmd->elem_count) continue;
|
|
|
|
|
+
|
|
|
|
|
+ {
|
|
|
|
|
+ SDL_Rect r;
|
|
|
|
|
+ r.x = cmd->clip_rect.x;
|
|
|
|
|
+ r.y = cmd->clip_rect.y;
|
|
|
|
|
+ r.w = cmd->clip_rect.w;
|
|
|
|
|
+ r.h = cmd->clip_rect.h;
|
|
|
|
|
+ SDL_SetRenderClipRect(sdl->renderer, &r);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ {
|
|
|
|
|
+ const void *vertices = nk_buffer_memory_const(&vbuf);
|
|
|
|
|
+
|
|
|
|
|
+ SDL_RenderGeometryRaw(
|
|
|
|
|
+ sdl->renderer,
|
|
|
|
|
+ (SDL_Texture *)cmd->texture.ptr,
|
|
|
|
|
+ (const float*)((const nk_byte*)vertices + vp), vs,
|
|
|
|
|
+ (const SDL_FColor*)((const nk_byte*)vertices + vc), vs,
|
|
|
|
|
+ (const float*)((const nk_byte*)vertices + vt), vs,
|
|
|
|
|
+ (vbuf.needed / vs),
|
|
|
|
|
+ (void *) offset, cmd->elem_count, 2);
|
|
|
|
|
+
|
|
|
|
|
+ offset += cmd->elem_count;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ SDL_SetRenderClipRect(sdl->renderer, &saved_clip);
|
|
|
|
|
+ if (!clipping_enabled) {
|
|
|
|
|
+ SDL_SetRenderClipRect(sdl->renderer, NULL);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ nk_clear(&sdl->ctx);
|
|
|
|
|
+ nk_buffer_clear(&sdl->ogl.cmds);
|
|
|
|
|
+ nk_buffer_free(&vbuf);
|
|
|
|
|
+ nk_buffer_free(&ebuf);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+NK_INTERN void
|
|
|
|
|
+nk_sdl_clipboard_paste(nk_handle usr, struct nk_text_edit *edit)
|
|
|
|
|
+{
|
|
|
|
|
+ char *text;
|
|
|
|
|
+ int len;
|
|
|
|
|
+ NK_UNUSED(usr);
|
|
|
|
|
+
|
|
|
|
|
+ /* this function returns empty string on failure, not NULL */
|
|
|
|
|
+ text = SDL_GetClipboardText();
|
|
|
|
|
+ NK_ASSERT(text);
|
|
|
|
|
+
|
|
|
|
|
+ if (text[0] != '\0') {
|
|
|
|
|
+ /* FIXME: there is a bug in Nuklear that affects UTF8 clipboard handling
|
|
|
|
|
+ * "len" should be a buffer length, but due to bug it must be a glyph count
|
|
|
|
|
+ * see: https://github.com/Immediate-Mode-UI/Nuklear/pull/841 */
|
|
|
|
|
+#if 0
|
|
|
|
|
+ len = nk_strlen(text);
|
|
|
|
|
+#else
|
|
|
|
|
+ len = SDL_utf8strlen(text);
|
|
|
|
|
+#endif
|
|
|
|
|
+ nk_textedit_paste(edit, text, len);
|
|
|
|
|
+ }
|
|
|
|
|
+ SDL_free(text);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+NK_INTERN void
|
|
|
|
|
+nk_sdl_clipboard_copy(nk_handle usr, const char *text, int len)
|
|
|
|
|
+{
|
|
|
|
|
+ const char *ptext;
|
|
|
|
|
+ char *str;
|
|
|
|
|
+ size_t buflen;
|
|
|
|
|
+ int i;
|
|
|
|
|
+ struct nk_sdl* sdl = (struct nk_sdl*)usr.ptr;
|
|
|
|
|
+ NK_ASSERT(sdl);
|
|
|
|
|
+ if (len <= 0 || text == NULL) return;
|
|
|
|
|
+
|
|
|
|
|
+ /* FIXME: there is a bug in Nuklear that affects UTF8 clipboard handling
|
|
|
|
|
+ * "len" is expected to be a buffer length, but due to bug it actually is a glyph count
|
|
|
|
|
+ * see: https://github.com/Immediate-Mode-UI/Nuklear/pull/841 */
|
|
|
|
|
+#if 0
|
|
|
|
|
+ buflen = len + 1;
|
|
|
|
|
+ NK_UNUSED(ptext);
|
|
|
|
|
+#else
|
|
|
|
|
+ ptext = text;
|
|
|
|
|
+ for (i = len; i > 0; i--)
|
|
|
|
|
+ (void)SDL_StepUTF8(&ptext, NULL);
|
|
|
|
|
+ buflen = (size_t)(ptext - text) + 1;
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+ str = sdl->allocator.alloc(sdl->allocator.userdata, 0, buflen);
|
|
|
|
|
+ if (!str) return;
|
|
|
|
|
+ SDL_strlcpy(str, text, buflen);
|
|
|
|
|
+ SDL_SetClipboardText(str);
|
|
|
|
|
+ sdl->allocator.free(sdl->allocator.userdata, str);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+NK_API struct nk_context*
|
|
|
|
|
+nk_sdl_init(SDL_Window *win, SDL_Renderer *renderer, struct nk_allocator allocator)
|
|
|
|
|
+{
|
|
|
|
|
+ struct nk_sdl* sdl;
|
|
|
|
|
+ NK_ASSERT(win);
|
|
|
|
|
+ NK_ASSERT(renderer);
|
|
|
|
|
+ NK_ASSERT(allocator.alloc);
|
|
|
|
|
+ NK_ASSERT(allocator.free);
|
|
|
|
|
+ sdl = allocator.alloc(allocator.userdata, 0, sizeof(*sdl));
|
|
|
|
|
+ NK_ASSERT(sdl);
|
|
|
|
|
+ SDL_zerop(sdl);
|
|
|
|
|
+ sdl->allocator.userdata = allocator.userdata;
|
|
|
|
|
+ sdl->allocator.alloc = allocator.alloc;
|
|
|
|
|
+ sdl->allocator.free = allocator.free;
|
|
|
|
|
+ sdl->win = win;
|
|
|
|
|
+ sdl->renderer = renderer;
|
|
|
|
|
+ nk_init(&sdl->ctx, &sdl->allocator, 0);
|
|
|
|
|
+ sdl->ctx.userdata = nk_handle_ptr((void*)sdl);
|
|
|
|
|
+ sdl->ctx.clip.copy = nk_sdl_clipboard_copy;
|
|
|
|
|
+ sdl->ctx.clip.paste = nk_sdl_clipboard_paste;
|
|
|
|
|
+ sdl->ctx.clip.userdata = nk_handle_ptr((void*)sdl);
|
|
|
|
|
+ nk_buffer_init(&sdl->ogl.cmds, &sdl->allocator, NK_BUFFER_DEFAULT_INITIAL_SIZE);
|
|
|
|
|
+ sdl->last_left_click = 0;
|
|
|
|
|
+ sdl->edit_was_active = false;
|
|
|
|
|
+ sdl->insert_toggle = false;
|
|
|
|
|
+ return &sdl->ctx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#ifdef NK_INCLUDE_FONT_BAKING
|
|
|
|
|
+NK_API struct nk_font_atlas*
|
|
|
|
|
+nk_sdl_font_stash_begin(struct nk_context* ctx)
|
|
|
|
|
+{
|
|
|
|
|
+ struct nk_sdl* sdl;
|
|
|
|
|
+ NK_ASSERT(ctx);
|
|
|
|
|
+ sdl = (struct nk_sdl*)ctx->userdata.ptr;
|
|
|
|
|
+ NK_ASSERT(sdl);
|
|
|
|
|
+ nk_font_atlas_init(&sdl->atlas, &sdl->allocator);
|
|
|
|
|
+ nk_font_atlas_begin(&sdl->atlas);
|
|
|
|
|
+ return &sdl->atlas;
|
|
|
|
|
+}
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+#ifdef NK_INCLUDE_FONT_BAKING
|
|
|
|
|
+NK_API void
|
|
|
|
|
+nk_sdl_font_stash_end(struct nk_context* ctx)
|
|
|
|
|
+{
|
|
|
|
|
+ struct nk_sdl* sdl;
|
|
|
|
|
+ const void *image; int w, h;
|
|
|
|
|
+ NK_ASSERT(ctx);
|
|
|
|
|
+ sdl = (struct nk_sdl*)ctx->userdata.ptr;
|
|
|
|
|
+ NK_ASSERT(sdl);
|
|
|
|
|
+ image = nk_font_atlas_bake(&sdl->atlas, &w, &h, NK_FONT_ATLAS_RGBA32);
|
|
|
|
|
+ NK_ASSERT(image);
|
|
|
|
|
+ nk_sdl_device_upload_atlas(&sdl->ctx, image, w, h);
|
|
|
|
|
+ nk_font_atlas_end(&sdl->atlas, nk_handle_ptr(sdl->ogl.font_tex), &sdl->ogl.tex_null);
|
|
|
|
|
+ if (sdl->atlas.default_font) {
|
|
|
|
|
+ nk_style_set_font(&sdl->ctx, &sdl->atlas.default_font->handle);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+NK_API int
|
|
|
|
|
+nk_sdl_handle_event(struct nk_context* ctx, SDL_Event *evt)
|
|
|
|
|
+{
|
|
|
|
|
+ struct nk_sdl* sdl;
|
|
|
|
|
+
|
|
|
|
|
+ NK_ASSERT(ctx);
|
|
|
|
|
+ NK_ASSERT(evt);
|
|
|
|
|
+
|
|
|
|
|
+ sdl = (struct nk_sdl*)ctx->userdata.ptr;
|
|
|
|
|
+ NK_ASSERT(sdl);
|
|
|
|
|
+
|
|
|
|
|
+ /* We only care about Window currently used by Nuklear */
|
|
|
|
|
+ if (sdl->win != SDL_GetWindowFromEvent(evt)) {
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ switch(evt->type)
|
|
|
|
|
+ {
|
|
|
|
|
+ case SDL_EVENT_KEY_UP: /* KEYUP & KEYDOWN share same routine */
|
|
|
|
|
+ case SDL_EVENT_KEY_DOWN:
|
|
|
|
|
+ {
|
|
|
|
|
+ int down = evt->type == SDL_EVENT_KEY_DOWN;
|
|
|
|
|
+ int ctrl_down = evt->key.mod & (SDL_KMOD_LCTRL | SDL_KMOD_RCTRL);
|
|
|
|
|
+
|
|
|
|
|
+ /* In 99% of the time, you want to use scancodes, not real key codes,
|
|
|
|
|
+ * see: https://wiki.libsdl.org/SDL3/BestKeyboardPractices */
|
|
|
|
|
+ switch(evt->key.scancode)
|
|
|
|
|
+ {
|
|
|
|
|
+ case SDL_SCANCODE_RSHIFT: /* RSHIFT & LSHIFT share same routine */
|
|
|
|
|
+ case SDL_SCANCODE_LSHIFT: nk_input_key(ctx, NK_KEY_SHIFT, down); break;
|
|
|
|
|
+ case SDL_SCANCODE_DELETE: nk_input_key(ctx, NK_KEY_DEL, down); break;
|
|
|
|
|
+ case SDL_SCANCODE_RETURN: nk_input_key(ctx, NK_KEY_ENTER, down); break;
|
|
|
|
|
+ case SDL_SCANCODE_TAB: nk_input_key(ctx, NK_KEY_TAB, down); break;
|
|
|
|
|
+ case SDL_SCANCODE_BACKSPACE: nk_input_key(ctx, NK_KEY_BACKSPACE, down); break;
|
|
|
|
|
+ case SDL_SCANCODE_HOME: nk_input_key(ctx, NK_KEY_TEXT_START, down);
|
|
|
|
|
+ nk_input_key(ctx, NK_KEY_SCROLL_START, down); break;
|
|
|
|
|
+ case SDL_SCANCODE_END: nk_input_key(ctx, NK_KEY_TEXT_END, down);
|
|
|
|
|
+ nk_input_key(ctx, NK_KEY_SCROLL_END, down); break;
|
|
|
|
|
+ case SDL_SCANCODE_PAGEDOWN: nk_input_key(ctx, NK_KEY_SCROLL_DOWN, down); break;
|
|
|
|
|
+ case SDL_SCANCODE_PAGEUP: nk_input_key(ctx, NK_KEY_SCROLL_UP, down); break;
|
|
|
|
|
+ case SDL_SCANCODE_A: nk_input_key(ctx, NK_KEY_TEXT_SELECT_ALL, down && ctrl_down); break;
|
|
|
|
|
+ case SDL_SCANCODE_Z: nk_input_key(ctx, NK_KEY_TEXT_UNDO, down && ctrl_down); break;
|
|
|
|
|
+ case SDL_SCANCODE_R: nk_input_key(ctx, NK_KEY_TEXT_REDO, down && ctrl_down); break;
|
|
|
|
|
+ case SDL_SCANCODE_C: nk_input_key(ctx, NK_KEY_COPY, down && ctrl_down); break;
|
|
|
|
|
+ case SDL_SCANCODE_V: nk_input_key(ctx, NK_KEY_PASTE, down && ctrl_down); break;
|
|
|
|
|
+ case SDL_SCANCODE_X: nk_input_key(ctx, NK_KEY_CUT, down && ctrl_down); break;
|
|
|
|
|
+ case SDL_SCANCODE_B: nk_input_key(ctx, NK_KEY_TEXT_LINE_START, down && ctrl_down); break;
|
|
|
|
|
+ case SDL_SCANCODE_E: nk_input_key(ctx, NK_KEY_TEXT_LINE_END, down && ctrl_down); break;
|
|
|
|
|
+ case SDL_SCANCODE_UP: nk_input_key(ctx, NK_KEY_UP, down); break;
|
|
|
|
|
+ case SDL_SCANCODE_DOWN: nk_input_key(ctx, NK_KEY_DOWN, down); break;
|
|
|
|
|
+ case SDL_SCANCODE_ESCAPE: nk_input_key(ctx, NK_KEY_TEXT_RESET_MODE, down); break;
|
|
|
|
|
+ case SDL_SCANCODE_INSERT:
|
|
|
|
|
+ if (down) sdl->insert_toggle = !sdl->insert_toggle;
|
|
|
|
|
+ if (sdl->insert_toggle) {
|
|
|
|
|
+ nk_input_key(ctx, NK_KEY_TEXT_INSERT_MODE, down);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ nk_input_key(ctx, NK_KEY_TEXT_REPLACE_MODE, down);
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ case SDL_SCANCODE_LEFT:
|
|
|
|
|
+ if (ctrl_down)
|
|
|
|
|
+ nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, down);
|
|
|
|
|
+ else
|
|
|
|
|
+ nk_input_key(ctx, NK_KEY_LEFT, down);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case SDL_SCANCODE_RIGHT:
|
|
|
|
|
+ if (ctrl_down)
|
|
|
|
|
+ nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, down);
|
|
|
|
|
+ else
|
|
|
|
|
+ nk_input_key(ctx, NK_KEY_RIGHT, down);
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ return 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ case SDL_EVENT_MOUSE_BUTTON_UP: /* MOUSEBUTTONUP & MOUSEBUTTONDOWN share same routine */
|
|
|
|
|
+ case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
|
|
|
|
+ {
|
|
|
|
|
+ const int x = evt->button.x, y = evt->button.y;
|
|
|
|
|
+ const int down = evt->button.down;
|
|
|
|
|
+ const double dt = (double)(evt->button.timestamp - sdl->last_left_click) / 1000000000.0;
|
|
|
|
|
+ switch(evt->button.button)
|
|
|
|
|
+ {
|
|
|
|
|
+ case SDL_BUTTON_LEFT:
|
|
|
|
|
+ nk_input_button(ctx, NK_BUTTON_LEFT, x, y, down);
|
|
|
|
|
+ nk_input_button(ctx, NK_BUTTON_DOUBLE, x, y,
|
|
|
|
|
+ down && dt > NK_SDL_DOUBLE_CLICK_LO && dt < NK_SDL_DOUBLE_CLICK_HI);
|
|
|
|
|
+ sdl->last_left_click = evt->button.timestamp;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case SDL_BUTTON_MIDDLE: nk_input_button(ctx, NK_BUTTON_MIDDLE, x, y, down); break;
|
|
|
|
|
+ case SDL_BUTTON_RIGHT: nk_input_button(ctx, NK_BUTTON_RIGHT, x, y, down); break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return 1;
|
|
|
|
|
+
|
|
|
|
|
+ case SDL_EVENT_MOUSE_MOTION:
|
|
|
|
|
+ ctx->input.mouse.pos.x = evt->motion.x;
|
|
|
|
|
+ ctx->input.mouse.pos.y = evt->motion.y;
|
|
|
|
|
+ ctx->input.mouse.delta.x = ctx->input.mouse.pos.x - ctx->input.mouse.prev.x;
|
|
|
|
|
+ ctx->input.mouse.delta.y = ctx->input.mouse.pos.y - ctx->input.mouse.prev.y;
|
|
|
|
|
+ return 1;
|
|
|
|
|
+
|
|
|
|
|
+ case SDL_EVENT_TEXT_INPUT:
|
|
|
|
|
+ {
|
|
|
|
|
+ nk_glyph glyph;
|
|
|
|
|
+ nk_size len;
|
|
|
|
|
+ NK_ASSERT(evt->text.text);
|
|
|
|
|
+ len = SDL_strlen(evt->text.text);
|
|
|
|
|
+ NK_ASSERT(len <= NK_UTF_SIZE);
|
|
|
|
|
+ NK_MEMCPY(glyph, evt->text.text, len);
|
|
|
|
|
+ nk_input_glyph(ctx, glyph);
|
|
|
|
|
+ }
|
|
|
|
|
+ return 1;
|
|
|
|
|
+
|
|
|
|
|
+ case SDL_EVENT_MOUSE_WHEEL:
|
|
|
|
|
+ nk_input_scroll(ctx, nk_vec2(evt->wheel.x, evt->wheel.y));
|
|
|
|
|
+ return 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ return 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+NK_API
|
|
|
|
|
+void nk_sdl_shutdown(struct nk_context* ctx)
|
|
|
|
|
+{
|
|
|
|
|
+ struct nk_sdl* sdl;
|
|
|
|
|
+ NK_ASSERT(ctx);
|
|
|
|
|
+ sdl = (struct nk_sdl*)ctx->userdata.ptr;
|
|
|
|
|
+ NK_ASSERT(sdl);
|
|
|
|
|
+
|
|
|
|
|
+#ifdef NK_INCLUDE_FONT_BAKING
|
|
|
|
|
+ if (sdl->atlas.font_num > 0)
|
|
|
|
|
+ nk_font_atlas_clear(&sdl->atlas);
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+ nk_buffer_free(&sdl->ogl.cmds);
|
|
|
|
|
+
|
|
|
|
|
+ if (sdl->ogl.font_tex != NULL) {
|
|
|
|
|
+ SDL_DestroyTexture(sdl->ogl.font_tex);
|
|
|
|
|
+ sdl->ogl.font_tex = NULL;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ nk_free(ctx);
|
|
|
|
|
+ sdl->allocator.free(sdl->allocator.userdata, sdl->debug_font);
|
|
|
|
|
+ sdl->allocator.free(sdl->allocator.userdata, sdl);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* Debug Font Width/Height of internal texture atlas
|
|
|
|
|
+ * This is a result of: ceil(sqrt('~' - ' '))
|
|
|
|
|
+ * There is a sanity check for this value in nk_sdl_style_set_debug_font */
|
|
|
|
|
+#define NK_SDL_DFWH (10)
|
|
|
|
|
+
|
|
|
|
|
+NK_INTERN float
|
|
|
|
|
+nk_sdl_query_debug_font_width(nk_handle handle, float height,
|
|
|
|
|
+ const char *text, int len)
|
|
|
|
|
+{
|
|
|
|
|
+ NK_UNUSED(handle);
|
|
|
|
|
+ return nk_utf_len(text, len) * height;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+NK_INTERN void
|
|
|
|
|
+nk_sdl_query_debug_font_glypth(nk_handle handle, float height,
|
|
|
|
|
+ struct nk_user_font_glyph *glyph,
|
|
|
|
|
+ nk_rune codepoint, nk_rune next_codepoint)
|
|
|
|
|
+{
|
|
|
|
|
+ char ascii;
|
|
|
|
|
+ int idx, x, y;
|
|
|
|
|
+ NK_UNUSED(next_codepoint);
|
|
|
|
|
+ NK_UNUSED(handle);
|
|
|
|
|
+
|
|
|
|
|
+ /* replace non-ASCII characters with question mark */
|
|
|
|
|
+ ascii = (codepoint < (nk_rune)' ' || codepoint > (nk_rune)'~')
|
|
|
|
|
+ ? '?' : (char)codepoint;
|
|
|
|
|
+ NK_ASSERT(ascii >= ' ' && ascii <= '~');
|
|
|
|
|
+
|
|
|
|
|
+ idx = (int)(ascii - ' ');
|
|
|
|
|
+ x = idx / NK_SDL_DFWH;
|
|
|
|
|
+ y = idx % NK_SDL_DFWH;
|
|
|
|
|
+ NK_ASSERT(x >= 0 && x < NK_SDL_DFWH);
|
|
|
|
|
+ NK_ASSERT(y >= 0 && y < NK_SDL_DFWH);
|
|
|
|
|
+
|
|
|
|
|
+ glyph->height = height;
|
|
|
|
|
+ glyph->width = height;
|
|
|
|
|
+ glyph->xadvance = height;
|
|
|
|
|
+ glyph->uv[0].x = (float)(x + 0) / NK_SDL_DFWH;
|
|
|
|
|
+ glyph->uv[0].y = (float)(y + 0) / NK_SDL_DFWH;
|
|
|
|
|
+ glyph->uv[1].x = (float)(x + 1) / NK_SDL_DFWH;
|
|
|
|
|
+ glyph->uv[1].y = (float)(y + 1) / NK_SDL_DFWH;
|
|
|
|
|
+ glyph->offset.x = 0.0f;
|
|
|
|
|
+ glyph->offset.y = 0.0f;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+NK_API void
|
|
|
|
|
+nk_sdl_style_set_debug_font(struct nk_context* ctx)
|
|
|
|
|
+{
|
|
|
|
|
+ struct nk_user_font* font;
|
|
|
|
|
+ struct nk_sdl* sdl;
|
|
|
|
|
+ SDL_Surface *surface;
|
|
|
|
|
+ SDL_Renderer *renderer;
|
|
|
|
|
+ char buf[2];
|
|
|
|
|
+ int x, y;
|
|
|
|
|
+ bool success;
|
|
|
|
|
+ NK_ASSERT(ctx);
|
|
|
|
|
+
|
|
|
|
|
+ sdl = (struct nk_sdl*)ctx->userdata.ptr;
|
|
|
|
|
+ NK_ASSERT(sdl);
|
|
|
|
|
+
|
|
|
|
|
+ if (sdl->debug_font) {
|
|
|
|
|
+ sdl->allocator.free(sdl->allocator.userdata, sdl->debug_font);
|
|
|
|
|
+ sdl->debug_font = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* sanity check: formal proof of NK_SDL_DFWH value (which is 10) */
|
|
|
|
|
+ NK_ASSERT(SDL_ceil(SDL_sqrt('~' - ' ')) == NK_SDL_DFWH);
|
|
|
|
|
+
|
|
|
|
|
+ /* We use another Software Renderer just to make sure
|
|
|
|
|
+ * that we won't mutate any state in the main Renderer. */
|
|
|
|
|
+ surface = SDL_CreateSurface(
|
|
|
|
|
+ NK_SDL_DFWH * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE,
|
|
|
|
|
+ NK_SDL_DFWH * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE,
|
|
|
|
|
+ SDL_PIXELFORMAT_RGBA32);
|
|
|
|
|
+ NK_ASSERT(surface);
|
|
|
|
|
+ renderer = SDL_CreateSoftwareRenderer(surface);
|
|
|
|
|
+ NK_ASSERT(renderer);
|
|
|
|
|
+ success = SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
|
|
|
|
|
+ NK_ASSERT(success);
|
|
|
|
|
+
|
|
|
|
|
+ /* SPACE is the first printable ASCII character */
|
|
|
|
|
+ NK_MEMCPY(buf, " ", sizeof(buf));
|
|
|
|
|
+ for (x = 0; x < NK_SDL_DFWH; x++)
|
|
|
|
|
+ {
|
|
|
|
|
+ for (y = 0; y < NK_SDL_DFWH; y++)
|
|
|
|
|
+ {
|
|
|
|
|
+ success = SDL_RenderDebugText(
|
|
|
|
|
+ renderer,
|
|
|
|
|
+ (float)(x * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE),
|
|
|
|
|
+ (float)(y * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE),
|
|
|
|
|
+ buf);
|
|
|
|
|
+ NK_ASSERT(success);
|
|
|
|
|
+ buf[0]++;
|
|
|
|
|
+
|
|
|
|
|
+ /* TILDE is the last printable ASCII character */
|
|
|
|
|
+ if (buf[0] > '~')
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ success = SDL_RenderPresent(renderer);
|
|
|
|
|
+ NK_ASSERT(success);
|
|
|
|
|
+
|
|
|
|
|
+ font = sdl->allocator.alloc(sdl->allocator.userdata, 0, sizeof(*font));
|
|
|
|
|
+ NK_ASSERT(font);
|
|
|
|
|
+ font->userdata.ptr = sdl;
|
|
|
|
|
+ font->height = SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
|
|
|
|
|
+ font->width = &nk_sdl_query_debug_font_width;
|
|
|
|
|
+ font->query = &nk_sdl_query_debug_font_glypth;
|
|
|
|
|
+
|
|
|
|
|
+ /* HACK: nk_sdl_device_upload_atlas turns pixels into SDL_Texture
|
|
|
|
|
+ * and sets said Texture into sdl->ogl.font_tex
|
|
|
|
|
+ * then nk_sdl_render expects same Texture at font->texture */
|
|
|
|
|
+ nk_sdl_device_upload_atlas(ctx, surface->pixels, surface->w, surface->h);
|
|
|
|
|
+ font->texture.ptr = sdl->ogl.font_tex;
|
|
|
|
|
+
|
|
|
|
|
+ sdl->debug_font = font;
|
|
|
|
|
+ nk_style_set_font(ctx, font);
|
|
|
|
|
+
|
|
|
|
|
+ SDL_DestroyRenderer(renderer);
|
|
|
|
|
+ SDL_DestroySurface(surface);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#endif /* NK_SDL3_RENDERER_IMPLEMENTATION_ONCE */
|
|
|
|
|
+#endif /* NK_SDL3_RENDERER_IMPLEMENTATION */
|
|
|
|
|
+
|