mirror of https://github.com/liamg/aminal.git
Merge pull request #20 from liamg/19=fix-scrollable-region-in-htop
fix scrollable region in htop
This commit is contained in:
commit
0b8f2ed44d
|
@ -5,8 +5,9 @@ Thanks for considering contributing to Aminal.
|
|||
## How do terminals even work?
|
||||
|
||||
- [ELI5 - How does a terminal emulator work?](https://www.reddit.com/r/linuxquestions/comments/3ciful/eli5_how_does_a_terminal_emulator_work/)
|
||||
- [Xterm Control Sequences](https://www.xfree86.org/4.8.0/ctlseqs.html)
|
||||
- [Xterm Control Sequences](https://www.xfree86.org/4.8.0/ctlseqs.html)
|
||||
- [VT100 Programmer Information](https://vt100.net/docs/vt100-ug/chapter3.html)
|
||||
- [VT100 Manual](http://www.bitsavers.org/pdf/dec/terminal/vt100/EK-VT100-UG-001_VT100_User_Guide_Aug78.pdf)
|
||||
|
||||
## What can I work on?
|
||||
|
||||
|
|
152
buffer/buffer.go
152
buffer/buffer.go
|
@ -56,34 +56,18 @@ func (buffer *Buffer) GetScrollOffset() uint {
|
|||
return buffer.scrollLinesFromBottom
|
||||
}
|
||||
|
||||
func (buffer *Buffer) HasScrollableRegion() bool {
|
||||
return buffer.topMargin > 0 || buffer.bottomMargin < uint(buffer.ViewHeight())-1
|
||||
}
|
||||
|
||||
func (buffer *Buffer) InScrollableRegion() bool {
|
||||
return buffer.HasScrollableRegion() && uint(buffer.cursorY) >= buffer.topMargin && uint(buffer.cursorY) <= buffer.bottomMargin
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -98,30 +82,6 @@ func (buffer *Buffer) ScrollUp(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.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 {
|
||||
*final = newLine()
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if buffer.Height() < int(buffer.ViewHeight()) {
|
||||
return
|
||||
}
|
||||
|
@ -201,6 +161,14 @@ func (buffer *Buffer) CursorLine() uint16 {
|
|||
return buffer.cursorY
|
||||
}
|
||||
|
||||
func (buffer *Buffer) TopMargin() uint {
|
||||
return buffer.topMargin
|
||||
}
|
||||
|
||||
func (buffer *Buffer) BottomMargin() uint {
|
||||
return buffer.bottomMargin
|
||||
}
|
||||
|
||||
// translates the cursor line to the raw buffer line
|
||||
func (buffer *Buffer) RawLine() uint64 {
|
||||
return buffer.convertViewLineToRawLine(buffer.cursorY)
|
||||
|
@ -231,6 +199,55 @@ func (buffer *Buffer) ViewHeight() uint16 {
|
|||
return buffer.viewHeight
|
||||
}
|
||||
|
||||
func (buffer *Buffer) Index() {
|
||||
|
||||
// This sequence causes the active position to move downward one line without changing the column position.
|
||||
// If the active position is at the bottom margin, a scroll up is performed."
|
||||
|
||||
if buffer.InScrollableRegion() {
|
||||
|
||||
if uint(buffer.cursorY) < buffer.bottomMargin {
|
||||
buffer.cursorY++
|
||||
return
|
||||
}
|
||||
|
||||
for i := buffer.topMargin; i < uint(buffer.cursorY); i++ {
|
||||
buffer.lines[i] = buffer.lines[i+1]
|
||||
}
|
||||
buffer.lines[buffer.cursorY] = newLine()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if buffer.cursorY >= buffer.ViewHeight()-1 {
|
||||
buffer.lines = append(buffer.lines, newLine())
|
||||
} else {
|
||||
buffer.cursorY++
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) ReverseIndex() {
|
||||
if buffer.InScrollableRegion() {
|
||||
|
||||
if uint(buffer.cursorY) > buffer.topMargin {
|
||||
buffer.cursorY--
|
||||
return
|
||||
}
|
||||
|
||||
for i := buffer.bottomMargin; i > uint(buffer.cursorY); i-- {
|
||||
buffer.lines[i] = buffer.lines[i-1]
|
||||
}
|
||||
buffer.lines[buffer.cursorY] = newLine()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if buffer.cursorY > 0 {
|
||||
|
||||
buffer.cursorY--
|
||||
}
|
||||
}
|
||||
|
||||
// Write will write a rune to the terminal at the position of the cursor, and increment the cursor position
|
||||
func (buffer *Buffer) Write(runes ...rune) {
|
||||
|
||||
|
@ -248,16 +265,21 @@ func (buffer *Buffer) Write(runes ...rune) {
|
|||
}
|
||||
line := buffer.getCurrentLine()
|
||||
|
||||
if buffer.replaceMode {
|
||||
for int(buffer.CursorColumn()) >= len(line.cells) {
|
||||
line.cells = append(line.cells, NewBackgroundCell(buffer.cursorAttr.BgColour))
|
||||
}
|
||||
line.cells[buffer.cursorX].attr = buffer.cursorAttr
|
||||
line.cells[buffer.cursorX].setRune(r)
|
||||
buffer.incrementCursorPosition()
|
||||
continue
|
||||
}
|
||||
|
||||
if buffer.CursorColumn() >= buffer.Width() { // if we're after the line, move to next
|
||||
|
||||
if buffer.autoWrap {
|
||||
buffer.cursorX = 0
|
||||
|
||||
if buffer.cursorY >= buffer.ViewHeight()-1 {
|
||||
buffer.lines = append(buffer.lines, newLine())
|
||||
} else {
|
||||
buffer.cursorY++
|
||||
}
|
||||
buffer.NewLine()
|
||||
|
||||
newLine := buffer.getCurrentLine()
|
||||
newLine.setWrapped(true)
|
||||
|
@ -331,24 +353,7 @@ func (buffer *Buffer) NewLine() {
|
|||
|
||||
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.Index()
|
||||
}
|
||||
|
||||
func (buffer *Buffer) MovePosition(x int16, y int16) {
|
||||
|
@ -373,6 +378,7 @@ func (buffer *Buffer) MovePosition(x int16, y int16) {
|
|||
|
||||
func (buffer *Buffer) SetPosition(col uint16, line uint16) {
|
||||
defer buffer.emitDisplayChange()
|
||||
|
||||
if col >= buffer.ViewWidth() {
|
||||
col = buffer.ViewWidth() - 1
|
||||
//logrus.Errorf("Cannot set cursor position: column %d is outside of the current view width (%d columns)", col, buffer.ViewWidth())
|
||||
|
@ -381,12 +387,14 @@ func (buffer *Buffer) SetPosition(col uint16, line uint16) {
|
|||
line = buffer.ViewHeight() - 1
|
||||
//logrus.Errorf("Cannot set cursor position: line %d is outside of the current view height (%d lines)", line, buffer.ViewHeight())
|
||||
}
|
||||
|
||||
buffer.cursorX = col
|
||||
buffer.cursorY = line
|
||||
}
|
||||
|
||||
func (buffer *Buffer) GetVisibleLines() []Line {
|
||||
lines := []Line{}
|
||||
|
||||
for i := buffer.Height() - int(buffer.ViewHeight()); i < buffer.Height(); i++ {
|
||||
y := i - int(buffer.scrollLinesFromBottom)
|
||||
if y >= 0 && y < len(buffer.lines) {
|
||||
|
|
2
main.go
2
main.go
|
@ -86,7 +86,7 @@ func main() {
|
|||
// parse this
|
||||
conf := getConfig()
|
||||
|
||||
os.Setenv("TERM", "xterm-256color")
|
||||
os.Setenv("TERM", "xterm-256color") // contraversial! easier than installing terminfo everywhere, but obviously going to be slightly different to xterm functionality, so we'll see...
|
||||
|
||||
logger, err := getLogger(conf)
|
||||
if err != nil {
|
||||
|
|
|
@ -31,18 +31,12 @@ func swallowHandler(n int) func(pty chan rune, terminal *Terminal) error {
|
|||
}
|
||||
|
||||
func indexHandler(pty chan rune, terminal *Terminal) error {
|
||||
// @todo is thus right?
|
||||
// "This sequence causes the active position to move downward one line without changing the column position. If the active position is at the bottom margin, a scroll up is performed."
|
||||
if terminal.ActiveBuffer().CursorLine() == terminal.ActiveBuffer().ViewHeight()-1 {
|
||||
terminal.ActiveBuffer().NewLine()
|
||||
return nil
|
||||
}
|
||||
terminal.ActiveBuffer().MovePosition(0, 1)
|
||||
terminal.ActiveBuffer().Index()
|
||||
return nil
|
||||
}
|
||||
|
||||
func reverseIndexHandler(pty chan rune, terminal *Terminal) error {
|
||||
terminal.ActiveBuffer().MovePosition(0, -1)
|
||||
terminal.ActiveBuffer().ReverseIndex()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -62,6 +56,7 @@ func ansiHandler(pty chan rune, terminal *Terminal) error {
|
|||
|
||||
handler, ok := ansiSequenceMap[b]
|
||||
if ok {
|
||||
//terminal.logger.Debugf("Handling ansi sequence %c", b)
|
||||
return handler(pty, terminal)
|
||||
}
|
||||
|
||||
|
|
379
terminal/csi.go
379
terminal/csi.go
|
@ -6,20 +6,206 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
var csiSequenceMap = map[rune]csiSequenceHandler{
|
||||
'd': csiLinePositionAbsolute,
|
||||
'h': csiSetModeHandler,
|
||||
'l': csiResetModeHandler,
|
||||
'm': sgrSequenceHandler,
|
||||
'r': csiSetMarginsHandler,
|
||||
't': csiWindowManipulation,
|
||||
'J': csiEraseInDisplayHandler,
|
||||
'K': csiEraseInLineHandler,
|
||||
'L': csiInsertLinesHandler,
|
||||
'P': csiDeleteHandler,
|
||||
'S': csiScrollUpHandler,
|
||||
'T': csiScrollDownHandler,
|
||||
'X': csiEraseCharactersHandler,
|
||||
type csiSequenceHandler func(params []string, intermediate string, terminal *Terminal) error
|
||||
|
||||
type csiMapping struct {
|
||||
id rune
|
||||
handler csiSequenceHandler
|
||||
description string
|
||||
expectedParams *expectedParams
|
||||
}
|
||||
|
||||
type expectedParams struct {
|
||||
min uint8
|
||||
max uint8
|
||||
}
|
||||
|
||||
var csiSequences = []csiMapping{
|
||||
csiMapping{id: 'd', handler: csiLinePositionAbsolute, expectedParams: &expectedParams{min: 0, max: 1}, description: "Line Position Absolute [row] (default = [1,column]) (VPA)"},
|
||||
csiMapping{id: 'h', handler: csiSetModeHandler, expectedParams: &expectedParams{min: 1, max: 1}, description: "Set Mode (SM)"},
|
||||
csiMapping{id: 'l', handler: csiResetModeHandler, expectedParams: &expectedParams{min: 1, max: 1}, description: "Reset Mode (RM)"},
|
||||
csiMapping{id: 'm', handler: sgrSequenceHandler, description: "Character Attributes (SGR)"},
|
||||
csiMapping{id: 'r', handler: csiSetMarginsHandler, expectedParams: &expectedParams{min: 2, max: 2}, description: "Set Scrolling Region [top;bottom] (default = full size of window) (DECSTBM), VT100"},
|
||||
csiMapping{id: 't', handler: csiWindowManipulation, description: "Window manipulation"},
|
||||
csiMapping{id: 'J', handler: csiEraseInDisplayHandler, description: "Erase in Display (ED), VT100"},
|
||||
csiMapping{id: 'K', handler: csiEraseInLineHandler, description: "Erase in Line (EL), VT100"},
|
||||
csiMapping{id: 'L', handler: csiInsertLinesHandler, description: "Insert Ps Line(s) (default = 1) (IL)"},
|
||||
csiMapping{id: 'P', handler: csiDeleteHandler, description: " Delete Ps Character(s) (default = 1) (DCH)"},
|
||||
csiMapping{id: 'S', handler: csiScrollUpHandler, description: "Scroll up Ps lines (default = 1) (SU), VT420, ECMA-48"},
|
||||
csiMapping{id: 'T', handler: csiScrollDownHandler, description: "Scroll down Ps lines (default = 1) (SD), VT420"},
|
||||
csiMapping{id: 'X', handler: csiEraseCharactersHandler, description: "Erase Ps Character(s) (default = 1) (ECH"},
|
||||
csiMapping{id: 'A', handler: csiCursorUpHandler, description: "Cursor Up Ps Times (default = 1) (CUU)"},
|
||||
csiMapping{id: 'B', handler: csiCursorDownHandler, description: "Cursor Down Ps Times (default = 1) (CUD)"},
|
||||
csiMapping{id: 'C', handler: csiCursorForwardHandler, description: "Cursor Forward Ps Times (default = 1) (CUF)"},
|
||||
csiMapping{id: 'D', handler: csiCursorBackwardHandler, description: "Cursor Backward Ps Times (default = 1) (CUB)"},
|
||||
csiMapping{id: 'E', handler: csiCursorNextLineHandler, description: "Cursor Next Line Ps Times (default = 1) (CNL)"},
|
||||
csiMapping{id: 'F', handler: csiCursorPrecedingLineHandler, description: "Cursor Preceding Line Ps Times (default = 1) (CPL)"},
|
||||
csiMapping{id: 'G', handler: csiCursorCharacterAbsoluteHandler, description: "Cursor Character Absolute [column] (default = [row,1]) (CHA)"},
|
||||
csiMapping{id: 'H', handler: csiCursorPositionHandler, description: "Cursor Position [row;column] (default = [1,1]) (CUP)"},
|
||||
csiMapping{id: 'f', handler: csiCursorPositionHandler, description: "Horizontal and Vertical Position [row;column] (default = [1,1]) (HVP)"},
|
||||
}
|
||||
|
||||
func csiHandler(pty chan rune, terminal *Terminal) error {
|
||||
var final rune
|
||||
var b rune
|
||||
param := ""
|
||||
intermediate := ""
|
||||
CSI:
|
||||
for {
|
||||
b = <-pty
|
||||
switch true {
|
||||
case b >= 0x30 && b <= 0x3F:
|
||||
param = param + string(b)
|
||||
case b >= 0x20 && b <= 0x2F:
|
||||
//intermediate? useful?
|
||||
intermediate += string(b)
|
||||
case b >= 0x40 && b <= 0x7e:
|
||||
final = b
|
||||
break CSI
|
||||
}
|
||||
}
|
||||
|
||||
params := strings.Split(param, ";")
|
||||
if param == "" {
|
||||
params = []string{}
|
||||
}
|
||||
|
||||
for _, sequence := range csiSequences {
|
||||
if sequence.id == final {
|
||||
if sequence.expectedParams != nil && (uint8(len(params)) < sequence.expectedParams.min || uint8(len(params)) > sequence.expectedParams.max) {
|
||||
continue
|
||||
}
|
||||
terminal.logger.Debugf("CSI 0x%02X (ESC[%s%s%s) %s", final, param, intermediate, string(final), sequence.description)
|
||||
err := sequence.handler(params, intermediate, terminal)
|
||||
terminal.logger.Debugf("After CSI, state: Col %d, Line %d, Top: %d, Bottom %d", terminal.ActiveBuffer().CursorColumn(), terminal.ActiveBuffer().CursorLine(), terminal.ActiveBuffer().TopMargin(), terminal.ActiveBuffer().BottomMargin())
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("Unknown CSI control sequence: 0x%02X (ESC[%s%s%s)", final, param, intermediate, string(final))
|
||||
|
||||
}
|
||||
|
||||
func csiCursorUpHandler(params []string, intermediate string, terminal *Terminal) error {
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
terminal.ActiveBuffer().MovePosition(0, -int16(distance))
|
||||
return nil
|
||||
}
|
||||
|
||||
func csiCursorDownHandler(params []string, intermediate string, terminal *Terminal) error {
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
|
||||
terminal.ActiveBuffer().MovePosition(0, int16(distance))
|
||||
return nil
|
||||
}
|
||||
|
||||
func csiCursorForwardHandler(params []string, intermediate string, terminal *Terminal) error {
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
|
||||
terminal.ActiveBuffer().MovePosition(int16(distance), 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func csiCursorBackwardHandler(params []string, intermediate string, terminal *Terminal) error {
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
|
||||
terminal.ActiveBuffer().MovePosition(-int16(distance), 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func csiCursorNextLineHandler(params []string, intermediate string, terminal *Terminal) error {
|
||||
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
|
||||
terminal.ActiveBuffer().MovePosition(0, int16(distance))
|
||||
terminal.ActiveBuffer().SetPosition(0, terminal.ActiveBuffer().CursorLine())
|
||||
return nil
|
||||
}
|
||||
|
||||
func csiCursorPrecedingLineHandler(params []string, intermediate string, terminal *Terminal) error {
|
||||
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
terminal.ActiveBuffer().MovePosition(0, -int16(distance))
|
||||
terminal.ActiveBuffer().SetPosition(0, terminal.ActiveBuffer().CursorLine())
|
||||
return nil
|
||||
}
|
||||
|
||||
func csiCursorCharacterAbsoluteHandler(params []string, intermediate string, terminal *Terminal) error {
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil || params[0] == "" {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
|
||||
terminal.ActiveBuffer().SetPosition(uint16(distance-1), terminal.ActiveBuffer().CursorLine())
|
||||
return nil
|
||||
}
|
||||
|
||||
func csiCursorPositionHandler(params []string, intermediate string, terminal *Terminal) error {
|
||||
x, y := 1, 1
|
||||
if len(params) == 2 {
|
||||
var err error
|
||||
if params[0] != "" {
|
||||
y, err = strconv.Atoi(string(params[0]))
|
||||
if err != nil {
|
||||
y = 1
|
||||
}
|
||||
}
|
||||
if params[1] != "" {
|
||||
x, err = strconv.Atoi(string(params[1]))
|
||||
if err != nil {
|
||||
x = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
terminal.ActiveBuffer().SetPosition(uint16(x-1), uint16(y-1))
|
||||
return nil
|
||||
}
|
||||
|
||||
func csiScrollUpHandler(params []string, intermediate string, terminal *Terminal) error {
|
||||
|
@ -52,7 +238,10 @@ func csiInsertLinesHandler(params []string, intermediate string, terminal *Termi
|
|||
}
|
||||
}
|
||||
terminal.logger.Debugf("Inserting %d lines", count)
|
||||
return fmt.Errorf("Not supported")
|
||||
for i := 0; i < count; i++ {
|
||||
terminal.ActiveBuffer().Index()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func csiScrollDownHandler(params []string, intermediate string, terminal *Terminal) error {
|
||||
|
@ -76,6 +265,11 @@ func csiScrollDownHandler(params []string, intermediate string, terminal *Termin
|
|||
func csiSetMarginsHandler(params []string, intermediate string, terminal *Terminal) error {
|
||||
top := 1
|
||||
bottom := int(terminal.ActiveBuffer().ViewHeight())
|
||||
|
||||
if len(params) > 2 {
|
||||
return fmt.Errorf("Not set margins")
|
||||
}
|
||||
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
top, err = strconv.Atoi(params[0])
|
||||
|
@ -86,10 +280,7 @@ func csiSetMarginsHandler(params []string, intermediate string, terminal *Termin
|
|||
if len(params) > 1 {
|
||||
var err error
|
||||
bottom, err = strconv.Atoi(params[1])
|
||||
if err != nil {
|
||||
bottom = 1
|
||||
}
|
||||
if bottom > int(terminal.ActiveBuffer().ViewHeight()) {
|
||||
if err != nil || bottom > int(terminal.ActiveBuffer().ViewHeight()) {
|
||||
bottom = int(terminal.ActiveBuffer().ViewHeight())
|
||||
}
|
||||
}
|
||||
|
@ -127,8 +318,7 @@ func csiSetModeHandler(params []string, intermediate string, terminal *Terminal)
|
|||
}
|
||||
|
||||
func csiWindowManipulation(params []string, intermediate string, terminal *Terminal) error {
|
||||
// @todo this
|
||||
return nil
|
||||
return fmt.Errorf("Window manipulation is not yet supported")
|
||||
}
|
||||
|
||||
func csiLinePositionAbsolute(params []string, intermediate string, terminal *Terminal) error {
|
||||
|
@ -146,153 +336,6 @@ func csiLinePositionAbsolute(params []string, intermediate string, terminal *Ter
|
|||
return nil
|
||||
}
|
||||
|
||||
type csiSequenceHandler func(params []string, intermediate string, terminal *Terminal) error
|
||||
|
||||
// CSI: Control Sequence Introducer [
|
||||
func csiHandler(pty chan rune, terminal *Terminal) error {
|
||||
var final rune
|
||||
var b rune
|
||||
var err error
|
||||
param := ""
|
||||
intermediate := ""
|
||||
CSI:
|
||||
for {
|
||||
b = <-pty
|
||||
switch true {
|
||||
case b >= 0x30 && b <= 0x3F:
|
||||
param = param + string(b)
|
||||
case b >= 0x20 && b <= 0x2F:
|
||||
//intermediate? useful?
|
||||
intermediate += string(b)
|
||||
case b >= 0x40 && b <= 0x7e:
|
||||
final = b
|
||||
break CSI
|
||||
}
|
||||
}
|
||||
|
||||
params := strings.Split(param, ";")
|
||||
|
||||
handler, ok := csiSequenceMap[final]
|
||||
if ok {
|
||||
err = handler(params, intermediate, terminal)
|
||||
} else {
|
||||
|
||||
switch final {
|
||||
case 'A':
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
terminal.ActiveBuffer().MovePosition(0, -int16(distance))
|
||||
case 'B':
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
|
||||
terminal.ActiveBuffer().MovePosition(0, int16(distance))
|
||||
case 'C':
|
||||
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
|
||||
terminal.ActiveBuffer().MovePosition(int16(distance), 0)
|
||||
|
||||
case 'D':
|
||||
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
|
||||
terminal.ActiveBuffer().MovePosition(-int16(distance), 0)
|
||||
|
||||
case 'E':
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
|
||||
terminal.ActiveBuffer().MovePosition(0, int16(distance))
|
||||
terminal.ActiveBuffer().SetPosition(0, terminal.ActiveBuffer().CursorLine())
|
||||
|
||||
case 'F':
|
||||
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
terminal.ActiveBuffer().MovePosition(0, -int16(distance))
|
||||
terminal.ActiveBuffer().SetPosition(0, terminal.ActiveBuffer().CursorLine())
|
||||
|
||||
case 'G':
|
||||
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
distance, err = strconv.Atoi(params[0])
|
||||
if err != nil || params[0] == "" {
|
||||
distance = 1
|
||||
}
|
||||
}
|
||||
|
||||
terminal.ActiveBuffer().SetPosition(uint16(distance-1), terminal.ActiveBuffer().CursorLine())
|
||||
|
||||
case 'H', 'f':
|
||||
|
||||
x, y := 1, 1
|
||||
if len(params) == 2 {
|
||||
var err error
|
||||
if params[0] != "" {
|
||||
y, err = strconv.Atoi(string(params[0]))
|
||||
if err != nil {
|
||||
y = 1
|
||||
}
|
||||
}
|
||||
if params[1] != "" {
|
||||
x, err = strconv.Atoi(string(params[1]))
|
||||
if err != nil {
|
||||
x = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
terminal.ActiveBuffer().SetPosition(uint16(x-1), uint16(y-1))
|
||||
|
||||
default:
|
||||
err = fmt.Errorf("Unknown 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
|
||||
}
|
||||
|
||||
func csiDeleteHandler(params []string, intermediate string, terminal *Terminal) error {
|
||||
n := 1
|
||||
if len(params) >= 1 {
|
||||
|
|
|
@ -91,6 +91,7 @@ func (terminal *Terminal) processInput(ctx context.Context, pty chan rune) {
|
|||
handler, ok := escapeSequenceMap[b]
|
||||
|
||||
if ok {
|
||||
//terminal.logger.Debugf("Handling escape sequence: 0x%x", b)
|
||||
if err := handler(pty, terminal); err != nil {
|
||||
terminal.logger.Errorf("Error handling escape sequence: %s", err)
|
||||
}
|
||||
|
|
|
@ -11,12 +11,12 @@ import (
|
|||
func sgrSequenceHandler(params []string, intermediate string, terminal *Terminal) error {
|
||||
|
||||
if len(params) == 0 {
|
||||
return nil
|
||||
params = []string{"0"}
|
||||
}
|
||||
|
||||
for i := range params {
|
||||
switch params[i] {
|
||||
case "00", "0", "":
|
||||
case "00", "0":
|
||||
attr := terminal.ActiveBuffer().CursorAttr()
|
||||
*attr = buffer.CellAttributes{
|
||||
FgColour: terminal.config.ColourScheme.Foreground,
|
||||
|
|
Loading…
Reference in New Issue