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