diff --git a/buffer/buffer.go b/buffer/buffer.go index aa74f46..9c841d3 100644 --- a/buffer/buffer.go +++ b/buffer/buffer.go @@ -232,18 +232,6 @@ func (buffer *Buffer) MovePosition(x int16, y int16) { buffer.SetPosition(toX, toY) } -func (buffer *Buffer) ShowCursor() { - -} - -func (buffer *Buffer) HideCursor() { - -} - -func (buffer *Buffer) SetCursorBlink(enabled bool) { - -} - func (buffer *Buffer) SetPosition(col uint16, line uint16) { defer buffer.emitDisplayChange() if col >= buffer.ViewWidth() { @@ -339,11 +327,31 @@ func (buffer *Buffer) EraseDisplay() { } } +func (buffer *Buffer) EraseCharacters(n int) { + defer buffer.emitDisplayChange() + + line := buffer.getCurrentLine() + + max := int(buffer.cursorX) + n + if max > len(line.cells) { + max = len(line.cells) + } + + for i := int(buffer.cursorX); i < max; i++ { + line.cells[i].erase() + } +} + func (buffer *Buffer) EraseDisplayFromCursor() { defer buffer.emitDisplayChange() line := buffer.getCurrentLine() - line.cells = line.cells[:buffer.cursorX] + max := int(buffer.cursorX) + if max > len(line.cells) { + max = len(line.cells) + } + + line.cells = line.cells[:max] for i := buffer.cursorY + 1; i < buffer.ViewHeight(); i++ { rawLine := buffer.convertViewLineToRawLine(i) if int(rawLine) < len(buffer.lines) { diff --git a/gui/gui.go b/gui/gui.go index bb6531d..41f7e47 100644 --- a/gui/gui.go +++ b/gui/gui.go @@ -195,9 +195,11 @@ func (gui *GUI) Render() error { } } - cx := int(gui.terminal.GetLogicalCursorX()) - cy := int(gui.terminal.GetLogicalCursorY()) - gui.renderer.DrawCursor(cx, cy, gui.config.ColourScheme.Cursor) + if gui.terminal.Modes().ShowCursor { + cx := int(gui.terminal.GetLogicalCursorX()) + cy := int(gui.terminal.GetLogicalCursorY()) + gui.renderer.DrawCursor(cx, cy, gui.config.ColourScheme.Cursor) + } _ = fps /* diff --git a/terminal/csi.go b/terminal/csi.go index e161f3e..2e2b3c0 100644 --- a/terminal/csi.go +++ b/terminal/csi.go @@ -11,6 +11,69 @@ var csiSequenceMap = map[rune]csiSequenceHandler{ 'P': csiDeleteHandler, 'J': csiEraseInDisplayHandler, 'K': csiEraseInLineHandler, + 'h': csiSetModeHandler, + 'l': csiResetModeHandler, + 'd': csiLinePositionAbsolute, + 't': csiWindowManipulation, + 'X': csiEraseCharactersHandler, +} + +func csiSetMode(modeStr string, enabled bool, terminal *Terminal) error { + switch modeStr { + case "?1": + terminal.modes.ApplicationCursorKeys = enabled + case "?12": + terminal.modes.BlinkingCursor = enabled + case "?25": + terminal.modes.ShowCursor = enabled + default: + return fmt.Errorf("Unsupported CSI %sl code", modeStr) + } + + return nil +} + +func csiEraseCharactersHandler(params []string, intermediate string, terminal *Terminal) error { + count := 1 + if len(params) > 0 { + var err error + count, err = strconv.Atoi(params[0]) + if err != nil { + count = 1 + } + } + + terminal.buffer.EraseCharacters(count) + + return nil +} + +func csiResetModeHandler(params []string, intermediate string, terminal *Terminal) error { + return csiSetMode(strings.Join(params, ""), false, terminal) +} + +func csiSetModeHandler(params []string, intermediate string, terminal *Terminal) error { + return csiSetMode(strings.Join(params, ""), true, terminal) +} + +func csiWindowManipulation(params []string, intermediate string, terminal *Terminal) error { + // @todo this + return nil +} + +func csiLinePositionAbsolute(params []string, intermediate string, terminal *Terminal) error { + col := 1 + if len(params) > 0 { + var err error + col, err = strconv.Atoi(params[0]) + if err != nil { + col = 1 + } + } + + terminal.buffer.SetPosition(uint16(col), terminal.buffer.CursorLine()) + + return nil } type csiSequenceHandler func(params []string, intermediate string, terminal *Terminal) error @@ -153,19 +216,7 @@ CSI: terminal.buffer.SetPosition(uint16(x-1), uint16(y-1)) default: - switch param + intermediate + string(final) { - case "?25h": - terminal.buffer.ShowCursor() - case "?25l": - terminal.buffer.HideCursor() - case "?12h": - terminal.buffer.SetCursorBlink(true) - case "?12l": - terminal.buffer.SetCursorBlink(false) - default: - err = fmt.Errorf("Unknown CSI control sequence: 0x%02X (ESC[%s%s%s)", final, param, intermediate, string(final)) - } - + err = fmt.Errorf("Unknown CSI control sequence: 0x%02X (ESC[%s%s%s)", final, param, intermediate, string(final)) } } terminal.logger.Debugf("Received CSI control sequence: 0x%02X (ESC[%s%s%s)", final, param, intermediate, string(final)) diff --git a/terminal/terminal.go b/terminal/terminal.go index da6f5f5..ef959f1 100644 --- a/terminal/terminal.go +++ b/terminal/terminal.go @@ -26,6 +26,13 @@ type Terminal struct { titleHandlers []chan bool pauseChan chan bool resumeChan chan bool + modes Modes +} + +type Modes struct { + ShowCursor bool + ApplicationCursorKeys bool + BlinkingCursor bool } type Winsize struct { @@ -53,6 +60,9 @@ func New(pty *os.File, logger *zap.SugaredLogger, config config.Config) *Termina titleHandlers: []chan bool{}, pauseChan: make(chan bool, 1), resumeChan: make(chan bool, 1), + modes: Modes{ + ShowCursor: true, + }, } } @@ -68,6 +78,10 @@ func (terminal *Terminal) AttachTitleChangeHandler(handler chan bool) { terminal.titleHandlers = append(terminal.titleHandlers, handler) } +func (terminal *Terminal) Modes() Modes { + return terminal.modes +} + func (terminal *Terminal) emitTitleChange() { for _, h := range terminal.titleHandlers { go func(c chan bool) {