anansi

package module
v0.0.0-...-863c820 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 12, 2019 License: BSD-3-Clause Imports: 15 Imported by: 7

README

AnANSI - a bag of collected wisdom for manipulating terminals

... or yet ANother ANSI terminal library.

Why?

  • Designed to be a loosely coupled set of principled layers, rather than (just) one unified convenient interface
  • Be more Go-idiomatic / natural: e.g. ansi.DecodeEscape following utf8.DecodeRune convention, rather than heavier weight event parsing/handling
  • Supporting use cases other than fullscreen raw mode
  • Allow applications to choose input modality, rather than lock-in to one paradigm like non-blocking/SIGIO
  • Support implementing terminal emulators, e.g. to build a multiplexer or debug wrapper

Status

Experimental: AnANSI is currently in initial exploration mode, and while things on master are reasonably stable, there's no guarantees yet.

Demos

The Decode Demo Command demonstrates ansi Decoding, optionally with mouse reporting and terminal state manipulation (for raw and alternate screen mode). When run in batch mode (on a regular file rather than a terminal), it replaces ansi escape sequences with [ansi ...] representation strings; there is also a -strip flag, to simply strip all escape sequences from a file.

The Palette Demo Command demonstrates the various ansi color spaces, and optionally some vendor standard color themes.

There's also a lolwut demo, which is a port of antirez's, demonstrating braille-bitmap rendering capability. It has an optional interactive animated mode of which demonstrates the experimental x/platform layer.

There's another x/platform demo that draws a colorful test pattern.

Done

Experimental cohesive x/platform layer:

  • provides a platform.Events queue layered on top of anansi.input, which contains parsed rune, ansi.Escape, and ansi.MouseState data
  • synthesizes all of the below anansi pieces (Term, Input, Output, etc) into one cohesive platform.Context which supports a single combined round of non-blocking input processing and output generation
  • provides signal handling for typical things like SIGINT, SIGERM, SIGHUP, and SIGWINCH
  • drives a platform.Client in a platform.Tick loop at a desired Frames-Per-Second (FPS) rate
  • provides input record and replay on top of (de)serialized client and platform state
  • supports inter-frame background work
  • provides a diagnostic HUD overlay that displays things like Go's log output, FPS, time, mouse state, screen size, etc

Toplevel anansi package:

  • anansi.Term, anansi.Context, anansi.Attr, and anansi.Mode provide cohesive management of terminal state such as raw mode, ANSI escape sequenced modes, and SGR attribute state
  • anansi.Input supports reading input from a file handle, implementing both blocking .ReadMore() and non-blocking .ReadAny() modes
  • anansi.Output mediates flushing output from any io.WriterTo (implemented by both anansi.Cursor and anansi.Screen) into a file handle. It properly handles non-blocking IO (by temporarily doing a blocking write if necessary) to coexist with anansi.Input (since stdin and stdout share the same underlying file descriptor)
  • anansi.Cursor represents cursor state including position, visibility, and SGR attribute(s); it supports processing under an anansi.Buffer
  • anansi.Grid provides a 2d array of rune andansi.SGRAttr data; it supports processing under an anansi.Buffer.
  • anansi.Screen combines an anansi.Cursor with anansi.Grid, supporting differential screen updates and final post-update cursor display
  • anansi.Bitmap provides a 2d bitmap that can be rendered or drawn into braille runes.
  • Both anansi.Grid and anansi.Bitmap support anansi.Styled rendering into an anansi.Buffer, or drawing into an (other) anansi.Grid.
  • anansi.Buffer supports deferred writing to a terminal; the primary trick that it adds beyond a basic bytes.Buffer convenience, is allowing the users to process escape sequences, no matter how they're written. This enables keeping virtual state (such as cursor position or a cell grid) up to date without locking downstream users into specific APIs for writing

Core anansi/ansi package:

  • ansi.DecodeEscape provides escape sequence decoding as similarly to utf8.DecodeRune as possible. Additional support for decoding escape arguments is provided (DecodeNumber, DecodeSGR, DecodeMode, and DecodeCursorCardinal)
  • ansi.SGRAttr supports dealing with terminal colors and text attributes
  • ansi.MouseState supports handling xterm extended mouse reporting
  • function definitions like ansi.CUP and ansi.SM for building control sequences terminal state management
  • ansi.Mode supports setting and clearing various modes such as mouse reporting (and its optional extra levels like motion and full button reporting)
  • ansi.Point and ansi.Rectangle support sane handling of 1,1-originated screen geometry
Errata
  • differential screen update is still not perfect, although the glitches that were previously present are now lessened due to the functional test; however this was done by removing a (perhaps premature) cursor movement optimization to simplify diffing
  • Works For Me ™ in tmux-under-iTerm2: should also work in other modern xterm-descended terminals, such as the libvte family; however terminfo detection not yet used by the platform layer, so basic things like smcup/rmcup inversion may by broken
  • anansi.Screen doesn't (yet) implement full vt100 emulation, notably lacking is scrolling region support
  • there's something glitchy with trying to write into the final cell (last column of last row), sometimes it seems to trigger a scroll (as when used by hud log view) sometimes not (as when background filled by demo)
WIP
  • an experimental mid-tier anui package architected around a stack of cooperative layers with user-interaction-authority over each other provides a re-usable fullscreen run loop for many application cases.
  • an interact command demo which allows you to interactively manipulate arguments passed to a dynamically executed command
TODO
  • fancier image rendition (e.g. leveraging iTerm2's image support)
  • special decoding for CSI M, whose arg follows AFTER
  • provide DecodeEscapeInString(s string) for completeness
  • support bracketed paste mode (and decoding pastes from it)
  • consider compacting the record file format; maybe also compression it
  • terminfo layer:
    • automated codegen (for builtins)
    • full load rather than the termbox-inherited cherry picking
  • terminal interrogation:
    • where's the cursor?
    • CSI DA
    • CSI DSR
Branches

AnANSI uses a triple branch (master, rc, and dev) pattern that I've found useful:

  • the master branch has relatively stable code but is still pre v1.0.0, and so is not actually stable; tests must pass on all commits. NOTE any package under anansi/x/ doesn't even have the tacit attempt made at stability that the rest of anansi/ on master does.
  • the rc branch contains code that is stable-ish: tests should pass on all commits
  • the dev branch contains the sum of all hopes/fears, tests may not pass

Resources

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var TransparentBrailleRunes = ZeroRuneStyle('\u2800')

TransparentBrailleRunes maps the empty braille rune to (U+2800) to 0.

Functions

func DrawBitmap

func DrawBitmap(dst Grid, src Bitmap, styles ...Style)

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

func DrawGrid(dst, src Grid, styles ...Style)

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

func IsStandardTermFile(f *os.File) bool

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

func IsTerminal(f *os.File) bool

IsTerminal returns true only if the given file is attached to an interactive terminal.

func MustParseBitmap

func MustParseBitmap(set string, lines ...string) (stride int, data []bool)

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

func ParseBitmap(set string, lines ...string) (stride int, data []bool, err error)

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).

func Process

func Process(proc Processor, p []byte) (n int)

Process decodes ansi escapes and utf8 runes from p, passing them to proc.

func SigErr

func SigErr(sig os.Signal) error

SigErr is a convenience constructor for SignalError values.

func WriteBitmap

func WriteBitmap(w io.Writer, bi Bitmap, styles ...Style) (int, error)

WriteBitmap writes a bitmap's contents as braille runes into an io.Writer. Optional style(s) may be passed to control graphical rendition of the braille runes.

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

func (at *Attr) Enter(term *Term) (err error)

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

func (at *Attr) Exit(term *Term) error

Exit restores termios attributes, and clears the File pointer if it was set by Enter

func (Attr) IsTerminal

func (at Attr) IsTerminal() bool

IsTerminal returns true only if the underlying file is attached to an interactive terminal.

