Skip to content

Commit

Permalink
fix(terminal): prepare for DECRQM, XTGETTCAP, CSI U. #74, #88, #89
Browse files Browse the repository at this point in the history
  • Loading branch information
ericwq committed Jul 28, 2024
1 parent ca05211 commit 04c931c
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 2 deletions.
66 changes: 64 additions & 2 deletions terminal/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package terminal

import (
"encoding/base64"
"encoding/hex"
"fmt"
"sort"
"strconv"
Expand Down Expand Up @@ -51,6 +52,7 @@ const (
CSI_DECSCL
CSI_DECSCUSR
CSI_privSM
CSI_DECRQM
CSI_DECSTBM
CSI_DECSTR
CSI_ECMA48_SL
Expand Down Expand Up @@ -81,7 +83,9 @@ const (
CSI_VPR
CSI_XTMODKEYS
CSI_XTWINOPS
CSI_U
DCS_DECRQSS
DCS_XTGETTCAP
ESC_BI
ESC_DCS
ESC_DECALN
Expand Down Expand Up @@ -139,6 +143,7 @@ var strHandlerID = [...]string{
"csi_decscl",
"csi_decscusr",
"csi_decset",
"csi_decrqm",
"csi_decstbm",
"csi_decstr",
"csi_ecma48_SL",
Expand Down Expand Up @@ -169,7 +174,9 @@ var strHandlerID = [...]string{
"csi_vpr",
"csi_xtmodkeys",
"csi_xtwinops",
"csi_u",
"dcs_decrqss",
"dcs_xtgettcap",
"esc_bi",
"esc_dcs",
"esc_decaln",
Expand Down Expand Up @@ -1499,7 +1506,7 @@ func hdl_csi_privSM(emu *Emulator, params []int) {
case 2:
resetCharsetState(&emu.charsetState) // Designate USASCII for character sets G0-G3 (DECANM), VT100, and set VT100 mode.
emu.setCompatLevel(CompatLevel_VT400)
// emu.framebuffer.DS.compatLevel = CompatLevelVT400
// util.Logger.Warn("DECANM", "changeTo", "Designate USASCII for character sets G0-G3 (DECANM), VT100, and set VT100 mode", "params", params)
case 3:
emu.switchColMode(ColMode_C132)
case 4:
Expand Down Expand Up @@ -1593,6 +1600,18 @@ func hdl_csi_privSM(emu *Emulator, params []int) {
}
}

// TODO: implement it
func hdl_csi_decrqm(emu *Emulator, params []int) {
resp := fmt.Sprintf("\x1B?%d;%d$y", params[0], 0)
util.Logger.Warn("DECRQM is not implemented!")
emu.writePty(resp)
}

// TODO: implement it
func hdl_csi_u(_ *Emulator, _ []int) {
util.Logger.Warn("CSI U is not implemented!")
}

// CSI ? Pm l
// DEC Private Mode Reset (DECRST).
func hdl_csi_privRM(emu *Emulator, params []int) {
Expand All @@ -1604,7 +1623,7 @@ func hdl_csi_privRM(emu *Emulator, params []int) {
case 2:
resetCharsetState(&emu.charsetState) // Designate VT52 mode (DECANM), VT100.
emu.setCompatLevel(CompatLevel_VT52)
// emu.framebuffer.DS.compatLevel = CompatLevel_VT52
// util.Logger.Warn("DECANM", "changeTo", "Designate VT52 mode", "params", params)
case 3:
emu.switchColMode(ColMode_C80)
case 4:
Expand Down Expand Up @@ -1760,6 +1779,49 @@ func hdl_dcs_decrqss(emu *Emulator, arg string) {
}
}

/*
DCS + q Pt ST
Request Termcap/Terminfo String (XTGETTCAP), xterm. The
string following the "q" is a list of names encoded in
hexadecimal (2 digits per character) separated by ; which
correspond to termcap or terminfo key names.
A few special features are also recognized, which are not key
names:
o Co for termcap colors (or colors for terminfo colors), and
o TN for termcap name (or name for terminfo name).
o RGB for the ncurses direct-color extension.
Only a terminfo name is provided, since termcap
applications cannot use this information.
xterm responds with
DCS 1 + r Pt ST for valid requests, adding to Pt an = , and
the value of the corresponding string that xterm would send,
or
DCS 0 + r ST for invalid requests.
The strings are encoded in hexadecimal (2 digits per
character). If more than one name is given, xterm replies
with each name/value pair in the same response. An invalid
name (one not found in xterm's tables) ends processing of the
list of names.
*/
func hdl_dcs_xtgettcap(_ *Emulator, arg string) {
name := strings.Split(arg, ";")
for i := range name {
dst := make([]byte, hex.DecodedLen(len(name[i])))
n, err := hex.Decode(dst, []byte(name[i]))
if err != nil {
util.Logger.Warn("XTGETTCAP decode error", "i", i, "name[i]", name[i], "error", err)
}
util.Logger.Warn("XTGETTCAP", "i", i, "name[i]", dst[:n])
// TODO: lookup terminfo and return the result
}
util.Logger.Warn("XTGETTCAP is not implemented!", "arg", arg, "name", name)
}

// CSI Pl ; Pr s
//
// Set left and right margins (DECSLRM), VT420 and up. This is
Expand Down
41 changes: 41 additions & 0 deletions terminal/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -1189,6 +1189,30 @@ func (p *Parser) handle_privRM() (hd *Handler) {
return hd
}

func (p *Parser) handle_DECRQM() (hd *Handler) {
params := p.copyArgs()

hd = &Handler{id: CSI_DECRQM, ch: p.ch, sequence: p.historyString()}
hd.handle = func(emu *Emulator) {
hdl_csi_decrqm(emu, params)
}

p.setState(InputState_Normal)
return hd
}

func (p *Parser) handle_CSI_U() (hd *Handler) {
params := p.copyArgs()

hd = &Handler{id: CSI_U, ch: p.ch, sequence: p.historyString()}
hd.handle = func(emu *Emulator) {
hdl_csi_u(emu, params)
}

p.setState(InputState_Normal)
return hd
}

// Set Top and Bottom Margins
func (p *Parser) handle_DECSTBM() (hd *Handler) {
params := p.copyArgs()
Expand Down Expand Up @@ -1230,6 +1254,11 @@ func (p *Parser) handle_DCS() (hd *Handler) {
hd.handle = func(emu *Emulator) {
hdl_dcs_decrqss(emu, arg)
}
} else if strings.HasPrefix(arg, "+q") {
hd = &Handler{id: DCS_XTGETTCAP, ch: p.ch, sequence: p.historyString()}
hd.handle = func(emu *Emulator) {
hdl_dcs_xtgettcap(emu, arg[2:])
}
} else {
util.Logger.Warn("DCS", "unimplement", "DCS", "arg", arg, "seq", p.historyString())
}
Expand Down Expand Up @@ -1973,6 +2002,18 @@ func (p *Parser) ProcessInput(chs ...rune) (hd *Handler) {
hd = p.handle_privSM() // DECSET
case 'l':
hd = p.handle_privRM() // DECRST
case 'u':
hd = p.handle_CSI_U()
case '$':
p.argBuf.WriteRune(ch)
case 'p':
p.argBuf.WriteRune(ch)
if p.argBuf.String() == "$p" {
hd = p.handle_DECRQM()
} else {
p.unhandledInput()
}
p.argBuf.Reset()
default:
p.unhandledInput()
}
Expand Down
53 changes: 53 additions & 0 deletions terminal/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4928,3 +4928,56 @@ func TestMixSequence(t *testing.T) {
})
}
}

func TestNvimClean(t *testing.T) {
tc := []struct {
label string
seq string
hdIDs []int
}{
{
"first", "\x1b[?1049h\x1b[22;0;0t\x1b[?1h\x1b=\x1b[H\x1b[2J\x1b[?2004h\x1b[?2026$p\x1b[0m\x1b[4:3m\x1bP$qm\x1b\\\x1b[?u\x1b[c\x1b[?25h",

[]int{
CSI_privSM, CSI_XTWINOPS, CSI_privSM, ESC_DECKPAM, CSI_CUP, CSI_ED, CSI_privSM,
CSI_DECRQM, CSI_SGR, CSI_SGR, DCS_DECRQSS, CSI_U, CSI_priDA, CSI_privSM,
},
},
{
"second", "\x1b]11;?\a\x1bP+q5463;524742;73657472676266;73657472676262\x1b\\\x1b[0m\x1b[48;2;1;2;3m\x1bP$qm\x1b\\",
[]int{OSC_10_11_12_17_19, DCS_XTGETTCAP, CSI_SGR, CSI_SGR, DCS_DECRQSS},
},
}

p := NewParser()
emu := NewEmulator3(8, 4, 0)
for _, v := range tc {
t.Run(v.label, func(t *testing.T) {
// process control sequence
hds := make([]*Handler, 0, 16)
hds = p.processStream(v.seq, hds)

if len(hds) != len(v.hdIDs) {
for i := range hds {
if i <= len(v.hdIDs)-1 && hds[i].id != v.hdIDs[i] {
t.Logf("NvimClean %s: i=%d, got %s, expect =%s, seq=%q\n",
v.label, i, strHandlerID[hds[i].id], strHandlerID[v.hdIDs[i]], hds[i].sequence)
} else {
t.Logf("NvimClean %s: i=%d, got %s, seq=%q\n",
v.label, i, strHandlerID[hds[i].id], hds[i].sequence)
}
}
t.Fatalf("%s got %d handlers, expect %d handlers", v.label, len(hds), len(v.hdIDs))
}

// handle the control sequence
for j, hd := range hds {
hd.handle(emu)
if hd.id != v.hdIDs[j] { // validate the control sequences id
t.Fatalf("%s: seq=%q \n hd.index=%d expect %s, got %s\n",
v.label, v.seq, j, strHandlerID[v.hdIDs[j]], strHandlerID[hd.id])
}
}
})
}
}

0 comments on commit 04c931c

Please sign in to comment.