ncurses初体验
我们都知道GUI:Graphical User Interface,即图形用户接口。除了GUI以外,还有一种交互接口,那就是TUI,这里的T其实就是text,也就是字符交互界面。也正是我们现在使用的交互界面。在Linux下的TUI交互界面,主要就是由ncurses库实现的。下面就是关于ncurses的一个示例代码:
c
#include <ncurses.h>
int main(int argc, const char *argv[])
{
//使用Ncurses的第一步:初始化ncurses环境
initscr();
//输出字符串
printw("Hello World!");
//将字符串刷新到屏幕
refresh();
//等待按键输入,以达到暂停程序的目的
getch();
// 结束 ncurses 模式
endwin();
return 0;
}
运行代码之前需要安装一下ncurses库
c
sudo apt update
sudo apt install libncurses5-dev -y
c
gcc test.c -o test -Wall -lncurses
./test
运行成功以后会发现屏幕上输出了:Hello World!
(按任意键退出程序)
和以往的程序不同,它并不是在命令行下输出的,而是打开了一个界面。程序结束以后又返回到了命令行交互界面上了。
运行ncurses程序需要有以下几个注意事项:
- 系统一定安装了ncurses的库;
- 所有ncurses程序一般从
initscr()函数开始,到endwin()函数结束。 refresh()也是必不可少的函数,官方解释是把窗口内容刷新到界面。实际使用当中,发现只需要执行一次,后续的所有内容都会刷新到界面。反之,你将看不到任何内容。printw()是用于进行输出的函数,除此以外,ncurses还提供的更多方便的输出函数接口,下面会进行介绍。
ncurses的更详细内容:ncurses(3x) - Linux manual page (man7.org)
ncurses的输出函数
printw()
c
int printw(const char *fmt, ...);
addch()
c
int addch(char ch);
指定位置输出:
c
int mvprintw(int y, int x, const char *fmt, ...);
int mvaddch(int y, int x, chtype ch);
ncurses的窗口
initscr()这个函数的含义是初始化stdscr(即标准屏幕窗口)。这个窗口也是ncurses运行以后的第一个,也是默认的输出窗口。除此以外,你可以创建一个新的窗口:
c
WINDOW *newwin(int nlines, int ncols, int begin_y, int begin_x);
参数解释
nlines:窗口的行数,即窗口的高度。
ncols:窗口的列数,即窗口的宽度。
begin_y:窗口在屏幕上的起始行坐标(即窗口的顶部边缘的纵坐标)。
begin_x:窗口在屏幕上的起始列坐标(即窗口的左边缘的横坐标)。
返回值
成功:返回一个指向 WINDOW 结构体的指针,该结构体代表创建的窗口。
失败:返回 NULL,表示创建窗口失败。
示例代码:
c
#include <ncurses.h>
int main(int argc, const char *argv[])
{
//第一步:初始化环境,初始化stdsdc窗口
initscr();
//刷新数据
refresh();
//创建窗口
WINDOW *win = newwin(20, 30, 0, 0);
//绘制窗口边界
box(win, 0, 0);
//输出字符串
mvwaddch(win, 1, 1, '@');
//刷新显示
wrefresh(win);
//等待按键输入,达到阻塞目的
getch();
//销毁不使用的窗口
delwin(win);
//结束环境
endwin();
return 0;
}
注意事项:
- 窗口创建完成以后,最终需要调用
delwin()销毁 - 可以使用
box()会在窗口周围绘制出窗口边界,这样有利于识别窗口
ncurses方向按键与非阻塞
方向按键:
c
#include <ncurses.h>
int main(int argc, const char *argv[])
{
initscr(); //初始化scr主窗口界面
keypad(stdscr, TRUE); //开启特殊按键功能
curs_set(0);//隐藏光标
refresh(); //刷新主窗口
WINDOW *win = newwin(20, 30, 0, 0); //创建窗口win
box(win, 0, 0); //绘制窗口边界
wrefresh(win); //刷新窗口数据
//循环检测按键
int ch = 0;
while('q' != ch){
ch = getch();
werase(win); //清理窗口数据
box(win, 0, 0); //绘制窗口边界
switch(ch) {
case KEY_DOWN:
mvwprintw(win, 1, 1, "KEY_DOWN");
break;
case KEY_UP:
mvwprintw(win, 1, 1, "KEY_UP");
break;
case KEY_LEFT:
mvwprintw(win, 1, 1, "KEY_LEFT");
break;
case KEY_RIGHT:
mvwprintw(win, 1, 1, "KEY_RIGHT");
break;
default:
mvwprintw(win, 1, 1, "nother key");
}
wrefresh(win); //刷新窗口数据
}
delwin(win); //销毁窗口
endwin(); // 销毁scr主窗口
return 0;
}
注意事项:
- 每次窗口内容更新之前需要调用werase()清理之前遗留的数据。
- 每次窗口内容更新之后需要调用wrefresh()把数据更新到屏幕上
开启非阻塞:
c
#include <ncurses.h>
int main(int argc, const char *argv[])
{
initscr(); //初始化scr主窗口界面
nodelay(stdscr, TRUE); //开启非阻塞功能
keypad(stdscr, TRUE); //开启特殊按键功能
curs_set(0);//隐藏光标
refresh(); //刷新主窗口
WINDOW *win = newwin(20, 30, 0, 0); //创建窗口win
box(win, 0, 0); //绘制窗口边界
wrefresh(win); //刷新窗口数据
//循环检测按键
int ch = 0;
while('q' != ch){
ch = getch();
//werase(win); //清理窗口数据
//box(win, 0, 0); //绘制窗口边界
//wrefresh(win); //刷新窗口数据
switch(ch) {
case KEY_DOWN:
mvwprintw(win, 1, 1, "KEY_DOWN ");
break;
case KEY_UP:
mvwprintw(win, 1, 1, "KEY_UP ");
break;
case KEY_LEFT:
mvwprintw(win, 1, 1, "KEY_LEFT ");
break;
case KEY_RIGHT:
mvwprintw(win, 1, 1, "KEY_RIGHT");
break;
default:
static int a;
a = a < 10 ? a+1 : 1;
mvwprintw(win, 2, 1, " ");
mvwprintw(win, 2, a, "-");
}
napms(40); //暂停40毫秒
wrefresh(win); //刷新窗口数据
}
delwin(win); //销毁窗口
endwin(); // 销毁scr主窗口
return 0;
}
- 刷新数据的逻辑变得复杂,需要注意刷新数据函数的特性
- 非阻塞以后的更新速度非常快,这将影响后续的游戏运行速度。需要调用napms()调节速度。