func (*Attr) SetEcho

func (at *Attr) SetEcho(echo bool) error

SetEcho toggles input echoing mode, which is off by default in raw mode, and on in normal mode.

func (*Attr) SetRaw

func (at *Attr) SetRaw(raw bool) error

SetRaw controls whether the terminal should be in raw mode.

Raw mode is suitable for full-screen terminal user interfaces, eliminating keyboard shortcuts for job control, echo, line buffering, and escape key debouncing.

func (Attr) Size

func (at Attr) Size() (size image.Point, err error)

Size reads and returns the current terminal size.

type AttrStyle

type AttrStyle ansi.SGRAttr

AttrStyle implements a Style that returns a fixed ansi attr for any non-zero runes.

func (AttrStyle) Style

func (as AttrStyle) Style(p ansi.Point, pr, r rune, pa, a ansi.SGRAttr) (rune, ansi.SGRAttr)

Style replaces the passed attr with the receiver if the passed rune is non-0.

type Bitmap

type Bitmap struct {
	Bit    []bool
	Stride int
	Rect   image.Rectangle
}

Bitmap is a 2-color bitmap targeting unicode braille runes.

func (*Bitmap) Get

func (bi *Bitmap) Get(p image.Point) bool

Get a single bitmap cell value.

func (*Bitmap) Load

func (bi *Bitmap) Load(stride int, data []bool)

Load the given bit data into the bitmap.

func (*Bitmap) Resize

func (bi *Bitmap) Resize(sz image.Point)

Resize the bitmap, re-allocating its bit storage.

func (*Bitmap) Rune

func (bi *Bitmap) Rune(p image.Point) (c rune)

Rune builds a unicode braille rune representing a single 2x4 rectangle of bits, anchored at the give top-left point.

func (*Bitmap) RuneSize

func (bi *Bitmap) RuneSize() (sz image.Point)

RuneSize returns the size of the bitmap in runes.

func (*Bitmap) Set

func (bi *Bitmap) Set(p image.Point, b bool)

Set a single bitmap cell value.

func (Bitmap) SubAt

func (bi Bitmap) SubAt(at image.Point) Bitmap

SubAt is a convenience for calling SubRect with at as the new Min point, and the receiver's Rect.Max point.

func (Bitmap) SubRect

func (bi Bitmap) SubRect(r image.Rectangle) Bitmap

SubRect returns a subgrid, sharing the receiver's Rune/Attr/Stride data, but with a new bounding Rect. Clamps r.Max to bi.Rect.Max, and returns the zero Bitmap if r.Min is not in bi.Rect.

func (Bitmap) SubSize

func (bi Bitmap) SubSize(sz image.Point) Bitmap

SubSize is a convenience for calling SubRect with a Max point determined by adding the given size to the receiver's Rect.Min 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

func (b *Buffer) Bytes() []byte

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) Grow

func (b *Buffer) Grow(n int)

Grow the internal buffer to have room for at least n bytes.

func (*Buffer) Len

func (b *Buffer) Len() int

Len returns the number of unwritten bytes in the buffer.

func (*Buffer) Process

func (b *Buffer) Process(proc Processor)

Process bytes written to the internal buffer, decoding runes and escape sequences, and passing them to the given processor.

func (*Buffer) Reset

func (b *Buffer) Reset()

Reset the internal buffer.

func (*Buffer) Skip

func (b *Buffer) Skip(n int)

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) Write

func (b *Buffer) Write(p []byte) (n int, err error)

Write to the internal buffer.

func (*Buffer) WriteByte

func (b *Buffer) WriteByte(c byte) error

WriteByte to the internal buffer.

func (*Buffer) WriteESC

func (b *Buffer) WriteESC(seqs ...ansi.Escape) int

WriteESC writes one or more ANSI escapes to the internal buffer, returning the number of bytes written.

func (*Buffer) WriteRune

func (b *Buffer) WriteRune(r rune) (n int, err error)

WriteRune to the internal buffer.

func (*Buffer) WriteSGR

