Documentation
¶
Overview ¶
Package program runs a terminal interface.
It owns the terminal, the frame schedule, and the one goroutine that is allowed to touch the interface's state. It knows nothing about what the interface is for: what it drives is a Component, which draws itself and answers input, and everything a component needs from the program it asks for through a Runtime.
The concurrency model, in full ¶
One goroutine draws and handles input. Anything that happens elsewhere — a request finishing, a file changing, a timer firing — reaches the interface through a Dispatcher obtained from Runtime.Dispatcher, and runs there. That is the whole of it, and it is why state reached only from that goroutine needs no internal lock.
The program parks when there is nothing to do. It wakes for input, for posted work, and for the terminal reporting progress — never on a clock that runs regardless. A component that wants a clock starts one with Runtime.Every, and an interface with nothing animating costs nothing.
The two places an interface can be ¶
A program either takes a screen of its own, which it gives back on the way out, or draws in the terminal's own screen as a block with the session's output above it. The second is what Config.Inline asks for, and it is the difference between a program the user enters and leaves and one that is part of their session: what an inline interface has finished with is printed with InlineRuntime.Print and belongs to the terminal from then on — scrollable, selectable, and still there afterwards.
Index ¶
- Constants
- Variables
- func Run(ctx context.Context, cfg Config) (err error)
- type BellHost
- type Clipboard
- type Component
- type Config
- type CopyHost
- type DirectoryHost
- type Dispatcher
- type Environment
- type FrameWriter
- type GroundHost
- type HandoverHost
- type Host
- type ImageHost
- type Images
- type InlineRuntime
- type KeyboardHost
- type NotifyHost
- type PasteHost
- type Printable
- type Runtime
- func (r *Runtime) Clipboard() Clipboard
- func (r *Runtime) Dispatcher() Dispatcher
- func (r *Runtime) Environment() Environment
- func (r *Runtime) Every(d time.Duration, fn func()) (stop func())
- func (r *Runtime) Images() Images
- func (r *Runtime) Quit()
- func (r *Runtime) Refresh()
- func (r *Runtime) Session() Session
- type Session
- type TitleHost
- type WheelHost
Constants ¶
const DefaultFrameRate = 16 * time.Millisecond
DefaultFrameRate is the fastest a program redraws. A terminal cannot usefully show more, and a stream of updates would otherwise ask for a frame each.
Variables ¶
var ErrFrameTimeout = errors.New("program: frame writer did not drain")
ErrFrameTimeout means a frame writer did not account for its pending frames before display ownership had to change. The program refuses the transition: a late frame would otherwise be written into the next owner's output.
Functions ¶
Types ¶
type BellHost ¶ added in v0.0.5
type BellHost interface{ Bell() }
BellHost rings the user-facing host's audible or visible bell.
type Clipboard ¶ added in v0.0.5
type Clipboard struct {
// contains filtered or unexported fields
}
Clipboard is the clipboard associated with the user-facing host. Its zero value refuses writes and ignores reads.
func (Clipboard) Paste ¶ added in v0.0.5
func (c Clipboard) Paste()
Paste requests clipboard contents. A successful answer arrives as input.Paste.
type Component ¶
Component is an interface a program can run: it draws itself into the space it is given, and says whether it wants an event.
It is handed a view that is already positioned and clipped, so its coordinates are its own. An event it does not consume is dropped by the program — a component is the root of its own tree and there is nobody above it to pass one on to.
type Config ¶
type Config struct {
// Root builds the component to run on a screen of its own. It is given the runtime
// first, so the component can hold it from the moment it exists. Returning nil is
// an error.
Root func(*Runtime) Component
// Inline builds the component to run as a block in the terminal's own screen,
// with output that is finished printed above it. Its component is given an
// [InlineRuntime], which is a [Runtime] that can also print.
// Returning nil is an error.
Inline func(*InlineRuntime) Component
// Terminal says which of the terminal's optional behaviours to ask for. Ignored
// when Host is set.
//
// AltScreen is the program's to decide rather than the caller's, because where
// frames go is the rendering model and not an input capability: it follows from
// which of Root and Inline was set. Asking for it alongside Inline is a
// contradiction and is reported as one.
Terminal term.Options
// Color says how much colour the terminal can show. The zero value, [grid.Auto],
// asks [term.DetectDepth] — which is the one thing in this library that reads
// its environment rather than making a request and letting it be ignored,
// because a truecolor sequence a terminal cannot read prints wrong rather than
// degrading.
//
// Setting it is how a program that already knows — from its own configuration,
// or because it is writing to something that is not a terminal at all — takes
// that decision back.
Color grid.Depth
// Host overrides where input comes from and frames go. Nil opens the real terminal
// and gives it back on the way out.
Host Host
// FrameRate caps how often the interface redraws. Zero uses [DefaultFrameRate].
FrameRate time.Duration
}
Config is what a program needs to run.
Exactly one of Root and Inline says what to run, and which one it is decides where the interface is drawn: Root takes a screen of its own, Inline draws in the terminal's own screen and prints finished output into its scrollback.
type CopyHost ¶ added in v0.0.5
CopyHost writes to the clipboard associated with the user-facing host.
type DirectoryHost ¶ added in v0.0.5
DirectoryHost tells a terminal how to resolve relative paths in program output.
type Dispatcher ¶ added in v0.0.5
type Dispatcher struct {
// contains filtered or unexported fields
}
Dispatcher is a copyable, concurrency-safe handle into a running program. Its zero value drops work. It deliberately exposes no owner-only operation.
func (Dispatcher) Post ¶ added in v0.0.5
func (d Dispatcher) Post(fn func())
Post runs fn on the interface goroutine and requests a frame afterwards. A nil function requests only the frame. Calls never wait, preserve FIFO acceptance order, and are dropped after the program stops or on a zero Dispatcher.
Never waiting is the whole point and it is also the whole cost: the queue has no upper bound, so a producer posting faster than the interface goroutine can drain grows it without limit. Nothing here can fix that, because the only two things a queue can do when it is full are block the caller and lose work, and this edge exists to do neither. Coalescing therefore belongs to the caller, at the source: post the state a burst arrived at rather than one call per item, or keep the state somewhere the interface goroutine reads and post nothing but a request for a frame — which is what a nil function is for, and why several of them collapse into one. Runtime.Every is the same discipline applied to a clock.
type Environment ¶ added in v0.0.5
type Environment struct {
// contains filtered or unexported fields
}
Environment is the stable set of terminal facts learned before a program runs. Its zero value reports that nothing was learned.
func (Environment) Ground ¶ added in v0.0.5
func (e Environment) Ground() grid.Ground
Ground reports the host's foreground and background colours when known.
func (Environment) Keyboard ¶ added in v0.0.5
func (e Environment) Keyboard() (input.KeyboardFlags, bool)
Keyboard reports negotiated keyboard protocol features.
func (Environment) Wheel ¶ added in v0.0.5
func (e Environment) Wheel() input.Wheel
Wheel reports how host wheel events should be scaled.
type FrameWriter ¶ added in v0.0.5
type FrameWriter interface {
Queue(frame []byte) uint64
Progress() <-chan struct{}
Written() uint64
Err() error
Drain(timeout time.Duration) bool
}
FrameWriter is the part of a frame queue the program needs.
It is defined by the consumer rather than exposing term.Writer through Host. Implementations must preserve queue order, report progress as a watermark and be safe for concurrent use. term.Writer is the standard implementation.
type GroundHost ¶ added in v0.0.5
GroundHost supplies the colours discovered before a program starts.
type HandoverHost ¶ added in v0.0.5
HandoverHost temporarily gives exclusive ownership of its display to run.
type Host ¶
type Host interface {
// Events is the input, closed when the input ends.
Events() <-chan input.Event
// Writer is where frames go. The interface is defined here, where it is used;
// a host is not coupled to the terminal package's concrete writer.
Writer() FrameWriter
// Size is the terminal's size in cells.
Size() (w, h int, err error)
}
Host is where a program's input comes from and its frames go.
A program opens the real terminal unless it is given one of these. Being able to supply it is what lets an interface be driven and inspected in a test, with no terminal in sight.
Everything beyond transport is optional. Each independently useful operation is represented by a small consumer interface such as GroundHost, CopyHost or NotifyHost, so implementing one never silently depends on implementing its neighbours. ImageHost is the exception because its transport and geometry form one protocol. Absent capabilities receive harmless defaults.
type ImageHost ¶ added in v0.0.5
type ImageHost interface {
Graphics() graphics.Protocol
CellSize() (image.Point, bool)
Transmit(png []byte) (graphics.Image, error)
}
ImageHost transmits images and reports the protocol geometry needed to place them. These methods form one capability: a handle from Transmit cannot be placed without the protocol and cell geometry that interpret it.
type Images ¶ added in v0.0.5
type Images struct {
// contains filtered or unexported fields
}
Images is the host's image transport. Its zero value reports no protocol and refuses transmission.
func (Images) CellSize ¶ added in v0.0.5
CellSize reports one terminal cell's pixel size when known.
type InlineRuntime ¶ added in v0.0.5
type InlineRuntime struct{ *Runtime }
InlineRuntime is a Runtime that can publish completed output into terminal scrollback. It is only constructed for Config.Inline, and its zero value is inert.
func (*InlineRuntime) Append ¶ added in v0.0.5
func (r *InlineRuntime) Append(draw func(grid.View) bool)
Append continues the last published row until draw reports completion.
func (*InlineRuntime) Print ¶ added in v0.0.5
func (r *InlineRuntime) Print(p Printable)
Print publishes a measured drawable above an inline interface.
type KeyboardHost ¶ added in v0.0.5
type KeyboardHost interface {
Keyboard() (input.KeyboardFlags, bool)
}
KeyboardHost supplies keyboard protocol features negotiated with the host.
type NotifyHost ¶ added in v0.0.5
type NotifyHost interface{ Notify(text string) }
NotifyHost sends a notification through the user-facing host.
type PasteHost ¶ added in v0.0.5
type PasteHost interface{ Paste() }
PasteHost requests text from the clipboard associated with the user-facing host. Answers arrive asynchronously through Host.Events as an input.Paste.
type Printable ¶
Printable is something that can say how tall it is at a width and then draw itself into that space.
The interface is defined here where printing consumes it. Any higher-level value with the same drawing and measuring behaviour satisfies it without an adapter.
type Runtime ¶ added in v0.0.5
type Runtime struct {
// contains filtered or unexported fields
}
Runtime is the program resource owned by the interface goroutine.
It is concrete rather than a provider-defined interface: consumers that need only a subset declare that interface where they use it. Background work receives only Runtime.Dispatcher, preserving ownership in the type system. Host features are grouped into the concrete Environment, Clipboard, Session and Images values rather than flattened into one capability catalogue. The zero value is inert; it is safe to embed in an object that has not been attached to a program.
func (*Runtime) Dispatcher ¶ added in v0.0.5
func (r *Runtime) Dispatcher() Dispatcher
Dispatcher returns the concurrency-safe handle for background work.
func (*Runtime) Environment ¶ added in v0.0.5
func (r *Runtime) Environment() Environment
Environment returns the host facts available to this runtime.
type Session ¶ added in v0.0.5
type Session struct {
// contains filtered or unexported fields
}
Session controls the live terminal session around rendered frames. It groups operations that must remain ordered with the interface owner's state. Its zero value performs harmless notification no-ops and can hand control to a callback, but cannot suspend a process.
It holds the runtime rather than the resolved services the other capabilities hold, because two of its methods need the owner itself and not what the host can answer: see Session.Hand.
func (Session) Bell ¶ added in v0.0.5
func (s Session) Bell()
Bell asks the host for the user's attention.
func (Session) Hand ¶ added in v0.0.5
Hand gives exclusive display ownership to run and repaints after it returns. If pending frames cannot drain, it returns ErrFrameTimeout without calling run.
func (Session) ReportDirectory ¶ added in v0.0.5
ReportDirectory tells the host which directory relative links belong to.