• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 183,  comments - 10,  trackbacks - 0

             

             

             

             

             

             

            Unix Curses 庫(kù)導(dǎo)論

            Norman Matloff

            http://heather.cs.ucdavis.edu/~matloff/

            原版地址:http://heather.cs.ucdavis.edu/~matloff/UnixAndC/CLanguage/Curses.pdf

            加州大學(xué)戴維斯分校計(jì)算機(jī)科學(xué)系

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

            翻譯:Mark

            goonyangxiaofang@163.com


             

            目錄

            1 歷史

            1.1 Curses 庫(kù)的目的

            1.2 Curses 角色的演化

            2 包含和庫(kù)文件

            3 兩個(gè)例子

            3.1 一個(gè)簡(jiǎn)單的,快速引導(dǎo)的例子

            3.2 第二個(gè),更有用的例子

            3.3 例子中 Coded Raw 模式注釋

            4 重要的調(diào)試筆記

            4.1 GDB

            4.2 DDD

            5 一些主要的 Curses APIs,屬性和環(huán)境變量

            5.1 環(huán)境

            5.2 APIs

            5.3 屬性

            6 進(jìn)一步學(xué)習(xí)

             


             

            1 歷史

            1.1 Curses 庫(kù)的目的

            許多被廣泛使用的程序需要有一個(gè)終端光標(biāo)移動(dòng)功能。比如 vi (或者 vim 變種) 編輯器,它的許多功能都需要這樣的功能。例如,當(dāng)輸入 j 鍵的時(shí)候,光標(biāo)會(huì)移動(dòng)到上一行;輸入 dd 當(dāng)前行會(huì)被刪除,下面的行都向上移動(dòng)一行,上面的行保持不變。

             

            不同的終端有不同類型的光標(biāo)動(dòng)作,這樣導(dǎo)致了一個(gè)潛在的問題。例如,如果一個(gè)程序想在 VT100 終端上向上移動(dòng)一行,這個(gè)程序需要發(fā)送 Escape[ A 字符。

             

            printf(“%c%c%c”, 27, ‘[’, ‘A’);

             

            (Escape 鍵的 ASCII 值碼為 27)。但是對(duì)于 Televideo 920C 終端,程序必須發(fā)送 ctrl-K 字符,它的 ASCII 值為 11

             

            printf(“%c”, 11);

             

            很明顯,像 vi 這樣的程序的作者為了適應(yīng)各種終端將變的瘋狂和更糟。其他的每一個(gè)需要光標(biāo)運(yùn)動(dòng)的程序員需要重新發(fā)明輪子,和 vi 作者做的工作一樣,這樣將會(huì)浪費(fèi)很多時(shí)間。

             

            這就是開發(fā) curses 庫(kù)的原因。它的目的就是為了減輕那些面向不同的終端需要寫不同的代碼的程序員的減輕。程序只需要去調(diào)用庫(kù),庫(kù)可以知道針對(duì)什么樣的終端具體做什么。

             

            例如,如果你需要清屏,這里就不需要直接使用上面說(shuō)到的任何字符序列。相反地,你只需要調(diào)用

             

            clear();

             

            curses 可以幫助這個(gè)程序做具體的工作,例如可以輸出字符 Escape[ A ,以清屏。

             

            1.2 Curses 角色的演化

            Unix 軟件的演化進(jìn)程中,curses 庫(kù)的發(fā)展是重要的一步。即便 VT100 終端成為了標(biāo)準(zhǔn),curses 庫(kù)繼續(xù)扮演著重要的角色,它為程序員提供了一個(gè)抽象層,使其避免了解具體的 VT100 的光標(biāo)動(dòng)作代碼。

             

            Curses 庫(kù)在今天的面向 GUI 世界里一直起著重要的作用,因?yàn)樵诤芏鄳?yīng)用中,使用鍵盤要比使用鼠標(biāo)更為方便(許多 Microsoft Windows 應(yīng)用程序里都提供了鍵盤快捷鍵)。今天,物理終端被很少使用了,但是典型的 Unix 工作包含了一些效仿 VT100 終端的 文本窗口。Vi 編輯器和其他基于 curses 的應(yīng)用程序繼續(xù)很流行。

            2 包含和庫(kù)文件

            為了使用 curses ,你必須在你的源代碼中包含這條語(yǔ)句

             

            #include <curses.h>

             

            你必須鏈接 curses 庫(kù)

             

            gcc –g sourcefile.c –lcurses

             


             

            3 兩個(gè)例子

            在例子中學(xué)習(xí)

             

            試著去運(yùn)行這些程序!你不鍵入這些源代碼,相反地,你可以獲取從產(chǎn)生這個(gè)文檔的生文件中獲取它們,http://heather.cs.ucdavis.edu/~matloff/UnixAndC/CLanguage/Curses.tex,然后將非代碼刪除。

             

            如果你比較急,你可以直接去第二個(gè)例子,有更好的注釋并且使用了更多的 curses 特性。

             

             

            3.1 一個(gè)簡(jiǎn)單的,快速引導(dǎo)的例子

            第一個(gè)例子幾乎沒做什么。你可以認(rèn)為這是一個(gè)原始的乏味的游戲。但是它給我們一個(gè)開頭。

             

            源文件最上面的注釋告訴了你這個(gè)游戲做什么。編譯并運(yùn)行這個(gè)程序,輸入你喜歡的字符。然后參考解釋的注釋閱讀代碼(從 main() 函數(shù)開始,一般情況下你應(yīng)該如此)。

             

            1       // 簡(jiǎn)單的 curses 例子;畫輸入的字符,按照列向下的方式,當(dāng)?shù)竭_(dá)最后一行時(shí)轉(zhuǎn)向

            2       // 右,當(dāng)最右邊到達(dá)時(shí)環(huán)繞。

            3      

            4       #include <curses.h>

            5      

            6       int r, c; // 當(dāng)前行與列

            7       int nrows, ncols; // 窗口的行列數(shù)

            8      

            9       void draw(char dc)

            10     {

            11              move(r, c); // 移動(dòng)到當(dāng)前行和列

            12              delch();

            13              insch(dc); // 替換字符

            14              refresh(); // 更新屏幕

            15              ++r; // 下一行

            16              // 檢查是否需要轉(zhuǎn)向右或是環(huán)繞

            17              if (r == nrows)

            18              {

            19                       r = 0;

            20                       ++c;

            21                       if (c == ncols)

            22                       {

            23                                 c = 0;

            24                       }

            25              }

            26     }

            27    

            28     int main()

            29     {

            30              int i;

            31              char d;

            32              WINDOW* wnd;

            33    

            34              wnd = initscr(); // 初始化窗口

            35              cbreak(); // 為鍵入鍵設(shè)置不要等

            36              noecho(); // 設(shè)置不回送

            37              getmaxyx(wnd, nrows, ncols);         //窗口的大小

            38              clear();     // 清屏,設(shè)置光標(biāo)到 (0, 0)

            39              refresh(); // 實(shí)現(xiàn)從上次刷新所有改變

            40    

            41              r = 0;

            42              c = 0;

            43              while (1)

            44              {

            45                       d = getch();       // 鍵盤輸入

            46                       if (d == 'q')

            47                       {

            48                                 break;

            49                       }

            50                       draw(d);

            51              }

            52              endwin(); // 還原原始窗口和離開

            53     }

             

             

            3.2 第二個(gè),更有用的例子

            這個(gè)程序你可以實(shí)際使用。如果你需要結(jié)束掉一些進(jìn)程,這個(gè)程序可以允許瀏覽和刪除你想刪的進(jìn)程。

             

            同樣,編譯和運(yùn)行這個(gè)程序,結(jié)束一些你已經(jīng)用于其他目的已創(chuàng)建的垃圾進(jìn)程。(特別地,你可以刪除 psax 它自身。)然后依據(jù)解釋的注釋閱讀該代碼。

             

            1       // psax.c

            2      

            3       // 運(yùn)行 shell 命令 'ps ax', 在最后行顯示它的輸出,盡可能多的窗口適合;允許用戶在窗口中上下移動(dòng),可以選擇結(jié)束那些高亮的進(jìn)程。

            4      

            5       // 用法: psax

            6      

            7       // 用戶命令

            8      

            9       // 'u': 上移

            10     // 'd': 下移

            11     // 'k': 刪除高亮的進(jìn)程

            12     // 'r': 重新運(yùn)行 'ps ax' 更新

            13     // 'q': 結(jié)束

            14    

            15     // 可能的擴(kuò)展:允許滾動(dòng),這樣可以使用戶查看所有的 'ps ax' 輸出,而不只是最后行;

            16     // 允許卷上長(zhǎng)行;

            17     // 詢問用戶是否確認(rèn)結(jié)束一個(gè)進(jìn)程

            18    

            19     #define MAXROW 1000

            20     #define MAXCOL 500

            21    

            22     #include <curses.h>

            23    

            24     WINDOW* scrn; // 指向 curses 窗口對(duì)象

            25    

            26     char cmdoutlines[MAXROW][MAXCOL]; // 'ps ax' 的輸出,最好使用 malloc()

            27    

            28     int ncmdlines; // cmdoutlines 的行數(shù)

            29     int nwinlines;   // xterm equiv. 窗口中 'ps ax' 輸出的行數(shù)

            30     int winrow;                // 屏幕上當(dāng)前行的位置

            31     int cmdstartrow;     // 顯示的 cmdoutlines 的第一行的索引

            32     int cmdlastrow;        // 顯示的 cmdoutlines 的最后一行的索引

            33    

            34     // winrow 上用黑體重寫

            35     void highlight()

            36     {

            37              int clinenum;

            38              attron(A_BOLD);

            39    

            40              clinenum = cmdstartrow + winrow;

            41    

            42              mvaddstr(winrow, 0, cmdoutlines[clinenum]);

            43              attroff(A_BOLD);

            44              refresh();

            45     }

            46    

            47     void runpsax()

            48     {

            49              FILE* p;

            50              char ln[MAXCOL];

            51              int row, tmp;

            52              p = popen("ps ax", "r");

            53              for (row = 0; row < MAXROW; ++row)

            54              {

            55                       tmp = fgets(ln, MAXCOL, p);

            56                       if (tmp == NULL)

            57                       {

            58                                 break;

            59                       }

            60                       strncpy(cmdoutlines[row], ln, COLS);

            61                       cmdoutlines[row][MAXCOL - 1] = 0;

            62              }

            63              ncmdlines = row;

            64              close(p);

            65     }

            66    

            67     void showlastpart()

            68     {

            69              int row;

            70              clear();

            71    

            72              if (ncmdlines <= LINES)

            73              {

            74                       cmdstartrow = 0;

            75                       nwinlines = ncmdlines;

            76              }

            77              else

            78              {

            79                       cmdstartrow = ncmdlines - LINES;

            80                       nwinlines = LINES;

            81              }

            82              cmdlastrow = cmdstartrow + nwinlines - 1;

            83    

            84              for (row = cmdstartrow, winrow = 0; row <= cmdlastrow; ++row, ++winrow)

            85              {

            86                       mvaddstr(winrow, 0, cmdoutlines[row]);

            87              }

            88              refresh();

            89              --winrow;

            90              highlight();

            91     }

            92    

            93     void updown(int inc)

            94     {

            95              int tmp = winrow + inc;

            96              if (tmp >= 0 && tmp < LINES)

            97              {

            98                       mvaddstr(winrow, 0, cmdoutlines[cmdstartrow + winrow]);

            99                       winrow = tmp;

            100                     highlight();

            101            }

            102 }

            103 

            104 void rerun()

            105 {

            106            runpsax();

            107            showlastpart();

            108 }

            109 

            110 void prockill()

            111 {

            112            char* pid;

            113            pid = strtok(cmdoutlines[cmdstartrow + winrow], " ");

            114            kill(atoi(pid), 9);

            115            rerun();

            116 }

            117 

            118 int main()

            119 {

            120            char c;

            121            scrn = initscr();

            122            noecho();

            123            cbreak();

            124            runpsax();

            125            showlastpart();

            126            while (1)

            127            {

            128                     c = getch();

            129                     if (c == 'u')

            130                     {

            131                              updown(-1);

            132                     }

            133                     else if (c == 'd')

            134                     {

            135                              updown(1);

            136                     }

            137                     else if (c == 'r')

            138                     {

            139                              rerun();

            140                     }

            141                     else if (c == 'k')

            142                     {

            143                              prockill();

            144                     }

            145                     else

            146                     {

            147                              break;

            148                     }

            149            }

            150            endwin();

            151            return 0;

            152 }

             

             

            3.3 例子中 Coded 和 Raw 模式注釋

            在你寫的大多數(shù)程序中,鍵盤操作是出于熟模式下的。也就是說(shuō),在你按下回車鍵之前你的鍵盤輸入是不會(huì)發(fā)送給程序的(例如,不會(huì)發(fā)送給 scanf() 函數(shù)或 cin )。這種方式可以允許你使用退格鍵來(lái)刪除那些你鍵入的但是又想撤銷字符。并且,你的程序不能識(shí)別到你刪除的字符以及退格鍵(其 ASCII 碼為 8)。

             

            記住,當(dāng)你在鍵盤上敲擊字符時(shí),字符被送到操作系統(tǒng)。操作系統(tǒng)一般情況下將這些字符傳遞給你的應(yīng)用程序(例如,傳遞給調(diào)用接口 scanf() 或者 cin ),但是如果操作系統(tǒng)發(fā)現(xiàn)了退格鍵字符,操作系統(tǒng)不會(huì)將這個(gè)字符傳遞給應(yīng)用程序。事實(shí)上,操作系統(tǒng)在用戶鍵入回車鍵之前不會(huì)將任何字符傳遞給程序。

             

            另一種是原生模式。這種模式下,操作系統(tǒng)將每個(gè)字符傳遞給應(yīng)用程序。如果用戶鍵入了退格鍵,它被看作與其他字符一樣,沒有什么特殊的操作。應(yīng)用程序接收到一個(gè) ASCII 碼為 8 的字符,這由程序員決定如何處理這個(gè)字符。

             

            你可以調(diào)用 cbreak() 函數(shù)從熟模式切換到原生模式,也可以調(diào)用 nocbreak() 函數(shù)從原生模式切換到熟模式。

             

            相似地,默認(rèn)模式是為了操作系統(tǒng)來(lái)回顯字符。如果用戶鍵入 A 鍵(沒有 Shift),操作系統(tǒng)將在屏幕上打印出 ‘a’ 。有些情況使我們不需要回顯字符,例如在我們意見看到的例子中,或者在輸入密碼的情況下。我們可以通過(guò)調(diào)用 noecho() 函數(shù)關(guān)閉回顯功能,也可以調(diào)用 echo() 函數(shù)恢復(fù)回顯功能。


             

            4 重要的調(diào)試筆記

            不用使用 printf() 或者 cout 來(lái)調(diào)試!確保你使用調(diào)試工具,例如 GDB 或者帶有 DDD 界面的 GDB 。如果在日常編程工作中不適用一個(gè)調(diào)試工具,你將花費(fèi)大量的不必要的時(shí)間和碰到很多的挫折。請(qǐng)看我的調(diào)試幻燈片演示:http://heather.cs.ucdavis.edu/~matloff/debug.html

             

            curses 程序中,你無(wú)法使用 printf() cout 來(lái)調(diào)試程序,因?yàn)槟欠N將會(huì)把你的程序輸出弄的一團(tuán)糟。所以一個(gè)調(diào)試工具是必要的。這里我們使用 GDB DDD 來(lái)調(diào)試 curses ,這些調(diào)試工具在 Unix 世界里非常常用。你使用這個(gè)程序?qū)⒛愕恼{(diào)試信息輸出與你的 curses 應(yīng)用程序輸出分開。這里我將演示如何調(diào)試。

             

            4.1 GDB

            在文本窗口中啟動(dòng) GDB 。為你的 curses 應(yīng)用程序選擇另一個(gè)窗口來(lái)運(yùn)行,為后面的窗口選擇一個(gè)設(shè)備名字(我們可以叫做“執(zhí)行窗口”)。在窗口中運(yùn)行 tty Unix 命令。在這個(gè)例子中,我們假設(shè)命令輸出是 “/dev/pts/10” 。在 GDB 中的命令是:

            (gdb) tty /dev/pts/10

             

            run 命令之前,我們還要做其他事情。在執(zhí)行窗口中鍵入:

            Sleep 10000

             

            Unix sleep 命令讓 shell 在給定的時(shí)間內(nèi)進(jìn)入失效狀態(tài),在這個(gè)例子中是 10 秒。這個(gè)是很必要的,因?yàn)檫@樣可以使我們?cè)诖翱谥墟I入的信息多傳遞給我們的應(yīng)用程序而不是傳遞給了 shell

             

            現(xiàn)在回到 GDB 并且執(zhí)行 run 命令。記住,不管何時(shí)你的程序執(zhí)行到了斷點(diǎn)并且從鍵盤輸入,你必須在執(zhí)行窗口中鍵入(你也將要看到程序的輸出)。把這些做完后,在執(zhí)行窗口中鍵入 ctrl-C 刪除 sleep 命令,確保 shell 可用。

             

            注意如果有什么錯(cuò)誤并且你的程序提前結(jié)束了,執(zhí)行窗口可以保持一些非標(biāo)準(zhǔn)的終端配置,不如 cbreak 模式。修改這個(gè),回到窗口并鍵入 ctrl-j ‘reset’ 再一次鍵入 ctrl-j

             

            4.2 DDD

            DDD 差不多。只是點(diǎn)擊視圖|執(zhí)行窗口,一個(gè)彈出來(lái)的窗口作為執(zhí)行窗口。


             

            5 一些主要的 Curses APIs,屬性和環(huán)境變量

            5.1 環(huán)境

            ·窗口中行和列的的號(hào)碼從 0 開始,從上到下從左到右。所以左上角的最表是 (0, 0)

            ·LINES, COLS

                     窗口中行和列的數(shù)目。

             

            5.2 APIs

            (許多 API 是宏而不是真正的函數(shù))

            這里有一些你會(huì)調(diào)用的 curses 函數(shù)。注意這里羅列的函數(shù)中只有一些是可以使用的。可以使用 curses 做許多其他的東西,例如子窗口,窗體樣式輸入等。第六部分有對(duì)資源的介紹。

            ·WINDOW* initsrc()

                     REQUIRED. curses 初始化整個(gè)屏幕。返回一個(gè)執(zhí)行 WINDOW 類型結(jié)構(gòu)體的指針,

                     該指針被其他函數(shù)所使用。

            ·endwin()

                     重新設(shè)置終端,例如恢復(fù)回顯功能、熟模式等。

            ·cbreak()

            設(shè)置終端,鍵入的字符即是所讀取的字符,不用等待鍵入回車鍵。退格鍵和其他控制鍵(包括回車鍵)失去了他們?cè)瓉?lái)的意義。

            ·nocbreak()

                     回到一般模式

            ·noecho()

                     關(guān)閉將輸入字符顯示到屏幕上的回顯功能。

            ·echo()

                     恢復(fù)回顯功能

            ·clear()

                     清屏,從新將光標(biāo)設(shè)置在左上角。

            ·move(int, int)

                     將光標(biāo)設(shè)置到指定的列和行。

            ·addch(char)

            在當(dāng)前光標(biāo)位置打印給定的字符,改寫這個(gè)位置之前的字符,將光標(biāo)移到右面的下一個(gè)位置。

            ·insch(char)

                     addch() 一樣,插入代替改寫,所有右邊的字符都向右移動(dòng)一個(gè)位置。

            ·mvaddstr(int, int, char*)

                     將光標(biāo)移動(dòng)到指定的行和列,在指定位置打印字符串。

            ·refresh()

            更新屏幕,以相應(yīng)上一次調(diào)用這個(gè)函數(shù)所發(fā)生的所有變化。不過(guò)我們做了什么變化,例如調(diào)用上面的 addch() 或者不會(huì)出現(xiàn)在屏幕上的變化,只有調(diào)用了 refresh() 才回有效果。

            ·delch()

            將當(dāng)前光標(biāo)處的字符刪除掉,右邊的所有字符都將向做移動(dòng)一個(gè)位置,光標(biāo)的位置不會(huì)變化。

            ·int getch()

                     從鍵盤處讀取一個(gè)字符。

            ·char inch()

                     返回當(dāng)前光標(biāo)下的字符。

            ·getyx(WINDOWS*, int, int)

                     返回窗口的光標(biāo)當(dāng)前位置的行和列數(shù)。

                     (注意,這里函數(shù)是宏,所以這里是 ints 不是只想 int 的指針)

            ·getmaxyx(WINDOW*, int, int)

                     返回指定窗口的行和列數(shù)。

            ·scanw(), printw()

            curses 環(huán)境下的類似與 scanf() printf() 的函數(shù)。避免在 curses 環(huán)境下使用 scanf() printf() 函數(shù),否則將會(huì)造成奇異的結(jié)果。注意 printw() scanw() 函數(shù)重復(fù)調(diào)用 addch(), 所以他們是做改寫工作不是插入。Scanw 知道遇到換行符或者回車符的時(shí)候才結(jié)束,wgetstr() 函數(shù)也是如此。

            ·attron(const), attroff() const

                     將給定的屬性開啟或者關(guān)閉。

            5.3 屬性

            字符可以通過(guò)許多不同的方式來(lái)顯示,比如一般模式(A_NORMAL)和 黑體模式(A_BOLD)。可以通過(guò) APIs attron() attroff() 函數(shù)設(shè)置。前面的被調(diào)用后,所有打印在屏幕上的內(nèi)容使用給定參數(shù)的屬性,后面的被調(diào)用則終止這種狀態(tài)。


             

            6 進(jìn)一步學(xué)習(xí)

            man 手冊(cè)中有基本的幫助教程。

             

            man curses

             

            (你可以用 ncurese 代替 curses) 個(gè)別的函數(shù)有單獨(dú)的 man 頁(yè)面。

             

            在網(wǎng)上有很多好的輔導(dǎo)資料。在你最喜愛的搜索引擎中鍵入“curses tutorials”或者“ncurses tutorials”。以可以從這個(gè)輔導(dǎo)材料開始:http://www.linux.com/howtos/NCURSES-Programming-HOWTO/index.html

             

            注意,盡管有一些關(guān)于非 C 語(yǔ)言的輔導(dǎo)資料,例如 Perl 或者 Python 的。我不推薦這些(包括我自己,Python)。不僅語(yǔ)法存在不同,還有 API 也有很多的不同。

            posted on 2011-07-21 17:12 unixfy 閱讀(342) 評(píng)論(0)  編輯 收藏 引用

            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            9191精品国产免费久久| 人妻精品久久久久中文字幕69| 亚洲精品高清国产一线久久| 亚洲日韩中文无码久久| 久久婷婷五月综合97色直播| 77777亚洲午夜久久多喷| 亚洲精品无码久久久影院相关影片| 婷婷五月深深久久精品| 久久99精品久久久久久| 久久国产精品偷99| 97精品伊人久久大香线蕉| 亚洲级αV无码毛片久久精品| 国产午夜久久影院| 无码人妻久久一区二区三区蜜桃 | 精品少妇人妻av无码久久| 好久久免费视频高清| 美女久久久久久| 九九99精品久久久久久| 久久精品国产免费观看三人同眠| 国产V亚洲V天堂无码久久久| 伊人久久亚洲综合影院| 久久99精品国产| 无码专区久久综合久中文字幕| 久久99精品久久久久久不卡| 久久久久亚洲AV成人片| 一级做a爰片久久毛片看看| 97久久超碰成人精品网站| 久久人人爽人人爽人人片av麻烦| 国产69精品久久久久99尤物| 久久精品国产亚洲av影院| 国产精品久久久久久久久软件 | 亚洲AV日韩精品久久久久久久| 精品水蜜桃久久久久久久| 久久久久无码精品国产不卡| 久久精品日日躁夜夜躁欧美| 性做久久久久久免费观看| 精品乱码久久久久久夜夜嗨| 国产69精品久久久久99| 国产精自产拍久久久久久蜜| 久久最新精品国产| 久久中文娱乐网|