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 } } }