UNIX 螢?zāi)粚?dǎo)向程式的發(fā)展利器 - curses
校園網(wǎng)路策進(jìn)會(huì)
會(huì)長 林建宏
■ 前言
相信您在網(wǎng)路上一定用過如 tin,elm 等工具, 這些軟體有項(xiàng)共同的特色,
即他們能利用上下左右等方向鍵來控制游標(biāo)的位置. 除此之外, 這些程式
的畫面也較為美觀. 對(duì) Programming 有興趣的朋友一定對(duì)此感到好奇, 也
許他能在 PC 上用 Turbo C 輕易地寫出類似的程式, 然而, 但當(dāng)他將相同
的程式一字不變地移到工作站上來編譯時(shí), 卻出現(xiàn)一堆抓也抓不完的錯(cuò)誤.
其實(shí), 原因很簡單, 他使用的函式庫可能在 UNIX 上是沒有定義的. 有些
在 Turbo-C 上被廣泛使用的一些函式, 可能在 UNIX 上是不被定義的.
為了因應(yīng)網(wǎng)路上各式各樣的終端機(jī)形態(tài) (terminal), UNIX 上特別發(fā)展出
一套函式庫, 專門用來處理 UNIX 上游標(biāo)移動(dòng)及螢?zāi)坏娘@示. 這就是本篇
文章要為您介紹的 - curses.h 函式庫. 利用這個(gè)函式庫, 您也可以寫出
像 elm 般利用方向鍵來移動(dòng)光棒位置的程式. (CCCA 近來所提供的線上選
課程式, 及程式服務(wù)界面, 即是筆者利用 curses 發(fā)展而成的 )
■ curses 的歷史與版本
cureses 最早是由柏克萊大學(xué)的 Bill Joy 及 Ken Arnold 所發(fā)展出來的.
當(dāng)時(shí)發(fā)展此一函式庫主要原因是為了提高程式對(duì)不同終端機(jī)的相容性而設(shè)
計(jì)的. 因此, 利用 curses 發(fā)展出來的程式將和您所使用的終端機(jī)無關(guān).
也就是說, 您不必?fù)?dān)心您的程式因?yàn)閾Q了一部終端機(jī)而無法使用. 這對(duì)程
式設(shè)計(jì)師而言, 尤其是網(wǎng)路上程式的撰寫, 是件相當(dāng)重要的一件事.
curses之所以能對(duì)上百種以上的終端機(jī)工作, 是因?yàn)樗鼘⑺薪K端機(jī)的資
料, 存放在一個(gè)叫 termcap 的資料庫, ( 而在第二版的 System V 系統(tǒng)中
, 新版的 curses 以 terminfo 取代原來的 termcap). 有了這些記錄, 程
式就能夠知道遇到哪一種終端機(jī)時(shí), 須送什麼字元才能移動(dòng)游標(biāo)的位置,
送什麼字元才能清除整個(gè)螢?zāi)磺宄? (* 注一)
另外, 本文的介紹 以 System V 的 curses 版本為主.
■ 如何在您的程式使用 curses ?
在您的 C 程式的檔頭將 <curses.h> include 進(jìn)來.當(dāng)您引進(jìn) curses.h
這個(gè)函式庫後, 系統(tǒng)會(huì)自動(dòng)將 <stdio.h> 和 <unctl.h>一并 include 進(jìn)
來.另外, 在 System V 版本中, <terminfo.h> 這個(gè)函式庫也將一并
include進(jìn)來.
#include <curses.h>
main()
{
: :
: :
}
當(dāng)然, 您的系統(tǒng)內(nèi)必須放有 curses.h 這個(gè)函式庫.
■ 如何編譯(compile)
當(dāng)您編輯好您的程式, 在 UNIX 提示符號(hào)下鍵入:
% /usr/5bin/cc [file.c] -lcurses
^^^^^^^
引進(jìn) curses.h 這個(gè) library
或 % /usr/5bin/cc [file.c] -lcurses -ltermlib
(*注二)
■ 如何開始我的第一個(gè) curses 程式?
在開始使用 curses 的一切命令之前, 您必須先利用 initscr()這個(gè)函式
來開啟 curses 模式.
相對(duì)的, 在結(jié)束 curses 模式前 ( 通常在您結(jié)束程式前 ) 也必須以
endwin()來關(guān)閉 curses 模式.
#include <curses.h>
main()
{
initscr();
: :
: :
: :
endwin();
}
這是一般 curses 程式標(biāo)準(zhǔn)的模式.
此外, 您可以就您程式所須, 而做不同的設(shè)定. 當(dāng)然, 您可以不做設(shè)定,而
只是呼叫 initscr().
您可以自己寫一個(gè)函式來存放所有您所須要的設(shè)定. 平常使用時(shí), 只要呼
叫這個(gè)函式即可啟動(dòng) curses 并完成一切設(shè)定.
下面的例子, 即是筆者將平常較常用的一些設(shè)定放在一個(gè)叫 initial()的函
式內(nèi).
void initial()
{
initscr();
cbreak();
nonl();
noecho();
intrflush(stdscr,FALSE);
keypad(stdscr,TRUE);
refresh();
}
各函式分別介紹如下:
□ initscr()
initscr() 是一般 curses 程式必須先呼叫的函數(shù), 一但這個(gè)函數(shù)
被呼叫之後, 系統(tǒng)將根據(jù)終端機(jī)的形態(tài)并啟動(dòng) curses 模式.
□ endwin()
curses 通常以呼叫 endwin() 來結(jié)束程式. endwin() 可用來關(guān)閉
curses 模式, 或是暫時(shí)的跳離 curses 模式.如果您在程式中須要
call shell ( 如呼叫 system() 函式 ) 或是需要做 system call,
就必須先以 endwin() 暫時(shí)跳離 curses 模式. 最後再以
wrefresh() doupdate() 來重返 curses 模式.
□ cbreak()
nocbreak()
當(dāng) cbreak 模式被開啟後, 除了 DELETE 或 CTRL 等仍被視為特殊
控制字元外一切輸入的字元將立刻被一一讀取.當(dāng)處於 nocbreak 模
式時(shí), 從鍵盤輸入的字元將被儲(chǔ)存在 buffer 里直到輸入 RETURN
或 NEWLINE.在較舊版的 curses 須呼叫 crmode(),nocrmode() 來
取代 cbreak(),nocbreak()
□ nl()
nonl()
用來決定當(dāng)輸入資料時(shí), 按下 RETURN 鍵是否被對(duì)應(yīng)為 NEWLINE 字
元 ( 如 \n ).
而輸出資料時(shí), NEWLINE 字元是否被對(duì)應(yīng)為 RETURN 和 LINDFEED
系統(tǒng)預(yù)設(shè)是開啟的.
□ echo()
noecho()
此函式用來控制從鍵盤輸入字元時(shí)是否將字元顯示在終端機(jī)上.系統(tǒng)
預(yù)設(shè)是開啟的.
□ intrflush(win,bf)
呼叫 intrflush 時(shí)須傳入兩個(gè)值:
win 為一 WINDOW 型態(tài)指標(biāo), 通常傳入標(biāo)準(zhǔn)輸出入螢?zāi)?stdscr
bf 為 TRUE 或 FALSE
當(dāng) bf 為 true 時(shí), 當(dāng)輸入中斷字元 ( 如 break) 時(shí), 中斷的反應(yīng)
將較為快速.但可能會(huì)造成螢?zāi)坏腻e(cuò)亂.
□ keypad(win,bf)
呼叫 keypad 時(shí)須傳入兩個(gè)值:
win 為一 WINDOW 型態(tài)指標(biāo), 通常傳入標(biāo)準(zhǔn)輸出入螢?zāi)?stdscr
bf 為 TRUE 或 FALSE
當(dāng)開啟 keypad 後, 可以使用鍵盤上的一些特殊字元, 如上下左右
等方向鍵, curses 會(huì)將這些特殊字元轉(zhuǎn)換成 curses.h 內(nèi)定義的一
些特殊鍵. 這些定義的特殊鍵通常以 KEY_ 開頭.
□ refresh()
refresh() 為 curses 最常呼叫的一個(gè)函式.
curses 為了使螢?zāi)惠敵鋈脒_(dá)最佳化, 當(dāng)您呼叫螢?zāi)惠敵龊狡髨D改
變螢?zāi)簧系漠嬅鏁r(shí), curses 并不會(huì)立刻對(duì)螢?zāi)蛔龈淖? 而是等到
refresh() 呼叫後, 才將剛才所做的變動(dòng)一次完成. 其馀的資料將
維持不變. 以盡可能送最少的字元至螢?zāi)簧? 減少螢?zāi)恢乩L的時(shí)間.
如果是 initscr() 後第一次呼叫 refresh(), curses 將做清除螢
幕的工作.
■ 游標(biāo)的控制
move(y,x) 將游標(biāo)移動(dòng)至 x,y 的位置
getyx(win,y,x) 得到目前游標(biāo)的位置
(請(qǐng)注意! 是 y,x 而不是 &y,&x )
■ 有關(guān)清除螢?zāi)坏暮?/pre>
clear()
erase() 將整個(gè)螢?zāi)磺宄?
(請(qǐng)注意配合refresh() 使用)
■ 如何在螢?zāi)簧巷@示字元
echochar(ch) 顯示某個(gè)字元
addch(ch) 顯示某個(gè)字元
mvaddch(y,x,ch) 在(x,y) 上顯示某個(gè)字元
相當(dāng)於呼叫 move(y,x);addch(ch);
addstr(str) 顯示一串字串
mvaddstr(y,x,str) 在(x,y) 上顯示一串字串
相當(dāng)於呼叫 move(y,x);addstr(str);
printw(format,str) 類似 printf() , 以一定的格式輸出至螢?zāi)?
mvprintw(y,x,format,str) 在(x,y) 位置上做 printw 的工作.
相當(dāng)於呼叫 move(y,x);printw(format,str);
■ 如何從鍵盤上讀取字元
getch() 從鍵盤讀取一個(gè)字元 (注意! 傳回的是整數(shù)值)
getstr() 從鍵盤讀取一串字元
scanw(format,&arg1,&arg2...) 如同 scanf, 從鍵盤讀取一串字元
□例:
int ch;
char string1[80]; /* 請(qǐng)注意! 不可宣告為 char *string1; */
char string2[80];
echo(); /* 開啟 echo 模式, 使輸入立刻顯示在螢?zāi)簧?*/
ch=getch();
string1=getstr();
scanw("%s",string2);
mvprintw(10,10,"String1=%s",string1);
mvprintw(11,10,"String2=%s",string2);
■ 如何利用方向鍵
curses 將一些如方向鍵等特殊控制字元, 以 KEY_ 為開頭定義在 curses.h
這個(gè)檔案里頭, 如 KEY_UP 即代表方向鍵的 " ↑ ". 但, 如果您想使用
curses.h 所為您定義的這些特殊鍵的話, 您就必須將 keypad 設(shè)定為
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
以上僅列出筆者較常使用的一些控制鍵, 至於其他控制鍵的定義, 請(qǐng)自行參
閱 man curses (* 注三)
一并為您列出其他常用的一些特殊字元
[TAB] /t
[ENTER] /r
[ESC] 27
[BACKSPACE] 127
■ 如何改變螢?zāi)伙@示字元的屬性
為了使輸出的螢?zāi)划嬅娓鼮樯鷦?dòng)美麗, 我們常須要在螢?zāi)簧献鲆恍┤绶窗?
閃爍等變化. curses 定義了一些特殊的屬性, 透過這些定義, 我們也可以
在 curses 程式□控制螢?zāi)坏妮敵鲎兓?
attron(mod) 開啟屬性
attroff(mod) 關(guān)閉屬性
curses.h 里頭定義了一些屬性, 如:
A_UNDERLINE 加底線
A_REVERSE 反白
A_BLINK 閃爍
A_BOLD 高亮度
A_NORMAL 標(biāo)準(zhǔn)模式 (只能配合 attrset() 使用)
當(dāng)使用 attron() 開啟某一種特殊屬性模式後, 接下來在螢?zāi)坏妮敵龆紩?huì)以
該種屬性出現(xiàn). 直到您呼叫 attroff() 將此模式關(guān)閉.
請(qǐng)注意, 當(dāng)您欲 attron() 開啟另一種屬性時(shí), 請(qǐng)記得利用 attroff()先關(guān)
閉原來的屬性, 或直接以 attrset(A_NORMAL) 將所有特殊屬性關(guān)閉.否則,
curses 會(huì)將兩種屬性做重疊處理.
□例:
attrset(A_NORMAL); /* 先將屬性設(shè)定為正常模式 */
attron(A_UNDERLINE); /* 加底線 */
mvaddstr(9,10,"加底線"); /* 加底線輸出一串字元 */
attroff(A_UNDERLINE); /* 關(guān)閉加底線模式, 恢復(fù)正常模式 */
attron(A_REVERSE); /* 開啟反白模式 */
mvaddstr(10,10,"反白"); /* 輸出一串反白字元 */
attroff(A_REVERSE); /* 關(guān)閉反白模式, 恢復(fù)正常模式 */
attron(A_BLINK); /* 開啟閃爍模式 */
mvaddstr(11,10,"閃爍"); /* 輸出一串閃爍字元 */
attroff(A_BLINK); /* 關(guān)閉閃爍模式, 恢復(fù)正常模式 */
attron(A_BOLD); /* 開啟高亮度模式 */
mvaddstr(12,10,"高亮度"); /* 輸出一串高亮度字元 */
attroff(A_BOLD); /* 關(guān)閉高亮度模式, 恢復(fù)正常模式 */
■ 其他常用的一些函式
beep() 發(fā)出一聲嗶聲
box(win,ch1,ch2) 自動(dòng)畫方框 ch1: 畫方框時(shí)垂直方向所用字元
ch2: 畫方框時(shí)水平方向所用字元
example: box(stdscr,'|','-');
將以 | 及 - 圍成一個(gè)方框
■ 應(yīng)用完整□例
下面所舉的例子, 即完全利用剛剛所介紹的含式來完成.這個(gè)程式可將從鍵
盤上讀取的字元顯示在螢?zāi)簧? 并且可以上下左右方向鍵來控制游標(biāo)的位置
, 當(dāng)按下 [ESC] 後, 程式即結(jié)束.
您有沒有發(fā)現(xiàn), 這不就是一個(gè)簡單全螢?zāi)痪庉嬈鞯碾r形嗎?
#include <curses.h> /* 引進(jìn) curses.h , 并自動(dòng)引進(jìn) stdio.h */
#define StartX 1 /* 決定游標(biāo)初始位置 */
#define StartY 1
void initial();
main()
{
int x=StartX; /* 宣告 x,y 并設(shè)定其初值 */
int y=StartY;
int ch; /* 宣告 ch 為整數(shù),配合 getch() 使用 */
initial(); /* 呼叫 initial(), 啟動(dòng) curses 模式, */
/* 并完成其它設(shè)定 */
box(stdscr,'|','-'); /* 畫方框 */
attron(A_REVERSE); /* 開啟反白模式 */
mvaddstr(0,20,"Curses Program"); /* 在 (20,0) 處輸出反白字元 */
attroff(A_REVERSE); /* 關(guān)閉反白模式 */
move(x,y); /* 將游標(biāo)移至初始位置 */
do { /* 以無限回圈不斷等待輸入 */
ch=getch(); /* 等待自鍵盤輸入字元
switch(ch) { /* 判斷輸入字元為何 */
case KEY_UP: --y; /* 判斷是否"↑"鍵被按下 */
break;
case KEY_DOWN: ++y; /* 判斷是否"↓"鍵被按下 */
break;
case KEY_RIGHT: ++x; /* 判斷是否"→"鍵被按下 */
break;
case KEY_LEFT: --x; /* 判斷是否"←"鍵被按下 */
break;
case '\r': /* 判斷是否 ENTER 鍵被按下 */
++y;
x=0;
break;
case '\t': /* 判斷是否 TAB 鍵被按下 */
x+=7;
break;
case 127: /* 判斷是否 BACKSPACE 鍵被按下 */
mvaddch(y,--x,' ');/* delete 一個(gè)字元 */
break;
case 27: endwin(); /* 判斷是否[ESC]鍵被按下 */
exit(1); /* 結(jié)束 curses 模式 */
/* 結(jié)束此程式 */
default:
addch(ch); /* 如果不是特殊字元, 將此字元印出 */
x++;
break;
}
move(y,x); /* 移動(dòng)游標(biāo)至現(xiàn)在位置 */
} while (1);
}
void initial() /* 自定開啟 curses 函式 */
{
initscr();
cbreak();
nonl();
noecho();
intrflush(stdscr,FALSE);
keypad(stdscr,TRUE);
refresh();
}
■ 後記
學(xué)完了上述的一些命令, 相不相信您已經(jīng)可以寫出一個(gè)漂亮的全螢?zāi)痪庉?
器了? 事實(shí)上, curses 提供的函式不下 200 個(gè), 可是筆者認(rèn)為, 一切再
復(fù)雜的函式都可以用本文提到的一些組合變化而成, 學(xué)了太多的函式, 只
是徒增自己困擾罷了. 當(dāng)然, 如果您對(duì)其它函式有興趣, 可以自行參閱
curses 說明檔. ( 方法: % man curses ) 本文不過行拋磚引玉之效, 也
希望未來能陸續(xù)出現(xiàn)更多同學(xué)自行創(chuàng)作的程式.
* 任何疑問及建議, 歡迎 e-mail 至 ljh@CCCA.NCTU.edu.tw. 謝謝 ! *
注一:
請(qǐng)參考 /usr/share/lib/termcup
/usr/share/lib/terminfo/s/sun
注二:
1.如果是 BSD 的版本, 需使用
cc [file.c] -lcurses -ltermcap 來完成 compile.
2.計(jì)中工作站不知何故將原來的 /usr/5bin/cc 更改為 /usr/5bin/cc.org
因此, 您若想在計(jì)中工作站 compile curses 程式.需以 /usr/5bin/cc.org
取代 /usr/5bin/cc , 否則 compile 可能發(fā)生錯(cuò)誤.
3.較舊版的 curses 需同時(shí)引進(jìn) curses 和 termlib 這兩個(gè) library,
因此, 您必須使用 /usr/5bin/cc [file.c] -lcurses -ltermlib 來 compile.
注三:
根據(jù)筆者的經(jīng)驗(yàn), 上下左右方向鍵應(yīng)可正常使用而不會(huì)發(fā)生問題, 但其它
如 PgUp,PgDn,功能鍵,Home,End 等特殊鍵, 很容易因機(jī)器, 鍵盤不同而無
法使用, 因此, 若您的程式須要在不同的機(jī)器上使用, 建議您只用方向鍵來
控制, 其它的特殊鍵少用為妙.
至於 PgUp,PgDn 一些特殊鍵的控制方法, 由於較為復(fù)雜, 有興趣的同學(xué)可參
考 tin 原始程式 curses.c 內(nèi)所使用的一些方法.
UNIX 螢?zāi)粚?dǎo)向程式的發(fā)展利器 - curses (二)
校園網(wǎng)路策進(jìn)會(huì)
會(huì)長 林建宏
在上期為您介紹完了 curses.h 函式庫的一些基本函式呼叫後在, 在本期里
, 我們將繼續(xù)為您介紹 curses 有關(guān)多視窗處理的函式. 有了這些函式, 我們
可以在程式里同時(shí)處理多個(gè)不同的視窗. 如 joe 編輯器內(nèi)我們可將螢?zāi)磺懈?
成好幾個(gè)小螢?zāi)? 并且可以在這些不同的螢?zāi)婚g做切換并編輯不同的檔案, 這
就是多視處理的應(yīng)用. 另外, 有關(guān) POP-UP 視窗的制作, 以及視窗的卷動(dòng), 在
本文里, 我們將以簡單的例子, 告訴您這些功能是如何做到的. 關(guān)於一些較基
本函式的用法, 我們將不再特別介紹. 如果您尚未熟悉 curses 基本函式使用
方法, 請(qǐng)參閱上一期 (80 期 ) 通訊.
■ 視窗的建立
視窗的建立, 以 newwin() 這個(gè)函式來完成. 同時(shí), 需宣告此視窗為 WINDOW
結(jié)構(gòu)變數(shù).
WINDOW *newwin(lines,colums,start_y,start_x);
WINDOW *win;
win=newwin(10,20,0,0);
如此, 將以 (0,0) 為原點(diǎn), 取一個(gè) 10 列 20 行的矩形為一新的視窗. 今後
我們只要呼叫 win 這個(gè)變數(shù), 就可以對(duì)這新視窗做處理.
如: wmove(win,3,2);
■ 多視窗處理函式的格式
這一類函式和一般的基本函式極為類似, 幾乎每一個(gè)基本函式都有一個(gè)對(duì)應(yīng)的
視窗處理函式. 一般將 'w' 加在函式的里頭作為區(qū)別, 'w' 乃 'window' 之
意. 另外, 因?yàn)榭赏瑫r(shí)處理多個(gè)視窗, 在呼叫使用時(shí), 需特別指定欲處理的視
窗. 當(dāng)然, 如果您指定對(duì) stdscr 做處理, 由於是對(duì)標(biāo)準(zhǔn)輸出入螢?zāi)惶幚? 其
作用將相當(dāng)於一般基本的函式.
如:
wmove(win,y,x) 即對(duì) win 這個(gè)視窗做 move() 動(dòng)作.
wmove(stdscr,y,x) 相當(dāng)於 move(y,x)
介紹一些較重要的函式
wmove(win,y,x)
touchwin(win)
wrefresh(win)
mvwaddstr(win,y,x,str)
wattron(attr)
delwin(win)
subwin(win,ny,nx,y,x)
其他函式多和基本函式互為對(duì)應(yīng), 故不全部列出, 詳細(xì)名稱可參考 curses
的 online manual.
■ 視窗內(nèi)的座標(biāo)系
視窗內(nèi)的座標(biāo)系, 將以此視窗的起始點(diǎn)為新原點(diǎn), 并以其相對(duì)位置作為新的
座標(biāo). 舉例來說
win=newwin(10,20,5,5);
wmove(win,2,3);
將以 (5,5) 為新原點(diǎn), y 方向移動(dòng) 2 單位, x 方向移動(dòng) 3 單位. 因此實(shí)際
上, 游標(biāo)將移動(dòng)至 y=7 x=8 的位置上.
■ POP-UP 視窗的建立
利用 curses 所提供的視窗處理函式, 我們可以做出像 ONLINE HELP 的 POP
-UP 畫面. 當(dāng)按下某鍵後, 一個(gè)新的視窗將像 " 跳 " 出來一般覆蓋原來的畫
面. 當(dāng)關(guān)掉此視窗後, 又不會(huì)影響到原來被覆蓋的畫面.
下面的例子, 我們及模擬 ONLINE HELP 的形式, 當(dāng)按下 'h' 鍵時(shí), 視窗即出現(xiàn)
#include <curses.h>
main()
{
int ch,x,y;
WINDOW *win;
initscr(); ←┐
cbreak; │ 啟動(dòng) curses 模式
noecho(); │
nonl(); ←┘
win=newwin(4,30,LINES/2-3, COLS/2-15);/* 建立一個(gè)新視窗, 其中LINES,COLS
*/
box(win,'|','-'); /* 為 curses 內(nèi)定值,
即螢?zāi)恍?列數(shù)*/
mvwaddstr(win,1,4,"This is another screen");
mvwaddstr(win,2,2,"Press anykey to continue..");
for (y=0;y<LINES;++y) /* 以'@'填滿螢?zāi)?*/
for (x=0;x<COLS;++x)
mvprintw(y,x,"@");
for(;;) {
refresh();
ch=getch();
switch(ch) {
case 'q': /* 按 'q' 鍵離開 */
endwin();
exit(0);
case '\t': /* 按 [TAB] 鍵 呼叫另一視窗 */
touchwin(win); /* wrefresh() 前需 touchwin() */
wrefresh(win);
getch(); /* 按任意鍵關(guān)閉視窗 */
touchwin(stdscr);
break;
default:break;
}
}
}
執(zhí)行結(jié)果:
┌————————————————————————————┐
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
└————————————————————————————┘
↑ 原來畫面被 '@' 填滿, 按下[TAB]鍵後
↓ 出現(xiàn) POP-UP 畫面.
┌————————————————————————————┐
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
│ @@@@@@@@@@@@@□---------------------------+@@@@@@@@@@@ │
│ @@@@@@@@@@@@@| This is another screen |@@@@@@@@@@@ │
│ @@@@@@@@@@@@@| Press anykey to continue.. |@@@@@@@@@@@ │
│ @@@@@@@@@@@@@□---------------------------+@@@@@@@@@@@ │
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
│ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
└————————————————————————————┘
■ 視窗的卷動(dòng)
視窗的卷動(dòng), 掖Q用來配合視窗的處理, 當(dāng)我們持續(xù)對(duì)視窗輸出直到視窗的游
標(biāo)移動(dòng)至最後一列時(shí), 如果我們?cè)佥敵鲆涣谢蚴禽敵鲆粋€(gè)換行字元時(shí), 視窗可
整個(gè)往上卷動(dòng)一行. 這對(duì)我們撰寫一個(gè)編輯程式時(shí), 是尤其重要的, 一個(gè)畫面
無法卷動(dòng)的編輯器, 勢必?zé)o法處理超過一個(gè)螢?zāi)淮笮〉臋n案.
視窗的卷動(dòng)是預(yù)設(shè)為關(guān)閉的, 并以 scrollok() 來控制開閉.
scrollok(win,TRUE); 開啟
scrollok(win,FALSE); 關(guān)閉
下面的例子因?yàn)椴粩嗟剌敵?0,1,2.. 故將以一個(gè) 40 * 10 的視窗不停的卷動(dòng)
#include <curses.h>
main()
{
int i;
WINDOW *scrwin,*boxwin;
initscr(); ←┐
cbreak; │ 啟動(dòng) curses 模式
noecho(); │
nonl(); ←┘
scrwin=newwin(10,40,LINES/2-6,COLS/2-25); /* 設(shè)定另一視窗大小 */
boxwin=newwin(12,42,LINES/2-7,COLS/2-26); /* 設(shè)定外框視窗大小 */
scrollok(scrwin,TRUE); /* 開啟視窗卷動(dòng)功能 */
box(boxwin,'|','-');
refresh();
wrefresh(boxwin);
for (i=0;;++i) /* 不斷地在視窗內(nèi)輸出 0-8 的數(shù)字,使視窗卷動(dòng)
*/
{
wprintw(scrwin,"%d",i%9);
wrefresh(scrwin);
}
}
執(zhí)行結(jié)果:
┌——————————————————————┐
│ □---------------------□ │
│ |3456780123456780123412| ↑ 視 │
│ |3456780123456780123456| │ 窗 │
│ |7801234567801234567801| │ 不 │
│ |2345678012345678012345| │ 停 │
│ |6780123456780123456780| │ 往 │
│ |1234567801234567801234| │ 上 │
│ |5678012345678012345678| │ 卷 │
│ |0123456780123456780123| │ 動(dòng) │
│ □---------------------□ │
│ │
└——————————————————————┘
■ □例 - 模擬 joe 分割畫面同時(shí)編輯兩個(gè)檔案
在下面的例子里, 我們應(yīng)用了多視窗處理的函式, 改良上回介紹的編輯器,
在這個(gè)程式里, 我們可以同時(shí)編輯兩個(gè)畫面, 并以 [ESC] 做不同視窗間的
切換. 同時(shí), 按下 [TAB] 鍵, 會(huì)出現(xiàn) POP-UP 的 ONLINE HELP.
#include <curses.h>
void initial();
main()
{
WINDOW *win[2],*curwin,*helpwin;
int nowwin;
int x,y;
int i;
int ch;
initial();
win[0]=newwin(LINES/2-1,COLS-1,0,0); /* 設(shè)定兩個(gè)視窗的大小*/
win[1]=newwin(LINES/2-1,COLS-1,LINES/2,0);
helpwin=newwin(3,30,2,COLS/2-15 ); /* ONLINE HELP 的大小 */
box(helpwin,'|','-');
mvwaddstr(helpwin,0,10,"ONLINE HELP"); /* ONLINE HELP 的內(nèi)容 */
mvwaddstr(helpwin,1,4,"Hit any key to continue..");
for (i=0;i<COLS-1;++i) /* 畫兩個(gè)視窗間的界限 */
mvaddch(LINES/2-1,i,'-');
nowwin=0; /* 先指定游標(biāo)在第一視窗 */
curwin=win[nowwin];
getyx(curwin,y,x);
move(0,0);
refresh();
refresh();
do {
ch=getch();
switch(ch) {
case KEY_UP: --y; /* 判斷是否"↑"鍵被按下 */
break;
case KEY_DOWN: ++y; /* 判斷是否"↓"鍵被按下 */
break;
case KEY_RIGHT: ++x; /* 判斷是否"→"鍵被按下 */
break;
case KEY_LEFT: --x; /* 判斷是否"←"鍵被按下 */
break;
case '\r': /* 判斷是否 ENTER 鍵被按下 */
++y;
x=0;
break;
case '\t': /* 判斷是否 TAB 鍵被按下 */
touchwin(helpwin);
wrefresh(helpwin); /* 呼叫 ONLINE HELP */
getch();
touchwin(win[1-nowwin]); /* 重畫第一,二視窗 */
wrefresh(win[1-nowwin]);
touchwin(curwin);
wrefresh(curwin);
break;
case 127: /* 判斷是否 BACKSPACE 鍵被按下 */
wmove(curwin,y,--x);/* delete 一個(gè)字元 */
waddch(curwin,' ');
break;
case 27 : nowwin=1-nowwin; /* [ESC] 鍵切換視窗 */
curwin=win[nowwin];
getyx(curwin,y,x);
break;
default:
waddch(curwin,ch);
x++;
break;
}
wmove(curwin,y,x);
wrefresh(curwin);
} while(1);
}
void initial()
{
initscr(); ←┐
cbreak(); │ 啟動(dòng) curses 模式
nonl(); │
noecho(); ←┘
intrflush(stdscr,FALSE);
keypad(stdscr,TRUE);
refresh();
}
執(zhí)行結(jié)果:
┌—————————————————————————————┐
│ screen1 │
┌→ │ this is screen 1, you can press [ESC] to │
以 │ │ switch between screen 1 and screen 2. │
[ESC]│ │ │
切 │ │ │
換 │ │----------------------------------------------------------│
游 │ │ screen 2 │
標(biāo) │ │ │
位 └→ │ _ (游標(biāo)) │
置 │ │
└—————————————————————————————┘
↑ 按下[TAB] 鍵,出現(xiàn) ONLINE HELP
↓
┌—————————————————————————————┐
│ screen1 │
│ this is screen 1, you can press [ESC] to │
│ switch□--------ONLINE HELP--------□ │
│ | Hit any key to continue..| │
│ □---------------------------□ │
│----------------------------------------------------------│
│ screen 2 │
│ │
│ │
│ │
└—————————————————————————————┘
↑ 按任意鍵, ONLINE HELP 關(guān)閉
↓
┌—————————————————————————————┐
│ screen1 │
│ this is screen 1, you can press [ESC] to │
│ switch between screen 1 and screen 2. │
│ │
│ │
│----------------------------------------------------------│
│ screen 2 │
│ │
│ _ (游標(biāo)) │
│ │
└—————————————————————————————┘
■ 結(jié)語
我們以連續(xù)兩期來介紹 curses.h 函式庫的使用方法, 相信同學(xué)對(duì)撰寫這類的
程式應(yīng)該不再陌生. 所謂『戲法人人會(huì)變, 巧妙各有不同』. 知道了基本函式
的呼叫方法, 能不能寫出實(shí)用的程式, 就靠各位的巧思和創(chuàng)造力了.
有任何問題建議, 歡迎 E-mail 至 ljh@CCCA.NCTU.edu.tw , 謝謝 !
發(fā)信人: Cardinal.bbs@mic.ee.ntu.edu.tw (Cardinal), 信區(qū): unix
標(biāo) 題: Re: 請(qǐng)問誰會(huì)用 curses 顯示 ANSI color 字
發(fā)信站: 臺(tái)大電機(jī) Maxwell 站
首先聲明,這一封的內(nèi)容應(yīng)該屬於 programming board,但是現(xiàn)在有不止一個(gè)
人問我這個(gè)問題,所以在這個(gè)版再把詳細(xì)的方法說明一遍。如果有人看不懂而
仍然有興趣的,請(qǐng) mail 給我 (Cardinal.bbs@mic.ee.ntu.edu.tw) ,不要在
這邊 reply,我會(huì)考慮在私下或在 programming board解決你的問題。
==> 在 Cardinal@Maxwell (Cardinal) 的文章中提到:
: 1.開一個(gè) new window (newwin)
: 2.設(shè)定 window 的彩色屬性 (wattrset)
==> 在設(shè)定彩色屬性之前應(yīng)該先設(shè)定顏色的 "pair" ,所謂的 "pair" 是指
foreground及background的顏色。curses的顏色有下面幾種 (type為
short) :
COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE,
COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE
用init_pair(short pair, short f_color, short b_color)來設(shè)定 pair,
for example:
init_pair(100, COLOR_RED, COLOR_BLUE)
就設(shè)定了編號(hào)為 100, 藍(lán)底紅字的 color pair 了.
另外你要是嫌這幾種顏色太單調(diào)了, 可以用 init_color 來設(shè)定色彩, 細(xì)節(jié)
這邊就不談了.
然後就用 wattrset(WINDOW* pwindow, short color_pair)設(shè)定你window的
顏色, for example:
wattrset(pwindow, 100) 就設(shè)定了一個(gè)藍(lán)底紅字的 window (不要忘記
這兒的 100 是剛剛用 init_pair設(shè)定的值)
: 3.印在 window 的字就自動(dòng)變成那個(gè)顏色了 (mvwprintw, mvwaddstr, ...)
==> 這句... 該不會(huì)有問題吧.
: 4.想要印不同顏色的字,只要把那個(gè)字 "OR" (|) 不同的顏色即可 (記住,
: 這種有屬性的字要用 int,不能用 char)
==> 其實(shí)型別不是用 int, 而是用 chtype (不過沒有差別, 去查查 curses.h就
知道) , 譬如說, 你想要在剛剛設(shè)定為藍(lán)底紅字的 window印一個(gè)別的顏色的
'A' 字, 可以這麼做 :
init_pair(another_color_pair, COLOR_隨便, COLOR_隨便) -->先設(shè)定另一
個(gè) color pair
char cascii = 'A';
chtype cascii_color = cascii | another_color_pair;
再把 cascii_color 印出來就是一個(gè)你想要顏色的 A 了.
--
~ Cardinal ~
From: Cardinal (Cardinal)
Title: 關(guān)於精華區(qū)...
Date: Fri Mar 10 20:36:27 1995
您好:
在 programming 版精華區(qū) unix - curses libraries 中有一篇文章是我寫的,
剛剛來這邊找資料時(shí)翻到的, 真是受寵若驚. 不過原來的文章 (如何用 curses
顯示彩色) 有一點(diǎn)忘了提到, 希望您能把下面的說明加進(jìn)去:
1. init_color及init_pair 是 SystemVR3以後的標(biāo)準(zhǔn), 不適用於 BSD 或
SunOS.
2. 在 SunOS 上要達(dá)成這樣的目的, 我知道的有兩種解法
a.有一款大同的中文工作站有支援 init_color & init_pair 的 library
這一型的 library 與 SunOS 為 object-code compatible.
b.ncurses 支援 init_pair & init_color