上一章我们从底层去学习终端的控制,这一章我们将继续讲讲终端,但是我们会从更高的角度来谈。对于一个程序,我们是希望能够比较友好的界面给用户使用,这一章讲的就是如何使用curses去管理我们的屏幕。
虽说现在的UI做得都很炫,对比起来,终端使用的界面是很不友善的,而且没有什么学习价值。但是我觉得终端下面的窗口控制思想跟那些图像界面下的UI控制思想是接近的。通过这章的学习,你能够从底层理解你现在使用的友善的窗口是如何发展过来的。
说概念感觉是比较抽象,具体问说使用curses能做什么吧,看一看VI你就知道了。它的多窗口,编辑命令分离的这些功能,都可以通过curses来实现。
使用curses是通过引用curses.h这个文件,使用前一般都是需要安装的(我的机器上是要的)。
初始化,退出curses模式:
int main(void) {
initscr(); /* 初始化 */
……
endwin(); /* 退出 */
exit(EXIT_SUCESS);
}
这个跟使用C语言的BGI图像编辑模式的方式是类似的,都是需要先初始化模式,才可以使用。
初始化之后,我们可能做简单的测试,例如下面的几句代码:
move(2, 5);
printw(“%s”, “Hello World!”);
refresh();
看到变量名,应该差不多知道这三行代码所做的事吧,就是把光标移动到第2行,第5列,打印“Hello World!”,然后刷新页面。要记得使用这个refresh()函数,系统是不会自动刷新页面的,所以打印之后要refresh()才能看到最终结果。
文本模式下,有几个比较好用的函数:
echo() 打开输入回显功能
noecho() 关闭输入回显功能
cbreak() 打开字符终端功能
nocbreak() 关闭字符终端功能(只有回车时才发生终端)
对于那些隐私输入,像输入密码这种行为,我们可以通过关闭回显功能来实现隐藏输入字符。只要把输入代码前后加上 noecho(); 就可以了,不要使用完之后记得echo()下,毕竟大部分情况下还是需要回显字符的。
cbreak(),看起来比较费解,英文的全称是char break,也就是当输入一个字符,程序继续执行,而不是在等待输入。终端默认对输入的处理是这样的:当用户输入时,系统是把输入的字符传送到缓冲区,当用户按下Enter时,才把输入的数据传送给程序处理。这么做的好处是CPU可以跟输入输出等外设分开来,并行工作,而不用一直忙等用户输入。并且,用户输入错误时可以随时删除重写。
但是有些情况下,我们需要程序对每个输入字符都做处理并及时反馈,这就可以使用cbreak()去开启字符中断功能。但是也有一点不好的地方,就是用户如果要使用Backspace去删除已写的数据,要求程序员要自己做处理。
下面是键盘输入的一些函数
#include <curses.h>
int getch(void); 单字符输入
int getstr(char *string); 字符串输入
int getnstr(char *string, int number_of_characters); 跟上面一样,不过限制最多n个字符
int scanw(char *format, …); 跟scanf()用法一样
讲完了文本模式的基础,下面要讲讲基于文本模式的多窗口管理。先要声明,这里的多窗口不是想Windows下面的那种多窗口,而是在一个终端上面的多窗口,来一张图,也许大家才比较清楚。
对于终端里面的每个窗口,可以看成是一个个的实体,它记录了窗口的基本属性,有长度、宽度、位置,是否置于顶端等数据。
新建、删除一个窗口:
WINDOW *newwin(int number_of_lines, int number_of_cols, int start_y, int start_x);
int delete(WINDOW *window_to_delete);
输出字符串函数:
int printw(char *format, …);
int wprintw(WINDOW *window_ptr, char *format, …);
int mvprintw(int y, int x, char *format, …);
int mvwprintw(WINDOW *window_ptr, int y, int x, char *format, …);
为了不死记硬背这些函数,我觉得需要解释一下这里面的规律,“w”是window的意思,“mv”是move的意思。加w说明是指定窗口,加mv说明是指定位置。这样记起来就不费力了。然后要注意使用mvwprintw()函数时,打印字符串的实际位置不是(x,y),(x,y)是指定窗口的相对位置。
WINDOW相关操作:
Int mvwin(WINDOW *window_to_move, int new_y, int new_x); /*移动指定窗口*/
Int wrefresh(WINDOW *window_ptr); /*刷新指定窗口*/
Int wclear(WINDOW *window_ptr); /*清除指定窗口*/
Int touchwin(WINDOW *window_ptr); /*把某个窗口置顶*/
相信前面都理解的话,前三个函数理解起来都没有问题。需要特别提一下是最后一个touchwin()函数,它的作用是把窗口置顶。效果就相当我们在使用window窗口时,有很多个窗口,由于显示关系,在前面的窗口会覆盖后面窗口的一部分。通过调用touchwin(),就可以让某个窗口不被其他窗口覆盖,完全显示在终端上面。
有两个函数要注意下:
clear() 清屏
clrtoeol() 清除一行
其实理解clrtoeol的全称也就清楚了,clear to end of line。所以我得说一句,英语要是学得好,记这些函数也就相对不费力。
上面只是基于文本的终端管理里面的一小部分东西,由于篇幅关系,无法展开来讲。上面的一些知识点只是我认为比较重要的地方,如果有说得不对的地方,还请大家多多指出。