作者 | Jim Hall
譯者 | leemeans
怎樣使用 curses 來繪製終端螢幕?
雖然圖形介面非常酷,但是不是所有的程式都需要點選式的介面。例如,令人尊敬的 Vi 編輯器在第一個 GUI 出現之前在純文字終端運行了很久。
Vi 編輯器是一個在“文字”樣式下繪製的面向螢幕程式的例子。它使用了一個叫 curses 的庫。這個庫提供了一系列的程式設計介面來操縱終端螢幕。curses 庫產生於 BSD UNIX,但是 Linux 系統透過 ncurses 庫提供這個功能。
[要瞭解 ncurses “過去曾引起的風暴”,參見 ncurses: Portable Screen-Handling for Linux[1], 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 檔案計劃[2]的 Pradeep Padala 寫的 NCURSES Programming HOWTO[3]。
關於作者
Jim Hall 是一個自由及開源軟體的倡議者,他最有名的工作是 FreeDOS 計劃,也同樣致力於開源軟體的可用性。Jim 是在明尼蘇達州的拉姆齊縣的資訊長。
via: http://www.linuxjournal.com/content/getting-started-ncurses
作者:Jim Hall[5] 譯者:leemeans 校對:wxy
本文由 LCTT 原創編譯,Linux中國 榮譽推出