ncurses 入門指南
雖然圖形界面非常酷,但是不是所有的程序都需要點擊式的界面。例如,令人尊敬的 Vi 編輯器在第一個 GUI 出現之前在純文本終端運行了很久。
Vi 編輯器是一個在「文本」模式下繪製的 面向屏幕 程序的例子。它使用了一個叫 curses 的庫。這個庫提供了一系列的編程介面來操縱終端屏幕。curses 庫產生於 BSD UNIX,但是 Linux 系統通過 ncurses 庫提供這個功能。
[要了解 ncurses 「過去曾引起的風暴」,參見 ncurses: Portable Screen-Handling for Linux, September 1, 1995, by Eric S. Raymond.]
使用 curses 創建程序實際上非常簡單。在這個文章中,我展示了一個利用 curses 來在終端屏幕上繪圖的示常式序。
謝爾賓斯基三角形
簡單展示一些 curses 函數的一個方法是生成 謝爾賓斯基三角形 。如果你對生成謝爾賓斯基三角形的這種方法不熟悉的話,這裡是一些產生謝爾賓斯基三角形的規則:
- 設置定義三角形的三個點。
- 隨機選擇任意的一個點
(x,y)
。
然後:
- 在三角形的頂點中隨機選擇一個點。
- 將新的
x,y
設置為先前的x,y
和三角頂點的中間點。 - 重複(上述步驟)。
所以我按照這些指令寫了這個程序,程序使用 curses 函數來向終端屏幕繪製謝爾賓斯基三角形:
/* triangle.c */
#include <curses.h>
#include <stdlib.h>
#include "getrandom_int.h"
#define ITERMAX 10000
int main(void)
{
long iter;
int yi, xi;
int y[3], x[3];
int index;
int maxlines, maxcols;
/* initialize curses */
initscr();
cbreak();
noecho();
clear();
/* initialize triangle */
maxlines = LINES - 1;
maxcols = COLS - 1;
y[0] = 0;
x[0] = 0;
y[1] = maxlines;
x[1] = maxcols / 2;
y[2] = 0;
x[2] = maxcols;
mvaddch(y[0], x[0], '0');
mvaddch(y[1], x[1], '1');
mvaddch(y[2], x[2], '2');
/* initialize yi,xi with random values */
yi = getrandom_int() % maxlines;
xi = getrandom_int() % maxcols;
mvaddch(yi, xi, '.');
/* iterate the triangle */
for (iter = 0; iter < ITERMAX; iter++) {
index = getrandom_int() % 3;
yi = (yi + y[index]) / 2;
xi = (xi + x[index]) / 2;
mvaddch(yi, xi, '*');
refresh();
}
/* done */
mvaddstr(maxlines, 0, "Press any key to quit");
refresh();
getch();
endwin();
exit(0);
}
讓我一邊解釋一邊瀏覽這個程序。首先,getrandom_int()
函數是我對 Linux 系統調用 getrandom()
的包裝器。它保證返回一個正整數(int
)值。(LCTT 譯註:getrandom()
系統調用按照位元組返回隨機值到一個變數中,值是隨機的,不保證正負,使用 stdlib.h
的 random()
函數可以達到同樣的效果)另外,按照上面的規則,你應該能夠辨認出初始化和迭代謝爾賓斯基三角形的代碼。除此之外,我們來看看我用來在終端上繪製三角形的 curses 函數。
大多數 curses 程序以這四條指令開頭。 initscr()
函數獲取包括大小和特徵在內的終端類型,並設置終端支持的 curses 環境。cbreak()
函數禁用行緩衝並設置 curses 每次只接受一個字元。noecho()
函數告訴 curses 不要把輸入回顯到屏幕上。而 clear()
函數清空了屏幕:
initscr();
cbreak();
noecho();
clear();
之後程序設置了三個定義三角的頂點。注意這裡使用的 LINES
和 COLS
,它們是由 initscr()
來設置的。這些值告訴程序在終端的行數和列數。屏幕坐標從 0
開始,所以屏幕左上角是 0
行 0
列。屏幕右下角是 LINES - 1
行,COLS - 1
列。為了便於記憶,我的程序里把這些值分別設為了變數 maxlines
和 maxcols
。
在屏幕上繪製文字的兩個簡單方法是 addch()
和 addstr()
函數。也可以使用相關的 mvaddch()
和 mvaddstr()
函數可以將字元放到一個特定的屏幕位置。我的程序在很多地方都用到了這些函數。首先程序繪製三個定義三角的點並標記為 '0'
,'1'
和 '2'
:
mvaddch(y[0], x[0], '0');
mvaddch(y[1], x[1], '1');
mvaddch(y[2], x[2], '2');
為了繪製任意的一個初始點,程序做了類似的一個調用:
mvaddch(yi, xi, '.');
還有為了在謝爾賓斯基三角形遞歸中繪製連續的點:
mvaddch(yi, xi, '*');
當程序完成之後,將會在屏幕左下角(在 maxlines
行,0
列)顯示一個幫助信息:
mvaddstr(maxlines, 0, "Press any key to quit");
注意 curses 在內存中維護了一個版本的屏幕顯示,並且只有在你要求的時候才會更新這個屏幕,這很重要。特別是當你想要向屏幕顯示大量的文字的時候,這樣程序會有更好的性能表現。這是因為 curses 只能更新在上次更新之後改變的這部分屏幕。想要讓 curses 更新終端屏幕,請使用 refresh()
函數。
在我的示常式序中,我選擇在「繪製」每個謝爾賓斯基三角形中的連續點時更新屏幕。通過這樣做,用戶可以觀察三角形中的每次迭代。(LCTT 譯註:由於 CPU 太快,迭代過程執行就太快了,所以其實很難直接看到迭代過程)
在退出之前,我使用 getch()
函數等待用戶按下一個鍵。然後我調用 endwin()
函數退出 curses 環境並返回終端程序到一般控制。
getch();
endwin();
編譯和示例輸出
現在你已經有了你的第一個 curses 示常式序,是時候編譯運行它了。記住 Linux 操作系統通過 ncurses 庫來實現 curses 功能,所以你需要在編譯的時候通過 -lncurses
來鏈接——例如:
$ ls
getrandom_int.c getrandom_int.h triangle.c
$ gcc -Wall -lncurses -o triangle triangle.c getrandom_int.c
(LCTT 譯註:此處命令行有問題,-lncurses
選項在我的 Ubuntu 16.04 系統 + gcc 4.9.3 環境下,必須放在命令行最後,否則找不到庫文件,鏈接時會出現未定義的引用。)
在標準的 80x24 終端運行這個 triangle
程序並沒什麼意思。在那樣的解析度下你不能看見謝爾賓斯基三角形的很多細節。如果你運行終端窗口並設置非常小的字體大小,你可以更加容易地看到謝爾賓斯基三角形的不規則性質。在我的系統上,輸出如圖 1。
圖 1. triangle 程序的輸出
雖然迭代具有隨機性,但是每次謝爾賓斯基三角形的運行看起來都會很一致。唯一的不同是最初繪製到屏幕的一些點的位置不同。在這個例子中,你可以看到三角形開始的一個小圓點,在點 1 附近。看起來程序接下來選擇了點 2,然後你可以看到在圓點和「2」之間的星號。並且看起來程序隨機選擇了點 2 作為下一個隨機數,因為你可以看到在第一個星號和「2」之間的星號。從這裡開始,就不能繼續分辨三角形是怎樣被畫出來的了,因為所有的連續點都屬於三角形區域。
開始學習 ncurses
這個程序是一個怎樣使用 curses 函數繪製字元到屏幕的簡單例子。按照你的程序的需要,你可以通過 curses 做得更多。在下一篇文章中,我將會展示怎樣使用 curses 讓用戶和屏幕交互。如果你對於學習 curses 有興趣,我建議你去讀位於 Linux 文檔計劃 的 Pradeep Padala 寫的 NCURSES Programming HOWTO。
關於作者
Jim Hall 是一個自由及開源軟體的倡議者,他最有名的工作是 FreeDOS 計劃,也同樣致力於開源軟體的可用性。Jim 是在明尼蘇達州的拉姆齊縣的首席信息官。
via: http://www.linuxjournal.com/content/getting-started-ncurses
作者:Jim Hall 譯者:leemeans 校對:wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive