screen

package
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Aug 28, 2026 License: MIT Imports: 2 Imported by: 0

Documentation

Overview

Package screen is a tcell.Screen whose terminal is somewhere else.

It holds cells, turns what was drawn into frames, and accepts the events a terminal would have produced. It does not know how a frame reaches a terminal, and it does not know that the interface drawing on it is a database client — which is what lets all of it be tested without either.

Capabilities are reported from the Caps it was built with rather than guessed. Whatever is holding the real terminal is the only thing that can answer how many colours it has, and a screen that understates that makes the interface degrade itself somewhere no later stage can repair.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Caps

type Caps struct {
	Width, Height int
	Colors        int
	CharacterSet  string
	HasMouse      bool
}

Caps is what the real terminal can do.

It is supplied rather than probed because the terminal is elsewhere. Every field here is a question only the process holding it can answer.

Width and Height are the size at the handshake and nothing keeps them current: SetSize resizes the buffer and leaves these alone, so Size is the authority on how big the screen is once a terminal has been resized.

type Cell

type Cell struct {
	X, Y  int
	Main  rune
	Comb  []rune
	Style Style
	Width int
}

Cell is one position on the screen and what belongs there.

Width travels with it so a wrong one can be seen rather than inferred. The client does not need it to place the next cell — every cell carries its own coordinates.

type Cursor

type Cursor struct {
	X, Y    int
	Visible bool
}

Cursor is where the terminal should put the caret.

type Frame

type Frame struct {
	Cells  []Cell
	Cursor Cursor
}

Frame is what changed since the last one, plus where the caret ended up.

func (Frame) Merge

func (f Frame) Merge(next Frame) Frame

Merge folds next into f so one frame can be delivered where two were waiting.

It is sound because both are differences against the same picture — the one the reader is still showing, having applied neither. For any position the later cell wins; a position only f touched survives, which is the whole difference between merging and discarding. Discarding would leave whatever f was carrying stale on the screen for good.

The cursor is not a position on the screen but a single value, so the newer one is simply correct.

type Screen

type Screen struct {
	// contains filtered or unexported fields
}

Screen implements tcell.Screen against a cell buffer instead of a terminal.

func New

func New(caps Caps) *Screen

New returns a screen sized and capability-reported per caps.

func (*Screen) Attach

func (s *Screen) Attach(sink Sink)

Attach makes sink the destination and repaints everything into it.

The repaint is not politeness: Show clears dirty flags whether or not anyone was listening, so a screen drawn while detached has no record of what changed. Without the full frame, the first one after re-attaching is empty and the terminal keeps showing whatever it had.

func (*Screen) Beep

func (s *Screen) Beep() error

func (*Screen) CanDisplay

func (s *Screen) CanDisplay(r rune, _ bool) bool

CanDisplay answers for the client's character set, because that is where the encoder actually lives. Under UTF-8 everything goes; under anything else only ASCII is safe to promise, and the client's own screen substitutes for the rest.

func (*Screen) ChannelEvents

func (s *Screen) ChannelEvents(ch chan<- tcell.Event, quit <-chan struct{})

func (*Screen) CharacterSet

func (s *Screen) CharacterSet() string

func (*Screen) Clear

func (s *Screen) Clear()

Clear fills with the default style rather than the one SetStyle last set, which is what tcell's own screens do.

func (*Screen) Colors

func (s *Screen) Colors() int

func (*Screen) Detach

func (s *Screen) Detach()

Detach stops emitting. The interface goes on drawing and does not learn that nobody is watching.

It waits on deliver so that a frame emit has already built cannot land in a sink this call has dropped.

func (*Screen) DisableFocus

func (s *Screen) DisableFocus()

func (*Screen) DisableMouse

func (s *Screen) DisableMouse()

func (*Screen) DisablePaste

func (s *Screen) DisablePaste()

func (*Screen) EnableFocus

func (s *Screen) EnableFocus()

func (*Screen) EnableMouse

