From 8e5a9fb26d54ca3f501088c44230a898255daf25 Mon Sep 17 00:00:00 2001 From: Liam Galvin Date: Sun, 2 Sep 2018 20:59:35 +0100 Subject: [PATCH] start scrolling --- buffer/buffer.go | 92 ++++++++++++++++++++++++++----- terminal/csi.go | 126 ++++++++++++++----------------------------- terminal/modes.go | 83 ++++++++++++++++++++++++++++ terminal/output.go | 3 +- terminal/terminal.go | 3 ++ 5 files changed, 209 insertions(+), 98 deletions(-) create mode 100644 terminal/modes.go diff --git a/buffer/buffer.go b/buffer/buffer.go index cd0da94..8b4509b 100644 --- a/buffer/buffer.go +++ b/buffer/buffer.go @@ -46,7 +46,7 @@ func (buffer *Buffer) SetReplaceMode() { buffer.replaceMode = true } -func (buffer *Buffer) SetMargins(top uint, bottom uint) { +func (buffer *Buffer) SetVerticalMargins(top uint, bottom uint) { buffer.topMargin = top buffer.bottomMargin = bottom } @@ -57,11 +57,36 @@ func (buffer *Buffer) GetScrollOffset() uint { func (buffer *Buffer) ScrollDown(lines uint16) { + defer buffer.emitDisplayChange() + + // scrollable region is enabled + if buffer.topMargin > 0 || buffer.bottomMargin < uint(buffer.ViewHeight())-1 { + + for c := 0; c < int(lines); c++ { + + for i := buffer.topMargin; i < buffer.bottomMargin; i++ { + above := buffer.getViewLine(uint16(i)) + below := buffer.getViewLine(uint16(i + 1)) + above.cells = below.cells + } + final := buffer.getViewLine(uint16(buffer.bottomMargin)) + + lineIndex := buffer.convertViewLineToRawLine(uint16(buffer.bottomMargin + 1)) + + if lineIndex < uint64(len(buffer.lines)) { + *final = buffer.lines[lineIndex] + } else { + *final = newLine() + } + } + + return + } + if buffer.Height() < int(buffer.ViewHeight()) { return } - defer buffer.emitDisplayChange() if uint(lines) > buffer.scrollLinesFromBottom { lines = uint16(buffer.scrollLinesFromBottom) } @@ -70,11 +95,36 @@ func (buffer *Buffer) ScrollDown(lines uint16) { func (buffer *Buffer) ScrollUp(lines uint16) { - if buffer.Height() < int(buffer.ViewHeight()) { + defer buffer.emitDisplayChange() + + // scrollable region is enabled + if buffer.topMargin > 0 || buffer.bottomMargin < uint(buffer.ViewHeight())-1 { + + for c := 0; c < int(lines); c++ { + + for i := buffer.bottomMargin; i > buffer.topMargin+1; i-- { + below := buffer.getViewLine(uint16(i)) + above := buffer.getViewLine(uint16(i - 1)) + below.cells = above.cells + } + final := buffer.getViewLine(uint16(buffer.topMargin)) + + lineIndex := buffer.convertViewLineToRawLine(uint16(buffer.topMargin - 1)) + + if lineIndex >= 0 && lineIndex < uint64(len(buffer.lines)) { + *final = buffer.lines[lineIndex] + } else { + panic("hmm!?") + *final = newLine() + } + } + return } - defer buffer.emitDisplayChange() + if buffer.Height() < int(buffer.ViewHeight()) { + return + } if uint(lines)+buffer.scrollLinesFromBottom >= (uint(buffer.Height()) - uint(buffer.ViewHeight())) { buffer.scrollLinesFromBottom = uint(buffer.Height()) - uint(buffer.ViewHeight()) @@ -279,12 +329,26 @@ func (buffer *Buffer) CarriageReturn() { func (buffer *Buffer) NewLine() { defer buffer.emitDisplayChange() + + buffer.cursorX = 0 + + if (buffer.topMargin > 0 || buffer.bottomMargin < uint(buffer.ViewHeight())-1) && uint(buffer.cursorY) == buffer.bottomMargin { + // scrollable region is enabled + for i := buffer.topMargin; i < buffer.bottomMargin; i++ { + above := buffer.getViewLine(uint16(i)) + below := buffer.getViewLine(uint16(i + 1)) + above.cells = below.cells + } + final := buffer.getViewLine(uint16(buffer.bottomMargin)) + *final = newLine() + return + } + if buffer.cursorY >= buffer.ViewHeight()-1 { buffer.lines = append(buffer.lines, newLine()) } else { buffer.cursorY++ } - buffer.cursorX = 0 } func (buffer *Buffer) MovePosition(x int16, y int16) { @@ -344,23 +408,27 @@ func (buffer *Buffer) Clear() { // creates if necessary func (buffer *Buffer) getCurrentLine() *Line { + return buffer.getViewLine(buffer.cursorY) +} - if buffer.cursorY >= buffer.ViewHeight() { // @todo is this okay? +func (buffer *Buffer) getViewLine(index uint16) *Line { + + if index >= buffer.ViewHeight() { // @todo is this okay? return &buffer.lines[len(buffer.lines)-1] } if len(buffer.lines) < int(buffer.ViewHeight()) { - for int(buffer.cursorY) >= len(buffer.lines) { + for int(index) >= len(buffer.lines) { buffer.lines = append(buffer.lines, newLine()) } - return &buffer.lines[int(buffer.cursorY)] + return &buffer.lines[int(index)] } - if int(buffer.RawLine()) < len(buffer.lines) { - return &buffer.lines[buffer.RawLine()] + if int(buffer.convertViewLineToRawLine(index)) < len(buffer.lines) { + return &buffer.lines[buffer.convertViewLineToRawLine(index)] } - panic(fmt.Sprintf("Failed to retrieve line for %d %d", buffer.cursorX, buffer.cursorY)) + panic(fmt.Sprintf("Failed to retrieve line for %d", index)) } func (buffer *Buffer) EraseLine() { @@ -547,5 +615,5 @@ func (buffer *Buffer) ResizeView(width uint16, height uint16) { line = buffer.getCurrentLine() buffer.cursorX = uint16((len(line.cells) - cXFromEndOfLine) - 1) - buffer.SetMargins(0, uint(buffer.viewHeight-1)) + buffer.SetVerticalMargins(0, uint(buffer.viewHeight-1)) } diff --git a/terminal/csi.go b/terminal/csi.go index 0820aa7..2ea40b2 100644 --- a/terminal/csi.go +++ b/terminal/csi.go @@ -15,12 +15,14 @@ var csiSequenceMap = map[rune]csiSequenceHandler{ 't': csiWindowManipulation, 'J': csiEraseInDisplayHandler, 'K': csiEraseInLineHandler, + 'L': csiInsertLinesHandler, 'P': csiDeleteHandler, - 'T': csiScrollHandler, + 'S': csiScrollUpHandler, + 'T': csiScrollDownHandler, 'X': csiEraseCharactersHandler, } -func csiScrollHandler(params []string, intermediate string, terminal *Terminal) error { +func csiScrollUpHandler(params []string, intermediate string, terminal *Terminal) error { distance := 1 if len(params) > 1 { return fmt.Errorf("Not supported") @@ -32,6 +34,41 @@ func csiScrollHandler(params []string, intermediate string, terminal *Terminal) distance = 1 } } + terminal.logger.Debugf("Scrolling up %d", distance) + terminal.ScrollUp(uint16(distance)) + return nil +} + +func csiInsertLinesHandler(params []string, intermediate string, terminal *Terminal) error { + count := 1 + if len(params) > 1 { + return fmt.Errorf("Not supported") + } + if len(params) == 1 { + var err error + count, err = strconv.Atoi(params[0]) + if err != nil { + count = 1 + } + } + terminal.logger.Debugf("Inserting %d lines", count) + panic("Not supported") + return nil +} + +func csiScrollDownHandler(params []string, intermediate string, terminal *Terminal) error { + distance := 1 + if len(params) > 1 { + return fmt.Errorf("Not supported") + } + if len(params) == 1 { + var err error + distance, err = strconv.Atoi(params[0]) + if err != nil { + distance = 1 + } + } + terminal.logger.Debugf("Scrolling down %d", distance) terminal.ScrollDown(uint16(distance)) return nil } @@ -61,90 +98,9 @@ func csiSetMarginsHandler(params []string, intermediate string, terminal *Termin top-- bottom-- - terminal.logger.Warnf("Request to set margins from line %d to %d", top, bottom) - + terminal.ActiveBuffer().SetVerticalMargins(uint(top), uint(bottom)) terminal.ActiveBuffer().SetPosition(0, 0) - return fmt.Errorf("Not supported") -} - -func csiSetMode(modeStr string, enabled bool, terminal *Terminal) error { - - /* - Mouse support - - #define SET_X10_MOUSE 9 - #define SET_VT200_MOUSE 1000 - #define SET_VT200_HIGHLIGHT_MOUSE 1001 - #define SET_BTN_EVENT_MOUSE 1002 - #define SET_ANY_EVENT_MOUSE 1003 - - #define SET_FOCUS_EVENT_MOUSE 1004 - - #define SET_EXT_MODE_MOUSE 1005 - #define SET_SGR_EXT_MODE_MOUSE 1006 - #define SET_URXVT_EXT_MODE_MOUSE 1015 - - #define SET_ALTERNATE_SCROLL 1007 - */ - - switch modeStr { - case "4": - if enabled { // @todo support replace mode - terminal.ActiveBuffer().SetInsertMode() - } else { - terminal.ActiveBuffer().SetReplaceMode() - } - case "?1": - terminal.modes.ApplicationCursorKeys = enabled - case "?7": - // auto-wrap mode - //DECAWM - terminal.ActiveBuffer().SetAutoWrap(enabled) - case "?9": - if enabled { - terminal.logger.Infof("Turning on X10 mouse mode") - terminal.SetMouseMode(MouseModeX10) - } else { - terminal.logger.Infof("Turning off X10 mouse mode") - terminal.SetMouseMode(MouseModeNone) - } - case "?12", "?13": - terminal.modes.BlinkingCursor = enabled - case "?25": - terminal.modes.ShowCursor = enabled - case "?47", "?1047": - if enabled { - terminal.UseAltBuffer() - } else { - terminal.UseMainBuffer() - } - case "?1000", "?1006;1000", "?10061000": // ?10061000 seen from htop - // enable mouse tracking - // 1000 refers to ext mode for extended mouse click area - otherwise only x <= 255-31 - if enabled { - terminal.logger.Infof("Turning on VT200 mouse mode") - terminal.SetMouseMode(MouseModeVT200) - } else { - terminal.logger.Infof("Turning off VT200 mouse mode") - terminal.SetMouseMode(MouseModeNone) - } - case "?1048": - if enabled { - terminal.ActiveBuffer().SaveCursor() - } else { - terminal.ActiveBuffer().RestoreCursor() - } - case "?1049": - if enabled { - terminal.UseAltBuffer() - } else { - terminal.UseMainBuffer() - } - default: - return fmt.Errorf("Unsupported CSI %sl code", modeStr) - } - return nil } @@ -334,7 +290,7 @@ CSI: 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)) + fmt.Printf("CSI 0x%02X (ESC[%s%s%s)\n", final, param, intermediate, string(final)) return err } diff --git a/terminal/modes.go b/terminal/modes.go new file mode 100644 index 0000000..099163f --- /dev/null +++ b/terminal/modes.go @@ -0,0 +1,83 @@ +package terminal + +import "fmt" + +func csiSetMode(modeStr string, enabled bool, terminal *Terminal) error { + + /* + Mouse support + + #define SET_X10_MOUSE 9 + #define SET_VT200_MOUSE 1000 + #define SET_VT200_HIGHLIGHT_MOUSE 1001 + #define SET_BTN_EVENT_MOUSE 1002 + #define SET_ANY_EVENT_MOUSE 1003 + + #define SET_FOCUS_EVENT_MOUSE 1004 + + #define SET_EXT_MODE_MOUSE 1005 + #define SET_SGR_EXT_MODE_MOUSE 1006 + #define SET_URXVT_EXT_MODE_MOUSE 1015 + + #define SET_ALTERNATE_SCROLL 1007 + */ + + switch modeStr { + case "4": + if enabled { // @todo support replace mode + terminal.ActiveBuffer().SetInsertMode() + } else { + terminal.ActiveBuffer().SetReplaceMode() + } + case "?1": + terminal.modes.ApplicationCursorKeys = enabled + case "?7": + // auto-wrap mode + //DECAWM + terminal.ActiveBuffer().SetAutoWrap(enabled) + case "?9": + if enabled { + terminal.logger.Infof("Turning on X10 mouse mode") + terminal.SetMouseMode(MouseModeX10) + } else { + terminal.logger.Infof("Turning off X10 mouse mode") + terminal.SetMouseMode(MouseModeNone) + } + case "?12", "?13": + terminal.modes.BlinkingCursor = enabled + case "?25": + terminal.modes.ShowCursor = enabled + case "?47", "?1047": + if enabled { + terminal.UseAltBuffer() + } else { + terminal.UseMainBuffer() + } + case "?1000", "?1006;1000", "?10061000": // ?10061000 seen from htop + // enable mouse tracking + // 1000 refers to ext mode for extended mouse click area - otherwise only x <= 255-31 + if enabled { + terminal.logger.Infof("Turning on VT200 mouse mode") + terminal.SetMouseMode(MouseModeVT200) + } else { + terminal.logger.Infof("Turning off VT200 mouse mode") + terminal.SetMouseMode(MouseModeNone) + } + case "?1048": + if enabled { + terminal.ActiveBuffer().SaveCursor() + } else { + terminal.ActiveBuffer().RestoreCursor() + } + case "?1049": + if enabled { + terminal.UseAltBuffer() + } else { + terminal.UseMainBuffer() + } + default: + return fmt.Errorf("Unsupported CSI %sl code", modeStr) + } + + return nil +} diff --git a/terminal/output.go b/terminal/output.go index 318823c..7f38eac 100644 --- a/terminal/output.go +++ b/terminal/output.go @@ -26,6 +26,7 @@ var escapeSequenceMap = map[rune]escapeSequenceHandler{ } func newLineSequenceHandler(pty chan rune, terminal *Terminal) error { + terminal.logger.Debugf("New line!") terminal.ActiveBuffer().NewLine() return nil } @@ -94,7 +95,7 @@ func (terminal *Terminal) processInput(ctx context.Context, pty chan rune) { terminal.logger.Errorf("Error handling escape sequence: %s", err) } } else { - terminal.logger.Debugf("Received character 0x%X: %q", b, string(b)) + //terminal.logger.Debugf("Received character 0x%X: %q", b, string(b)) if b >= 0x20 { terminal.ActiveBuffer().Write(b) } else { diff --git a/terminal/terminal.go b/terminal/terminal.go index 33b6dab..d49cfcb 100644 --- a/terminal/terminal.go +++ b/terminal/terminal.go @@ -133,10 +133,13 @@ func (terminal *Terminal) GetScrollOffset() uint { } func (terminal *Terminal) ScrollDown(lines uint16) { + terminal.logger.Infof("Scrolling down %d", lines) terminal.ActiveBuffer().ScrollDown(lines) + } func (terminal *Terminal) ScrollUp(lines uint16) { + terminal.logger.Infof("Scrolling up %d", lines) terminal.ActiveBuffer().ScrollUp(lines) }