何為終端:
http://www.PRogramgo.com/article/8062473246/
http://www.programgo.com/article/7784652169/;jsessionid=242CCD1156839513C97203D4E4623CB7
一個終端模擬器打開的窗口可以視為一個終端,tty1到tty7也是終端
終端也是一個設備文件,通常一個終端有自己的標準輸入,標準輸出,標準錯誤,它們都可以被重定向。
有一個特殊設備/dev/tty始終指向當前終端或當前的登錄回話。通過對/dev/tty操作來避免重定向導致的問題
若想知道標準輸出是否被重定向,只需要檢查底層文件描述符是否關聯到一個終端即可。
系統調用
#include <unisd.h>
int isatty(int fd):
如果打開的文件描述符fd連接到一個終端,則系統調用isatty返回1,否者返回0;。
終端驅動程序和通用接口:
有時,程序需要更加精細的終端控制能力,而不是僅通過簡單的文件操作來完成對終端的一些控制。
linux提供了一組編程接口用來控制終端驅動程序的行為,從而使得更好地控制終端的輸入和輸出。
termios是在POSIX規范中定義的標準接口。通過設置termios類型的數據結構中的值和使用一小組
函數調用,你就可以對終端接口進行控制。
可以被調整用來影響終端的值按照不同的模式被分成如下幾組:
輸入模式
輸出模式
控制模式
本地模式
特殊控制字符
最小termios結構的典型定義如下
#include <termios.h>
struct termios{
tcflag_t c_iflag;
tcflag_t c_oflag;
tcflag_t c_cflag;
tcflag_t c_lflag;
cc_t c_cc[NCCS];
};
int tcgetattr(int fd, struct termios *termios_p);
把當前終端接口變量的值寫入termios_p參數指向的結構。
int tcsetattr(int fd, int actions, const struct termios *termios_p);
把termios_p的值寫入終端設置,此外程序有責任將終端狀態恢復為改變之前的狀態
action參數:
TCSANOW:立刻對值進行修改
TCSADRAIN:等當前的輸出完成后再對值進行修改
TCSAFLUSH:等當前的輸出完成后再對值進行修改,但丟棄還未從read調用返回的當前可用的任何輸入
輸入模式:輸入模式控制輸入數據(終端驅動程序從串行口或鍵盤接收到的字符)在被傳遞給程序之前的處理方式。
通過設置termios結構中的c_iflag成員的標志對它們進行控制。所有的標志都被定義為宏
一般默認設置就可以,無需改動。
如下:
BRKINT: 在當前輸入行檢測到一個終止狀態時,產生一個中斷
IGNBRK: 忽略輸入行中的終止狀態
ICRNL: 將接收到的回車符轉換為新行符
IGNCR: 忽略接收到的回車符
INLCR: 將接收到的新行符轉換為回車符
IGNPAR: 忽略奇偶校驗錯誤的字符
INPCK: 對接收到的字符執行奇偶校驗
PARMRK: 對奇偶校驗出錯做出標記
ISTRip: 將所有接收到的字符裁剪為7比特
IXOFF: 對輸入啟用軟件流控
IXON: 對輸出啟用軟件流控
輸出模式:同樣的通過設置c_oflag來控制,如果OPOST沒有被設置,則其他所有標志都被忽略
OPOST 打開輸出處理功能
ONLCR 將輸出中的換行符轉換為回車/換行符
OCRNL 將輸出中的回車符轉換為新行符
ONOCR 在第0列不輸出回車符
ONLRET 不輸出回車符
OFILL 發送填充字符以提供延時
OFDEL 用DEL而不是NULL字符作為填充字符
NLDLY 新行符延時選擇
CRDLY 回車符延時選擇
TABDLY 制表符延時選擇
BSDLY 退格符延時選擇
VTDLY 垂直制表符延時選擇
FFDLY 換頁符延時選擇
控制模式:
控制模式控制終端的硬件特性。同樣的設置c_cflag成員
控制模式主要用于串行線連接調制解調器的情況,雖然它也可以用來和終端進行“對話”。但與通過使用termios的控制模式來修改默認的線路行為相比,直接修改終端配置文件通常更加容易些。????
CLOCAL 忽略所有調制解調器狀態的狀態行
CREAD 啟用字符接收器
CS5 發送或接收字符時使用5比特
CS6 發送或接收字符時使用6比特
CS7 發送或接收字符時使用7比特
CS8 發送或接收字符時使用8比特
CSTOPB 每個字符使用兩個停止位而不是一個
HUPCL 關閉時掛斷調制解調器,
PARENB 啟用奇偶校驗碼的生成和檢測功能
PARODD 使用奇校驗而不是偶校驗
本地模式:
本地模式控制終端的各種特性,通過設置c_lflag成員
ECHO ***啟用輸入字符的本地回顯功能
ECHOE 接收到ERASE時執行退格、空格、退格的動作組合
ECHOK 接收到KILL字符時執行行刪除操作
ECHONL 回顯新行符
ICANON ***啟用標準輸入處理
IEXTEN 啟用基于特定實現的函數
ISIG 啟用信號
NOFLSH 禁止清空隊列
TOSTOP 在試圖進行寫操作之前給后臺進程發送一個信號
特殊控制字符:
特殊控制字符是一些字符組合,如Ctrl+C,當用戶鍵入這樣的組合鍵時,終端會采取一些特殊的處理方式。
c_cc數組成員將各種特殊控制字符映射到對應的支持函數。每個字符的位置(也就是數組下標)是由一個宏定義的,
但并不限制這些字符必須是控制字符。
根據本地模式中的ICANON標志,即是否將終端設置為標準模式或非標準模式c_cc數組有很大差別。
在標準模式下可用的數組下標
VEOF
VEOL
VERASE
VINTR
VKILL
VQUIT
VSUSP
VSTART
VSTOP
在非標準模式下可用的數組下標
VINTR
VMIN
VQUIT
VSUSP
VTIME
VSTART
VSTOP
在shell下使用stty -a可以查看當前的termios設置情況
終端速度:
speed_t cfgetispeed(const struct termios *);
speed_t cfgetospeed(const struct termios *);
int cfsetispeed(struct termios *, speed_t speed);
int cfsetospeed(struct termios *, speed_t speed);
這些函數都是作用于termios結構體的,所以需要調用tcsetattr才能生效
speed參數可以為B0, B1200, B2400, B9600, B19200, B38400
其他函數:
int tcdrain(int fd);//等待所有的輸出,才返回
int tcflow(int fd, int flowtype);//暫停或重新開始輸出
int tcflush(int fd, int in_out_selector);//清空輸入、輸出或兩者都清空
終端的輸出:
通過termios可以控制鍵盤的輸入,但是確不能夠控制輸出到屏幕的內容。比如在特定的位置輸出。
幾乎所有的終端都是用escape轉義序列控制(以escape字符開頭的字符串)來控制光標的位置和終端的其他屬性——比如黑體和閃爍等,但是他們在實現手段上沒有統一的標準。terminfo軟件包便解決了,終端之間的差異問題,使程序的移植性更好。
terminfo軟件包包含一個由大量不同類型終端的功能標志和escape轉義序列等信息構成的數據庫,并為它們提供了一個統一的編程接口。一個使用這個軟件包的程序能夠隨著數據庫的擴展來適應未來的終端類型,對不同類型終端的支持不再需要由應用程序自身來提供。
每個終端類型對應一個terminfo文件。每個terminfo定義由3種類型的數據項組成。每個數據項被成為capname(可以簡單的將一個數據項理解為這個終端的什么輸入與什么動作或什么信息之間的映射關系),它們分別用于終端的一種功能標志,三種類型的功能標志為。
布爾功能標志:指出終端是否支持某個特定的功能 比如支持XON/XOFF流控 xon
數值功能標志:定義長度 比如 cols#80 lines#24
字符串功能標志:它有兩種終端作用 ,用于訪問終端功能的輸出字符串和當用戶按下特定按鍵時終端接收 到的輸入字符串。比如刪除到行尾 el=Esc,[,K 它在terminfo源文件中寫為el=/E[K,還有很多內容不詳細列舉(page165)
使用terminfo功能標志:
使用terminfo的第一步是調用setupterm設置終端類型,這將為當前的終端類型初始化一個TERMINAL結構
#include <term.h>
int setupterm(char *term, int fd, int *errret);
term: 將終端類型設置為term指向的值,如果為NULL,就使用環境變量TERM的值
fd:為一個打開的文件描述符,它用于向終端寫數據,比如 stdout
errret:如果他不是一個空指針,返回值保存在errret中
-1:terminfo數據庫不存在
0:terminfo數據庫中沒有匹配的數據項
1:成功
返回值:其在成功時返回OK,失敗時返回ERR。如果errret被設置為空指針,setupterm函數會在失敗時輸出一條診斷信息并導致程序直接退出。
第二步:通過調用合適的函數獲得功能標志
int tigetflag(char *capname);//返回terminfo中的布爾功能標志,失敗返回 -1
int tigetnum(char *capname);//數值功能標志. -2
char *tigetstr(char *capname);//字符串功能標志 (char *)-1
例:獲得當前終端的大小
1. setupterm(NULL, fileno(stdout), (int *)0);
2. nrows = tigetnum("lines");
ncolumns = tigetnum("cols");
第三步 //如果時帶參數的字符串功能字,設置參數
char *tparm(char *cap, long p1, long p2, ..., long p9);//最多可以設置9個參數
第四步 //將字符串發送到終端,使用以下函數可以正確處理終端完成一個操作所需要的延時
int putp(char *const str);//它將控制字符串發送到標準輸出stdout OK | ERR
int tputs(char *const str, int affcnt, int (*putfunc)(int));//這是為了不能通過標準輸出stdout訪問終端的情況準備的
affcnt: 表明受這一變化影響的行數,一般為1
putfunc:用于輸出字符串的函數,其參數和返回值類型必須與putchar函數相同
其實putp 相當于 tputs(string, 1, putchar);
例:將光標移動到指定位置
1. cursor = tigetstr("cup");
2. esc_sequence = tparm(cursor, 5, 30);//第5行,第30列
3. putp(esc_sequence):
新聞熱點
疑難解答