没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
Unix/Linux
下的
Curses
——库开发指南 第一章 Curses
库开
发简介
分类:Unix/Linux2009-10-29 09:331631 人阅读评论(5)收藏举报
1.1 什么是 curses
curses 实际上是一个函数开发包,专门用来进行 UNIX 下终端环境下的屏幕界面处理以及
I/O 处理。通过这些函数库,C 和 C++程序就可以控制终端的视频显示以及输入输出。使用
curses 包中的函数,用户可以非常方便的创建和操作窗口,使用菜单以及表单,而且最为
重要的一点是使用 curses 包编写的程序将独立于各种具体的终端,这样的一个直接的好处
就是程序具有良好的移植性。这一点在网络上显得尤其重要,因为你面对的可能是上百种
终端,如果为每一个终端都专门重新编写一套新的程序,那么复杂程度出乎想象,而且几
乎不可能。为了能够达到这样的目的,curses 包使用了终端描述数据库(TerminalDescriptio
nDatabases)terminfo(TERMinalINFOrmationdatabase)或者
termcap(TERMinalCAPabilitiedatabase),这两个数据库里存放了不同终端的操作
控制码和转义序列以及其余相关信息,这样当使用每一个终端的时候,curses 将首先在终
端描述数据库中查找是否存在该类型的终端描述信息,如果找到则进行适当的处理。如果
数据库中没有这种终端信息,则程序无法在该终端上运行,除非用户自己增加新的终端描
述。具体的如何在终端描述数据库中增加自定义终端在第八章“terminfo 数据库”中有详细的
介绍。
1.1.1curses 发展历史
curses 是怎么来的?curses 的名称起源于“cursoroptimization”,即光标优化的意思。它最
早是由巴克利大学的 BillJoy 和 KenArnold 发展而来,主要是处理游戏 rogue 的屏幕界面。
rogue 是一个古老的基于文本的的冒险类游戏。在当时,仅仅控制游戏屏幕的外观显示就需
要编写大量的代码,因为它们使用的是古老的 termios 甚至是 tty 接口。巨大的工作量迫使
BillJoy 和 KenArnold 将 rogue 游戏中的所有的屏幕处理和光标移动的函数汇集到一个函数
库中。这就形成了最早的也是最简单的 curses 处理库的雏形。它最终随着 BSDUNIX 的早
期版本发行开来。在这个版本中使用的是当时业已存在的 termcap 数据库来描述终端信息。
后来贝尔实验室的 MarkHorton 在 SystemIIIUNIX 中重新编写了 curses。它相对以前的版
本有了很大的扩展和提高,增加了一些非常新的特性。它首先将 termcap 数据库改进为ter
minfo 数据库。terminfo 数据库完全由 Horton 开发编写,它是从 termcap 发展而来,而且更
为中要重要的是其中引进了参数化性能的概念,这样使得描述多视频属性以及彩色终端成
为可能。在后来的 AT&TSystemV版本中,curses 就扩展了更多功能和性能,包括了对窗
体、菜单、面板、表单等组件以及对鼠标的支持。这时候的 curses 内容以及设计与最初的
BSD 版本的curses 在功能和复杂性上已经相去甚远。
1.1.2curses 包内容
本书的 curses 以 SystemVUNIX 的版本为主,curses 包主要包括下面的四个开发库,如表
1.1 所示。在后面的章节中我们会针对每一个库进行详细深入的探讨。
表 1.1curses 包内容
库名 描述
curses
最早的 curses 包只包含这一部分,主要控制屏幕的输入和输出,光标的
操作,窗口的创建和操作等。
panel
类似于窗口堆栈,不同的窗口可以存放于其中,并且可以在其中进行移
动。
menu
新增的部分,主要包括创建菜单并且与之交互的函数,主要用来接受用
户的选择。
form
包括创建表单以及与之进行交互的函数,主要用来接受用户数据输入
1.1.3curses 包移植性
正如前言部分我们曾经提到过,使用 curses 包与使用低层终端函数编写的程序最主要的差
别在于 curses 程序是独立于具体终端的,也就是说在某个终端上编写的程序可以完整的移
植到另外的终端上而不需要进行任何改动。curses 包的可移植性是 curses 包的最大特性。
curses 包的这种终端独立性归根于终端描述数据库 terminfo 和 termcap。terminfo和 termcap
数据库中包含了所有终端的描述信息。termcap 数据库是在最早的的 BSDUNIX 中使用,在
后来的 SystemIII 中则使用 terminfo 数据库。terminfo 数据库是从 termcap 数据库发展而来,
组织方式相对于 termcap 来说有了进一步的优化,而且描述的终端信息有了进一步的增加。
需要使用的数据库可以在程序编译的时候通过 cc 命令指定,具体的细节在这一章的末尾
会有探讨。
正如前面所说,curses 正是通过使用 terminfo 数据库使得程序可以在不同的终端上可以移植,
那么系统是如何做到这一点的呢?
从第一章的图 0.1 可以看出,对于使用 curses 进行处理的程序员来说他实际上处理的是虚拟
终端。curses 完成了物理终端到虚拟终端的“映射”。用 curses 编写的程序在它们每次被调用
的时候都需要引用终端描述数据库。数据库中的终端描述信息包括了终端的一系列的性能
参数,在 curses 包中我们定义了很多的变量与这些性能参数对应。当程序执行的时候,程
序首先获取终端类型,然后根据终端类型获取终端描述数据库中具体的性能,最后将这些
性能参数读进 curses 中预定义的相应的变量中。当程序与终端进行交互从而需要调用相应
的函数的时候,它将从头文件的性能变量中为终端获取必要的控制码,一旦需要某个性能
参数,只要找到相应的变量即可,从而达到以不变应万变的效果。例如在 curses 包中我们
定义了 LINES 和 COLS 变量对应终端能够显示的最大行数和最大列数这两个性能,不同的
终端的 LINES 和 COLS 的值可能不同,比如通常的终端的行数为 39 行,如果使用了软标
签,行数将减一变为 38。但这种变化都由 curses 幕后自动完成,用户完全不需要理会,用
户需要记住的仅是 LINES 和 COLS 以及它们代表的含义。这样,程序就可以运行在各种不
同的终端上,唯一的缺陷就是这种终端首先必须在终端信息描述库中存在,否则就无法直
接使用 curses 包,弥补的办法就是需要自己在终端信息描述库中增加终端描述信息。
1.2 使用 curses 包示例
1.2.1 简单的 curses 应用程序
现在我们先看一个简单的 curses 应用程序 1-1,这个程序中包含了 curses 包中最常使用的一
些函数,也许开始看不懂,我们会在后面进行详细的讲解。
程序 1-1简单的 curses 程序
程序名称bullseye.c
编译命令cc–obullseyebullseye.c–lcurses
#include<curses.h>
#include<signal.h>
staticvoid"nish(intsig);
main(intargc,char**argv)
{
(void)sigaction(SIGINT,finish);
initscr();//初始化 curses 包
keypad(stdscr,TRUE);//允许键盘映射
(void)nonl();
(void)cbreak();
(void)noecho();
//判断是否支持彩色
if(has_colors())
{
start_color();
//初始化颜色配对表
init_pair(0,COLOR_BLACK,COLOR_BLACK);
init_pair(1,COLOR_GREEN,COLOR_BLACK);
init_pair(2,COLOR_RED,COLOR_BLACK);
init_pair(3,COLOR_CYAN,COLOR_BLACK);
init_pair(4,COLOR_WHITE,COLOR_BLACK);
init_pair(5,COLOR_MAGENTA,COLOR_BLACK);
init_pair(6,COLOR_BLUE,COLOR_BLACK);
init_pair(7,COLOR_YELLOW,COLOR_BLACK);
}
attron(A_BLINK|COLOR_PAIR(2));
move(LINES/2+1,COLS-4);
addstr(“Eye”);
refresh();
sleep(2);
move(LINES/2–3,COLS/2-3);
addstr(“Bulls”);
refresh();
sleep(2);
"nish(0);
}
staticvoid"nish(intsig)
{
endwin();
exit(0);
}
在上面的程序 1-1 中我们只是简单的将光标移动到屏幕中央附近的两个不同位置,然后在
这两个位置上输出单词 BlueEye 和 Bulls,字体的颜色分量分别为(Green,Green,Black),
并同时进行闪烁。我们通过函数 move()进行光标移动以及函数 addstr()输出单词。下面我们
详细讨论这个程序所涉及到的问题,这些问题对所有的使用 curses 包的程序都是非常重要
的。
1.2.2 开始使用 curses 包
1.2.2.1 头文件
每一个使用 curses 包的程序都必须在程序中包括相应库所使用的头文件。头文件中定义了
各种各样的数据类型以及宏,同时声明了各种能够在程序中引用的常量和函数。我们通常
所用到的头文件如表 1.2 所示。
表 1.2curses 包以及其对应的头文件
库名 头文件
curses curses.h
panel panel.h
menu menu.h
form form.h
我们在使用 curses 编写程序的时候可能会用到上面的一个以上的库。但是 curses 库是每一
个程序都必须包含的,它定义的一些公共的函数和变量是每一个 curses 程序都需要的。另
外,在程序进行编译的时候我们必须将使用到的所有的库都一起编译进去,否则程序将无
法编译通过。如果用户在 AT&TUNIXPC 上使用终端访问方法(TAM–TerminalAcces
sMethod)进行编程的话,则还需要包括 TAM 库。一旦程序正确编译,它就可以执行并
进行调试。同时系统中的环境变量必须设置正确,这样编译程序才能找到终端描述数据库。
示例程序的一开始我们就包括了头文件 curses.h。curses.h 中定义了 LINES 和 COLS 两个变
量。程序中通过这两个变量来计算光标的位置从而能够通过 move 函数将光标放置在屏幕
的中央附近。由于这两个值是与具体的终端的尺寸关联的,因此不管我们的程序运行在什
么样的终端上,光标的位置都是处于屏幕的相同的位置。
另一方面 curses.h 中也定义了 refresh(),实际上它是一个宏定义,具体的定义如下:
#de$nerefresh()wrefresh(stdscr)
从上面的定义可以看出,窗口中调用 refresh()实际上是调用函数 wrefresh()来对标准屏幕进
行刷新。不仅 refresh(),事实上 curses 中的很多函数都是这种伪函数。它们之间遵循一定的
命名规范,我们在第二章将详细讨论。
为了能够在程序意外中断的时候对 curses 包进行必要的处理,我们对中断信号进行适当处
理,因此必须包含信号处理的头文件 signal.h。同时我们定义了信号处理函数 finish();
1.2.2.2curses 初始化
在主函数中设置了信号处理函数之后我们就调用了 initscr(),一般情况下在其余的 curses 函
数被调用之前我们就必须首先调用 initscr()。initscr()对 curses 包进行一些初始化的工作,而
且在每一个程序里面,这个函数只能调用一次。它的作用主要包括下面几个方面:
■通过读取 TERM 环境变量的值来决定当前使用的终端类型,开启终端模式。
■根据终端的具体情况将终端的一些性能参数读进相关变量中,完成对相关数据结构的初
始化工作,例如示例程序 1-1 正是在 initscr()中获取了 LINES 和 COLS 的值。
■创建和初始化标准屏幕 stdscr 和当前屏幕 curscr,同时为它们分配必要的存储空间。
■通知 refresh()函数首次调用的时候能够清除屏幕
如果在终端初始化的过程中遇到错误,比如程序当前运行的终端在终端信息描述库中并没
有描述,那么程序将会在 stderr 上输出错误信息,同时退出程序。另一方面,由于 initscr()
涉及到窗口空间分配,因此可能导致内存溢出,虽然这种情况极少发生。一旦发生,
initscr()将中断处理同时返回错误信息。
需要强调的一点是,initscr()必须在所有其它的操作 stdscr 和 curscr 的函数之前调用,否则
一旦引用到窗口的地方程序将会由于应用程序段寻址错误而“coredump”。因此大部分情况
下我们总是在程序开始就调用 initscr()。其余一些函数如果不涉及到窗口方面就可以在
initscr()之前调用,比如 slk_init(),filter(),ripofflines(),use_env()等等。
如果你的程序使用的是多个终端,那么我们将使用 newterm()代替 initscr()。对于每一个你
希望与之交互的终端设备,都调用一次 newterm()。newterm()返回一个 SCREEN 结构,用
来引用某个终端。在需要从某个终端接受输入或者进行输出时候,必须通过 set_term()将它
设置为当前终端,所有的 curses 函数操作的仅仅是当前终端。
1.2.2.3 终端模式设置
程序使用 initscr()进行初始化之后,程序对终端的模式进行了一些设置。终端模式实际上是
一系列的开关属性,它们直接影响着终端如何处理输入以及输出。具体的关于终端设置模
式的细节在第二章的终端模式一节中会讨论,这里仅仅一带而过。
keypad()用来控制是否将键盘上的特殊字符比如上下左右键等转换成 curses 包中定义的对
应的特殊键。比如将“↓”对应成 KEY_DOWN,“→”转换成 KEY_RIGHT。程序 1-1 中将建
立这种映射关系。
nonl()用来控制程序将回车键不要转换为换行符。
cbreak()用来控制程序一一读取除了 DELETE 或者 CTRL 等特殊字符以外的所有字符。
noecho()使得键盘输入的字符不需要直接在屏幕上显示出来,这通常在将按键作为控制键
时候非常有用。
1.2.2.4 颜色处理
为了能够使得显示的字符为彩色,我们必须设置色彩属性。在设置色彩属性之前我们必须
能够判断终端是否支持彩色,为此调用函数 has_colors()来判断。一旦终端支持彩色,我们
将使用 init_pair()开始初始化颜色配对表,颜色配对表用来设置字符的前景色和背景色。关
于它的细节在第二章后详细的讨论。
剩余63页未读,继续阅读
资源评论
- yehao862012-02-22不错,值得推荐,内容比较基础,适合初学者
- wyjskyapple22012-11-23内容很不错,初学者很适用
- karill2013-01-31东西不错,不过确实doc不好看~还是pdf方便
- 顺眼2012-08-17内容符合 但是doc格式的不方便阅读
moudangxia
- 粉丝: 0
- 资源: 6
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功