The terminal window is simply an emulation of a "Dumb terminal - Wikipedia" which can do nothing but display lines of characters, traditionally 24 lines of 80 ASCII characters, with new characters being added only at the end of the last line.
(Think of a typewriter.)
Terminals gradually got "smarter", adding the ability to move the cursor to a specific location, insert lines, display the next character in colour, etc.
But every manufacturer did it in their own way, and there was no single standard, so every program would have built into it the knowledge of the specific hardware it was being used with. (I remember being shown a database interface program at a trade show in the early 1980s. The presenter gave a list of a dozen or so common terminal types that it knew how to work with, and said that for only $X they could teach it to handle whatever other type we had at our company.)
Meanwhile, BSD had developed the Termcap - Wikipedia library for their UNIX system.
It specified a text-based database that defined a set of standard capabilities for each type of terminal, and provided a C library that used this database to determine the appropriate format of the string to send to the terminal based on the TERM
environment variable. Not all terminals supported all capabilities of course, but that was okay, programs would know to use only those capabilities that were available (e.g. if a terminal didn't have direct addressing, it might have move up/down one line and move left/right one position).
termcap.small · freebsd/freebsd · GitHub shows a sample termcap entry for a vt100 terminal:
vt100|dec-vt100|vt100-am|vt100am|dec vt100:\
:do=2\E[B:co#80:li#24:cl=50\E[H\E[J:sf=2*\ED:\
:le=^H:bs:am:cm=5\E[%i%d;%dH:nd=2\E[C:up=2\E[A:\
:ce=3\E[K:cd=50\E[J:so=2\E[7m:se=2\E[m:us=2\E[4m:ue=2\E[m:\
:md=2\E[1m:mr=2\E[7m:mb=2\E[5m:me=2\E[m:\
:is=\E>\E[?1;3;4;5l\E[?7;8h\E[1;24r\E[24;1H:\
:if=/usr/share/tabset/vt100:nw=2\EE:ho=\E[H:\
:as=2\E(0:ae=2\E(B:\
:ac=``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||:\
:rs=\E>\E[?1;3;4;5l\E[?7;8h:ks=\E[?1h\E=:ke=\E[?1l\E>:\
:ku=\EOA:kd=\EOB:kr=\EOC:kl=\EOD:kb=\177:\
:k0=\EOy:k1=\EOP:k2=\EOQ:k3=\EOR:k4=\EOS:k5=\EOt:\
:k6=\EOu:k7=\EOv:k8=\EOl:k9=\EOw:k;=\EOx:@8=\EOM:\
:K1=\EOq:K2=\EOr:K3=\EOs:K4=\EOp:K5=\EOn:pt:sr=2*\EM:xn:\
:sc=2\E7:rc=2\E8:cs=5\E[%i%d;%dr:UP=2\E[%dA:DO=2\E[%dB:RI=2\E[%dC:\
:LE=2\E[%dD:ct=2\E[3g:st=2\EH:ta=^I:ms:bl=^G:cr=^M:eo:it#8:\
:RA=\E[?7l:SA=\E[?7h:po=\E[5i:pf=\E[4i:
Later, a curses - Wikipedia library was developed to simplify the process, providing functions to generate the appropriate codes with specified values. Any program could simply say something like tgoto(capabilities, column, row)
, and the library would find the terminal type in the TERM
environment variable, look up the capabilities for that specific type in the termcap database, and generate a string containing the appropriate terminal commands for moving the cursor to the specified position.
termcap(3) - OpenBSD manual pages shows declarations of a few libcurses functions:
#include <curses.h>
#include <term.h>
extern char PC;
extern char * UP;
extern char * BC;
extern short ospeed;
int tgetent(char *bp, const char *name);
int tgetflag(char *id);
int tgetnum(char *id);
char *tgetstr(char *id, char **area);
char *tgoto(const char *cap, int col, int row);
int tputs(const char *str, int affcnt, int (*putc)(int));
Programs, such as vi - Wikipedia, used these libraries to provide an editor that would work on any terminal with an addressable screen. A new type of terminal required only defining its termcap entry, and it would automatically, and instantly, work with vi
and any other program based on that library.
Eventually termcap
was replaced by an improved version called terminfo
, and curses
was replaced by a new version ncurses
, but the underlying principles remained the same.
Note that this all relies on UNIX's ability to read a character entered from the keyboard without automatically displaying it on the terminal. Many operating systems at the time could not do this. Some wouldn't even accept input from the terminal until an entire line (or even screen) had been entered.