package tcell
import (
"sync"
"unicode/utf8"
"golang.org/x/text/transform"
)
func NewSimulationScreen (charset string ) SimulationScreen {
if charset == "" {
charset = "UTF-8"
}
ss := &simscreen {charset : charset }
ss .Screen = &baseScreen {screenImpl : ss }
return ss
}
type SimulationScreen interface {
Screen
InjectKeyBytes (buf []byte ) bool
InjectKey (key Key , r rune , mod ModMask )
InjectMouse (x, y int , buttons ButtonMask , mod ModMask )
GetContents () (cells []SimCell , width int , height int )
GetCursor () (x int , y int , visible bool )
GetTitle () string
GetClipboardData () []byte
}
type SimCell struct {
Bytes []byte
Style Style
Runes []rune
}
type simscreen struct {
physw int
physh int
fini bool
style Style
evch chan Event
quit chan struct {}
front []SimCell
back CellBuffer
clear bool
cursorx int
cursory int
cursorvis bool
mouse bool
paste bool
charset string
encoder transform .Transformer
decoder transform .Transformer
fillchar rune
fillstyle Style
fallback map [rune ]string
title string
clipboard []byte
Screen
sync .Mutex
}
func (s *simscreen ) Init () error {
s .evch = make (chan Event , 10 )
s .quit = make (chan struct {})
s .fillchar = 'X'
s .fillstyle = StyleDefault
s .mouse = false
s .physw = 80
s .physh = 25
s .cursorx = -1
s .cursory = -1
s .style = StyleDefault
if enc := GetEncoding (s .charset ); enc != nil {
s .encoder = enc .NewEncoder ()
s .decoder = enc .NewDecoder ()
} else {
return ErrNoCharset
}
s .front = make ([]SimCell , s .physw *s .physh )
s .back .Resize (80 , 25 )
s .fallback = make (map [rune ]string )
for k , v := range RuneFallbacks {
s .fallback [k ] = v
}
return nil
}
func (s *simscreen ) Fini () {
s .Lock ()
s .fini = true
s .back .Resize (0 , 0 )
s .Unlock ()
if s .quit != nil {
close (s .quit )
}
s .physw = 0
s .physh = 0
s .front = nil
}
func (s *simscreen ) SetStyle (style Style ) {
s .Lock ()
s .style = style
s .Unlock ()
}
func (s *simscreen ) drawCell (x , y int ) int {
mainc , combc , style , width := s .back .GetContent (x , y )
if !s .back .Dirty (x , y ) {
return width
}
if x >= s .physw || y >= s .physh || x < 0 || y < 0 {
return width
}
simc := &s .front [(y *s .physw )+x ]
if style == StyleDefault {
style = s .style
}
simc .Style = style
simc .Runes = append ([]rune {mainc }, combc ...)
simc .Bytes = nil
if x > s .physw -width {
simc .Runes = []rune {' ' }
simc .Bytes = []byte {' ' }
return width
}
lbuf := make ([]byte , 12 )
ubuf := make ([]byte , 12 )
nout := 0
for _ , r := range simc .Runes {
l := utf8 .EncodeRune (ubuf , r )
nout , _, _ = s .encoder .Transform (lbuf , ubuf [:l ], true )
if nout == 0 || lbuf [0 ] == '\x1a' {
if subst , ok := s .fallback [r ]; ok {
simc .Bytes = append (simc .Bytes ,
[]byte (subst )...)
} else if r >= ' ' && r <= '~' {
simc .Bytes = append (simc .Bytes , byte (r ))
} else if simc .Bytes == nil {
simc .Bytes = append (simc .Bytes , '?' )
}
} else {
simc .Bytes = append (simc .Bytes , lbuf [:nout ]...)
}
}
s .back .SetDirty (x , y , false )
return width
}
func (s *simscreen ) ShowCursor (x , y int ) {
s .Lock ()
s .cursorx , s .cursory = x , y
s .showCursor ()
s .Unlock ()
}
func (s *simscreen ) HideCursor () {
s .ShowCursor (-1 , -1 )
}
func (s *simscreen ) showCursor () {
x , y := s .cursorx , s .cursory
if x < 0 || y < 0 || x >= s .physw || y >= s .physh {
s .cursorvis = false
} else {
s .cursorvis = true
}
}
func (s *simscreen ) hideCursor () {
s .cursorvis = false
}
func (s *simscreen ) SetCursor (CursorStyle , Color ) {}
func (s *simscreen ) Show () {
s .Lock ()
s .resize ()
s .draw ()
s .Unlock ()
}
func (s *simscreen ) clearScreen () {
for i := range s .front {
s .front [i ].Style = s .fillstyle
s .front [i ].Runes = []rune {s .fillchar }
s .front [i ].Bytes = []byte {byte (s .fillchar )}
}
s .clear = false
}
func (s *simscreen ) draw () {
s .hideCursor ()
if s .clear {
s .clearScreen ()
}
w , h := s .back .Size ()
for y := 0 ; y < h ; y ++ {
for x := 0 ; x < w ; x ++ {
width := s .drawCell (x , y )
x += width - 1
}
}
s .showCursor ()
}
func (s *simscreen ) EnableMouse (...MouseFlags ) {
s .mouse = true
}
func (s *simscreen ) DisableMouse () {
s .mouse = false
}
func (s *simscreen ) EnablePaste () {
s .paste = true
}
func (s *simscreen ) DisablePaste () {
s .paste = false
}
func (s *simscreen ) EnableFocus () {
}
func (s *simscreen ) DisableFocus () {
}
func (s *simscreen ) Size () (int , int ) {
s .Lock ()
w , h := s .back .Size ()
s .Unlock ()
return w , h
}
func (s *simscreen ) resize () {
w , h := s .physw , s .physh
ow , oh := s .back .Size ()
if w != ow || h != oh {
s .back .Resize (w , h )
ev := NewEventResize (w , h )
s .postEvent (ev )
}
}
func (s *simscreen ) Colors () int {
return 256
}
func (s *simscreen ) postEvent (ev Event ) {
select {
case s .evch <- ev :
case <- s .quit :
}
}
func (s *simscreen ) InjectMouse (x , y int , buttons ButtonMask , mod ModMask ) {
ev := NewEventMouse (x , y , buttons , mod )
s .postEvent (ev )
}
func (s *simscreen ) InjectKey (key Key , r rune , mod ModMask ) {
ev := NewEventKey (key , r , mod )
s .postEvent (ev )
}
func (s *simscreen ) InjectKeyBytes (b []byte ) bool {
failed := false
outer :
for len (b ) > 0 {
if b [0 ] >= ' ' && b [0 ] <= 0x7F {
ev := NewEventKey (KeyRune , rune (b [0 ]), ModNone )
s .postEvent (ev )
b = b [1 :]
continue
}
if b [0 ] < 0x80 {
mod := ModNone
if Key (b [0 ]) >= KeyCtrlA && Key (b [0 ]) <= KeyCtrlZ {
mod = ModCtrl
}
ev := NewEventKey (Key (b [0 ]), 0 , mod )
s .postEvent (ev )
b = b [1 :]
continue
}
utfb := make ([]byte , len (b )*4 )
for l := 1 ; l < len (b ); l ++ {
s .decoder .Reset ()
nout , nin , _ := s .decoder .Transform (utfb , b [:l ], true )
if nout != 0 {
r , _ := utf8 .DecodeRune (utfb [:nout ])
if r != utf8 .RuneError {
ev := NewEventKey (KeyRune , r , ModNone )
s .postEvent (ev )
}
b = b [nin :]
continue outer
}
}
failed = true
b = b [1 :]
continue
}
return !failed
}
func (s *simscreen ) Sync () {
s .Lock ()
s .clear = true
s .resize ()
s .back .Invalidate ()
s .draw ()
s .Unlock ()
}
func (s *simscreen ) CharacterSet () string {
return s .charset
}
func (s *simscreen ) SetSize (w , h int ) {
s .Lock ()
newc := make ([]SimCell , w *h )
for row := 0 ; row < h && row < s .physh ; row ++ {
for col := 0 ; col < w && col < s .physw ; col ++ {
newc [(row *w )+col ] = s .front [(row *s .physw )+col ]
}
}
s .cursorx , s .cursory = -1 , -1
s .physw , s .physh = w , h
s .front = newc
s .back .Resize (w , h )
s .Unlock ()
}
func (s *simscreen ) GetContents () ([]SimCell , int , int ) {
s .Lock ()
cells , w , h := s .front , s .physw , s .physh
s .Unlock ()
return cells , w , h
}
func (s *simscreen ) GetCursor () (int , int , bool ) {
s .Lock ()
x , y , vis := s .cursorx , s .cursory , s .cursorvis
s .Unlock ()
return x , y , vis
}
func (s *simscreen ) RegisterRuneFallback (r rune , subst string ) {
s .Lock ()
s .fallback [r ] = subst
s .Unlock ()
}
func (s *simscreen ) UnregisterRuneFallback (r rune ) {
s .Lock ()
delete (s .fallback , r )
s .Unlock ()
}
func (s *simscreen ) CanDisplay (r rune , checkFallbacks bool ) bool {
if enc := s .encoder ; enc != nil {
nb := make ([]byte , 6 )
ob := make ([]byte , 6 )
num := utf8 .EncodeRune (ob , r )
enc .Reset ()
dst , _ , err := enc .Transform (nb , ob [:num ], true )
if dst != 0 && err == nil && nb [0 ] != '\x1A' {
return true
}
}
if !checkFallbacks {
return false
}
if _ , ok := s .fallback [r ]; ok {
return true
}
return false
}
func (s *simscreen ) HasMouse () bool {
return false
}
func (s *simscreen ) Resize (int , int , int , int ) {}
func (s *simscreen ) HasKey (Key ) bool {
return true
}
func (s *simscreen ) Beep () error {
return nil
}
func (s *simscreen ) Suspend () error {
return nil
}
func (s *simscreen ) Resume () error {
return nil
}
func (s *simscreen ) Tty () (Tty , bool ) {
return nil , false
}
func (s *simscreen ) GetCells () *CellBuffer {
return &s .back
}
func (s *simscreen ) EventQ () chan Event {
return s .evch
}
func (s *simscreen ) StopQ () <-chan struct {} {
return s .quit
}
func (s *simscreen ) SetTitle (title string ) {
s .title = title
}
func (s *simscreen ) GetTitle () string {
return s .title
}
func (s *simscreen ) SetClipboard (data []byte ) {
s .clipboard = data
}
func (s *simscreen ) GetClipboard () {
if s .clipboard != nil {
ev := NewEventClipboard (s .clipboard )
s .postEvent (ev )
}
}
func (s *simscreen ) GetClipboardData () []byte {
return s .clipboard
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .