escape sequence goes both way

tags: learning learning terminal software

content

  • the communication between a program and a terminal emulator is:
program -> sends bytes to tty -> terminal is tty -> terminal interprets the bytes based on some protocol -> renders the screen user sees 
  • this flow goes both ways
user interacts with terminal, key strokes, etc -> terminal sends bytes to program's stdin -> program decides what to do about it
ESC = "\x1b"
def read_key():
    # get file descriptor for stdin
    fd = sys.stdin.fileno()
    # save current terminal settings
    old = termios.tcgetattr(fd)
    try:
        # disable line buffering and echo so we get each keypress immediately
        tty.setraw(fd)
        # read one byte
        ch = sys.stdin.read(1)
        # if it's an escape byte (0x1b), an escape sequence may follow
        if ch == ESC:
            # read the next byte
            ch2 = sys.stdin.read(1)
            # CSI (Control Sequence Introducer): ESC [ means an arrow/special key
            if ch2 == "[":
                # read the final byte: A=up, B=down, C=right, D=left
                ch3 = sys.stdin.read(1)
                return ESC + "[" + ch3
        # regular key, just return the single character
        return ch
    finally:
        # restore original terminal settings
        termios.tcsetattr(fd, termios.TCSADRAIN, old)

explanation:

╭──────┬──────┬────────────────────────────────────────────────────╮
│ Byte │ Hex  │ Meaning                                            │
├──────┼──────┼────────────────────────────────────────────────────┤
│ ESC  │ 0x1b │ "what follows is a special sequence"               │
├──────┼──────┼────────────────────────────────────────────────────┤
│ [    │ 0x5b │ CSI introducer — together with ESC, signals a      │
│      │      │ control sequence                                   │
├──────┼──────┼────────────────────────────────────────────────────┤
│ A    │ 0x41 │ the specific key (A=up, B=down, C=right, D=left)   │
╰──────┴──────┴────────────────────────────────────────────────────╯
  • The function read_key
    • reads these 3 bytes one at a time
    • and returns them concatenated as a single string \x1b[A
    • assembles escape sequences
    • without it, a single arrow keypress would arrive as 3 separate read(1) calls (\x1b, [, A)

asciicast

Note

The shell (like bash) is just another program that reads from stdin, interprets commands, and launches other programs

up

down

reference