A reckoner seed: scribbler, a window held by name — machines/seeds/seedscribbler.shoddy
A session can draw. SCRIBOPEN opens a
window and keeps it open under a name. SCRIBFILL,
SCRIBPIXEL, SCRIBTEXT and
SCRIBBLIT draw on it. SCRIBSAVE writes a PNG
— an ordinary image file. CLOSE shuts it.
That sentence was refused for a long time. The grounds turned out to be three different things wearing one coat, and each was answered differently.
| The objection | What answered it |
|---|---|
The handle. A live resource must not reach the stack, because UNDO restores the stack and the thing naming the window would go with it. | reckoner's named-resource table. SCRIBOPEN binds under a name; nothing ever pushes the window. |
| The main thread. A window must be created and pumped on the process's main thread, which a shell's read loop looked to own. | Nothing. It never did. The mill already runs the Shoddy program on a background thread and keeps the main one pumping windows — handling their events. So a window opened mid-session is the supported path, and it stays live while the prompt waits. |
The aborts. ScribblerOpen dies when there is no window backend — which is every way of running a program except mill run. | The runtime. TryScribblerOpen reports it instead, so a session in a woven binary or under the debugger is told rather than ended. |
A slot, not a value, and this is the one place
scribbler differs from seedrecio. A file
handle is a number, so RECOPEN puts it straight in the
binding. A Scribbler is its own value kind, and the binding
has no room for one. So TryScribblerOpen answers a slot
number instead. ScribblerOf turns that number back into the
window, and ScribblerShut lets it go. The seed holds an
integer, and the ordinary drawing builtins do the work, untouched.
The words are SCRIB*, not
SCRIBBLER*. The builtins keep their names and stay
out of the dictionary. A session's SCRIBPIXEL takes a
name where the builtin takes a window. They are different words,
and a shared spelling would be a lie about which one you had.
The drawing words answer nothing, which is where they
part company with the builtins. A builtin threads the window through so a
program can chain. A session already has the name, and would only have to
DROP what came back. SCRIBSAVE is the one
exception, because whether it wrote is a real question.
What is not here, and is not forgotten. The event
words — ScribblerPoll, ScribblerWait,
ScribblerSetInterval — are left out on purpose. They
exist for a frame loop, a program that redraws the window over and over.
A reckoner line is a transaction that answers and ends. A word that
blocked for the next event would hold the session inside one line with no
way back. Reading the mouse from a calculator prompt is a different design
and not this one.
Quitting closes it. halifax shuts every bound resource on the way out. For a file that is good manners — the process exit would have closed it anyway. For a window it is not optional. The mill's pump loop keeps running while any window is open, so a session that opened one and quit would leave the mill alive with no prompt.
> 200 120 "w" SCRIBOPEN
ok: opened w
[ empty ]
> "w" 10 40 90 SCRIBFILL
[ empty ]
> "w" 20 20 2 255 220 90 "SHODDY" SCRIBTEXT
[ empty ]
> "w" 60 70 255 80 80 SCRIBPIXEL
[ empty ]
> "w" 60 70 SCRIBGETPIXEL
x: { 255 80 80 }
> "w" SCRIBBLIT
[ empty ]
> "w" "shoddy.png" SCRIBSAVE
x: True
> "w" CLOSE
ok: closed w
Nothing appears until SCRIBBLIT, which is what makes a
whole picture arrive at once rather than a pixel at a time. And the
window survives UNDO: wind the stack all the way back and
"w" SCRIBWIDTH still answers. UNDO restores a
stack and does not un-draw a picture.
| Word | Description |
|---|---|
| SCRIBOPEN ( w h name -- ) | Open a drawing window w by h pixels and keep it open under that name. Needs a mill started with mill run — anything else has no window backend and is told so. |
| SCRIBWIDTH ( name -- n ) | How many pixels wide that window is. |
| SCRIBHEIGHT ( name -- n ) | How many pixels tall that window is. |
| SCRIBTITLE ( name s -- ) | Put s in that window's title bar. |
| SCRIBFILL ( name r g b -- ) | Fill the whole window with one colour. |
| SCRIBPIXEL ( name x y r g b -- ) | Set one pixel. Coordinates count from 0 and are clamped to the window; colours are 0 to 255. |
| SCRIBGETPIXEL ( name x y -- list ) | The colour at x, y as a list of red, green and blue. |
| SCRIBTEXT ( name x y scale r g b s -- ) | Draw s at x, y in the built-in 8×8 font, scale times its own size. A scale of 1 is eight pixels tall; below 1 is refused. |
| SCRIBBLIT ( name -- ) | Show what has been drawn. Nothing appears until this is called. |
| SCRIBSAVE ( name path -- ok ) | Write the window to a PNG file; True on success, False on a path it cannot write. The one drawing word that answers something. |
CLOSE and BOUND are
reckoner's own and work here without this seed
saying anything about them. There is deliberately no
SCRIBCLOSE: one way to shut a resource is the whole reason
the table is uniform.
| User | How | |
|---|---|---|
| halifax | The calculator's drawing words — a window opened from the prompt, drawn on across lines, and shut by CLOSE or by quitting. | |
| sparky | Sparky folds it too, onto a pixel buffer with no window — a drawing comes back to the client as a PNG. |
A mill claims this seed by folding RckSeedScribbler over
its reckoner state, which is all halifax does.
| Machine | Why | |
|---|---|---|
| cuttle | The Cell type coordinates and colours cross the bridge as. | |
| reckoner | RckReg and the argument readers, and the named-resource table — RckBind, RckBoundTo, RckResOf — that makes an open window reachable after UNDO. | |
| scribbler | The domain this seed bridges: the Scribbler type and the drawing words the seed calls behind each name. | |
| seq | List plumbing under the argument readers. |