6.2 curses术语和概念

curses例程工作在屏幕、窗口和子窗口之上。所谓屏幕就是你正在写的设备(通常是终端屏幕,也可能是xterm屏幕)。屏幕占据了设备上全部的可用显示面积,当然,如果设备是X视窗中的一个终端窗口,则屏幕就是该终端窗口内所有可用的字符位置。无论何时,至少存在一个curses窗口,我们称之为stdscr,它与物理屏幕的尺寸完全一样。你可以创建一些尺寸小于该屏幕的窗口,窗口可以互相重叠,它们还可以拥有自己的多个子窗口,但每个子窗口必须总是被包含在它的父窗口内。

curses函数库用两个数据结构来映射终端屏幕,它们是stdscr和curscr。两者中,stdscr更重要一些,它会在curses函数产生输出时被刷新。stdscr数据结构对应的是“标准屏幕”,它的工作方式与stdio函数库中的标准输出stdout非常相似。它是curses程序中的默认输出窗口。curscr数据结构和stdscr相似,但它对应的是当前屏幕的样子。在程序调用refresh函数之前,输出到stdscr上的内容不会显示在屏幕上。curses函数库会在refresh函数被调用时比较stdscr(屏幕将会是什么样子)与第二个数据结构curscr(屏幕当前的样子)之间的不同之处,然后用这两个数据结构之间的差异来刷新屏幕。

有的curses程序需要知道curses维护的stdscr结构,因为有些curses函数需要以该结构为参数。但真正的stdscr结构是与具体实现相关的,它决不能被直接访问。curses程序无需使用curscr数据结构。

综上所述,在curses程序中输出字符的过程如下所示。

(1)使用curses函数刷新逻辑屏幕。

(2)要求curses用refresh函数来刷新物理屏幕。

除了易于编程以外,分成两个步骤来完成字符输出的好处还在于,curses屏幕的刷新效率很高。虽然这点对控制台屏幕来说并不重要,但如果你是通过慢速网络连接到主机上来运行程序,则屏幕刷新效率的提高意义就很大了。

一个curses程序会多次调用逻辑屏幕输出函数,例如在屏幕上移动光标到达正确的位置,然后输出文本、绘制线框。在程序执行的某些阶段,用户需要看到全部的输出结果。这时curses一般会通过调用refresh函数计算出让物理屏幕和逻辑屏幕相对应的最佳途径。curses通过使用合适的终端功能标志及优化光标的移动来刷新屏幕,与立刻执行所有的屏幕写操作相比,curses所需要输出的字符要少得多。

逻辑屏幕的布局通过一个字符数组来实现,它以屏幕的左上角——坐标(0,0)为起点,通过行号和列号来组织,如图6-1所示。

6.2 curses术语和概念 - 图1

图 6-1

所有的curses函数使用的坐标都是y值(行号)在前、x值(列号)在后。每个位置不仅包含该屏幕位置处的字符,还包含它的属性。可显示的属性依赖物理终端的功能标志,但一般至少会支持粗体和下划线这两个属性。Linux控制台通常还支持反白显示和色彩属性,后面将介绍这方面的内容。

由于curses函数库在使用时需要创建和删除一些临时的数据结构,所以所有的curses程序必须在开始使用curses函数库之前对其进行初始化,并在结束使用后允许curses恢复原先设置。这两项工作是由initscr和endwin函数分别完成的。

实 验 一个“Hello World”curses程序

在本例中,你将编写一个非常简单的curses程序screen1.c,来显示这些及其他一些基本函数的使用方法。然后我们再介绍它们的函数原型。

(1)在程序里加上curses.h头文件,在main函数中增加初始化和重置curses库的函数调用:

6.2 curses术语和概念 - 图2

(2)在初始化和重置操作之间,增加将光标移动到逻辑屏幕上坐标(5,15)处、输出“Hello World”,然后刷新物理屏幕的代码。最后,调用函数sleep(2)将程序暂停两秒钟,以便在程序结束前看到输出的结果:

6.2 curses术语和概念 - 图3

运行程序时,你将在空白屏幕的左上部分看到“Hello World”字符串,如图6-2所示。

6.2 curses术语和概念 - 图4

图 6-2

实验解析

这个程序初始化curses函数库,将光标移动到屏幕上的某个位置,然后显示一些文本。稍停片刻后,它关闭curses函数库并退出。