曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 9118|回复: 3
打印 上一主题 下一主题

终端输出(Terminal Output)

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34397
跳转到指定楼层
楼主
发表于 2009-3-8 18:34:37 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
使用 termios结构,可以控制键盘的输入;如果对程序输出到屏幕中的内容也有同样的控制权那就最好不过了!用 printf 语句可以把字符输出到屏幕上,但是没法把输出的数据放在特定的位置。

终端类型(Terminal Type)

许多UNIX 需要用到终端才能被使用。然而时至今日,在许多情况下,“终端”也许实际指的就是一个运行着终端仿真程序的PC或者是一个在窗口环境中的终端应用程序(如X11中的xterm)。

历史上,曾经有不同的生产商制造了大量的的硬件终端。尽管它们都使用 escape 转义序列(escape sequences)(一个以 escape 字符开头的字符串)来控制光标的位置以及终端的其它属性,如黑体(bold)和闪烁(blinking)等,但它们通常没有什么很好的标准化。一些较老的终端还有不同的卷屏功能,在 backspace 动作时,可能会或许并不会进行删除动作,可以说是千差万别。

escape 转义序列有一个ANSI 标准(通常是以用在数字设备公司的VT系列终端作为基础,但并没有完全相同)。许多软件终端程序提供一个标准的硬件终端仿真,通常见到的是 VT100VT200ANSI,有时也有其它的。

