wp输入法安卓版:第三章curses库窗口

来源:百度文库 编辑:中财网 时间:2024/04/29 22:54:41
3.1 curses窗口简介
3.1.1窗口概念
窗口是curses库中最重要的一个组件,它实际上是屏幕上的一块矩形区域,在上面我们可以进行各种输出以及操作。curses库中提供了大量的函数以允许我们创建和操作自己的窗口,而不仅仅是只能使用标准窗口stdscr。对自定义窗口进行操作的函数一般与对标准窗口进行操作的函数是相同的,除非它们需要特别的参数,这个参数通常为一个指向自定义窗口的指针。curses也允许创建一种称之为基垫的窗口,英文名称为pad。基垫的各个方面与窗口并无二异,只是它的大小和位置不再局限于终端屏幕的可视部分。它可以比标准屏幕大,位置也可以位于标准屏幕之外而我们看不到。因为窗口函数只是curses库函数的一部分,因此我们可以用同样的方法来编译使用窗口的程序。我们在这一章只介绍最常用的一些函数,必要时我们会给出几个综合的例子,从而加深对窗口的理解和应用能力。
窗口可以位于标准屏幕的任何位置,它们之间可以相互重迭包括与标准屏幕。窗口同时可以包含与它们相关联的子窗口。任何在父窗口和子窗口重迭区域的变化都会同时影响到它们中的任何一个。与子窗口类似,基垫也同样有子基垫,英文名称为subpad。子基垫是与父基垫相关联并且位于父基垫之内的窗口。刷新基垫和子基垫与刷新窗口和子窗口有些细微的差异。这种具体的差异我们在后面会慢慢介绍。
图3.1具体的演示了窗口、子窗口、基垫、子基垫之间的相互关系。

图3.1
3.1.2窗口数据结构
在使用窗口之前我们先看一下窗口的数据结构定义,通常它在头文件curses.h中:
typedef struct _win_st  WINDOW;
struct _win_st
{
short     _cury, _curx;     /* 光标当前位置 */
short          _maxy, _maxx; /* 最大的位置值 */
short         _begy, _begx; /* (0,0) 窗口相对于标准窗口的位置 */
char      _flags;
short      _yoffset;        /* 偏移量 */
bool      _clear,     /* clearok() 信息 */
_leave,     /* leaveok() 信息 */
_immed,     /* window in immediate mode */
_sync;     /* auto syncup of parent */
WINDOW     *_padwin; /* 当前窗口的”pad”区域*/
#ifdef _VR3_COMPAT_CODE
_ochtype **       _y16;         /* MUST stay at this offset in WINDOW */
#endif
short         *_firstch;         /* first change in line */
short         *_lastch;         /* last change in line */
short         _tmarg, _bmarg; /* scrolling region bounds */
/* MUST stay at this offset in WINDOW */
unsigned _scroll : 1;         /* scrollok() info */
unsigned _use_idl     : 1;
unsigned _use_keypad : 1;
unsigned _notimeout : 1;
unsigned _use_idc     : 1;
chtype         _attrs; /* current window attributes */
chtype         _bkgd; /* background, normally blank */
int             _delay; /* delay period on wgetch */
/* 0:  for nodelay */
/* <0: for infinite delay */
/* >0: delay time in millisec */
short        _ndescs;         /* number of descendants */
short        _parx, _pary;     /* coords relative to parent (0,0) */
WINDOW    *_parent;         /*如果窗口为子窗口则这指向父窗口 */
chtype    **_y;         /* lines of data */
short        _nbyte;    /* number of bytes to come */
short        _index;    /* index to hold coming char */
char        _waitc[CSMAX]; /* array to hold partial m-width char */
bool        _insmode;    /* TRUE for inserting, */
/* FALSE for adding */
};
上面的结构基本包含了一个窗口所需要的各种资料,在以后的创建和使用窗口的过程中我们返回以及需要的都是这个的结构。下面是几个最经常用到的成员:
_cury, _curx:当前窗口中光标的位置
_begy, _begx:当前窗口的左上角相对于标准屏幕的位置
_parent:当前窗口的父窗口
_parx, _pary:子窗口的左上角相对于父窗口的位置
_attrs:当前窗口的属性
_bkgd:当前窗口的背景字符
3.2窗口操作
自定义窗口函数跟与标准屏幕交互的函数基本是相同的,除非它需要特殊的参数来指明函数作用的窗口不是标准屏幕。比如标准屏幕刷新用refresh() 函数,而窗口刷新却是通过wrefresh()进行,wrefresh()有一个指针用来指向需要刷新的窗口。当它调用的时候,屏幕上只有代表窗口的部分才会进行刷新,标准屏幕的其余部分并不会刷新。下面我们对窗口的函数进行详细的探讨,同时在一些函数说明后面会附上完整的使用代码,这些代码都是在SCO UNIX上测试过的,在别的平台上只要稍做改动甚至不需要改动就可以直接编译。
3.2.1创建和删除窗口
函数newwin()用来创建窗口并且返回WINDOW指针。这个指针在各个窗口处理函数中作为参数传递过去。newwin()语法如表3.1所示。
表3.1 newwin()函数概述
头文件
curses.h
概述
WINDOW *newwin(lines,cols,begin_y,begin_x);
int lines,cols,begin_y,begin_x;
返回
成功
失败
*WINDOW
(*WINDOW)NULL
newwin()的参数含义如下:
■ line和cols给出了需要创建的窗口所占的总行数和总列数,它们都是整数值。
■ begin_y 和begin_x给出了窗口的左上角所在的行数和列数,它们也是整数值。需要注意的是begin_y和begin_x的顺序,它们与通常的(x,y)顺序正好相反。
newwin()返回的是一个WINDOW类型的指针。如果创建失败,函数返回(WINDOW*)NULL。一旦获取了WINDOW结构,我们就可以访问这个结构中的所有的成员。例如,下面的程序片断中我们在标准屏幕上创建了一个窗口midscreen:
WINDOW* midscreen;
midscreen=newwin(5,10,9,53);
创建的窗口有五行十列,在标准屏幕上它的左上角的位置是(9,35)。
如果lines或者cols为0的话,函数将自动创建一个行数和列数分别为LINES和 COLS的窗口,这个窗口的范围包括整个终端屏幕。例如下面的程序片断创建一个行数为LINES,列数为COLS的窗口,它的左上角为(0,0)。
newwin(0,0,0,0)
必须注意的是创建窗口的大小不能超出实际屏幕的大小,窗口的行和列数的最大值也就是LINES和COLS,如果超过了,则返回失败。
对于那些不再使用的窗口我们有必要及时清除它。窗口清除包括两方面的含义:清除窗口画面和清除窗口内存。对于清除窗口画面,我们可以使用函数werase()和wclear()实现,它们的作用和第二章的erase()、clear()非常类似。如果窗口局部清除,则可以使用wclrtobot()和wclrtoeol(),它们的用法和clrtobot()、clrtoeol()类似。
事实上,不管werase()还是wclear(),它们并不能清除菜单所占的内存空间,因此有可能无意中会造成内存泄漏,因此我们还必须调用函数delwin()释放内存。函数语法如表3.2所示:
表3.2 delwin()函数概述
头文件
curses.h
概述
int delwin(win);
WINDOW* win;
有必要指出的是,在主窗口删除之前我们必须先删除与它关联的所有子窗口。
3.2.2创建子窗口
一个窗口通常都是有一些子窗口与之关联的。subwin()函数创建一个子窗口,同时返回一个指向子窗口的指针。一个子窗口实际上是与父窗口共享所有的或者部分的字符空间的窗口。从WINDOW结构中可以看出,结构中的_parent就是指向父窗口的指针,通过户取这个_parent指针,我们可以得到子窗口的父窗口。
表3.3 subwin()函数概述
头文件
curses.h
概述
WINDOW *subwin(win,lines,cols,begin_y,beign_x)
WINDOW *win;
int lines,cols,begin_y,begin_x;
subwin的参数的意义如下:
■ win是指向当前创建的子窗口的父窗口的指针。一旦函数调用成功,子窗口结构中的_parent指针将指向父窗口win。
■ lines和cols给出了创建的子窗口的总行数和总列数。
■ beign_y和begin_x是子窗口的左上角在标准屏幕中的相对位置。注意不是在父窗口中的相对位置。
subwin()同样返回一个WINDOW类型的指针,如果执行失败,它将同样返回(WINDOW*)NULL。由于子窗口位于复窗口之中,因此对其中任何一个的改变都会同时影响到它们。它们共享部分字符空间。
subwin()函数的最通常的用法是将已存在的窗口切分成不同的区域。例如在下面的程序片断中,subwin在标准屏幕的下面部分创建了一个称之为cmdmenu的窗口。
WINDOW* cmdmenu;
cmdmenu = subwin(stdscr , 5 , 80 ,19 , 0 );
在这个例子中,cmdmenu与标准屏幕共享部分字符空间,因此改变cmdmenu同样影响标准屏幕。
相对于subwin(),我们在程序中更经常使用的则是derwin()函数。它的语法如表3.4所示。
表3.4 derwin()函数概述
头文件
curses.h
概述
WINDOW *derwin(win,lines,cols,begin_y,beign_x)
WINDOW *win;
int lines,cols,begin_y,begin_x;
derwin()与subwin()唯一的不同则是参数begin_y和begin_x。在derwin()中,begin_y和begin_x都是相对于父窗口而言。因此从这个意义上讲,它可能比subwin()更方便使用。
3.2.3在窗口中进行输入和输出
从窗口中接受输入和进行输出的函数与在标准屏幕中进行输入和输出的函数是很相似的,它们的命名都遵循curses命名规范。两个之间唯一的不同之处在于我们必须增加窗口的指针作为额外的参数。这些函数的名称通常由标准屏幕的操作函数在头部加上字符“w”组合而成,同时将窗口的指针作为第一个参数传递。例如,标准屏幕上的字符输出函数 addch(‘c’)在自定义窗口函数中就变成waddch(win,‘c’),完成的功能就是在窗口win的当前位置上输出字符‘c’。下面就是一些常用的函数列表,我们不再重复描述它们的作用。
■ wmove(win,y,x)将窗口移动到(y,x)处
■ waddch(win,ch)在窗口的当前位置增加字符‘ch’
■ mvwaddch(win,y,x,ch)在窗口中将光标移动到(y,x),同时输出‘ch’
■ waddstr(win,str)在窗口中增加字符串
■ mvwaddstr(win,y,x,str)将窗口光标移动到(y,x)同时输出str字符串
■ wprintw(win,fmt [ ,arg…])在窗口中格式化输出
■ mvwprintw(win ,fmt [ ,arg…])在窗口中将光标移动后格式化输出
■ wgetch(win)在窗口中获取输入字符
■ mvwgetch(win,y,x)在窗口中移动到指定位置后获取输入字符
■ wgetstr(win,str)在窗口中获取输入字符串
■ mvwgetstr(win,y,x,str)在窗口中移动后获取输入字符串
■ wscanw(win,fmt [ ,arg…])窗口中格式化输入
■ mvwscanw(win,y,x,fmt [,arg…])窗口中光标移动后格式化输入
■ winsch(win,c)窗口中插入字符
■ mvwinsch(win,y,x,c)窗口中光标移动后插入字符
■ winsertln(win,y,x)窗口中插入一行
■ wdelch(win)窗口中删除一个字符
■ mvwdelch(win,y,x)窗口中移动后删除一个字符
■ wdeleteln(win)窗口中删除一行
■ wclear(win)和werase(win)清除窗口
■ wclrtoeol(win)和wclrtobot(win)将当前位置到窗口底端的所有字符清除
■ wstandout(win)和wstandend(win)将给定窗口设置为高亮显示和正常显示
■ wattrset(win,attr),wattroff(win,attr)和wattron(win,attr)设置窗口显示属性
由于基垫也是窗口,因此上面的函数同样可以在基垫中使用,但是wrefresh()和wnoutrefresh()两个函数除外,因为基垫由于包含标准屏幕的不可见部分,因此它的刷新与窗口有些差异,wnoutrefresh()在后面的部分进行讲解。在基垫中我们使用prefresh()和pnoutrefresh()实现同样的功能。
3.2.4窗口坐标
一旦窗口创建之后,窗口的相关属性将被赋到WINDOW结构中。因此我们只要能够访问WINDOW中的结构就可以获取窗口的有关坐标属性。表3.5所示的几个函数可以让我们获取创建的窗口的一些有用的属性。
表3.5 窗口坐标函数概述
头文件
curses.h
概述
void getbegyx(WINDOW *win,int y,int x);
void getmaxyx(WINDOW *win,int y,int x);
void getparyx(WINDOW *win,int y,int x);
void getyx(WINDOW *win,int y,int x);
getbegyx()函数用来获取指定窗口的起始坐标。坐标值保存在变量y,x中。
getmaxyx()函数用来获取窗口的最大坐标。
getparyx()函数用来获取子窗口相对于父窗口的起始坐标。
getyx()函数获取当前窗口中的光标位置。
需要注意的是坐标y,x的前面并没有‘&’地址符号。
3.2.5窗口拷贝
curses中有的时候需要将某个窗口的内容拷贝到另外的一个窗口中,我们称之为窗口拷贝。为此curses中提供了三个函数:overlay()、overwrite()、copywin()。它们的用法如表3.6所示。
表3.6 窗口拷贝函数概述
头文件
curses.h
概述
int overlay(WINDOW *srcwin,WINDOW  *dstwin);
int overwrite(WINDOW *srcwin,WINDOW *dstwin);
int copywin(scrwin,dstwin,sminrowl,smincol,dminrow,dmincol,dmaxrow,dmaxcol,overlay);
WINDOW *scrwin,*dstwin;
int sminrow,smincol,dminrow,dmincol,dmaxrow,dmaxcol;
int overlay;
返回
成功
失败
OK
ERR
overlay()函数将一个窗口中的内容拷贝到另外的一个窗口中,窗口所占用的内存空间并不进行拷贝。拷贝过程中字符与窗口的相对位置保持不变,即如果一个字符在原始窗口中的位置为(10,10),它在目标窗口中的位置也是(10,10)。
overlay()的参数的含义如下:
■ srcwin是指向被拷贝的窗口的指针,即源窗口。
■ dstwin是指向接受拷贝的窗口的指针,即目标窗口。
srcwin和dstwin的尺寸不需要完全相同。如果srcwin大于dstwin窗口,函数仅仅拷贝srcwin中适合的dstwin的部分。
overlay()是一种非破坏性的拷贝(non-destructive),它不会拷贝源窗口上的空字符,因此如果源窗口的某个位置为空字符,而目标窗口对应位置不为空字符,那么overlay()拷贝后目标窗口的原有字符将继续保留;源窗口中的非空字符将覆盖目标窗口对应位置上的字符。这一点我们可以从下面的示例程序看出来。
这个函数通常用来从重迭窗口中建立组合屏幕。例如下面的程序片断中overlay用两个不同的窗口组合成了标准屏幕。
WINDOW *info,*cmdinfo;
overlay(info,stdscr);
overlay(cmdmenu,stdscr);
refresh();
注意这个函数不能适用于非关联窗口,所谓非关联窗口即两个窗口之间没有任何关联。
与overlay()相同,overwrite()也可以将一个窗口中的内容拷贝到另外一个窗口。它与overlay()唯一的区别就是它是一种破坏性的拷贝(destructive)。它用源窗口的所有内容完全覆盖目标窗口,包括空字符。因此一旦在第二个窗口上写入第一个窗口的内容,第二个窗口以前的内容将被销毁。函数overwrite()的参数的含义如下:
■ srcwin是拷贝的源窗口。
■ dstwin是拷贝的目标窗口。
与overlay()一样,如果srcwin的大小超过了dstwin,那么函数仅仅拷贝srcwin中与dstwin对应的部分。
例如,在下面的程序片断中,overwrite将一个窗口中的内容拷贝到标准屏幕上。
WINDOW *work;
overwrite(word,stdscr);
refresh();
不管是overlay()还是overwrite(),它们实际上底层调用的都是copywin()函数。如果你不想使用overlay()和overwrite(),你可以直接使用copywin()。只是copywin()的函数形式更为复杂一些。它的参数含义如下:
srcwin是拷贝的源窗口指针。
dstwin是拷贝的目标窗口指针。
dminrow、dmincol是目标窗口中拷贝区域的左上角的位置,而dmaxrow和dmaxcol是区域的高度和宽度。
sminrow、smincol是源窗口中需要拷贝的区域的左上角的位置,它的右下角的位置由目标窗口中的区域决定,通常为sminrow+dmaxrow和smincol+dmaxcol。
overlay用来表明拷贝是破坏性还是非破坏性的。
copywin()的功能较overlay()和overwrite()相比更为强大,它可以将源窗口的任何部分
复制到目标窗口的任何部分。因此overlay()和overwrite()仅仅是copywin()的一个功能子集。
下面我们看一个窗口拷贝的例子,其中我们尤其需要注意的是overlay()和overwrite()的区别:对目标窗口的破坏性。在程序中我们将屏幕背景设置为字符ACS_CKBOARD,这样对比效果能够清楚一些。
程序3-1 窗口拷贝函数使用示例
程序名称 win_copy.c
编译命令cc –o win_copy win_copy.c -lcurses
#include 
main(void)
{ WINDOW *scrwin;
int ch;
int w,h,i,j;
initscr();
getmaxyx(stdscr,h,w);
for(i=0;ifor(j=0;jmvaddch(i,j,ACS_CKBOARD);
refresh();
scrwin=newwin(5,25,4,15);
box(scrwin,0,0);
mvwaddstr(scrwin,2,2,"窗口拷贝函数使用示例");
refresh();
wrefresh(scrwin);
ch=getch();
if(ch=='c')
{
overlay(scrwin,stdscr);
mvwin(scrwin,10,15);
refresh();
wrefresh(scrwin);
sleep(1);
endwin();
}
else
endwin();
}
程序运行结果如图3.2所示:

图3.2
从图3.2可以看出,使用overlay()函数,目标窗口stdscr中的确有一些部分没有被破坏掉,而它们对应的源窗口部分正好是空字符。
如果将overlay()替换成overwrite(),则程序运行结果则如图3.3所示:

图3.3
从图3.3也能够看出overlay()会将源窗口中的所有字符包括空字符拷贝到目标窗口上。
3.2.6移动窗口
mvwin()函数将一个指定的窗口从一个位置移动到另外一个位置。移动后窗口的左上角位置由函数指定。函数语法如表3.7所示。
表3.7 mvwin函数概述
头文件
curses.h
概述
int mvwin(win,y,x)
window *win;
int  y,x;
返回
成功
失败
OK
ERR
函数的参数含义如下:
■ win是需要移动的窗口的指针。
■ y为窗口移动后左上角所在的行数。
■ x为窗口移动后右上角所在的列数。
如果我们需要查看的窗口被其余的窗口覆盖,那么就需要使用移动函数将原来的窗口移开。在下面的程序中我们给出了一个完整的例子,通过方向键我们可以任意的在屏幕上移动给定的窗口alertWindow。
程序3-2 窗口移动示例程序
程序名称 mvwin.c
编译命令 cc –o mvwin  mvwin.c –lcurses
#include 
#include 
#include 
main()
{
int ch;
int y,x;
WINDOW *alertWindow;
initscr();
noecho();
keypad(stdscr,TRUE);
refresh();
alertWindow = newwin(8,40,6,20);
box(alertWindow,'┃','━');
mvwaddstr(alertWindow,2,8,"please move by ←↑→↓");
mvwaddstr(alertWindow,4,8,"press any key to exit");
wattron(alertWindow,A_REVERSE);
mvwaddstr(alertWindow,6,18,"OK");
wattroff(alertWindow,A_REVERSE);
wrefresh(alertWindow);
ch=getch();
while(ch==KEY_RIGHT||ch==KEY_LEFT||ch==KEY_UP||ch==KEY_DOWN)
{
clear(); refresh();
y=alertWindow->_begy;
x=alertWindow->_begx;
switch (ch){
case KEY_RIGHT:
x++;
mvwin(alertWindow,y,x);
wrefresh(alertWindow);
break;
case KEY_LEFT:
x--;
mvwin(alertWindow,y,x);
wrefresh(alertWindow);
break;
case KEY_UP:
y--;
mvwin(alertWindow,y,x);
wrefresh(alertWindow);
break;
case KEY_DOWN:
y++;
mvwin(alertWindow,y,x);
wrefresh(alertWindow);
break;
}
ch=getch();
}
delwin(alertWindow);
endwin();
}

程序运行结果如图3.4所示:图3.4
3.2.7激活窗口
通常情况下,窗口之间是相互重迭的,touchwin()函数可以将特定窗口激活,使它获取当前操作的焦点。它通过调用refresh()函数进行刷新从而将这个窗口的所有内容显示在标准屏幕上。函数语法如表3.8所示。
表3.8 touchwin函数概述
头文件
curses.h
概述
void touchwin(win)
WINDOW *win;
返回
成功
失败
OK
ERR
win是需要激活的窗口的指针。假设有两个窗口rightscreen和leftscreen,leftscreen被rightscreen窗口覆盖,下面的程序代码可以用来激活窗口leftscreen的:
touchwin(leftscreen);
下面我们给出程序3-3,完整的演示了窗口激活函数的用法,在主窗口中我们一旦输入一个不是‘q’的键,则主窗口中将弹出一个消息框,继续则消息框消失。函数执行过程中使用了touchwin()函数激活消息窗口。关键部分我们给出注释。
程序3-3 窗口激活示例程序
程序3-3 窗口激活示例程序
程序名称 touchwin.c
编译命令 cc –o touchwin  touchwin.c –lcurses
#include 
#include 
#include 
main()
{
int ch,i;
WINDOW *alertWindow;
char strArr[7][80];
strcpy (strArr[0]," #####   #     #  ######    #####   #######   #####");
strcpy (strArr[1],"#     #  #     #  #     #  #     #  #        #     #");
strcpy (strArr[2],"#        #     #  #     #  #        #        #");
strcpy (strArr[3],"#        #     #  ######    #####   #####     #####");
strcpy (strArr[4],"#        #     #  #   #          #  #              #");
strcpy (strArr[5],"#     #  #     #  #    #   #     #  #        #     #");
strcpy (strArr[6]," #####    #####   #     #   #####   #######   #####");
initscr();//初始化curses库
noecho();
keypad(stdscr,TRUE);
refresh();
box(stdscr,'┃','━');//在窗口的四周进行边界装饰
for (i=0; i<7; i++)
{
mvwaddstr(stdscr,8+i, 16, strArr[i]);//在主窗口上显示”CURSES”字样
}
alertWindow = newwin(8,40,6,20); //创建弹出窗口
while ( (ch = getch()) != 'q')//
{
box(alertWindow,'┃','━');
mvwaddstr(alertWindow,2,8,"这是一个弹出窗口");
mvwaddstr(alertWindow,4,8,"请按任意键继续!");
wattron(alertWindow,A_REVERSE);//打开窗口反显属性
mvwaddstr(alertWindow,6,18,"OK");//反色显示”OK”字样
wattroff(alertWindow,A_REVERSE);//关闭窗口反显属性
touchwin(alertWindow);//激活消息框窗口
wrefresh(alertWindow);//刷新显示
getch();
touchwin(stdscr);//激活stdscr窗口
wrefresh(stdscr);
}
delwin(alertWindow);//一旦推出将删除消息窗口
endwin();
}
程序的执行结果如图3.5和3.6所示:图3.5
图3.63.2.8窗口边界修饰
从前面的例子中我们看到在创建窗口之后我们都调用了box()函数,这个函数是用来在创建的窗口周围装饰边框的。事实上到目前位置如果没有box()函数,我们创建的窗口在屏幕上其实还是看不出来的,窗口的周围并没有我们想象中的边框。在标准屏幕上我们无法将创建的窗口与标准屏幕区分开来。区分的唯一办法就是调用box()函数在创建的窗口的周围加上边框。box()函数语法如表3.9所示。
表3.9 box()函数概述
头文件
curses.h
概述
int box(win,vert, hort)
WINDOW *win;
chtype vert;
chtype hort;
返回
成功
失败
OK
ERR
win是需要画出边界的窗口。
vert是垂直方向的字符,通常情况下为ACS_VLINE,即'┃'。
hort是水平方向的字符,通常情况下为ACS_HLINE,即'━' 。
需要注意的是,‘┃’和'━'不是通常键盘上的‘|’和 ‘-’。‘|’和‘-’是单字节字符,而‘┃’和‘━’ 是双字节字符。
例如下面的程序代码在给定的窗口cmdmenu周围画上边界。
WINDOW *cmdmenu;
cmdmenu = subwin(stdscr ,5 , 80 , 19 , 0 );
box(cmdmenu, '┃','━');
如果你不想使用box(cmdmenu,'┃','━'),可以使用box(cmdmenu,0,0),它们的效果一样。
提到box() 函数,不能不提border()函数,它可以在给定的窗口绘制边界,它的作用与box()类似,只是用法更为复杂。函数语法如表3.10所示。
表3.10 border函数概述
头文件
curses.h
概述
int border(win,ls,rs,ts,bs,tl,tr,bl,br);
WINDOW *win;
chtype ls,rs,ts,bs,tl,tr,bl,br;
返回
成功
失败
OK
ERR
参数WINDOW是需要绘制边界的窗口,ls,rs,ts,bs,tl,tr,bl,br分别是各个方向上绘制的字符。
变量名
解释
ls
窗口左边字符
rs
窗口右边字符
ts
窗口上边字符
bs
窗口下边字符
tl
窗口左上角字符
tr
窗口右上角字符
bl
窗口左下角字符
br
窗口右下角字符
除了绘制窗口边界,curses中还提供了两个绘制直线的函数vline()和hline()。它们分别绘制垂直线和水平线。函数语法如表3.11所示。
表3.11直线绘制函数概述
头文件
curses.h
概述
int hline(chtype ch,int n);
int vline(chtype ch,int n);
int whline(WINDOW *win,chtype ch,int n);
int wvline(WINDOW *win,chtype ch,int n);
返回
成功
失败
OK
ERR
上面的函数中,ch是直线字符,对于hline是‘━’,对于vline是‘┃’。n用来指定最大的线长。hline()和vline()通常在标准屏幕中绘制直线,而whline()和wvline()则可以在任意窗口中绘制直线,win是窗口指针。
3.2.9设置窗口标志
每一个窗口都具有一定的标志,包括光标(cursor),滚动(scroll),屏幕清除(clear-screen)等等。函数leaveok(),scrollok()和clearok()等函数可以用来设置或者清除这些标志。这些标志会影响函数refresh()产生的效果。函数语法如表3.12所示。
表3.12窗口标志函数概述
头文件
curses.h
概述
void leaveok(win,state)
WINDOW *win;
bool state;
void scrollok(win,state)
WINDOW *win;
bool state;
void clearok(win,state)
WINDOW *win;
bool state;
leaveok()函数设置和清除窗口的cursor标志,这个标志用来决定refresh()函数如何放置终端光标和屏幕刷新后的窗口指针。如果设置了这个标志,refresh()函数在最后一个字符拷贝到屏幕上后将离开光标,同时将它移动到窗口中的相应位置上, 否则refresh()将光标移动到标准屏幕上与当前光标在窗口中同样的位置。
函数的参数含义如下:
■ win是需要设置标志的窗口的指针。
■ state是布尔类型的值,用来决定设置还是取消这个标志。如果state为TRUE,则设置标志,否则如果为FALSE,取消设置。
例如下面的例子设置了curses标志:
leaveok(stdscr,TRUE);
scrollok()函数设置给定窗口的滚动标志,如果标志设置,则窗口允许滚动,否则不允许滚动。函数的参数的意义如下:
■ win是指向需要设置标志的窗口的指针。
■ state决定是否需要设置标志,如果为TRUE,需要设置,否则不需要设置标志。
如果窗口允许滚动,我们可以通过函数scroll()执行滚动操作,函数语法如表3.13所示。
表3.13窗口滚动函数概述
头文件
curses.h
概述
void scroll(win)
WINDOW *win;
返回
成功
失败
OK
ERR
win是指向需要滚动的窗口的指针。
如果我们所需要的内容在给定的屏幕内无法一次阅读,我们就必须在窗口内进行滚动。滚动也是我们最经常使用技术之一。下面我们给出一个详细的滚动的程序示例3-4。
程序3-4 窗口滚动示例函数
程序名称 scrollwin.c
编译命令cc –o scrollwin scrollwin.c –lcurse
#include 
#include 
static int finish(int sig);
WINDOW *scrwin,*boxwin;
main()
{
int i;
char ch;
initscr();
cbreak();
noecho();
nonl();
scrwin=newwin(10,40,LINES/2-6,COLS/2-25);
boxwin=newwin(12,42,LINES/2-7,COLS/2-26);
scrollok(scrwin,TRUE);
box(boxwin,'┃','━');
refresh();
wrefresh(boxwin);
signal(SIGINT,finish);
signal(SIGQUIT,finish);
for(i=0;;i++)
{
if(i%20==0)
sleep(1);//把这行删掉,看看会有什么后果!J
wprintw(scrwin,"output string %d/n",i%9);
wrefresh(scrwin);
}
}
int finish(int sig)
{
endwin();
exit(0);
}
程序运行结果如图3.7所示。程序中按下DEL键后程序将退出。
在讨论clearok()函数之前我们先来讨论一下WINDOW结构中的_clear标志位。如果设置了该标志,那么当调用它时,它会用refresh()来发送控制代码到终端,refresh检查窗体的宽度是否是屏幕的宽度(使用_FULLWIN标志位)。 如果是的话,它将用内置的终端方法刷新屏幕,它将写入除了空白字符外的文本字符到屏幕,这是一种非常快速清屏的方法。为什么仅仅当窗体的宽度和屏幕宽度相等时才用内置的终端方法清屏呢?那是因为控制终
图3.7
端代码不仅仅只清除窗体自身,它还可以清除当前屏幕。_clear标志位由clearok()函数控制。

因此clearok()函数实际上是对给定的屏幕设置或者取消屏幕清除(_clear)标志,参数的含义跟上面的两个函数的参数一样。下面的例子中对标准屏幕设置了clear标志。clearok(stdscr,true);
如果对当前屏幕curscr设置了_clear标志,那么对于refresh()的每一次调用都会自动的清除屏幕,而不管是哪个窗口在调用中。
3.2.10窗口刷新
在前面我们不止一次谈到了刷新的问题,目前curses中还提供了如表3.14所示的一些刷新函数。
表3.14窗口刷新函数概述
头文件
curses.h
概述
int wrefresh(win)
WINDOW  *win
int wnoutrefresh(win)
WINDOW  *win
int doupdate(win)
WINDOW  *win
int redrawwin(win)
WINDOW  *win
int wredrawln(win,beg_line,num_lines);
WINDOW *win;
int beg_line;
int num_lines
返回
成功
失败
OK
ERR
wrefresh()函数刷新屏幕上特定窗口的内容,它将指定虚拟窗口的内容写入终端物理屏幕上。它实际上是依次调用函数wnoutrefresh()和doupdate()。除了所有的窗口结构之外,系统中定义了另外两个数据结构来描述终端屏幕:物理屏幕和虚拟屏幕。物理屏幕描述了屏幕上实际显示的内容,虚拟屏幕描述了将要显示的内容。wrefresh()首先调用函数wnoutrefresh()将指定的窗口内容拷贝到虚拟屏幕的数据结构中,接着调用doupdate()进行刷新。在刷新过程中虚拟屏幕与实际屏幕进行比较然后仅仅刷新不同之处。
如果同时有几个窗口都必须刷新,那么每一次对wrefresh()函数的调用实际上都是对wnoutrefresh()和doupdate()轮流调用,这样的结果就是进行多次重复的屏幕输出。
事实上,这正是直接调用wrefresh()的一个缺点,它是一种较低效率的刷新。如果我们直接分开调用wnoutrefresh()和doupdate(),刷新效率比使用wrefresh()本身直接刷新更有效。通过对每一个窗口调用wnoutrefresh(),然后只集中进行一次doupdate(),这样需要输出的字符的总数量和总的处理时间得到最小化。在程序3-5中我们多次wnoutrefresh(),最后只使用了一次doupdate()函数。
程序3-5 窗口刷新示例程序
程序名称 doupdate.c
编译命令cc –o doupdate doupdate.c –lcurse
#include 
main()
{
WINDOW *w1, *w2;
initscr();
w1=newwin(10,40,8,36);
box(w1,'┃','━');
w2=newwin(8,14,5,5);
box(w2,'┃','━'); waddstr(w1,”bulls”);
wnoutrefresh(w1);
waddstr(w2,”eye”);
wnoutrefresh(w2);
doupdate();
endwin();
}
在程序3-5中我们使用wnoutrefresh()和doupdate()函数代替wrefresh()的效果我们可以从图3.8、3.9、3.10看出来。
同样在基垫中函数poutrefresh()和doupdate()可以用来代替prefresh()来完成更高效的刷新。
除了刷新窗口以外,有的时候我们在操作的时候可能会将窗口内容破坏掉,为了恢复,这就需要重画窗口。curses中提供了两个函数redrawwin()和wredrawln()分别完成窗口整体和局部的重画。
redrawwin()用来通知curses系统指定窗口中的内容可能部分被破坏,在向它写入新的内容之前必须将它重画。它的重画针对整个窗口,函数指针win指定需要重画的窗口。
与redrawwin()类似,wredrawln()也完成窗口的重画,但它只完成窗口指定区域内的重画,区域范围通过参数beg_line和num_lines指定。beg_line为区域的起始行,num_lines为区域的行数。win为窗口指针。
过多的使用redrawwin()函数可能导致通讯量猛增,因为即使小范围重画,redrawwin()也会刷新整个窗口。redrawwin()重画可能导致了通讯中传输了大量的无效数据。因此大部分情况我们宁可使用wredrawln()。
3.2.11屏幕转储
在window下我们可以抓取屏幕,在Unix终端环境下我们却无法做到这一点。但curses中还是提供了两个函数putwin()和getwin()我们可以将窗口中内容写入某个文件中或者从文件中恢复窗口内容。这两个函数的语法如表3.15所示。
表3.15 屏幕转储函数概述
头文件
curses.h
概述
int putwin(win,filep);
WINDOW *win;
FILE *filep;
WINDOW *getwin(filep);
FILE *filep;
返回
成功
失败
OK
ERR
putwin()将窗口win中的所有数据拷贝到由filep指定的已经打开的文件中,而getwin()则返回由filep中的内容创建的窗口的指针。这就是所谓的屏幕窗口转储。
除了上面两个函数,scr_dump()和scr_restore()可以完成同样的效果。只不过它们只能针对标准屏幕stdscr操作,而不是窗口。函数语法如表3.16所示。
表3.16 屏幕转储函数概述
头文件
curses.h
概述
int scr_dump(const char *filename);
int scr_restore(const char *filename);
返回
成功
失败
OK
ERR
需要注意的是如果屏幕使用scr_dump()存储,则只能使用scr_restore()进行恢复。它们与putwin()和getwin()不能混合使用。如果使用scr_restore()读取putwin()写入的数据或者使用getwin()读取scr_dump()写入的数据将导致错误。
在程序3-6和3-7中我们演示了putwin()和getwin()函数的用法,putwin()将窗口win保存到文件a.txt中,窗口win之外的则不保存;getwin()函数则将文件a.txt中的内容恢复出来。
程序3-6 屏幕转储函数putwin()示例
程序3-6 窗口转储putwin()示例程序
程序名称 putwin.c
编译命令 cc –o putwin putwin.c -lcurses
#include 
#include 
#include 
main(void)
{
FILE *fp;
WINDOW *win;
int ch;
if((fp=fopen("a.txt","wr"))==NULL)
printf("无法打开文件a.txt/n");
initscr();
printw("窗口之外的内容");
win=newwin(10,30,5,20);
box(win,0,0);
mvwaddstr(win,2,5,"putwin()函数示例程序");
refresh();
wrefresh(win);
ch=getch();
putwin(win,fp);
endwin();
}
程序3-7屏幕转储示例程序
程序名称 getwin.c
编译命令 cc –o getwin getwin.c -lcurses
#include 
#include 
#include 
main(void)
{
FILE *fp;
WINDOW *win;
int ch;
if((fp=fopen("a.txt","r"))==NULL)
printf("无法打开文件a.txt/n");
initscr();
win=getwin(fp);
box(win,0,0);
refresh();
wrefresh(win);
ch=getch();
delwin(win);
endwin();
}
3.2.12窗口使用示例——使用窗口构建菜单
在应用系统中我们经常使用到菜单,通过菜单我们可以进行各种选择。但是Unix下编写菜单程序并不是一件简单的事情,如果做的扩充性更好一些则更不容易。综合前面的关于curses窗口的学习,到现在我们可以使用curses窗口自己创建一个简单的菜单程序。另外curses包中提供了菜单库,这样我们可以直接使用菜单库方便的创建菜单了。菜单程序如下:
程序3-8 使用curses窗口创建菜单例程序
程序名称 menuwin.c
编译命令cc –o menuwin menuwin.c –lcurse
#include 
#include 
#define ENTER 10
#define ESCAPE 27
void init_curses()
{
initscr();
start_color();
init_pair(1,COLOR_WHITE,COLOR_BLUE);
init_pair(2,COLOR_BLUE,COLOR_WHITE);
init_pair(3,COLOR_RED,COLOR_WHITE);
init_pair(4,COLOR_GREEN,COLOR_BLUE);
curs_set(0);
noecho();
keypad(stdscr,TRUE);
}
void draw_menubar(WINDOW *menubar)
{
wbkgd(menubar,COLOR_PAIR(2));
waddstr(menubar,"  Menu1");
wattron(menubar,COLOR_PAIR(3));
waddstr(menubar,"(F1)");
wattroff(menubar,COLOR_PAIR(3));
wmove(menubar,0,20);
waddstr(menubar,"  Menu2");
wattron(menubar,COLOR_PAIR(3));
waddstr(menubar,"(F2)");
wattroff(menubar,COLOR_PAIR(3));
}
WINDOW **draw_menu(int start_col)
{
int i;
WINDOW **items;
items=(WINDOW **)malloc(9*sizeof(WINDOW *));
items[0]=newwin(10,19,2,start_col);
wbkgd(items[0],COLOR_PAIR(2));
box(items[0],ACS_VLINE,ACS_HLINE);
items[1]=subwin(items[0],1,17,3,start_col+1);
items[2]=subwin(items[0],1,17,4,start_col+1);
items[3]=subwin(items[0],1,17,5,start_col+1);
items[4]=subwin(items[0],1,17,6,start_col+1);
items[5]=subwin(items[0],1,17,7,start_col+1);
items[6]=subwin(items[0],1,17,8,start_col+1);
items[7]=subwin(items[0],1,17,9,start_col+1);
items[8]=subwin(items[0],1,17,10,start_col+1);
for (i=1;i<9;i++)
wprintw(items[i],"Item%d",i);
wbkgd(items[1],COLOR_PAIR(2));
wrefresh(items[0]);
return items;
}
void delete_menu(WINDOW **items,int count)
{
int i;
for (i=0;idelwin(items[i]);
free(items);
}
int scroll_menu(WINDOW **items,int count,int menu_start_col)
{
int key;
int selected=0;
while (1) {
key=getch();
if (key==KEY_DOWN || key==KEY_UP) {
wbkgd(items[selected+1],COLOR_PAIR(2));
wnoutrefresh(items[selected+1]);
if (key==KEY_DOWN) {
selected=(selected+1) % count;
} else {
selected=(selected+count-1) % count;
}
wbkgd(items[selected+1],COLOR_PAIR(1));
wnoutrefresh(items[selected+1]);
doupdate();
} else if (key==KEY_LEFT || key==KEY_RIGHT) {
delete_menu(items,count+1);
touchwin(stdscr);
refresh();
items=draw_menu(20-menu_start_col);
return scroll_menu(items,8,20-menu_start_col);
} else if (key==ESCAPE) {
return -1;
} else if (key==ENTER) {
return selected;
}
}
}
int main()
{
int key;
WINDOW *menubar,*messagebar;
init_curses();
bkgd(COLOR_PAIR(4));
menubar=subwin(stdscr,1,80,1,0);
messagebar=subwin(stdscr,1,79,23,1);
draw_menubar(menubar);
move(2,1);
printw("Press F1 or F2 to open the menus. ");
printw("ESC quits.");
box(stdscr,0,0);
refresh();
do {
int selected_item;
WINDOW **menu_items;
key=getch();
werase(messagebar);
wrefresh(messagebar);
if (key==KEY_F(1)) {
menu_items=draw_menu(1);
selected_item=scroll_menu(menu_items,8,0);
delete_menu(menu_items,9);
if (selected_item<0)
wprintw(messagebar,"You haven't selected any item.");
else
wprintw(messagebar,
"You have selected menu item %d.",selected_item+1);
touchwin(stdscr);
refresh();
} else if (key==KEY_F(2)) {
menu_items=draw_menu(20);
selected_item=scroll_menu(menu_items,8,20);
delete_menu(menu_items,9);
if (selected_item<0)
wprintw(messagebar,"You haven't selected any item.");
else
wprintw(messagebar,
"You have selected menu item %d.",selected_item+1);
touchwin(stdscr);
refresh();
}
} while (key!=ESCAPE);
delwin(menubar);
delwin(messagebar);
endwin();
return 0;
}
程序的运行界面如图3.11所示:

图3.11
3.3基垫——另一种窗口
基垫也是窗口,但是它与窗口稍微有点不同。窗口的大小不能超出物理屏幕的范围,而基垫则没有这个限制,它可以非常大甚至完全不可见。基垫显示在物理屏幕上的部分由基垫的刷新函数决定。大部分窗口操作的函数都可以直接用在基垫上,只有少数才是基垫独有的,比如创建基垫和子基垫以及刷新基垫的函数等等。
由于基垫可以完全不可见,因此这也导致了基垫和窗口的一个最主要的差异,即就是基垫是与标准屏幕没有直接关联的,理解这一点是理解基垫的关键。
3.3.1创建和销毁基垫
newpad()函数可以创建一个基垫,并且返回一个WINDOW类型的指针,这一点与newwin()非常的类似。其函数语法如表3.17所示。
表3.17基垫创建函数概述
头文件
curses.h
概述
WINDOW *newpad(lines,cols)
int lines,cols;
返回
成功
失败
WINDOW*
(*WINDOW)NULL
参数lines 和cols指定了创建的基垫的总行数和总列数。它们的值可以超出LINES和COLS的范围。比如下面的代码就可以创建一个80行128列的基垫:
WINDOW  *pad;
pad=newpad(80,128);
将newpad(lines,cols)和newwin(lines,cols,begin_x,begin_y)两个函数一对比就可以看出,newpad()中缺少了newwin()中的定位参数begin_x和begin_y。正如上面所言,由于与标准屏幕没有任何的关联,因此这些在标准屏幕中定位的参数在这儿就用不到。
3.3.2创建子基垫
subpad()函数创建一个基垫的子基垫,并且返回创建的子基垫的窗口指针。子基垫共享父基垫的全部或者部分字符空间。函数语法如表3.18所示。
表3.18子基垫创建函数概述
头文件
curses.h
概述
WINDOW *subpad(pad,lines,cols,begin_y,begin_x)
WINDOW *pad;
int lines,cols,begin_y,beign_x;
返回
成功
失败
WINDOW*
(*WINDOW)NULL
subpad()的函数参数的意义如下:
■ pad是需要创建的子基垫的父基垫。
■ lines和cols是创建的子基垫的行数和列数。
■ begin_y和begin_x是创建的子基垫的左上角在父基垫中的相对位置。
下面的代码在pad中创建一个子基垫subpad:
WINDOW  *subpad;
subpad=subpad(pad,10,10,30,40);
3.3.3刷新基垫

窗口一旦创建,它与标准屏幕的相对位置就固定下来了,因此我们可以使用wrefresh()对窗口进行刷新。而基垫则不同,创建后它与标准屏幕的相对位置并不固定,它游离于标准屏幕之外,因此我们不能使用wrefresh()进行刷新,而只能使用prefresh()函数。prefresh()和wrefresh()的作用是一样的。但是由于基垫不与标准屏幕的任何部分关联,因此我们必须用额外的参数来指定基垫中需要刷新的区域,以及指定它在屏幕上显示的区域,这两块区域大小相等。它们之间的关系可以从图3.12看出来。在下面的表述中为了简单起见,我们将基垫中需要刷新的区域简称为B区域,屏幕上显示区域称之为A区域。prefresh()的语法见表3.19。
图3.12
表3.19基垫刷新函数概述
头文件
curses.h
概述
int  prefresh(pad,prow,pcol,sminrow,smincol,smaxrow,smaxcol)
WINDOW *pad;
int prow,pcol;
int sminrow,smincol,smaxrow,smaxcol;
返回
成功
失败
OK
ERR
其中参数含义如下:
■ pad是需要刷新的基垫的指针。
■ prow和pcol描述了在基垫中需要刷新的区域,即B区域的左上角的位置。
sminrow,smincol,smaxrow,smaxcol 指定标准屏幕上显示基垫的矩形区域,即A区域。sminrow,smincol是A区域左上角的位置,smaxrow,smaxcol是A区域右下角的位置。如果prow,pcol,sminrow,smincol等的值为负值,则将它们当作0处理。
上面的参数的具体意义从图3-12中也可以看出来。另一方面,prefresh()函数中只给出了B区域的左上角位置,却没有给出右下角的位置。由于A、B两个区域尺寸必须相同,因此实际上我们可以根据A区域推断出B区域的右下角的位置的。这两个矩形完整的包含在它们各自的数据结构中。
与wrefresh()一样,prefresh()也是一种低效率的刷新,它也是pnoutrefresh()函数和doupdate()函数的联合使用。我们也可以通过单独使用pnoutrefresh()和doupdate()来提高刷新效率。其原因和用法与wnoutrefresh()和doupdate()相同,这儿不再重复。
3.3.4基垫使用示例
虽然基垫也是窗口的一种,但是由于它与窗口不同的创建和刷新方法,尤其是刷新区域和显示区域的关系,使得初次接触基垫的人容易困惑。下面我们给出示例程序3-9,然后根据上面的描述,配以图表,详细讲解,希望能够解惑。
建议:看完程序之后运行一下,看看结果,然后再看讲解!
程序3-9 基垫使用示例程序
程序名称 usepad.c
编译命令 cc –o usepad usepad.c –lcurses
1  #include 
2  #include 
3  main()
4  {
5  int i,j;
6  int w,h;
7  char lines[128];
8  WINDOW *pad;
9  initscr();
10  getmaxyx(stdscr,h,w);
11  for(i=0;i12 for(j=0;j13 mvaddch(i,j,ACS_CKBOARD);
14 refresh();
15 pad=newpad(80,128);
16 for(i=0;i<80;i++)
17 {
18 sprintf(lines,"This is Lines %d",i);
19 mvwprintw(pad,i,0,"%s",lines);
20 }
21 prefresh(pad,0,0,5,5,15,45);
22 sleep(2);
23 i=1;
24 for(i=0;i<50;i++)
25 {
26 prefresh(pad,i++,0,5,5,15,45);
27 usleep(30000);
28 }
29 getch();
30 delwin(pad);
31 endwin();
32 return 0;
33 }

在15至20行处我们创建了一个基垫pad,共有80行,128列。接着在基垫pad上输出“This is Lines 0”类似的字符,共八十行,结果如图3.13所示:
图3.13

在第21行我们调用prefresh(pad,0,0,5,5,15,45),在标准屏幕上创建基垫显示区域,左上角的位置为(5,5),右下角的位置为(15,45),这样标准屏幕上基垫的显示区域为10行40列。同时可以确定基垫中刷新区域范围,左上角为(0,0),右下角为(9,39)。程序的运行结果就是将基垫内矩形区域(0,0,9,39)的内容在标准屏幕的(5,5,15,45)范围内显示出来。如图3.14所示:图3.14
接着第24行到第28行进入循环,它每次将基垫窗口中的不同部分刷新显示到标准屏幕上。第一次是(0,0)到(9,39)的区域,第二次是(1,0)到(10,39)的区域,按此规律依次进行五十次。由于显示内容的连续性,使得产生滚动的效果。