The F8 menu is redrawn in the Niveus language: a window with three lights, bevelled keys with a lit edge, wells for panels, anti-aliased corners and a soft shadow, set in Instrument Sans rasterised into include/gfx/nvfont.h (tools/python/mknvfont.py, make nvfont). Scale doubles on screens 1400 px tall or more. Lists taller than the screen scroll. The keyboard still drives everything, and a mouse, touchpad or tablet does too: both UEFI pointer protocols are polled, hover moves the highlight, a click activates. The cursor stays hidden until the pointer has really done something, since the firmware installs a pointer protocol with or without a mouse. The 'keep this resolution?' countdown is one ten-second budget however many times the pointer moves. Loaders found at well-known paths on any volume (Windows, Ubuntu, Debian, Fedora, Arch, openSUSE, systemd-boot, rEFInd, other BOOTX64.EFI) are listed and chain-loaded with LoadImage/StartImage. 'Reboot to firmware' used to halt; it now sets OsIndications and restarts, and Restart and Shut Down call ResetSystem. Checked under QEMU+OVMF: menu, resolution list (incl. 2560x1600 at double scale), system information, shut down, firmware setup, chain-loading a copy of the loader from a second disk, and a full boot of the ISO to the login prompt. Pointer hover and click were checked only with synthetic events: in this setup a USB mouse or tablet delivered no event to the loader (one USB I/O handle appeared but no pointer protocol came from it), and why was not established. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EVRhxi9zpdW2uDUN4FVcao
80 lines
3.3 KiB
Python
80 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Rasterise Niveus's interface face into a header the boot manager can draw.
|
|
|
|
The loader cannot carry a TrueType rasteriser, and the answer to that has been
|
|
the same since include/gfx/font.h: do the work at build time and commit the
|
|
result. This takes Instrument Sans (SIL OFL, see docs/Website/niveus/fonts/)
|
|
at three sizes and writes 8-bit coverage bitmaps, one per printable ASCII
|
|
character, with the metrics needed to lay them out proportionally.
|
|
|
|
python3 tools/python/mknvfont.py docs/Website/niveus/fonts/static include/gfx/nvfont.h
|
|
|
|
Needs Pillow; nothing else, and nothing at all at build time of the OS.
|
|
"""
|
|
import sys, os
|
|
from PIL import ImageFont
|
|
|
|
# Each size twice: x1 for ordinary screens and x2 for the ones where a 15 px
|
|
# face would be a speck. Rendering at the real size beats doubling pixels.
|
|
SIZES = []
|
|
for _scale in (1, 2):
|
|
SIZES += [
|
|
("body%d" % _scale, "InstrumentSans-Medium.ttf", 15 * _scale),
|
|
("small%d" % _scale, "InstrumentSans-Regular.ttf", 12 * _scale),
|
|
("title%d" % _scale, "InstrumentSans-SemiBold.ttf", 24 * _scale),
|
|
]
|
|
FIRST, LAST = 32, 126
|
|
|
|
|
|
def render(path, px):
|
|
f = ImageFont.truetype(path, px)
|
|
asc, desc = f.getmetrics()
|
|
glyphs, blob = [], bytearray()
|
|
for code in range(FIRST, LAST + 1):
|
|
ch = chr(code)
|
|
adv = int(round(f.getlength(ch) * 64)) # 26.6 fixed point: whole-pixel advances make text look cramped
|
|
mask, (ox, oy) = f.getmask2(ch, mode="L", anchor="ls")
|
|
w, h = mask.size
|
|
data = bytes(mask) if (w and h) else b""
|
|
glyphs.append((len(blob), w, h, ox, oy, adv))
|
|
blob += data
|
|
return asc, desc, glyphs, blob
|
|
|
|
|
|
def main():
|
|
src, out = sys.argv[1], sys.argv[2]
|
|
lines = [
|
|
"#pragma once",
|
|
"",
|
|
"// GENERATED by tools/python/mknvfont.py -- do not edit. Run `make nvfont`.",
|
|
"// source: docs/Website/niveus/fonts/static (Instrument Sans, SIL OFL 1.1)",
|
|
"//",
|
|
"// 8-bit coverage bitmaps for printable ASCII, proportionally spaced, used by",
|
|
"// the boot manager (boot/common/ui.c). Not included by the kernel.",
|
|
"",
|
|
"struct nvglyph { unsigned short off; unsigned char w, h; signed char ox, oy; unsigned short adv; /* 1/64 px */ };",
|
|
"struct nvfont { unsigned char ascent, descent; const struct nvglyph *g; const unsigned char *bits; };",
|
|
"#define NVFONT_FIRST %d" % FIRST,
|
|
"#define NVFONT_LAST %d" % LAST,
|
|
"",
|
|
]
|
|
for name, fn, px in SIZES:
|
|
asc, desc, glyphs, blob = render(os.path.join(src, fn), px)
|
|
lines.append("static const struct nvglyph nvg_%s[] = {" % name)
|
|
for (o, w, h, ox, oy, adv) in glyphs:
|
|
lines.append(" {%d,%d,%d,%d,%d,%d}," % (o, w, h, ox, oy, adv))
|
|
lines.append("};")
|
|
lines.append("static const unsigned char nvb_%s[] = {" % name)
|
|
for i in range(0, len(blob), 24):
|
|
lines.append(" " + ",".join(str(b) for b in blob[i:i + 24]) + ",")
|
|
if not blob:
|
|
lines.append(" 0")
|
|
lines.append("};")
|
|
lines.append("static const struct nvfont nvfont_%s = { %d, %d, nvg_%s, nvb_%s };" %
|
|
(name, asc, desc, name, name))
|
|
lines.append("")
|
|
open(out, "w").write("\n".join(lines) + "\n")
|
|
print("wrote", out)
|
|
|
|
|
|
main()
|