func (s *Screen) EnableMouse(...tcell.MouseFlags)

EnableMouse and friends record nothing, which makes them a requirement on whatever holds the terminal: it must enable mouse, paste and focus reporting on its own screen before it connects. The interface asking here cannot reach the terminal, so anything the client left off is never sent and nothing later can ask for it again.

func (*Screen) EnablePaste

func (s *Screen) EnablePaste()

func (*Screen) Fill

func (s *Screen) Fill(r rune, style tcell.Style)

func (*Screen) Fini

func (s *Screen) Fini()

Fini releases anything blocked in PollEvent. It is safe to call twice: the interface calls it on the way out, and so does whatever owns the screen.

func (*Screen) Get

func (s *Screen) Get(x, y int) (string, tcell.Style, int)

func (*Screen) GetClipboard

func (s *Screen) GetClipboard()

func (*Screen) GetContent

func (s *Screen) GetContent(x, y int) (rune, []rune, tcell.Style, int)

func (*Screen) HasKey

func (s *Screen) HasKey(tcell.Key) bool

HasKey answers for every key because the screen that decides which keys reach anyone is the real one on the client, and Caps carries no key set to answer from. Refusing here would suppress a key the terminal can send.

func (*Screen) HasMouse

func (s *Screen) HasMouse() bool

func (*Screen) HasPendingEvent

func (s *Screen) HasPendingEvent() bool

func (*Screen) HideCursor

func (s *Screen) HideCursor()

func (*Screen) Init

func (s *Screen) Init() error

func (*Screen) LockRegion

func (s *Screen) LockRegion(int, int, int, int, bool)

LockRegion marks cells the terminal must not touch during a redraw. There is no terminal here to hold off.

func (*Screen) PollEvent

func (s *Screen) PollEvent() tcell.Event

func (*Screen) PostEvent

func (s *Screen) PostEvent(ev tcell.Event) error

func (*Screen) PostEventWait

func (s *Screen) PostEventWait(ev tcell.Event)

PostEventWait is what whatever reads the transport should use: a key that arrived while the interface was busy should wait its turn rather than be refused.

func (*Screen) Put

func (s *Screen) Put(x, y int, str string, style tcell.Style) (string, int)

func (*Screen) PutStr

func (s *Screen) PutStr(x, y int, str string)

PutStr writes with the default style, which is what tcell.Screen promises. Substituting the SetStyle default here would make this screen draw text a real one would not, and the whole design rests on the two being the same screen.

func (*Screen) PutStrStyled

func (s *Screen) PutStrStyled(x, y int, str string, style tcell.Style)

func (*Screen) RegisterRuneFallback

func (s *Screen) RegisterRuneFallback(rune, string)

Rune fallbacks are the client's business: substitution happens where the encoder is, on the real screen. Recording them here would mean two substitution tables that can disagree.

func (*Screen) Resize

func (s *Screen) Resize(int, int, int, int)

Resize is tcell's vestigial window-resize request; no backend implements it.

func (*Screen) Resume

func (s *Screen) Resume() error

func (*Screen) SetCell

func (s *Screen) SetCell(x, y int, style tcell.Style, ch ...rune)

SetCell is tcell's deprecated spelling of Put, kept because the interface still carries it.

func (*Screen) SetClipboard

func (s *Screen) SetClipboard(data []byte)

func (*Screen) SetContent

func (s *Screen) SetContent(x, y int, mainc rune, combc []rune, style tcell.Style)

func (*Screen) SetCursorStyle

func (s *Screen) SetCursorStyle(cs tcell.CursorStyle, cc ...tcell.Color)

func (*Screen) SetSize

func (s *Screen) SetSize(w, h int)

SetSize is what a client resize arrives as.

The order is load-bearing. tview relayouts when it sees the resize event and asks the screen how big it is while doing so; posting the event before the buffer has been resized lays the interface out at the size it just stopped being.

func (*Screen) SetStyle

