1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
package terminal
// stepAfterEscape decides what kind of sequence an ESC begins.
func (p *Parser) stepAfterEscape(b byte) {
switch b {
case '[':
p.state = inCSI
case ']':
p.state = inOSC
p.osc = p.osc[:0]
case 'P', 'X', '^', '_':
// A device control, privacy message or application command. None is
// implemented, and all of them run to a string terminator.
p.state = inOSC
p.osc = p.osc[:0]
case '(', ')', '*', '+', '%', '#', ' ':
// A character-set or size selector: one more byte follows, and this
// emulator works in UTF-8 regardless.
p.state = ignoring
default:
p.simpleEscape(b)
p.state = ground
}
}
// simpleEscape runs the escape sequences that are one byte long.
func (p *Parser) simpleEscape(b byte) {
switch b {
case 'D': // IND, index: down one line, scrolling if need be.
p.screen.LineFeed()
case 'M': // RI, reverse index: up one line, scrolling if need be.
p.screen.ReverseLineFeed()
case 'E': // NEL, next line.
p.screen.CarriageReturn()
p.screen.LineFeed()
case '7': // DECSC, save the cursor and the style.
p.screen.SaveCursor()
case '8': // DECRC, restore them.
p.screen.RestoreCursor()
case 'c': // RIS, reset to the initial state.
p.screen.Reset()
}
// Anything else — keypad modes, character sets — changes nothing this
// emulator models, and is dropped rather than printed.
}
// stepIgnoring skips the one byte that completes a sequence being ignored.
func (p *Parser) stepIgnoring(byte) {
p.state = ground
}
// stepOSC collects an operating-system command until its terminator.
func (p *Parser) stepOSC(b byte) {
switch {
case b == bell:
p.endOSC()
case b == escape:
p.state = inOSCEscape
case len(p.osc) >= maxOSC:
// A program that has sent this much without a terminator is not going
// to send one.
p.state = ground
default:
p.osc = append(p.osc, b)
}
}
// stepOSCEscape decides whether the ESC inside an OSC ended it.
func (p *Parser) stepOSCEscape(b byte) {
if b == '\\' { // ST, the string terminator.
p.endOSC()
return
}
// It was not a terminator, so the OSC is abandoned and this byte starts
// whatever the ESC really began.
p.state = ground
p.step(escape)
p.step(b)
}
// endOSC acts on a completed operating-system command.
//
// Only the window title is kept. The rest — colour palettes, clipboard
// requests, working-directory hints — is dropped, which is what a terminal
// that does not implement them is expected to do.
func (p *Parser) endOSC() {
p.state = ground
command := string(p.osc)
p.osc = p.osc[:0]
// "0;title" sets both the icon name and the title; "2;title" the title.
for _, prefix := range []string{"0;", "2;"} {
if len(command) >= len(prefix) && command[:len(prefix)] == prefix {
p.title = command[len(prefix):]
return
}
}
}
|