curses库使用

curses术语和概念

  stdscr是指curses窗口,它与显示器的物理屏幕的尺寸完全一样。curses函数库用两个数据结构来映射终端屏幕,即stdscrcurscr

  • stdscr:对应的是标准屏幕,它的工作方式与stdout相似,是curses程序中的默认输出窗口。
  • curscr:对应的是当前屏幕,在程序调用refresh函数之前,输出到stdscr上的内容不会显示在屏幕上。curses函数库会在refresh函数被调用时比较stdscr与第二个数据结构curscr之间的不同之处,然后用这两个数据结构之间的差异来刷新屏幕。

  逻辑屏幕的布局通过一个字符数组来实现,它以屏幕的左上角即坐标(0, 0)为起点,通过行号和列号来组织。所有cueses函数使用的坐标都是y值(行号)在前,x值(列号)在后。每个位置不仅包含该屏幕处的字符,还包含它的属性(粗体、下划线、反白显示、色彩)。

#include <unistd.h>
#include <stdlib.h>
#include <ncurses.h>

int main() {
    initscr();
    move ( 5, 15 );
    printw ( "%s", "Hello World" );
    refresh();
    sleep ( 2 );
    endwin();
    exit ( EXIT_SUCCESS );
}

由于curses函数库在使用时需要创建和删除一些临时的数据结构,所以所有的curses程序必须在开始使用curses函数库之前对其进行初始化,并在结束使用后允许curses恢复原先设置。这两项工作由initscrendwin函数分别完成。

屏幕

  initscr函数与endwin函数:所有的curses程序必须以initscr函数开始,以endwin函数结束。initscr函数原型如下:

#include <curses.h>
WINDOW *initscr ( void );

如果成功,它返回一个指向stdscr结构的指针;如果失败,输出一条诊断错误信息并使程序退出。initscr函数在一个程序中只能调用一次。
  endwin函数原型如下:

int endwin ( void );

调用成功时返回OK,失败时返回ERR
  输出到屏幕:curses函数库提供了一些用于刷新屏幕的基本函数。chtype等同于unsigned long,由typedef定义。

#include <curses.h>

int addch ( const chtype char_to_add ); /* 在光标的当前位置添加指定的字符 */
int addchstr ( chtype *const string_to_add ); /* 在光标的当前位置添加指定的字符串 */
int printw ( char *format, ... ); /* 对字符串进行格式化,并将其添加到光标的当前位置 */
int refresh ( void ); /* 刷新物理屏幕,成功时返回OK,错误时返回ERR */
int box ( WINDOW *win_ptr, chtype vertical_char, chtype horizontal_char ); /* 用来围绕一个窗口绘制方框 */
int insch ( chtype char_to_insert ); /* 插入一个字符,将已有的字符向右移 */
int insertIn ( void ); /* 插入一个空白行,将现有行依次向下移一行 */
int delch ( void ); /* 删除一个字符,将已有的字符向左移 */
int deleteIn ( void ); /* 删除一行,将现有行向上移一行 */
int beep ( void ); /* 在终端上发出声音 */
int flash ( void ); /* 使屏幕闪烁 */

  清除屏幕:清除屏幕上的某个区域主要有4种方法:

#include <curses.h>
int erase ( void ); /* 在每个屏幕位置写上空白字符 */
int clear ( void ); /* 用于清屏,但在下次调用refresh函数时重现屏幕原文 */
int clrtobot ( void ); /* 清除当前光标位置直到屏幕结尾的所有内容 */
int clrtoeol ( void ); /* 清除当前光标位置直到光标所处行行尾的所有内容 */

  move函数:用来将逻辑光标的位置移到指定地点,屏幕坐标以左上角(0, 0)为起点。并且有两个包含物理屏幕尺寸大小的外部整数LINESCOLUMNS,它们可用于决定参数new_ynew_x的最大值。

#include <curses.h>
int move ( int new_y, int new_x );

调用move函数本身并不会使物理光标移动,它仅改变逻辑屏幕上的光标位置,下次的输出内容就将出现在该位置上。如果希望物理屏幕上的光标位置在调用move函数之后立刻有变化,则需在它之后立刻调用refresh函数
  leaveok函数:设置一个标志,该标志用于控制在屏幕刷新后curses将物理光标放置的位置:

