[root@localhost ~]# ls -l /usr/include/*curses.h
lrwxrwxrwx 1 root root 16 12-02 21:39 /usr/include/curses.h -> ncurses/curses.h
lrwxrwxrwx 1 root root 17 12-02 21:39 /usr/include/ncurses.h -> ncurses/ncurses.h
[root@localhost ~]# ls -l /usr/lib/lib*curses*
lrwxrwxrwx 1 root root 12 12-02 21:39 /usr/lib/libcurses.a -> libncurses.a
lrwxrwxrwx 1 root root 17 12-02 21:39 /usr/lib/libcurses.so -> libncurses.so.5.5
lrwxrwxrwx 1 root root 13 12-02 21:39 /usr/lib/libcursesw.a -> libncursesw.a
lrwxrwxrwx 1 root root 18 12-02 21:39 /usr/lib/libcursesw.so -> libncursesw.so.5.5
-rw-r--r-- 1 root root 469236 2007-01-06 /usr/lib/libncurses.a
-rw-r--r-- 1 root root 123506 2007-01-06 /usr/lib/libncurses++.a
-rw-r--r-- 1 root root 584508 2007-01-06 /usr/lib/libncurses_g.a
lrwxrwxrwx 1 root root 15 12-02 21:39 /usr/lib/libncurses.so -> libncurses.so.5
lrwxrwxrwx 1 root root 17 12-02 21:36 /usr/lib/libncurses.so.5 -> libncurses.so.5.5
-rwxr-xr-x 1 root root 297464 2007-01-06 /usr/lib/libncurses.so.5.5
-rw-r--r-- 1 root root 123506 2007-01-06 /usr/lib/libncurses++w.a
-rw-r--r-- 1 root root 522346 2007-01-06 /usr/lib/libncursesw.a
-rw-r--r-- 1 root root 663886 2007-01-06 /usr/lib/libncursesw_g.a
lrwxrwxrwx 1 root root 16 12-02 21:39 /usr/lib/libncursesw.so -> libncursesw.so.5
lrwxrwxrwx 1 root root 18 12-02 21:36 /usr/lib/libncursesw.so.5 -> libncursesw.so.5.5
-rwxr-xr-x 1 root root 330428 2007-01-06 /usr/lib/libncursesw.so.5.5
gcc program.c -o promgram.exe -lcurses
gcc -I/usr/include/ncurses program.c -o program -lncurses
使用 curses 函数去更新一个逻辑屏幕;
请求 curses 通过 refresh 来刷新物理屏幕。
#include <unistd.h>
#include <stdlib.h>
#include <curses.h>
int main()
{
initscr();
move(5, 15);
printw("%s", "hello,wrold");
refresh();
sleep(3); /*休息3秒以看到效果*/
endwin();
exit(EXIT_SUCCESS);
}
#include <unistd.h>
#include <stdlib.h>
#include <curses.h>
int main()
{
initscr();
move(5, 15);
printw("%s", "hello,wrold");
refresh();
sleep(3);
endwin();
sleep(3);
clearok(stdscr, 1);
refresh();
sleep(3);
endwin();
exit(EXIT_SUCCESS);
}
#include <curses.h>
int addch(const chtype char_to_add);
int addchstr(chtype *const string_to_add);
int printfw(char *format, ...);
int refresh(void);
int box(WINDOW *win_ptr, chtype vertical_char, chtype horizontal_char);
int insch(chtype char_to_insert);
int insertln(void);
int delch(void);
int beep(void);
int flash(void);
在标准的 curses 里,你可能仅用到“正常”字符。而在扩展的 curses 中,你可以使用 ACS_VLINE 和 ACS_HLINE 这两个定义画出更好看的框子。基于此,你的终端需要支持画线字符(line-drawing characters)。通常的,这些在 xterm 窗口里会有更好的表现,而标准的控制台就差些。然而对这两者的支持差距却趋向日益变大,所以如果对移植性看得比较重要,那么还是要避免使用它们。
#include <curses.h>
chtype inch(void);
int instr(char *string);
int innstr(char *string, int number_of_characters);
inch() 函数总是可用的,但 instr() 和 innstr() 却不是总能被支持。 |
inch() 函数返回一个当前屏幕光标位置处那个字符以及它的属性信息。注意,inch 并不是返回一个字符,而是一个 chtype 类型的数据。 |
#include <curses.h>
int erase(void);
int clear(void);
int clrtobot(void);
int clrtoeol(void);
#include <curses.h>
int move(int new_y, int new_x);
int leaveok(WINDOW *window_ptr, bool leave_flag);
#include <curses.h>
int attron(chtype attribute);
int attroff(chtype attribute);
int attrset(chtype attribute);
int standout(void);
int standend(void);
#include <curses.h>
int echo(void);
int noecho(void);
int cbreak(void);
int nocbreak(void);
int raw(void);
int noraw(void);
#include <curses.h>
int getch(void);
int getstr(char *string);
int getnstr(char *string, int number_of_characters);
int scanw(char *format, ...);
[/table][table=100%,#f9f7ed] 这些函数与他们的 ”非curses“ 对等函数,如 getchar, gets, scanf 的行为非常相似。 注意:getstr() 函数并没有对返回的字符串的长度没做限制,所以必须非常小心的使用。 如果你的 curses 版本支持 getnstr,此函数可以限制读入字符的长度。建议使用此函数,而不是 getstr 。它们与 gets 和 fgets 的行为相类似。
作者: beyes 时间: 2009-3-21 14:13
标题: 测试 cbreak mode 示例
测试代码:
#include <unistd.h>
#include <stdlib.h>
#include <curses.h>
void input();
int main()
{
initscr();
standout();
move(9,10);
printw("%s", "----Test nocbeak mode----");
refresh();
standend();
input();
sleep(3);
standout();
move(9,10);
printw("----Test cbreak-mode----");
refresh();
standend();
cbreak();
input();
sleep(3);
nocbreak();
endwin();
exit(EXIT_SUCCESS);
}
void input()
{
move(10,10);
printw("%s", "Inpuut y/n: ");
refresh();
if(getch()== 'y' || getch()== 'n') {
move(12, 10);
printw("%s", "Input correct");
refresh();
} else {
move(12, 10);
printw("%s", "Input Wrong");
refresh();
}
}
程序说明:
一开始在默认情况下,程序处于 non-cbreak-mode 模式,也就是 getch() 需要等待键入回车后才能收到输入的字符;
在输入回车后,程序判断了输入字符的对错,然后进入 cbreak-mode 模式,这时候就只要输入一个字符,无需再回车,程序马上做出判断。此后显示完后退出。
作者: beyes 时间: 2009-3-21 14:50
标题: 键盘输入(模式与检测)测试示例
键盘控制测试代码:#include <unistd.h>
#include <stdlib.h>
#include <curses.h>
#include <string.h>
#define PW_LEN 256
#define NAME_LEN 256
int main()
{
char name[NAME_LEN];
char password[PW_LEN];
const char *real_password = "xyzzy";
int i = 0;
initscr();
printw("%s", "Please login:");
move(7, 10);
printw("%s", "User name: ");
getstr(name);
move(8, 10);
printw("%s", "Password: ");
refresh();
cbreak();
noecho();
/*内存初始化,password所指向的内存区域的sizeof()个字节空间都为'\\0'*/
memset(password, '\\0', sizeof(password));
while(i < 5) {
password = getch();
if(password == '\\n') break;
move(8, 20 + i);
addch('*');
refresh();
i++;
}
echo();
nocbreak();
move(11, 10);
if(strncmp(real_password, password, strlen(real_password)) == 0)
printw("%s", "Correct");
else printw("%s", "Wrong");
printw("%s", " password");
refresh();
sleep(3);
endwin();
exit(EXIT_SUCCESS);
}
程序说明:
使用 noecho 函数关闭了键盘输入的回显功能,用 cbreak 函数把输入情况设置为 ” 字符中止模式“ 之后,开辟了一小块内存做好接收口令的准备工作。构成口令的每一个字符一经敲入就立刻得到处理。随后,在屏幕的下一个位置显示一个 "*" 号,注意每次要对屏幕进行刷新。用户按下回车后,我们使用 strncmp 把刚输入的口令和保存在程序里的口令字进行比较。
作者: beyes 时间: 2009-3-21 19:09
标题: 窗口-窗口的结构
在物理屏幕上也可以同时显示多个不同尺寸的窗口。
stdscr 只是 WINDOW 结构的一个特例,就像 stdout 是一个文件流的特例一样。
WINDOW 结构一般在 curses.h 中有声明,对它的存取操作必须感规定的指令进行,程序永远不允许直接访问它,因为这个结构依赖于 curses 函数库的具体实现,它在不同版本的 curses 函数库里会有所变化。
你可以通过 newwin 和 delwin 来创建和销毁窗口:
#include <curses.h>
WINDOW *newwin(int num_of_lines, int num_of_cols, int start_y, int start_x);
int delwin(WINDOW *window_to_delete);
newwin 函数创建一个新的窗口,窗口从屏幕位置(start_y, start_x)开始,尺寸由 num_of_lines 和 num_of_cols 来表示。此函数返回一个指向新窗口的指针,若创建失败则返回 null 。如果想让新窗口的右下角正好落在屏幕的右下角位置上,可以把它的行数和列数设置为 0 。窗口不论新旧大小,都不能超过当前屏幕。所以,若是新窗口的任何部分落在屏幕区域以外的地方 newwin 就会失败。由 newwin 创建的新窗口完全独立于任何现存的任何窗口。默认情况下,它会放在任何显存窗口的上面,遮盖(但不是改变)他们的内容。
delwin 函数删除由 newwin 创建的前一个窗口。因为调用 newwin 时可能已经分配过内存,所以当不再需要时应该将其删除掉。
注意:永远不要删除 curses 自己的窗口 -- stdscr 和 curscr 。
作者: beyes 时间: 2009-3-21 20:31
标题: 窗口--通用化函数(generalized functions)
在前面已经使用过 addch 和 printw 函数在屏幕上增添字符。与其它函数一起,它们都可以在前面添加个前缀。如:
前缀 w 表示对窗口进行操作;
前缀 mv 表示光标移动;
前缀 mvw 表示移动和对。
如果到 curses 头文件里查看更多的 curses 实现,就会发现在前面接触到的许多函数是一些简单的宏定义(#define 语句),宏定义的内容则是更通用化的函数。
如果添加了 w 前缀,就必须在它的参数表面前增加一个 WINDOW 指针;
如果添加了 mv 前缀,就需要添加两个参数 --屏幕纵坐标 x 和屏幕横坐标 y ,这两个坐标值设设定了执行这一操作的屏幕位置。y 和 x 是相对于窗口的坐标值而不是相对于屏幕的坐标值,(0, 0)代表的是窗口的左上角。
添加了 mvw 前缀,需要添加 3 个参数,一个是 WINDOW 指针,另外两个是 y 和 x 值。WINOW 指针永远出现在屏幕坐标值的前面,可有时候从函数前缀上看好像应该把 y 和 x 坐标放在最前面,遇到这种情况千万不要犯糊涂,否则容易出问题。
下面是 addch 和 printw 系列全体函数的框架定义:
其它一些函数,比如 inch, 也有加 “mv" 和 "mvw" 前缀的通用化变体。#include <curses.h>
int addch(const chtype char);
int waddch(WINDOW *window_pointer, const chtype char);
int mvaddch(int y, int x, const chtype char);
int mvwaddch(WINDOW *window_pointer, int y, int x, const chtype char);
int printw(char *format, ...);
int wprintw(WINDOW *window_pointer, char *format, ...);
int mvprintw(int y, int x, char *format, ...);
int mvwprintw(WINDOW *window_pointer, int y, int x, char *format, ...);
作者: beyes 时间: 2009-3-21 20:59
标题: 窗口--移动和刷新窗口
以下函数可以移动和重新绘制窗口:
#include <curses.h>
int mvwin(WINDOW *window_to_move, int new_y, int new_x);
int wrefresh(WINDOW *window_ptr);
int wclear(WINDOW *window_ptr);
int werase(WINDOW *window_ptr);
int touchwin(WINDOW *window_ptr);
int scrollok(WINDOW *window_ptr, bool scroll_flag);
int scroll(WINDOW *window_ptr);
mvwin 函数在屏幕上移动窗口。因为不允许窗口任何部分超出屏幕区域。mvwin 如果试图移动窗口以致于它的任何部分落在了屏幕区域外头,那么忙这个函数就会失败。
wrefresh, wclear 和 werase 函数只是在前面所见过的函数的通用化版本。它们在加入了 WINDOW 指针后,它们就可以对指定的窗口进行操作,而不局限于 stdscr 。
touchwin 有些特殊。它通知 curses 库由参数指针所指向的窗口已经发生改变。这意味着,在下一次调用 wrefresh 时 curses 总将会重新绘制那个窗口。这个函数对于在屏幕上有几个函数堆叠的时需要安排哪一个窗口出来显示非常有用。
两个 scroll 函数控制窗口的卷屏情况。 scrollok 函数,当传递一个布尔值 true 时,其允许一个窗口卷屏。默认地,窗口是不会卷屏的。
scroll 函数就是简单的把窗口中的内容往上卷一行。一些 curses 实现也有 wsctl 函数,这可以一次卷几行内容,这个参数值还可能是负数。
作者: beyes 时间: 2009-3-22 01:35
标题: 窗口函数的应用示例(多窗口)
测试代码:
#include <unistd.h>
#include <stdlib.h>
#include <curses.h>
int main()
{
WINDOW *new_window_ptr;
WINDOW *popup_window_ptr;
int x_loop;
int y_loop;
char a_letter = 'a';
initscr();
move(5, 5);
printw("%s", "Testing multiple windows");
refresh();
sleep(2);
for(y_loop = 0; y_loop < LINES - 1; y_loop++) {
for(x_loop = 0; x_loop < COLS - 1; x_loop++) {
mvwaddch(stdscr, y_loop, x_loop, a_letter);
a_letter++;
if(a_letter > 'z') a_letter = 'a';
}
}
/*刷新屏幕*/
refresh();
sleep(3);
new_window_ptr = newwin(10, 20, 5, 5);/*创建新窗口*/
mvwprintw(new_window_ptr, 2, 2, "%s", "Hello World");
mvwprintw(new_window_ptr, 5, 2, "%s",
"Notice how very long lines wrap inside the window");
wrefresh(new_window_ptr);
sleep(3);
a_letter = '0';
for(y_loop = 0; y_loop < LINES - 1; y_loop++) {
for(x_loop = 0; x_loop < COLS - 1; x_loop++) {
mvwaddch(stdscr, y_loop, x_loop, a_letter);
a_letter++;
if(a_letter > '9')
a_letter = '0';
}
}
refresh();
sleep(3);
/*这里刷新并不会改变什么,因为确实没对new_window做什么改变*/
wrefresh(new_window_ptr);
sleep(6);
/*new_window会被刷新带到前面来,因为 touchwin 函数欺骗了 curses ,以致 curses 认为 new_window 已经发生改变*/
touchwin(new_window_ptr);
wrefresh(new_window_ptr);
sleep(3);
popup_window_ptr = newwin(10, 20, 8, 8);
box(popup_window_ptr, '+', '~'); /*给窗口指定边框*/
mvwprintw(popup_window_ptr, 5, 2, "%s", "Pop Up Window!");
wrefresh(popup_window_ptr);
sleep(3);
touchwin(new_window_ptr); /*切换到 new_window */
wrefresh(new_window_ptr);
sleep(3);
wclear(new_window_ptr); /*对 new_window 进行清屏*/
wrefresh(new_window_ptr);
sleep(3);
delwin(new_window_ptr); /*不再需要 new_window,释放掉*/
touchwin(popup_window_ptr); /*切换到 popup_window */
wrefresh(popup_window_ptr);
sleep(3);
delwin(popup_window_ptr); /*释放掉 popup */
touchwin(stdscr);
refresh();
sleep(3);
endwin();
exit(EXIT_SUCCESS);
}
说明:
curses 在刷新窗口的时候并不考虑窗口在屏幕上出现的先后顺序,所谓窗口之间的上下继承关系也无从谈起。为了确保 curses 能够以正确的顺序绘制窗口,就必须以正确的顺序对他们进行刷新。解决这个问题的办法之一是把全体窗口的指针都保存到一个数组或列表里,通过这个数组来安排它们显示到屏幕上去的正确顺序。
作者: beyes 时间: 2009-3-22 01:49
标题: 窗口--优化窗口刷新
在上帖的例子中可见,刷新多窗口需要一些技巧,但并不至于无章可循。然而在一个慢速网络上,当刷新终端时,可能会出现较严重的潜在问题。幸运的是,这个问题在现在来说已经很少存在了。
我们的目标是,要尽可能的减少往屏幕上绘制的字符个数,因为在慢速的链路上对屏幕的绘制可能慢得令人无法忍受。curses 提供了一个非常手段 -- 用以下的两个函数:
#include <curses.h>
int wnoutrefresh(WINDOW *window_ptr);
int doupdate(void);
wnoutrefresh 函数决定需要往屏幕上送哪个字符,但实际上并没有发送它们。真正把字符发送到中断的工作由 doupdate 来完成。
如果你简单的调用 wnoutrefresh ,然后紧跟着调用 doupdate,其效果与调用 wrefresh 一样。
如果想重新绘制一叠窗口,可以对每个窗口都调用 wnoutrefresh (当然要有正确的顺序),然后在最后一个 wnoutrefresh 后统一调用 doupdate 。这样就可以让 curses 对轮流的对每一个窗口执行更新计算工作然后输出到屏幕上来。这种做法可以最大限度地减少需要 curses 发送的字符个数。
作者: beyes 时间: 2009-3-22 15:06
标题: 窗口 -- 子窗口
多窗口的一种特殊情况是 "子窗口"。可以使用以下函数创建与销毁子窗口:
#include <curses.h>
WINDOW *subwin(WINDOW *parent, int num_of_lines, int num_of_cols, int start_y, int start_x);
int delwin(WINDOW *window_to_delete);
subwin 函数有着和 newwin 几乎完全一样的参数,删除子窗口和删除其它窗口一样,也是用 delwin 函数。
和对待新的窗口一样,我们可以使用 delwin 来删除子窗口,而且还可以使用带有 mvw 前缀的相关函数去写子窗口。确实,在大部分时候,子窗口的行为特征和新窗口非常相像,但是有一个非常重要的区别:
子窗口没有独立的屏幕字符存储区,不保存自己屏幕字符的集合;它和父窗口共享着同一块字符存储区,这个区域及其大小是创建子窗口时由父窗口指定的。这就意味着,在子窗口中的任何改变都将映射到它的父窗口中去;当子窗口被删除时,屏幕不会改变什么。
这乍一看(at first sight),子窗口看起来似乎没什么用。那为什么不直接就修改父窗口里的内容呢?
子窗口的主要用途是卷动其它窗口中里的部分内容。在编写 curses 程序时,需要卷动屏幕中一小块子区域那太稀松平常不过了。通过创建一个子窗口然后对子窗口进行卷屏,你可以得到你想要的结果。
使用子窗口的一个限制是:应用程序必须在刷新屏幕之前必须在父窗口里调用 touchwin 函数。
作者: beyes 时间: 2009-3-23 12:25
标题: 子窗口应用示例
测试代码:
#include <unistd.h>
#include <stdlib.h>
#include <curses.h>
int main()
{
WINDOW *sub_window_ptr;
int x_loop;
int y_loop;
int counter;
char a_letter = '1';
initscr();
/*以 0-9 来填充屏幕*/
for(y_loop = 0; y_loop < LINES - 1; y_loop++) {
for( x_loop = 0; x_loop < COLS - 1; x_loop++) {
mvwaddch(stdscr, y_loop, x_loop, a_letter);
a_letter++;
if(a_letter > '9') a_letter = '1';
}
}
/*创建子窗口,stdscr 是父窗口指针*/
sub_window_ptr = subwin(stdscr, 10, 20, 10, 10);
scrollok(sub_window_ptr, 1); /*滚屏设定*/
touchwin(stdscr);
refresh();
sleep(3);
werase(sub_window_ptr);
mvwprintw(sub_window_ptr, 2, 0, "%s\\n", "window will scroll");
wrefresh(sub_window_ptr);
sleep(3);
for(counter = 1; counter < 10; counter++) {
wprintw(sub_window_ptr, "%s\\n", "text is scrolling");
wrefresh(sub_window_ptr);
sleep(5);
}
delwin(sub_window_ptr);
touchwin(stdscr);
refresh();
sleep(3);
endwin();
exit(EXIT_SUCCESS);
}
说明:
在子窗口里的内容在 for 循环的控制下会不断滚屏输出。
注意,在子窗口删除及 window(stdscr) 被刷新后,之前在子窗口中输出的内容仍保留在屏幕中,这是因为子窗口中刷新出来的数据正是来自与父窗口 stdscr 。
作者: beyes 时间: 2009-3-23 12:28
标题: 键盘上的数字小键盘(keypad)
在前面已经看到 curses 提供处理键盘的一些工具。许多按键至少都会有光标移动键和功能键。许多键盘还有数字小键盘以及其它按键,如 Insert 和 Home 键。
在绝大部分的终端上,解码这些按键都算是一个难题。因为一般情况下按下这些按键后,它们会发送一个以转义字符打头的字符串。应用程序不仅要设法分辨出 ”单独按下 Escap 按键“ 和 ”按下功能按键爱你发送出来的一连串字符“ 之间的差别,还要对付因终端型号的不同而产生的 “同一逻辑按键使用不同的转义序列” 的情况,这两个问题交织在一起,错综复杂。
幸运的是,curses 为管理功能键提供了一个精巧的工具。对于每一个终端来说,它们的功能键发送出来的转义序列都被保存,通常是在一个 terminfo 结构里,并且所包含的 curses.h 头文件有一套前缀为 KEY_ 的逻辑按键 #defines 定义。
当 curses 启动时,转义序列和逻辑按键之间的转换被禁止,这需要 keypad 函数重新开启。如果成功就返回 OK,如果失败就返回 ERR 。
#include <cuurses.h>
int keypad(WINDOW *window_ptr, bool keypad_on);
一旦通过调用 keypad ,并设置 keypad_on 参数为 ture 时, keypad mode 被使能。接着,curses 接管(take over)按键的转义序列的处理工作。这样,读取键盘不但返回被按下的按键,也返回与逻辑按键对应的 KEY_defines 。
在使用 keypad mode 时有 3 个小小的限制:
第一个问题是识别 escape 转义序列需要一定的时间,而许多网络协议要么把字符打成数据包(这会导致 escpae 转义序列的识别不正确),要么会从某个地方开始分断它们(这将导致功能键的转义序列被识别成 escape 字符和其它彼此没有联系的字符)。在 WAN 网络或其它繁忙的链路上这一情况将更为剧烈。唯一解决的办法是尝试对终端进行编程使之对你所希望用的每一个功能键发送单独的,独一无二的字符,然而这会限制控制字符的数量。
为了让 curses 能够区分 “单独按下 Escape按键” 和一个以 Escape 字符打头的键盘转义序列,它就必须等待很短的一小段时间。在 keypad 模式被激活后,处理 Escape 按键所造成的非常微小的延迟也能被注意到。
curses 不能处理 “非独一无二” 的转义序列。如果你的终端有两个都发送同一个转义序列的不同按键,curses 将对此不做处理,因为它不能告诉该返回哪一个逻辑按键。
作者: beyes 时间: 2009-3-24 11:03
标题: keypad 模式测试
测试代码:
#include <unistd.h>
#include <stdlib.h>
#include <curses.h>
#define LOCAL_ESCAPE_KEY 27
int main()
{
int key;
initscr();
crmode(); /*切换到 CBREAK 模式,即字符一输入就被处理*/
keypad(stdscr, TRUE); /*激活 keypad 模式*/
noecho(); /*输入不回显*/
clear();
mvprintw(5, 5, "Key pad demostration. Press 'q' to quit");
move(7,5);
refresh();
key = getch(); /*检测按键*/
while(key != ERR && key != 'q') {
move(7,5);
clrtoeol(); /*如果没错误则清(7,5)起到其所在的行末*/
if((key >= 'A' && key <= 'Z') ||
(key >= 'a' && key <= 'z')) {
printw("Key was %c", (char)key);
}
else {
switch(key) {
case LOCAL_ESCAPE_KEY: printw("%s", "Escape key"); break;
case KEY_END: printw("%s", "END key"); break;
case KEY_BEG: printw("%s", "BEGINNING key"); break;
case KEY_RIGHT: printw("%s", "RIGHT key"); break;
case KEY_LEFT: printw("%s", "LEFT key"); break;
case KEY_UP: printw("%s", "UP key"); break;
case KEY_DOWN: printw("%s", "DOWN key"); break;
default: printw("Unmatched - %d", key); break;
}/* switch */
} /* else */
refresh();
key = getch();
}/* while */
endwin();
exit(EXIT_SUCCESS);
}
程序说明:
在上面程序中,必须关闭回显功能,否则光标在按键按下时会发生移动
作者: beyes 时间: 2009-3-25 01:21
标题: 使用颜色
刚开始时,非常少的 ”哑“ 终端是支持颜色显示功能的,所以早期版本的 curses 对这方面也不做支持。而在 ncurses 及现在大部分的 curses 实现中都支持颜色显示。不幸的是,curses 的“哑屏幕” 出身已经影响到了 API, curses 在使用颜色方面也有许多严格的限制,这反应了早期的终端在颜色显示方面相当差的兼容能力。
屏幕上的每一个字符都 可以用多种颜色中的一种,字符所在的背景色也是一样可以选择。例如,你可以用绿色来 写文本,而文本所在的背景色可选为红色。
curses 所支持的颜色显示有一点不寻常,在这里,一个字符的颜色并没有定义得与其背景色无关( independently of 与...无关;不取决与...);而是,你必须定义一个字符的前景色和背景色作为一个 “颜色对” (color pair)。
在确定你可以使用 curses 的颜色功能之前,你必须检查当前的中断是否支持颜色显示,然后对 curses 的颜色例程进行初始化。对于此,可以使用两个函数:has_colors 和 start_color :
#include <curses.h>
bool has_colors(void);
int start_color(void);
如果终端支持颜色显示,那么 has_colors 函数返回 ture 。接着,就可以调用 start_color 了,如果颜色功能初始化成功,函数返回 OK 。一旦 start_color 一经调用并且颜色显示功能被初始化成功,变量 COLOR_PAIRS 就被设置为该终端所能支持的 “颜色对” 的最大值。一般情况下限制为 64 个 “颜色对”。变量 COLORS 定义了可选择颜色种类的最大值,此值一般为 8 。在内部,0-63一般作为每种 "颜色对“ 的特定 ID 。
在你可以使用颜色作为属性之前,你必须对你所想用的 ”颜色对“ 进行初始化——用 init_pair 函数来完成此工作。访问颜色属性使用 COLOR_PAIR 函数:
#include <curses.h>
int init_pair(short pair_number, short foreground, short background);
int COLOR_PAIR(int pair_number);
int pair_content(short pair_number, short *foreground, short *background);
curses.h 通常都定义了一些基本的颜色,其命名以 COLOR_ 开始。
pair_content() 函数允许对之前定义的 ”颜色对“ 信息进行检索(retrieve)。
比如,要定义绿色背景上的红色内容为一号 ”颜色对“,那么如下定义:init_pair(1, COLOR_RED, COLOR_GREEN);
经过上面的定义后,这个颜色组合可以用来作为颜色的属性了,注意一下 COLOR_PAIR 的用法:wattron(window_ptr, COLOR_PAIR(1));
由于 COLOR_PAIR 已经被定义为一个属性,接下来就可以使用其他的属性与之组合。在 PC 上,通过组合 COLOR_PAIR 属性和附加属性 A_BOLD 属性以达到在屏幕上显示出更浓的颜色效果,组合两个属性的方法为使用 OR 位运算符,如下所示:wattron(window_ptr, COLOR_PAIR(1)|A_BOLD);
作者: beyes 时间: 2009-3-26 16:55
标题: 终端颜色显示测试
测试代码:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <curses.h>
int main()
{
int i;
initscr();
if (!has_colors()) { /*终端是否支持颜色显示*/
endwin();
fprintf(stderr, "Error - no color support on this terminal\\n");
exit(1);
}
if (start_color() != OK) { /*初始化颜色功能是否成功*/
endwin();
fprintf(stderr, "Error - could not initialize colors\\n");
exit(2);
}
clear();
mvprintw(5, 5, "There are %d COLORS, and %d COLOR_PAIRS available",
COLORS, COLOR_PAIRS); /* 一般COLORS为8种; COLOR_PAIRS 为 64 */
refresh();
init_pair(1, COLOR_RED, COLOR_BLACK);
init_pair(2, COLOR_RED, COLOR_GREEN);
init_pair(3, COLOR_GREEN, COLOR_RED);
init_pair(4, COLOR_YELLOW, COLOR_BLUE);
init_pair(5, COLOR_BLACK, COLOR_WHITE);
init_pair(6, COLOR_MAGENTA, COLOR_BLUE);
init_pair(7, COLOR_CYAN, COLOR_WHITE);
for (i = 1; i <= 7; i++) {
attroff(A_BOLD);
attrset(COLOR_PAIR(i)); /* 也可以用 attron 来代替*/
mvprintw(5 + i, 5, "Color pair %d", i);
attrset(COLOR_PAIR(i) | A_BOLD);
mvprintw(5 + i, 25, "Bold color pair %d", i);
refresh();
sleep(1);
}
endwin();
exit(EXIT_SUCCESS);
}
程序说明:
在检查了屏幕是否支持彩色显示后,程序对颜色处理进行了初始化以及定义了一些 ”颜色对“ 。
attrset() 函数和 attron() 函数在此可以互换使用。
在颜色属性定义中,COLOR_PAIR() 的返回值亦是其中一种。
在用 init_pair() 函数进行初始化后,在内部就存在了一个 "颜色对" 的特定 ID ,当使用 COLOR_PAIR() 函数时,就会查找到这个 ID ,于是便知道了之前初始化的颜色属性。那么 COLOR_PAIR() 函数的返回值就可以传递给 attron(), attroff(), attrset() 等函数所用。
作者: beyes 时间: 2009-3-26 21:59
标题: 重定义颜色
作为早期的 ”哑终端“ 的遗留问题---在任何一次只能显示非常少的颜色,但允许对颜色进行配置,curses 允许使用 init_color() 函数进行颜色重定义:这个函数把一个可用色彩的深度(0 ~ COLORS 之间)细调为一个新的值,新值的范围从 0 - 1000 。include <curses.h>
ini init_color(short color_number, short red, short green, short blue);
作者: beyes 时间: 2009-3-27 00:34
标题: 逻辑屏幕和显示平面
在编写比较高级的 curses 程序时,我们可以先创建一个逻辑屏幕,然后再把它的全部或部分内容输出到物理屏幕上,这个办法简便易行,效果也不错。如果还能有一个尺寸大于物理屏幕的逻辑屏幕,再根据需要一次只显示逻辑屏幕的某个部分,其效果往往会更好。
在前面讲过,所有的窗口不能大于物理屏幕。但实际上,curses 允许逻辑屏幕大于正常窗口,并且专门为尺寸大于正常窗口的逻辑屏幕上的信息处理准备了特殊的数据结构 -- 显示平面(pad)。
创建显示平面和创建正常窗口的原理非常相似:include <curses.h>
WINDOW *newpad(int number_of_lines, int number_of_columns);
注意,和 newwin() 一样,newpad() 函数的返回值是一个指向 WINDOW 结构的指针。Pad 的删除同样使用 delwin() 函数。
对”显示平面“有其独立的函数。显示平面不受屏幕坐标位置的限制,所以在刷新时必须指定希望把显示平面的哪个区间输出到屏幕上,还必须设定它在屏幕上占据的坐标位置。以下是对显示平面进行刷新的 prefresh 函数的定义:include <curses.h>
int prefresh(WINDOW *pad_ptr, int pad_row, int pad_column,
int screen_row_min, int screen_col_min,
int screen_row_max, int screen_col_max);
这个函数的作用是把显示平面从 (pad_row, pad_column) 开始的区间写到屏幕上的一块区域中,这块区域被定义为坐标 (screen_row_min, screen_col_min) 和 (screen_row_max, screen_col_max) 之间的那一部分。
一个附加的函数是 pnoutrefresh() ,它和 wnoutrefresh() 的作用类似,功能也是使得屏幕的更新变得更为有效。
作者: beyes 时间: 2009-3-27 02:18
标题: pad 的应用举例
测试代码:
#include <unistd.h>
#include <stdlib.h>
#include <curses.h>
int main()
{
WINDOW *pad_ptr;
int x, y;
int pad_lines;
int pad_cols;
char disp_char;
initscr();
/*显示平面比终端的长宽都大 50 个字符宽度*/
pad_lines = LINES + 50;
pad_cols = COLS + 50;
pad_ptr = newpad(pad_lines, pad_cols);
disp_char = 'a';
for(x = 0; x < pad_lines; x++) {
for(y = 0; y < pad_cols; y++) {
mvwaddch(pad_ptr, x, y, disp_char);
if(disp_char == 'z') disp_char = 'a';
else disp_char++;
}
}
mvprintw(0,0, "The MAX LINES On This Terminal Is %d", LINES);
mvprintw(1,0, "The MAX COLS On This Terminal Is %d", COLS);
refresh();
prefresh(pad_ptr, 0, 0, 2, 2, 9, 9);
sleep(2);
prefresh(pad_ptr, LINES + 5, COLS + 7, 5, 5, 21, 19);
sleep(2);
delwin(pad_ptr);
endwin();
exit(EXIT_SUCCESS);
}
说明:
上面我的终端可以容纳24行80列的字符。
由可以看到,参数 (0,0) 表示要显示的内容从 (0,0) 坐标起,显示内容的多少限定在(2-9) 行,(2-9) 列这么一块区域里。prefresh(pad_ptr, 0, 0, 2, 2, 9, 9)
上一条语句效果一样,由上可见,{ (LINES+5),(COLS+7) } 表示的字符为 j 。prefresh(pad_ptr, LINES + 5, COLS + 7, 5, 5, 21, 19);
程序运行如下图所示:
![]()
欢迎光临 曲径通幽论坛 (http://www.groad.net/bbs/) Powered by Discuz! X3.2