| 🛟 Updated. 28d5985 k33g 6h ago | 1 | package terminal |
| 2 | |
| 3 | // stepCSI collects the parameters of a control sequence and runs it when its |
| 4 | // final byte arrives. |
| 5 | // |
| 6 | // The shape is ESC [ [private] [params] [intermediate] final, where the |
| 7 | // parameters are decimal numbers separated by semicolons. |
| 8 | func (p *Parser) stepCSI(b byte) { |
| 9 | switch { |
| 10 | case b >= '0' && b <= '9': |
| 11 | p.digit(b) |
| 12 | case b == ';': |
| 13 | p.nextParam() |
| 14 | case b == '?' || b == '<' || b == '=' || b == '>': |
| 15 | p.private = b |
| 16 | case b >= ' ' && b <= '/': |
| 17 | p.intermediate = b |
| 18 | case b >= '@' && b <= '~': |
| 19 | p.runCSI(b) |
| 20 | p.state = ground |
| 21 | default: |
| 22 | // A control character inside a sequence is acted on where it stands, |
| 23 | // which is what a real terminal does with, say, a carriage return |
| 24 | // arriving mid-sequence. |
| 25 | p.stepGround(b) |
| 26 | } |
| 27 | } |
| 28 | |
| 29 | // digit adds a decimal digit to the parameter being collected. |
| 30 | func (p *Parser) digit(b byte) { |
| 31 | if len(p.params) == 0 { |
| 32 | p.params = append(p.params, 0) |
| 33 | } |
| 34 | last := len(p.params) - 1 |
| 35 | p.params[last] = p.params[last]*10 + int(b-'0') |
| 36 | } |
| 37 | |
| 38 | // nextParam starts the next parameter of the sequence. |
| 39 | func (p *Parser) nextParam() { |
| 40 | if len(p.params) == 0 { |
| 41 | p.params = append(p.params, 0) |
| 42 | } |
| 43 | if len(p.params) < maxParams { |
| 44 | p.params = append(p.params, 0) |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | // param returns parameter i, or a default when it was not given. An omitted |
| 49 | // parameter is zero in the protocol, and nearly every sequence reads zero as |
| 50 | // "the default", which is what makes this one helper enough. |
| 51 | func (p *Parser) param(i, fallback int) int { |
| 52 | if i >= len(p.params) || p.params[i] == 0 { |
| 53 | return fallback |
| 54 | } |
| 55 | return p.params[i] |
| 56 | } |
| 57 | |
| 58 | // runCSI performs the sequence its final byte names. |
| 59 | func (p *Parser) runCSI(final byte) { |
| 60 | if p.private == '?' { |
| 61 | p.runPrivateMode(final) |
| 62 | return |
| 63 | } |
| 64 | |
| 65 | switch final { |
| 66 | case 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'f', 'd': |
| 67 | p.runMovement(final) |
| 68 | case 'J': |
| 69 | p.screen.EraseDisplay(eraseMode(p.param(0, 0))) |
| 70 | case 'K': |
| 71 | p.screen.EraseLine(eraseMode(p.param(0, 0))) |
| 72 | case 'L': |
| 73 | p.screen.InsertLines(p.param(0, 1)) |
| 74 | case 'M': |
| 75 | p.screen.DeleteLines(p.param(0, 1)) |
| 76 | case '@': |
| 77 | p.screen.InsertChars(p.param(0, 1)) |
| 78 | case 'P': |
| 79 | p.screen.DeleteChars(p.param(0, 1)) |
| 80 | case 'X': |
| 81 | p.screen.EraseChars(p.param(0, 1)) |
| 82 | case 'S': |
| 83 | p.screen.ScrollUp(p.param(0, 1)) |
| 84 | case 'T': |
| 85 | p.screen.ScrollDown(p.param(0, 1)) |
| 86 | case 'r': |
| 87 | p.runSetScrollRegion() |
| 88 | case 'm': |
| 89 | p.runSGR() |
| 90 | case 's': |
| 91 | p.screen.SaveCursor() |
| 92 | case 'u': |
| 93 | p.screen.RestoreCursor() |
| 94 | } |
| 95 | // Everything else is dropped: device reports, tab-stop management, window |
| 96 | // manipulation, and the non-private modes — of which only insert mode would |
| 97 | // mean anything here, and programs that want to push text along use ICH, |
| 98 | // which is implemented. A terminal that does not implement a sequence must |
| 99 | // swallow it rather than print it. |
| 100 | } |
| 101 | |
| 102 | // runMovement performs the sequences that only move the cursor. |
| 103 | func (p *Parser) runMovement(final byte) { |
| 104 | switch final { |
| 105 | case 'A': // CUU, up |
| 106 | p.screen.MoveBy(-p.param(0, 1), 0) |
| 107 | case 'B': // CUD, down |
| 108 | p.screen.MoveBy(p.param(0, 1), 0) |
| 109 | case 'C': // CUF, forward |
| 110 | p.screen.MoveBy(0, p.param(0, 1)) |
| 111 | case 'D': // CUB, back |
| 112 | p.screen.MoveBy(0, -p.param(0, 1)) |
| 113 | case 'E': // CNL, down and to the first column |
| 114 | p.screen.MoveBy(p.param(0, 1), 0) |
| 115 | p.screen.CarriageReturn() |
| 116 | case 'F': // CPL, up and to the first column |
| 117 | p.screen.MoveBy(-p.param(0, 1), 0) |
| 118 | p.screen.CarriageReturn() |
| 119 | case 'G': // CHA, to a column |
| 120 | p.screen.MoveTo(p.screen.Cursor().Row, p.param(0, 1)-1) |
| 121 | case 'd': // VPA, to a row |
| 122 | p.screen.MoveTo(p.param(0, 1)-1, p.screen.Cursor().Col) |
| 123 | case 'H', 'f': // CUP, HVP, to a row and column |
| 124 | p.screen.MoveTo(p.param(0, 1)-1, p.param(1, 1)-1) |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | // runSetScrollRegion performs DECSTBM. With no parameters it opens the region |
| 129 | // back up to the whole screen. |
| 130 | func (p *Parser) runSetScrollRegion() { |
| 131 | _, height := p.screen.Size() |
| 132 | |
| 133 | top := p.param(0, 1) - 1 |
| 134 | bottom := p.param(1, height) - 1 |
| 135 | if bottom <= top { |
| 136 | bottom = height - 1 |
| 137 | } |
| 138 | |
| 139 | p.screen.SetScrollRegion(top, bottom) |
| 140 | } |
| 141 | |
| 142 | // eraseMode turns the number in an erase sequence into a mode, treating |
| 143 | // anything unexpected as "from the cursor onwards". |
| 144 | func eraseMode(n int) EraseMode { |
| 145 | switch n { |
| 146 | case 1: |
| 147 | return EraseToStart |
| 148 | case 2, 3: |
| 149 | return EraseAll |
| 150 | default: |
| 151 | return EraseToEnd |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | // runPrivateMode performs the DEC private set and reset sequences, ESC [ ? … h |
| 156 | // and ESC [ ? … l. |
| 157 | func (p *Parser) runPrivateMode(final byte) { |
| 158 | if final != 'h' && final != 'l' { |
| 159 | return |
| 160 | } |
| 161 | set := final == 'h' |
| 162 | |
| 163 | for i := range max(len(p.params), 1) { |
| 164 | p.setPrivateMode(p.param(i, 0), set) |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | // The DEC private modes this emulator implements. |
| 169 | const ( |
| 170 | modeApplicationCursor = 1 // DECCKM |
| 171 | modeAutoWrap = 7 // DECAWM |
| 172 | modeCursorVisible = 25 // DECTCEM |
| 173 | modeAlternate47 = 47 // the oldest alternate-screen switch |
| 174 | modeAlternate1047 = 1047 // alternate screen |
| 175 | modeSaveCursor1048 = 1048 // save and restore the cursor |
| 176 | modeAlternate1049 = 1049 // save the cursor, then the alternate screen |
| 177 | ) |
| 178 | |
| 179 | // setPrivateMode turns one DEC private mode on or off. |
| 180 | func (p *Parser) setPrivateMode(mode int, set bool) { |
| 181 | switch mode { |
| 182 | case modeApplicationCursor: |
| 183 | p.screen.SetApplicationCursor(set) |
| 184 | case modeAutoWrap: |
| 185 | p.screen.SetAutoWrap(set) |
| 186 | case modeCursorVisible: |
| 187 | p.screen.SetCursorVisible(set) |
| 188 | case modeAlternate47, modeAlternate1047: |
| 189 | p.screen.UseAlternate(set) |
| 190 | case modeSaveCursor1048: |
| 191 | p.saveOrRestore(set) |
| 192 | case modeAlternate1049: |
| 193 | // The one programs actually use: save the cursor on the way in and put |
| 194 | // it back on the way out, around the alternate screen. |
| 195 | if set { |
| 196 | p.screen.SaveCursor() |
| 197 | p.screen.UseAlternate(true) |
| 198 | return |
| 199 | } |
| 200 | p.screen.UseAlternate(false) |
| 201 | p.screen.RestoreCursor() |
| 202 | } |
| 203 | // Every other private mode — mouse reporting, bracketed paste, focus |
| 204 | // events — is accepted and ignored. The program is told nothing, which is |
| 205 | // exactly what a terminal without those features looks like. |
| 206 | } |
| 207 | |
| 208 | // saveOrRestore saves the cursor when setting a mode and restores it when |
| 209 | // resetting one. |
| 210 | func (p *Parser) saveOrRestore(set bool) { |
| 211 | if set { |
| 212 | p.screen.SaveCursor() |
| 213 | return |
| 214 | } |
| 215 | p.screen.RestoreCursor() |
| 216 | } |