6.3 屏幕
正如你所看到的,所有的curses程序必须以initscr函数开始,以endwin函数结束。下面是它们的头文件定义:
initscr函数在一个程序中只能调用一次。如果成功,它返回一个指向stdscr结构的指针;如果失败,它就输出一条诊断错误信息并使程序退出。
endwin函数在成功时返回OK,失败时返回ERR。你可以先调用endwin函数退出curses,然后通过调用clearok(stdscr,1)和refresh函数继续curses操作。这实际上是首先让curses忘记物理屏幕的样子,然后强迫它执行一次完整的屏幕原文重现。
6.3.1 输出到屏幕
curses函数库提供了一些用于刷新屏幕的基本函数,它们是:
curses有其自己的字符类型chtype,它可能比标准的char类型包含更多的二进制位。在ncurses的标准Linux版本中,chtype实际上是unsigned long类型的一个typedef类型定义。
add系列函数在光标的当前位置添加指定的字符或字符串。printw函数采用与printf函数相同的方法对字符串进行格式化,然后将其添加到光标的当前位置。refresh函数的作用是刷新物理屏幕,成功时返回OK,发生错误时返回ERR。box函数用来围绕一个窗口绘制方框。
在标准curses函数库中,垂直和水平线字符可能只能使用普通字符。但在扩展curses函数库中,你可以利用两个定义ACS_VLINE和ACS_HLINE来分别提供垂直和水平线字符,它们可以让你绘制更好看的方框,但这需要终端支持这些画线字符。一般来说,这个功能在xterm窗口中比在标准控制台中工作得更好,但系统对该功能的支持往往是不完整的,所以如果需要考虑程序的可移植性,我们建议最好不要在程序中使用它们。
insch函数插入一个字符,将已有字符向右移,但此操作对行尾的影响并未定义,具体情况取决于你所使用的终端。insertln函数的作用是插入一个空白行,将现有行依次向下移一行。两个delete函数的作用与上述两个insert函数正好相反。
如果要让程序发出声音,你可以调用beep函数。但因为有极少部分终端不能发出声音,所以有些curses设置会在调用beep函数时让屏幕闪烁。如果你在一个比较繁忙的办公室上班,蜂鸣就可能产生于各种机器设备,这时,你可能更愿意选择屏幕闪烁这种方式。正如你预期的那样,flash函数的作用就是使屏幕闪烁,但如果无法产生闪烁效果,它将尝试在终端上发出声音。
6.3.2 从屏幕读取
你可以从屏幕上读取字符,虽然这个功能并不常用,因为一般来说,要想了解屏幕上所写内容很容易。但如果需要该功能,可用下面这些函数实现它:
inch函数总是可用的,但instr和innstr函数并不总被支持。inch函数返回光标当前位置的字符及其属性信息。注意,inch函数返回的并不是一个字符,而是一个chtype类型的变量,而instr和innstr函数则将返回内容写到字符数组中。
6.3.3 清除屏幕
清除屏幕上的某个区域主要有4种方法,它们是:
erase函数在每个屏幕位置写上空白字符。clear函数的功能类似erase函数,它也是用于清屏,但它还通过在内部调用一个底层函数clearok来强制重现屏幕原文。clearok函数会强制执行清屏操作,并在下次调用refresh函数时重现屏幕原文。
clear函数通常是使用一个终端命令来清除整个屏幕,而不是尝试删除当前屏幕上每个非空白的位置。因此,clear函数是一种可以彻底清除屏幕的可靠方法。当屏幕显示变得混乱时,clear函数和refresh函数的结合提供了一种有效的重新绘制屏幕的手段。
clrtobot函数清除当前光标位置直到屏幕结尾的所有内容。clrtoeol函数清除当前光标位置直到光标所处行行尾的所有内容。
6.3.4 移动光标
用于移动光标的函数只有1个,另有1个函数用来控制在刷新屏幕后curses将光标放置的位置:
move函数用来将逻辑光标的位置移到指定地点。记住,屏幕坐标以左上角(0,0)为起点。在大多数curses版本中,有两个包含物理屏幕尺寸大小的外部整数LINES和COLUMNS,它们可用于决定参数new_y和new_x的最大可取值。调用move函数本身并不会使物理光标移动,它仅改变逻辑屏幕上的光标位置,下次的输出内容就将出现在该位置上。如果希望物理屏幕上的光标位置在调用move函数之后立刻有变化,就需在它之后立刻调用refresh函数。
leaveok函数设置了一个标志,该标志用于控制在屏幕刷新后curses将物理光标放置的位置。默认情况下,该标志为false,这意味着屏幕刷新后,硬件光标将停留在屏幕上逻辑光标所处的位置。如果该标志被设置为true,则硬件光标会被随机地放置在屏幕上的任意位置。一般来说,默认选项更符合用户的需求,这能确保光标停留在一个有意义的位置。
6.3.5 字符属性
每个curses字符都可以有一些属性用于控制该字符在屏幕上的显示方式,前提是用于显示的硬件设备能够支持要求的属性。预定义的属性有A_BLINK、A_BOLD、A_DIM、A_REVERSE、A_STANDOUT和A_UNDERLINE。你可以用下面这些函数来设置单个属性或同时设置多个属性:
attrset函数设置curses属性,attron和attroff函数在不影响其他属性的前提下启用或关闭指定的属性。standout和standend函数提供了一种更加通用的强调或“突出”模式,在大多数终端上,它通常被映射为反白显示。
实 验 移动、插入和属性
现在,你已掌握了许多管理屏幕的方法,下面可以尝试编写一个更复杂的例子moveadd.c了。你将在程序中包含多个对refresh和sleep函数的调用,以便了解在程序执行的每个阶段屏幕的显示情况。一般情况下,curses程序会尽可能少地刷新屏幕,因为这并不是一种很有效的操作。这里的代码主要是方便演示。
(1)在程序的开始包含一些必要的头文件,定义几个字符数组和一个指向这些数组的指针,然后对curses结构进行初始化:
(2)现在是最初要显示的3组文本,它们会以1秒为间隔依次显示在屏幕上。请注意对文本属性标志的开关:
(3)确定演员并将他们的名字以一次一个字符的方式插入到指定的位置:
(4)最后,将光标移动到屏幕的右下角,然后结束程序:
当运行这个程序时,最终的屏幕如图6-3所示。
图 6-3
糟糕的是,这里的屏幕截图并未很好地表现出屏幕完整的效果,它也未能显示出光标的位置,光标的位置应该在屏幕的右下角。
你可能会发现,与标准控制台相比,xterm能更加准确、可靠地显示curses程序的输出效果。
实验解析
在初始化一些变量和curses屏幕之后,使用move函数在屏幕上移动光标。通过attron和attroff函数来控制显示在屏幕上指定位置的文本的属性。然后,程序使用insch函数来演示如何插入字符。最后,程序关闭curses函数库并结束。