func (b *Buffer) WriteSGR(attrs ...ansi.SGRAttr) int

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

func (b *Buffer) WriteSeq(seqs ...ansi.Seq) int

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

func (b *Buffer) WriteString(s string) (n int, err error)

WriteString to the internal buffer.

func (*Buffer) WriteTo

func (b *Buffer) WriteTo(w io.Writer) (n int64, err error)

WriteTo writes all bytes from the internal buffer to the given io.Writer.

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.

func Contexts

func Contexts(cs ...Context) Context

Contexts returns a Context that: calls all given context Enter()s in order, stopping on first failure; and calls all given context Exit()s in reverse order, returning the first error, but proceeding to all all remaining Exit() even under error.

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

func (cs *Cursor) ApplyTo(w io.Writer, cur Cursor) (int, Cursor, error)

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

func (cs *Cursor) Hide() ansi.Seq

Hide returns the control sequence necessary to hide the cursor if it is visible, the zero sequence otherwise.

func (*Cursor) MergeSGR

func (cs *Cursor) MergeSGR(attr ansi.SGRAttr) ansi.SGRAttr

MergeSGR merges the given SGR attribute into Attr, returning the difference.

func (*Cursor) NewLine

func (cs *Cursor) NewLine() ansi.Seq

NewLine moves the cursor to the start of the next line, returning the necessary ansi sequence.

func (*Cursor) ProcessANSI

func (cs *Cursor) ProcessANSI(e ansi.Escape, a []byte)

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

func (cs *Cursor) Show() ansi.Seq

Show returns the control sequence necessary to show the cursor if it is not visible, the zero sequence otherwise.

func (Cursor) String

func (cs Cursor) String() string

func (*Cursor) To

func (cs *Cursor) To(pt ansi.Point) ansi.Seq

To constructs an ansi control sequence that will move the cursor to the given screen point using absolute (ansi.CUP) or relative (ansi.{CUU,CUD,CUF,CUD}) if possible. Returns a zero sequence if the cursor is already at the given point.

type DefaultRuneStyle

type DefaultRuneStyle rune

DefaultRuneStyle maps 0 runes to a fixed rune value, only when their coresponding SGRAttr value is non-zero.

func (DefaultRuneStyle) Style

func (fs DefaultRuneStyle) Style(p ansi.Point, pr, r rune, pa, a ansi.SGRAttr) (rune, ansi.SGRAttr)

Style replaces the passed rune with the receiver if the passed rune is 0 and the passed attr is not.

type ExitError

type ExitError interface {
	error
	ExitCode() int
}

ExitError may be implemented by an error to customize the exit code under MustRun.

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.

func (FillRuneStyle) Style

func (fs FillRuneStyle) Style(p ansi.Point, pr, r rune, pa, a ansi.SGRAttr) (rune, ansi.SGRAttr)

Style replaces the passed rune with the receiver if the passed rune is 0.

type Grid

type Grid struct {
	Rect   ansi.Rectangle
	Stride int
	Attr   []ansi.SGRAttr
	Rune   []rune
}

Grid is a grid of screen cells.

func (Grid) Bounds

func (g Grid) Bounds() ansi.Rectangle

Bounds returns the screen bounding rectangle of the grid.

func (Grid) CellOffset

func (g Grid) CellOffset(pt ansi.Point) (int, bool)

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

func (g Grid) Eq(other Grid, zero rune) bool

Eq returns true only if the other grid has the same size and contents as the receiver.

func (Grid) Full

func (g Grid) Full() Grid

Full returns the full grid containing the receiver grid, reversing any sub-grid targeting done by SubRect().

func (*Grid) IsSub

func (g *Grid) IsSub() bool

IsSub returns true if the grid's bounding rectangle only covers a sub-section of its underlying data.

func (*Grid) Resize

func (g *Grid) Resize(size image.Point) bool

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

func (g Grid) SubAt(at ansi.Point) Grid

SubAt is a convenience for calling SubRect with at as the new Min point, and the receiver's Rect.Max point.

