* added Tab Stops support

* added support for Screen Mode (DECSCNM) -- reverse colors

* bug fix: cursor rendition in Origin Mode
This commit is contained in:
rrrooommmaaa 2019-01-30 18:42:55 +03:00 committed by Liam Galvin
parent 97fe7362ce
commit 23797d50f3
10 changed files with 150 additions and 71 deletions

View File

@ -361,7 +361,7 @@ func (buffer *Buffer) RestoreCursor() {
} }
func (buffer *Buffer) CursorAttr() *CellAttributes { func (buffer *Buffer) CursorAttr() *CellAttributes {
return &buffer.terminalState.cursorAttr return &buffer.terminalState.CursorAttr
} }
func (buffer *Buffer) GetCell(viewCol uint16, viewRow uint16) *Cell { func (buffer *Buffer) GetCell(viewCol uint16, viewRow uint16) *Cell {
@ -391,7 +391,12 @@ func (buffer *Buffer) CursorColumn() uint16 {
return buffer.terminalState.cursorX return buffer.terminalState.cursorX
} }
// Line returns cursor line // CursorLineAbsolute returns absolute cursor line coordinate (ignoring Origin Mode)
func (buffer *Buffer) CursorLineAbsolute() uint16 {
return buffer.terminalState.cursorY
}
// CursorLine returns cursor line (in Origin Mode it is relative to the top margin)
func (buffer *Buffer) CursorLine() uint16 { func (buffer *Buffer) CursorLine() uint16 {
if buffer.terminalState.OriginMode { if buffer.terminalState.OriginMode {
result := buffer.terminalState.cursorY - uint16(buffer.terminalState.topMargin) result := buffer.terminalState.cursorY - uint16(buffer.terminalState.topMargin)
@ -502,7 +507,7 @@ func (buffer *Buffer) InsertBlankCharacters(count int) {
index := int(buffer.RawLine()) index := int(buffer.RawLine())
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
cells := buffer.lines[index].cells cells := buffer.lines[index].cells
buffer.lines[index].cells = append(cells[:buffer.terminalState.cursorX], append([]Cell{buffer.terminalState.defaultCell}, cells[buffer.terminalState.cursorX:]...)...) buffer.lines[index].cells = append(cells[:buffer.terminalState.cursorX], append([]Cell{buffer.terminalState.DefaultCell(true)}, cells[buffer.terminalState.cursorX:]...)...)
} }
} }
@ -619,9 +624,9 @@ func (buffer *Buffer) Write(runes ...rune) {
} }
for int(buffer.CursorColumn()) >= len(line.cells) { for int(buffer.CursorColumn()) >= len(line.cells) {
line.cells = append(line.cells, buffer.terminalState.defaultCell) line.cells = append(line.cells, buffer.terminalState.DefaultCell(int(buffer.CursorColumn()) == len(line.cells)))
} }
line.cells[buffer.terminalState.cursorX].attr = buffer.terminalState.cursorAttr line.cells[buffer.terminalState.cursorX].attr = buffer.terminalState.CursorAttr
line.cells[buffer.terminalState.cursorX].setRune(r) line.cells[buffer.terminalState.cursorX].setRune(r)
buffer.incrementCursorPosition() buffer.incrementCursorPosition()
continue continue
@ -635,11 +640,11 @@ func (buffer *Buffer) Write(runes ...rune) {
newLine := buffer.getCurrentLine() newLine := buffer.getCurrentLine()
if len(newLine.cells) == 0 { if len(newLine.cells) == 0 {
newLine.cells = append(newLine.cells, buffer.terminalState.defaultCell) newLine.cells = append(newLine.cells, buffer.terminalState.DefaultCell(true))
} }
cell := &newLine.cells[0] cell := &newLine.cells[0]
cell.setRune(r) cell.setRune(r)
cell.attr = buffer.terminalState.cursorAttr cell.attr = buffer.terminalState.CursorAttr
} else { } else {
// no more room on line and wrapping is disabled // no more room on line and wrapping is disabled
@ -650,12 +655,12 @@ func (buffer *Buffer) Write(runes ...rune) {
} else { } else {
for int(buffer.CursorColumn()) >= len(line.cells) { for int(buffer.CursorColumn()) >= len(line.cells) {
line.cells = append(line.cells, buffer.terminalState.defaultCell) line.cells = append(line.cells, buffer.terminalState.DefaultCell(int(buffer.CursorColumn()) == len(line.cells)))
} }
cell := &line.cells[buffer.CursorColumn()] cell := &line.cells[buffer.CursorColumn()]
cell.setRune(r) cell.setRune(r)
cell.attr = buffer.terminalState.cursorAttr cell.attr = buffer.terminalState.CursorAttr
} }
buffer.incrementCursorPosition() buffer.incrementCursorPosition()
@ -848,7 +853,7 @@ func (buffer *Buffer) EraseLineToCursor() {
line := buffer.getCurrentLine() line := buffer.getCurrentLine()
for i := 0; i <= int(buffer.terminalState.cursorX); i++ { for i := 0; i <= int(buffer.terminalState.cursorX); i++ {
if i < len(line.cells) { if i < len(line.cells) {
line.cells[i].erase(buffer.terminalState.defaultCell.attr.BgColour) line.cells[i].erase(buffer.terminalState.CursorAttr.BgColour)
} }
} }
} }
@ -909,7 +914,7 @@ func (buffer *Buffer) EraseCharacters(n int) {
} }
for i := int(buffer.terminalState.cursorX); i < max; i++ { for i := int(buffer.terminalState.cursorX); i < max; i++ {
line.cells[i].erase(buffer.terminalState.defaultCell.attr.BgColour) line.cells[i].erase(buffer.terminalState.CursorAttr.BgColour)
} }
} }
@ -939,7 +944,7 @@ func (buffer *Buffer) EraseDisplayToCursor() {
if i >= len(line.cells) { if i >= len(line.cells) {
break break
} }
line.cells[i].erase(buffer.terminalState.defaultCell.attr.BgColour) line.cells[i].erase(buffer.terminalState.CursorAttr.BgColour)
} }
for i := uint16(0); i < buffer.terminalState.cursorY; i++ { for i := uint16(0); i < buffer.terminalState.cursorY; i++ {
rawLine := buffer.convertViewLineToRawLine(i) rawLine := buffer.convertViewLineToRawLine(i)
@ -1086,3 +1091,11 @@ func (buffer *Buffer) Compare(path string) bool {
} }
return bytes.Equal(f, bufferContent) return bytes.Equal(f, bufferContent)
} }
func (buffer *Buffer) ReverseVideo() {
defer buffer.emitDisplayChange()
for _, line := range buffer.lines {
line.ReverseVideo()
}
}

