diff --git a/input/da1.go b/input/da1.go index d7529d54..7fb2f308 100644 --- a/input/da1.go +++ b/input/da1.go @@ -8,11 +8,10 @@ type PrimaryDeviceAttributesEvent []uint func parsePrimaryDevAttrs(csi *ansi.CsiSequence) Event { // Primary Device Attributes da1 := make(PrimaryDeviceAttributesEvent, len(csi.Params)) - csi.Range(func(i int, p int, hasMore bool) bool { - if !hasMore { + for i, p := range csi.Params { + if !ansi.Parameter(p).HasMore() { da1[i] = uint(p) } - return true - }) + } return da1 } diff --git a/input/go.mod b/input/go.mod index 80b89f91..ba9c7ab0 100644 --- a/input/go.mod +++ b/input/go.mod @@ -3,7 +3,7 @@ module github.com/charmbracelet/x/input go 1.18 require ( - github.com/charmbracelet/x/ansi v0.4.5 + github.com/charmbracelet/x/ansi v0.5.3-0.20241204155720-fa6b43c98350 github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f github.com/muesli/cancelreader v0.2.2 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e @@ -11,6 +11,7 @@ require ( ) require ( + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect ) diff --git a/input/go.sum b/input/go.sum index 3586cddb..0603ffe5 100644 --- a/input/go.sum +++ b/input/go.sum @@ -1,7 +1,11 @@ github.com/charmbracelet/x/ansi v0.4.5 h1:LqK4vwBNaXw2AyGIICa5/29Sbdq58GbGdFngSexTdRM= github.com/charmbracelet/x/ansi v0.4.5/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/ansi v0.5.3-0.20241204155720-fa6b43c98350 h1:LEZviFqdD7htj5oKO6o053ko6s1H+LAiOd0nbKZa1cE= +github.com/charmbracelet/x/ansi v0.5.3-0.20241204155720-fa6b43c98350/go.mod h1:KBUFw1la39nl0dLl10l5ORDAqGXaeurTQmwyyVKse/Q= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= diff --git a/input/kitty.go b/input/kitty.go index f2e390ef..21466477 100644 --- a/input/kitty.go +++ b/input/kitty.go @@ -5,6 +5,7 @@ import ( "unicode/utf8" "github.com/charmbracelet/x/ansi" + "github.com/charmbracelet/x/ansi/parser" ) // KittyKeyboardEvent represents Kitty keyboard progressive enhancement flags. @@ -213,7 +214,11 @@ func parseKittyKeyboard(csi *ansi.CsiSequence) Event { var isRelease bool key := Key{} - if params := csi.Subparams(0); len(params) > 0 { + pparams := make([]int, 0, len(csi.Params)) + for _, p := range csi.Params { + pparams = append(pparams, int(p)) + } + if params := parser.Subparams(pparams, 0); len(params) > 0 { code := params[0] if sym, ok := kittyKeyMap[code]; ok { key.Sym = sym @@ -253,7 +258,8 @@ func parseKittyKeyboard(csi *ansi.CsiSequence) Event { } } } - if params := csi.Subparams(1); len(params) > 0 { + + if params := parser.Subparams(pparams, 1); len(params) > 0 { mod := params[0] if mod > 1 { key.Mod = fromKittyMod(mod - 1) diff --git a/input/mouse.go b/input/mouse.go index 99e6de5e..dcdc192a 100644 --- a/input/mouse.go +++ b/input/mouse.go @@ -137,10 +137,11 @@ var mouseSGRRegex = regexp.MustCompile(`(\d+);(\d+);(\d+)([Mm])`) // // https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Extended-coordinates func parseSGRMouseEvent(csi *ansi.CsiSequence) Event { - x := csi.Param(1) - y := csi.Param(2) + b, _ := csi.Param(0, -1) + x, _ := csi.Param(1, -1) + y, _ := csi.Param(2, -1) release := csi.Command() == 'm' - mod, btn, _, isMotion := parseMouseButton(csi.Param(0)) + mod, btn, _, isMotion := parseMouseButton(b) // (1,1) is the upper left. We subtract 1 to normalize it to (0,0). x-- diff --git a/input/mouse_test.go b/input/mouse_test.go index f9ea9e31..7d8750f5 100644 --- a/input/mouse_test.go +++ b/input/mouse_test.go @@ -312,8 +312,12 @@ func TestParseSGRMouseEvent(t *testing.T) { re = 'm' } return &ansi.CsiSequence{ - Params: []int{b, x + 1, y + 1}, - Cmd: int(re) | ('<' << parser.MarkerShift), + Params: []ansi.Parameter{ + ansi.Parameter(b), + ansi.Parameter(x + 1), + ansi.Parameter(y + 1), + }, + Cmd: ansi.Command(int(re) | ('<' << parser.MarkerShift)), } } diff --git a/input/parse.go b/input/parse.go index 1d7fe2ef..172dd487 100644 --- a/input/parse.go +++ b/input/parse.go @@ -160,7 +160,7 @@ func parseCsi(b []byte) (int, Event) { } var csi ansi.CsiSequence - var params [parser.MaxParamsSize]int + var params [parser.MaxParamsSize]ansi.Parameter var paramsLen int var i int @@ -173,7 +173,7 @@ func parseCsi(b []byte) (int, Event) { // Initial CSI byte if i < len(b) && b[i] >= '<' && b[i] <= '?' { - csi.Cmd |= int(b[i]) << parser.MarkerShift + csi.Cmd = ansi.Command(int(csi.Cmd) | int(b[i])<= len(b) || b[i] < 0x40 || b[i] > 0x7E { @@ -228,7 +228,7 @@ func parseCsi(b []byte) (int, Event) { } // Add the final byte - csi.Cmd |= int(b[i]) + csi.Cmd = ansi.Command(int(csi.Cmd) | int(b[i])) i++ csi.Params = params[:paramsLen] @@ -243,21 +243,26 @@ func parseCsi(b []byte) (int, Event) { if paramsLen != 2 { return i, UnknownCsiEvent(b[:i]) } - return i, ReportModeEvent{Mode: csi.Param(0), Value: csi.Param(1)} + mode, _ := csi.Param(0, 0) + value, _ := csi.Param(1, 0) + return i, ReportModeEvent{Mode: mode, Value: value} } case 'c': // Primary Device Attributes return i, parsePrimaryDevAttrs(&csi) case 'u': // Kitty keyboard flags - if param := csi.Param(0); param != -1 { + param, _ := csi.Param(0, -1) + if param != -1 { return i, KittyKeyboardEvent(param) } case 'R': // This report may return a third parameter representing the page // number, but we don't really need it. if paramsLen >= 2 { - return i, CursorPositionEvent{Row: csi.Param(0), Column: csi.Param(1)} + row, _ := csi.Param(0, 0) + col, _ := csi.Param(1, 0) + return i, CursorPositionEvent{Row: row, Column: col} } } return i, UnknownCsiEvent(b[:i]) @@ -276,11 +281,13 @@ func parseCsi(b []byte) (int, Event) { switch cmd { case 'm': // XTerm modifyOtherKeys - if paramsLen != 2 || csi.Param(0) != 4 { + p0, _ := csi.Param(0, 0) + if paramsLen != 2 || p0 != 4 { return i, UnknownCsiEvent(b[:i]) } - return i, ModifyOtherKeysEvent(csi.Param(1)) + p1, _ := csi.Param(1, 0) + return i, ModifyOtherKeysEvent(p1) default: return i, UnknownCsiEvent(b[:i]) } @@ -309,8 +316,10 @@ func parseCsi(b []byte) (int, Event) { // // For a non ambiguous cursor position report, use // [ansi.RequestExtendedCursorPosition] (DECXCPR) instead. - if csi.Param(0) != 1 { - return i, CursorPositionEvent{Row: csi.Param(0), Column: csi.Param(1)} + row, _ := csi.Param(0, 1) + col, _ := csi.Param(1, 1) + if row != 1 { + return i, CursorPositionEvent{Row: row, Column: col} } fallthrough @@ -332,10 +341,12 @@ func parseCsi(b []byte) (int, Event) { case 'Z': k = KeyPressEvent{Sym: KeyTab, Mod: ModShift} } - if paramsLen > 1 && csi.Param(0) == 1 { + p0, _ := csi.Param(0, 0) + if paramsLen > 1 && p0 == 1 { // CSI 1 ; A + p1, _ := csi.Param(1, 0) if paramsLen > 1 { - k.Mod |= KeyMod(csi.Param(1) - 1) + k.Mod |= KeyMod(p1 - 1) } } return i, k @@ -350,7 +361,9 @@ func parseCsi(b []byte) (int, Event) { if paramsLen != 2 { return i, UnknownCsiEvent(b[:i]) } - return i, ReportModeEvent{Mode: csi.Param(0), Value: csi.Param(1)} + mod, _ := csi.Param(0, 0) + value, _ := csi.Param(1, 0) + return i, ReportModeEvent{Mode: mod, Value: value} case 'u': // Kitty keyboard protocol & CSI u (fixterms) if paramsLen == 0 { @@ -363,18 +376,19 @@ func parseCsi(b []byte) (int, Event) { return i, UnknownCsiEvent(b[:i]) } - rc := uint16(csi.Param(5)) - if rc == 0 { - rc = 1 - } - + rc, _ := csi.Param(5, 1) + vkc, _ := csi.Param(0, -1) + vsc, _ := csi.Param(1, -1) + uc, _ := csi.Param(2, -1) + kd, _ := csi.Param(3, -1) + cs, _ := csi.Param(4, -1) event := parseWin32InputKeyEvent( - coninput.VirtualKeyCode(csi.Param(0)), // Vk wVirtualKeyCode - coninput.VirtualKeyCode(csi.Param(1)), // Sc wVirtualScanCode - rune(csi.Param(2)), // Uc UnicodeChar - csi.Param(3) == 1, // Kd bKeyDown - coninput.ControlKeyState(csi.Param(4)), // Cs dwControlKeyState - rc, // Rc wRepeatCount + coninput.VirtualKeyCode(vkc), // Vk VirtualKeyCode + coninput.VirtualKeyCode(vsc), // Sc VirtualScanCode + rune(uc), // Uc UnicodeChar + kd == 1, // Kd bKeyDown + coninput.ControlKeyState(cs), // Cs dwControlKeyState + uint16(rc), // Rc wRepeatCount ) if event == nil { @@ -387,7 +401,7 @@ func parseCsi(b []byte) (int, Event) { return i, UnknownCsiEvent(b[:i]) } - param := csi.Param(0) + param, _ := csi.Param(0, -1) switch cmd { case '~': switch param { @@ -454,7 +468,8 @@ func parseCsi(b []byte) (int, Event) { // modifiers if paramsLen > 1 { - k.Mod |= KeyMod(csi.Param(1) - 1) + mod, _ := csi.Param(1, 0) + k.Mod |= KeyMod(mod - 1) } // Handle URxvt weird keys @@ -669,7 +684,7 @@ func parseDcs(b []byte) (int, Event) { return 2, KeyPressEvent{Rune: rune(b[1]), Mod: ModAlt} } - var params [16]int + var params [16]ansi.Parameter var paramsLen int var dcs ansi.DcsSequence @@ -684,7 +699,7 @@ func parseDcs(b []byte) (int, Event) { // initial DCS byte if i < len(b) && b[i] >= '<' && b[i] <= '?' { - dcs.Cmd |= int(b[i]) << parser.MarkerShift + dcs.Cmd = ansi.Command(int(dcs.Cmd) | int(b[i])<= len(b) || b[i] < 0x40 || b[i] > 0x7E { @@ -729,7 +744,7 @@ func parseDcs(b []byte) (int, Event) { } // Add the final byte - dcs.Cmd |= int(b[i]) + dcs.Cmd = ansi.Command(int(dcs.Cmd) | int(b[i])) i++ start := i // start of the sequence data @@ -757,7 +772,7 @@ func parseDcs(b []byte) (int, Event) { switch dcs.Intermediate() { case '+': // XTGETTCAP responses - switch param := dcs.Param(0); param { + switch param, _ := dcs.Param(0, -1); param { case 0, 1: tc := parseTermcap(b[start:end]) // XXX: some terminals like KiTTY report invalid responses with diff --git a/input/xterm.go b/input/xterm.go index 00a67514..3a66a35c 100644 --- a/input/xterm.go +++ b/input/xterm.go @@ -6,8 +6,10 @@ import ( func parseXTermModifyOtherKeys(csi *ansi.CsiSequence) Event { // XTerm modify other keys starts with ESC [ 27 ; ; ~ - mod := KeyMod(csi.Param(1) - 1) - r := rune(csi.Param(2)) + m, _ := csi.Param(1, 0) + mod := KeyMod(m - 1) + p, _ := csi.Param(2, 0) + r := rune(p) switch r { case ansi.BS: