最近,我一直在想 vim 的用户界面是如何工作的,当你启动它时,它会占据整个终端窗口,而不仅仅是打印文本,可能需要你输入一些内容,当它完成后,会在下面打印另一行命令行。
出于好奇,这是如何工作的?
答案1
终端窗口只是一个“哑终端 - 维基百科“它只能显示一行行的字符,传统上是 24 行,每行 80 个 ASCII 字符,只在最后一行的末尾添加新字符。(想象一下打字机。)
终端逐渐变得“智能”,增加了将光标移动到特定位置、插入行、以彩色显示下一个字符等功能。
但每个制造商都有自己的做法,没有单一的标准,所以每个程序都会内置它所使用的特定硬件的知识。(我记得在 1980 年代初期的一次贸易展览会上展示了一个数据库接口程序。演示者列出了十几种它知道如何使用的常见终端类型,并说只需 X 美元,他们就可以教它处理我们公司拥有的任何其他类型。)
与此同时,BSD 开发了Termcap - 维基百科库用于他们的 UNIX 系统。它指定了一个基于文本的数据库,该数据库为每种类型的终端定义了一组标准功能,并提供了一个 C 库,该库使用此数据库根据环境变量确定要发送到终端的字符串的适当格式TERM
。当然,并非所有终端都支持所有功能,但没关系,程序会知道只使用可用的功能(例如,如果终端没有直接寻址,它可能会向上/向下移动一行并向左/向右移动一个位置)。
termcap.small · freebsd/freebsd · GitHub显示 vt100 终端的 termcap 条目示例:
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:
后来,诅咒 - 维基百科库的开发是为了简化该过程,它提供了生成具有指定值的适当代码的函数。任何程序都可以简单地输入类似 的内容tgoto(capabilities, column, row)
,然后库就会在TERM
环境变量中找到终端类型,在 termcap 数据库中查找该特定类型的功能,并生成一个字符串,其中包含用于将光标移动到指定位置的适当终端命令。
termcap(3) - OpenBSD 手册页显示一些 libcurses 函数的声明:
#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));
程序,例如vi - 维基百科使用这些库来提供可以在任何具有可寻址屏幕的终端上运行的编辑器。新类型的终端只需要定义其 termcap 条目,它就会自动且立即与vi
基于该库的任何其他程序一起工作。
最终termcap
被一个名为的改进版本所取代terminfo
,并被curses
新版本所取代ncurses
,但基本原理保持不变。
请注意,这一切都依赖于 UNIX 读取键盘输入字符的能力没有自动将其显示在终端上。当时许多操作系统都无法做到这一点。有些甚至不接受来自终端的输入,直到输入了整行(甚至整屏)。
答案2
类 Unix 操作系统上具有全屏文本用户界面(TUI)的程序使用名为的光标定位库curses
。
该库最初使用称为的终端功能数据库,termcap
后来被称为的系统取代terminfo
。
GUI 桌面中的 TUI 应用程序利用窗口调整大小信号,例如sigwinch