Notes on terminal emulation

Closed questions

  • Does at Tab character just move the cursor, or actually wipe out the cells it passes over?

    • The command printf 'hello\r\tworld\n' produces the text

      hello   world

      so tabs just move the cursor.

  • If you insert characters before a string that was positioned with a Tab, does the string stay where it is (like in Vim) or does it get shifted across?
    • The command printf '\033[2J\033[1;1H\thello\r\033[4@\n' produces a hello preceeded by 12 empty cells, so the fact that a given string was aligned with a tab is not recorded.

The VT52

  • A VT52 sure is simpler to begin with than a VT100.
  • The VT52 manual says that it doesn't support line-wrap, but xterm's VT52 mode does it anyway.

xterm extensions

  • There's two framebuffers, the standard screen and the alternate screen. What settings are per-framebuffer and what settings are for the terminal as a whole?

    • It seems that the cursor-position is set for the terminal as a whole:

      printf '\033[1;1H\033[?47h\033[5;5H\033[?47l'

      After running this command, the normal framebuffer is active but the cursor is located when it was put while the alternate framebuffer was active.

The Framebuffer

  • I'm thinking a good model for a framebuffer might be a list of lines, where each line has a list of text-runs, and each text-run has:
    • A starting column
    • Video attributes
    • A string to display
  • What do we do about 'background colour erase'?
    • As far as I can tell, this just means than any cell touched by an erasure escape sequence will have its background-colour set to the current background-colour.
    • I like the text-run system because it lets me distinguish 'bits of the display that have had spaces drawn on them' from 'bits of the display that have been erased' - which is important when you want to clone certain bits of xterm behaviour, such as 'selecting past the end of the line means the newline gets copied too'.
    • The downside of the text-run system is that parts of the screen with no content (such as freshly-erased lines) can't have attributes, which the BCE module requires.
    • How about we add 'trailing background colour' to the text-run state?
      • When we create a new text-run in the middle of an existing text-run, trailingBG doesn't matter because there'll be no trailing space.
      • When we create a new text-run in the middle of an existing space, trailingBG should be the same as the one that was in effect where our first character goes.
      • When we run 'erase to end of line', we remove all the text-runs from here on, and set our trailingBG to the current BG.
      • What about things like Insert/Delete Char?
    • We should also have a trailingBG attribute for each line, to cope with blank space between the beginning of the line and the first text-run.


Tabs move the cursor, rather than printing output.

Note: xterm seems to not distinguish between:

printf 'hello   world'


printf 'hello\tworld'

...both produce a three-cell gap between the two words (as opposed to three 1-cell space characters, as iTerm does).

On the other hand, when you include colours, they are different:

printf "$(tput setab 3)hello$(tput setab 4)\\t$(tput setab 5)world\\n"
printf "$(tput setab 3)hello$(tput setab 4)   $(tput setab 5)world\\n"

When the words are seperated by a tab, a gap is left coloured in the original background colour. When the words are separated by spaces, those cells have their background colour set to '4' (blue). So, uh, that makes things messy.

Printing across a word-wrap merges lines

If two consecutive physical lines are distinct (resizing the window does not affect the alignment of the second line), and text is printed across their shared boundary, the two lines are joined into one logical line, and after that resizing the window does affect the alignment of the second line.


printf 'hello\nworld\n\033[2A\033[78Gencyc\n\n'

Resizing the terminal pins the lower-left corner of the framebuffer.

In iTerm and, when making the framebuffer narrower, lines that wrap always push the lines above them up. For example, if physical and logical line 23 is exactly the width of the terminal, and the terminal is subsequently resized narrower such that it wraps onto two lines, those will be physical lines 22 and 23 (but still one logical line).


printf '\033[2J\033[1;79H01\n'
for (( i=2; i <= 23; i=i+1 )); do 
     printf '\033[79G%02i\n' $i
printf '\033[1;1H'

A double-width character at the end of a line wraps

In xterm and, printing a double-width character at the end of a line causes that character to appear at the beginning of the next line (in, such wrapping occurs dynamically, so dragging the window 1 cell wider makes it jump back to the end of the first line; in xterm the wrapping occurs statically).


printf '\033[80G病\n'
changed October 17, 2007