func (Grid) SubRect

func (g Grid) SubRect(r ansi.Rectangle) Grid

SubRect returns a sub-grid, sharing the receiver's Rune/Attr/Stride data, but with a new bounding Rect. Clamps r.Max to g.Rect.Max, and returns the zero Grid if r.Min is not in g.Rect.

func (Grid) SubSize

func (g Grid) SubSize(sz image.Point) Grid

SubSize is a convenience for calling SubRect with a Max point determined by adding the given size to the receiver's Rect.Min point.

type Input

type Input struct {
	File        *os.File
	MinReadSize int
	// contains filtered or unexported fields
}

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) AtEOF

func (in *Input) AtEOF() bool

AtEOF returns true if the last input read returned io.EOF.

func (*Input) Close

func (in *Input) Close() error

Close stops any signal notification setup by Notify().

func (*Input) Decode

func (in *Input) Decode() (e ansi.Escape, a []byte, ok bool)

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

func (in *Input) Enter(term *Term) error

Enter gets the current fcntl flags for restoration during Exit(), and sets non-blocking/async modes if needed.

func (*Input) Exit

func (in *Input) Exit(term *Term) error

Exit restores fcntl flags to their Enter() time value.

func (*Input) IsRecording

func (in *Input) IsRecording(dest *os.File) bool

IsRecording returns true only if a recording destination is in effect (as given to SetRecording).

func (*Input) Notify

func (in *Input) Notify(sigio chan os.Signal) error

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

func (in *Input) ReadAny() (n int, err error)

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

func (in *Input) ReadMore() (int, error)

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

func (in *Input) SetRecording(dest io.Writer)

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) AddMode

func (mode *Mode) AddMode(ms ...ansi.Mode)

AddMode adds Set/Reset pairs from one or more ansi.Mode values.

func (*Mode) AddModePair

func (mode *Mode) AddModePair(set, reset ansi.Seq)

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

func (mode *Mode) AddModeSeq(seqs ...ansi.Seq)

AddModeSeq appends one or more ansi sequences to the set buffer and prepends them to the reset buffer.

func (*Mode) Enter

func (mode *Mode) Enter(term *Term) error

Enter writes the modes' Set() string to the terminal's output file.

func (*Mode) Exit

func (mode *Mode) Exit(term *Term) error

Exit writes the modes' Reset() string to the terminal's output file.

type Output

type Output struct {
	File    *os.File
	Flushed int
	// contains filtered or unexported fields
}

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) Enter

func (out *Output) Enter(term *Term) error

Enter is a no-op.

func (*Output) Exit

func (out *Output) Exit(term *Term) error

Exit is a no-op.

func (*Output) Flush

func (out *Output) Flush(wer io.WriterTo) error

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

func (out *Output) Stalls(consume bool) []time.Duration

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

func (out *Output) TrackStalls(n int)

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

type Processor interface {
	ProcessANSI(e ansi.Escape, a []byte)
}

Processor receives decoded ANSI escape sequences and Unicode runes from Buffer.Process.

type Screen

type Screen struct {
	Cursor Cursor
	Grid
}

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

func WriteGrid(w io.Writer, g Grid, prior Screen, styles ...Style) (int, Screen, error)

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

func (sc Screen) Full() Screen

Full returns a shallow copy of the screen with the Grid restored to its full area.

func (*Screen) ProcessANSI

func (sc *Screen) ProcessANSI(e ansi.Escape, a []byte)

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

func (sc *Screen) Resize(size image.Point) bool

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) String

func (sc Screen) String() string

func (Screen) SubAt

func (sc Screen) SubAt(at ansi.Point) Screen

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

func (sc Screen) SubRect(r ansi.Rectangle) Screen

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

func (sc Screen) SubSize(sz image.Point) Screen

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.

func (*Screen) To

func (sc *Screen) To(pt ansi.Point)

To sets the virtual cursor point to the supplied one.

func (*Screen) Update