#include <curses.h>
int leaveok ( WINDOW *window_ptr, bool leave_flag );

默认情况下,该标志为false,意为屏幕刷新后,硬件光标将停留在屏幕上逻辑光标所处的位置;如果该标志设置为true,则硬件光标会被随机地放置在屏幕上的任意位置。一般来说,默认选项更符合用户的需求,这能确保光标停留在一个有意义的位置。
  字符属性:每个curses字符都可以有一些属性用于控制该字符在屏幕上的显示方式,前提是用于显示的硬件设备能够支持要求的属性。预定义的属性有A_BLINKA_BOLDA_DIMA_REVERSEA_STANDOUTA_UNDERLINE

属性说明
A_NORMALNormal display (no highlight)
A_STANDOUTBest highlighting mode of the terminal
A_UNDERLINEUnderlining
A_REVERSEReverse video
A_BLINKBlinking
A_DIMHalf bright
A_BOLDExtra bright or bold
A_PROTECTProtected mode
A_INVISInvisible or blank mode
A_ALTCHARSETAlternate character set

函数如下:

#include <curses.h>

int attron ( chtype attribute ); /* 启用指定的属性 */
int attroff ( chtype attribute ); /* 关闭指定的属性 */
int attrset ( chtype attribute ); /* 设置curses属性 */
int standout ( void ); /* 提供了一种强调或“突出”模式,在大多数终端上,通常被映射为反白显示 */

示例代码如下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <curses.h>

int main() {
    const char witch_one[] = " First Witch  ";
    const char witch_two[] = " Second Witch  ";
    const char *scan_ptr;
    initscr();
    move ( 5, 15 );
    attron ( A_BOLD );
    printw ( "%s", "Macbeth" );
    attroff ( A_BOLD );
    refresh();
    sleep ( 1 );
    move ( 8, 15 );
    attron ( A_STANDOUT );
    printw ( "%s", "Thunder and Lightning" );
    attroff ( A_STANDOUT );
    refresh();
    sleep ( 1 );
    move ( 10, 10 );
    printw ( "%s", "When shall we three meet again" );
    move ( 11, 23 );
    printw ( "%s", "In thunder, lightning, or in rain?" );
    move ( 13, 10 );
    printw ( "%s", "When the hurlyburly's done," );
    move ( 14, 23 );
    printw ( "%s", "When the battle's lost and won." );
    refresh();
    sleep ( 1 );
    attron ( A_DIM );
    scan_ptr = witch_one + strlen ( witch_one ) - 1;

    while ( scan_ptr != witch_one ) {
        move ( 10, 10 );
        insch ( *scan_ptr-- );
    }

    scan_ptr = witch_two + strlen ( witch_two ) - 1;

    while ( scan_ptr != witch_two ) {
        move ( 13, 10 );
        insch ( *scan_ptr-- );
    }

    attroff ( A_DIM );
    refresh();
    sleep ( 1 );
    move ( LINES - 1, COLS - 1 );
    refresh();
    sleep ( 1 );
    endwin();
    exit ( EXIT_SUCCESS );
}

输出效果:先在第5行第15列的位置输出Macbeth加粗,再在第8行第15列的位置输出Thunder and Lightning加亮。之后很短的间隔,在第10行第10列的位置输出When shall we three meet again,在第11行第23列的位置同时输出In thunder, lightning, or in rain?,在第13行第10列的位置同时输出When the hurlyburly's done,,在第14行第23列的位置同时输出When the battle's lost and won.。再经过很短的间隔,在第一句和第三句前分别加上两个数组的内容。在第10行第10列的位置插入(space)First Witch(space)(space),在第13行第10列的位置同时插入(space)Second Witch(space)(space)

键盘

  输入字符的回显:键盘读取例程由键盘模式控制。用于设置键盘模式的函数有:

#include <curses.h>
int echo ( void );
int noecho ( void );

用于开启或关闭输入字符的回显功能。键盘模式如下:

  • cooked:所有处理是基于行的。
  • cbreak:字符一经键入,就立刻被传送给程序。

以下函数用于控制在终端上输入的字符传送给curses程序的方式:

int cbreak ( void );
int nocbreak ( void );

curses程序通过调用initscr函数开始运行时,输入模式被设置为预处理模式(或称cooked模式)。这意味着所有处理都是基于行的,也就是说,只有在用户按下回车键后,输入的数据才会被传送给程序。在该模式下,键盘特殊字符被启用,所以按下合适的组合键即可在程序中产生一个信号。程序可通过调用cbreak函数将输入模式设置为cbreak模式,在这种模式下,字符一经键入就立刻被传递给程序,在此模式下,键盘特殊字符也被启用。但一些简单的字符,如Backspace会直接传递给程序处理,所以如果想让退格键保底原来的功能,就必须自己在程序中实现它。
  关闭特殊字符的处理:

int raw ( void );
int noraw ( void );

raw函数调用的作用是关闭特殊字符的处理,所以执行该函数调用后,再想通过输入特殊字符序列来产生信号或进行流控就不可能了。noraw函数调用同时恢复cooked模式和特殊字符处理模式。
  键盘输入:

#include <curses.h>
int getch ( void );
int getstr ( char *string );
int getnstr ( char *string, int number_of_characters );
int scanw ( char *format, ... );

类似于getchargetsscanf,但要注意的是,getstr函数对其返回的字符串的长度没有限制,要尽可能使用getnstr来代替getstr

#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();
    move ( 5, 10 );
    printw ( "%s", "Please login:" );
    move ( 7, 10 );
    printw ( "%s", "User name: " );
    getstr ( name );
    move ( 8, 10 );
    printw ( "%s", "Password: " );
    refresh();
    cbreak();
    noecho();
    memset ( password, '\0', sizeof ( password ) );

    while ( i < PW_LEN ) {
        password[i] = getch();

        if ( password[i] == '\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 ( 2 );
    endwin();
    exit ( EXIT_SUCCESS );
}

在第5行第10列的位置输出Please login:,在第7行第10列的位置输出User name:(space)。输入用户名(随意命名),然后回车。在第8行第10列的位置输出Password:(space),然后输入密码;若密码不为xyzzy,在第11行第10列的位置输出Wrong password,有背景填充;若相符,则在第11行第10列的位置输出Correct password,无背景。

窗口

  以上程序一直将终端作为一个全屏幕的输出介质,但curses函数库在物理屏幕上可同时显示多个不同尺寸的窗口。
  WINDOW结构:标准屏幕stdscr只是WINDOW结构的一个特例,就像标准输出stdout是文件流的一个特例一样。可以用函数调用newwindelwin来创建和销毁窗口。newwin函数原型如下:

#include <curses.h>
WINDOW *newwin ( int num_of_lines, int num_of_cols, int start_y, intstart_x );

该窗口从屏幕位置(start_y, start_x)开始,行数和列数分别由参数num_of_linesnum_of_cols指定。如果想让新窗口的右下角正好落在屏幕的右下角上,可以将函数的行、列参数设为0。该函数返回一个指向新窗口的指针,如果新窗口创建失败则返回null
  注意,所有的窗口范围都必须在当前屏幕范围之内,如果新窗口的任何部分落在当前屏幕范围之外,则newwin函数调用失败;通过newwin函数创建的新窗口完全独立于所有已存在的窗口。默认情况下,它被放置在任何已有窗口之上,覆盖(但不是改变)它们的内容。
  delwin函数原型如下:

int delwin ( WINDOW *window_to_delete );

因为调用newwin函数可能会给新窗口分配内存,所以当不再需要这些窗口时,不要忘记通过delwin函数将其删除。注意,不要删除curses自己的窗口stdscrcurscr

通用函数

  在本章中以前学过的函数都可以加上一些前缀变为通用函数。前缀w用于窗口,但必须在该函数的参数表的最前面增加一个WINDOW指针参数;前缀mv用于光标移动,但需要在函数的参数表的最前面增加两个参数,分别是纵坐标y和横坐标x,这两个坐标值指定了执行操作的位置。坐标值yx是相对于窗口而不是相对于屏幕的,坐标(0, 0)代表窗口的左上角;前缀mvw用于在窗口中移动光标,但需要传递3个参数,即一个WINDOW指针、yx坐标值,并且WINDOW指针参数总是出现在屏幕坐标值之前。

#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, intx, char *format, ... );
int mvwprintw ( WINDOW *window_pointer, int y, int x, char *format, ... );

移动和更新窗口

  通过以下函数,可以移动和重新绘制窗口:

#include <curses.h>
int mvwin ( WINDOW *window_to_move, int new_y, int new_x );

因为不允许窗口的任何部分超出屏幕范围,所以如果在调用mvwin函数时,将窗口的某个部分移动到屏幕区域之外,mvwin函数调用将会失败。

int wrefresh ( WINDOW *window_ptr );
int wclear ( WINDOW *window_ptr );
int werase ( WINDOW *window_ptr );
int touchwin ( WINDOW *window_ptr;

通过curses函数库,其指针参数指向的窗口内容已发生改变。这就意味着在下次调用wrefresh函数时,curses必须重新绘制该窗口,即使用户实际上并未修改该窗口中的内容。当屏幕上重叠着多个窗口时,可以通过该函数来安排要显示的窗口。

int scrollok ( WINDOW *window_ptr, bool scroll_flag );

该函数控制着窗口的卷屏。如果传递给scrollok函数的是布尔值true(通常是非零值),则允许窗口卷屏。在默认情况下,窗口是不能卷屏的。

int scroll ( WINDOW *window_ptr );

该函数是把窗口内容上卷一行。

#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 ( x_loop = 0; x_loop < COLS - 1; x_loop++ ) { /* COLS指现在屏幕列数 */
        for ( y_loop = 0; y_loop < LINES - 1; y_loop++ ) { /* LINES指现在屏幕行数 */
            mvwaddch ( stdscr, y_loop, x_loop, a_letter );
            a_letter++;

            if ( a_letter > 'z' ) {
                a_letter = 'a';
            }
        }
    }

    refresh();
    sleep ( 5 );
    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 howvery long lines wrap inside the window" );
    wrefresh ( new_window_ptr );
    sleep ( 2 );
    a_letter = '0';

    for ( x_loop = 0; x_loop < COLS - 1; x_loop++ ) {
        for ( y_loop = 0; y_loop < LINES - 1; y_loop++ ) {
            mvwaddch ( stdscr, y_loop, x_loop, a_letter );
            a_letter++;

            if ( a_letter > '9' ) {
                a_letter = '0';
            }
        }
    }

    refresh();
    sleep ( 2 );
    wrefresh ( new_window_ptr );
    sleep ( 2 );
    touchwin ( new_window_ptr );
    wrefresh ( new_window_ptr );
    sleep ( 2 );
    popup_window_ptr = newwin ( 10, 20, 8, 8 );
    box ( popup_window_ptr, '|', '-' );
    mvwprintw ( popup_window_ptr, 5, 2, "%s", "Pop UpWindow!" );
    wrefresh ( popup_window_ptr );
    sleep ( 2 );
    touchwin ( new_window_ptr );
    wrefresh ( new_window_ptr );
    sleep ( 2 );
    wclear ( new_window_ptr );
    wrefresh ( new_window_ptr );
    sleep ( 2 );
    delwin ( new_window_ptr );
    touchwin ( popup_window_ptr );
    wrefresh ( popup_window_ptr );
    sleep ( 2 );
    delwin ( popup_window_ptr );
    touchwin ( stdscr );
    refresh();
    sleep ( 2 );
    endwin();
    exit ( EXIT_SUCCESS );
}

