Documentation ¶
Index ¶
- Variables
- func DrawBitmap(dst Grid, src Bitmap, styles ...Style)
- func DrawGrid(dst, src Grid, styles ...Style)
- func IsStandardTermFile(f *os.File) bool
- func IsTerminal(f *os.File) bool
- func MustParseBitmap(set string, lines ...string) (stride int, data []bool)
- func MustRun(err error)
- func ParseBitmap(set string, lines ...string) (stride int, data []bool, err error)
- func Process(proc Processor, p []byte) (n int)
- func SigErr(sig os.Signal) error
- func WriteBitmap(w io.Writer, bi Bitmap, styles ...Style) (int, error)
- type Attr
- type AttrStyle
- type Bitmap
- func (bi *Bitmap) Get(p image.Point) bool
- func (bi *Bitmap) Load(stride int, data []bool)
- func (bi *Bitmap) Resize(sz image.Point)
- func (bi *Bitmap) Rune(p image.Point) (c rune)
- func (bi *Bitmap) RuneSize() (sz image.Point)
- func (bi *Bitmap) Set(p image.Point, b bool)
- func (bi Bitmap) SubAt(at image.Point) Bitmap
- func (bi Bitmap) SubRect(r image.Rectangle) Bitmap
- func (bi Bitmap) SubSize(sz image.Point) Bitmap
- type Buffer
- func (b *Buffer) Bytes() []byte
- func (b *Buffer) Discard()
- func (b *Buffer) Grow(n int)
- func (b *Buffer) Len() int
- func (b *Buffer) Process(proc Processor)
- func (b *Buffer) Reset()
- func (b *Buffer) Skip(n int)
- func (b *Buffer) Write(p []byte) (n int, err error)
- func (b *Buffer) WriteByte(c byte) error
- func (b *Buffer) WriteESC(seqs ...ansi.Escape) int
- func (b *Buffer) WriteRune(r rune) (n int, err error)
- func (b *Buffer) WriteSGR(attrs ...ansi.SGRAttr) int
- func (b *Buffer) WriteSeq(seqs ...ansi.Seq) int
- func (b *Buffer) WriteString(s string) (n int, err error)
- func (b *Buffer) WriteTo(w io.Writer) (n int64, err error)
- type Context
- type Cursor
- func (cs *Cursor) ApplyTo(w io.Writer, cur Cursor) (int, Cursor, error)
- func (cs *Cursor) Hide() ansi.Seq
- func (cs *Cursor) MergeSGR(attr ansi.SGRAttr) ansi.SGRAttr
- func (cs *Cursor) NewLine() ansi.Seq
- func (cs *Cursor) ProcessANSI(e ansi.Escape, a []byte)
- func (cs *Cursor) Show() ansi.Seq
- func (cs Cursor) String() string
- func (cs *Cursor) To(pt ansi.Point) ansi.Seq
- type DefaultRuneStyle
- type ExitError
- type FillRuneStyle
- type Grid
- func (g Grid) Bounds() ansi.Rectangle
- func (g Grid) CellOffset(pt ansi.Point) (int, bool)
- func (g Grid) Clear()
- func (g Grid) Eq(other Grid, zero rune) bool
- func (g Grid) Full() Grid
- func (g *Grid) IsSub() bool
- func (g *Grid) Resize(size image.Point) bool
- func (g Grid) SubAt(at ansi.Point) Grid
- func (g Grid) SubRect(r ansi.Rectangle) Grid
- func (g Grid) SubSize(sz image.Point) Grid
- type Input
- func (in *Input) AtEOF() bool
- func (in *Input) Close() error
- func (in *Input) Decode() (e ansi.Escape, a []byte, ok bool)
- func (in *Input) Enter(term *Term) error
- func (in *Input) Exit(term *Term) error
- func (in *Input) IsRecording(dest *os.File) bool
- func (in *Input) Notify(sigio chan os.Signal) error
- func (in *Input) ReadAny() (n int, err error)
- func (in *Input) ReadMore() (int, error)
- func (in *Input) SetRecording(dest io.Writer)
- type InputError
- type InputFrame
- type InputReplay
- type InputSignal
- type Mode
- type Output
- type Processor
- type Screen
- func (sc *Screen) Clear()
- func (sc Screen) Full() Screen
- func (sc *Screen) ProcessANSI(e ansi.Escape, a []byte)
- func (sc *Screen) Resize(size image.Point) bool
- func (sc Screen) String() string
- func (sc Screen) SubAt(at ansi.Point) Screen
- func (sc Screen) SubRect(r ansi.Rectangle) Screen
- func (sc Screen) SubSize(sz image.Point) Screen
- func (sc *Screen) To(pt ansi.Point)
- func (sc *Screen) Update(w io.Writer, prior Screen) (int, Screen, error)
- type ScreenDiffer
- type Signal
- type SignalError
- type Style
- type StyleFunc
- type Term
- type TermScreen
- type VirtualCursor
- func (c *VirtualCursor) Apply(cs Cursor)
- func (c *VirtualCursor) Hide()
- func (c *VirtualCursor) Reset()
- func (c *VirtualCursor) Show()
- func (c *VirtualCursor) To(pt ansi.Point)
- func (c *VirtualCursor) Write(p []byte) (n int, err error)
- func (c *VirtualCursor) WriteByte(b byte) error
- func (c *VirtualCursor) WriteESC(seqs ...ansi.Escape) int
- func (c *VirtualCursor) WriteRune(r rune) (n int, err error)
- func (c *VirtualCursor) WriteSGR(attrs ...ansi.SGRAttr) (n int)
- func (c *VirtualCursor) WriteSeq(seqs ...ansi.Seq) int
- func (c *VirtualCursor) WriteString(s string) (n int, err error)
- func (c *VirtualCursor) WriteTo(w io.Writer) (n int64, err error)
- type VirtualScreen
- func (vsc *VirtualScreen) Write(p []byte) (n int, err error)
- func (vsc *VirtualScreen) WriteByte(b byte) error
- func (vsc *VirtualScreen) WriteESC(seqs ...ansi.Escape) int
- func (vsc *VirtualScreen) WriteRune(r rune) (n int, err error)
- func (vsc *VirtualScreen) WriteSGR(attrs ...ansi.SGRAttr) (n int)
- func (vsc *VirtualScreen) WriteSeq(seqs ...ansi.Seq) int
- func (vsc *VirtualScreen) WriteString(s string) (n int, err error)
- type ZeroRuneStyle
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var TransparentBrailleRunes = ZeroRuneStyle('\u2800')
TransparentBrailleRunes maps the empty braille rune to (U+2800) to 0.
Functions ¶
func DrawBitmap ¶
DrawBitmap draw's a bitmap's braille runes into the destination grid.
Optional rendering styles may be passed to control the graphical rendition and transparency of the braille runes. The styles are passed any prior grid attributes for each target cell.
One particularly useful style to use is ElideStyle(0x2800), which will map any empty braille runes to the zero rune, causing only non-empty braille runes to be drawn.
Use sub-grids to target specific regions; see Grid.SubRect.
func DrawGrid ¶
DrawGrid copies the source grid's cells into the destination grid, applying any optional styles to each cell.
The default is an opaque copy: each source cell simply overwrites each corresponding destination cell.
A (partially) transparent draw may be done by providing one or more style options.
Use sub-grids to copy to/from specific regions; see Grid.SubRect.
func IsStandardTermFile ¶
IsStandardTermFile returns true only if the given file's name corresponds to a standard process terminal file; that is if it's one of /dev/stdin, /dev/stdout, /dev/stderr, or /dev/tty.
func IsTerminal ¶
IsTerminal returns true only if the given file is attached to an interactive terminal.
func MustParseBitmap ¶
MustParseBitmap is an infaliable version of ParseBitmapString: it panics if any non-nil error is returned by it.
func MustRun ¶
func MustRun(err error)
MustRun is a useful wrapper for the outermost Term.RunWith: if the error value implements ExitError, and its ExitCode method returns non-0, it calls os.Exit; otherwise any non-nil error value is log.Fatal-ed.
func ParseBitmap ¶
ParseBitmap parses a convenience representation for creating bitmaps. The set string argument indicates how a 1 (or true) bit will be recognized; it may be any 1 or 2 rune string. Any other single or double runes in the strings will be mapped to zero (allowing the caller to put anything there for other, self-documenting, purposes).
Types ¶
type Attr ¶
type Attr struct {
// contains filtered or unexported fields
}
Attr implements Context-ual manipulation and interrogation of terminal state, using the termios IOCTLs and ANSI control sequences where possible.
func (*Attr) Enter ¶
Enter default the Attr's file to the term's Output File, records its original termios attributes, and then applies termios attributes.
func (*Attr) Exit ¶
Exit restores termios attributes, and clears the File pointer if it was set by Enter
func (Attr) IsTerminal ¶
IsTerminal returns true only if the underlying file is attached to an interactive terminal.
func (*Attr) SetEcho ¶
SetEcho toggles input echoing mode, which is off by default in raw mode, and on in normal mode.
type AttrStyle ¶
AttrStyle implements a Style that returns a fixed ansi attr for any non-zero runes.
type Bitmap ¶
Bitmap is a 2-color bitmap targeting unicode braille runes.
func (*Bitmap) Rune ¶
Rune builds a unicode braille rune representing a single 2x4 rectangle of bits, anchored at the give top-left point.
func (Bitmap) SubAt ¶
SubAt is a convenience for calling SubRect with at as the new Min point, and the receiver's Rect.Max point.
type Buffer ¶
type Buffer struct {
// contains filtered or unexported fields
}
Buffer implements a deferred buffer of ANSI output, providing convenience methods for writing various ansi escape sequences, and keeping an observant processor up to date.
func (*Buffer) Bytes ¶
Bytes returns a byte slice containing all bytes written into the internal buffer. Returned slice is only valid until the next call to a buffer method.
func (*Buffer) Discard ¶
func (b *Buffer) Discard()
Discard processed bytes, re-using internal buffer space during the next Write*.
func (*Buffer) Process ¶
Process bytes written to the internal buffer, decoding runes and escape sequences, and passing them to the given processor.
func (*Buffer) Skip ¶
Skip Process()ing of n bytes written to the internal buffer. Useful when the processor wants to intermediate a buffer write, handling its own semantic update and avoiding (re)parsing the written bytes.
func (*Buffer) WriteESC ¶
WriteESC writes one or more ANSI escapes to the internal buffer, returning the number of bytes written.
func (*Buffer) WriteSGR ¶
WriteSGR writes one or more ANSI SGR sequences to the internal buffer, returning the number of bytes written; updates Attr cursor state. Skips any zero attr values (NOTE 0 attr value is merely implicit clear, not the explicit SGRAttrClear).
func (*Buffer) WriteSeq ¶
WriteSeq writes one or more ANSI ansi.escape sequences to the internal buffer, returning the number of bytes written. Skips any zero sequences provided.
func (*Buffer) WriteString ¶
WriteString to the internal buffer.
type Context ¶
type Context interface { // Enter is called to (re)establish terminal context at the start of the // first Term.RunWith and at the end of every Term.RunWithout. Enter(term *Term) error // Exit is called to restore original terminal context at the end of the // first Term.RunWith and at the start of Term.RunWithout. Exit(term *Term) error }
Context provides a piece of terminal context setup and teardown logic.
See Term.RunWith and Term.RunWithout for more detail.
type Cursor ¶
type Cursor struct { ansi.Point Attr ansi.SGRAttr Visible bool // contains filtered or unexported fields }
Cursor represents terminal cursor state, including position, graphics attributes, and visibility.
TODO more things like cursor shape and color
func (*Cursor) ApplyTo ¶
ApplyTo applies the receiver cursor state into the passed state value, writing any necessary control sequences into the provided buffer. Returns the number of bytes written, and the updated cursor state.
func (*Cursor) Hide ¶
Hide returns the control sequence necessary to hide the cursor if it is visible, the zero sequence otherwise.
func (*Cursor) MergeSGR ¶
MergeSGR merges the given SGR attribute into Attr, returning the difference.
func (*Cursor) NewLine ¶
NewLine moves the cursor to the start of the next line, returning the necessary ansi sequence.
func (*Cursor) ProcessANSI ¶
ProcessANSI updates cursor state to reflect having written the given escape value or rune to a terminal.
Graphic runes advance the cursor X position.
Supported escape sequences:
- CUU, CUD, CUF, and CUB all relatively update Point
- CUP sets Point absolutely
- SGR merges into Attr (see SGRAttr.Merge)
- SM and RM implement modes:
- private mode 25 updates Visible
Any errors decoding escape arguments are silenced, and the offending escape sequence(s) ignored.
func (*Cursor) Show ¶
Show returns the control sequence necessary to show the cursor if it is not visible, the zero sequence otherwise.
type DefaultRuneStyle ¶
type DefaultRuneStyle rune
DefaultRuneStyle maps 0 runes to a fixed rune value, only when their coresponding SGRAttr value is non-zero.
type FillRuneStyle ¶
type FillRuneStyle rune
FillRuneStyle maps 0 runes to a fixed rune value. Useful for normalizing transparent rune values when rendering a base grid.
type Grid ¶
Grid is a grid of screen cells.
func (Grid) CellOffset ¶
CellOffset returns the offset of the screen cell and true if it's within the Grid's Bounds().
func (Grid) Clear ¶
func (g Grid) Clear()
Clear the (maybe sub) grid; zeros all runes an attributes.
func (Grid) Eq ¶
Eq returns true only if the other grid has the same size and contents as the receiver.
func (Grid) Full ¶
Full returns the full grid containing the receiver grid, reversing any sub-grid targeting done by SubRect().
func (*Grid) IsSub ¶
IsSub returns true if the grid's bounding rectangle only covers a sub-section of its underlying data.
func (*Grid) Resize ¶
Resize the grid to have room for n cells. Returns true only if the resize was a change, false if it was a no-op.
func (Grid) SubAt ¶
SubAt is a convenience for calling SubRect with at as the new Min point, and the receiver's Rect.Max point.
type Input ¶
Input supports reading terminal input from a file handle with a buffer for things like escape sequences. It supports both blocking and non-blocking reads. It is not safe to use Input in parallel from multiple goroutines, such users need to layer a lock around an Input.
Example (Blocking) ¶
Reading input in blocking mode, like a simple REPL-style program might.
See cmd/decode/main.go for a more advanced example (including use of raw mode in addition to line-oriented as shown here).
package main import ( "fmt" "os" "github.com/jcorbin/anansi" ) func main() { term := anansi.NewTerm(os.Stdin, os.Stdout) term.SetEcho(true) anansi.MustRun(term.RunWith(func(term *anansi.Term) error { for { // process any (maybe partial) input first before stopping on error _, err := term.ReadMore() i := 0 for e, a, ok := term.Decode(); ok; e, a, ok = term.Decode() { if !e.IsEscape() { fmt.Printf("read[%v]: %q\n", i, rune(e)) } else if a != nil { fmt.Printf("read[%v]: %v %q\n", i, e, a) } else { fmt.Printf("read[%v]: %v\n", i, e) } i++ } if err != nil { return err // likely io.EOF } } })) }
Output:
Example (Nonblocking) ¶
Reading input in non-blocking mode 10 times a second, like an animated frame-rendering-loop program might.
package main import ( "fmt" "os" "syscall" "time" "github.com/jcorbin/anansi" "github.com/jcorbin/anansi/ansi" ) func main() { // We need to handle these signals so that we restore terminal state // properly (raw mode and exit the alternate screen). halt := anansi.Notify(syscall.SIGTERM, syscall.SIGINT) term := anansi.NewTerm(os.Stdin, os.Stdout, &halt) // run in a dedicated fullscreen, and handle input as it comes in term.SetRaw(true) term.AddMode(ansi.ModeAlternateScreen) anansi.MustRun(term.RunWith(func(term *anansi.Term) error { for range time.Tick(time.Second / 10) { // poll for halting signal before reading input if err := halt.AsErr(); err != nil { return err } // process any (maybe partial) input first before stopping on error // NOTE the need for CR below since we're in raw mode _, err := term.ReadAny() i := 0 for e, a, ok := term.Decode(); ok; e, a, ok = term.Decode() { if e == 0x03 { return fmt.Errorf("read %v", e) // stop on Ctrl-C } else if !e.IsEscape() { fmt.Printf("read[%v]: %q\r\n", i, rune(e)) } else if a != nil { fmt.Printf("read[%v]: %v %q\r\n", i, e, a) } else { fmt.Printf("read[%v]: %v\r\n", i, e) } i++ } if err != nil { return err // likely io.EOF } } return nil })) }
Output:
Example (NonblockingAsync) ¶
Reading input driven by asynchronous notifications (and doing so in the normative non-blocking way). This is another option for a fullscreen program when there's no need for an animation frame rendering loop or when input is processed independently from one.
package main import ( "fmt" "os" "syscall" "github.com/jcorbin/anansi" "github.com/jcorbin/anansi/ansi" ) func main() { // We need to handle these signals so that we restore terminal state // properly (raw mode and exit the alternate screen). halt := anansi.Notify(syscall.SIGTERM, syscall.SIGINT) term := anansi.NewTerm(os.Stdin, os.Stdout, &halt) // run in a dedicated fullscreen, and handle input as it comes in term.SetRaw(true) term.AddMode(ansi.ModeAlternateScreen) anansi.MustRun(term.RunWith(func(term *anansi.Term) error { canRead := make(chan os.Signal, 1) if err := term.Notify(canRead); err != nil { return err } for { select { case sig := <-halt.C: return anansi.SigErr(sig) case <-canRead: // process any (maybe partial) input first before stopping on error // NOTE the need for CR below since we're in raw mode _, err := term.ReadAny() i := 0 for e, a, ok := term.Decode(); ok; e, a, ok = term.Decode() { if e == 0x03 { return fmt.Errorf("read %v", e) // stop on Ctrl-C } else if !e.IsEscape() { fmt.Printf("read[%v]: %q\r\n", i, rune(e)) } else if a != nil { fmt.Printf("read[%v]: %v %q\r\n", i, e, a) } else { fmt.Printf("read[%v]: %v\r\n", i, e) } i++ } if err != nil { return err // likely io.EOF } } } })) }
Output:
func (*Input) Decode ¶
Decode decodes the next available ANSI escape sequence or UTF8 rune from the internal buffer filled by ReadMore or ReadAny. If the ok return value is false, then none can be decoded without first reading more input.
The caller should call e.IsEscape() to tell the difference between escape-sequence-signifying runes, and normal ones. Normal runes may then be cast and handled ala `if !e.IsEscape() { r := rune(e) }`.
NOTE any returned escape argument slice becomes invalid after the next call to Decode; the caller MUST copy any bytes out if it needs to retain them.
func (*Input) Enter ¶
Enter gets the current fcntl flags for restoration during Exit(), and sets non-blocking/async modes if needed.
func (*Input) IsRecording ¶
IsRecording returns true only if a recording destination is in effect (as given to SetRecording).
func (*Input) Notify ¶
Notify sets up async notification to the given channel, replacing (stopping notifications to) any prior channel passed to Notify(). If the passed channel is nil, async mode is disabled.
func (*Input) ReadAny ¶
ReadAny reads any (and all!) available bytes from the underlying file into the internal byte buffer; uses non-blocking reads. Returns the number of bytes read and any error.
func (*Input) ReadMore ¶
ReadMore from the underlying file into the internal byte buffer; it may block until at least one new byte has been read. Returns the number of bytes read and any error.
func (*Input) SetRecording ¶
SetRecording enables (or disables if nil-argument) input recording. When enabled, all read bytes are written to the given destination with added timing marks.
Timing marks are encoded as an ANSI Application Program Command (APC) string; schematically:
APC "recTime:" RFC3339Nano ST
For example:
"\x1b_recTime:12345\x1b\\any bytes read at that time"
In specific: ReadAny() records the time it was called (even if no bytes were available to be read); ReadMore() records the time that its blocking read call returned non-zero bytes (modulo go scheduler lag of course).
Any read error(s) are recorded similiarly:
APC "recReadErr:" error-string ST
type InputError ¶
type InputError []byte
InputError represets a recorderded read error.
func (InputError) Error ¶
func (ie InputError) Error() string
func (InputError) String ¶
func (ie InputError) String() string
type InputFrame ¶
type InputFrame struct { T time.Time // input timestamp E error // input read error B []byte // read input M []byte // pass through APC message }
InputFrame is a frame of recorded input.
func (InputFrame) String ¶
func (frm InputFrame) String() string
type InputReplay ¶
type InputReplay []InputFrame
InputReplay is a session of recorded input.
func ReadInputReplay ¶
func ReadInputReplay(f *os.File) (InputReplay, error)
ReadInputReplay reads an InputReplay from the provided file.
func (InputReplay) Duration ¶
func (rs InputReplay) Duration() time.Duration
Duration returns the total elapsed time of the replay.
type InputSignal ¶
type InputSignal struct {
Signal
}
InputSignal is a convenience wrapper for using a Signal and Input.Notify contextually.
func (*InputSignal) Enter ¶
func (is *InputSignal) Enter(term *Term) error
Enter opens the signal, and sets up input notification.
type Mode ¶
type Mode struct {
Set, Reset []byte
}
Mode holds a set/reset byte buffer to write to a terminal file during Enter/Exit. Primary useful for set/reset mode control sequences.
func (*Mode) AddModePair ¶
AddModePair adds the byte representation of the given set/reset sequences to the mode's Set/Reset byte buffers; appends to Set, prepends to Reset.
func (*Mode) AddModeSeq ¶
AddModeSeq appends one or more ansi sequences to the set buffer and prepends them to the reset buffer.
type Output ¶
Output supports writing buffered output from a io.WriterTo (implemented by both Cursor and Screen) into a file handle (presumably attached to a terminal). It is not safe to use Output in parallel from multiple goroutines, such users need to layer a lock around an Output.
func (*Output) Flush ¶
Flush calls the given io.Writerto on any active file handle. If EWOULDBLOCK occurs, it transitions the file into blocking mode, and restarts the write.
func (*Output) Stalls ¶
Stalls resets the stalls counter, returning the prior value; a stall happens when a flush must do a blocking write on an otherwise non-blocking underlying file. The caller must use the returned duration slice immediately, as it will be reused if full or if consume was true.
func (*Output) TrackStalls ¶
TrackStalls allocates a buffer for tracking stall times; otherwise Stalls() will always return nil. If output is used with a non-blocking file handle, and if a Flush() write encounters syscall.EWOULDBLOCK, then it switches the file handle into blocking mode, and performs a blocking write.
Such blocking flush is counted as a "stall", and the total time spent performing FCNTL syscalls and the blocking is counted in any internal buffer (allocated by TrackStalls) for further collection and reporting. Once the buffer fills, such timing measurements cease to be taken, as if no buffer was available. Users interested in collecting these metrics should attempt to harvest data using the Stalls() method, and only process such data when it is full (len() == cap()).
type Processor ¶
Processor receives decoded ANSI escape sequences and Unicode runes from Buffer.Process.
type Screen ¶
Screen extends Cursor with a Grid of screen content. allowing consumers to reason about the state of the terminal screen (virtual, real, or otherwise).
func WriteGrid ¶
WriteGrid writes a grid's contents into an io.Writer, relative to current cursor state and any prior screen contents.
To force an absolute (non-differential) update, pass an empty prior grid. Returns the number of bytes written, final cursor state, and any write error encountered.
func (*Screen) Clear ¶
func (sc *Screen) Clear()
Clear the screen grid, and reset cursor state (to invisible nowhere).
func (Screen) Full ¶
Full returns a shallow copy of the screen with the Grid restored to its full area.
func (*Screen) ProcessANSI ¶
ProcessANSI updates screen state to reflect having written the given escape value or rune to a terminal; in addition to CursorState.ProcessANSI semantics:
Graphic runes update the virtual cell grid, using the current cursor SGR attribute, at the current cursor point.
Supported escape sequences:
- ED to erase display
- EL to erase line
- cursor movement sequences, as per CursorState.ProcessANSI, but clamped to the screen bounds
Any errors decoding escape arguments are silenced, and the offending escape sequence(s) ignored.
func (*Screen) Resize ¶
Resize the underlying Grid, and zero the cursor position if out of bounds. Returns true only if the resize was a change, false if it was a no-op.
func (Screen) SubAt ¶
SubAt returns a shallow copy of the screen with a sub-Grid anchored at the given point; clamps the cursor to the new bounding rectangle.
func (Screen) SubRect ¶
SubRect returns a shallow copy of the screen with a sub-Grid bounded by the given rectangle; clamps the cursor to the new bounding rectangle.
func (Screen) SubSize ¶
SubSize returns a shallow copy of the screen with a sub-Grid resized to the given size; clamps the cursor to the new bounding rectangle.
type ScreenDiffer ¶
type ScreenDiffer struct { UserCursor Cursor VirtualScreen Real Screen }
ScreenDiffer supports deferred screen updating by tracking desired virtual screen state vs last known (Real) screen state. It also supports tracking final desired user cursor state, separate from any cursor state used to update the virtual screen. Primitive vt100 emulation is provided through Write* methods and Buffer processing.
func (*ScreenDiffer) Clear ¶
func (sc *ScreenDiffer) Clear()
Clear the virtual screen, user cursor state, and internal buffer.
func (*ScreenDiffer) Invalidate ¶
func (sc *ScreenDiffer) Invalidate()
Invalidate forces the next WriteTo() to perform a full redraw.
func (*ScreenDiffer) Reset ¶
func (sc *ScreenDiffer) Reset()
Reset calls Clear() and restores virtual cursor state.
func (*ScreenDiffer) Resize ¶
func (sc *ScreenDiffer) Resize(size image.Point) bool
Resize the current screen state, and invalidate to cause a full redraw.
func (*ScreenDiffer) WriteTo ¶
func (sc *ScreenDiffer) WriteTo(w io.Writer) (n int64, err error)
WriteTo writes the content and ansi control sequences necessary to synchronize Real screen state to match VirtualScreen state. performs a differential update if possible, falling back to a full redraw if necessary or forced by Invalidate().
If the given io.Writer implements higher level ansi writing methods they are used directly; otherwise an internal Buffer is used to first assemble the needed output, then delegating to Buffer.WriteTo to flush output.
When using an internal Buffer, resuming a partial write after EWOULDBLOCK is supported, skipping the assembly step described above.
type Signal ¶
Signal supports Term-contextual signal notification.
Example:
usr1 := anansi.Notify(syscall.SIGUSR1) anansi.NewTerm(in, out, &usr1).RunWith(func(term *anansi.Term) error { for sig := range usr1 { // TODO something } })
See ExampleSignal for a more normative example.
Example ¶
Handling some of the usual terminal lifecycle signals.
package main import ( "errors" "log" "os" "syscall" "github.com/jcorbin/anansi" ) func main() { var ( halt = anansi.Notify(syscall.SIGTERM, syscall.SIGHUP) stop = anansi.Notify(syscall.SIGINT) resize = anansi.Notify(syscall.SIGWINCH) ) term := anansi.NewTerm( os.Stdin, os.Stdout, &halt, &resize, ) anansi.MustRun(term.RunWith(func(term *anansi.Term) error { for { select { case sig := <-halt.C: // Terminate program asap on a halting signal; wrapping it in // anansi.SigErr provides transparency both internally, when // anansi.MustRun logs, and externally by setting the normative // exit code "killed by signal X" status code. return anansi.SigErr(sig) case <-stop.C: // Interrupt, on the other hand, may not always result in // immediate halt, it may only mean "stop / cancel whatever // operation is being currently run, but don't halt". Here we // just return a regular error, which will cause a normal "exit // code 1", externally hiding the fact that we stopped due to // SIGINT. return errors.New("stop") case <-resize.C: sz, _ := term.Size() log.Printf("Terminal resized to %v", sz) } } })) }
Output:
func (*Signal) AsErr ¶
AsErr is a convenience for handling a termination signal set: it returns a SignalError if from any available signal on the channel, or nil if no signal is available.
func (*Signal) Exit ¶
Exit is a no-op; NOTE signal notification is not stopped during temporary teardown (e.g when suspending).
type SignalError ¶
SignalError supports passing signals as errors.
func (SignalError) Error ¶
func (sig SignalError) Error() string
func (SignalError) ExitCode ¶
func (sig SignalError) ExitCode() int
ExitCode returns the corresponding value that the program should exit with due to the wrapped signal.
func (SignalError) Signal ¶
func (sig SignalError) Signal()
Signal implements the os.Signal interface, allowing SignalError to double both as an error, and a wrapped signal value.
func (SignalError) String ¶
func (sig SignalError) String() string
type Style ¶
Style allows styling of cell data during a drawing or rendering routine. Its eponymous method gets called for each cell as it is about to be rendered
The Style method receives the point being rendered on screen or within a destination Grid. Its pr and pa arguments contain any prior rune and attribute data when drawing in a Grid, while the r and a arguments contain the source rune and attribute being drawn.
Whatever rune and attribute values are returned by Style() will be the ones drawn/rendered.
Style implementations are also called with all-zero argument values (i.e. an invalid ansi point) to probe any default rune/attr values. These default values are then used as fill values when rendering or drawing
var ( // TransparentRunes is a style that stops 0 rune values from overwriting // any prior rune values when drawing. TransparentRunes Style = transparentRuneStyle{} // TransparentAttrBG is a style that stops 0 background attribute from // overwriting any prior attribute background value when drawing. TransparentAttrBG Style = transparentAttrStyle(ansi.SGRAttrBGMask) // TransparentAttrFG is a style that stops 0 foreground attribute from // overwriting any prior attribute foreground value when drawing. This // includes text attributes like bold and italics, not just foreground // color. TransparentAttrFG Style = transparentAttrStyle(ansi.SGRAttrFGMask | ansi.SGRAttrMask) // TransparentAttrBGFG is a style that acts as both TransparentBG and // TransparentAttrFG combined. TransparentAttrBGFG Style = transparentAttrStyle(ansi.SGRAttrBGMask | ansi.SGRAttrFGMask | ansi.SGRAttrMask) )
var NoopStyle Style = _noopStyle{}
NoopStyle is a no-op style, used as a zero fill by Styles.
type Term ¶
Term combines a terminal file handle with attribute control and further Context-ual state.
func NewTerm ¶
NewTerm constructs a new terminal attached the given file pair, and with the given context.
func OpenTerm ¶
OpenTerm opens the standard terminal, attached to the controlling terminal. Prefers to existing os.Stdin and os.Stdout files if they're still attached, opens /dev/tty otherwise.
func (*Term) AddContext ¶
AddContext adds one or more Contexts to the terminal. Panics if called under RunWith.
func (*Term) IsTerminal ¶
IsTerminal returns true only if both terminal input and output file handles are both connected to a valid terminal.
func (*Term) RunWith ¶
RunWith runs the given function within the terminal's context, Enter()ing it if necessary, and Exit()ing it if Enter() was called after the given function returns. Exit() is called even if the within function returns an error or panics.
If the context implements a `Close() error` method, then it will also be called immediately after Exit(). This allows a Context implementation to differentiate between temporary teardown, e.g. suspending under RunWithout, and final teardown as RunWith returns.
func (*Term) RunWithout ¶
RunWithout runs the given function without the terminal's context, Exit()ing it if necessary, and Enter()ing it if deactivation was necessary. Re-Enter() is not called is not done if a non-nil error is returned, or if the without function panics.
type TermScreen ¶
type TermScreen struct {
ScreenDiffer
}
TermScreen supports attaching a ScreenDiffer to a Term's Context.
Example (Termapp) ¶
An example of building a simple fullscreen terminal application with an async io loop.
package main import ( "fmt" "os" "syscall" "time" "github.com/jcorbin/anansi" "github.com/jcorbin/anansi/ansi" ) var ( // We need to handle these signals so that we restore terminal state // properly (raw mode and exit the alternate screen). halt = anansi.Notify(syscall.SIGTERM, syscall.SIGINT) // terminal resize signals resize = anansi.Notify(syscall.SIGWINCH) // input availability notification inputReady anansi.InputSignal // The virtual screen that will be our canvas. screen anansi.TermScreen ) // logf prints a timestamped message to the virtual screen func logf(mess string, args ...interface{}) { fmt.Fprintf(&screen, "t:%v ", time.Now()) fmt.Fprintf(&screen, mess, args...) screen.WriteString("\r\n") } // An example of building a simple fullscreen terminal application with an // async io loop. func main() { term := anansi.NewTerm(os.Stdin, os.Stdout, &halt, &resize, &inputReady) term.SetRaw(true) term.AddMode(ansi.ModeAlternateScreen) resize.Send("initialize screen size") anansi.MustRun(term.RunWith(run)) } // run implements the main event loop under managed terminal context. func run(term *anansi.Term) error { for { select { case sig := <-halt.C: return anansi.SigErr(sig) case <-resize.C: if err := screen.SizeToTerm(term); err != nil { return err } logf("resized:%v", screen.Bounds().Size()) update(term) case <-inputReady.C: _, err := term.ReadAny() if uerr := update(term); err == nil { err = uerr } if err != nil { return err // likely io.EOF } } } } // update handles any available input then flushes the screen. func update(term *anansi.Term) error { for e, a, ok := term.Decode(); ok; e, a, ok = term.Decode() { switch e { case 0x03: // stop on Ctrl-C return fmt.Errorf("read %v", e) case 0x0c: // clear screen on Ctrl-L screen.Clear() // clear virtual contents screen.To(ansi.Pt(1, 1)) // cursor back to top screen.Invalidate() // force full redraw logf("<clear>") default: // log input to screen if !e.IsEscape() { logf("rune:%q", rune(e)) } else if a == nil { logf("escape:%v", e) } else { logf("escape:%v arg:%q", e, a) } } } return term.Flush(&screen) }
Output:
func (*TermScreen) Exit ¶
func (tsc *TermScreen) Exit(term *Term) error
Exit Reset()s all virtual state, and restores real terminal graphics and cursor state.
func (*TermScreen) SizeToTerm ¶
func (tsc *TermScreen) SizeToTerm(term *Term) error
SizeToTerm invalidates and resizes the screen to match the passed terminal's current size.
type VirtualCursor ¶
VirtualCursor supports collecting buffered ansi output while tracking virtual cursor state and last known Real cursor state. Buffered output can be flushed with WriteTo(), or discarded with Reset(). Real cursor state is only affected after a WriteTo(), and is restored after a Reset().
func (*VirtualCursor) Apply ¶
func (c *VirtualCursor) Apply(cs Cursor)
Apply the given cursor state, writing any necessary escape sequences into the internal buffer.
func (*VirtualCursor) Hide ¶
func (c *VirtualCursor) Hide()
Hide ensures that the cursor is not visible, writing the necessary control sequence into the internal buffer if this is a change.
func (*VirtualCursor) Reset ¶
func (c *VirtualCursor) Reset()
Reset the internal buffer and restore cursor state to last state affected by WriteTo.
func (*VirtualCursor) Show ¶
func (c *VirtualCursor) Show()
Show ensures that the cursor is visible, writing the necessary control sequence into the internal buffer if this is a change.
func (*VirtualCursor) To ¶
func (c *VirtualCursor) To(pt ansi.Point)
To moves the cursor to the given point using absolute (ansi.CUP) or relative (ansi.{CUU,CUD,CUF,CUD}) if possible.
func (*VirtualCursor) Write ¶
func (c *VirtualCursor) Write(p []byte) (n int, err error)
Write to the internal buffer, updating cursor state per any ANSI escape sequences, and advancing cursor position by rune count (clamped to screen size).
func (*VirtualCursor) WriteByte ¶
func (c *VirtualCursor) WriteByte(b byte) error
WriteByte to the internal buffer, advancing cursor position (clamped to screen size).
func (*VirtualCursor) WriteESC ¶
func (c *VirtualCursor) WriteESC(seqs ...ansi.Escape) int
WriteESC writes one or more ANSI escapes to the internal buffer, returning the number of bytes written; updates cursor state as appropriate.
func (*VirtualCursor) WriteRune ¶
func (c *VirtualCursor) WriteRune(r rune) (n int, err error)
WriteRune to the internal buffer, advancing cursor position (clamped to screen size).
func (*VirtualCursor) WriteSGR ¶
func (c *VirtualCursor) WriteSGR(attrs ...ansi.SGRAttr) (n int)
WriteSGR writes one or more ANSI SGR sequences to the internal buffer, returning the number of bytes written; updates Attr cursor state.
func (*VirtualCursor) WriteSeq ¶
func (c *VirtualCursor) WriteSeq(seqs ...ansi.Seq) int
WriteSeq writes one or more ANSI escape sequences to the internal buffer, returning the number of bytes written; updates cursor state as appropriate.
func (*VirtualCursor) WriteString ¶
func (c *VirtualCursor) WriteString(s string) (n int, err error)
WriteString to the internal buffer, updating cursor state per any ANSI escape sequences, and advancing cursor position by rune count (clamped to screen size).
type VirtualScreen ¶
type VirtualScreen struct { Screen // contains filtered or unexported fields }
VirtualScreen implements minimal terminal emulation around ScreenState.
Normal textual output and control sequences may be written to it, using either high-level convenience methods, or the standard low-level byte/string/rune/byte writing methods. All such output is collected and processed through an internal Buffer, and ScreenState updated accordingly.
TODO the vt100 emulation is certainly too minimal at present; it needs to be completed, and validated. E.g. cursor position is currently clamped to the screen size, rather than wrapped.
func (*VirtualScreen) Write ¶
func (vsc *VirtualScreen) Write(p []byte) (n int, err error)
Write writes bytes to the internal buffer, updating screen state as describe on VirtualScreen. Always returns nil error.
func (*VirtualScreen) WriteByte ¶
func (vsc *VirtualScreen) WriteByte(b byte) error
WriteByte writes a single byte to the internal buffer, updating screen state as described on VirtualScreen. Always returns nil error.
func (*VirtualScreen) WriteESC ¶
func (vsc *VirtualScreen) WriteESC(seqs ...ansi.Escape) int
WriteESC writes one or more ANSI escapes to the internal buffer, updating screen state as described on VirtualScreen.
func (*VirtualScreen) WriteRune ¶
func (vsc *VirtualScreen) WriteRune(r rune) (n int, err error)
WriteRune writes a single rune to the internal buffer, updating screen state as described on VirtualScreen. Always returns nil error.
func (*VirtualScreen) WriteSGR ¶
func (vsc *VirtualScreen) WriteSGR(attrs ...ansi.SGRAttr) (n int)
WriteSGR writes one or more ANSI SGR sequences to the internal buffer, updating screen state as described on VirtualScreen.
func (*VirtualScreen) WriteSeq ¶
func (vsc *VirtualScreen) WriteSeq(seqs ...ansi.Seq) int
WriteSeq writes one or more ANSI escape sequences to the internal buffer, updating screen state as described on VirtualScreen.
func (*VirtualScreen) WriteString ¶
func (vsc *VirtualScreen) WriteString(s string) (n int, err error)
WriteString writes a string to the internal buffer, updating screen state as describe on VirtualScreen. Always returns nil error.
type ZeroRuneStyle ¶
type ZeroRuneStyle rune
ZeroRuneStyle maps a fixed rune to 0. Useful for implementing transparency of rune values, e.g. space space characters or empty braille cells.
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
Package ansi provides support for text encoded ANSI, focusing especially on escape and control sequences.
|
Package ansi provides support for text encoded ANSI, focusing especially on escape and control sequences. |
cmd
|
|
Package terminfo contains a simple and incomplete implementation of the terminfo database.
|
Package terminfo contains a simple and incomplete implementation of the terminfo database. |
x
|
|