aboutsummaryrefslogtreecommitdiffstats
Commit message (Collapse)AuthorAgeFilesLines
* Carry variation sequences (cmap format 14)eunhhu2026-09-285-21/+119
| | | | | | | | | | | | | | | The section now carries each sequence the font maps, a base and a selector, with the glyph HarfBuzz gives the pair (a default one's the base's own). As HarfBuzz's normalizer does, a sequence the font has becomes that one glyph, the two characters' clusters merged; one it lacks still passes on as two, the selector hidden. The header grows to 88 bytes for the table. The random check now mixes in the font's own sequences and stray selectors: 30000 runs over Noto Sans CJK's 14787 ideographic variation sequences are HarfBuzz's, and so are the installed fonts' and Noto's. HarfBuzz's suite: 5014 of 5199 the same, none different; what is left is what the build refuses and one run without a plan.
* Map characters as HarfBuzz maps them, not as it lists themeunhhu2026-09-286-48/+91
| | | | | | | | | | | | | | | The shaping section's character map came from hb_face_collect_unicodes. HarfBuzz's own suite showed where that list and its mapping part: it cuts a format 13 map short (AdobeBlank2's whole Unicode to one glyph), and a symbol map (3,0) it searches through the private use area, Latin and, in an old Arabic font, Arabic too. Now every code point is asked with hb_font_get_nominal_glyph (about 30 ms a font) and the map carried as groups: codes going to consecutive glyphs, or all to one (12 bytes each, so a format 13 map stays small). HarfBuzz's suite: 5011 of 5199 cases the same (1355 before). The installed fonts (213) and the Noto ones (21) still shape as HarfBuzz does on random runs.
* Hold shaping to HarfBuzz's own test suite, case by caseeunhhu2026-09-284-3/+328
| | | | | | | | | | | | | | | | | | | | | | text/harfbuzz-suite.py reads a HarfBuzz release's test/shape/data (in-house, text-rendering-tests, aots: fonts and inputs, many added when a font or a shaper once went wrong) and shapes every case both ways with the new vega-symbols shape-cases: it compiles a font's section for the scripts its cases need (guessed as HarfBuzz guesses them), then compares each run with HarfBuzz's, glyph for glyph and unit for unit. The oracle is HarfBuzz run live, not the recorded outputs, which depend on options Vega has no counterpart for (font functions, sizes, output formats). Cases asking for what Vega does not do (user features, variations, languages, context, cluster levels, buffer flags, vertical text) and fonts of other systems are left out and counted by why. text/harfbuzz-suite.known lists what differs today, and why: AAT, the character map built from what HarfBuzz lists rather than how it maps (format 13, symbol maps into the private use area), variation selectors, Arabic fallback with shared glyphs, kern format 2, Zawgyi, and one run without a plan. Any other difference fails the run; a listed case that agrees is named. make check-harfbuzz HARFBUZZ_SRC=... runs it (make check too, when given): 5929 cases, 5199 compared, 1355 the same.
* Set type on the pixel surfaces: the grid lays out, the fonts shapeeunhhu2026-09-289-91/+612
| | | | | | | | | | | | | | | | | | | | | | | | vega_draw keeps what it wrote as spans (VegaSpan: a row, the columns it was given, where its cells reached, its text and tone). A span written where the same pen stopped follows the one before it (a field's name and value, the help line, the prompt); one written after the pen moved (a table's column, a list's label, a badge) starts afresh at its column, and a badge sits flush with its end. When the first font shapes, the pixels set type: a span and those following it are one line of text, laid out as one paragraph in the font stack (bidi and kerning cross the spans; each glyph takes the tone of the span its character came from), from its column up to the nearest of its spans' ends and the next span on the row, cut at a grapheme cluster boundary with an ellipsis if it must be. The prompt gets a text cursor. --symbols repeats: fonts in order of preference (at most 8); the cells, when the first font does not shape, also fall back through them. The check sets a label in one script, alone in its list, and holds it to what the font that has it sets by itself (vega-symbols paragraph, which now sizes itself to its text with width 0), pixel for pixel: Latin kerning, Hangul, Arabic and Devanagari over DejaVu Sans, Noto Sans CJK KR and FreeSans.
* Set text in a stack of fonts: each grapheme cluster in the first that has iteunhhu2026-09-288-47/+131
| | | | | | | | | | | | | | vega_layout takes fonts in order of preference (VegaFonts). A grapheme cluster goes to the first font that has all its characters (default ignorables aside: shaping hides them); a cluster of Common and Inherited characters stays in the font before it when that one has it, so the spaces between Arabic words stay in the Arabic font; failing that, the first font with its first character, else the first font. Runs split where the font changes too. Positions are in the first font's units, a run's pen scaled as a whole so rounding does not add up. Callers can narrow what a font has (a symbol file holds only the glyphs its codes reach). vega_default_ignorable is shape.c's HarfBuzz 10.2 set, now public.
* Size the checks' terminals before the program startseunhhu2026-09-281-4/+3
| | | | | | | | The live terminal checks forked a pty, started Vega's TUI, and only then set the window size and sent SIGWINCH. A TUI that read the size first drew its opening frames at the pty's default size, and replaying them into the smaller screen failed now and then (one run in about eleven). The child now sets the size on its own terminal before it starts.
* Shape Syriac, stretch with stch, and fall back for fonts without Arabic formseunhhu2026-09-287-40/+853
| | | | | | | | | | | | | | | | | | | | | The rest of HarfBuzz 10.2's Arabic shaper. Joining types now come from HarfBuzz's own table (Unicode 16, with Syriac's Alaph and Dalath-Rish groups the state machine's last two columns use), the general category deciding where it has none, so Syriac shapes with fin2, fin3 and med2. What stch multiplies is recorded and, after positioning, stretched under the word before it, repeating pieces copied as often as fit. A font with presentation forms but no Arabic features gets HarfBuzz's fallback lookups, which the build tool writes as OpenType and the runtime applies at their pause like any GSUB lookup. The generator now reads HarfBuzz's generated tables through their lookup functions' ranges and offsets: rows whose range starts mid-row have blank cells in front, which the row comments alone miscounted (Arabic Supplement); the Indic table, read again this way, did not change. Noto Sans Syriac, FreeSans's Syriac, Unifont's Arabic and Noto Sans Arabic shape as HarfBuzz does at 3,000 runs a plan; no installed font is refused any more (213 fonts, 1,822 plans).
* Shape Thai and Lao as HarfBuzz doeseunhhu2026-09-284-14/+147
| | | | | | | | | | | | | | The Thai shaper, from HarfBuzz 10.2's source: SARA AM comes apart into NIKHAHIT and SARA AA, the NIKHAHIT moving back over the above-base marks (and counting as a mark when advances are zeroed). A font with no Thai script of its own (only DFLT or latn in GSUB) gets its marks shifted to its private-use glyphs, Windows' before the Mac's, as the stacking around the base asks; the planner records, as HarfBuzz's does, whether the font had the script under its own tag. Noto Sans Thai and Lao, TLWG Loma, FreeSerif's Thai, DejaVu Sans's Lao and Unifont shape as HarfBuzz does at 3,000 runs a plan, and Loma with its thai script taken out (so the private-use path runs) at 5,000.
* Shape the Universal Shaping Engine's scripts as HarfBuzz doeseunhhu2026-09-287-25/+839
| | | | | | | | | | | | | | | | | | | | | | The USE shaper, from HarfBuzz 10.2's source, for Sinhala, Tibetan, Javanese, Balinese, Mongolian and the rest of its eighty-odd scripts (and Indic fonts with the new dev3-style tags). The syllable machine reads past CGJs and ZWNJs that marks follow; rphf goes on each syllable's first glyphs and records the repha it made, pref records its pre-base glyph, with substitution marks cleared between; a repha moves before the first post-base glyph and pre-base vowels move back. Scripts that join like Arabic take Arabic's forms (Mongolian's free variation selectors their base's), the others join syllable to syllable. Marks lose their advances before GPOS. text/syllabic.py carries HarfBuzz's compressed USE table over as it is, compiles the USE grammar with the others (100,000 random runs against Ragel's machine: no syllable differs), and checks at compile time that the categories syllabic.h names are the machines' own. Noto Sans Sinhala, Javanese, Balinese, Mongolian, Noto Serif Tibetan, FreeSerif's Sinhala and Unifont's five shape as HarfBuzz does at 5,000 runs a plan; all installed fonts (1,748 plans) and 21 Noto fonts too.
* Leave U+180F visible, as HarfBuzz 10.2 doeseunhhu2026-09-281-1/+1
| | | | | | | HarfBuzz's is_default_ignorable takes U+180B..180E from the Mongolian block, not the fourth free variation selector (U+180F, default ignorable since Unicode 14), so it keeps its own glyph rather than the invisible one. Unifont's Mongolian showed it.
* Shape Khmer and Myanmar as HarfBuzz doeseunhhu2026-09-284-34/+246
| | | | | | | | | | | | Two more shapers on the syllabic groundwork, from HarfBuzz 10.2's source. Khmer reorders before any lookup: a coeng and Ro go before the base, masked for pref, what follows them for cfar, a left matra goes first; split matras decompose with U+17C1. Myanmar finds the base after a kinzi, gives each glyph its place around it and sorts by place, and zeroes mark advances before GPOS rather than after. Noto Sans Khmer and Noto Sans Myanmar shape as HarfBuzz does at 20,000 runs a plan, and so do Unifont's Khmer and Myanmar (no GSUB).
* Shape the nine Indic scripts as HarfBuzz doeseunhhu2026-09-288-84/+2065
| | | | | | | | | | | | | | | | | | | | | | | | | | The Indic shaper, from HarfBuzz 10.2's source: syllables found by the Indic machine; each syllable's base consonant, its pre-base matras and reph moved around it and masked for the basic features, applied one at a time within the syllable; then matras, reph and pre-base-reordering consonants placed again by what the features made. Vowel constraints, Indic decompositions and compositions, dotted circles for broken syllables, and per-syllable matching come with it. Plans carry GSUB pauses (steps where the shaper does its part) and the stages of the features the shaper asks whether they would substitute; the runtime answers with each subtable's would_apply, as HarfBuzz does. Masks are now the common ones and each shaper's own. text/syllabic.py reads HarfBuzz's Ragel grammars (Indic, Khmer, Myanmar) and compiles each to a minimal DFA with Ragel's scanner semantics; beside the machines Ragel generated from the same grammars, 100,000 random runs each find no syllable that differs. It also reads HarfBuzz's Indic table (Unicode 16) cell for cell. Every installed font shapes as HarfBuzz does (213 fonts, 1,688 plans); the Indic ones at 20,000 runs a plan (FreeSerif's eight scripts, FreeSans, Unifont's nine without GSUB), and nine new-spec Noto Sans fonts at 5,000.
* Give Telugu's length marks classes 4 and 5, as HarfBuzz 10.2 doeseunhhu2026-09-281-2/+4
| | | | | | | HarfBuzz reorders marks by a modified combining class; for U+0C55 and U+0C56 (canonical classes 84 and 91) it is 4 and 5, so they sort before the virama. Vega had 0, which rustybuzz 0.20 uses; HarfBuzz 10.2's hb-unicode.hh says otherwise, and Unifont's Telugu showed it.
* Sort a stage's lookups with HarfBuzz's own quicksorteunhhu2026-09-281-6/+64
| | | | | | | | | | HarfBuzz sorts each stage's lookups by index with hb_qsort, its copy of Isaac Turner's quicksort, and then merges entries of the same lookup: masks and joiner flags combine, but the per-syllable and random flags are the first entry's. Which entry comes first is the quicksort's doing, so the planner now sorts the same way instead of stably. Noto Sans Malayalam, whose psts and rlig share a lookup, needed it: ma with a combining dot joined across syllables in HarfBuzz and not here.
* Shape Hangul, kern with kern tables, and place marks without GPOS, as ↵eunhhu2026-09-285-50/+625
| | | | | | | | | | | | | | | | | | | | | | | | HarfBuzz does The planner now picks who positions as HarfBuzz 10.2 does: GPOS, the old kern table when GPOS has no kern feature, and marks placed by combining class and ink boxes when there is no GPOS; the Hangul shaper composes syllables the font has whole, takes apart those it lacks into jamo for ljmo, vjmo and tjmo, and moves tone marks before their syllable. The section grows to an 80-byte header (the kern table, and each glyph's ink box when a plan places marks without GPOS) and 88-byte plans (the kern mask, the jamo masks). Kern tables are applied pair subtable by pair subtable with HarfBuzz's own binary search; state machines, class subtables and cross-stream kerning, which no installed font has, are refused. The check spells Hangul syllables every way text does. Every installed font Vega accepts shapes as HarfBuzz does (209 fonts, 1,520 plans, a thousand runs each), and the fonts refused before at 20,000 runs a plan: Noto Sans CJK KR and Baekmuk for Hangul, IPA Gothic and Noto Mono for marks without GPOS, FreeSans, FreeSerif, Liberation and DejaVu Sans ExtraLight for kern tables, Unifont's Latin, Greek, Cyrillic, Hebrew and Hangul.
* Compose Yiddish yod-yod, not yod, with patah into FB1Feunhhu2026-09-281-1/+1
| | | | | | HarfBuzz's Hebrew presentation forms join U+05F2 and patah into U+FB1F; Vega had U+05D9 there. Only fonts without a GPOS mark feature take the rule, so no font Vega accepted showed it; Unifont's Hebrew did.
* Speak AT-SPI2 on the accessibility bus from the display, over Vega's own D-Buseunhhu2026-09-288-8/+1552
| | | | | | | | | | | | | | | | vega display --access joins the accessibility bus as AT-SPI2 objects for screen readers such as Orca, with no toolkit, ATK or libdbus: dbus.c speaks the D-Bus wire format (EXTERNAL authentication, headers, aligned bodies, calls, replies, signals). The tree follows what the say surface says: a heading, a label per field, a list box per list whose items know their place and which is selected and focused, tables, the entry, the keys as buttons; answers come from the app when asked, one object has the focus, and focus moves are said as events. Assistive technology can act too: Selection focuses a row, Action presses a key. The live loop now draws only when something changed, so a screen reader's questions no longer cost frames. The check walks the tree with gdbus on a private session bus with at-spi-bus-launcher, and follows the focus through keys, Selection, Action and the events.
* Make the web surface accessible the way the say surface is, checked in ↵eunhhu2026-09-283-25/+128
| | | | | | | | | | | | | Chromium's accessibility tree Lists are listboxes that keep the keyboard focus, the focused row their active descendant, each option with its place in the whole list though only the visible rows are drawn; tables are grids with row indices; tones other than normal and muted are said, not only coloured; keys are named buttons. The page now updates in place instead of rebuilding each list, so the focus a screen reader follows survives every redraw. The browser test reads Chromium's accessibility tree: roles, names, focus, selection and the active descendant moving within the same list.
* Set paragraphs into lines: bidi, script runs, shaping, line breaking, visual ↵eunhhu2026-09-286-5/+587
| | | | | | | | | | | | | | | order text/layout.c puts the checked parts together: a paragraph's levels, scripts resolved for Common and Inherited characters, break opportunities, one shaping to measure, lines broken at the last opportunity that fits (trailing spaces hang, and a line that shapes wider than measured backs off), then per line L1, runs of one level and one script shaped again, the runs in L2's order and their glyphs placed. test-layout checks 30,000 random mixed-script paragraphs: lines cover the text, mandatory breaks end lines, lines fit when they can, and glyphs come in the order UAX #9 gives the characters. vega-symbols paragraph draws a layout through the symbol file's glyphs.
* Break text into lines, graphemes, words and sentences as Unicode's tests requireeunhhu2026-09-2810-3/+5679
| | | | | | | | | UAX #14 line breaking (LB1-LB31, numbers tailored as Unicode's tests are) and UAX #29's grapheme cluster, word and sentence boundaries, each from generated property tables, the rules taken in order, in linear time. They pass all of Unicode 15.1's LineBreakTest (10,274 cases), GraphemeBreakTest (1,187), WordBreakTest (1,826) and SentenceBreakTest (512).
* Shape text as HarfBuzz does, from a symbol file, checked against HarfBuzzeunhhu2026-09-289-35/+3357
| | | | | | | | | | | | | The build tool compiles a font's shaping plans the way HarfBuzz's planner does (features, stages, masks, lookups per script and direction) and carries them with the font's character map, advances and GDEF, GSUB and GPOS tables in a section of the symbol file (VEGASYM4); glyphs shaping reaches become symbols of their own. A small engine ports HarfBuzz 10's default, Arabic and Hebrew shaping step for step and reads the tables in place, every read bounds-checked. The build refuses a file whose shaping differs from HarfBuzz's on random runs: DejaVu Sans agrees on 600,000 runs in six scripts both ways, and every installed font the tool accepts agrees too, at 1.05-1.18 times HarfBuzz's time.
* Generate the Unicode properties shaping needseunhhu2026-09-283-2/+6353
| | | | | | Joining types, general categories and scripts (with their ISO 15924 codes) as two-stage tables, canonical decompositions one step at a time, and Extended_Pictographic, from the UCD by text/unicode.py.
* Normalize text to NFD and NFC, passing all of Unicode's normalization testseunhhu2026-09-287-3/+4383
|
* Resolve bidirectional text as UAX #9 does, passing all of Unicode's bidi testseunhhu2026-09-287-2/+2488
|
* Build the node's surface again when Pure's headers changeeunhhu2026-09-281-2/+5
| | | | | | | | vega-surface.so is compiled against Pure's app and module layout, which a node checks when it maps the surface (sizeof PureApp and PureModule): a surface left from before a change of those headers was refused, and the node showed no frame. It now depends on Pure's core headers and serve/surface.h, as the vega command already depends on libpure.a.
* Centre a wide character's glyph in its two cellseunhhu2026-09-273-3/+8
| | | | | | | | A Hangul or CJK glyph is one em across, less than two cells of a monospace Latin font (1.2 em for DejaVu Sans Mono), and was drawn from the left of its cells, so syllables stood apart unevenly. A cached glyph now keeps its advance in pixels, and a wide character's glyph is drawn centred in its two cells.
* Type Hangul on the keyboard (두벌식)eunhhu2026-09-276-20/+267
| | | | | | | | | | | | | | | | | | Surfaces that read keys (the display, through evdev) had only a US layout's letters. After the 한/영 key (Lang1, or AltRight standing for it), letter keys now give jamo that compose syllables in the entry line (surfaces/hangul.c): an initial, a vowel and an optional final, joined vowels and finals, a final that moves on to a vowel after it (도깨비불), Shift's doubled consonants, a lone jamo as its compatibility letter. The syllable being composed shows after the entry line; Backspace takes it back a jamo at a time before deleting what was typed; Enter, Tab, the toggle or any key but a letter finishes it. The script language gains hangul, letters HEX and back to drive it. The check types ten cases through it (안녕하세요, 값을, 안자, 와원, 까원, 갑 after Backspace, 한글, ㅏ, 안ㅈ, 안녕 2) and follows the composing line jamo by jamo; the messenger check has Alice type 안녕 on her display with recorded keys, drawn on Bob's.
* Add a messenger between two nodes, sealed end to endeunhhu2026-09-273-1/+216
| | | | | | | | | | | | | examples/messenger.aup keeps the lines typed here and sends each to the other node, and keeps the other's. The check runs Alice's and Bob's nodes, each with a key of its own, sealing to the other's and taking lines only from the other (pure-serve --key, --send, --seal, --sender), each drawn by Vega in the node, with a relay between them: a line typed on Alice's display reaches Bob's node and is drawn on his, Bob's answer posted by a program is drawn on hers, each last frame is the one the vega command draws from that node's store, and the relay saw no line's text nor a field's name, only one sealed session each way naming the sender's key.
* Draw a node's frames into files, and follow its saves after a recordingeunhhu2026-09-271-45/+68
| | | | | | | | | Vega in a node takes a second form, pixels --grid WxH --out PREFIX: each frame the node's saves draw goes into PREFIX-N.ppm, no display controller needed (one card takes one master, so a second node on the machine draws this way). And in a node, recorded events no longer end the surface: their last frame is printed when they end, and the node's saves are drawn on until it stops, so an answer that comes later shows.
* Show a Pure node's app on its display, as a surface in the nodeeunhhu2026-09-276-19/+222
| | | | | | | | | | | | | | | | | | | | | The vega host builds a second way, as build/vega-surface.so, which pure-serve maps and runs beside its connection threads (pure-serve --surface vega-surface.so -- display ..., Pure's design 010). There the app it draws is a reader of the node's last save, read where it is, and what people type goes to the node's owner: a key press as the key input's event, the typed line as the text input's, each applied and saved with the node's requests. It waits on its input devices and the node's changes descriptor together, leaves timers and signals to the node, and stops with it. The library calls the app model pure-serve exports and links none of its own; it exports pure_surface_run alone. Without --live or --input it takes no input: the node's saves alone draw it. The check gives a node an item over HTTP, then runs the display in the node with recorded keys (an item, Enter, Tab, Space): the last frame is the one vega tui draws for the same state and keys, it came back from the display controller as drawn, and the node's views show both items.
* Take keyboards, mice and touch screens on the display (evdev)eunhhu2026-09-277-16/+394
| | | | | | | | | | | | | | vega display --live reads the kernel's input events with no library: keys become Pure's key names and the text they type on a US layout, going through the TUI's own handling (the input line takes text, else the app takes keys); a mouse moves an outlined pointer and clicks, a touch screen presses where it is touched, scaled from the device's range; the line pressed is the row. --input FILE reads recorded events to their end and prints the last frame, which is how this screenless Pi checks it: after typing two items with Shift, Enter, Tab, Space and a touch on a row, the display's last frame is the TUI's after the same keys and click, and came back from the display controller as drawn.
* Put the frames on a display through KMS, checked by writebackeunhhu2026-09-278-14/+421
| | | | | | | | | | | | | | | vega display draws the same frames on a display with the kernel's mode setting and no library between: a connected connector's mode on a CRTC, its primary plane flipped between two dumb buffers by atomic commits, the grid as many cells as fit. Frames are drawn in cached memory and copied over, since blending over write-combined buffers read them back slowly (a 1080p frame took 370 ms; now 20.6 ms with a vertical sync and the check below). This Pi has no screen but its display controller has writeback connectors: --writeback sets a mode on one and takes every frame back as the controller put it out. Each came back exactly as drawn, and the new check finds it the pixel surface's frame for the same grid, pixel for pixel.
* Draw the TUI's cells as pixels with the symbols' glyphseunhhu2026-09-2713-75/+460
| | | | | | | | | | | | | | | vega pixels runs the same scripts as tui --dump and writes each frame as an image: the view's cells (only those on screen are evaluated) become glyphs, each drawn once per size with the exact area of its curves, in its tone's colour on its row's background. No page, no scene in between. A 1920x1350 grid takes 12 ms a frame here, writing the image included. Symbol files carry the font's line now (format 3: ascent and descent where format 2 kept zeros), the cell height and baseline; and the build tool takes fonts in order, later ones filling the codes earlier ones lack: a CJK font alone drew ambiguous-width arrows two cells wide in a one-cell grid, which the new check caught, so a monospace Latin font comes first and Hangul after.
* Draw text from an atlas the CPU fills, composed by the GPUeunhhu2026-09-277-274/+268
| | | | | | | | | | | | | | | | | Each size's symbols are drawn on the CPU into one 8-bit atlas, every pixel the exact area its curves cover, and text is a quad per glyph reading it. A 1080p page of 11,382 glyphs composes in 1.07 ms at 16 px (it took 525 ms computing coverage per fragment), and the composed pixels are the CPU raster's exactly (worst difference 0 for DejaVu's ASCII, 1 for Noto's 11,172 Hangul syllables). Computing coverage on the GPU lost on both counts: a compute pass filling the atlas took 5-13x longer than one Cortex-A76 core (35 against 2.7 ms at 16 px), and its faster sorting-network form computed whole rows wrongly under this Mesa V3D. Coverage now has one implementation, in C, and the shaders only compose. vega_symbol_raster takes a row stride to draw into the atlas in place.
* Name Vega's parts: lib/, surfaces/ and symbols/eunhhu2026-09-2715-8/+23
| | | | | | | src/ held two parts, the surfaces that show modules to people and the symbol format with its CPU and GPU drawing. As in Pure (design 009), top level directories are now the parts, and the README says what each builds.
* Link Pure's libpure.a instead of compiling its sourceseunhhu2026-09-272-9/+12
| | | | | | Pure builds the app model and the tree store as one library with headers in core/; Vega no longer lists Pure's files, so a new one there cannot break it.
* Draw symbols on the GPU from the point table as it iseunhhu2026-09-276-6/+462
| | | | | | | | | | | | | | | | OpenGL ES 3.1: the file's points go up as one storage buffer, each symbol gets 16 row bands listing the curves they reach (cut by integer division of doubled coordinates, so CPU and shader agree), and every pixel of a symbol's box sums the exact area of its bands' curves, as vega_coverage does. vega-symbols gpu draws every symbol against the CPU reference and times 1080p pages. On the Pi 5's V3D it matches the reference (mean 0.001-0.1 levels, worst 4) but takes 142-757 ms a page, 83% of it in the area function. A sorting network in registers made it 2.3x faster yet computes wrong rows in the fragment stage with this driver, while the same code is exact in a compute shader; so drawing each glyph size once into an atlas by compute comes next.
* Store symbol outlines as point chains, and keep glyph advances wholeeunhhu2026-09-275-157/+378
| | | | | | | | | | | | | | | | | | | | Format 2 (VEGASYM2) writes each contour as a chain of 4-byte points, on or off the curve as in TrueType: endpoints are shared, lines have no control point, and two off points imply the on point between them. A curve is still had from one point and its two neighbours, so a GPU can point at curves by point index and upload the table as it is. Cubics (CFF, SVG curves and arcs) become quadratic splines the cu2qu way, joins left implied, within a unit. Files are about half of format 1: 1.3-1.4x a TrueType font's glyf (the whole subset font's size), 2.0-2.9x CFF, 2.6x smaller than the SVG icons. Opening checks each symbol's first and last point instead of every curve: 0.34 ms rather than 19 ms for 11,172 Hangul syllables. Points decode without branching on their flags (9.7 ns a point), still 1.2-10x cheaper than FreeType's outline. Glyph advances were half their width since outlines are doubled for FreeType's implied points; they now match hmtx, and the check says so.
* Draw glyphs and icons from one symbol format, by exact pixel areaeunhhu2026-09-2711-3/+998
| | | | | | | | | | | | | | | | | | | Symbol files (VEGASYM1) hold glyphs and icons as quadratic curves, verified once per symbol and then read in place. vega-symbols builds them from fonts (through FreeType, losslessly: twice the font's units keeps TrueType's implied points whole) and from solid SVG paths (arcs included), refusing by name what an icon asks for that symbols cannot hold. The reference rasterizer computes each pixel's covered area exactly: every curve's integral of clamp(1 - x) dy inside the pixel, split where it crosses the pixel's sides. The Slug-style two-ray average first tried missed FreeType by 15.4 levels on average at 12 px; exact area is within 1.2-3.5 at 8-16 px, and where it differs most it matches 64x64 supersampling, not FreeType. Reaching an outline costs 4.6-29x less than through FreeType, but the files are 2.1-6.6x larger than glyf or CFF: design 002 has the numbers and the next question (points shared along contours, lines without control points).
* Keep Python's bytecode caches out of the repositoryeunhhu2026-09-272-0/+1
|
* Read module files of any sizeeunhhu2026-09-261-4/+16
| | | | | | A module may now be some 2 MiB; the buffer that keeps the last module's bytes (for --watch's digest) grows to the file instead of 128 KiB.
* Add a board used by people and programs alikeeunhhu2026-09-265-3/+120
| | | | | | | | | | examples/board.aup is a kanban board: people add and move cards on Vega's surfaces, programs post JSON to the same module served by pure-serve, with no routes written. The checks write cards with the CLI into a store, serve that store over HTTP where a program moves one and adds another, and read it again with the CLI, the focus still on the card it was on.
* Give live surfaces a clock and run timers; add a stopwatcheunhhu2026-09-268-10/+203
| | | | | | | | | | | | | The terminal and the web page set the time events see to the wall clock and tick an app's timers on time: each round ticks what is due, draws, then waits for input, a frame or the next tick, and a tick is news to every open page at once. Scripts have no clock: time MS sets the time the next events see and tick TIMER ticks, so an app that uses time replays exactly. A module swap reschedules the timers. examples/stopwatch.aup runs on every surface; the checks drive it by script, on a pseudo-terminal by the clock, and on the web page.
* Add a tally example: a keyed list on every surfaceeunhhu2026-09-262-1/+39
| | | | | | | | examples/tally.aup counts typed words in a list keyed by the word: the screen stays sorted with each word once, typing a word again counts it and moves the focus to it. It runs on the three surfaces with the other examples, and vega check finds nothing in it.
* Swap a running app's module: --watch and the swap commandeunhhu2026-09-268-43/+222
| | | | | | | | | | | | | vega tui|web MODULE --watch replaces the running app's module when its file is written again (aup watch writes it); states keep their values by name through pure_app_replace and every web page hears of it. The file is compared by the digest of its bytes, at most five times a second, so a module of the same size written within the same second is not missed; a module that does not load leaves the app as it was and is not tried again. Modules alternate between two slots so a failed load never touches the running one. Scripts get swap MODULE; whether a module runs frames is now a property of the screen.
* Restate the bytes a changed frame sendseunhhu2026-09-261-1/+1
| | | | | | The todo example now counts its open tasks, so the field line changes with more events; a changed frame sends 143 B on average, not 139 B.
* Show filtered lists and their shared focuseunhhu2026-09-268-19/+139
| | | | | | | | | | | | | | | | A list may now be filtered (each t in tasks where !t.done), and several filtered lists over one list share one focus: the focus view names an item, so Vega asks Pure which row shows it and highlights it only in the list that holds it; the other lists have no focus (null in the web surface's JSON, and the spoken surface names the item where it is). A click or point on a row points at the item that row shows, and the wheel on a list without the focus focuses the row under it. Pure's rows now take a mutable app. examples/today.aup shows what is open and what is done; its CLI output matches a model over random adds, moves, toggles and points into either list, and the web surface leaves the other list without a focus.
* Count the open tasks in the todo exampleeunhhu2026-09-263-7/+12
| | | | | | | | The left field said how many tasks there were; it now says how many are still open, "2 of 3", with aup's count(t in tasks where !t.done), which Pure runs as a fold over the list on every surface. The reference model counts the same, and in the browser the field follows a task marked done.
* Style the web page's button and name keys as the TUI doeseunhhu2026-09-261-2/+7
| | | | | | The Enter button matches the input beside it, and the key legend shows arrows and short names (↑ ↓ Del PgUp) instead of Pure's key names.
* Add a web surface: the same spec as a pageeunhhu2026-09-269-23/+752
| | | | | | | | | | | | | | | | | | | vega web MODULE serves the screen to a browser (127.0.0.1 unless given --listen): one dependency-free page draws the title, fields, lists as listboxes and tables, with tones as colors and the focus highlighted. It asks for the screen as JSON with the rows it can show per list or table, so only those cells are evaluated, scrolled like the TUI around the focus. Keys go as KeyboardEvent.code, which is Pure's key naming already; typed text commits with Enter; a click or the wheel moves the focus through pure_app_point; an event stream tells every open page of each change. Scrolling, key presses and text commits are now shared helpers. The checks drive it against the model (1,200 random steps: title, field, rows, focus, the window of items with badges and tones), check a table's columns and cells, post at random to every example, and in headless Chromium over the DevTools protocol press keys, click a row and type Korean text into the page itself.