newwin(10, 20, 5, 5)参数的含义如下:

  • 10num_of_lines,窗口的行数为10行。
  • 20num_of_cols,窗口的列数为20列。
  • 5start_y,该窗口从屏幕位置第5行开始。
  • 5start_x,该窗口从屏幕位置第5列开始。

  为什么要使用touchwin?因为通过curses函数库其指针参数指向的窗口内容已发生改变。这就意味着在下次调用wrefresh函数时,curses必须重新绘制该窗口,即使用户实际上并未修改该窗口中的内容。所以当屏幕上重叠着多个窗口时,可以通过该函数来安排要显示的窗口。

优化屏幕刷新

  为了尽量减少需要在屏幕上绘制的字符数目,curses函数库为此提供了一种特殊手段,这需要使到下面2个函数:

#include <curses.h>
int wnoutrefresh ( WINDOW *window_ptr );
int doupdate ( void );

wnoutrefresh函数用于决定把哪些字符发送到屏幕上,但它并不真正地发送这些字符,真正将更新发送到终端的工作由doupdate函数来完成。如果只是调用wnoutrefresh函数,然后立刻调用doupdate函数,则它的效果与直接调用wrefresh完全一样。
  但如果想重新绘制多个窗口,可以为每个窗口分别调用wnoutrefresh函数,然后只需在调用最后一个wnoutrefresh之后调用一次doupdate函数即可。这允许curses依次为每个窗口执行屏幕更新计算工作,最后仅把最终的更新结果输出到屏幕上,这种做法可以最大限度地减少curses需要发送的字符数目。