func (s *Screen) SetStyle(style tcell.Style)

func (*Screen) SetTitle

func (s *Screen) SetTitle(title string)

func (*Screen) Show

func (s *Screen) Show()

Show clears the dirty flags whether or not a sink is listening, because the buffer is tview's and the interface draws on it either way. What that costs is the record of everything drawn while detached, which is why Attach repaints rather than waiting for the next Show.

func (*Screen) ShowCursor

func (s *Screen) ShowCursor(x, y int)

func (*Screen) Size

func (s *Screen) Size() (int, int)

func (*Screen) Suspend

func (s *Screen) Suspend() error

Suspend and Resume put the terminal back in its original mode for a shell. The terminal is in another process, which suspends itself if it wants to.

func (*Screen) Sync

func (s *Screen) Sync()

Sync emits every cell the buffer holds, skipping only the column behind a wide rune as Show does.

It does not go through the buffer's dirty flags. tcell marks a cell dirty by clearing the record of what was last drawn there, which leaves a cell nothing was ever written to indistinguishable from a clean one — so an invalidated blank screen reports nothing to send. A repaint has to mean every cell, because the terminal being repainted for may be showing anything at all.

func (*Screen) Tty

func (s *Screen) Tty() (tcell.Tty, bool)

Tty is the file descriptor a terminal screen was built on. There is none.

func (*Screen) UnregisterRuneFallback

func (s *Screen) UnregisterRuneFallback(rune)

type Sink

type Sink interface {
	Frame(Frame)
	SetTitle(string)
	SetClipboard([]byte)
	// RequestClipboard asks the terminal for its contents. Most terminals
	// refuse, so no reply is the ordinary case.
	RequestClipboard()
	Bell()
}

Sink is where a drawn screen goes.

It is an interface rather than a set of callbacks so that "attached" is one thing to hold and one thing to drop, and so a test can record every kind of output in one place.

No method may block. SetTitle, SetClipboard, GetClipboard and Beep are called from the goroutine that draws the interface, which is also the goroutine reading rows off the database connection; MySQL will not accept another statement until that result set is drained, so a sink that waits on a wedged terminal stalls the statement, its cancellation and the schema browser with it. Frame is called from that same goroutine for an ordinary draw, and from whichever goroutine attached the sink for the repaint that Attach sends. Frames have a bounded path for this in proto.FrameQueue — the other four methods have nothing behind them, and whatever implements them owns that.

An implementation must not call back into the screen — Detach, Show or Sync — from inside Frame. Delivery is serialised behind a single lock that Frame is called under, and Detach, Show and Sync all take that same lock, so a call back in from inside Frame blocks forever. "The write failed, drop the client" is the most natural thing to reach for in a transport, and it has to be done from somewhere other than inside Frame.

type Style

type Style struct {
	Fg, Bg         tcell.Color
	Attrs          tcell.AttrMask
	UnderlineStyle tcell.UnderlineStyle
	UnderlineColor tcell.Color
}

Style is tcell.Style in a form an encoder can carry.

tcell.Style has no exported fields, and encoding/gob refuses a type that has none: "gob: type tcell.Style has no exported fields". A cell carrying one would not travel at all — the encode fails before a byte is written, and the reader waits for a frame that never comes. Nothing catches that at compile time, which is why this type exists and why a test pins the round trip.

The hyperlink a style can carry (OSC 8) is not here: tcell offers Url and UrlId as setters and no getters, so it cannot be read back out. dv writes no hyperlink markup — see the risk note in the spec — and a style that carried one would arrive without it.

func StyleFrom

func StyleFrom(s tcell.Style) Style

StyleFrom flattens a tcell.Style.

func (Style) Tcell

func (s Style) Tcell() tcell.Style

Tcell rebuilds the style for a screen to draw with.

Attributes goes last because Underline sets and clears AttrUnderline as a side effect; applying the recorded mask afterwards restores exactly what was decomposed.

Jump to

Keyboard shortcuts

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