/* * main.cpp * MontaukOS Crashpad — displays crash reports in a popup window * Copyright (c) 2026 Daniel Hammer */ #include #include #include #include #include #include using namespace gui; static constexpr int WINDOW_W = 460; static constexpr int WINDOW_H = 480; static constexpr int PADDING = 12; static constexpr int HEADER_H = 48; static constexpr int COL1_W = 200; static constexpr int SECTION_GAP = 10; WsWindow g_win; montauk::abi::CrashReportInfo g_report = {}; int g_scroll = 0; // Colors - light theme matching DiskDetail static constexpr Color BG = Color::from_rgb(0xFF, 0xFF, 0xFF); static constexpr Color ROW_EVEN = Color::from_rgb(0xF7, 0xF7, 0xF7); static constexpr Color ACCENT = Color::from_rgb(0x33, 0x66, 0xCC); static constexpr Color BORDER = Color::from_rgb(0xE0, 0xE0, 0xE0); static constexpr Color TEXT_COLOR = Color::from_rgb(0x22, 0x22, 0x22); static constexpr Color DIM_TEXT = Color::from_rgb(0x66, 0x66, 0x66); static constexpr Color VALUE_COL = Color::from_rgb(0x22, 0x22, 0x22); // Custom hex formatter (avoids snprintf which isn't in freestanding libc) static void format_hex16(char* out, uint64_t val) { out[0] = '0'; out[1] = 'x'; static const char hex[] = "0123456789ABCDEF"; for (int i = 0; i < 16; i++) { out[2 + i] = hex[(val >> (60 - i * 4)) & 0xF]; } out[18] = '\0'; } static void format_decimal(char* out, int val) { if (val == 0) { out[0] = '0'; out[1] = '\0'; return; } if (val < 0) { *out++ = '-'; val = -val; } char tmp[16]; int i = 0; while (val > 0) { tmp[i++] = '0' + (val % 10); val /= 10; } while (i > 0) { *out++ = tmp[--i]; } *out = '\0'; } // Draw a section header label + horizontal rule static void section_header(Canvas& c, int& y, int w, int fh, const char* label) { if (y < -fh - 8 || y > c.h + fh + 8) { y += fh + 8; return; } c.text(PADDING, y, label, ACCENT); y += fh; c.hline(PADDING, y, w - PADDING * 2, BORDER); y += 2; } // Draw one alternating table row (key left, value right) static void table_row(Canvas& c, int& y, int row_h, int col1_w, int w, int rowIdx, const char* key, const char* val, Color val_col) { if (y > c.h + row_h || y < -row_h) { y += row_h; return; } if (rowIdx % 2 == 0) c.fill_rect(PADDING, y, w - PADDING * 2, row_h, ROW_EVEN); int ty = y + (row_h - system_font_height()) / 2; c.text(PADDING + 8, ty, key, DIM_TEXT); c.text(PADDING + col1_w + 8, ty, val, val_col); y += row_h; } static void render() { if (g_win.id < 0) return; Canvas c = g_win.canvas(); c.fill(BG); int fh = system_font_height(); int row_h = fh + 8; int w = g_win.width; int y = PADDING - g_scroll; int rowIdx = 0; // Process info header (outside table) if (y >= -fh * 2 && y < c.h + fh) { char buf[128]; char* p = buf; // Process name (large, white) const char* name = g_report.processName; while (*name) *p++ = *name++; *p++ = ' '; *p++ = '('; *p++ = 'P'; *p++ = 'I'; *p++ = 'D'; *p++ = ' '; format_decimal(p, g_report.pid); while (*p) p++; *p++ = ')'; *p = '\0'; c.text(PADDING, y, buf, TEXT_COLOR); y += fh + 4; // Exception name below char excbuf[64]; p = excbuf; const char* exc = g_report.exceptionName; while (*exc) *p++ = *exc++; *p++ = ' '; *p++ = '('; *p++ = 'v'; *p++ = 'e'; *p++ = 'c'; *p++ = 't'; *p++ = 'o'; *p++ = 'r'; *p++ = ' '; *p++ = '0'; *p++ = 'x'; static const char hex[] = "0123456789ABCDEF"; *p++ = hex[(g_report.exceptionVector >> 4) & 0xF]; *p++ = hex[g_report.exceptionVector & 0xF]; *p++ = ')'; *p++ = '\0'; c.text(PADDING, y, excbuf, DIM_TEXT); y += fh; } else { y += fh + 4 + fh; } y += SECTION_GAP; // Registers section section_header(c, y, w, fh, "Registers"); char hexbuf[32]; auto draw_hex_row = [&](const char* key, uint64_t val) { format_hex16(hexbuf, val); table_row(c, y, row_h, COL1_W, w, rowIdx++, key, hexbuf, VALUE_COL); }; draw_hex_row("Instruction Pointer", g_report.instructionPointer); draw_hex_row("Code Segment", g_report.codeSegment); draw_hex_row("Flags", g_report.flags); draw_hex_row("Stack Pointer", g_report.stackPointer); draw_hex_row("Stack Segment", g_report.stackSegment); y += SECTION_GAP; // Page Fault section (only for vector 0x0E) if (g_report.exceptionVector == 0x0E) { section_header(c, y, w, fh, "Page Fault"); format_hex16(hexbuf, g_report.faultingAddress); table_row(c, y, row_h, COL1_W, w, rowIdx++, "Faulting Address (CR2)", hexbuf, TEXT_COLOR); // Error code bits auto draw_pf_bit = [&](const char* label, bool val) { Color col = val ? Color::from_rgb(0x22, 0x88, 0x22) : Color::from_rgb(0xAA, 0xAA, 0xAA); table_row(c, y, row_h, COL1_W, w, rowIdx++, label, val ? "1" : "0", col); }; draw_pf_bit("Present", g_report.pfPresent); draw_pf_bit("Write", g_report.pfWrite); draw_pf_bit("User", g_report.pfUser); draw_pf_bit("Reserved Write", g_report.pfReservedWrite); draw_pf_bit("Instruction Fetch", g_report.pfInstructionFetch); draw_pf_bit("Protection Key", g_report.pfProtectionKey); draw_pf_bit("Shadow Stack", g_report.pfShadowStack); draw_pf_bit("SGX", g_report.pfSGX); y += SECTION_GAP; } g_win.present(); } extern "C" void _start() { fonts::init(); // Poll for crash report int available = montauk::crash_report(&g_report); if (available <= 0) montauk::exit(1); if (!g_win.create("Process Crashed", WINDOW_W, WINDOW_H)) montauk::exit(1); render(); while (true) { montauk::abi::WinEvent ev; int r = g_win.poll(&ev); if (r < 0) break; if (r == 0) { montauk::sleep_ms(16); continue; } if (ev.type == 3) break; // close if (ev.type == 0 && ev.key.pressed) { if (ev.key.scancode == 0x01) break; // Escape if (ev.key.scancode == 0x51) { // Page Down: scroll down g_scroll += 60; render(); } if (ev.key.scancode == 0x49) { // Page Up: scroll up g_scroll -= 60; if (g_scroll < 0) g_scroll = 0; render(); } } if (ev.type == 2) { // Resize render(); } } g_win.destroy(); montauk::exit(0); }