子窗口

  子窗口的创建和删除可以用以下几个函数来完成:

#include <curses.h>
WINDOW *subwin ( WINDOW *parent, intnum_of_lines, int num_of cols, int start_y, int start_x );
int delwin ( WINDOW *window_to_delete );

  子窗口与新窗口(newwin)的重要区别是,子窗口没有自己独立的屏幕字符存储空间,它们与其父窗口(在创建子窗口时指定)共享同一字符存储空间。这意味着,对子窗口中内容的任何修改都会反映到其父窗口中,所以删除子窗口时,屏幕显示不会发生任何变化。
  子窗口的用途是,提供了一种简洁的方式来卷动另一窗口里的部分内容。编写curses程序时,经常会需要卷动屏幕的某个小区域。通过将这个小区域定义为一个子窗口,然后对其卷动,就能达到这种效果。

#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();

    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';
            }
        }
    }

    sub_window_ptr = subwin ( stdscr, 10, 20, 10, 10 );
    scrollok ( sub_window_ptr, 1 );
    touchwin ( stdscr );
    refresh();
    sleep ( 1 );
    werase ( sub_window_ptr );
    mvwprintw ( sub_window_ptr, 2, 0, "%s", "This window willnow scroll" );
    wrefresh ( sub_window_ptr );
    sleep ( 1 );

    for ( counter = 1; counter < 10; counter++ ) {
        wprintw ( sub_window_ptr, "%s", "This text is both wrappingand scrolling." );
        wrefresh ( sub_window_ptr );
        sleep ( 1 );
    }

    delwin ( sub_window_ptr );
    touchwin ( stdscr );
    refresh();
    sleep ( 1 );
    endwin();
    exit ( EXIT_SUCCESS );
}

  简要描述子窗口的处理过程:

  1. subwin创建子窗口并且指定与其父窗口共享同一字符存储空间,子窗口只占一个指定的小区域。提供了一种简洁的方式来卷动另一窗口里的部分内容。允许子窗口卷屏,卷动其所在屏幕的某个小区域。
  2. 利用touchwin函数来安排要显示的窗口,使子窗口在指定位置输出指定字符,更新间隔很短。
  3. delwin删除已显示过的子窗口,删除子窗口时,屏幕显示不会发生任何变化。

keypad模式

  键盘上的方向键、功能键及数字小键盘等按键会发送以escape字符开头的字符串序列。curses函数库提供了一个用于管理功能键的功能。头文件curses.h通过一组以KEY_为前缀的定义来管理逻辑键。Keypad模式是指按键转义序列,此时读键盘操作不仅能够返回用户按下的键,还将返回与逻辑按键对应的KEY_定义(在curses.h头文件中定义)。

