diff options
| -rw-r--r-- | kon/kondebug.h | 748 | ||||
| -rw-r--r-- | src/impl/kondebug.c | 2 |
2 files changed, 750 insertions, 0 deletions
diff --git a/kon/kondebug.h b/kon/kondebug.h new file mode 100644 index 0000000..9fbe881 --- /dev/null +++ b/kon/kondebug.h @@ -0,0 +1,748 @@ +/* SPDX-License-Identifier: MIT */ +/* + * kondebug.h + * + * Copyright (c) 2026 KonAki + * + * 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. + */ + +#ifndef KONDEBUG_H +#define KONDEBUG_H + +#include <stdint.h> + +#include "konwinlib.h" +#include "konsofren.h" +#include "konfont.h" +#include "konassets.h" + +/*** definition ***/ + +typedef struct kon_debug kon_debug_t; + +typedef enum kon_debugVarType { + KON_VAR_BOOL = 0, + KON_VAR_INT, + KON_VAR_FLOAT +} kon_debugVarType_t; + +/* argv[0] is the command name */ +typedef void (*kon_debugCommandFn)(kon_debug_t *debug, void *user, int argc, char **argv); + +#ifdef KON_DEVELOPER + +kon_debug_t *kon_debugCreate(kon_font_t *font); +void kon_debugDestroy(kon_debug_t *debug); + +/* defaults are F1 for the overlay and ` for the console */ +void kon_debugSetKeys(kon_debug_t *debug, int overlayKey, int consoleKey); +void kon_debugSetAssets(kon_debug_t *debug, kon_assets_t *assets); + +/* name and help have to stay valid, use string literals */ +int kon_debugCommand(kon_debug_t *debug, const char *name, const char *help, kon_debugCommandFn fn, void *user); +int kon_debugVar(kon_debug_t *debug, const char *name, kon_debugVarType_t type, void *value); + +void kon_debugLog(kon_debug_t *debug, const char *format, ...); + +/* returns 1 when the event was used by the console, so the game should skip it */ +int kon_debugFeedEvent(kon_debug_t *debug, const kon_event_t *event); +int kon_debugConsoleOpen(const kon_debug_t *debug); + +/* call once a frame, draw last so it ends up on top */ +void kon_debugFrame(kon_debug_t *debug); +void kon_debugDraw(kon_debug_t *debug, kon_framebuffer_t *fb); + +float kon_debugFps(const kon_debug_t *debug); + +#else + +static inline kon_debug_t *kon_debugCreate(kon_font_t *font) { (void)font; return NULL; } +static inline void kon_debugDestroy(kon_debug_t *debug) { (void)debug; } +static inline void kon_debugSetKeys(kon_debug_t *debug, int overlayKey, int consoleKey) { (void)debug; (void)overlayKey; (void)consoleKey; } +static inline void kon_debugSetAssets(kon_debug_t *debug, kon_assets_t *assets) { (void)debug; (void)assets; } +static inline int kon_debugCommand(kon_debug_t *debug, const char *name, const char *help, kon_debugCommandFn fn, void *user) { (void)debug; (void)name; (void)help; (void)fn; (void)user; return 0; } +static inline int kon_debugVar(kon_debug_t *debug, const char *name, kon_debugVarType_t type, void *value) { (void)debug; (void)name; (void)type; (void)value; return 0; } +#define kon_debugLog(...) ((void)0) +static inline int kon_debugFeedEvent(kon_debug_t *debug, const kon_event_t *event) { (void)debug; (void)event; return 0; } +static inline int kon_debugConsoleOpen(const kon_debug_t *debug) { (void)debug; return 0; } +static inline void kon_debugFrame(kon_debug_t *debug) { (void)debug; } +static inline void kon_debugDraw(kon_debug_t *debug, kon_framebuffer_t *fb) { (void)debug; (void)fb; } +static inline float kon_debugFps(const kon_debug_t *debug) { (void)debug; return 0.0f; } + +#endif + +#endif + +/*** implementation ***/ + +#if defined(KONDEBUG_IMPLEMENTATION) && defined(KON_DEVELOPER) + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <stdarg.h> + +#include "kontime.h" + +#define KON_DEBUG_LOGLINES 256 +#define KON_DEBUG_LINELEN 160 +#define KON_DEBUG_INPUT 256 +#define KON_DEBUG_HISTORY 16 +#define KON_DEBUG_COMMANDS 64 +#define KON_DEBUG_VARS 64 +#define KON_DEBUG_ARGS 16 +#define KON_DEBUG_GRAPH 120 +#define KON_DEBUG_TYPES 8 + +typedef struct kon_debugCmd { + const char *name; + const char *help; + kon_debugCommandFn fn; + void *user; +} kon_debugCmd_t; + +typedef struct kon_debugVarEntry { + const char *name; + kon_debugVarType_t type; + void *value; + int watched; +} kon_debugVarEntry_t; + +struct kon_debug { + kon_font_t *font; + kon_assets_t *assets; + int lineH, ascent; + + int overlayKey, consoleKey; + int overlay; + int open; + float slide; + int swallowTick; + + char log[KON_DEBUG_LOGLINES][KON_DEBUG_LINELEN]; + int logCount, logHead, scroll; + + char input[KON_DEBUG_INPUT]; + int length, cursor; + char history[KON_DEBUG_HISTORY][KON_DEBUG_INPUT]; + int historyCount, historyPos; + + kon_debugCmd_t commands[KON_DEBUG_COMMANDS]; + int commandCount; + kon_debugVarEntry_t vars[KON_DEBUG_VARS]; + int varCount; + + kon_time last; + double frameDt; + double accumTime; + int accumFrames; + float fps; + float graph[KON_DEBUG_GRAPH]; + int graphHead; +}; + +/*** private helpers ***/ + +static void kon_debugText_(const kon_debug_t *d, kon_framebuffer_t *fb, int x, int y, const char *s, uint32_t color) +{ + kon_drawText(fb, d->font, x, y + d->ascent, s, color); +} + +static void kon_debugPushLine_(kon_debug_t *d, const char *line) +{ + int slot = (d->logHead + d->logCount) % KON_DEBUG_LOGLINES; + + if (d->logCount == KON_DEBUG_LOGLINES) { + slot = d->logHead; + d->logHead = (d->logHead + 1) % KON_DEBUG_LOGLINES; + } else { + d->logCount++; + } + snprintf(d->log[slot], KON_DEBUG_LINELEN, "%s", line); + if (d->scroll > 0 && d->scroll < d->logCount) d->scroll++; +} + +static const char *kon_debugVarText_(const kon_debugVarEntry_t *v, char *buffer, size_t size) +{ + switch (v->type) { + case KON_VAR_BOOL: snprintf(buffer, size, "%s", *(int *)v->value ? "true" : "false"); break; + case KON_VAR_INT: snprintf(buffer, size, "%d", *(int *)v->value); break; + default: snprintf(buffer, size, "%g", (double)*(float *)v->value); break; + } + return buffer; +} + +static int kon_debugTokenize_(char *line, char **argv, int max) +{ + int argc = 0; + + while (*line && argc < max) { + while (*line == ' ' || *line == '\t') line++; + if (!*line) break; + if (*line == '"') { + argv[argc++] = ++line; + while (*line && *line != '"') line++; + } else { + argv[argc++] = line; + while (*line && *line != ' ' && *line != '\t') line++; + } + if (*line) *line++ = 0; + } + return argc; +} + +static void kon_debugSetVar_(kon_debug_t *d, kon_debugVarEntry_t *v, const char *text) +{ + char out[64]; + + switch (v->type) { + case KON_VAR_BOOL: + if (!strcmp(text, "1") || !strcmp(text, "true") || !strcmp(text, "on")) *(int *)v->value = 1; + else if (!strcmp(text, "0") || !strcmp(text, "false") || !strcmp(text, "off")) *(int *)v->value = 0; + else { + kon_debugLog(d, "%s expects 0 or 1", v->name); + return; + } + break; + case KON_VAR_INT: *(int *)v->value = atoi(text); break; + default: *(float *)v->value = (float)atof(text); break; + } + kon_debugLog(d, "%s = %s", v->name, kon_debugVarText_(v, out, sizeof(out))); +} + +static void kon_debugExecute_(kon_debug_t *d, const char *line) +{ + char copy[KON_DEBUG_INPUT], *argv[KON_DEBUG_ARGS]; + int argc, i; + + snprintf(copy, sizeof(copy), "%s", line); + argc = kon_debugTokenize_(copy, argv, KON_DEBUG_ARGS); + if (argc == 0) return; + + for (i = 0; i < d->commandCount; i++) { + if (!strcmp(argv[0], d->commands[i].name)) { + d->commands[i].fn(d, d->commands[i].user, argc, argv); + return; + } + } + for (i = 0; i < d->varCount; i++) { + kon_debugVarEntry_t *v = &d->vars[i]; + char out[64]; + + if (strcmp(argv[0], v->name)) continue; + if (argc > 1) kon_debugSetVar_(d, v, argv[1]); + else kon_debugLog(d, "%s = %s", v->name, kon_debugVarText_(v, out, sizeof(out))); + return; + } + kon_debugLog(d, "unknown command '%s', try help", argv[0]); +} + +static void kon_debugComplete_(kon_debug_t *d) +{ + const char *matches[KON_DEBUG_COMMANDS + KON_DEBUG_VARS]; + int count = 0, i, common; + size_t prefix = (size_t)d->cursor; + + for (i = 0; i < d->cursor; i++) if (d->input[i] == ' ') return; + if (prefix == 0) return; + + for (i = 0; i < d->commandCount; i++) if (!strncmp(d->commands[i].name, d->input, prefix)) matches[count++] = d->commands[i].name; + for (i = 0; i < d->varCount; i++) if (!strncmp(d->vars[i].name, d->input, prefix)) matches[count++] = d->vars[i].name; + if (count == 0) return; + + common = (int)strlen(matches[0]); + for (i = 1; i < count; i++) { + int n = 0; + + while (n < common && matches[i][n] == matches[0][n]) n++; + common = n; + } + + if (count > 1) { + char line[KON_DEBUG_LINELEN] = ""; + + for (i = 0; i < count; i++) { + if (strlen(line) + strlen(matches[i]) + 2 >= sizeof(line)) break; + strcat(line, matches[i]); + strcat(line, " "); + } + kon_debugLog(d, "%s", line); + } + + { + char rest[KON_DEBUG_INPUT]; + int add = common - d->cursor + (count == 1 ? 1 : 0); + + snprintf(rest, sizeof(rest), "%s", d->input + d->cursor); + if (d->length + add >= KON_DEBUG_INPUT) return; + memcpy(d->input, matches[0], (size_t)common); + if (count == 1) d->input[common] = ' '; + memcpy(d->input + common + (count == 1 ? 1 : 0), rest, strlen(rest) + 1); + d->length = (int)strlen(d->input); + d->cursor = common + (count == 1 ? 1 : 0); + } +} + +static void kon_debugInsert_(kon_debug_t *d, char c) +{ + if (d->length + 1 >= KON_DEBUG_INPUT) return; + memmove(d->input + d->cursor + 1, d->input + d->cursor, (size_t)(d->length - d->cursor) + 1); + d->input[d->cursor++] = c; + d->length++; +} + +static void kon_debugSetInput_(kon_debug_t *d, const char *text) +{ + snprintf(d->input, sizeof(d->input), "%s", text); + d->length = (int)strlen(d->input); + d->cursor = d->length; +} + +static void kon_debugSubmit_(kon_debug_t *d) +{ + char line[KON_DEBUG_INPUT + 4]; + + if (d->length == 0) return; + snprintf(line, sizeof(line), "> %s", d->input); + kon_debugPushLine_(d, line); + + if (d->historyCount == 0 || strcmp(d->history[d->historyCount - 1], d->input)) { + if (d->historyCount == KON_DEBUG_HISTORY) { + memmove(d->history[0], d->history[1], sizeof(d->history[0]) * (KON_DEBUG_HISTORY - 1)); + d->historyCount--; + } + snprintf(d->history[d->historyCount++], KON_DEBUG_INPUT, "%s", d->input); + } + d->historyPos = d->historyCount; + + { + char copy[KON_DEBUG_INPUT]; + + snprintf(copy, sizeof(copy), "%s", d->input); + d->input[0] = 0; + d->length = d->cursor = 0; + d->scroll = 0; + kon_debugExecute_(d, copy); + } +} + +/*** builtin commands ***/ + +static void kon_debugCmdHelp_(kon_debug_t *d, void *user, int argc, char **argv) +{ + int i; + + (void)user; + if (argc > 1) { + for (i = 0; i < d->commandCount; i++) { + if (!strcmp(argv[1], d->commands[i].name)) { + kon_debugLog(d, "%s: %s", d->commands[i].name, d->commands[i].help); + return; + } + } + kon_debugLog(d, "no help for '%s'", argv[1]); + return; + } + kon_debugLog(d, "commands:"); + for (i = 0; i < d->commandCount; i++) kon_debugLog(d, " %-10s %s", d->commands[i].name, d->commands[i].help); + kon_debugLog(d, "variables (type the name to read, name value to set):"); + for (i = 0; i < d->varCount; i++) kon_debugLog(d, " %s", d->vars[i].name); +} + +static void kon_debugCmdClear_(kon_debug_t *d, void *user, int argc, char **argv) +{ + (void)user; (void)argc; (void)argv; + d->logCount = d->logHead = d->scroll = 0; +} + +static void kon_debugCmdEcho_(kon_debug_t *d, void *user, int argc, char **argv) +{ + char line[KON_DEBUG_LINELEN] = ""; + int i; + + (void)user; + for (i = 1; i < argc; i++) { + if (strlen(line) + strlen(argv[i]) + 2 >= sizeof(line)) break; + strcat(line, argv[i]); + strcat(line, " "); + } + kon_debugLog(d, "%s", line); +} + +static void kon_debugCmdFps_(kon_debug_t *d, void *user, int argc, char **argv) +{ + (void)user; + d->overlay = argc > 1 ? atoi(argv[1]) & 3 : (d->overlay + 1) & 3; +} + +static void kon_debugCmdWatch_(kon_debug_t *d, void *user, int argc, char **argv) +{ + int i; + + (void)user; + if (argc < 2) { + kon_debugLog(d, "usage: watch <variable>"); + return; + } + for (i = 0; i < d->varCount; i++) { + if (!strcmp(argv[1], d->vars[i].name)) { + d->vars[i].watched = !d->vars[i].watched; + if (d->overlay == 0) d->overlay = 1; + return; + } + } + kon_debugLog(d, "no variable '%s'", argv[1]); +} + +static void kon_debugCmdAssets_(kon_debug_t *d, void *user, int argc, char **argv) +{ + int i, count; + kon_assetInfo_t info; + + (void)user; (void)argc; (void)argv; + if (!d->assets) { + kon_debugLog(d, "no asset manager attached"); + return; + } + count = kon_assetsCount(d->assets); + for (i = 0; i < count; i++) { + if (!kon_assetsInfo(d->assets, i, &info)) continue; + kon_debugLog(d, "%-7s %3d ref %8lu b %s%s", info.typeName, info.refs, (unsigned long)info.bytes, info.name, info.loose ? " (loose)" : ""); + } + kon_debugLog(d, "%d assets, %lu bytes", count, (unsigned long)kon_assetsBytes(d->assets)); +} + +/*** debug ***/ + +kon_debug_t *kon_debugCreate(kon_font_t *font) +{ + kon_debug_t *d; + int h = 0; + + if (!font) return NULL; + d = calloc(1, sizeof(kon_debug_t)); + if (!d) return NULL; + + d->font = font; + kon_measureText(font, "A", NULL, &h); + d->ascent = h * 4 / 5; + d->lineH = h + 2; + d->overlayKey = KON_KEY_F1; + d->consoleKey = KON_KEY_GRAVE; + d->last = kon_getTime(); + + kon_debugCommand(d, "help", "list commands, or help <command>", kon_debugCmdHelp_, NULL); + kon_debugCommand(d, "clear", "clear the console", kon_debugCmdClear_, NULL); + kon_debugCommand(d, "echo", "print text", kon_debugCmdEcho_, NULL); + kon_debugCommand(d, "fps", "overlay mode 0-3, or cycle", kon_debugCmdFps_, NULL); + kon_debugCommand(d, "watch", "show a variable in the overlay", kon_debugCmdWatch_, NULL); + kon_debugCommand(d, "assets", "list loaded assets", kon_debugCmdAssets_, NULL); + kon_debugLog(d, "type help for commands, tab completes"); + return d; +} + +void kon_debugDestroy(kon_debug_t *d) +{ + free(d); +} + +void kon_debugSetKeys(kon_debug_t *d, int overlayKey, int consoleKey) +{ + if (!d) return; + d->overlayKey = overlayKey; + d->consoleKey = consoleKey; +} + +void kon_debugSetAssets(kon_debug_t *d, kon_assets_t *assets) +{ + if (!d) return; + d->assets = assets; +} + +int kon_debugCommand(kon_debug_t *d, const char *name, const char *help, kon_debugCommandFn fn, void *user) +{ + if (!d) return 0; + kon_debugCmd_t *c; + + if (d->commandCount == KON_DEBUG_COMMANDS) return 0; + c = &d->commands[d->commandCount++]; + c->name = name; + c->help = help; + c->fn = fn; + c->user = user; + return 1; +} + +int kon_debugVar(kon_debug_t *d, const char *name, kon_debugVarType_t type, void *value) +{ + if (!d) return 0; + kon_debugVarEntry_t *v; + + if (d->varCount == KON_DEBUG_VARS) return 0; + v = &d->vars[d->varCount++]; + v->name = name; + v->type = type; + v->value = value; + v->watched = 0; + return 1; +} + +void kon_debugLog(kon_debug_t *d, const char *format, ...) +{ + if (!d) return; + char line[KON_DEBUG_LINELEN]; + va_list args; + + va_start(args, format); + vsnprintf(line, sizeof(line), format, args); + va_end(args); + kon_debugPushLine_(d, line); +} + +int kon_debugConsoleOpen(const kon_debug_t *d) +{ + if (!d) return 0; + return d->open; +} + +float kon_debugFps(const kon_debug_t *d) +{ + if (!d) return 0.0f; + return d->fps; +} + +int kon_debugFeedEvent(kon_debug_t *d, const kon_event_t *e) +{ + if (!d) return 0; + + if (e->type == KON_EVENT_KEY_DOWN) { + if (e->key == d->consoleKey) { + d->open = !d->open; + d->swallowTick = 1; + return 1; + } + d->swallowTick = 0; + if (e->key == d->overlayKey) { + d->overlay = (d->overlay + 1) & 3; + return 1; + } + } + if (!d->open) return 0; + + if (e->type == KON_EVENT_TEXT) { + if (d->swallowTick && e->codepoint == '`') { + d->swallowTick = 0; + return 1; + } + if (e->codepoint >= 32 && e->codepoint <= 126) kon_debugInsert_(d, (char)e->codepoint); + return 1; + } + if (e->type == KON_EVENT_KEY_UP) return 1; + if (e->type != KON_EVENT_KEY_DOWN) return 0; + + switch (e->key) { + case KON_KEY_ENTER: kon_debugSubmit_(d); break; + case KON_KEY_ESCAPE: d->open = 0; break; + case KON_KEY_TAB: kon_debugComplete_(d); break; + case KON_KEY_LEFT: if (d->cursor > 0) d->cursor--; break; + case KON_KEY_RIGHT: if (d->cursor < d->length) d->cursor++; break; + case KON_KEY_BACKSPACE: + if (d->cursor > 0) { + memmove(d->input + d->cursor - 1, d->input + d->cursor, (size_t)(d->length - d->cursor) + 1); + d->cursor--; + d->length--; + } + break; + case KON_KEY_DELETE: + if (d->cursor < d->length) { + memmove(d->input + d->cursor, d->input + d->cursor + 1, (size_t)(d->length - d->cursor)); + d->length--; + } + break; + case KON_KEY_UP: + if (d->historyPos > 0) kon_debugSetInput_(d, d->history[--d->historyPos]); + break; + case KON_KEY_DOWN: + if (d->historyPos < d->historyCount - 1) kon_debugSetInput_(d, d->history[++d->historyPos]); + else { + d->historyPos = d->historyCount; + kon_debugSetInput_(d, ""); + } + break; + case KON_KEY_PAGE_UP: d->scroll += 8; break; + case KON_KEY_PAGE_DOWN: d->scroll -= 8; break; + default: break; + } + if (d->scroll < 0) d->scroll = 0; + if (d->scroll > d->logCount - 1) d->scroll = d->logCount > 0 ? d->logCount - 1 : 0; + return 1; +} + +void kon_debugFrame(kon_debug_t *d) +{ + if (!d) return; + kon_time now = kon_getTime(); + double dt = now - d->last; + + d->last = now; + d->frameDt = dt; + d->accumTime += dt; + d->accumFrames++; + if (d->accumTime >= 0.5) { + d->fps = (float)((double)d->accumFrames / d->accumTime); + d->accumTime = 0.0; + d->accumFrames = 0; + } + d->graph[d->graphHead] = (float)(dt * 1000.0); + d->graphHead = (d->graphHead + 1) % KON_DEBUG_GRAPH; +} + +static void kon_debugDrawOverlay_(kon_debug_t *d, kon_framebuffer_t *fb) +{ + char line[128]; + int x = 4, y = 4, i, rows; + uint32_t white = 0xFFFFFFFF, dim = 0xFFB0B0C0; + float worst = 0.0f; + + snprintf(line, sizeof(line), "%.0f fps %.2f ms", (double)d->fps, d->frameDt * 1000.0); + kon_fillRectangle(fb, x - 2, y - 2, 170, d->lineH + 4, 0xB0000000); + kon_debugText_(d, fb, x, y, line, white); + y += d->lineH + 4; + + if (d->overlay >= 1) { + kon_fillRectangle(fb, x - 2, y - 2, KON_DEBUG_GRAPH + 4, 34, 0xB0000000); + for (i = 0; i < KON_DEBUG_GRAPH; i++) { + float ms = d->graph[(d->graphHead + i) % KON_DEBUG_GRAPH]; + int h = (int)(ms * 30.0f / 33.3f); + uint32_t color = ms < 17.5f ? 0xFF50D060 : (ms < 34.0f ? 0xFFE0C040 : 0xFFE05050); + + if (ms > worst) worst = ms; + if (h > 30) h = 30; + if (h < 1) h = 1; + kon_fillRectangle(fb, x + i, y + 30 - h, 1, h, color); + } + y += 36; + } + + for (i = 0; i < d->varCount; i++) { + char value[64]; + + if (!d->vars[i].watched) continue; + snprintf(line, sizeof(line), "%s = %s", d->vars[i].name, kon_debugVarText_(&d->vars[i], value, sizeof(value))); + kon_fillRectangle(fb, x - 2, y - 1, 170, d->lineH, 0xB0000000); + kon_debugText_(d, fb, x, y, line, dim); + y += d->lineH; + } + + if (d->overlay >= 2 && d->assets) { + const char *names[KON_DEBUG_TYPES]; + int counts[KON_DEBUG_TYPES], types = 0, total = kon_assetsCount(d->assets); + size_t bytes[KON_DEBUG_TYPES]; + kon_assetInfo_t info; + + for (i = 0; i < total; i++) { + int t; + + if (!kon_assetsInfo(d->assets, i, &info)) continue; + for (t = 0; t < types; t++) if (names[t] == info.typeName) break; + if (t == types) { + if (types == KON_DEBUG_TYPES) continue; + names[types] = info.typeName; + counts[types] = 0; + bytes[types] = 0; + types++; + } + counts[t]++; + bytes[t] += info.bytes; + } + + y += 2; + snprintf(line, sizeof(line), "assets %d, %.1f KB", total, (double)kon_assetsBytes(d->assets) / 1024.0); + kon_fillRectangle(fb, x - 2, y - 1, 220, d->lineH, 0xB0000000); + kon_debugText_(d, fb, x, y, line, white); + y += d->lineH; + for (i = 0; i < types; i++) { + snprintf(line, sizeof(line), " %-7s %3d %8.1f KB", names[i], counts[i], (double)bytes[i] / 1024.0); + kon_fillRectangle(fb, x - 2, y - 1, 220, d->lineH, 0xB0000000); + kon_debugText_(d, fb, x, y, line, dim); + y += d->lineH; + } + + if (d->overlay >= 3) { + rows = (fb->height - y) / d->lineH - 1; + for (i = 0; i < total && i < rows; i++) { + if (!kon_assetsInfo(d->assets, i, &info)) continue; + snprintf(line, sizeof(line), "%-6s x%d %7.1fK %s%s", info.typeName, info.refs, (double)info.bytes / 1024.0, info.name, info.loose ? " *" : ""); + kon_fillRectangle(fb, x - 2, y - 1, 330, d->lineH, 0xB0000000); + kon_debugText_(d, fb, x, y, line, dim); + y += d->lineH; + } + } + } else if (d->overlay >= 2) { + kon_debugText_(d, fb, x, y + 2, "no asset manager attached", dim); + } +} + +static void kon_debugDrawConsole_(kon_debug_t *d, kon_framebuffer_t *fb) +{ + int full = fb->height / 2, height = (int)((float)full * d->slide); + int inputY, rows, i, line, cx, w = 0; + char shown[KON_DEBUG_INPUT + 4]; + + if (height < d->lineH + 6) return; + + kon_fillRectangle(fb, 0, 0, fb->width, height, 0xE0101018); + kon_fillRectangle(fb, 0, height - 1, fb->width, 1, 0xFF7088E0); + + inputY = height - d->lineH - 4; + kon_fillRectangle(fb, 0, inputY - 2, fb->width, d->lineH + 4, 0xFF181824); + + rows = (inputY - 4) / d->lineH; + for (i = 0; i < rows; i++) { + int index = d->logCount - 1 - d->scroll - i; + + if (index < 0) break; + line = (d->logHead + index) % KON_DEBUG_LOGLINES; + kon_debugText_(d, fb, 6, inputY - 4 - (i + 1) * d->lineH + d->lineH - 2, d->log[line], d->log[line][0] == '>' ? 0xFF9098C0 : 0xFFE0E0E8); + } + + snprintf(shown, sizeof(shown), "> %s", d->input); + kon_debugText_(d, fb, 6, inputY, shown, 0xFFFFFFFF); + + snprintf(shown, sizeof(shown), "> %.*s", d->cursor, d->input); + kon_measureText(d->font, shown, &w, NULL); + cx = 6 + w; + kon_fillRectangle(fb, cx, inputY, 2, d->lineH - 2, 0xFFFFFFFF); +} + +void kon_debugDraw(kon_debug_t *d, kon_framebuffer_t *fb) +{ + if (!d) return; + float target = d->open ? 1.0f : 0.0f; + float step = (float)d->frameDt * 8.0f; + + if (d->slide < target) d->slide = d->slide + step > target ? target : d->slide + step; + else if (d->slide > target) d->slide = d->slide - step < target ? target : d->slide - step; + + if (d->overlay) kon_debugDrawOverlay_(d, fb); + if (d->slide > 0.0f) kon_debugDrawConsole_(d, fb); +} + +#endif diff --git a/src/impl/kondebug.c b/src/impl/kondebug.c new file mode 100644 index 0000000..3a9abb1 --- /dev/null +++ b/src/impl/kondebug.c @@ -0,0 +1,2 @@ +#define KONDEBUG_IMPLEMENTATION +#include "kondebug.h" |