View File

@ -393,7 +393,7 @@ func TestGetCellWithBadCursor(t *testing.T) {
func TestCursorAttr(t *testing.T) { func TestCursorAttr(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 2, CellAttributes{}, 1000)) b := NewBuffer(NewTerminalState(80, 2, CellAttributes{}, 1000))
assert.Equal(t, &b.terminalState.cursorAttr, b.CursorAttr()) assert.Equal(t, &b.terminalState.CursorAttr, b.CursorAttr())
} }
func TestCursorPositionQuerying(t *testing.T) { func TestCursorPositionQuerying(t *testing.T) {

View File

@ -17,7 +17,7 @@ type CellAttributes struct {
Dim bool Dim bool
Underline bool Underline bool
Blink bool Blink bool
Reverse bool Inverse bool
Hidden bool Hidden bool
} }
@ -40,20 +40,20 @@ func (cell *Cell) Rune() rune {
} }
func (cell *Cell) Fg() [3]float32 { func (cell *Cell) Fg() [3]float32 {
if cell.Attr().Reverse { if cell.Attr().Inverse {
return cell.attr.BgColour return cell.attr.BgColour
} }
return cell.attr.FgColour return cell.attr.FgColour
} }
func (cell *Cell) Bg() [3]float32 { func (cell *Cell) Bg() [3]float32 {
if cell.Attr().Reverse { if cell.Attr().Inverse {
return cell.attr.FgColour return cell.attr.FgColour
} }
return cell.attr.BgColour return cell.attr.BgColour
} }
func (cell *Cell) erase(bgColour [3]float32) { func (cell *Cell) erase(bgColour [3]float32) {
cell.setRune(0) cell.setRune(0)
cell.attr.BgColour = bgColour cell.attr.BgColour = bgColour
} }
@ -69,3 +69,9 @@ func NewBackgroundCell(colour [3]float32) Cell {
}, },
} }
} }
func (cellAttr *CellAttributes) ReverseVideo() {
oldFgColour := cellAttr.FgColour
cellAttr.FgColour = cellAttr.BgColour
cellAttr.BgColour = oldFgColour
}