#define KEY_CODE_YES    0400           /* A wchar_t contains a key code      */
#define KEY_MIN         0401           /* Minimum curses key                 */
#define KEY_BREAK       0401           /* Break key (unreliable)             */
#define KEY_SRESET      0530           /* Soft (partial) reset(unreliable)   */
#define KEY_RESET       0531           /* Reset or hard reset(unreliable)    */
#define KEY_DOWN        0402           /* down-arrow key                     */
#define KEY_UP          0403           /* up-arrow key                       */
#define KEY_LEFT        0404           /* left-arrow key                     */
#define KEY_RIGHT       0405           /* right-arrow key                    */
#define KEY_HOME        0406           /* home key                           */
#define KEY_BACKSPACE   0407           /* backspace key                      */
#define KEY_F0          0410           /* Function keys. Space for 64        */
#define KEY_F(n)        (KEY_F0 + (n)) /* Value of function key n            */
#define KEY_DL          0510           /* delete-line key                    */
#define KEY_IL          0511           /* insert-line key                    */
#define KEY_DC          0512           /* delete-character key               */
#define KEY_IC          0513           /* insert-character key               */
#define KEY_EIC         0514           /* sent by rmir or smir in insertmode */
#define KEY_CLEAR       0515           /* clear-screen or erase key          */
#define KEY_EOS         0516           /* clear-to-end-of-screen key         */
#define KEY_EOL         0517           /* clear-to-end-of-line key           */
#define KEY_SF          0520           /* scroll-forward key                 */
#define KEY_SR          0521           /* scroll-backward key                */
#define KEY_NPAGE       0522           /* next-page key                      */
#define KEY_PPAGE       0523           /* previous-page key                  */
#define KEY_STAB        0524           /* set-tab key                        */
#define KEY_CTAB        0525           /* clear-tab key                      */
#define KEY_CATAB       0526           /* clear-all-tabs key                 */
#define KEY_ENTER       0527           /* enter/send key                     */
#define KEY_PRINT       0532           /* print key                          */
#define KEY_LL          0533           /* lower-left key (home down)         */
#define KEY_A1          0534           /* upper left of keypad               */
#define KEY_A3          0535           /* upper right of keypad              */
#define KEY_B2          0536           /* center of keypad                   */
#define KEY_C1          0537           /* lower left of keypad               */
#define KEY_C3          0540           /* lower right of keypad              */
#define KEY_BTAB        0541           /* back-tab key                       */
#define KEY_BEG         0542           /* begin key                          */
#define KEY_CANCEL      0543           /* cancel key                         */
#define KEY_CLOSE       0544           /* close key                          */
#define KEY_COMMAND     0545           /* command key                        */
#define KEY_COPY        0546           /* copy key                           */
#define KEY_CREATE      0547           /* create key                         */
#define KEY_END         0550           /* end key                            */
#define KEY_EXIT        0551           /* exit key                           */
#define KEY_FIND        0552           /* find key                           */
#define KEY_HELP        0553           /* help key                           */
#define KEY_MARK        0554           /* mark key                           */
#define KEY_MESSAGE     0555           /* message key                        */
#define KEY_MOVE        0556           /* move key                           */
#define KEY_NEXT        0557           /* next key                           */
#define KEY_OPEN        0560           /* open key                           */
#define KEY_OPTIONS     0561           /* options key                        */
#define KEY_PREVIOUS    0562           /* previous key                       */
#define KEY_REDO        0563           /* redo key                           */
#define KEY_REFERENCE   0564           /* reference key                      */
#define KEY_REFRESH     0565           /* refresh key                        */
#define KEY_REPLACE     0566           /* replace key                        */
#define KEY_RESTART     0567           /* restart key                        */
#define KEY_RESUME      0570           /* resume key                         */
#define KEY_SAVE        0571           /* save key                           */
#define KEY_SBEG        0572           /* shifted begin key                  */
#define KEY_SCANCEL     0573           /* shifted cancel key                 */
#define KEY_SCOMMAND    0574           /* shifted command key                */
#define KEY_SCOPY       0575           /* shifted copy key                   */
#define KEY_SCREATE     0576           /* shifted create key                 */
#define KEY_SDC         0577           /* shifted delete-character key       */
#define KEY_SDL         0600           /* shifted delete-line key            */
#define KEY_SELECT      0601           /* select key                         */
#define KEY_SEND        0602           /* shifted end key                    */
#define KEY_SEOL        0603           /* shifted clear-to-end-of-linekey    */
#define KEY_SEXIT       0604           /* shifted exit key                   */
#define KEY_SFIND       0605           /* shifted find key                   */
#define KEY_SHELP       0606           /* shifted help key                   */
#define KEY_SHOME       0607           /* shifted home key                   */
#define KEY_SIC         0610           /* shifted insert-character key       */
#define KEY_SLEFT       0611           /* shifted left-arrow key             */
#define KEY_SMESSAGE    0612           /* shifted message key                */
#define KEY_SMOVE       0613           /* shifted move key                   */
#define KEY_SNEXT       0614           /* shifted next key                   */
#define KEY_SOPTIONS    0615           /* shifted options key                */
#define KEY_SPREVIOUS   0616           /* shifted previous key               */
#define KEY_SPRINT      0617           /* shifted print key                  */
#define KEY_SREDO       0620           /* shifted redo key                   */
#define KEY_SREPLACE    0621           /* shifted replace key                */
#define KEY_SRIGHT      0622           /* shifted right-arrow key            */
#define KEY_SRSUME      0623           /* shifted resume key                 */
#define KEY_SSAVE       0624           /* shifted save key                   */
#define KEY_SSUSPEND    0625           /* shifted suspend key                */
#define KEY_SUNDO       0626           /* shifted undo key                   */
#define KEY_SUSPEND     0627           /* suspend key                        */
#define KEY_UNDO        0630           /* undo key                           */
#define KEY_MOUSE       0631           /* Mouse event has occurred           */
#define KEY_RESIZE      0632           /* Terminal resize event              */
#define KEY_EVENT       0633           /* We were interrupted by an event    */
#define KEY_MAX         0777           /* Maximum key value is 0633          */

