...

Source file src/github.com/gdamore/tcell/v2/simulation.go

Documentation: github.com/gdamore/tcell/v2

     1  // Copyright 2022 The TCell Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use file except in compliance with the License.
     5  // You may obtain a copy of the license at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package tcell
    16  
    17  import (
    18  	"sync"
    19  	"unicode/utf8"
    20  
    21  	"golang.org/x/text/transform"
    22  )
    23  
    24  // NewSimulationScreen returns a SimulationScreen.  Note that
    25  // SimulationScreen is also a Screen.
    26  func NewSimulationScreen(charset string) SimulationScreen {
    27  	if charset == "" {
    28  		charset = "UTF-8"
    29  	}
    30  	s := &simscreen{charset: charset}
    31  	return s
    32  }
    33  
    34  // SimulationScreen represents a screen simulation.  This is intended to
    35  // be a superset of normal Screens, but also adds some important interfaces
    36  // for testing.
    37  type SimulationScreen interface {
    38  	// InjectKeyBytes injects a stream of bytes corresponding to
    39  	// the native encoding (see charset).  It turns true if the entire
    40  	// set of bytes were processed and delivered as KeyEvents, false
    41  	// if any bytes were not fully understood.  Any bytes that are not
    42  	// fully converted are discarded.
    43  	InjectKeyBytes(buf []byte) bool
    44  
    45  	// InjectKey injects a key event.  The rune is a UTF-8 rune, post
    46  	// any translation.
    47  	InjectKey(key Key, r rune, mod ModMask)
    48  
    49  	// InjectMouse injects a mouse event.
    50  	InjectMouse(x, y int, buttons ButtonMask, mod ModMask)
    51  
    52  	// GetContents returns screen contents as an array of
    53  	// cells, along with the physical width & height.   Note that the
    54  	// physical contents will be used until the next time SetSize()
    55  	// is called.
    56  	GetContents() (cells []SimCell, width int, height int)
    57  
    58  	// GetCursor returns the cursor details.
    59  	GetCursor() (x int, y int, visible bool)
    60  
    61  	Screen
    62  }
    63  
    64  // SimCell represents a simulated screen cell.  The purpose of this
    65  // is to track on screen content.
    66  type SimCell struct {
    67  	// Bytes is the actual character bytes.  Normally this is
    68  	// rune data, but it could be be data in another encoding system.
    69  	Bytes []byte
    70  
    71  	// Style is the style used to display the data.
    72  	Style Style
    73  
    74  	// Runes is the list of runes, unadulterated, in UTF-8.
    75  	Runes []rune
    76  }
    77  
    78  type simscreen struct {
    79  	physw int
    80  	physh int
    81  	fini  bool
    82  	style Style
    83  	evch  chan Event
    84  	quit  chan struct{}
    85  
    86  	front     []SimCell
    87  	back      CellBuffer
    88  	clear     bool
    89  	cursorx   int
    90  	cursory   int
    91  	cursorvis bool
    92  	mouse     bool
    93  	paste     bool
    94  	charset   string
    95  	encoder   transform.Transformer
    96  	decoder   transform.Transformer
    97  	fillchar  rune
    98  	fillstyle Style
    99  	fallback  map[rune]string
   100  
   101  	sync.Mutex
   102  }
   103  
   104  func (s *simscreen) Init() error {
   105  	s.evch = make(chan Event, 10)
   106  	s.quit = make(chan struct{})
   107  	s.fillchar = 'X'
   108  	s.fillstyle = StyleDefault
   109  	s.mouse = false
   110  	s.physw = 80
   111  	s.physh = 25
   112  	s.cursorx = -1
   113  	s.cursory = -1
   114  	s.style = StyleDefault
   115  
   116  	if enc := GetEncoding(s.charset); enc != nil {
   117  		s.encoder = enc.NewEncoder()
   118  		s.decoder = enc.NewDecoder()
   119  	} else {
   120  		return ErrNoCharset
   121  	}
   122  
   123  	s.front = make([]SimCell, s.physw*s.physh)
   124  	s.back.Resize(80, 25)
   125  
   126  	// default fallbacks
   127  	s.fallback = make(map[rune]string)
   128  	for k, v := range RuneFallbacks {
   129  		s.fallback[k] = v
   130  	}
   131  	return nil
   132  }
   133  
   134  func (s *simscreen) Fini() {
   135  	s.Lock()
   136  	s.fini = true
   137  	s.back.Resize(0, 0)
   138  	s.Unlock()
   139  	if s.quit != nil {
   140  		close(s.quit)
   141  	}
   142  	s.physw = 0
   143  	s.physh = 0
   144  	s.front = nil
   145  }
   146  
   147  func (s *simscreen) SetStyle(style Style) {
   148  	s.Lock()
   149  	s.style = style
   150  	s.Unlock()
   151  }
   152  
   153  func (s *simscreen) Clear() {
   154  	s.Fill(' ', s.style)
   155  }
   156  
   157  func (s *simscreen) Fill(r rune, style Style) {
   158  	s.Lock()
   159  	s.back.Fill(r, style)
   160  	s.Unlock()
   161  }
   162  
   163  func (s *simscreen) SetCell(x, y int, style Style, ch ...rune) {
   164  
   165  	if len(ch) > 0 {
   166  		s.SetContent(x, y, ch[0], ch[1:], style)
   167  	} else {
   168  		s.SetContent(x, y, ' ', nil, style)
   169  	}
   170  }
   171  
   172  func (s *simscreen) SetContent(x, y int, mainc rune, combc []rune, st Style) {
   173  
   174  	s.Lock()
   175  	s.back.SetContent(x, y, mainc, combc, st)
   176  	s.Unlock()
   177  }
   178  
   179  func (s *simscreen) GetContent(x, y int) (rune, []rune, Style, int) {
   180  	var mainc rune
   181  	var combc []rune
   182  	var style Style
   183  	var width int
   184  	s.Lock()
   185  	mainc, combc, style, width = s.back.GetContent(x, y)
   186  	s.Unlock()
   187  	return mainc, combc, style, width
   188  }
   189  
   190  func (s *simscreen) drawCell(x, y int) int {
   191  
   192  	mainc, combc, style, width := s.back.GetContent(x, y)
   193  	if !s.back.Dirty(x, y) {
   194  		return width
   195  	}
   196  	if x >= s.physw || y >= s.physh || x < 0 || y < 0 {
   197  		return width
   198  	}
   199  	simc := &s.front[(y*s.physw)+x]
   200  
   201  	if style == StyleDefault {
   202  		style = s.style
   203  	}
   204  	simc.Style = style
   205  	simc.Runes = append([]rune{mainc}, combc...)
   206  
   207  	// now emit runes - taking care to not overrun width with a
   208  	// wide character, and to ensure that we emit exactly one regular
   209  	// character followed up by any residual combing characters
   210  
   211  	simc.Bytes = nil
   212  
   213  	if x > s.physw-width {
   214  		simc.Runes = []rune{' '}
   215  		simc.Bytes = []byte{' '}
   216  		return width
   217  	}
   218  
   219  	lbuf := make([]byte, 12)
   220  	ubuf := make([]byte, 12)
   221  	nout := 0
   222  
   223  	for _, r := range simc.Runes {
   224  
   225  		l := utf8.EncodeRune(ubuf, r)
   226  
   227  		nout, _, _ = s.encoder.Transform(lbuf, ubuf[:l], true)
   228  
   229  		if nout == 0 || lbuf[0] == '\x1a' {
   230  
   231  			// skip combining
   232  
   233  			if subst, ok := s.fallback[r]; ok {
   234  				simc.Bytes = append(simc.Bytes,
   235  					[]byte(subst)...)
   236  
   237  			} else if r >= ' ' && r <= '~' {
   238  				simc.Bytes = append(simc.Bytes, byte(r))
   239  
   240  			} else if simc.Bytes == nil {
   241  				simc.Bytes = append(simc.Bytes, '?')
   242  			}
   243  		} else {
   244  			simc.Bytes = append(simc.Bytes, lbuf[:nout]...)
   245  		}
   246  	}
   247  	s.back.SetDirty(x, y, false)
   248  	return width
   249  }
   250  
   251  func (s *simscreen) ShowCursor(x, y int) {
   252  	s.Lock()
   253  	s.cursorx, s.cursory = x, y
   254  	s.showCursor()
   255  	s.Unlock()
   256  }
   257  
   258  func (s *simscreen) HideCursor() {
   259  	s.ShowCursor(-1, -1)
   260  }
   261  
   262  func (s *simscreen) showCursor() {
   263  
   264  	x, y := s.cursorx, s.cursory
   265  	if x < 0 || y < 0 || x >= s.physw || y >= s.physh {
   266  		s.cursorvis = false
   267  	} else {
   268  		s.cursorvis = true
   269  	}
   270  }
   271  
   272  func (s *simscreen) hideCursor() {
   273  	// does not update cursor position
   274  	s.cursorvis = false
   275  }
   276  
   277  func (s *simscreen) SetCursorStyle(CursorStyle) {}
   278  
   279  func (s *simscreen) Show() {
   280  	s.Lock()
   281  	s.resize()
   282  	s.draw()
   283  	s.Unlock()
   284  }
   285  
   286  func (s *simscreen) clearScreen() {
   287  	// We emulate a hardware clear by filling with a specific pattern
   288  	for i := range s.front {
   289  		s.front[i].Style = s.fillstyle
   290  		s.front[i].Runes = []rune{s.fillchar}
   291  		s.front[i].Bytes = []byte{byte(s.fillchar)}
   292  	}
   293  	s.clear = false
   294  }
   295  
   296  func (s *simscreen) draw() {
   297  	s.hideCursor()
   298  	if s.clear {
   299  		s.clearScreen()
   300  	}
   301  
   302  	w, h := s.back.Size()
   303  	for y := 0; y < h; y++ {
   304  		for x := 0; x < w; x++ {
   305  			width := s.drawCell(x, y)
   306  			x += width - 1
   307  		}
   308  	}
   309  	s.showCursor()
   310  }
   311  
   312  func (s *simscreen) EnableMouse(...MouseFlags) {
   313  	s.mouse = true
   314  }
   315  
   316  func (s *simscreen) DisableMouse() {
   317  	s.mouse = false
   318  }
   319  
   320  func (s *simscreen) EnablePaste() {
   321  	s.paste = true
   322  }
   323  
   324  func (s *simscreen) DisablePaste() {
   325  	s.paste = false
   326  }
   327  
   328  func (s *simscreen) Size() (int, int) {
   329  	s.Lock()
   330  	w, h := s.back.Size()
   331  	s.Unlock()
   332  	return w, h
   333  }
   334  
   335  func (s *simscreen) resize() {
   336  	w, h := s.physw, s.physh
   337  	ow, oh := s.back.Size()
   338  	if w != ow || h != oh {
   339  		s.back.Resize(w, h)
   340  		ev := NewEventResize(w, h)
   341  		s.PostEvent(ev)
   342  	}
   343  }
   344  
   345  func (s *simscreen) Colors() int {
   346  	return 256
   347  }
   348  
   349  func (s *simscreen) ChannelEvents(ch chan<- Event, quit <-chan struct{}) {
   350  	defer close(ch)
   351  	for {
   352  		select {
   353  		case <-quit:
   354  			return
   355  		case <-s.quit:
   356  			return
   357  		case ev := <-s.evch:
   358  			select {
   359  			case <-quit:
   360  				return
   361  			case <-s.quit:
   362  				return
   363  			case ch <- ev:
   364  			}
   365  		}
   366  	}
   367  }
   368  
   369  func (s *simscreen) PollEvent() Event {
   370  	select {
   371  	case <-s.quit:
   372  		return nil
   373  	case ev := <-s.evch:
   374  		return ev
   375  	}
   376  }
   377  
   378  func (s *simscreen) HasPendingEvent() bool {
   379  	return len(s.evch) > 0
   380  }
   381  
   382  func (s *simscreen) PostEventWait(ev Event) {
   383  	s.evch <- ev
   384  }
   385  
   386  func (s *simscreen) PostEvent(ev Event) error {
   387  	select {
   388  	case s.evch <- ev:
   389  		return nil
   390  	default:
   391  		return ErrEventQFull
   392  	}
   393  }
   394  
   395  func (s *simscreen) InjectMouse(x, y int, buttons ButtonMask, mod ModMask) {
   396  	ev := NewEventMouse(x, y, buttons, mod)
   397  	s.PostEvent(ev)
   398  }
   399  
   400  func (s *simscreen) InjectKey(key Key, r rune, mod ModMask) {
   401  	ev := NewEventKey(key, r, mod)
   402  	s.PostEvent(ev)
   403  }
   404  
   405  func (s *simscreen) InjectKeyBytes(b []byte) bool {
   406  	failed := false
   407  
   408  outer:
   409  	for len(b) > 0 {
   410  		if b[0] >= ' ' && b[0] <= 0x7F {
   411  			// printable ASCII easy to deal with -- no encodings
   412  			ev := NewEventKey(KeyRune, rune(b[0]), ModNone)
   413  			s.PostEvent(ev)
   414  			b = b[1:]
   415  			continue
   416  		}
   417  
   418  		if b[0] < 0x80 {
   419  			mod := ModNone
   420  			// No encodings start with low numbered values
   421  			if Key(b[0]) >= KeyCtrlA && Key(b[0]) <= KeyCtrlZ {
   422  				mod = ModCtrl
   423  			}
   424  			ev := NewEventKey(Key(b[0]), 0, mod)
   425  			s.PostEvent(ev)
   426  			b = b[1:]
   427  			continue
   428  		}
   429  
   430  		utfb := make([]byte, len(b)*4) // worst case
   431  		for l := 1; l < len(b); l++ {
   432  			s.decoder.Reset()
   433  			nout, nin, _ := s.decoder.Transform(utfb, b[:l], true)
   434  
   435  			if nout != 0 {
   436  				r, _ := utf8.DecodeRune(utfb[:nout])
   437  				if r != utf8.RuneError {
   438  					ev := NewEventKey(KeyRune, r, ModNone)
   439  					s.PostEvent(ev)
   440  				}
   441  				b = b[nin:]
   442  				continue outer
   443  			}
   444  		}
   445  		failed = true
   446  		b = b[1:]
   447  		continue
   448  	}
   449  
   450  	return !failed
   451  }
   452  
   453  func (s *simscreen) Sync() {
   454  	s.Lock()
   455  	s.clear = true
   456  	s.resize()
   457  	s.back.Invalidate()
   458  	s.draw()
   459  	s.Unlock()
   460  }
   461  
   462  func (s *simscreen) CharacterSet() string {
   463  	return s.charset
   464  }
   465  
   466  func (s *simscreen) SetSize(w, h int) {
   467  	s.Lock()
   468  	newc := make([]SimCell, w*h)
   469  	for row := 0; row < h && row < s.physh; row++ {
   470  		for col := 0; col < w && col < s.physw; col++ {
   471  			newc[(row*w)+col] = s.front[(row*s.physw)+col]
   472  		}
   473  	}
   474  	s.cursorx, s.cursory = -1, -1
   475  	s.physw, s.physh = w, h
   476  	s.front = newc
   477  	s.back.Resize(w, h)
   478  	s.Unlock()
   479  }
   480  
   481  func (s *simscreen) GetContents() ([]SimCell, int, int) {
   482  	s.Lock()
   483  	cells, w, h := s.front, s.physw, s.physh
   484  	s.Unlock()
   485  	return cells, w, h
   486  }
   487  
   488  func (s *simscreen) GetCursor() (int, int, bool) {
   489  	s.Lock()
   490  	x, y, vis := s.cursorx, s.cursory, s.cursorvis
   491  	s.Unlock()
   492  	return x, y, vis
   493  }
   494  
   495  func (s *simscreen) RegisterRuneFallback(r rune, subst string) {
   496  	s.Lock()
   497  	s.fallback[r] = subst
   498  	s.Unlock()
   499  }
   500  
   501  func (s *simscreen) UnregisterRuneFallback(r rune) {
   502  	s.Lock()
   503  	delete(s.fallback, r)
   504  	s.Unlock()
   505  }
   506  
   507  func (s *simscreen) CanDisplay(r rune, checkFallbacks bool) bool {
   508  
   509  	if enc := s.encoder; enc != nil {
   510  		nb := make([]byte, 6)
   511  		ob := make([]byte, 6)
   512  		num := utf8.EncodeRune(ob, r)
   513  
   514  		enc.Reset()
   515  		dst, _, err := enc.Transform(nb, ob[:num], true)
   516  		if dst != 0 && err == nil && nb[0] != '\x1A' {
   517  			return true
   518  		}
   519  	}
   520  	if !checkFallbacks {
   521  		return false
   522  	}
   523  	if _, ok := s.fallback[r]; ok {
   524  		return true
   525  	}
   526  	return false
   527  }
   528  
   529  func (s *simscreen) HasMouse() bool {
   530  	return false
   531  }
   532  
   533  func (s *simscreen) Resize(int, int, int, int) {}
   534  
   535  func (s *simscreen) HasKey(Key) bool {
   536  	return true
   537  }
   538  
   539  func (s *simscreen) Beep() error {
   540  	return nil
   541  }
   542  
   543  func (s *simscreen) Suspend() error {
   544  	return nil
   545  }
   546  
   547  func (s *simscreen) Resume() error {
   548  	return nil
   549  }
   550  

View as plain text