【 tulaoshi.com - 编程语言 】
校园网路策进会
会长 林建宏
■ 前言
相信您在网路上一定用过如 tin,elm 等工具, 这些软体有项共同的特色,
即他们能利用上下左右等方向键来控制游标的位置. 除此之外, 这些程式
的画面也较为美观. 对 Programming 有爱好的朋友一定对此感到好奇, 也
许他能在 PC 上用 Turbo C 轻易地写出类似的程式, 然而, 但当他将相同
的程式一字不变地移到工作站上来编译时, 却出现一堆抓也抓不完的错误.
其实, 原因很简单, 他使用的函式库可能在 UNIX 上是没有定义的. 有些
在 Turbo-C 上被广泛使用的一些函式, 可能在 UNIX 上是不被定义的.
为了因应网路上各式各样的终端机形态 (terminal), UNIX 上非凡发展出
一套函式库, 专门用来处理 UNIX 上游标移动及萤幕的显示. 这就是本篇
文章要为您介绍的 - curses.h 函式库. 利用这个函式库, 您也可以写出
像 elm 般利用方向键来移动光棒位置的程式. (CCCA 近来所提供的线上选
课程式, 及程式服务界面, 即是笔者利用 curses 发展而成的 )
■ curses 的历史与版本
cureses 最早是由柏克莱大学的 Bill Joy 及 Ken Arnold 所发展出来的.
当时发展此一函式库主要原因是为了提高程式对不同终端机的相容性而设
计的. 因此, 利用 curses 发展出来的程式将和您所使用的终端机无关.
也就是说, 您不必担心您的程式因为换了一部终端机而无法使用. 这对程
式设计师而言, 尤其是网路上程式的撰写, 是件相当重要的一件事.
curses之所以能对上百种以上的终端机工作, 是因为它将所有终端机的资
料, 存放在一个叫 termcap 的资料库, ( 而在第二版的 System V 系统中
, 新版的 curses 以 terminfo 取代原来的 termcap). 有了这些记录, 程
式就能够知道碰到哪一种终端机时, 须送什麽字元才能移动游标的位置,
送什麽字元才能清除整个萤幕清除. (* 注一)
另外, 本文的介绍 以 System V 的 curses 版本为主.
■ 如何在您的程式使用 curses ?
在您的 C 程式的档头将 curses.h include 进来.当您引进 curses.h
这个函式库後, 系统会自动将 stdio.h 和 unctl.h一并 include 进
来.另外, 在 System V 版本中, terminfo.h 这个函式库也将一并
include进来.
#include curses.h
main()
{
: :
: :
}
当然, 您的系统内必须放有 curses.h 这个函式库.
■ 如何编译(compile)
当您编辑好您的程式, 在 UNIX 提示符号下键入:
% /usr/5bin/cc [file.c] -lcurses
^^^^^^^
引进 curses.h 这个 library
或 % /usr/5bin/cc [file.c] -lcurses -ltermlib
(*注二)
■ 如何开始我的第一个 curses 程式?
在开始使用 curses 的一切命令之前, 您必须先利用 initscr()这个函式
来开启 curses 模式.
相对的, 在结束 curses 模式前 ( 通常在您结束程式前 ) 也必须以
endwin()来关闭 curses 模式.
#include curses.h
main()
{
initscr();
: :
: :
: :
endwin();
}
这是一般 curses 程式标准的模式.
此外, 您可以就您程式所须, 而做不同的设定. 当然, 您可以不做设定,而
只是呼叫 initscr().
您可以自己写一个函式来存放所有您所须要的设定. 平常使用时, 只要呼
叫这个函式即可启动 curses 并完成一切设定.
下面的例子, 即是笔者将平常较常用的一些设定放在一个叫 initial()的函
式内.
void initial()
{
initscr();
cbreak();
nonl();
noecho();
intrflush(stdscr,FALSE);
keypad(stdscr,TRUE);
refresh();
}
各函式分别介绍如下:
□ initscr()
initscr() 是一般 curses 程式必须先呼叫的函数, 一但这个函数
被呼叫之後, 系统将根据终端机的形态并启动 curses 模式.
□ endwin()
curses 通常以呼叫 endwin() 来结束程式. endwin() 可用来关闭
curses 模式, 或是暂时的跳离 curses 模式.假如您在程式中须要
call shell ( 如呼叫 system() 函式 ) 或是需要做 system call,
就必须先以 endwin() 暂时跳离 curses 模式. 最後再以
wrefresh() doupdate() 来重返 curses 模式.
□ cbreak()
nocbreak()
当 cbreak 模式被开启後, 除了 DELETE 或 CTRL 等仍被视为非凡
控制字元外一切输入的字元将马上被一一读取.当处於 nocbreak 模
式时, 从键盘输入的字元将被储存在 buffer 里直到输入 RETURN
或 NEWLINE.在较旧版的 curses 须呼叫 crmode(),nocrmode() 来
取代 cbreak(),nocbreak()
□ nl()
nonl()
用来决定当输入资料时, 按下 RETURN 键是否被对应为 NEWLINE 字
元 ( 如 ).
而输出资料时, NEWLINE 字元是否被对应为 RETURN 和 LINDFEED
系统预设是开启的.
□ echo()
noecho()
此函式用来控制从键盘输入字元时是否将字元显示在终端机上.系统
预设是开启的.
□ intrflush(win,bf)
呼叫 intrflush 时须传入两个值:
win 为一 WINDOW 型态指标, 通常传入标准输出入萤幕 stdscr
bf 为 TRUE 或 FALSE
当 bf 为 true 时, 当输入中断字元 ( 如 break) 时, 中断的反应
将较为快速.但可能会造成萤幕的错乱.
□ keypad(win,bf)
呼叫 keypad 时须传入两个值:
win 为一 WINDOW 型态指标, 通常传入标准输出入萤幕 stdscr
bf 为 TRUE 或 FALSE
当开启 keypad 後, 可以使用键盘上的一些非凡字元, 如上下左右
等方向键, curses 会将这些非凡字元转换成 curses.h 内定义的一
些非凡键. 这些定义的非凡键通常以 KEY_ 开头.
□ refresh()
refresh() 为 curses 最常呼叫的一个函式.
curses 为了使萤幕输出入达最佳化, 当您呼叫萤幕输出函式企图改
变萤幕上的画面时, curses 并不会马上对萤幕做改变, 而是等到
refresh() 呼叫後, 才将刚才所做的变动一次完成. 其馀的资料将
维持不变. 以尽可能送最少的字元至萤幕上. 减少萤幕重绘的时间.
假如是 initscr() 後第一次呼叫 refresh(), curses 将做清除萤
幕的工作.
■ 游标的控制
move(y,x) 将游标移动至 x,y 的位置
getyx(win,y,x) 得到目前游标的位置
(请注重! 是 y,x 而不是 &y,&x )
■ 有关清除萤幕的函式
clear()
erase() 将整个萤幕清除
(请注重配合refresh() 使用)
■ 如何在萤幕上显示字元
echochar(ch) 显示某个字元
addch(ch) 显示某个字元
mvaddch(y,x,ch) 在(x,y) 上显示某个字元
相当於呼叫 move(y,x);addch(ch);
addstr(str) 显示一串字串
mvaddstr(y,x,str) 在(x,y) 上显示一串字串
相当於呼叫 move(y,x);addstr(str);
printw(format,str) 类似 printf() , 以一定的格式输出至萤幕
mvprintw(y,x,format,str) 在(x,y) 位置上做 printw 的工作.
相当於呼叫 move(y,x);printw(format,str);
■ 如何从键盘上读取字元
getch() 从键盘读取一个字元 (注重! 传回的是整数值)
getstr() 从键盘读取一串字元
scanw(format,&arg1,&arg2...) 如同 scanf, 从键盘读取一串字元
□例:
int ch;
char string1[80]; /* 请注重! 不可宣告为 char *string1; */
char string2[80];
echo(); /* 开启 echo 模式, 使输入马上显示在萤幕上 */
ch=getch();
string1=getstr();
scanw("%s",string2);
mvprintw(10,10,"String1=%s",string1);
mvprintw(11,10,"String2=%s",string2);
■ 如何利用方向键
curses 将一些如方向键等非凡控制字元, 以 KEY_ 为开头定义在 curses.h
这个档案里头, 如 KEY_UP 即代表方向键的 " ↑ ". 但, 假如您想使用
curses.h 所为您定义的这些非凡键的话, 您就必须将 keypad 设定为
TRUE. 否则, 您就必须自己为所有的非凡键定义了.
curses.h 为一些非凡键的定义如下:
KEY_UP 0403 ↑
KEY_DOWN 0402 ↓
KEY_LEFT 0404 ←
KEY_RIGHT 0405 →
KEY_HOME 0406 Home key (upward+left arrow)
KEY_BACKSPACE 0407 backspace (unreliable)
KEY_F0 0410 Function keys.
KEY_F(n) (KEY_F0+(n)) Formula for f .
KEY_NPAGE 0522 Next page
KEY_PPAGE 0523 Previous page
以上仅列出笔者较常使用的一些控制键, 至於其他控制键的定义, 请自行参
阅 man curses (* 注三)
一并为您列出其他常用的一些非凡字元
[TAB] /t
[ENTER] /r
[ESC] 27
[BACKSPACE] 127