func (sc *Screen) Update(w io.Writer, prior Screen) (int, Screen, error)

Update syncs screen state from the receiver against the given prior screen state, generating writing any/all necessary ansi control sequences into the given writer. Returns the number of bytes written and the final screen state, which will now equal the receiver state.

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

type Signal struct {
	Notify []os.Signal
	C      chan os.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 Notify

func Notify(notify ...os.Signal) Signal

Notify is a convenience constructor for Signal values.

func (*Signal) AsErr

func (sig *Signal) AsErr() error

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) Close

func (sig *Signal) Close() error

Close stops notification any non-nil channel, and nils it out.

func (*Signal) Enter

func (sig *Signal) Enter(term *Term) error

Enter calls Open, ensuring signal notification is started.

func (*Signal) Exit

func (sig *Signal) Exit(term *Term) error

Exit is a no-op; NOTE signal notification is not stopped during temporary teardown (e.g when suspending).

func (*Signal) Open

func (sig *Signal) Open() error

Open allocates a signal channel (of capacity 1) if none has been allocated already, and then calls signal.Notify if sig.Notify is non-empty.

func (*Signal) Send

func (sig *Signal) Send(mess string)

Send a synthetic signal to the channel, e.g. to prime it so that it fires immediately to initialize a run loop.

type SignalError

type SignalError struct{ Sig os.Signal }

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

type Style interface {
	Style(p ansi.Point, pr, r rune, pa, a ansi.SGRAttr) (rune, ansi.SGRAttr)
}

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.

func Styles

func Styles(ss ...Style) Style

Styles combines zero or more styles into a non-nil Style; if given none, it returns a no-op Style; if given many, it returns a Style that calls each in turn.

type StyleFunc

type StyleFunc func(p ansi.Point, pr, r rune, pa, a ansi.SGRAttr) (rune, ansi.SGRAttr)

StyleFunc is a convenience type alias for implementing Style.

func (StyleFunc) Style

func (f StyleFunc) Style(p ansi.Point, pr, r rune, pa, a ansi.SGRAttr) (rune, ansi.SGRAttr)

Style calls the aliased function pointer

type Term

type Term struct {
	Attr
	Mode
	Input
	Output
	// contains filtered or unexported fields
}

Term combines a terminal file handle with attribute control and further Context-ual state.

func NewTerm

func NewTerm(in, out *os.File, cs ...Context) *Term

NewTerm constructs a new terminal attached the given file pair, and with the given context.

func OpenTerm

func OpenTerm() (*Term, error)

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

func (term *Term) AddContext(cs ...Context)

AddContext adds one or more Contexts to the terminal. Panics if called under RunWith.

func (*Term) IsTerminal

func (term *Term) IsTerminal() bool

IsTerminal returns true only if both terminal input and output file handles are both connected to a valid terminal.

func (*Term) RunWith

func (term *Term) RunWith(within func(*Term) error) (err error)

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

func (term *Term) RunWithout(without func(*Term) error) (err error)

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.

func (*Term) Suspend

func (term *Term) Suspend() error

Suspend signals the process to stop, and blocks on its later restart. If the terminal is currently active, this is done under RunWithout to restore prior terminal state.

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) Enter

func (tsc *TermScreen) Enter(term *Term) error

Enter calls SizeToTerm.

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

type VirtualCursor struct {
	Cursor
	Real Cursor
	// contains filtered or unexported fields
}

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).

func (*VirtualCursor) WriteTo

func (c *VirtualCursor) WriteTo(w io.Writer) (n int64, err error)

WriteTo writes all bytes from the internal buffer to the given io.Writer. If that succeeds, then the current Cursor is set to the Real cursor state; otherwise, Cursor and Real are both zeroed.

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.

func (ZeroRuneStyle) Style

func (es ZeroRuneStyle) Style(p ansi.Point, pr, r rune, pa, a ansi.SGRAttr) (rune, ansi.SGRAttr)

Style replaces the passed rune and attr with 0 if the rune equals the receiver.

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

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL