From c4d4f205483bbe2ea56c359adf5bd67f96f7332f Mon Sep 17 00:00:00 2001 From: Nexoniarz Date: Sat, 12 Sep 2026 18:28:23 +0200 Subject: [PATCH] gfx: Guru on the crash screen, and the loader rebuilds when the logo changes PSOGM drew ":(" -- two dots and a parenthesis. It now draws Guru, the icon from docs/branding/: a chamfered head with two eye knockouts and a flat mouth, in the :| register. The old reasoning is kept in the file rather than deleted, because it is the thing being traded away: ":(" was "legible as a mood without being a picture of anything, and does not pretend the machine has feelings about what just happened." That is a real argument and a face is a louder claim. What it buys is identity -- ":(" is recognisably Microsoft's, and a crash screen is the single screenshot most likely to leave the machine, so it is the worst place to be wearing somebody else's face. The octagon also quotes the chamfered corners of the old SchOS letterforms, which is the only part of that mark still in use. Still drawn from primitives, for the original reason: it scales with the display and costs no space in the kernel image. A scanline fill for the head, two rectangles and one more knocked out of it -- no allocation, no locks, nothing that can fail halfway, which is the whole constraint of panic context. The geometry is expressed in the same 200-unit box docs/branding/guru.svg uses so the two cannot drift without somebody editing a number on purpose. The eyes and mouth are knocked out of the head rather than drawn on top of it, which is what keeps them legible small: a hole in a solid reads at sizes a stroke does not. Verified by crashing on purpose -- echo c > /proc/sysrq -- and screendumping the framebuffer through the QEMU monitor. The serial report still says ":(", and should: Guru cannot be drawn in ASCII, and a mood marker in a log is not the icon. Separately, and found by looking at the boot splash rather than at the code: boot/common/ui.c includes logo.h, theme.h and font.h, and the Makefile rule for the loader listed none of them. A new logo therefore did not rebuild the loader. The splash had been drawing a mask compiled six hours earlier while `make` reported nothing to do, and the only symptom was a boot screen quietly disagreeing with the kernel about what the logo is. The kernel half never showed it because those objects are built with -MD -MP and track their own headers. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01VQ7Qx8E1q1M1bbCq8QLHS2 --- Makefile | 9 +++- docs/branding/README.md | 18 +++++++ kernel/core/panic.c | 106 ++++++++++++++++++++++------------------ 3 files changed, 85 insertions(+), 48 deletions(-) diff --git a/Makefile b/Makefile index 27cb7a9..25ec4b6 100644 --- a/Makefile +++ b/Makefile @@ -471,8 +471,15 @@ kernel: $(BUILD)/kernel/kernel # ----------------------------------------------------------------------- UEFI_CFLAGS = --target=x86_64-unknown-windows -ffreestanding -fshort-wchar -mno-red-zone -fno-stack-protector -Wall -Wextra -O1 -I./include +# The three headers at the end are the ones boot/common/ui.c actually includes, +# and leaving them out meant a new logo did not rebuild the loader: the splash +# kept drawing whatever mask was compiled in months earlier, `make` reported +# nothing to do, and the only symptom was a boot screen that disagreed with the +# kernel about what the logo is. The kernel half never showed it because those +# objects are built with -MD -MP and pick up header changes on their own. $(BUILD)/uefi/BOOTX64.EFI: boot/uefi/main.c boot/uefi/efi.h include/schboot.h \ - boot/common/ui.c boot/common/ui.h + boot/common/ui.c boot/common/ui.h \ + include/logo.h include/theme.h include/font.h @mkdir -p $(BUILD)/uefi clang $(UEFI_CFLAGS) -c boot/uefi/main.c -o $(BUILD)/uefi/main.obj clang $(UEFI_CFLAGS) -c boot/common/ui.c -o $(BUILD)/uefi/ui.obj diff --git a/docs/branding/README.md b/docs/branding/README.md index c498293..0f86089 100644 --- a/docs/branding/README.md +++ b/docs/branding/README.md @@ -164,6 +164,24 @@ If either is edited, edit both. --- +## Where each mark is actually drawn + +| | | +|---|---| +| Loader splash | Shift, from `include/logo.h`, shaded per row by `metal()` in `boot/common/ui.c` — silver, not accent. | +| Kernel console / early boot | Shift, same mask, composited flat in a theme colour by `gfx_draw_logo`. | +| PSOGM | Guru, drawn from primitives by `draw_guru()` in `kernel/core/panic.c`. Never a bitmap: it scales with the display and costs nothing in the kernel image. | +| Serial crash report | `:(`, as text. Guru cannot be drawn in ASCII, and a mood marker in a log is not the icon. | +| SchFS | **Nowhere yet.** `uberblock.svg` has no consumer; it is waiting for `projects/schfs-linux/` to have a README worth heading. | + +One trap, since it has already been stepped in: `boot/common/ui.c` includes +`logo.h`, `theme.h` and `font.h`, and the Makefile rule for the loader did not +list them. A new logo therefore did not rebuild the loader — `make` reported +nothing to do and the splash kept drawing a mask compiled months earlier. The +kernel half never showed the problem because those objects are built with +`-MD -MP` and track their own headers. **Changing anything in here means the +loader must actually relink**, and that needs `clang` and `lld-link`. + ## What this does not cover - **No trademark position.** These are marks the project uses, not marks anyone diff --git a/kernel/core/panic.c b/kernel/core/panic.c index 75588bc..28df9b9 100644 --- a/kernel/core/panic.c +++ b/kernel/core/panic.c @@ -90,63 +90,75 @@ put_hex(char *p, char *end, uint64 v, int digits) /* ------------------------------------------------------------------ */ /* The face */ /* */ -/* ":(" — two dots and a parenthesis, drawn large. It is a sad face */ -/* only if you tilt your head, which is exactly why it works: it is */ -/* legible as a mood without being a picture of anything, and it does */ -/* not pretend the machine has feelings about what just happened. */ +/* Guru: a chamfered head with two eyes and a flat mouth. See */ +/* docs/branding/ -- it is the system's icon, and the only place it is */ +/* allowed to appear is here. */ /* */ -/* Drawn from primitives rather than stored as a bitmap, so it scales */ -/* with the display and costs no space in a kernel image that stage2 */ -/* loads one sector at a time by PIO. */ +/* This replaced ":(", two dots and a parenthesis, whose argument was */ +/* that it is "legible as a mood without being a picture of anything, */ +/* and does not pretend the machine has feelings about what just */ +/* happened." That reasoning was good and is recorded because it is */ +/* the thing being traded away: a face is a louder claim than a */ +/* punctuation mark, and it has to earn the volume. */ +/* */ +/* What it buys is identity. ":(" is Microsoft's, recognisably, and a */ +/* crash screen is the single screenshot most likely to leave the */ +/* machine -- so it is the worst place to be wearing somebody else's */ +/* face. The octagon also quotes the chamfered corners of the old */ +/* SchOS letterforms, which is the only part of that mark still in use. */ +/* */ +/* Still drawn from primitives rather than stored as a bitmap, for the */ +/* original reason: it scales with the display and costs no space in a */ +/* kernel image. Two rectangles and a run of spans -- no allocation, no */ +/* locks, nothing that can fail halfway. */ /* ------------------------------------------------------------------ */ +/* Geometry, in the 200-unit box docs/branding/guru.svg is drawn in, so the + * two cannot drift apart without somebody editing a number here on purpose. + * The head spans 24..176 of that box; everything is expressed against it. */ +#define GURU_BOX_LO 24 /* head's left/top edge in the SVG box */ +#define GURU_BOX_SPAN 152 /* head's width and height there */ +#define GURU_CHAMFER 34 /* corner cut */ + +/* SVG coordinate -> pixel offset within a head `h` tall. */ static int -isqrt(int v) +gmap(int v, int h) { - if (v <= 0) - return 0; - - // Newton's method, iterated only while the estimate is still *decreasing*. - // - // The obvious "stop when it stops changing" version does not terminate: on a - // non-square it settles into a two-cycle rather than a fixed point, and - // isqrt(3) alternates between 1 and 2 forever. In a panic handler that is a - // hang with the screen half drawn and no way to find out why. - int x = v; - int y = (x + 1) / 2; - while (y < x) { - x = y; - y = (x + v / x) / 2; - } - return x; -} - -// The "(" of ":(" — the left half of a squashed circle, so the tips point right -// and the middle bulges left, the way the glyph does. -static void -draw_paren(int cx, int cy, int h, int thick, uint32 color) -{ - int r = h / 2; - for (int dy = -r; dy <= r; dy++) { - int dx = isqrt(r * r - dy * dy); - // Narrow it: a true half-circle reads as a bracket, not a parenthesis. - int x = cx - (dx * 45) / 100; - gfx_fill_circle(x, cy + dy, thick / 2, color); - } + return (v - GURU_BOX_LO) * h / GURU_BOX_SPAN; } static void -draw_sad_face(int x, int y, int h, uint32 color) +draw_guru(int x, int y, int h, uint32 color, uint32 bg) { - int dot = h / 12; - int gap = h / 4; + /* The head, as horizontal spans. A scanline fill rather than a polygon + * routine: the shape is convex and its left edge is one subtraction, so + * this is the whole of it. */ + for (int py = 0; py < h; py++) { + int sy = GURU_BOX_LO + py * GURU_BOX_SPAN / h; /* back to SVG space */ + int inset = 0; + if (sy < GURU_BOX_LO + GURU_CHAMFER) + inset = (GURU_BOX_LO + GURU_CHAMFER) - sy; + else if (sy > GURU_BOX_LO + GURU_BOX_SPAN - GURU_CHAMFER) + inset = sy - (GURU_BOX_LO + GURU_BOX_SPAN - GURU_CHAMFER); - // The colon. - gfx_fill_circle(x + dot, y + h / 2 - gap, dot, color); - gfx_fill_circle(x + dot, y + h / 2 + gap, dot, color); + int x0 = gmap(GURU_BOX_LO + inset, h); + int x1 = gmap(GURU_BOX_LO + GURU_BOX_SPAN - inset, h); + if (x1 > x0) + gfx_fill_rect(x + x0, y + py, x1 - x0, 1, color); + } - // The parenthesis, a little to the right of it. - draw_paren(x + dot * 2 + h / 2, y + h / 2, h, dot * 2, color); + /* The eyes and the mouth are knocked out of the head rather than drawn on + * top of it, which is what keeps them legible when the head is small: a + * hole in a solid reads at sizes a stroke does not. */ + int ey = gmap(82, h), eh = gmap(104, h) - gmap(82, h); + int e1x = gmap(74, h), e2x = gmap(110, h); + int ew = gmap(90, h) - gmap(74, h); + gfx_fill_rect(x + e1x, y + ey, ew, eh, bg); + gfx_fill_rect(x + e2x, y + ey, ew, eh, bg); + + int mx = gmap(72, h), mw = gmap(128, h) - gmap(72, h); + int my = gmap(128, h), mh = gmap(139, h) - gmap(128, h); + gfx_fill_rect(x + mx, y + my, mw, mh, bg); } /* ------------------------------------------------------------------ */ @@ -435,7 +447,7 @@ paint(char *reason, addr_t *pcs, int npcs) int y = sh / 8; int face_h = 96; - draw_sad_face(left, y, face_h, PSOGM_FG); + draw_guru(left, y, face_h, PSOGM_FG, PSOGM_BG); y += face_h + ch * 2; // Left-aligned, not centred. Centred text reads as a poster; this is a