curses在启动时会关闭转义序列与逻辑键之间的转换功能,该功能需要通过keypad函数来启用:

#include <curses.h>
int keypad ( WINDOW *window_ptr, bool keypad_on );

keypad_on参数设置为true,然后调用keypad函数来启用keypad模式。函数调用成功时,返回OK;调用失败时,返回ERR
  几种特殊字符(以escape开头的转义序列)如下:

  • END:键盘End键。
  • BEGINNINGbegin键(没有)。
  • RIGHT:向右的方向键。
  • LEFT:向左的方向键。
  • UP:向上的方向键。
  • DOWN:向下的方向键。

彩色显示

  curses只能以一种非常有限的方式来使用色彩,屏幕上的每个字符位置都有它的前景色或背景色。但curses对字符颜色的定义及其背景色的定义并不能完全独立。必须同时定义一个字符的前景色与背景色,将它们称之为颜色组合(color pair)。
  curses颜色例程初始化:

#include <curses.h>
bool has_colors ( void ); /* 如果终端支持颜色,该函数将返回true */
int start_color ( void ); /* 若该函数成功初始化了颜色显示功能,它将返回OK */

一旦start_color函数被成功调用,变量COLOR_PAIRS将被设置为终端所能支持的颜色组合数目的最大值,一般为64(8 * 8)。变量COLORS定义可用颜色数目的最大值,一般只有8种。在内部实现中,每种可用的颜色以一个从063的数字作为其唯一的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 ); /* 获取已定义颜色信息 */

彩色显示代码如下:

#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 );
    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 ) );
        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 );
}

pad

  Pad指一个尺寸大于物理屏幕的逻辑屏幕,可一次只显示该逻辑屏幕的某个部分。在一般情况下,逻辑屏幕不允许大于物理屏幕。Pad结构非常类似于WINDOW结构,所有执行写窗口操作的curses函数同样可用于pad
  创建pad:创建pad的方式与创建正常窗口的方式基本相同:

#include <curses.h>
WINDOW *newpad ( int number_of_lines, int number_of_colums );

paddelwin函数来删除,这与正常的窗口的删除一样:

int delwin ( WINDOW *window_to_delete );

  pad刷新:Pad使用不同的函数执行刷新操作。因为一个pad并不局限于某个特定的屏幕位置,所以必须指定希望放到屏幕上的pad范围及其放置在屏幕上的位置。

#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从坐标(pad_row, pad_colum)开始的区域写到屏幕上指定的显示区域,该显示区域的范围从坐标(screen_row_min, screen_col_min)(screen_row_max, screen_col_max)
  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();
    /* 定义长度和宽度,大于屏幕 */
    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++;
            }
        }
    }

    /* 将pad的不同区域绘制到屏幕的不同位置上,结束程序 */
    prefresh ( pad_ptr, 5, 7, 2, 2, 9, 9 );
    sleep ( 1 );
    prefresh ( pad_ptr, LINES + 5, COLS + 7, 5, 5, 21, 19 );
    sleep ( 1 );
    delwin ( pad_ptr );
    endwin();
    exit ( EXIT_SUCCESS );
}
  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值