github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/utils/virtualterm/term.go (about)

     1  package virtualterm
     2  
     3  import (
     4  	"github.com/lmorg/murex/debug"
     5  )
     6  
     7  // Term is the display state of the virtual term
     8  type Term struct {
     9  	cells  [][]cell
    10  	size   xy
    11  	curPos xy
    12  	sgr    sgr
    13  	state  PtyState
    14  	//mutex  sync.Mutex
    15  	mutex debug.BadMutex
    16  }
    17  
    18  type cell struct {
    19  	char rune
    20  	sgr  sgr
    21  }
    22  
    23  type sgr struct {
    24  	bitwise sgrFlag
    25  	fg      rgb
    26  	bg      rgb
    27  }
    28  
    29  // PtyState defines some basic emulation states for the virtual TTY
    30  type PtyState struct {
    31  	LfIncCr bool // if false, \n acts as a \r\n
    32  }
    33  
    34  func (c *cell) differs(oldChar rune, oldSgr *sgr) bool {
    35  	if c.sgr.bitwise != oldSgr.bitwise {
    36  		return true
    37  	}
    38  
    39  	if c.char == 0 && oldChar != 0 {
    40  		return true
    41  	}
    42  
    43  	if c.sgr.fg.Red != oldSgr.fg.Red ||
    44  		c.sgr.fg.Green != oldSgr.fg.Green ||
    45  		c.sgr.fg.Blue != oldSgr.fg.Blue {
    46  		return true
    47  	}
    48  
    49  	if c.sgr.bg.Red != oldSgr.bg.Red ||
    50  		c.sgr.bg.Green != oldSgr.bg.Green ||
    51  		c.sgr.bg.Blue != oldSgr.bg.Blue {
    52  		return true
    53  	}
    54  
    55  	return false
    56  }
    57  
    58  func (sgr *sgr) checkFlag(flag sgrFlag) bool {
    59  	return sgr.bitwise&flag != 0
    60  }
    61  
    62  type xy struct {
    63  	X int
    64  	Y int
    65  }
    66  
    67  type rgb struct {
    68  	Red, Green, Blue byte
    69  }
    70  
    71  // NewTerminal creates a new virtual term
    72  func NewTerminal(x, y int) *Term {
    73  	cells := make([][]cell, y, y)
    74  	for i := range cells {
    75  		cells[i] = make([]cell, x, x)
    76  	}
    77  
    78  	return &Term{
    79  		cells: cells,
    80  		size:  xy{x, y},
    81  		state: PtyState{LfIncCr: true},
    82  	}
    83  }
    84  
    85  // GetSize outputs mirror those from terminal and readline packages
    86  func (term *Term) GetSize() (int, int, error) {
    87  	return term.size.X, term.size.Y, nil
    88  }
    89  
    90  // MakeRaw sets the virtual TTY to a raw state
    91  func (term *Term) MakeRaw() PtyState {
    92  	old := term.state
    93  	term.state.LfIncCr = false
    94  	return old
    95  }
    96  
    97  // Restore returns the virtual TTY to a previous state
    98  func (term *Term) Restore(state PtyState) {
    99  	term.state = state
   100  }
   101  
   102  // format
   103  
   104  func (term *Term) sgrReset() {
   105  	term.sgr.bitwise = 0
   106  	term.sgr.fg = rgb{}
   107  	term.sgr.bg = rgb{}
   108  }
   109  
   110  func (term *Term) sgrEffect(flag sgrFlag) {
   111  	term.sgr.bitwise |= flag
   112  }
   113  
   114  func (c *cell) clear() {
   115  	c.char = 0
   116  	c.sgr = sgr{}
   117  }
   118  
   119  // moveCursor functions DON'T effect other contents in the grid
   120  
   121  func (term *Term) moveCursorBackwards(i int) (overflow int) {
   122  	term.curPos.X -= i
   123  	if term.curPos.X < 0 {
   124  		overflow = term.curPos.X * -1
   125  		term.curPos.X = 0
   126  	}
   127  
   128  	return
   129  }
   130  
   131  func (term *Term) moveCursorForwards(i int) (overflow int) {
   132  	term.curPos.X += i
   133  	if term.curPos.X >= term.size.X {
   134  		overflow = term.curPos.X - (term.size.X - 1)
   135  		term.curPos.X = term.size.X - 1
   136  	}
   137  
   138  	return
   139  }
   140  
   141  func (term *Term) moveCursorUpwards(i int) (overflow int) {
   142  	term.curPos.Y -= i
   143  	if term.curPos.Y < 0 {
   144  		overflow = term.curPos.Y * -1
   145  		term.curPos.Y = 0
   146  	}
   147  
   148  	return
   149  }
   150  
   151  func (term *Term) moveCursorDownwards(i int) (overflow int) {
   152  	term.curPos.Y += i
   153  	if term.curPos.Y >= term.size.Y {
   154  		overflow = term.curPos.Y - (term.size.Y - 1)
   155  		term.curPos.Y = term.size.Y - 1
   156  	}
   157  
   158  	return
   159  }
   160  
   161  func (term *Term) cell() *cell { return &term.cells[term.curPos.Y][term.curPos.X] }
   162  
   163  // moveGridPos functions DO effect other contents in the grid
   164  
   165  func (term *Term) moveContentsUp() {
   166  	var i int
   167  	for ; i < term.size.Y-1; i++ {
   168  		term.cells[i] = term.cells[i+1]
   169  	}
   170  	term.cells[i] = make([]cell, term.size.X, term.size.X)
   171  }
   172  
   173  func (term *Term) wrapCursorForwards() {
   174  	term.curPos.X += 1
   175  
   176  	if term.curPos.X >= term.size.X {
   177  		overflow := term.curPos.X - (term.size.X - 1)
   178  		term.curPos.X = 0
   179  
   180  		if overflow > 0 && term.moveCursorDownwards(1) > 0 {
   181  			term.moveContentsUp()
   182  			term.moveCursorDownwards(1)
   183  		}
   184  	}
   185  }
   186  
   187  func (term *Term) eraseDisplayAfter() {
   188  	for y := term.curPos.Y; y < term.size.Y; y++ {
   189  		for x := term.curPos.X; x < term.size.X; x++ {
   190  			term.cells[y][x].clear()
   191  		}
   192  	}
   193  }