View File

@ -20,6 +20,12 @@ func (line *Line) Cells() []Cell {
return line.cells return line.cells
} }
func (line *Line) ReverseVideo() {
for i, _ := range line.cells {
line.cells[i].attr.ReverseVideo()
}
}
// Cleanse removes null bytes from the end of the row // Cleanse removes null bytes from the end of the row
func (line *Line) Cleanse() { func (line *Line) Cleanse() {
cut := 0 cut := 0

View File

@ -4,8 +4,7 @@ type TerminalState struct {
scrollLinesFromBottom uint scrollLinesFromBottom uint
cursorX uint16 cursorX uint16
cursorY uint16 cursorY uint16
cursorAttr CellAttributes CursorAttr CellAttributes
defaultCell Cell
viewHeight uint16 viewHeight uint16
viewWidth uint16 viewWidth uint16
topMargin uint // see DECSTBM docs - this is for scrollable regions topMargin uint // see DECSTBM docs - this is for scrollable regions
@ -13,6 +12,7 @@ type TerminalState struct {
ReplaceMode bool // overwrite character at cursor or insert new ReplaceMode bool // overwrite character at cursor or insert new
OriginMode bool // see DECOM docs - whether cursor is positioned within the margins or not OriginMode bool // see DECOM docs - whether cursor is positioned within the margins or not
LineFeedMode bool LineFeedMode bool
ScreenMode bool // DECSCNM (black on white background)
AutoWrap bool AutoWrap bool
maxLines uint64 maxLines uint64
tabStops map[uint16]struct{} tabStops map[uint16]struct{}
@ -23,9 +23,8 @@ func NewTerminalState(viewCols uint16, viewLines uint16, attr CellAttributes, ma
b := &TerminalState{ b := &TerminalState{
cursorX: 0, cursorX: 0,
cursorY: 0, cursorY: 0,
cursorAttr: attr, CursorAttr: attr,
AutoWrap: true, AutoWrap: true,
defaultCell: Cell{attr: attr},
maxLines: maxLines, maxLines: maxLines,
viewWidth: viewCols, viewWidth: viewCols,
viewHeight: viewLines, viewHeight: viewLines,
@ -36,6 +35,19 @@ func NewTerminalState(viewCols uint16, viewLines uint16, attr CellAttributes, ma
return b return b
} }
func (terminalState *TerminalState) DefaultCell(applyEffects bool) Cell {
attr := terminalState.CursorAttr
if !applyEffects {
attr.Blink = false
attr.Bold = false
attr.Dim = false
attr.Inverse = false
attr.Underline = false
attr.Dim = false
}
return Cell{attr: attr}
}
func (terminalState *TerminalState) SetVerticalMargins(top uint, bottom uint) { func (terminalState *TerminalState) SetVerticalMargins(top uint, bottom uint) {
terminalState.topMargin = top terminalState.topMargin = top
terminalState.bottomMargin = bottom terminalState.bottomMargin = bottom

View File

@ -48,6 +48,7 @@ type GUI struct {
resizeLock *sync.Mutex resizeLock *sync.Mutex
handCursor *glfw.Cursor handCursor *glfw.Cursor
arrowCursor *glfw.Cursor arrowCursor *glfw.Cursor
defaultCell *buffer.Cell
prevLeftClickX uint16 prevLeftClickX uint16
prevLeftClickY uint16 prevLeftClickY uint16
@ -185,6 +186,22 @@ func (gui *GUI) resizeToTerminal(newCols uint, newRows uint) {
gui.window.SetSize(roundedWidth, roundedHeight) // will trigger resize() gui.window.SetSize(roundedWidth, roundedHeight) // will trigger resize()
} }
func (gui *GUI) generateDefaultCell(reverse bool) {
color := gui.config.ColourScheme.Background
if reverse {
color = gui.config.ColourScheme.Foreground
}
cell := buffer.NewBackgroundCell(color)
gui.renderer.backgroundColour = color
gui.defaultCell = &cell
gl.ClearColor(
color[0],
color[1],
color[2],
1.0,
)
}
// can only be called on OS thread // can only be called on OS thread
func (gui *GUI) resize(w *glfw.Window, width int, height int) { func (gui *GUI) resize(w *glfw.Window, width int, height int) {
@ -232,7 +249,7 @@ func (gui *GUI) resize(w *glfw.Window, width int, height int) {
gui.logger.Debugf("Resize complete!") gui.logger.Debugf("Resize complete!")
gui.redraw(buffer.NewBackgroundCell(gui.config.ColourScheme.Background)) gui.redraw()
gui.window.SwapBuffers() gui.window.SwapBuffers()
} }
@ -279,6 +296,7 @@ func (gui *GUI) Render() error {
titleChan := make(chan bool, 1) titleChan := make(chan bool, 1)
resizeChan := make(chan bool, 1) resizeChan := make(chan bool, 1)
reverseChan := make(chan bool, 1)
gui.renderer = NewOpenGLRenderer(gui.config, gui.fontMap, 0, 0, gui.width, gui.height, gui.colourAttr, program) gui.renderer = NewOpenGLRenderer(gui.config, gui.fontMap, 0, 0, gui.width, gui.height, gui.colourAttr, program)
@ -297,6 +315,8 @@ func (gui *GUI) Render() error {
} }
}) })
gui.generateDefaultCell(false)
{ {
w, h := gui.window.GetFramebufferSize() w, h := gui.window.GetFramebufferSize()
gui.resize(gui.window, w, h) gui.resize(gui.window, w, h)
@ -320,21 +340,13 @@ func (gui *GUI) Render() error {
gl.Disable(gl.DEPTH_TEST) gl.Disable(gl.DEPTH_TEST)
gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
gl.ClearColor(
gui.config.ColourScheme.Background[0],
gui.config.ColourScheme.Background[1],
gui.config.ColourScheme.Background[2],
1.0,
)
gui.terminal.AttachTitleChangeHandler(titleChan) gui.terminal.AttachTitleChangeHandler(titleChan)
gui.terminal.AttachResizeHandler(resizeChan) gui.terminal.AttachResizeHandler(resizeChan)
gui.terminal.AttachReverseHandler(reverseChan)
ticker := time.NewTicker(time.Second) ticker := time.NewTicker(time.Second)
defer ticker.Stop() defer ticker.Stop()
defaultCell := buffer.NewBackgroundCell(gui.config.ColourScheme.Background)
go func() { go func() {
for { for {
<-ticker.C <-ticker.C
@ -359,20 +371,25 @@ func (gui *GUI) Render() error {
for !gui.window.ShouldClose() { for !gui.window.ShouldClose() {
forceRedraw := false
select { select {
case <-titleChan: case <-titleChan:
gui.window.SetTitle(gui.terminal.GetTitle()) gui.window.SetTitle(gui.terminal.GetTitle())
case <-resizeChan: case <-resizeChan:
cols, rows := gui.terminal.GetSize() cols, rows := gui.terminal.GetSize()
gui.resizeToTerminal(uint(cols), uint(rows)) gui.resizeToTerminal(uint(cols), uint(rows))
case reverse := <-reverseChan:
gui.generateDefaultCell(reverse)
forceRedraw = true
default: default:
// this is more efficient than glfw.PollEvents() // this is more efficient than glfw.PollEvents()
glfw.WaitEventsTimeout(0.02) // up to 50fps on no input, otherwise higher glfw.WaitEventsTimeout(0.02) // up to 50fps on no input, otherwise higher
} }
if gui.terminal.CheckDirty() { if gui.terminal.CheckDirty() || forceRedraw {
gui.redraw(defaultCell) gui.redraw()
if gui.showDebugInfo { if gui.showDebugInfo {
gui.textbox(2, 2, fmt.Sprintf(`Cursor: %d,%d gui.textbox(2, 2, fmt.Sprintf(`Cursor: %d,%d
@ -422,7 +439,7 @@ Buffer Size: %d lines
} }
func (gui *GUI) redraw(defaultCell buffer.Cell) { func (gui *GUI) redraw() {
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT) gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT)
lines := gui.terminal.GetVisibleLines() lines := gui.terminal.GetVisibleLines()
lineCount := int(gui.terminal.ActiveBuffer().ViewHeight()) lineCount := int(gui.terminal.ActiveBuffer().ViewHeight())
@ -446,18 +463,18 @@ func (gui *GUI) redraw(defaultCell buffer.Cell) {
colour = nil colour = nil
} }
cell := defaultCell cell := gui.defaultCell
if colour != nil || cursor || x < len(cells) { if colour != nil || cursor || x < len(cells) {
if x < len(cells) { if x < len(cells) {
cell = cells[x] cell = &cells[x]
if cell.Image() != nil { if cell.Image() != nil {
gui.renderer.DrawCellImage(cell, uint(x), uint(y)) gui.renderer.DrawCellImage(*cell, uint(x), uint(y))
continue continue
} }
} }
gui.renderer.DrawCellBg(cell, uint(x), uint(y), cursor, colour, false) gui.renderer.DrawCellBg(*cell, uint(x), uint(y), cursor, colour, false)
} }
} }

View File

@ -11,20 +11,21 @@ import (
) )
type OpenGLRenderer struct { type OpenGLRenderer struct {
areaWidth int areaWidth int
areaHeight int areaHeight int
areaX int areaX int
areaY int areaY int
cellWidth float32 cellWidth float32
cellHeight float32 cellHeight float32
termCols uint termCols uint
termRows uint termRows uint
cellPositions map[[2]uint][2]float32 cellPositions map[[2]uint][2]float32
config *config.Config config *config.Config
colourAttr uint32 colourAttr uint32
program uint32 program uint32
textureMap map[*image.RGBA]uint32 textureMap map[*image.RGBA]uint32
fontMap *FontMap fontMap *FontMap
backgroundColour [3]float32
} }
type rectangle struct { type rectangle struct {
@ -185,7 +186,7 @@ func (r *OpenGLRenderer) GetRectangleSize(col uint, row uint) (float32, float32)
func (r *OpenGLRenderer) getRectangle(col uint, row uint) *rectangle { func (r *OpenGLRenderer) getRectangle(col uint, row uint) *rectangle {
x := float32(float32(col) * r.cellWidth) x := float32(float32(col) * r.cellWidth)
y := float32(float32(row) * r.cellHeight) + r.cellHeight y := float32(float32(row)*r.cellHeight) + r.cellHeight
return r.newRectangle(x, y, r.colourAttr) return r.newRectangle(x, y, r.colourAttr)
} }
@ -207,13 +208,17 @@ func (r *OpenGLRenderer) DrawCellBg(cell buffer.Cell, col uint, row uint, cursor
} else { } else {
if cursor { if cursor {
bg = r.config.ColourScheme.Cursor if r.config.ColourScheme.Cursor != r.backgroundColour {
bg = r.config.ColourScheme.Cursor
} else {
bg = cell.Fg()
}
} else { } else {
bg = cell.Bg() bg = cell.Bg()
} }
} }
if bg != r.config.ColourScheme.Background || force { if bg != r.backgroundColour || force {
rect := r.getRectangle(col, row) rect := r.getRectangle(col, row)
rect.setColour(bg) rect.setColour(bg)
rect.Draw() rect.Draw()

View File

@ -47,18 +47,13 @@ func csiSetMode(modeStr string, enabled bool, terminal *Terminal) error {
terminal.SetSize(80, uint(lines)) terminal.SetSize(80, uint(lines))
} }
terminal.Clear() terminal.Clear()
/* /*
case "?4": case "?4":
// DECSCLM // DECSCLM
// @todo smooth scrolling / jump scrolling // @todo smooth scrolling / jump scrolling
case "?5": */
// DECSCNM case "?5": // DECSCNM
if enabled { terminal.SetScreenMode(enabled)
// @todo SCreeN Mode, black on white background
} else {
// @todo Normal screen (white on black background)
}
*/
case "?6": case "?6":
// DECOM // DECOM
terminal.SetOriginMode(enabled) terminal.SetOriginMode(enabled)

View File

@ -35,7 +35,7 @@ func sgrSequenceHandler(params []string, terminal *Terminal) error {
case "5", "05": case "5", "05":
terminal.ActiveBuffer().CursorAttr().Blink = true terminal.ActiveBuffer().CursorAttr().Blink = true
case "7", "07": case "7", "07":
terminal.ActiveBuffer().CursorAttr().Reverse = true terminal.ActiveBuffer().CursorAttr().Inverse = true
case "8", "08": case "8", "08":
terminal.ActiveBuffer().CursorAttr().Hidden = true terminal.ActiveBuffer().CursorAttr().Hidden = true
case "21": case "21":
@ -49,7 +49,7 @@ func sgrSequenceHandler(params []string, terminal *Terminal) error {
case "25": case "25":
terminal.ActiveBuffer().CursorAttr().Blink = false terminal.ActiveBuffer().CursorAttr().Blink = false
case "27": case "27":
terminal.ActiveBuffer().CursorAttr().Reverse = false terminal.ActiveBuffer().CursorAttr().Inverse = false
case "28": case "28":
terminal.ActiveBuffer().CursorAttr().Hidden = false terminal.ActiveBuffer().CursorAttr().Hidden = false
case "29": case "29":

View File

@ -41,6 +41,7 @@ type Terminal struct {
config *config.Config config *config.Config
titleHandlers []chan bool titleHandlers []chan bool
resizeHandlers []chan bool resizeHandlers []chan bool
reverseHandlers []chan bool
modes Modes modes Modes
mouseMode MouseMode mouseMode MouseMode
bracketedPasteMode bool bracketedPasteMode bool
@ -196,6 +197,10 @@ func (terminal *Terminal) AttachResizeHandler(handler chan bool) {
terminal.resizeHandlers = append(terminal.resizeHandlers, handler) terminal.resizeHandlers = append(terminal.resizeHandlers, handler)
} }
func (terminal *Terminal) AttachReverseHandler(handler chan bool) {
terminal.reverseHandlers = append(terminal.reverseHandlers, handler)
}
func (terminal *Terminal) Modes() Modes { func (terminal *Terminal) Modes() Modes {
return terminal.modes return terminal.modes
} }
@ -216,6 +221,14 @@ func (terminal *Terminal) emitResize() {
} }
} }
func (terminal *Terminal) emitReverse(reverse bool) {
for _, h := range terminal.reverseHandlers {
go func(c chan bool) {
c <- reverse
}(h)
}
}
func (terminal *Terminal) GetLogicalCursorX() uint16 { func (terminal *Terminal) GetLogicalCursorX() uint16 {
if terminal.ActiveBuffer().CursorColumn() >= terminal.ActiveBuffer().Width() { if terminal.ActiveBuffer().CursorColumn() >= terminal.ActiveBuffer().Width() {
return 0 return 0
@ -226,10 +239,10 @@ func (terminal *Terminal) GetLogicalCursorX() uint16 {
func (terminal *Terminal) GetLogicalCursorY() uint16 { func (terminal *Terminal) GetLogicalCursorY() uint16 {
if terminal.ActiveBuffer().CursorColumn() >= terminal.ActiveBuffer().Width() { if terminal.ActiveBuffer().CursorColumn() >= terminal.ActiveBuffer().Width() {
return terminal.ActiveBuffer().CursorLine() + 1 return terminal.ActiveBuffer().CursorLineAbsolute() + 1
} }
return terminal.ActiveBuffer().CursorLine() return terminal.ActiveBuffer().CursorLineAbsolute()
} }
func (terminal *Terminal) GetTitle() string { func (terminal *Terminal) GetTitle() string {
@ -349,3 +362,15 @@ func (terminal *Terminal) SetLineFeedMode() {
func (terminal *Terminal) ResetVerticalMargins() { func (terminal *Terminal) ResetVerticalMargins() {
terminal.terminalState.ResetVerticalMargins() terminal.terminalState.ResetVerticalMargins()
} }
func (terminal *Terminal) SetScreenMode(enabled bool) {
if terminal.terminalState.ScreenMode == enabled {
return
}
terminal.terminalState.ScreenMode = enabled
terminal.terminalState.CursorAttr.ReverseVideo()
for _, buffer := range terminal.buffers {
buffer.ReverseVideo()
}
terminal.emitReverse(enabled)
}