对于那些希望写些可以控制屏幕以及在不同的终端上运行的程序的程序员来说,千差万别的硬件终端会是一个主要的问题。例如,一个ANSI 终端使用转义序列 Esa-[-A 移向光标所在行的上一行,而在 ADM-3a 终端(几年前使用非常普遍)只需要使用一个控制字符 Ctrl-K 。

如果写一个能够处理多种不同类型的终端看起来是非常令人恐怖的事情,因为程序中需要为每一个类型的终端添加相应的代码。

所以,顺理成章的(not surprisingly)就出现了一个叫做 terminfo 的工具包。这样一来,就不需要有为了迎合每一种终端而专门去写一个程序。使用这么一个工具包,程序会对一个终端类型数据库进行查找以得到正确的设置信息。在大多数现代的 UNIX 系统中,包括Linux,这个数据库已经和另外一个名为 curses 的函数库软件包集成在一起。

在 linux 系统上, curses 软件包通常被实现为 ncurses 数据库。在编写程序时要包括上 提供了对 terminfo 函数进行模型化定义的头文件 ncurses.h 才行,而 terminfo 函数本身实际是在它们自己的头文件 term.h 里定义的。更正确的说,一般情况总是这样的。对于比较新的 Linux 版本来说, terminfo 和 ncurses 有逐渐融合的趋势,许多程序在使用 terminfo 函数时也必须包括上 ncurses 头文件。

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34397
沙发
 楼主| 发表于 2009-3-9 01:47:59 | 只看该作者

确定终端类型

linux 里有一个 TERM 变量,它被设置为当前使用中的终端类型。它通常是在登录的时候由系统自动设置的。系统管理员可以为每一个直接与系统连接的终端设置一个默认终端类型,而远程、网络用户也许需要为之安排一个提示符让他们选择终端的类型。TERM 变量可以通过 telnet 进行协调,由 rlogin 功能传递。

使用以下命令可以查看自己正在使用的终端在系统的眼里到底是哪种类型:
[root@localhost C]# echo $TERM
xterm
在上面的情况里,shell 是从一个名为 xterm 程序开始运行的,xterm 是一个 X 窗口系统下的终端仿真器。

terminfo 工具包里包含着一个拥有大量终端性能指标和转义字符这些信息构成的数据库,并且提供了统一的程序设计接口。有了这个工具包,程序设计工作得到极大的简化,并且能够随着数据库的扩展适应未来的终端,对不同类型的终端的支持不再需要由程序自己来提供。

terminfo 性能指标由“属性”来进行描述。这些“属性”被存储在一系列经过编译的 terminfo 文件里,一般被保存在 /usr/lib/terminfo 或者 /usr/share/terminfo 。对于每一个终端(也包括许多中打印机,它们也可以被定义在 terminfo 中),都有一个定义了性能指标和如何去访问这些特性的文件。避免创建一个非常大的目录,实际的文件被存放在子目录下面,子目录名简单的用终端类型的第一个字母来命名。因此,VT100型终端的定义就放在文件 ...terminfo/v/vt100 里。

对于每一种终端类型都对应有一个 terminfo 文件,文件格式为可读的源代码,然后用 tic 命令将其编译成一个更加紧凑(compact)有效的格式以方便应用程序使用。令人奇怪的是,X/Open 技术规范提到了源文件和编译格式的定义,但并没有提及从源代码转换为编译格式的 tic 命令。你可以使用 infocmp 程序打印出一个已编译 terminfo 条目的可读版本。

以下是对应于 VT100 终端的 terminfo 文件:
[root@localhost ~]# infocmp vt100
#       Reconstructed via infocmp from file: /usr/share/terminfo/v/vt100
vt100|vt100-am|dec vt100 (w/advanced video),
        am, mc5i, msgr, xenl, xon,
        cols#80, it#8, lines#24, vt#3,
        acsc=``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
        bel=^G, blink=\\E[5m$<2>, bold=\\E[1m$<2>,
        clear=\\E[H\\E[J$<50>, cr=^M, csr=\\E[%i%p1%d;%p2%dr,
        cub=\\E[%p1%dD, cub1=^H, cud=\\E[%p1%dB, cud1=^J,
        cuf=\\E[%p1%dC, cuf1=\\E[C$<2>,
        cup=\\E[%i%p1%d;%p2%dH$<5>, cuu=\\E[%p1%dA,
        cuu1=\\E[A$<2>, ed=\\E[J$<50>, el=\\E[K$<3>, el1=\\E[1K$<3>,
        enacs=\\E(B\\E)0, home=\\E[H, ht=^I, hts=\\EH, ind=^J, ka1=\\EOq,
        ka3=\\EOs, kb2=\\EOr, kbs=^H, kc1=\\EOp, kc3=\\EOn, kcub1=\\EOD,
        kcud1=\\EOB, kcuf1=\\EOC, kcuu1=\\EOA, kent=\\EOM, kf0=\\EOy,
        kf1=\\EOP, kf10=\\EOx, kf2=\\EOQ, kf3=\\EOR, kf4=\\EOS, kf5=\\EOt,
        kf6=\\EOu, kf7=\\EOv, kf8=\\EOl, kf9=\\EOw, lf1=pf1, lf2=pf2,
        lf3=pf3, lf4=pf4, mc0=\\E[0i, mc4=\\E[4i, mc5=\\E[5i, rc=\\E8,
        rev=\\E[7m$<2>, ri=\\EM$<5>, rmacs=^O, rmam=\\E[?7l,
        rmkx=\\E[?1l\\E>, rmso=\\E[m$<2>, rmul=\\E[m$<2>,
        rs2=\\E>\\E[?3l\\E[?4l\\E[?5l\\E[?7h\\E[?8h, sc=\\E7,
        sgr=\\E[0%?%p1%p6%|%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;m%?%p9%t\\016%e\\017%;$<2>,
        sgr0=\\E[m\\017$<2>, smacs=^N, smam=\\E[?7h, smkx=\\E[?1h\\E=,
        smso=\\E[7m$<2>, smul=\\E[4m$<2>, tbc=\\E[3g,
Xterm 终端 terminfo 定义
[root@localhost ~]# infocmp xterm
#       Reconstructed via infocmp from file: /usr/share/terminfo/x/xterm
xterm|xterm terminal emulator (X Window System),
        am, bce, km, mc5i, mir, msgr, npc, xenl,
        colors#8, cols#80, it#8, lines#24, pairs#64,
        acsc=``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
        bel=^G, blink=\\E[5m, bold=\\E[1m, cbt=\\E[Z, civis=\\E[?25l,
        clear=\\E[H\\E[2J, cnorm=\\E[?12l\\E[?25h, cr=^M,
        csr=\\E[%i%p1%d;%p2%dr, cub=\\E[%p1%dD, cub1=^H,
        cud=\\E[%p1%dB, cud1=^J, cuf=\\E[%p1%dC, cuf1=\\E[C,
        cup=\\E[%i%p1%d;%p2%dH, cuu=\\E[%p1%dA, cuu1=\\E[A,
        cvvis=\\E[?12;25h, dch=\\E[%p1%dP, dch1=\\E[P, dl=\\E[%p1%dM,
        dl1=\\E[M, ech=\\E[%p1%dX, ed=\\E[J, el=\\E[K, el1=\\E[1K,
        flash=\\E[?5h$<100/>\\E[?5l, home=\\E[H, hpa=\\E[%i%p1%dG,
        ht=^I, hts=\\EH, ich=\\E[%p1%d@, il=\\E[%p1%dL, il1=\\E[L,
        ind=^J, indn=\\E[%p1%dS, invis=\\E[8m,
        is2=\\E[!p\\E[?3;4l\\E[4l\\E>, kDC=\\E[3;2~, kEND=\\E[1;2F,
        kHOM=\\E[1;2H, kIC=\\E[2;2~, kLFT=\\E[1;2D, kNXT=\\E[6;2~,
        kPRV=\\E[5;2~, kRIT=\\E[1;2C, kb2=\\EOE, kbs=\\177, kcbt=\\E[Z,
        kcub1=\\EOD, kcud1=\\EOB, kcuf1=\\EOC, kcuu1=\\EOA,
        kdch1=\\E[3~, kend=\\EOF, kent=\\EOM, kf1=\\EOP, kf10=\\E[21~,
        kf11=\\E[23~, kf12=\\E[24~, kf13=\\EO2P, kf14=\\EO2Q,
        kf15=\\EO2R, kf16=\\EO2S, kf17=\\E[15;2~, kf18=\\E[17;2~,
        kf19=\\E[18;2~, kf2=\\EOQ, kf20=\\E[19;2~, kf21=\\E[20;2~,
        kf22=\\E[21;2~, kf23=\\E[23;2~, kf24=\\E[24;2~, kf25=\\EO5P,
        kf26=\\EO5Q, kf27=\\EO5R, kf28=\\EO5S, kf29=\\E[15;5~,
        kf3=\\EOR, kf30=\\E[17;5~, kf31=\\E[18;5~, kf32=\\E[19;5~,
        kf33=\\E[20;5~, kf34=\\E[21;5~, kf35=\\E[23;5~,
        kf36=\\E[24;5~, kf37=\\EO6P, kf38=\\EO6Q, kf39=\\EO6R,
        kf4=\\EOS, kf40=\\EO6S, kf41=\\E[15;6~, kf42=\\E[17;6~,
        kf43=\\E[18;6~, kf44=\\E[19;6~, kf45=\\E[20;6~,
        kf46=\\E[21;6~, kf47=\\E[23;6~, kf48=\\E[24;6~, kf49=\\EO3P,
        kf5=\\E[15~, kf50=\\EO3Q, kf51=\\EO3R, kf52=\\EO3S,
        kf53=\\E[15;3~, kf54=\\E[17;3~, kf55=\\E[18;3~,
        kf56=\\E[19;3~, kf57=\\E[20;3~, kf58=\\E[21;3~,
        kf59=\\E[23;3~, kf6=\\E[17~, kf60=\\E[24;3~, kf61=\\EO4P,
        kf62=\\EO4Q, kf63=\\EO4R, kf7=\\E[18~, kf8=\\E[19~, kf9=\\E[20~,
        khome=\\EOH, kich1=\\E[2~, kind=\\E[1;2B, kmous=\\E[M,
        knp=\\E[6~, kpp=\\E[5~, kri=\\E[1;2A, mc0=\\E[i, mc4=\\E[4i,
        mc5=\\E[5i, meml=\\El, memu=\\Em, op=\\E[39;49m, rc=\\E8,
        rev=\\E[7m, ri=\\EM, rin=\\E[%p1%dT, rmacs=\\E(B, rmam=\\E[?7l,
        rmcup=\\E[?1049l, rmir=\\E[4l, rmkx=\\E[?1l\\E>, rmso=\\E[27m,
        rmul=\\E[24m, rs1=\\Ec, rs2=\\E[!p\\E[?3;4l\\E[4l\\E>, sc=\\E7,
        setab=\\E[4%p1%dm, setaf=\\E[3%p1%dm,
        setb=\\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m,
        setf=\\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m,
        sgr=%?%p9%t\\E(0%e\\E(B%;\\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m,
        sgr0=\\E(B\\E[m, smacs=\\E(0, smam=\\E[?7h, smcup=\\E[?1049h,
        smir=\\E[4h, smkx=\\E[?1h\\E=, smso=\\E[7m, smul=\\E[4m,
        tbc=\\E[3g, u6=\\E[%i%d;%dR, u7=\\E[6n, u8=\\E[?1;2c, u9=\\E[c,
        vpa=\\E[%i%p1%dd,
每一个 terminfo 定义由3个类型的数据项组成。这三种数据项被成为“性能名”(capname),它们分别定义了一个终端的性能(capability)。

布尔型性能指标简单的指示了这个终端是否支持某种特定的功能。例如,如果某个终端支持 XON/XOFF 流控制,就能在上面的清单里看到布尔性能指标“xon”。

数字性能指标定义了一些关于尺寸长度的数字,比如 lines(屏幕上可以显示几行),cols(屏幕上可以显示出几列)。数字和性能指标名称之间是用一个#字号隔开的,如上面引用部分的粉红色高亮部分。

字符串性能指标稍微复杂一些。它们用来定义两种泾渭分明的性能:用来访问终端功能的输出字符串 和 用户按下特定按键(通常是功能键或数字小键盘(numberic keypad)上的特殊键)时终端接收到的输入字符串。有的字符串性能指标很简单,如 el,表示删除到这一行的末尾。在 xterm 终端上,转义字符序列需要这么做: Esc-[-K .在 terminfo 的源文代码格式里要写成 el=\\E[K .

说明一下 Esc-[-K 如在 xterm 终端中时,如以下输入:
[root@localhost ~]# DDDDDDDDDDDDDDDDDDDDDDDDDDDDD
上面,家粗高亮的 D 代表光标所在的地方,这时在键盘上键入 Ctrl+K 组合键,那么就会删除其后的所有字符(直到行末尾):
[root@localhost ~]# DDDDDDDDDD

特殊的按键也以类似的方法定义。例如,F1 功能键在 xterm 中对应的是发送转义序列 Esc-O-P. 这被定义成 kf1=\\EOP .

在转义序列需要一些参数时情况就会变得复杂一些。大多数终端可以移动光标到一个特定的行列位置。很明显,为每一个可能的光标位置定义不同的性能指标那是不现实(impractical)的,解决的办法是使用一个带有参数的通用性字符串。例如,VT100 终端会使用转义字符 Esc-[-<row>-;-<col>-H 把光标移动到指定位置。在 terminfo 源代码格式里,转义序列被写成相当复杂的格式:
cup=\\E[%i%p1%d;%p2%dH$<5> .

上面格式中各参数的意思是
\\E   :   发送 Escape 字符
[    :   发送 [ 字符
%i   :   增加参数的值
%p1 :   把第一个参数放在堆栈中
%d  :   以十进制数输出堆栈上的数字
;   :    发送 ; 字符
%p2 :   把第二个参数放到堆栈上
%d  :   以十进制数输出堆栈上的数字
H   :   发送 H 字符
 
这看起来很复杂,但允许参数以固定的顺序使用,这和终端所期望的出现在最终转义序列里的次序并没有冲突。%i 可以增加参数的值,这也是必须的;因为标准光标开始地址被指定在屏幕左上角的(0,0)的位置处,但 VT100 的这个位置的地址为(1,1)。最后一个参数 $<5> 表示终端需要延迟 5 个字符输出的时间后才会处理光标的移动。

UNIX/Linux 系统预定义了许多终端的性能指标。如果需要增加一个新的终端,你可以在 terminfo 手册页里查找完整的性能指标列表。一个好的开始通常是,首先找到一个和你所要定义的新终端类似的一个终端,也就是这个新终端即将作为这个已存在终端的一个变异体,或者一次性的完成(work through)整个终端的定义工作,然后再根据需求再进一步进行修订。

对于 man 手册页以外的标准参考是资料是 O'Reilly 出版社出版的《Termcap 和 Terminfo》一书,其作者是 John Strang 以及 Linda Mui。

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34397
板凳
 楼主| 发表于 2009-3-11 00:44:46 | 只看该作者

使用 terminfo

当需要使用 terminfo 时,所需要做的第一件事是通过调用 setupterm() 函数设置终端类型。这就会为当前的终端类型初始化一个 TERMINAL 结构体。随后,我们就可以查看这个终端的性能指标和使用它的功能。setupterm() 函数定义如下:
#include <term.h>
int setupterm(char *term, int fd, int *errret);
setupterm() 库函数设置当前的终端为由其参数 term 指定的类型。如果 term 是个空指针,那就会用环境变量 TERM 来代替。往终端里写数据写数据需要使用到一个打开的文件描述符,它被传递为参数 fd. 如果 errret 不是空指针的话,那么函数的结果存储在由 errret 指向的整型变量里面。它的含义如下:
[Plain Text] 纯文本查看 复制代码
-1 : 没有 terminfo 数据库
0 :  在 terminfo 数据库里没有匹配的条目
1 :  成功

setupterm() 函数如果成功的话就返回常数 OK,错误就返回 ERR。如果 errret 被设为空指针,setupterm 将打印出一个诊断信息,而如果失败的话整个程序都将退出。测试代码
#include <stdio.h>
#include <term.h>
#include <curses.h>
#include <stdlib.h>

int main()
{
        setupterm("unlisted", fileno(stdout), (int *)0);
        printf("Done\\n");
        exit(0);
}
编译运行及输出
[root@localhost C]# gcc -g setuperm.c -o setuperm.exe -lncurses
[root@localhost C]# ./setuperm.exe
'unlisted': unknown terminal type.
上面的代码,字符串“Done”没有被打印出来,因为 setupterm() 在失败时会让退出整个程序。
另外,在手头的这个 Linux 系统上,这里使用了 curses 库的 ncurses 实现以及一个标准头文件,这个标准头文件可以在标准位置找到。在这样的系统上,只需要简单的包含 curses.h ,并且为这个库指定 -lncurses 编译参数。
[root@localhost C]# ll /usr/include/curses.h
lrwxrwxrwx 1 root root 16 12-02 21:39 /usr/include/curses.h -> ncurses/curses.h
再看
[root@localhost C]# ll /usr/include/ncurses.h
lrwxrwxrwx 1 root root 17 12-02 21:39 /usr/include/ncurses.h -> ncurses/ncurses.h
还有
[root@localhost C]# ll /usr/include/ncurses/ncurses.h
lrwxrwxrwx 1 root root 8 12-02 21:39 /usr/include/ncurses/ncurses.h -> curses.h
由此可见真正用到的头文件是 /usr/include/ncurses/curses.h.
既然这样,那么如下编译也是可以的:
[root@localhost C]# gcc -g setuperm.c -o setuperm.exe -lcurses

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34397
地板
 楼主| 发表于 2009-3-11 02:27:34 | 只看该作者

菜单选择函数

通过菜单选择函数,可以进行清屏,在屏幕上移动鼠标,在屏幕的不同位置写数据。一旦调用了 setupterm() 函数,就可以用以下三个函数来访问 terminfo 性能指标了,一个函数对应每一个的性能指标类型:
#include <term.h>
int tigetflag(char *capname);
int tigetnum(char *capname);
char *tigetstr(char *capname);

tigetflag(),tigetnum(),tigetstr() 三个函数反别返回 terminfo 性能指标中的布尔指标,数字指标以及字符串指标。函数失败的时候(例如某个性能指标不存在的时候),tigetflag() 返回 -1,tigetnum() 返回 -2,tigetstr() 返回 (char *)-1

以下测试代码输出当前终端窗口的尺寸
int main()
{
        int nrows, ncolumns;

        setupterm(NULL, fileno(stdout), (int *)0);
        nrows = tigetnum("lines"); /*lines和cols在终端性能指标定义文件中可以看到*/
        ncolumns = tigetnum("cols");
        printf("This termianl has %d columns and %d rows\\n", ncolumns, nrows);
        exit(0);
}
运行及输出
[root@localhost C]# ./sizeterm.exe
This termianl has 80 columns and 24 rows

如果使用 tigetstr() 函数,就可以得到 xterm 终端类型的光标位置性能参数(cursor motion capability--cup),这样可以得到一个参数化的答案:<-[%i%p1%d;%p2%dH (注意这里 <- 并不是真正的输出,真正的输出也是一个箭头,但这里显示乱码),测试程序如:
#include <term.h>
#include <curses.h>
#include <stdlib.h>

int main()
{
        int nrows, ncolumns;

        setupterm(NULL, fileno(stdout), (int *)0);
        nrows = tigetnum("lines");
        ncolumns = tigetnum("cols");
        printf("This termianl has %d columns and %d rows\\n", ncolumns, nrows);
        printf("%s\\n", tigetstr("cup"));
        exit(0);
}
这个性能指标需要两个参数:光标要移动到的“行”和“列”。这两个参数坐标都是以屏幕左上角为 0 点开始计算的。

通过使用 tparm() 函数可以用实际的值来取代性能指标中的参数。tparm() 函数中共有高达 9 个参数可以被取代,并且函数可以返回一个有用的转义序列。tparm() 函数原型为:
#include <term.h>
char *tparm(char *cap, long p1, long p2, ..., long p9);
你一旦用 tparm() 构造了终端的转义序列,你接着就必须把其发送到终端。为了能够恰当的处理这些事情,你不能简单的用 printf() 函数来发送字符串到终端

其实,如果在不严格的情况下,用 printf() 函数是可以把 tigetstr() 函数得到的行为“执行”出来的,比如要进行一个清屏的动作就可以用 printf("%s", tigetstr("clear"); 这条语句来实现。
所以,从这里可以看出,printf() 函数把相应的内容送往终端,而如果是这些字符的特性正好符合了终端 terminfo 里所定义的转义序列,那么就会执行这些转义序列所指定的动作!

然而如上面所说的,printf() 函数是有着许多不足的;所以这就必须使用系统提供给我们以下几个特殊函数中的一个,它能正确的处理在终端完成一个操作时必须的延迟,这些函数定义如下:
#include <term.h>
int putp(char *const str);
int tputs(char *const str, int affcnt, int (*putfunc)(int));
putp() 函数成功时,返回 OK,失败时返回 ERR。putp() 函数以终端控制字符串为参数并把它们发送到 stdout 中去。
所以,如果要把光标移动到屏幕中的 5行;30列 中去,可以使用如下的代码:
char *cursor;
char *esc_sequence;
cursor = tigetstr("cup");
esc_sequence = tparm(cursor,5,30);
putp(esc_sequence);

tputs() 函数是为了不能通过 stdout 来访问终端准备的,它允许我们指定一个用来输出字符串的函数。它返回用户指定的字符串输出函数 putfunc() 的返回结果。
affcnt 参数用来表示这一变化所影响的行数,它常被设为 1 。
用来输出字符串的函数必须同 putchar() 函数有着相同的参数类型和返回类型。
事实上,putp(string) 就相当于 tputs(string, 1, putchar).

在一些老旧的 linux 发行版里,tputs() 最后一个参数被定义为 int (*putfunc)(char)。由于是 char ,所以会限制了一些应用,所以现在不实用这个老定义函数。

如果在手册页里查看 tparm() 函数的相关信息和终端的性能指标(terminal capabilities),或许会偶然碰到(come across) tgoto() 函数.显而易见,这个函数用来移动光标会容易些,但我们现在并不使用它。 这是因为1997年版的X/Open 技术规范(Single UNIX specification Version 2)并没有包含它。因此,在新的程序里强烈建议不再使用它。

应用举例
http://www.groad.net/bbs/read.php?tid=633

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-6-17 16:27 , Processed in 0.084027 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表