/* SPDX-License-Identifier: MIT */ /* * kondebug.c * * 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. */ #include "kondebug.h" #ifdef KON_DEVELOPER #include #include #include #include #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; float baseSize, size; int ctrl; int overlayKey, consoleKey; int overlay; int open; float slide; int swallowTick; int quit; 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; double times[4]; /* update, draw, present, blit */ float graph[KON_DEBUG_GRAPH]; int graphHead; }; /*** private helpers ***/ static void kon_debugMetrics_(kon_debug_t *d) { int h = 0; kon_measureText(d->font, "A", NULL, &h); d->ascent = h * 4 / 5; d->lineH = h + 2; } static void kon_debugResize_(kon_debug_t *d, float size) { if (size < 8.0f) size = 8.0f; if (size > 64.0f) size = 64.0f; if (size == d->size) return; if (kon_fontResize(d->font, size)) { d->size = size; kon_debugMetrics_(d); } } 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 "); 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_debugCmdQuit_(kon_debug_t *d, void *user, int argc, char **argv) { (void)user; (void)argc; (void)argv; d->quit = 1; } static void kon_debugCmdFontSize_(kon_debug_t *d, void *user, int argc, char **argv) { (void)user; if (argc > 1) kon_debugResize_(d, (float)atof(argv[1])); kon_debugLog(d, "fontsize = %g", (double)d->size); } 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; if (!font) return NULL; d = calloc(1, sizeof(kon_debug_t)); if (!d) return NULL; d->font = font; d->baseSize = d->size = kon_fontPixelHeight(font); kon_debugMetrics_(d); d->overlayKey = KON_KEY_F1; d->consoleKey = KON_KEY_GRAVE; d->last = kon_getTime(); kon_debugCommand(d, "help", "list commands, or help ", 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_debugCommand(d, "fontsize", "text size in pixels, ctrl + and ctrl - too", kon_debugCmdFontSize_, NULL); kon_debugCommand(d, "quit", "close the game", kon_debugCmdQuit_, NULL); kon_debugCommand(d, "exit", "close the game", kon_debugCmdQuit_, 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; } int kon_debugQuitRequested(const kon_debug_t *d) { return d ? d->quit : 0; } 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->key == KON_KEY_LEFT_CTRL || e->key == KON_KEY_RIGHT_CTRL) { if (e->type == KON_EVENT_KEY_DOWN) d->ctrl = 1; if (e->type == KON_EVENT_KEY_UP) d->ctrl = 0; } if (e->type == KON_EVENT_KEY_DOWN) { if (d->ctrl && (d->open || d->overlay)) { if (e->key == KON_KEY_EQUAL) { kon_debugResize_(d, d->size + 2.0f); return 1; } if (e->key == KON_KEY_MINUS) { kon_debugResize_(d, d->size - 2.0f); return 1; } if (e->key == KON_KEY_0) { kon_debugResize_(d, d->baseSize); return 1; } } 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->ctrl) return 1; 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_MOUSE_WHEEL) { d->scroll += e->wheel * 3; if (d->scroll < 0) d->scroll = 0; if (d->scroll > d->logCount - 1) d->scroll = d->logCount > 0 ? d->logCount - 1 : 0; 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; } void kon_debugTimes(kon_debug_t *d, double update, double draw, double present, double blit) { double in[4]; int i; if (!d) return; in[0] = update; in[1] = draw; in[2] = present; in[3] = blit; for (i = 0; i < 4; i++) d->times[i] += (in[i] - d->times[i]) * 0.1; } static void kon_debugBox_(const kon_debug_t *d, kon_framebuffer_t *fb, int x, int y, const char *s, uint32_t color) { int w = 0; kon_measureText(d->font, s, &w, NULL); kon_fillRectangle(fb, x - 2, y - 1, w + 4, d->lineH, 0xB0000000); kon_debugText_(d, fb, x, y, s, color); } 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_debugBox_(d, fb, x, y, line, white); y += d->lineH + 3; if (d->overlay >= 1) { snprintf(line, sizeof(line), "upd %.1f draw %.1f scale %.1f blit %.1f", d->times[0] * 1000.0, d->times[1] * 1000.0, d->times[2] * 1000.0, d->times[3] * 1000.0); kon_debugBox_(d, fb, x, y, line, dim); y += d->lineH + 3; 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_debugBox_(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_debugBox_(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_debugBox_(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_debugBox_(d, fb, x, y, line, dim); y += d->lineH; } } } else if (d->overlay >= 2) { kon_debugBox_(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->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