• <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 庫導論

            Norman Matloff

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

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

            加州大學戴維斯分校計算機科學系

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

            翻譯:Mark

            goonyangxiaofang@163.com


             

            目錄

            1 歷史

            1.1 Curses 庫的目的

            1.2 Curses 角色的演化

            2 包含和庫文件

            3 兩個例子

            3.1 一個簡單的,快速引導的例子

            3.2 第二個,更有用的例子

            3.3 例子中 Coded Raw 模式注釋

            4 重要的調試筆記

            4.1 GDB

            4.2 DDD

            5 一些主要的 Curses APIs,屬性和環境變量

            5.1 環境

            5.2 APIs

            5.3 屬性

            6 進一步學習

             


             

            1 歷史

            1.1 Curses 庫的目的

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

             

            不同的終端有不同類型的光標動作,這樣導致了一個潛在的問題。例如,如果一個程序想在 VT100 終端上向上移動一行,這個程序需要發送 Escape[ A 字符。

             

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

             

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

             

            printf(“%c”, 11);

             

            很明顯,像 vi 這樣的程序的作者為了適應各種終端將變的瘋狂和更糟。其他的每一個需要光標運動的程序員需要重新發明輪子,和 vi 作者做的工作一樣,這樣將會浪費很多時間。

             

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

             

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

             

            clear();

             

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

             

            1.2 Curses 角色的演化

            Unix 軟件的演化進程中,curses 庫的發展是重要的一步。即便 VT100 終端成為了標準,curses 庫繼續扮演著重要的角色,它為程序員提供了一個抽象層,使其避免了解具體的 VT100 的光標動作代碼。

             

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

            2 包含和庫文件

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

             

            #include <curses.h>

             

            你必須鏈接 curses

             

            gcc –g sourcefile.c –lcurses

             


             

            3 兩個例子

            在例子中學習

             

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

             

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

             

             

            3.1 一個簡單的,快速引導的例子

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

             

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

             

            1       // 簡單的 curses 例子;畫輸入的字符,按照列向下的方式,當到達最后一行時轉向

            2       // 右,當最右邊到達時環繞。

            3      

            4       #include <curses.h>

            5      

            6       int r, c; // 當前行與列

            7       int nrows, ncols; // 窗口的行列數

            8      

            9       void draw(char dc)

            10     {

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

            12              delch();

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

            14              refresh(); // 更新屏幕

            15              ++r; // 下一行

            16              // 檢查是否需要轉向右或是環繞

            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(); // 為鍵入鍵設置不要等

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

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

            38              clear();     // 清屏,設置光標到 (0, 0)

            39              refresh(); // 實現從上次刷新所有改變

            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 第二個,更有用的例子

            這個程序你可以實際使用。如果你需要結束掉一些進程,這個程序可以允許瀏覽和刪除你想刪的進程。

             

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

             

            1       // psax.c

            2      

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

            4      

            5       // 用法: psax

            6      

            7       // 用戶命令

            8      

            9       // 'u': 上移

            10     // 'd': 下移

            11     // 'k': 刪除高亮的進程

            12     // 'r': 重新運行 'ps ax' 更新

            13     // 'q': 結束

            14    

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

            16     // 允許卷上長行;

            17     // 詢問用戶是否確認結束一個進程

            18    

            19     #define MAXROW 1000

            20     #define MAXCOL 500

            21    

            22     #include <curses.h>

            23    

            24     WINDOW* scrn; // 指向 curses 窗口對象

            25    

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

            27    

            28     int ncmdlines; // cmdoutlines 的行數

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

            30     int winrow;                // 屏幕上當前行的位置

            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 模式注釋

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

             

            記住,當你在鍵盤上敲擊字符時,字符被送到操作系統。操作系統一般情況下將這些字符傳遞給你的應用程序(例如,傳遞給調用接口 scanf() 或者 cin ),但是如果操作系統發現了退格鍵字符,操作系統不會將這個字符傳遞給應用程序。事實上,操作系統在用戶鍵入回車鍵之前不會將任何字符傳遞給程序。

             

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

             

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

             

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


             

            4 重要的調試筆記

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

             

            curses 程序中,你無法使用 printf() cout 來調試程序,因為那種將會把你的程序輸出弄的一團糟。所以一個調試工具是必要的。這里我們使用 GDB DDD 來調試 curses ,這些調試工具在 Unix 世界里非常常用。你使用這個程序將你的調試信息輸出與你的 curses 應用程序輸出分開。這里我將演示如何調試。

             

            4.1 GDB

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

            (gdb) tty /dev/pts/10

             

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

            Sleep 10000

             

            Unix sleep 命令讓 shell 在給定的時間內進入失效狀態,在這個例子中是 10 秒。這個是很必要的,因為這樣可以使我們在窗口中鍵入的信息多傳遞給我們的應用程序而不是傳遞給了 shell

             

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

             

            注意如果有什么錯誤并且你的程序提前結束了,執行窗口可以保持一些非標準的終端配置,不如 cbreak 模式。修改這個,回到窗口并鍵入 ctrl-j ‘reset’ 再一次鍵入 ctrl-j

             

            4.2 DDD

            DDD 差不多。只是點擊視圖|執行窗口,一個彈出來的窗口作為執行窗口。


             

            5 一些主要的 Curses APIs,屬性和環境變量

            5.1 環境

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

            ·LINES, COLS

                     窗口中行和列的數目。

             

            5.2 APIs

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

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

            ·WINDOW* initsrc()

                     REQUIRED. curses 初始化整個屏幕。返回一個執行 WINDOW 類型結構體的指針,

                     該指針被其他函數所使用。

            ·endwin()

                     重新設置終端,例如恢復回顯功能、熟模式等。

            ·cbreak()

            設置終端,鍵入的字符即是所讀取的字符,不用等待鍵入回車鍵。退格鍵和其他控制鍵(包括回車鍵)失去了他們原來的意義。

            ·nocbreak()

                     回到一般模式

            ·noecho()

                     關閉將輸入字符顯示到屏幕上的回顯功能。

            ·echo()

                     恢復回顯功能

            ·clear()

                     清屏,從新將光標設置在左上角。

            ·move(int, int)

                     將光標設置到指定的列和行。

            ·addch(char)

            在當前光標位置打印給定的字符,改寫這個位置之前的字符,將光標移到右面的下一個位置。

            ·insch(char)

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

            ·mvaddstr(int, int, char*)

                     將光標移動到指定的行和列,在指定位置打印字符串。

            ·refresh()

            更新屏幕,以相應上一次調用這個函數所發生的所有變化。不過我們做了什么變化,例如調用上面的 addch() 或者不會出現在屏幕上的變化,只有調用了 refresh() 才回有效果。

            ·delch()

            將當前光標處的字符刪除掉,右邊的所有字符都將向做移動一個位置,光標的位置不會變化。

            ·int getch()

                     從鍵盤處讀取一個字符。

            ·char inch()

                     返回當前光標下的字符。

            ·getyx(WINDOWS*, int, int)

                     返回窗口的光標當前位置的行和列數。

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

            ·getmaxyx(WINDOW*, int, int)

                     返回指定窗口的行和列數。

            ·scanw(), printw()

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

            ·attron(const), attroff() const

                     將給定的屬性開啟或者關閉。

            5.3 屬性

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


             

            6 進一步學習

            man 手冊中有基本的幫助教程。

             

            man curses

             

            (你可以用 ncurese 代替 curses) 個別的函數有單獨的 man 頁面。

             

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

             

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

            posted on 2011-07-21 17:12 unixfy 閱讀(338) 評論(0)  編輯 收藏 引用
            亚洲AV无码久久| 欧美亚洲国产精品久久| 久久精品免费一区二区| 亚洲国产精品无码久久SM| 无码国内精品久久人妻| 国产99久久久国产精品~~牛| 久久精品国产亚洲Aⅴ蜜臀色欲| 日产精品久久久久久久| 久久超碰97人人做人人爱| 青青青青久久精品国产h| 久久香综合精品久久伊人| 久久亚洲私人国产精品| 国产精品欧美久久久久天天影视| 色婷婷综合久久久久中文字幕| 精品国产福利久久久| 亚洲国产天堂久久久久久| 精品熟女少妇a∨免费久久| 久久综合久久综合亚洲| 99久久免费国产精品| 久久青青草原精品国产| 久久这里的只有是精品23| 99久久综合狠狠综合久久| 国内精品久久久久| 亚洲精品国精品久久99热一| 欧美午夜A∨大片久久| 91精品国产综合久久四虎久久无码一级 | 无码任你躁久久久久久老妇| 精品国产乱码久久久久久郑州公司 | 97热久久免费频精品99| 无码国产69精品久久久久网站| 欧美精品乱码99久久蜜桃| 欧美伊人久久大香线蕉综合69 | 久久国产精品一国产精品金尊| 久久综合久久综合亚洲| 久久影视综合亚洲| 无夜精品久久久久久| 久久综合狠狠综合久久97色| 久久综合九色欧美综合狠狠| 久久综合给合综合久久| 亚洲欧美国产精品专区久久| 久久久久99这里有精品10|